void use_thunk (tree thunk_fndecl, bool emit_p) { tree a, t, function, alias; tree virtual_offset; HOST_WIDE_INT fixed_offset, virtual_value; bool this_adjusting = DECL_THIS_THUNK_P (thunk_fndecl); /* We should have called finish_thunk to give it a name. */ gcc_assert (DECL_NAME (thunk_fndecl)); /* We should never be using an alias, always refer to the aliased thunk. */ gcc_assert (!THUNK_ALIAS (thunk_fndecl)); if (TREE_ASM_WRITTEN (thunk_fndecl)) return; function = THUNK_TARGET (thunk_fndecl); if (DECL_RESULT (thunk_fndecl)) /* We already turned this thunk into an ordinary function. There's no need to process this thunk again. */ return; if (DECL_THUNK_P (function)) /* The target is itself a thunk, process it now. */ use_thunk (function, emit_p); /* Thunks are always addressable; they only appear in vtables. */ TREE_ADDRESSABLE (thunk_fndecl) = 1; /* Figure out what function is being thunked to. It's referenced in this translation unit. */ TREE_ADDRESSABLE (function) = 1; mark_used (function); if (!emit_p) return; if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function)) alias = make_alias_for_thunk (function); else alias = function; fixed_offset = THUNK_FIXED_OFFSET (thunk_fndecl); virtual_offset = THUNK_VIRTUAL_OFFSET (thunk_fndecl); if (virtual_offset) { if (!this_adjusting) virtual_offset = BINFO_VPTR_FIELD (virtual_offset); virtual_value = tree_low_cst (virtual_offset, /*pos=*/0); gcc_assert (virtual_value); } else virtual_value = 0; /* And, if we need to emit the thunk, it's used. */ mark_used (thunk_fndecl); /* This thunk is actually defined. */ DECL_EXTERNAL (thunk_fndecl) = 0; /* The linkage of the function may have changed. FIXME in linkage rewrite. */ TREE_PUBLIC (thunk_fndecl) = TREE_PUBLIC (function); DECL_VISIBILITY (thunk_fndecl) = DECL_VISIBILITY (function); DECL_VISIBILITY_SPECIFIED (thunk_fndecl) = DECL_VISIBILITY_SPECIFIED (function); if (flag_weak && TREE_PUBLIC (thunk_fndecl)) comdat_linkage (thunk_fndecl); if (flag_syntax_only) { TREE_ASM_WRITTEN (thunk_fndecl) = 1; return; } push_to_top_level (); if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function) && targetm.have_named_sections) { resolve_unique_section (function, 0, flag_function_sections); if (DECL_SECTION_NAME (function) != NULL && DECL_ONE_ONLY (function)) { resolve_unique_section (thunk_fndecl, 0, flag_function_sections); /* Output the thunk into the same section as function. */ DECL_SECTION_NAME (thunk_fndecl) = DECL_SECTION_NAME (function); } } /* The back-end expects DECL_INITIAL to contain a BLOCK, so we create one. */ DECL_INITIAL (thunk_fndecl) = make_node (BLOCK); /* Set up cloned argument trees for the thunk. */ t = NULL_TREE; for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a)) { tree x = copy_node (a); TREE_CHAIN (x) = t; DECL_CONTEXT (x) = thunk_fndecl; SET_DECL_RTL (x, NULL_RTX); DECL_HAS_VALUE_EXPR_P (x) = 0; t = x; } a = nreverse (t); DECL_ARGUMENTS (thunk_fndecl) = a; BLOCK_VARS (DECL_INITIAL (thunk_fndecl)) = a; if (this_adjusting && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset, virtual_value, alias)) { const char *fnname; current_function_decl = thunk_fndecl; DECL_RESULT (thunk_fndecl) = build_decl (RESULT_DECL, 0, integer_type_node); fnname = XSTR (XEXP (DECL_RTL (thunk_fndecl), 0), 0); init_function_start (thunk_fndecl); current_function_is_thunk = 1; assemble_start_function (thunk_fndecl, fnname); targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl, fixed_offset, virtual_value, alias); assemble_end_function (thunk_fndecl, fnname); init_insn_lengths (); current_function_decl = 0; cfun = 0; TREE_ASM_WRITTEN (thunk_fndecl) = 1; } else { /* If this is a covariant thunk, or we don't have the necessary code for efficient thunks, generate a thunk function that just makes a call to the real function. Unfortunately, this doesn't work for varargs. */ if (varargs_function_p (function)) error ("generic thunk code fails for method %q#D which uses %<...%>", function); DECL_RESULT (thunk_fndecl) = NULL_TREE; start_preparsed_function (thunk_fndecl, NULL_TREE, SF_PRE_PARSED); /* We don't bother with a body block for thunks. */ /* There's no need to check accessibility inside the thunk body. */ push_deferring_access_checks (dk_no_check); t = a; if (this_adjusting) t = thunk_adjust (t, /*this_adjusting=*/1, fixed_offset, virtual_offset); /* Build up the call to the real function. */ t = tree_cons (NULL_TREE, t, NULL_TREE); for (a = TREE_CHAIN (a); a; a = TREE_CHAIN (a)) t = tree_cons (NULL_TREE, a, t); t = nreverse (t); t = build_call (alias, t); CALL_FROM_THUNK_P (t) = 1; if (VOID_TYPE_P (TREE_TYPE (t))) finish_expr_stmt (t); else { if (!this_adjusting) { tree cond = NULL_TREE; if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE) { /* If the return type is a pointer, we need to protect against NULL. We know there will be an adjustment, because that's why we're emitting a thunk. */ t = save_expr (t); cond = cp_convert (boolean_type_node, t); } t = thunk_adjust (t, /*this_adjusting=*/0, fixed_offset, virtual_offset); if (cond) t = build3 (COND_EXPR, TREE_TYPE (t), cond, t, cp_convert (TREE_TYPE (t), integer_zero_node)); } if (IS_AGGR_TYPE (TREE_TYPE (t))) t = build_cplus_new (TREE_TYPE (t), t); finish_return_stmt (t); } /* Since we want to emit the thunk, we explicitly mark its name as referenced. */ mark_decl_referenced (thunk_fndecl); /* But we don't want debugging information about it. */ DECL_IGNORED_P (thunk_fndecl) = 1; /* Re-enable access control. */ pop_deferring_access_checks (); thunk_fndecl = finish_function (0); tree_lowering_passes (thunk_fndecl); expand_body (thunk_fndecl); } pop_from_top_level (); }
tree convert (tree type, tree expr) { tree e = expr; enum tree_code code = TREE_CODE (type); const char *invalid_conv_diag; tree ret; location_t loc = EXPR_LOCATION (expr); if (type == error_mark_node || error_operand_p (expr)) return error_mark_node; if ((invalid_conv_diag = targetm.invalid_conversion (TREE_TYPE (expr), type))) { error (invalid_conv_diag); return error_mark_node; } if (type == TREE_TYPE (expr)) return expr; ret = targetm.convert_to_type (type, expr); if (ret) return ret; STRIP_TYPE_NOPS (e); if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (expr)) && (TREE_CODE (TREE_TYPE (expr)) != COMPLEX_TYPE || TREE_CODE (e) == COMPLEX_EXPR)) return fold_convert_loc (loc, type, expr); if (TREE_CODE (TREE_TYPE (expr)) == ERROR_MARK) return error_mark_node; if (TREE_CODE (TREE_TYPE (expr)) == VOID_TYPE) { error ("void value not ignored as it ought to be"); return error_mark_node; } switch (code) { case VOID_TYPE: return fold_convert_loc (loc, type, e); case INTEGER_TYPE: case ENUMERAL_TYPE: if (flag_sanitize & SANITIZE_FLOAT_CAST && TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE && COMPLETE_TYPE_P (type) && current_function_decl != NULL_TREE && !lookup_attribute ("no_sanitize_undefined", DECL_ATTRIBUTES (current_function_decl))) { tree arg; if (in_late_binary_op) { expr = save_expr (expr); arg = expr; } else { expr = c_save_expr (expr); arg = c_fully_fold (expr, false, NULL); } tree check = ubsan_instrument_float_cast (loc, type, expr, arg); expr = fold_build1 (FIX_TRUNC_EXPR, type, expr); if (check == NULL) return expr; return fold_build2 (COMPOUND_EXPR, TREE_TYPE (expr), check, expr); } ret = convert_to_integer (type, e); goto maybe_fold; case BOOLEAN_TYPE: return fold_convert_loc (loc, type, c_objc_common_truthvalue_conversion (input_location, expr)); case POINTER_TYPE: case REFERENCE_TYPE: ret = convert_to_pointer (type, e); goto maybe_fold; case REAL_TYPE: ret = convert_to_real (type, e); goto maybe_fold; case FIXED_POINT_TYPE: ret = convert_to_fixed (type, e); goto maybe_fold; case COMPLEX_TYPE: /* If converting from COMPLEX_TYPE to a different COMPLEX_TYPE and e is not COMPLEX_EXPR, convert_to_complex uses save_expr, but for the C FE c_save_expr needs to be called instead. */ if (TREE_CODE (TREE_TYPE (e)) == COMPLEX_TYPE) { if (TREE_CODE (e) != COMPLEX_EXPR) { tree subtype = TREE_TYPE (type); tree elt_type = TREE_TYPE (TREE_TYPE (e)); if (in_late_binary_op) e = save_expr (e); else e = c_save_expr (e); ret = fold_build2_loc (loc, COMPLEX_EXPR, type, convert (subtype, fold_build1 (REALPART_EXPR, elt_type, e)), convert (subtype, fold_build1 (IMAGPART_EXPR, elt_type, e))); goto maybe_fold; } } ret = convert_to_complex (type, e); goto maybe_fold; case VECTOR_TYPE: ret = convert_to_vector (type, e); goto maybe_fold; case RECORD_TYPE: case UNION_TYPE: if (lang_hooks.types_compatible_p (type, TREE_TYPE (expr))) return e; break; default: break; maybe_fold: if (TREE_CODE (ret) != C_MAYBE_CONST_EXPR) ret = fold (ret); return ret; } error ("conversion to non-scalar type requested"); return error_mark_node; }
tree c_finish_omp_atomic (location_t loc, enum tree_code code, enum tree_code opcode, tree lhs, tree rhs, tree v, tree lhs1, tree rhs1, bool swapped, bool seq_cst) { tree x, type, addr; if (lhs == error_mark_node || rhs == error_mark_node || v == error_mark_node || lhs1 == error_mark_node || rhs1 == error_mark_node) return error_mark_node; /* ??? According to one reading of the OpenMP spec, complex type are supported, but there are no atomic stores for any architecture. But at least icc 9.0 doesn't support complex types here either. And lets not even talk about vector types... */ type = TREE_TYPE (lhs); if (!INTEGRAL_TYPE_P (type) && !POINTER_TYPE_P (type) && !SCALAR_FLOAT_TYPE_P (type)) { error_at (loc, "invalid expression type for %<#pragma omp atomic%>"); return error_mark_node; } /* ??? Validate that rhs does not overlap lhs. */ /* Take and save the address of the lhs. From then on we'll reference it via indirection. */ addr = build_unary_op (loc, ADDR_EXPR, lhs, 0); if (addr == error_mark_node) return error_mark_node; addr = save_expr (addr); if (TREE_CODE (addr) != SAVE_EXPR && (TREE_CODE (addr) != ADDR_EXPR || TREE_CODE (TREE_OPERAND (addr, 0)) != VAR_DECL)) { /* Make sure LHS is simple enough so that goa_lhs_expr_p can recognize it even after unsharing function body. */ tree var = create_tmp_var_raw (TREE_TYPE (addr), NULL); DECL_CONTEXT (var) = current_function_decl; addr = build4 (TARGET_EXPR, TREE_TYPE (addr), var, addr, NULL, NULL); } lhs = build_indirect_ref (loc, addr, RO_NULL); if (code == OMP_ATOMIC_READ) { x = build1 (OMP_ATOMIC_READ, type, addr); SET_EXPR_LOCATION (x, loc); OMP_ATOMIC_SEQ_CST (x) = seq_cst; return build_modify_expr (loc, v, NULL_TREE, NOP_EXPR, loc, x, NULL_TREE); return x; } /* There are lots of warnings, errors, and conversions that need to happen in the course of interpreting a statement. Use the normal mechanisms to do this, and then take it apart again. */ if (swapped) { rhs = build2_loc (loc, opcode, TREE_TYPE (lhs), rhs, lhs); opcode = NOP_EXPR; } x = build_modify_expr (loc, lhs, NULL_TREE, opcode, loc, rhs, NULL_TREE); if (x == error_mark_node) return error_mark_node; gcc_assert (TREE_CODE (x) == MODIFY_EXPR); rhs = TREE_OPERAND (x, 1); /* Punt the actual generation of atomic operations to common code. */ if (code == OMP_ATOMIC) type = void_type_node; x = build2 (code, type, addr, rhs); SET_EXPR_LOCATION (x, loc); OMP_ATOMIC_SEQ_CST (x) = seq_cst; /* Generally it is hard to prove lhs1 and lhs are the same memory location, just diagnose different variables. */ if (rhs1 && TREE_CODE (rhs1) == VAR_DECL && TREE_CODE (lhs) == VAR_DECL && rhs1 != lhs) { if (code == OMP_ATOMIC) error_at (loc, "%<#pragma omp atomic update%> uses two different variables for memory"); else error_at (loc, "%<#pragma omp atomic capture%> uses two different variables for memory"); return error_mark_node; } if (code != OMP_ATOMIC) { /* Generally it is hard to prove lhs1 and lhs are the same memory location, just diagnose different variables. */ if (lhs1 && TREE_CODE (lhs1) == VAR_DECL && TREE_CODE (lhs) == VAR_DECL) { if (lhs1 != lhs) { error_at (loc, "%<#pragma omp atomic capture%> uses two different variables for memory"); return error_mark_node; } } x = build_modify_expr (loc, v, NULL_TREE, NOP_EXPR, loc, x, NULL_TREE); if (rhs1 && rhs1 != lhs) { tree rhs1addr = build_unary_op (loc, ADDR_EXPR, rhs1, 0); if (rhs1addr == error_mark_node) return error_mark_node; x = omit_one_operand_loc (loc, type, x, rhs1addr); } if (lhs1 && lhs1 != lhs) { tree lhs1addr = build_unary_op (loc, ADDR_EXPR, lhs1, 0); if (lhs1addr == error_mark_node) return error_mark_node; if (code == OMP_ATOMIC_CAPTURE_OLD) x = omit_one_operand_loc (loc, type, x, lhs1addr); else { x = save_expr (x); x = omit_two_operands_loc (loc, type, x, x, lhs1addr); } } } else if (rhs1 && rhs1 != lhs) { tree rhs1addr = build_unary_op (loc, ADDR_EXPR, rhs1, 0); if (rhs1addr == error_mark_node) return error_mark_node; x = omit_one_operand_loc (loc, type, x, rhs1addr); } return x; }
static tree ubsan_maybe_instrument_reference_or_call (location_t loc, tree op, tree ptype, enum ubsan_null_ckind ckind) { if (!do_ubsan_in_current_function ()) return NULL_TREE; tree type = TREE_TYPE (ptype); tree orig_op = op; bool instrument = false; unsigned int mina = 0; if (flag_sanitize & SANITIZE_ALIGNMENT) { mina = min_align_of_type (type); if (mina <= 1) mina = 0; } while ((TREE_CODE (op) == NOP_EXPR || TREE_CODE (op) == NON_LVALUE_EXPR) && TREE_CODE (TREE_TYPE (op)) == POINTER_TYPE) op = TREE_OPERAND (op, 0); if (TREE_CODE (op) == NOP_EXPR && TREE_CODE (TREE_TYPE (op)) == REFERENCE_TYPE) { if (mina && mina > min_align_of_type (TREE_TYPE (TREE_TYPE (op)))) instrument = true; } else { if ((flag_sanitize & SANITIZE_NULL) && TREE_CODE (op) == ADDR_EXPR) { bool strict_overflow_p = false; /* tree_single_nonzero_warnv_p will not return true for non-weak non-automatic decls with -fno-delete-null-pointer-checks, which is disabled during -fsanitize=null. We don't want to instrument those, just weak vars though. */ int save_flag_delete_null_pointer_checks = flag_delete_null_pointer_checks; flag_delete_null_pointer_checks = 1; if (!tree_single_nonzero_warnv_p (op, &strict_overflow_p) || strict_overflow_p) instrument = true; flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks; } else if (flag_sanitize & SANITIZE_NULL) instrument = true; if (mina && mina > 1) { if (!POINTER_TYPE_P (TREE_TYPE (op)) || mina > get_pointer_alignment (op) / BITS_PER_UNIT) instrument = true; } } if (!instrument) return NULL_TREE; op = save_expr (orig_op); gcc_assert (POINTER_TYPE_P (ptype)); if (TREE_CODE (ptype) == REFERENCE_TYPE) ptype = build_pointer_type (TREE_TYPE (ptype)); tree kind = build_int_cst (ptype, ckind); tree align = build_int_cst (pointer_sized_int_node, mina); tree call = build_call_expr_internal_loc (loc, IFN_UBSAN_NULL, void_type_node, 3, op, kind, align); TREE_SIDE_EFFECTS (call) = 1; return fold_build2 (COMPOUND_EXPR, TREE_TYPE (op), call, op); }
tree ubsan_instrument_bounds (location_t loc, tree array, tree *index, bool ignore_off_by_one) { tree type = TREE_TYPE (array); tree domain = TYPE_DOMAIN (type); if (domain == NULL_TREE || TYPE_MAX_VALUE (domain) == NULL_TREE) return NULL_TREE; tree bound = TYPE_MAX_VALUE (domain); if (ignore_off_by_one) bound = fold_build2 (PLUS_EXPR, TREE_TYPE (bound), bound, build_int_cst (TREE_TYPE (bound), 1)); /* Detect flexible array members and suchlike, unless -fsanitize=bounds-strict. */ tree base = get_base_address (array); if ((flag_sanitize & SANITIZE_BOUNDS_STRICT) == 0 && TREE_CODE (array) == COMPONENT_REF && base && (INDIRECT_REF_P (base) || TREE_CODE (base) == MEM_REF)) { tree next = NULL_TREE; tree cref = array; /* Walk all structs/unions. */ while (TREE_CODE (cref) == COMPONENT_REF) { if (TREE_CODE (TREE_TYPE (TREE_OPERAND (cref, 0))) == RECORD_TYPE) for (next = DECL_CHAIN (TREE_OPERAND (cref, 1)); next && TREE_CODE (next) != FIELD_DECL; next = DECL_CHAIN (next)) ; if (next) /* Not a last element. Instrument it. */ break; /* Ok, this is the last field of the structure/union. But the aggregate containing the field must be the last field too, recursively. */ cref = TREE_OPERAND (cref, 0); } if (!next) /* Don't instrument this flexible array member-like array in non-strict -fsanitize=bounds mode. */ return NULL_TREE; } /* Don't emit instrumentation in the most common cases. */ tree idx = NULL_TREE; if (TREE_CODE (*index) == INTEGER_CST) idx = *index; else if (TREE_CODE (*index) == BIT_AND_EXPR && TREE_CODE (TREE_OPERAND (*index, 1)) == INTEGER_CST) idx = TREE_OPERAND (*index, 1); if (idx && TREE_CODE (bound) == INTEGER_CST && tree_int_cst_sgn (idx) >= 0 && tree_int_cst_le (idx, bound)) return NULL_TREE; *index = save_expr (*index); /* Create a "(T *) 0" tree node to describe the array type. */ tree zero_with_type = build_int_cst (build_pointer_type (type), 0); return build_call_expr_internal_loc (loc, IFN_UBSAN_BOUNDS, void_type_node, 3, zero_with_type, *index, bound); }
void do_jump (tree exp, rtx if_false_label, rtx if_true_label) { enum tree_code code = TREE_CODE (exp); rtx temp; int i; tree type; enum machine_mode mode; rtx drop_through_label = 0; switch (code) { case ERROR_MARK: break; case INTEGER_CST: temp = integer_zerop (exp) ? if_false_label : if_true_label; if (temp) emit_jump (temp); break; #if 0 /* This is not true with #pragma weak */ case ADDR_EXPR: /* The address of something can never be zero. */ if (if_true_label) emit_jump (if_true_label); break; #endif case NOP_EXPR: if (TREE_CODE (TREE_OPERAND (exp, 0)) == COMPONENT_REF || TREE_CODE (TREE_OPERAND (exp, 0)) == BIT_FIELD_REF || TREE_CODE (TREE_OPERAND (exp, 0)) == ARRAY_REF || TREE_CODE (TREE_OPERAND (exp, 0)) == ARRAY_RANGE_REF) goto normal; case CONVERT_EXPR: /* If we are narrowing the operand, we have to do the compare in the narrower mode. */ if ((TYPE_PRECISION (TREE_TYPE (exp)) < TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0))))) goto normal; case NON_LVALUE_EXPR: case ABS_EXPR: case NEGATE_EXPR: case LROTATE_EXPR: case RROTATE_EXPR: /* These cannot change zero->nonzero or vice versa. */ do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label); break; case BIT_AND_EXPR: /* fold_single_bit_test() converts (X & (1 << C)) into (X >> C) & 1. See if the former is preferred for jump tests and restore it if so. */ if (integer_onep (TREE_OPERAND (exp, 1))) { tree exp0 = TREE_OPERAND (exp, 0); rtx set_label, clr_label; /* Strip narrowing integral type conversions. */ while ((TREE_CODE (exp0) == NOP_EXPR || TREE_CODE (exp0) == CONVERT_EXPR || TREE_CODE (exp0) == NON_LVALUE_EXPR) && TREE_OPERAND (exp0, 0) != error_mark_node && TYPE_PRECISION (TREE_TYPE (exp0)) <= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp0, 0)))) exp0 = TREE_OPERAND (exp0, 0); /* "exp0 ^ 1" inverts the sense of the single bit test. */ if (TREE_CODE (exp0) == BIT_XOR_EXPR && integer_onep (TREE_OPERAND (exp0, 1))) { exp0 = TREE_OPERAND (exp0, 0); clr_label = if_true_label; set_label = if_false_label; } else { clr_label = if_false_label; set_label = if_true_label; } if (TREE_CODE (exp0) == RSHIFT_EXPR) { tree arg = TREE_OPERAND (exp0, 0); tree shift = TREE_OPERAND (exp0, 1); tree argtype = TREE_TYPE (arg); if (TREE_CODE (shift) == INTEGER_CST && compare_tree_int (shift, 0) >= 0 && compare_tree_int (shift, HOST_BITS_PER_WIDE_INT) < 0 && prefer_and_bit_test (TYPE_MODE (argtype), TREE_INT_CST_LOW (shift))) { HOST_WIDE_INT mask = (HOST_WIDE_INT) 1 << TREE_INT_CST_LOW (shift); do_jump (build2 (BIT_AND_EXPR, argtype, arg, build_int_cst_type (argtype, mask)), clr_label, set_label); break; } } } /* If we are AND'ing with a small constant, do this comparison in the smallest type that fits. If the machine doesn't have comparisons that small, it will be converted back to the wider comparison. This helps if we are testing the sign bit of a narrower object. combine can't do this for us because it can't know whether a ZERO_EXTRACT or a compare in a smaller mode exists, but we do. */ if (! SLOW_BYTE_ACCESS && TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST && TYPE_PRECISION (TREE_TYPE (exp)) <= HOST_BITS_PER_WIDE_INT && (i = tree_floor_log2 (TREE_OPERAND (exp, 1))) >= 0 && (mode = mode_for_size (i + 1, MODE_INT, 0)) != BLKmode && (type = lang_hooks.types.type_for_mode (mode, 1)) != 0 && TYPE_PRECISION (type) < TYPE_PRECISION (TREE_TYPE (exp)) && (cmp_optab->handlers[(int) TYPE_MODE (type)].insn_code != CODE_FOR_nothing)) { do_jump (convert (type, exp), if_false_label, if_true_label); break; } goto normal; case TRUTH_NOT_EXPR: do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label); break; case COND_EXPR: { rtx label1 = gen_label_rtx (); if (!if_true_label || !if_false_label) { drop_through_label = gen_label_rtx (); if (!if_true_label) if_true_label = drop_through_label; if (!if_false_label) if_false_label = drop_through_label; } do_pending_stack_adjust (); do_jump (TREE_OPERAND (exp, 0), label1, NULL_RTX); do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label); emit_label (label1); do_jump (TREE_OPERAND (exp, 2), if_false_label, if_true_label); break; } case TRUTH_ANDIF_EXPR: case TRUTH_ORIF_EXPR: case COMPOUND_EXPR: /* Lowered by gimplify.c. */ gcc_unreachable (); case COMPONENT_REF: case BIT_FIELD_REF: case ARRAY_REF: case ARRAY_RANGE_REF: { HOST_WIDE_INT bitsize, bitpos; int unsignedp; enum machine_mode mode; tree type; tree offset; int volatilep = 0; /* Get description of this reference. We don't actually care about the underlying object here. */ get_inner_reference (exp, &bitsize, &bitpos, &offset, &mode, &unsignedp, &volatilep, false); type = lang_hooks.types.type_for_size (bitsize, unsignedp); if (! SLOW_BYTE_ACCESS && type != 0 && bitsize >= 0 && TYPE_PRECISION (type) < TYPE_PRECISION (TREE_TYPE (exp)) && (cmp_optab->handlers[(int) TYPE_MODE (type)].insn_code != CODE_FOR_nothing)) { do_jump (convert (type, exp), if_false_label, if_true_label); break; } goto normal; } case EQ_EXPR: { tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0)); gcc_assert (GET_MODE_CLASS (TYPE_MODE (inner_type)) != MODE_COMPLEX_FLOAT); gcc_assert (GET_MODE_CLASS (TYPE_MODE (inner_type)) != MODE_COMPLEX_INT); if (integer_zerop (TREE_OPERAND (exp, 1))) do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label); else if (GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_INT && !can_compare_p (EQ, TYPE_MODE (inner_type), ccp_jump)) do_jump_by_parts_equality (exp, if_false_label, if_true_label); else do_compare_and_jump (exp, EQ, EQ, if_false_label, if_true_label); break; } case MINUS_EXPR: /* Nonzero iff operands of minus differ. */ exp = build2 (NE_EXPR, TREE_TYPE (exp), TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1)); /* FALLTHRU */ case NE_EXPR: { tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0)); gcc_assert (GET_MODE_CLASS (TYPE_MODE (inner_type)) != MODE_COMPLEX_FLOAT); gcc_assert (GET_MODE_CLASS (TYPE_MODE (inner_type)) != MODE_COMPLEX_INT); if (integer_zerop (TREE_OPERAND (exp, 1))) do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label); else if (GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_INT && !can_compare_p (NE, TYPE_MODE (inner_type), ccp_jump)) do_jump_by_parts_equality (exp, if_true_label, if_false_label); else do_compare_and_jump (exp, NE, NE, if_false_label, if_true_label); break; } case LT_EXPR: mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))); if (GET_MODE_CLASS (mode) == MODE_INT && ! can_compare_p (LT, mode, ccp_jump)) do_jump_by_parts_greater (exp, 1, if_false_label, if_true_label); else do_compare_and_jump (exp, LT, LTU, if_false_label, if_true_label); break; case LE_EXPR: mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))); if (GET_MODE_CLASS (mode) == MODE_INT && ! can_compare_p (LE, mode, ccp_jump)) do_jump_by_parts_greater (exp, 0, if_true_label, if_false_label); else do_compare_and_jump (exp, LE, LEU, if_false_label, if_true_label); break; case GT_EXPR: mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))); if (GET_MODE_CLASS (mode) == MODE_INT && ! can_compare_p (GT, mode, ccp_jump)) do_jump_by_parts_greater (exp, 0, if_false_label, if_true_label); else do_compare_and_jump (exp, GT, GTU, if_false_label, if_true_label); break; case GE_EXPR: mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))); if (GET_MODE_CLASS (mode) == MODE_INT && ! can_compare_p (GE, mode, ccp_jump)) do_jump_by_parts_greater (exp, 1, if_true_label, if_false_label); else do_compare_and_jump (exp, GE, GEU, if_false_label, if_true_label); break; case UNORDERED_EXPR: case ORDERED_EXPR: { enum rtx_code cmp, rcmp; int do_rev; if (code == UNORDERED_EXPR) cmp = UNORDERED, rcmp = ORDERED; else cmp = ORDERED, rcmp = UNORDERED; mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))); do_rev = 0; if (! can_compare_p (cmp, mode, ccp_jump) && (can_compare_p (rcmp, mode, ccp_jump) /* If the target doesn't provide either UNORDERED or ORDERED comparisons, canonicalize on UNORDERED for the library. */ || rcmp == UNORDERED)) do_rev = 1; if (! do_rev) do_compare_and_jump (exp, cmp, cmp, if_false_label, if_true_label); else do_compare_and_jump (exp, rcmp, rcmp, if_true_label, if_false_label); } break; { enum rtx_code rcode1; enum tree_code tcode1, tcode2; case UNLT_EXPR: rcode1 = UNLT; tcode1 = UNORDERED_EXPR; tcode2 = LT_EXPR; goto unordered_bcc; case UNLE_EXPR: rcode1 = UNLE; tcode1 = UNORDERED_EXPR; tcode2 = LE_EXPR; goto unordered_bcc; case UNGT_EXPR: rcode1 = UNGT; tcode1 = UNORDERED_EXPR; tcode2 = GT_EXPR; goto unordered_bcc; case UNGE_EXPR: rcode1 = UNGE; tcode1 = UNORDERED_EXPR; tcode2 = GE_EXPR; goto unordered_bcc; case UNEQ_EXPR: rcode1 = UNEQ; tcode1 = UNORDERED_EXPR; tcode2 = EQ_EXPR; goto unordered_bcc; case LTGT_EXPR: /* It is ok for LTGT_EXPR to trap when the result is unordered, so expand to (a < b) || (a > b). */ rcode1 = LTGT; tcode1 = LT_EXPR; tcode2 = GT_EXPR; goto unordered_bcc; unordered_bcc: mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))); if (can_compare_p (rcode1, mode, ccp_jump)) do_compare_and_jump (exp, rcode1, rcode1, if_false_label, if_true_label); else { tree op0 = save_expr (TREE_OPERAND (exp, 0)); tree op1 = save_expr (TREE_OPERAND (exp, 1)); tree cmp0, cmp1; /* If the target doesn't support combined unordered compares, decompose into two comparisons. */ if (if_true_label == 0) drop_through_label = if_true_label = gen_label_rtx (); cmp0 = fold_build2 (tcode1, TREE_TYPE (exp), op0, op1); cmp1 = fold_build2 (tcode2, TREE_TYPE (exp), op0, op1); do_jump (cmp0, 0, if_true_label); do_jump (cmp1, if_false_label, if_true_label); } } break; case TRUTH_AND_EXPR: /* High branch cost, expand as the bitwise AND of the conditions. Do the same if the RHS has side effects, because we're effectively turning a TRUTH_AND_EXPR into a TRUTH_ANDIF_EXPR. */ if (BRANCH_COST >= 4 || TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 1))) goto normal; if (if_false_label == NULL_RTX) { drop_through_label = gen_label_rtx (); do_jump (TREE_OPERAND (exp, 0), drop_through_label, NULL_RTX); do_jump (TREE_OPERAND (exp, 1), NULL_RTX, if_true_label); } else { do_jump (TREE_OPERAND (exp, 0), if_false_label, NULL_RTX); do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label); } break; case TRUTH_OR_EXPR: /* High branch cost, expand as the bitwise OR of the conditions. Do the same if the RHS has side effects, because we're effectively turning a TRUTH_OR_EXPR into a TRUTH_ORIF_EXPR. */ if (BRANCH_COST >= 4 || TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 1))) goto normal; if (if_true_label == NULL_RTX) { drop_through_label = gen_label_rtx (); do_jump (TREE_OPERAND (exp, 0), NULL_RTX, drop_through_label); do_jump (TREE_OPERAND (exp, 1), if_false_label, NULL_RTX); } else { do_jump (TREE_OPERAND (exp, 0), NULL_RTX, if_true_label); do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label); } break; /* Special case: __builtin_expect (<test>, 0) and __builtin_expect (<test>, 1) We need to do this here, so that <test> is not converted to a SCC operation on machines that use condition code registers and COMPARE like the PowerPC, and then the jump is done based on whether the SCC operation produced a 1 or 0. */ case CALL_EXPR: /* Check for a built-in function. */ { tree fndecl = get_callee_fndecl (exp); tree arglist = TREE_OPERAND (exp, 1); if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_EXPECT && arglist != NULL_TREE && TREE_CHAIN (arglist) != NULL_TREE) { rtx seq = expand_builtin_expect_jump (exp, if_false_label, if_true_label); if (seq != NULL_RTX) { emit_insn (seq); return; } } } /* Fall through and generate the normal code. */ default: normal: temp = expand_expr (exp, NULL_RTX, VOIDmode, 0); do_pending_stack_adjust (); if (GET_CODE (temp) == CONST_INT || (GET_CODE (temp) == CONST_DOUBLE && GET_MODE (temp) == VOIDmode) || GET_CODE (temp) == LABEL_REF) { rtx target = temp == const0_rtx ? if_false_label : if_true_label; if (target) emit_jump (target); } else if (GET_MODE_CLASS (GET_MODE (temp)) == MODE_INT && ! can_compare_p (NE, GET_MODE (temp), ccp_jump)) /* Note swapping the labels gives us not-equal. */ do_jump_by_parts_equality_rtx (temp, if_true_label, if_false_label); else { gcc_assert (GET_MODE (temp) != VOIDmode); /* The RTL optimizers prefer comparisons against pseudos. */ if (GET_CODE (temp) == SUBREG) { /* Compare promoted variables in their promoted mode. */ if (SUBREG_PROMOTED_VAR_P (temp) && REG_P (XEXP (temp, 0))) temp = XEXP (temp, 0); else temp = copy_to_reg (temp); } do_compare_rtx_and_jump (temp, CONST0_RTX (GET_MODE (temp)), NE, TYPE_UNSIGNED (TREE_TYPE (exp)), GET_MODE (temp), NULL_RTX, if_false_label, if_true_label); } } if (drop_through_label) { do_pending_stack_adjust (); emit_label (drop_through_label); } }
static tree gfc_trans_omp_atomic (gfc_code *code) { gfc_se lse; gfc_se rse; gfc_expr *expr2, *e; gfc_symbol *var; stmtblock_t block; tree lhsaddr, type, rhs, x; enum tree_code op = ERROR_MARK; bool var_on_left = false; code = code->block->next; gcc_assert (code->op == EXEC_ASSIGN); gcc_assert (code->next == NULL); var = code->expr1->symtree->n.sym; gfc_init_se (&lse, NULL); gfc_init_se (&rse, NULL); gfc_start_block (&block); gfc_conv_expr (&lse, code->expr1); gfc_add_block_to_block (&block, &lse.pre); type = TREE_TYPE (lse.expr); lhsaddr = gfc_build_addr_expr (NULL, lse.expr); expr2 = code->expr2; if (expr2->expr_type == EXPR_FUNCTION && expr2->value.function.isym->id == GFC_ISYM_CONVERSION) expr2 = expr2->value.function.actual->expr; if (expr2->expr_type == EXPR_OP) { gfc_expr *e; switch (expr2->value.op.op) { case INTRINSIC_PLUS: op = PLUS_EXPR; break; case INTRINSIC_TIMES: op = MULT_EXPR; break; case INTRINSIC_MINUS: op = MINUS_EXPR; break; case INTRINSIC_DIVIDE: if (expr2->ts.type == BT_INTEGER) op = TRUNC_DIV_EXPR; else op = RDIV_EXPR; break; case INTRINSIC_AND: op = TRUTH_ANDIF_EXPR; break; case INTRINSIC_OR: op = TRUTH_ORIF_EXPR; break; case INTRINSIC_EQV: op = EQ_EXPR; break; case INTRINSIC_NEQV: op = NE_EXPR; break; default: gcc_unreachable (); } e = expr2->value.op.op1; if (e->expr_type == EXPR_FUNCTION && e->value.function.isym->id == GFC_ISYM_CONVERSION) e = e->value.function.actual->expr; if (e->expr_type == EXPR_VARIABLE && e->symtree != NULL && e->symtree->n.sym == var) { expr2 = expr2->value.op.op2; var_on_left = true; } else { e = expr2->value.op.op2; if (e->expr_type == EXPR_FUNCTION && e->value.function.isym->id == GFC_ISYM_CONVERSION) e = e->value.function.actual->expr; gcc_assert (e->expr_type == EXPR_VARIABLE && e->symtree != NULL && e->symtree->n.sym == var); expr2 = expr2->value.op.op1; var_on_left = false; } gfc_conv_expr (&rse, expr2); gfc_add_block_to_block (&block, &rse.pre); } else { gcc_assert (expr2->expr_type == EXPR_FUNCTION); switch (expr2->value.function.isym->id) { case GFC_ISYM_MIN: op = MIN_EXPR; break; case GFC_ISYM_MAX: op = MAX_EXPR; break; case GFC_ISYM_IAND: op = BIT_AND_EXPR; break; case GFC_ISYM_IOR: op = BIT_IOR_EXPR; break; case GFC_ISYM_IEOR: op = BIT_XOR_EXPR; break; default: gcc_unreachable (); } e = expr2->value.function.actual->expr; gcc_assert (e->expr_type == EXPR_VARIABLE && e->symtree != NULL && e->symtree->n.sym == var); gfc_conv_expr (&rse, expr2->value.function.actual->next->expr); gfc_add_block_to_block (&block, &rse.pre); if (expr2->value.function.actual->next->next != NULL) { tree accum = gfc_create_var (TREE_TYPE (rse.expr), NULL); gfc_actual_arglist *arg; gfc_add_modify (&block, accum, rse.expr); for (arg = expr2->value.function.actual->next->next; arg; arg = arg->next) { gfc_init_block (&rse.pre); gfc_conv_expr (&rse, arg->expr); gfc_add_block_to_block (&block, &rse.pre); x = fold_build2 (op, TREE_TYPE (accum), accum, rse.expr); gfc_add_modify (&block, accum, x); } rse.expr = accum; } expr2 = expr2->value.function.actual->next->expr; } lhsaddr = save_expr (lhsaddr); rhs = gfc_evaluate_now (rse.expr, &block); x = convert (TREE_TYPE (rhs), build_fold_indirect_ref_loc (input_location, lhsaddr)); if (var_on_left) x = fold_build2 (op, TREE_TYPE (rhs), x, rhs); else x = fold_build2 (op, TREE_TYPE (rhs), rhs, x); if (TREE_CODE (TREE_TYPE (rhs)) == COMPLEX_TYPE && TREE_CODE (type) != COMPLEX_TYPE) x = fold_build1 (REALPART_EXPR, TREE_TYPE (TREE_TYPE (rhs)), x); x = build2_v (OMP_ATOMIC, lhsaddr, convert (type, x)); gfc_add_expr_to_block (&block, x); gfc_add_block_to_block (&block, &lse.pre); gfc_add_block_to_block (&block, &rse.pre); return gfc_finish_block (&block); }