#include "tm_p.h"
#include "target.h"
#include "langhooks.h"
+#include "basic-block.h"
#define CALLED_AS_BUILT_IN(NODE) \
(!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10))
static tree fold_builtin_strcmp (tree);
static tree fold_builtin_strncmp (tree);
static tree fold_builtin_signbit (tree);
+static tree fold_builtin_copysign (tree, tree);
+static tree fold_builtin_isascii (tree);
+static tree fold_builtin_toascii (tree);
+static tree fold_builtin_isdigit (tree);
+static tree fold_builtin_fabs (tree, tree);
+static tree fold_builtin_abs (tree, tree);
+static tree fold_builtin_unordered_cmp (tree, enum tree_code, enum tree_code);
static tree simplify_builtin_memcmp (tree);
static tree simplify_builtin_strcmp (tree);
if (!validate_arglist (arglist, POINTER_TYPE, VOID_TYPE))
return NULL_RTX;
- if (target == 0 || GET_CODE (target) != REG
+ if (target == 0 || !REG_P (target)
|| REGNO (target) < FIRST_PSEUDO_REGISTER)
target = gen_reg_rtx (TYPE_MODE (integer_type_node));
chain current, so the code is placed at the start of the
function. */
push_topmost_sequence ();
- emit_insn_before (seq, NEXT_INSN (get_insns ()));
+ emit_insn_before (seq, NEXT_INSN (entry_of_function ()));
pop_topmost_sequence ();
return temp;
}
rtx value = gen_reg_rtx (Pmode);
emit_move_insn (value, adjust_address (arguments, Pmode, size));
emit_move_insn (struct_value, value);
- if (GET_CODE (struct_value) == REG)
+ if (REG_P (struct_value))
use_reg (&call_fusage, struct_value);
size += GET_MODE_SIZE (Pmode);
}
rtx operand = XEXP (XEXP (XEXP (note, 0), 1), 0);
/* Check operand is a register with expected mode. */
if (operand
- && GET_CODE (operand) == REG
+ && REG_P (operand)
&& GET_MODE (operand) == mode)
{
/* Replace the REG_EQUAL note with a SQRT rtx. */
/* Make a place to write the result of the instruction. */
result = target;
if (! (result != 0
- && GET_CODE (result) == REG
+ && REG_P (result)
&& GET_MODE (result) == insn_mode
&& REGNO (result) >= FIRST_PSEUDO_REGISTER))
result = gen_reg_rtx (insn_mode);
/* Make a place to write the result of the instruction. */
result = target;
if (! (result != 0
- && GET_CODE (result) == REG && GET_MODE (result) == insn_mode
+ && REG_P (result) && GET_MODE (result) == insn_mode
&& REGNO (result) >= FIRST_PSEUDO_REGISTER))
result = gen_reg_rtx (insn_mode);
/* Make a place to write the result of the instruction. */
result = target;
if (! (result != 0
- && GET_CODE (result) == REG && GET_MODE (result) == insn_mode
+ && REG_P (result) && GET_MODE (result) == insn_mode
&& REGNO (result) >= FIRST_PSEUDO_REGISTER))
result = gen_reg_rtx (insn_mode);
/* Make a place to write the result of the instruction. */
result = target;
if (! (result != 0
- && GET_CODE (result) == REG && GET_MODE (result) == insn_mode
+ && REG_P (result) && GET_MODE (result) == insn_mode
&& REGNO (result) >= FIRST_PSEUDO_REGISTER))
result = gen_reg_rtx (insn_mode);
is inside a start_sequence, make the outer-level insn chain current, so
the code is placed at the start of the function. */
push_topmost_sequence ();
- emit_insn_after (seq, get_insns ());
+ emit_insn_after (seq, entry_of_function ());
pop_topmost_sequence ();
return val;
if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE)
{
tree p1 = build_pointer_type (TREE_TYPE (va_list_type_node));
- tree p2 = build_pointer_type (va_list_type_node);
-
- valist = build1 (ADDR_EXPR, p2, valist);
- valist = fold_convert (p1, valist);
+ valist = build_fold_addr_expr_with_type (valist, p1);
}
}
else
if (TREE_SIDE_EFFECTS (valist))
valist = save_expr (valist);
- valist = fold (build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)),
- valist));
+ valist = build_fold_indirect_ref (valist);
}
return valist;
return result;
}
+/* Like std_expand_builtin_va_arg, but gimplify instead of expanding. */
+
+tree
+std_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p)
+{
+ tree addr, t, type_size = NULL;
+ tree align, alignm1;
+ tree rounded_size;
+ HOST_WIDE_INT boundary;
+
+ /* Compute the rounded size of the type. */
+ align = size_int (PARM_BOUNDARY / BITS_PER_UNIT);
+ alignm1 = size_int (PARM_BOUNDARY / BITS_PER_UNIT - 1);
+ boundary = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type);
+
+ /* va_list pointer is aligned to PARM_BOUNDARY. If argument actually
+ requires greater alignment, we must perform dynamic alignment. */
+
+ if (boundary > PARM_BOUNDARY)
+ {
+ if (!PAD_VARARGS_DOWN)
+ {
+ t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist,
+ build2 (PLUS_EXPR, TREE_TYPE (valist), valist,
+ build_int_2 (boundary / BITS_PER_UNIT - 1, 0)));
+ gimplify_stmt (&t);
+ append_to_statement_list (t, pre_p);
+ }
+ t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist,
+ build2 (BIT_AND_EXPR, TREE_TYPE (valist), valist,
+ build_int_2 (~(boundary / BITS_PER_UNIT - 1), -1)));
+ gimplify_stmt (&t);
+ append_to_statement_list (t, pre_p);
+ }
+ if (type == error_mark_node
+ || (type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type))) == NULL
+ || TREE_OVERFLOW (type_size))
+ rounded_size = size_zero_node;
+ else
+ {
+ rounded_size = fold (build2 (PLUS_EXPR, sizetype, type_size, alignm1));
+ rounded_size = fold (build2 (TRUNC_DIV_EXPR, sizetype,
+ rounded_size, align));
+ rounded_size = fold (build2 (MULT_EXPR, sizetype,
+ rounded_size, align));
+ }
+
+ /* Reduce rounded_size so it's sharable with the postqueue. */
+ gimplify_expr (&rounded_size, pre_p, post_p, is_gimple_val, fb_rvalue);
+
+ /* Get AP. */
+ addr = valist;
+ if (PAD_VARARGS_DOWN && ! integer_zerop (rounded_size))
+ {
+ /* Small args are padded downward. */
+ addr = fold (build2 (PLUS_EXPR, TREE_TYPE (addr), addr,
+ fold (build3 (COND_EXPR, sizetype,
+ fold (build2 (GT_EXPR, sizetype,
+ rounded_size,
+ align)),
+ size_zero_node,
+ fold (build2 (MINUS_EXPR,
+ sizetype,
+ rounded_size,
+ type_size))))));
+ }
+
+ /* Compute new value for AP. */
+ if (! integer_zerop (rounded_size))
+ {
+ t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist,
+ build2 (PLUS_EXPR, TREE_TYPE (valist), valist,
+ rounded_size));
+ gimplify_stmt (&t);
+ append_to_statement_list (t, post_p);
+ }
+
+ addr = fold_convert (build_pointer_type (type), addr);
+ return build_fold_indirect_ref (addr);
+}
+
+/* Return a dummy expression of type TYPE in order to keep going after an
+ error. */
+
+static tree
+dummy_object (tree type)
+{
+ tree t = convert (build_pointer_type (type), null_pointer_node);
+ return build1 (INDIRECT_REF, type, t);
+}
+
+/* Like expand_builtin_va_arg, but gimplify instead of expanding. */
+
+enum gimplify_status
+gimplify_va_arg_expr (tree *expr_p, tree *pre_p, tree *post_p)
+{
+ tree promoted_type, want_va_type, have_va_type;
+ tree valist = TREE_OPERAND (*expr_p, 0);
+ tree type = TREE_TYPE (*expr_p);
+ tree t;
+
+ /* Verify that valist is of the proper type. */
+
+ want_va_type = va_list_type_node;
+ have_va_type = TREE_TYPE (valist);
+ if (TREE_CODE (want_va_type) == ARRAY_TYPE)
+ {
+ /* If va_list is an array type, the argument may have decayed
+ to a pointer type, e.g. by being passed to another function.
+ In that case, unwrap both types so that we can compare the
+ underlying records. */
+ if (TREE_CODE (have_va_type) == ARRAY_TYPE
+ || TREE_CODE (have_va_type) == POINTER_TYPE)
+ {
+ want_va_type = TREE_TYPE (want_va_type);
+ have_va_type = TREE_TYPE (have_va_type);
+ }
+ }
+
+ if (TYPE_MAIN_VARIANT (want_va_type) != TYPE_MAIN_VARIANT (have_va_type))
+ {
+ error ("first argument to `va_arg' not of type `va_list'");
+ return GS_ERROR;
+ }
+
+ /* Generate a diagnostic for requesting data of a type that cannot
+ be passed through `...' due to type promotion at the call site. */
+ else if ((promoted_type = lang_hooks.types.type_promotes_to (type))
+ != type)
+ {
+ static bool gave_help;
+
+ /* Unfortunately, this is merely undefined, rather than a constraint
+ violation, so we cannot make this an error. If this call is never
+ executed, the program is still strictly conforming. */
+ warning ("`%T' is promoted to `%T' when passed through `...'",
+ type, promoted_type);
+ if (! gave_help)
+ {
+ gave_help = true;
+ warning ("(so you should pass `%T' not `%T' to `va_arg')",
+ promoted_type, type);
+ }
+
+ /* We can, however, treat "undefined" any way we please.
+ Call abort to encourage the user to fix the program. */
+ inform ("if this code is reached, the program will abort");
+ t = build_function_call_expr (implicit_built_in_decls[BUILT_IN_TRAP],
+ NULL);
+ append_to_statement_list (t, pre_p);
+
+ /* This is dead code, but go ahead and finish so that the
+ mode of the result comes out right. */
+ *expr_p = dummy_object (type);
+ return GS_ALL_DONE;
+ }
+ else
+ {
+ /* Make it easier for the backends by protecting the valist argument
+ from multiple evaluations. */
+ if (TREE_CODE (va_list_type_node) == ARRAY_TYPE)
+ {
+ /* For this case, the backends will be expecting a pointer to
+ TREE_TYPE (va_list_type_node), but it's possible we've
+ actually been given an array (an actual va_list_type_node).
+ So fix it. */
+ if (TREE_CODE (TREE_TYPE (valist)) == ARRAY_TYPE)
+ {
+ tree p1 = build_pointer_type (TREE_TYPE (va_list_type_node));
+ valist = build_fold_addr_expr_with_type (valist, p1);
+ }
+ gimplify_expr (&valist, pre_p, post_p, is_gimple_val, fb_rvalue);
+ }
+ else
+ gimplify_expr (&valist, pre_p, post_p, is_gimple_min_lval, fb_lvalue);
+
+ if (!targetm.calls.gimplify_va_arg_expr)
+ /* Once most targets are converted this should abort. */
+ return GS_ALL_DONE;
+
+ *expr_p = targetm.calls.gimplify_va_arg_expr (valist, type, pre_p, post_p);
+ return GS_OK;
+ }
+}
+
/* Expand ARGLIST, from a call to __builtin_va_end. */
static rtx
if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
return tem;
- if (GET_CODE (tem) != REG
+ if (!REG_P (tem)
&& ! CONSTANT_P (tem))
tem = copy_to_mode_reg (Pmode, tem);
return tem;
switch (fcode)
{
- case BUILT_IN_ABS:
- case BUILT_IN_LABS:
- case BUILT_IN_LLABS:
- case BUILT_IN_IMAXABS:
- /* build_function_call changes these into ABS_EXPR. */
- abort ();
-
case BUILT_IN_FABS:
case BUILT_IN_FABSF:
case BUILT_IN_FABSL:
}
break;
- case BUILT_IN_CONJ:
- case BUILT_IN_CONJF:
- case BUILT_IN_CONJL:
- case BUILT_IN_CREAL:
- case BUILT_IN_CREALF:
- case BUILT_IN_CREALL:
- case BUILT_IN_CIMAG:
- case BUILT_IN_CIMAGF:
- case BUILT_IN_CIMAGL:
- /* expand_tree_builtin changes these into CONJ_EXPR, REALPART_EXPR
- and IMAGPART_EXPR. */
- abort ();
-
case BUILT_IN_EXP:
case BUILT_IN_EXPF:
case BUILT_IN_EXPL:
break;
default: /* just do library call, if unknown builtin */
- if (!DECL_ASSEMBLER_NAME_SET_P (fndecl))
- error ("built-in function `%s' not currently supported",
- IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+ break;
}
/* The switch statement above can drop through to cause the function
return NULL_TREE;
}
+/* Fold function call to builtin copysign, copysignf or copysignl.
+ Return NULL_TREE if no simplification can be made. */
+
+static tree
+fold_builtin_copysign (tree arglist, tree type)
+{
+ tree arg1, arg2;
+
+ if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
+ return NULL_TREE;
+
+ arg1 = TREE_VALUE (arglist);
+ arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+
+ /* copysign(X,X) is X. */
+ if (operand_equal_p (arg1, arg2, 0))
+ return fold_convert (type, arg1);
+
+ /* If ARG1 and ARG2 are compile-time constants, determine the result. */
+ if (TREE_CODE (arg1) == REAL_CST
+ && TREE_CODE (arg2) == REAL_CST
+ && !TREE_CONSTANT_OVERFLOW (arg1)
+ && !TREE_CONSTANT_OVERFLOW (arg2))
+ {
+ REAL_VALUE_TYPE c1, c2;
+
+ c1 = TREE_REAL_CST (arg1);
+ c2 = TREE_REAL_CST (arg2);
+ real_copysign (&c1, &c2);
+ return build_real (type, c1);
+ c1.sign = c2.sign;
+ }
+
+ /* copysign(X, Y) is fabs(X) when Y is always non-negative.
+ Remember to evaluate Y for side-effects. */
+ if (tree_expr_nonnegative_p (arg2))
+ return omit_one_operand (type,
+ fold (build1 (ABS_EXPR, type, arg1)),
+ arg2);
+
+ return NULL_TREE;
+}
+
/* Fold a call to builtin isascii. */
static tree
}
}
+/* Fold a call to fabs, fabsf or fabsl. */
+
+static tree
+fold_builtin_fabs (tree arglist, tree type)
+{
+ tree arg;
+
+ if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return 0;
+
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (arg) == REAL_CST)
+ return fold_abs_const (arg, type);
+ return fold (build1 (ABS_EXPR, type, arg));
+}
+
+/* Fold a call to abs, labs, llabs or imaxabs. */
+
+static tree
+fold_builtin_abs (tree arglist, tree type)
+{
+ tree arg;
+
+ if (!validate_arglist (arglist, INTEGER_TYPE, VOID_TYPE))
+ return 0;
+
+ arg = TREE_VALUE (arglist);
+ if (TREE_CODE (arg) == INTEGER_CST)
+ return fold_abs_const (arg, type);
+ return fold (build1 (ABS_EXPR, type, arg));
+}
+
+/* Fold a call to an unordered comparison function such as
+ __builtin_isgreater(). ARGLIST is the funtion's argument list
+ and TYPE is the functions return type. UNORDERED_CODE and
+ ORDERED_CODE are comparison codes that give the opposite of
+ the desired result. UNORDERED_CODE is used for modes that can
+ hold NaNs and ORDERED_CODE is used for the rest. */
+
+static tree
+fold_builtin_unordered_cmp (tree exp,
+ enum tree_code unordered_code,
+ enum tree_code ordered_code)
+{
+ tree fndecl = get_callee_fndecl (exp);
+ tree arglist = TREE_OPERAND (exp, 1);
+ tree type = TREE_TYPE (TREE_TYPE (fndecl));
+ enum tree_code code;
+ tree arg0, arg1;
+
+ if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
+ {
+ enum tree_code code0, code1;
+ tree type0, type1;
+ tree cmp_type = 0;
+
+ /* Check that we have exactly two arguments. */
+ if (arglist == 0 || TREE_CHAIN (arglist) == 0)
+ {
+ error ("too few arguments to function `%s'",
+ IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+ return error_mark_node;
+ }
+ else if (TREE_CHAIN (TREE_CHAIN (arglist)) != 0)
+ {
+ error ("too many arguments to function `%s'",
+ IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+ return error_mark_node;
+ }
+
+ arg0 = TREE_VALUE (arglist);
+ arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+
+ type0 = TREE_TYPE (arg0);
+ type1 = TREE_TYPE (arg1);
+
+ code0 = TREE_CODE (type0);
+ code1 = TREE_CODE (type1);
+
+ if (code0 == REAL_TYPE && code1 == REAL_TYPE)
+ /* Choose the wider of two real types. */
+ cmp_type = TYPE_PRECISION (type0) >= TYPE_PRECISION (type1)
+ ? type0 : type1;
+ else if (code0 == REAL_TYPE && code1 == INTEGER_TYPE)
+ cmp_type = type0;
+ else if (code0 == INTEGER_TYPE && code1 == REAL_TYPE)
+ cmp_type = type1;
+ else
+ {
+ error ("non-floating-point argument to function `%s'",
+ IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+ return error_mark_node;
+ }
+
+ arg0 = fold_convert (cmp_type, arg0);
+ arg1 = fold_convert (cmp_type, arg1);
+ }
+ else
+ {
+ arg0 = TREE_VALUE (arglist);
+ arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+ }
+
+ if (unordered_code == UNORDERED_EXPR)
+ {
+ if (!MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg0))))
+ return omit_two_operands (type, integer_zero_node, arg0, arg1);
+ return fold (build2 (UNORDERED_EXPR, type, arg0, arg1));
+ }
+
+ code = MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg0))) ? unordered_code
+ : ordered_code;
+ return fold (build1 (TRUTH_NOT_EXPR, type,
+ fold (build2 (code, type, arg0, arg1))));
+}
+
/* Used by constant folding to eliminate some builtin calls early. EXP is
the CALL_EXPR of a call to a builtin function. */
case BUILT_IN_FABS:
case BUILT_IN_FABSF:
case BUILT_IN_FABSL:
- if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
- return fold (build1 (ABS_EXPR, type, TREE_VALUE (arglist)));
+ return fold_builtin_fabs (arglist, type);
+
+ case BUILT_IN_ABS:
+ case BUILT_IN_LABS:
+ case BUILT_IN_LLABS:
+ case BUILT_IN_IMAXABS:
+ return fold_builtin_abs (arglist, type);
+
+ case BUILT_IN_CONJ:
+ case BUILT_IN_CONJF:
+ case BUILT_IN_CONJL:
+ if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE))
+ return fold (build1 (CONJ_EXPR, type, TREE_VALUE (arglist)));
+ break;
+
+ case BUILT_IN_CREAL:
+ case BUILT_IN_CREALF:
+ case BUILT_IN_CREALL:
+ if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE))
+ return non_lvalue (fold (build1 (REALPART_EXPR, type,
+ TREE_VALUE (arglist))));
+ break;
+
+ case BUILT_IN_CIMAG:
+ case BUILT_IN_CIMAGF:
+ case BUILT_IN_CIMAGL:
+ if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE))
+ return non_lvalue (fold (build1 (IMAGPART_EXPR, type,
+ TREE_VALUE (arglist))));
break;
case BUILT_IN_CABS:
case BUILT_IN_EXPF:
case BUILT_IN_EXPL:
return fold_builtin_exponent (exp, &dconste);
+
case BUILT_IN_EXP2:
case BUILT_IN_EXP2F:
case BUILT_IN_EXP2L:
return fold_builtin_exponent (exp, &dconst2);
+
case BUILT_IN_EXP10:
case BUILT_IN_EXP10F:
case BUILT_IN_EXP10L:
case BUILT_IN_POW10F:
case BUILT_IN_POW10L:
return fold_builtin_exponent (exp, &dconst10);
+
case BUILT_IN_LOG:
case BUILT_IN_LOGF:
case BUILT_IN_LOGL:
return fold_builtin_logarithm (exp, &dconste);
- break;
+
case BUILT_IN_LOG2:
case BUILT_IN_LOG2F:
case BUILT_IN_LOG2L:
return fold_builtin_logarithm (exp, &dconst2);
- break;
+
case BUILT_IN_LOG10:
case BUILT_IN_LOG10F:
case BUILT_IN_LOG10L:
return fold_builtin_logarithm (exp, &dconst10);
- break;
case BUILT_IN_TAN:
case BUILT_IN_TANF:
case BUILT_IN_ISDIGIT:
return fold_builtin_isdigit (arglist);
+ case BUILT_IN_COPYSIGN:
+ case BUILT_IN_COPYSIGNF:
+ case BUILT_IN_COPYSIGNL:
+ return fold_builtin_copysign (arglist, type);
+
+ case BUILT_IN_ISGREATER:
+ return fold_builtin_unordered_cmp (exp, UNLE_EXPR, LE_EXPR);
+ case BUILT_IN_ISGREATEREQUAL:
+ return fold_builtin_unordered_cmp (exp, UNLT_EXPR, LT_EXPR);
+ case BUILT_IN_ISLESS:
+ return fold_builtin_unordered_cmp (exp, UNGE_EXPR, GE_EXPR);
+ case BUILT_IN_ISLESSEQUAL:
+ return fold_builtin_unordered_cmp (exp, UNGT_EXPR, GT_EXPR);
+ case BUILT_IN_ISLESSGREATER:
+ return fold_builtin_unordered_cmp (exp, UNEQ_EXPR, EQ_EXPR);
+ case BUILT_IN_ISUNORDERED:
+ return fold_builtin_unordered_cmp (exp, UNORDERED_EXPR, NOP_EXPR);
+
default:
break;
}
/* If the len parameter is zero, return zero. */
if (host_integerp (len, 1) && tree_low_cst (len, 1) == 0)
- {
- /* Evaluate and ignore arg1 and arg2 in case they have
- side-effects. */
- return build2 (COMPOUND_EXPR, integer_type_node, arg1,
- build2 (COMPOUND_EXPR, integer_type_node,
- arg2, integer_zero_node));
- }
+ /* Evaluate and ignore arg1 and arg2 in case they have side-effects. */
+ return omit_two_operands (integer_type_node, integer_zero_node,
+ arg1, arg2);
p1 = c_getstr (arg1);
p2 = c_getstr (arg2);
/* If the len parameter is zero, return zero. */
if (integer_zerop (arg3))
- {
- /* Evaluate and ignore arg1 and arg2 in case they have
- side-effects. */
- return build2 (COMPOUND_EXPR, integer_type_node, arg1,
- build2 (COMPOUND_EXPR, integer_type_node,
- arg2, integer_zero_node));
- }
+ /* Evaluate and ignore arg1 and arg2 in case they have side-effects. */
+ return omit_two_operands (integer_type_node, integer_zero_node,
+ arg1, arg2);
/* If arg1 and arg2 are equal (and not volatile), return zero. */
if (operand_equal_p (arg1, arg2, 0))
/* If the requested length is zero, or the src parameter string
length is zero, return the dst parameter. */
if (integer_zerop (len) || (p && *p == '\0'))
- return build2 (COMPOUND_EXPR, TREE_TYPE (dst), src,
- build2 (COMPOUND_EXPR, integer_type_node, len, dst));
+ return omit_two_operands (TREE_TYPE (dst), dst, src, len);
/* If the requested len is greater than or equal to the string
length, call strcat. */
/* If either argument is "", return 0. */
if ((p1 && *p1 == '\0') || (p2 && *p2 == '\0'))
- {
- /* Evaluate and ignore both arguments in case either one has
- side-effects. */
- return build2 (COMPOUND_EXPR, integer_type_node, s1,
- build2 (COMPOUND_EXPR, integer_type_node,
- s2, integer_zero_node));
- }
+ /* Evaluate and ignore both arguments in case either one has
+ side-effects. */
+ return omit_two_operands (integer_type_node, integer_zero_node,
+ s1, s2);
return 0;
}
}