changeset 26644:2fc1e528e0e1 v8.2.3851

patch 8.2.3851: Vim9: overhead when comparing string, dict or function Commit: https://github.com/vim/vim/commit/265f811f5a2dac81d9698f5202a661a04ed095f1 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Dec 19 12:33:05 2021 +0000 patch 8.2.3851: Vim9: overhead when comparing string, dict or function Problem: Vim9: overhead when comparing string, dict or function. Solution: Call the intented compare function directly. Refactor to avoid duplicated code.
author Bram Moolenaar <Bram@vim.org>
date Sun, 19 Dec 2021 13:45:03 +0100
parents c1613719988c
children 6a31b4d210e0
files src/proto/typval.pro src/typval.c src/version.c src/vim9execute.c
diffstat 4 files changed, 318 insertions(+), 207 deletions(-) [+]
line wrap: on
line diff
--- a/src/proto/typval.pro
+++ b/src/proto/typval.pro
@@ -27,11 +27,11 @@ int check_for_opt_chan_or_job_arg(typval
 int check_for_job_arg(typval_T *args, int idx);
 int check_for_opt_job_arg(typval_T *args, int idx);
 int check_for_string_or_number_arg(typval_T *args, int idx);
+int check_for_opt_string_or_number_arg(typval_T *args, int idx);
 int check_for_buffer_arg(typval_T *args, int idx);
 int check_for_opt_buffer_arg(typval_T *args, int idx);
 int check_for_lnum_arg(typval_T *args, int idx);
 int check_for_opt_lnum_arg(typval_T *args, int idx);
-int check_for_opt_string_or_number_arg(typval_T *args, int idx);
 int check_for_string_or_blob_arg(typval_T *args, int idx);
 int check_for_string_or_list_arg(typval_T *args, int idx);
 int check_for_string_or_list_or_blob_arg(typval_T *args, int idx);
@@ -54,7 +54,12 @@ char_u *tv_get_string_buf_chk_strict(typ
 char_u *tv_stringify(typval_T *varp, char_u *buf);
 int tv_check_lock(typval_T *tv, char_u *name, int use_gettext);
 void copy_tv(typval_T *from, typval_T *to);
-int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, int ic);
+int typval_compare(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic);
+int typval_compare_list(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
+int typval_compare_blob(typval_T *tv1, typval_T *tv2, exprtype_T type, int *res);
+int typval_compare_dict(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
+int typval_compare_func(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
+int typval_compare_string(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
 char_u *typval_tostring(typval_T *arg, int quotes);
 int tv_islocked(typval_T *tv);
 int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive);
--- a/src/typval.c
+++ b/src/typval.c
@@ -1120,157 +1120,74 @@ copy_tv(typval_T *from, typval_T *to)
 }
 
 /*
- * Compare "typ1" and "typ2".  Put the result in "typ1".
+ * Compare "tv1" and "tv2".
+ * Put the result in "tv1".  Caller should clear "tv2".
  */
     int
 typval_compare(
-    typval_T	*typ1,   // first operand
-    typval_T	*typ2,   // second operand
-    exprtype_T	type,    // operator
-    int		ic)      // ignore case
+    typval_T	*tv1,	// first operand
+    typval_T	*tv2,	// second operand
+    exprtype_T	type,   // operator
+    int		ic)     // ignore case
 {
-    int		i;
     varnumber_T	n1, n2;
-    char_u	*s1, *s2;
-    char_u	buf1[NUMBUFLEN], buf2[NUMBUFLEN];
+    int		res = 0;
     int		type_is = type == EXPR_IS || type == EXPR_ISNOT;
 
-    if (type_is && typ1->v_type != typ2->v_type)
+    if (type_is && tv1->v_type != tv2->v_type)
     {
 	// For "is" a different type always means FALSE, for "notis"
 	// it means TRUE.
 	n1 = (type == EXPR_ISNOT);
     }
-    else if (typ1->v_type == VAR_BLOB || typ2->v_type == VAR_BLOB)
+    else if (tv1->v_type == VAR_BLOB || tv2->v_type == VAR_BLOB)
     {
-	if (type_is)
+	if (typval_compare_blob(tv1, tv2, type, &res) == FAIL)
 	{
-	    n1 = (typ1->v_type == typ2->v_type
-			    && typ1->vval.v_blob == typ2->vval.v_blob);
-	    if (type == EXPR_ISNOT)
-		n1 = !n1;
-	}
-	else if (typ1->v_type != typ2->v_type
-		|| (type != EXPR_EQUAL && type != EXPR_NEQUAL))
-	{
-	    if (typ1->v_type != typ2->v_type)
-		emsg(_("E977: Can only compare Blob with Blob"));
-	    else
-		emsg(_(e_invalblob));
-	    clear_tv(typ1);
+	    clear_tv(tv1);
 	    return FAIL;
 	}
-	else
-	{
-	    // Compare two Blobs for being equal or unequal.
-	    n1 = blob_equal(typ1->vval.v_blob, typ2->vval.v_blob);
-	    if (type == EXPR_NEQUAL)
-		n1 = !n1;
-	}
+	n1 = res;
     }
-    else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST)
+    else if (tv1->v_type == VAR_LIST || tv2->v_type == VAR_LIST)
     {
-	if (type_is)
+	if (typval_compare_list(tv1, tv2, type, ic, &res) == FAIL)
 	{
-	    n1 = (typ1->v_type == typ2->v_type
-			    && typ1->vval.v_list == typ2->vval.v_list);
-	    if (type == EXPR_ISNOT)
-		n1 = !n1;
-	}
-	else if (typ1->v_type != typ2->v_type
-		|| (type != EXPR_EQUAL && type != EXPR_NEQUAL))
-	{
-	    if (typ1->v_type != typ2->v_type)
-		emsg(_("E691: Can only compare List with List"));
-	    else
-		emsg(_("E692: Invalid operation for List"));
-	    clear_tv(typ1);
+	    clear_tv(tv1);
 	    return FAIL;
 	}
-	else
-	{
-	    // Compare two Lists for being equal or unequal.
-	    n1 = list_equal(typ1->vval.v_list, typ2->vval.v_list,
-							    ic, FALSE);
-	    if (type == EXPR_NEQUAL)
-		n1 = !n1;
-	}
+	n1 = res;
     }
-
-    else if (typ1->v_type == VAR_DICT || typ2->v_type == VAR_DICT)
+    else if (tv1->v_type == VAR_DICT || tv2->v_type == VAR_DICT)
     {
-	if (type_is)
+	if (typval_compare_dict(tv1, tv2, type, ic, &res) == FAIL)
 	{
-	    n1 = (typ1->v_type == typ2->v_type
-			    && typ1->vval.v_dict == typ2->vval.v_dict);
-	    if (type == EXPR_ISNOT)
-		n1 = !n1;
-	}
-	else if (typ1->v_type != typ2->v_type
-		|| (type != EXPR_EQUAL && type != EXPR_NEQUAL))
-	{
-	    if (typ1->v_type != typ2->v_type)
-		emsg(_("E735: Can only compare Dictionary with Dictionary"));
-	    else
-		emsg(_("E736: Invalid operation for Dictionary"));
-	    clear_tv(typ1);
+	    clear_tv(tv1);
 	    return FAIL;
 	}
-	else
-	{
-	    // Compare two Dictionaries for being equal or unequal.
-	    n1 = dict_equal(typ1->vval.v_dict, typ2->vval.v_dict,
-							    ic, FALSE);
-	    if (type == EXPR_NEQUAL)
-		n1 = !n1;
-	}
+	n1 = res;
     }
-
-    else if (typ1->v_type == VAR_FUNC || typ2->v_type == VAR_FUNC
-	|| typ1->v_type == VAR_PARTIAL || typ2->v_type == VAR_PARTIAL)
+    else if (tv1->v_type == VAR_FUNC || tv2->v_type == VAR_FUNC
+	|| tv1->v_type == VAR_PARTIAL || tv2->v_type == VAR_PARTIAL)
     {
-	if (type != EXPR_EQUAL && type != EXPR_NEQUAL
-		&& type != EXPR_IS && type != EXPR_ISNOT)
+	if (typval_compare_func(tv1, tv2, type, ic, &res) == FAIL)
 	{
-	    emsg(_("E694: Invalid operation for Funcrefs"));
-	    clear_tv(typ1);
+	    clear_tv(tv1);
 	    return FAIL;
 	}
-	if ((typ1->v_type == VAR_PARTIAL
-					&& typ1->vval.v_partial == NULL)
-		|| (typ2->v_type == VAR_PARTIAL
-					&& typ2->vval.v_partial == NULL))
-	    // When both partials are NULL, then they are equal.
-	    // Otherwise they are not equal.
-	    n1 = (typ1->vval.v_partial == typ2->vval.v_partial);
-	else if (type_is)
-	{
-	    if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC)
-		// strings are considered the same if their value is
-		// the same
-		n1 = tv_equal(typ1, typ2, ic, FALSE);
-	    else if (typ1->v_type == VAR_PARTIAL
-					&& typ2->v_type == VAR_PARTIAL)
-		n1 = (typ1->vval.v_partial == typ2->vval.v_partial);
-	    else
-		n1 = FALSE;
-	}
-	else
-	    n1 = tv_equal(typ1, typ2, ic, FALSE);
-	if (type == EXPR_NEQUAL || type == EXPR_ISNOT)
-	    n1 = !n1;
+	n1 = res;
     }
 
 #ifdef FEAT_FLOAT
     // If one of the two variables is a float, compare as a float.
     // When using "=~" or "!~", always compare as string.
-    else if ((typ1->v_type == VAR_FLOAT || typ2->v_type == VAR_FLOAT)
+    else if ((tv1->v_type == VAR_FLOAT || tv2->v_type == VAR_FLOAT)
 	    && type != EXPR_MATCH && type != EXPR_NOMATCH)
     {
 	float_T f1, f2;
 
-	f1 = tv_get_float(typ1);
-	f2 = tv_get_float(typ2);
+	f1 = tv_get_float(tv1);
+	f2 = tv_get_float(tv2);
 	n1 = FALSE;
 	switch (type)
 	{
@@ -1291,11 +1208,11 @@ typval_compare(
 
     // If one of the two variables is a number, compare as a number.
     // When using "=~" or "!~", always compare as string.
-    else if ((typ1->v_type == VAR_NUMBER || typ2->v_type == VAR_NUMBER)
+    else if ((tv1->v_type == VAR_NUMBER || tv2->v_type == VAR_NUMBER)
 	    && type != EXPR_MATCH && type != EXPR_NOMATCH)
     {
-	n1 = tv_get_number(typ1);
-	n2 = tv_get_number(typ2);
+	n1 = tv_get_number(tv1);
+	n2 = tv_get_number(tv2);
 	switch (type)
 	{
 	    case EXPR_IS:
@@ -1311,20 +1228,20 @@ typval_compare(
 	    default:  break;  // avoid gcc warning
 	}
     }
-    else if (in_vim9script() && (typ1->v_type == VAR_BOOL
-				    || typ2->v_type == VAR_BOOL
-				    || (typ1->v_type == VAR_SPECIAL
-					      && typ2->v_type == VAR_SPECIAL)))
+    else if (in_vim9script() && (tv1->v_type == VAR_BOOL
+				    || tv2->v_type == VAR_BOOL
+				    || (tv1->v_type == VAR_SPECIAL
+					      && tv2->v_type == VAR_SPECIAL)))
     {
-	if (typ1->v_type != typ2->v_type)
+	if (tv1->v_type != tv2->v_type)
 	{
 	    semsg(_(e_cannot_compare_str_with_str),
-		       vartype_name(typ1->v_type), vartype_name(typ2->v_type));
-	    clear_tv(typ1);
+		       vartype_name(tv1->v_type), vartype_name(tv2->v_type));
+	    clear_tv(tv1);
 	    return FAIL;
 	}
-	n1 = typ1->vval.v_number;
-	n2 = typ2->vval.v_number;
+	n1 = tv1->vval.v_number;
+	n2 = tv2->vval.v_number;
 	switch (type)
 	{
 	    case EXPR_IS:
@@ -1333,66 +1250,257 @@ typval_compare(
 	    case EXPR_NEQUAL:   n1 = (n1 != n2); break;
 	    default:
 		semsg(_(e_invalid_operation_for_str),
-						   vartype_name(typ1->v_type));
-		clear_tv(typ1);
+						   vartype_name(tv1->v_type));
+		clear_tv(tv1);
 		return FAIL;
 	}
     }
     else
     {
-	if (in_vim9script()
-	      && ((typ1->v_type != VAR_STRING && typ1->v_type != VAR_SPECIAL)
-	       || (typ2->v_type != VAR_STRING && typ2->v_type != VAR_SPECIAL)))
+	if (typval_compare_string(tv1, tv2, type, ic, &res) == FAIL)
 	{
-	    semsg(_(e_cannot_compare_str_with_str),
-		       vartype_name(typ1->v_type), vartype_name(typ2->v_type));
-	    clear_tv(typ1);
+	    clear_tv(tv1);
 	    return FAIL;
 	}
-	s1 = tv_get_string_buf(typ1, buf1);
-	s2 = tv_get_string_buf(typ2, buf2);
-	if (type != EXPR_MATCH && type != EXPR_NOMATCH)
-	    i = ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2);
-	else
-	    i = 0;
-	n1 = FALSE;
-	switch (type)
-	{
-	    case EXPR_IS:
-	    case EXPR_EQUAL:    n1 = (i == 0); break;
-	    case EXPR_ISNOT:
-	    case EXPR_NEQUAL:   n1 = (i != 0); break;
-	    case EXPR_GREATER:  n1 = (i > 0); break;
-	    case EXPR_GEQUAL:   n1 = (i >= 0); break;
-	    case EXPR_SMALLER:  n1 = (i < 0); break;
-	    case EXPR_SEQUAL:   n1 = (i <= 0); break;
-
-	    case EXPR_MATCH:
-	    case EXPR_NOMATCH:
-		    n1 = pattern_match(s2, s1, ic);
-		    if (type == EXPR_NOMATCH)
-			n1 = !n1;
-		    break;
-
-	    default:  break;  // avoid gcc warning
-	}
+	n1 = res;
     }
-    clear_tv(typ1);
+    clear_tv(tv1);
     if (in_vim9script())
     {
-	typ1->v_type = VAR_BOOL;
-	typ1->vval.v_number = n1 ? VVAL_TRUE : VVAL_FALSE;
+	tv1->v_type = VAR_BOOL;
+	tv1->vval.v_number = n1 ? VVAL_TRUE : VVAL_FALSE;
     }
     else
     {
-	typ1->v_type = VAR_NUMBER;
-	typ1->vval.v_number = n1;
+	tv1->v_type = VAR_NUMBER;
+	tv1->vval.v_number = n1;
     }
 
     return OK;
 }
 
 /*
+ * Compare "tv1" to "tv2" as lists acording to "type" and "ic".
+ * Put the result, false or true, in "res".
+ * Return FAIL and give an error message when the comparison can't be done.
+ */
+    int
+typval_compare_list(
+	typval_T    *tv1,
+	typval_T    *tv2,
+	exprtype_T  type,
+	int	    ic,
+	int	    *res)
+{
+    int	    val = 0;
+
+    if (type == EXPR_IS || type == EXPR_ISNOT)
+    {
+	val = (tv1->v_type == tv2->v_type
+				      && tv1->vval.v_list == tv2->vval.v_list);
+	if (type == EXPR_ISNOT)
+	    val = !val;
+    }
+    else if (tv1->v_type != tv2->v_type
+	    || (type != EXPR_EQUAL && type != EXPR_NEQUAL))
+    {
+	if (tv1->v_type != tv2->v_type)
+	    emsg(_("E691: Can only compare List with List"));
+	else
+	    emsg(_("E692: Invalid operation for List"));
+	return FAIL;
+    }
+    else
+    {
+	val = list_equal(tv1->vval.v_list, tv2->vval.v_list,
+							ic, FALSE);
+	if (type == EXPR_NEQUAL)
+	    val = !val;
+    }
+    *res = val;
+    return OK;
+}
+
+/*
+ * Compare "tv1" to "tv2" as blobs acording to "type".
+ * Put the result, false or true, in "res".
+ * Return FAIL and give an error message when the comparison can't be done.
+ */
+    int
+typval_compare_blob(
+	typval_T    *tv1,
+	typval_T    *tv2,
+	exprtype_T  type,
+	int	    *res)
+{
+    int	    val = 0;
+
+    if (type == EXPR_IS || type == EXPR_ISNOT)
+    {
+	val = (tv1->v_type == tv2->v_type
+			&& tv1->vval.v_blob == tv2->vval.v_blob);
+	if (type == EXPR_ISNOT)
+	    val = !val;
+    }
+    else if (tv1->v_type != tv2->v_type
+	    || (type != EXPR_EQUAL && type != EXPR_NEQUAL))
+    {
+	if (tv1->v_type != tv2->v_type)
+	    emsg(_("E977: Can only compare Blob with Blob"));
+	else
+	    emsg(_(e_invalblob));
+	return FAIL;
+    }
+    else
+    {
+	val = blob_equal(tv1->vval.v_blob, tv2->vval.v_blob);
+	if (type == EXPR_NEQUAL)
+	    val = !val;
+    }
+    *res = val;
+    return OK;
+}
+
+/*
+ * Compare "tv1" to "tv2" as dictionaries acording to "type" and "ic".
+ * Put the result, false or true, in "res".
+ * Return FAIL and give an error message when the comparison can't be done.
+ */
+    int
+typval_compare_dict(
+	typval_T    *tv1,
+	typval_T    *tv2,
+	exprtype_T  type,
+	int	    ic,
+	int	    *res)
+{
+    int	    val;
+
+    if (type == EXPR_IS || type == EXPR_ISNOT)
+    {
+	val = (tv1->v_type == tv2->v_type
+			&& tv1->vval.v_dict == tv2->vval.v_dict);
+	if (type == EXPR_ISNOT)
+	    val = !val;
+    }
+    else if (tv1->v_type != tv2->v_type
+		|| (type != EXPR_EQUAL && type != EXPR_NEQUAL))
+    {
+	if (tv1->v_type != tv2->v_type)
+	    emsg(_("E735: Can only compare Dictionary with Dictionary"));
+	else
+	    emsg(_("E736: Invalid operation for Dictionary"));
+	return FAIL;
+    }
+    else
+    {
+	val = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, FALSE);
+	if (type == EXPR_NEQUAL)
+	    val = !val;
+    }
+    *res = val;
+    return OK;
+}
+
+/*
+ * Compare "tv1" to "tv2" as funcrefs acording to "type" and "ic".
+ * Put the result, false or true, in "res".
+ * Return FAIL and give an error message when the comparison can't be done.
+ */
+    int
+typval_compare_func(
+	typval_T    *tv1,
+	typval_T    *tv2,
+	exprtype_T  type,
+	int	    ic,
+	int	    *res)
+{
+    int	    val = 0;
+
+    if (type != EXPR_EQUAL && type != EXPR_NEQUAL
+	    && type != EXPR_IS && type != EXPR_ISNOT)
+    {
+	emsg(_("E694: Invalid operation for Funcrefs"));
+	return FAIL;
+    }
+    if ((tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial == NULL)
+	    || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial == NULL))
+	// When both partials are NULL, then they are equal.
+	// Otherwise they are not equal.
+	val = (tv1->vval.v_partial == tv2->vval.v_partial);
+    else if (type == EXPR_IS || type == EXPR_ISNOT)
+    {
+	if (tv1->v_type == VAR_FUNC && tv2->v_type == VAR_FUNC)
+	    // strings are considered the same if their value is
+	    // the same
+	    val = tv_equal(tv1, tv2, ic, FALSE);
+	else if (tv1->v_type == VAR_PARTIAL && tv2->v_type == VAR_PARTIAL)
+	    val = (tv1->vval.v_partial == tv2->vval.v_partial);
+	else
+	    val = FALSE;
+    }
+    else
+	val = tv_equal(tv1, tv2, ic, FALSE);
+    if (type == EXPR_NEQUAL || type == EXPR_ISNOT)
+	val = !val;
+    *res = val;
+    return OK;
+}
+
+/*
+ * Compare "tv1" to "tv2" as strings according to "type" and "ic".
+ * Put the result, false or true, in "res".
+ * Return FAIL and give an error message when the comparison can't be done.
+ */
+    int
+typval_compare_string(
+	typval_T    *tv1,
+	typval_T    *tv2,
+	exprtype_T  type,
+	int	    ic,
+	int	    *res)
+{
+    int		i = 0;
+    int		val = FALSE;
+    char_u	*s1, *s2;
+    char_u	buf1[NUMBUFLEN], buf2[NUMBUFLEN];
+
+    if (in_vim9script()
+	  && ((tv1->v_type != VAR_STRING && tv1->v_type != VAR_SPECIAL)
+	   || (tv2->v_type != VAR_STRING && tv2->v_type != VAR_SPECIAL)))
+    {
+	semsg(_(e_cannot_compare_str_with_str),
+		   vartype_name(tv1->v_type), vartype_name(tv2->v_type));
+	return FAIL;
+    }
+    s1 = tv_get_string_buf(tv1, buf1);
+    s2 = tv_get_string_buf(tv2, buf2);
+    if (type != EXPR_MATCH && type != EXPR_NOMATCH)
+	i = ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2);
+    switch (type)
+    {
+	case EXPR_IS:
+	case EXPR_EQUAL:    val = (i == 0); break;
+	case EXPR_ISNOT:
+	case EXPR_NEQUAL:   val = (i != 0); break;
+	case EXPR_GREATER:  val = (i > 0); break;
+	case EXPR_GEQUAL:   val = (i >= 0); break;
+	case EXPR_SMALLER:  val = (i < 0); break;
+	case EXPR_SEQUAL:   val = (i <= 0); break;
+
+	case EXPR_MATCH:
+	case EXPR_NOMATCH:
+		val = pattern_match(s2, s1, ic);
+		if (type == EXPR_NOMATCH)
+		    val = !val;
+		break;
+
+	default:  break;  // avoid gcc warning
+    }
+    *res = val;
+    return OK;
+}
+/*
  * Convert any type to a string, never give an error.
  * When "quotes" is TRUE add quotes to a string.
  * Returns an allocated string.
--- a/src/version.c
+++ b/src/version.c
@@ -750,6 +750,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3851,
+/**/
     3850,
 /**/
     3849,
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -3809,71 +3809,67 @@ exec_instructions(ectx_T *ectx)
 		break;
 
 	    case ISN_COMPARELIST:
+	    case ISN_COMPAREDICT:
+	    case ISN_COMPAREFUNC:
+	    case ISN_COMPARESTRING:
+	    case ISN_COMPAREBLOB:
 		{
 		    typval_T	*tv1 = STACK_TV_BOT(-2);
 		    typval_T	*tv2 = STACK_TV_BOT(-1);
-		    list_T	*arg1 = tv1->vval.v_list;
-		    list_T	*arg2 = tv2->vval.v_list;
-		    int		cmp = FALSE;
+		    exprtype_T	exprtype = iptr->isn_arg.op.op_type;
 		    int		ic = iptr->isn_arg.op.op_ic;
-
-		    switch (iptr->isn_arg.op.op_type)
+		    int		res = FALSE;
+		    int		status = OK;
+
+		    SOURCING_LNUM = iptr->isn_lnum;
+		    if (iptr->isn_type == ISN_COMPARELIST)
+		    {
+			status = typval_compare_list(tv1, tv2,
+							   exprtype, ic, &res);
+		    }
+		    else if (iptr->isn_type == ISN_COMPAREDICT)
 		    {
-			case EXPR_EQUAL: cmp =
-				      list_equal(arg1, arg2, ic, FALSE); break;
-			case EXPR_NEQUAL: cmp =
-				     !list_equal(arg1, arg2, ic, FALSE); break;
-			case EXPR_IS: cmp = arg1 == arg2; break;
-			case EXPR_ISNOT: cmp = arg1 != arg2; break;
-			default: cmp = 0; break;
+			status = typval_compare_dict(tv1, tv2,
+							   exprtype, ic, &res);
+		    }
+		    else if (iptr->isn_type == ISN_COMPAREFUNC)
+		    {
+			status = typval_compare_func(tv1, tv2,
+							   exprtype, ic, &res);
+		    }
+		    else if (iptr->isn_type == ISN_COMPARESTRING)
+		    {
+			status = typval_compare_string(tv1, tv2,
+							   exprtype, ic, &res);
+		    }
+		    else
+		    {
+			status = typval_compare_blob(tv1, tv2, exprtype, &res);
 		    }
 		    --ectx->ec_stack.ga_len;
 		    clear_tv(tv1);
 		    clear_tv(tv2);
 		    tv1->v_type = VAR_BOOL;
-		    tv1->vval.v_number = cmp ? VVAL_TRUE : VVAL_FALSE;
+		    tv1->vval.v_number = res ? VVAL_TRUE : VVAL_FALSE;
+		    if (status == FAIL)
+			goto theend;
 		}
 		break;
 
-	    case ISN_COMPAREBLOB:
-		{
-		    typval_T	*tv1 = STACK_TV_BOT(-2);
-		    typval_T	*tv2 = STACK_TV_BOT(-1);
-		    blob_T	*arg1 = tv1->vval.v_blob;
-		    blob_T	*arg2 = tv2->vval.v_blob;
-		    int		cmp = FALSE;
-
-		    switch (iptr->isn_arg.op.op_type)
-		    {
-			case EXPR_EQUAL: cmp = blob_equal(arg1, arg2); break;
-			case EXPR_NEQUAL: cmp = !blob_equal(arg1, arg2); break;
-			case EXPR_IS: cmp = arg1 == arg2; break;
-			case EXPR_ISNOT: cmp = arg1 != arg2; break;
-			default: cmp = 0; break;
-		    }
-		    --ectx->ec_stack.ga_len;
-		    clear_tv(tv1);
-		    clear_tv(tv2);
-		    tv1->v_type = VAR_BOOL;
-		    tv1->vval.v_number = cmp ? VVAL_TRUE : VVAL_FALSE;
-		}
-		break;
-
-		// TODO: handle separately
-	    case ISN_COMPARESTRING:
-	    case ISN_COMPAREDICT:
-	    case ISN_COMPAREFUNC:
 	    case ISN_COMPAREANY:
 		{
 		    typval_T	*tv1 = STACK_TV_BOT(-2);
 		    typval_T	*tv2 = STACK_TV_BOT(-1);
 		    exprtype_T	exprtype = iptr->isn_arg.op.op_type;
 		    int		ic = iptr->isn_arg.op.op_ic;
+		    int		status;
 
 		    SOURCING_LNUM = iptr->isn_lnum;
-		    typval_compare(tv1, tv2, exprtype, ic);
+		    status = typval_compare(tv1, tv2, exprtype, ic);
 		    clear_tv(tv2);
 		    --ectx->ec_stack.ga_len;
+		    if (status == FAIL)
+			goto theend;
 		}
 		break;