static bool transform_ifelse (ext_cand *cand, rtx def_insn) { rtx set_insn = PATTERN (def_insn); rtx srcreg, dstreg, srcreg2; rtx map_srcreg, map_dstreg, map_srcreg2; rtx ifexpr; rtx cond; rtx new_set; gcc_assert (GET_CODE (set_insn) == SET); cond = XEXP (SET_SRC (set_insn), 0); dstreg = SET_DEST (set_insn); srcreg = XEXP (SET_SRC (set_insn), 1); srcreg2 = XEXP (SET_SRC (set_insn), 2); /* If the conditional move already has the right or wider mode, there is nothing to do. */ if (GET_MODE_SIZE (GET_MODE (dstreg)) >= GET_MODE_SIZE (cand->mode)) return true; map_srcreg = gen_rtx_REG (cand->mode, REGNO (srcreg)); map_srcreg2 = gen_rtx_REG (cand->mode, REGNO (srcreg2)); map_dstreg = gen_rtx_REG (cand->mode, REGNO (dstreg)); ifexpr = gen_rtx_IF_THEN_ELSE (cand->mode, cond, map_srcreg, map_srcreg2); new_set = gen_rtx_SET (VOIDmode, map_dstreg, ifexpr); if (validate_change (def_insn, &PATTERN (def_insn), new_set, true)) { if (dump_file) { fprintf (dump_file, "Mode of conditional move instruction extended:\n"); print_rtl_single (dump_file, def_insn); } return true; } return false; }
bool rtvec_all_equal_p (const_rtvec vec) { const_rtx first = RTVEC_ELT (vec, 0); /* Optimize the important special case of a vector of constants. The main use of this function is to detect whether every element of CONST_VECTOR is the same. */ switch (GET_CODE (first)) { CASE_CONST_UNIQUE: for (int i = 1, n = GET_NUM_ELEM (vec); i < n; ++i) if (first != RTVEC_ELT (vec, i)) return false; return true; default: for (int i = 1, n = GET_NUM_ELEM (vec); i < n; ++i) if (!rtx_equal_p (first, RTVEC_ELT (vec, i))) return false; return true; } }
static int record_stack_memrefs (rtx *xp, void *data) { rtx x = *xp; struct record_stack_memrefs_data *d = (struct record_stack_memrefs_data *) data; if (!x) return 0; switch (GET_CODE (x)) { case MEM: if (!reg_mentioned_p (stack_pointer_rtx, x)) return -1; /* We are not able to handle correctly all possible memrefs containing stack pointer, so this check is necessary. */ if (stack_memref_p (x)) { d->memlist = record_one_stack_memref (d->insn, xp, d->memlist); return -1; } return 1; case REG: /* ??? We want be able to handle non-memory stack pointer references later. For now just discard all insns referring to stack pointer outside mem expressions. We would probably want to teach validate_replace to simplify expressions first. We can't just compare with STACK_POINTER_RTX because the reference to the stack pointer might be in some other mode. In particular, an explicit clobber in an asm statement will result in a QImode clobber. */ if (REGNO (x) == STACK_POINTER_REGNUM) return 1; break; default: break; } return 0; }
static void record_effective_endpoints (void) { rtx next_insn; basic_block bb; rtx insn; for (insn = get_insns (); insn && GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) != NOTE_INSN_BASIC_BLOCK; insn = NEXT_INSN (insn)) continue; if (!insn) abort (); /* No basic blocks at all? */ if (PREV_INSN (insn)) cfg_layout_function_header = unlink_insn_chain (get_insns (), PREV_INSN (insn)); else cfg_layout_function_header = NULL_RTX; next_insn = get_insns (); FOR_EACH_BB (bb) { rtx end; if (PREV_INSN (BB_HEAD (bb)) && next_insn != BB_HEAD (bb)) bb->rbi->header = unlink_insn_chain (next_insn, PREV_INSN (BB_HEAD (bb))); end = skip_insns_after_block (bb); if (NEXT_INSN (BB_END (bb)) && BB_END (bb) != end) bb->rbi->footer = unlink_insn_chain (NEXT_INSN (BB_END (bb)), end); next_insn = NEXT_INSN (BB_END (bb)); } cfg_layout_function_footer = next_insn; if (cfg_layout_function_footer) cfg_layout_function_footer = unlink_insn_chain (cfg_layout_function_footer, get_last_insn ()); }
/* Callback function for arm_find_sub_rtx_with_code. DATA is safe to treat as a SEARCH_TERM, ST. This will hold a SEARCH_CODE. PATTERN is checked to see if it is an RTX with that code. If it is, write SEARCH_RESULT in ST and return 1. Otherwise, or if we have been passed a NULL_RTX return 0. If ST.FIND_ANY_SHIFT then we are interested in anything which can reasonably be described as a SHIFT RTX. */ static int arm_find_sub_rtx_with_search_term (rtx *pattern, void *data) { search_term *st = (search_term *) data; rtx_code pattern_code; int found = 0; gcc_assert (pattern); gcc_assert (st); /* Poorly formed patterns can really ruin our day. */ if (*pattern == NULL_RTX) return 0; pattern_code = GET_CODE (*pattern); if (st->find_any_shift) { unsigned i = 0; /* Left shifts might have been canonicalized to a MULT of some power of two. Make sure we catch them. */ if (arm_rtx_shift_left_p (*pattern)) found = 1; else for (i = 0; i < ARRAY_SIZE (shift_rtx_codes); i++) if (pattern_code == shift_rtx_codes[i]) found = 1; } if (pattern_code == st->search_code) found = 1; if (found) st->search_result = *pattern; return found; }
/* legitimate_address_p returns 1 if it recognizes an RTL expression "x" that is a valid memory address for an instruction. The MODE argument is the machine mode for the MEM expression that wants to use this address. */ int legitimate_address_p (enum machine_mode mode, rtx x, int strict) { rtx xfoo0, xfoo1; if (nonindexed_address_p (x, strict)) return 1; if (GET_CODE (x) != PLUS) return 0; /* Handle <address>[index] represented with index-sum outermost */ xfoo0 = XEXP (x, 0); xfoo1 = XEXP (x, 1); if (index_term_p (xfoo0, mode, strict) && nonindexed_address_p (xfoo1, strict)) return 1; if (index_term_p (xfoo1, mode, strict) && nonindexed_address_p (xfoo0, strict)) return 1; /* Handle offset(reg)[index] with offset added outermost */ if (indirectable_constant_address_p (xfoo0) && (BASE_REGISTER_P (xfoo1, strict) || reg_plus_index_p (xfoo1, mode, strict))) return 1; if (indirectable_constant_address_p (xfoo1) && (BASE_REGISTER_P (xfoo0, strict) || reg_plus_index_p (xfoo0, mode, strict))) return 1; return 0; }
static inline int ix86_comparison_operator_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) #line 1006 "../.././gcc/config/i386/predicates.md" { enum machine_mode inmode = GET_MODE (XEXP (op, 0)); enum rtx_code code = GET_CODE (op); if (inmode == CCFPmode || inmode == CCFPUmode) { enum rtx_code second_code, bypass_code; ix86_fp_comparison_codes (code, &bypass_code, &code, &second_code); return (bypass_code == UNKNOWN && second_code == UNKNOWN); } switch (code) { case EQ: case NE: return 1; case LT: case GE: if (inmode == CCmode || inmode == CCGCmode || inmode == CCGOCmode || inmode == CCNOmode) return 1; return 0; case LTU: case GTU: case LEU: case GEU: if (inmode == CCmode || inmode == CCCmode) return 1; return 0; case ORDERED: case UNORDERED: if (inmode == CCmode) return 1; return 0; case GT: case LE: if (inmode == CCmode || inmode == CCGCmode || inmode == CCNOmode) return 1; return 0; default: return 0; } }
static rtx conforming_compare (rtx_insn *insn) { rtx set, src, dest; set = single_set (insn); if (set == NULL) return NULL; src = SET_SRC (set); if (GET_CODE (src) != COMPARE) return NULL; dest = SET_DEST (set); if (!REG_P (dest) || REGNO (dest) != targetm.flags_regnum) return NULL; if (REG_P (XEXP (src, 0)) && (REG_P (XEXP (src, 1)) || CONSTANT_P (XEXP (src, 1)))) return src; return NULL; }
void print_rtl (FILE *outf, rtx rtx_first) { rtx tmp_rtx; outfile = outf; sawclose = 0; if (rtx_first == 0) { fputs (print_rtx_head, outf); fputs ("(nil)\n", outf); } else switch (GET_CODE (rtx_first)) { case INSN: case JUMP_INSN: case CALL_INSN: case NOTE: case CODE_LABEL: case BARRIER: for (tmp_rtx = rtx_first; tmp_rtx != 0; tmp_rtx = NEXT_INSN (tmp_rtx)) if (! flag_dump_unnumbered || !NOTE_P (tmp_rtx) || NOTE_LINE_NUMBER (tmp_rtx) < 0) { fputs (print_rtx_head, outfile); print_rtx (tmp_rtx); fprintf (outfile, "\n"); } break; default: fputs (print_rtx_head, outfile); print_rtx (rtx_first); } }
void symtab_make_decl_local (tree decl) { rtx rtl, symbol; if (TREE_CODE (decl) == VAR_DECL) DECL_COMMON (decl) = 0; else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); if (DECL_ONE_ONLY (decl) || DECL_COMDAT (decl)) { DECL_SECTION_NAME (decl) = 0; DECL_COMDAT (decl) = 0; } DECL_COMDAT_GROUP (decl) = 0; DECL_WEAK (decl) = 0; DECL_EXTERNAL (decl) = 0; DECL_VISIBILITY_SPECIFIED (decl) = 0; DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT; TREE_PUBLIC (decl) = 0; if (!DECL_RTL_SET_P (decl)) return; /* Update rtl flags. */ make_decl_rtl (decl); rtl = DECL_RTL (decl); if (!MEM_P (rtl)) return; symbol = XEXP (rtl, 0); if (GET_CODE (symbol) != SYMBOL_REF) return; SYMBOL_REF_WEAK (symbol) = DECL_WEAK (decl); }
static int uses_addressof (rtx x) { RTX_CODE code; int i, j; const char *fmt; if (x == NULL_RTX) return 0; code = GET_CODE (x); if (code == ADDRESSOF || x == current_function_internal_arg_pointer) return 1; if (code == MEM) return 0; /* Scan all subexpressions. */ fmt = GET_RTX_FORMAT (code); for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++) { if (*fmt == 'e') { if (uses_addressof (XEXP (x, i))) return 1; } else if (*fmt == 'E') { for (j = 0; j < XVECLEN (x, i); j++) if (uses_addressof (XVECEXP (x, i, j))) return 1; } } return 0; }
static struct df_link * get_defs (rtx insn, rtx reg, vec<rtx> *dest) { df_ref reg_info, *uses; struct df_link *ref_chain, *ref_link; reg_info = NULL; for (uses = DF_INSN_USES (insn); *uses; uses++) { reg_info = *uses; if (GET_CODE (DF_REF_REG (reg_info)) == SUBREG) return NULL; if (REGNO (DF_REF_REG (reg_info)) == REGNO (reg)) break; } gcc_assert (reg_info != NULL && uses != NULL); ref_chain = DF_REF_CHAIN (reg_info); for (ref_link = ref_chain; ref_link; ref_link = ref_link->next) { /* Problem getting some definition for this instruction. */ if (ref_link->ref == NULL) return NULL; if (DF_REF_INSN_INFO (ref_link->ref) == NULL) return NULL; } if (dest) for (ref_link = ref_chain; ref_link; ref_link = ref_link->next) dest->safe_push (DF_REF_INSN (ref_link->ref)); return ref_chain; }
void print_rtl (FILE *outf, const_rtx rtx_first) { const_rtx tmp_rtx; outfile = outf; sawclose = 0; if (rtx_first == 0) { fputs (print_rtx_head, outf); fputs ("(nil)\n", outf); } else switch (GET_CODE (rtx_first)) { case INSN: case JUMP_INSN: case CALL_INSN: case NOTE: case CODE_LABEL: case JUMP_TABLE_DATA: case BARRIER: for (tmp_rtx = rtx_first; tmp_rtx != 0; tmp_rtx = NEXT_INSN (tmp_rtx)) { fputs (print_rtx_head, outfile); print_rtx (tmp_rtx); fprintf (outfile, "\n"); } break; default: fputs (print_rtx_head, outfile); print_rtx (rtx_first); } }
void ubsan_expand_si_overflow_mul_check (gimple stmt) { rtx res, op0, op1; tree lhs, fn, arg0, arg1; rtx_code_label *done_label, *do_error; rtx target = NULL_RTX; lhs = gimple_call_lhs (stmt); arg0 = gimple_call_arg (stmt, 0); arg1 = gimple_call_arg (stmt, 1); done_label = gen_label_rtx (); do_error = gen_label_rtx (); do_pending_stack_adjust (); op0 = expand_normal (arg0); op1 = expand_normal (arg1); machine_mode mode = TYPE_MODE (TREE_TYPE (arg0)); if (lhs) target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE); enum insn_code icode = optab_handler (mulv4_optab, mode); if (icode != CODE_FOR_nothing) { struct expand_operand ops[4]; rtx_insn *last = get_last_insn (); res = gen_reg_rtx (mode); create_output_operand (&ops[0], res, mode); create_input_operand (&ops[1], op0, mode); create_input_operand (&ops[2], op1, mode); create_fixed_operand (&ops[3], do_error); if (maybe_expand_insn (icode, 4, ops)) { last = get_last_insn (); if (profile_status_for_fn (cfun) != PROFILE_ABSENT && JUMP_P (last) && any_condjump_p (last) && !find_reg_note (last, REG_BR_PROB, 0)) add_int_reg_note (last, REG_BR_PROB, PROB_VERY_UNLIKELY); emit_jump (done_label); } else { delete_insns_since (last); icode = CODE_FOR_nothing; } } if (icode == CODE_FOR_nothing) { struct separate_ops ops; machine_mode hmode = mode_for_size (GET_MODE_PRECISION (mode) / 2, MODE_INT, 1); ops.op0 = arg0; ops.op1 = arg1; ops.op2 = NULL_TREE; ops.location = gimple_location (stmt); if (GET_MODE_2XWIDER_MODE (mode) != VOIDmode && targetm.scalar_mode_supported_p (GET_MODE_2XWIDER_MODE (mode))) { machine_mode wmode = GET_MODE_2XWIDER_MODE (mode); ops.code = WIDEN_MULT_EXPR; ops.type = build_nonstandard_integer_type (GET_MODE_PRECISION (wmode), 0); res = expand_expr_real_2 (&ops, NULL_RTX, wmode, EXPAND_NORMAL); rtx hipart = expand_shift (RSHIFT_EXPR, wmode, res, GET_MODE_PRECISION (mode), NULL_RTX, 0); hipart = gen_lowpart (mode, hipart); res = gen_lowpart (mode, res); rtx signbit = expand_shift (RSHIFT_EXPR, mode, res, GET_MODE_PRECISION (mode) - 1, NULL_RTX, 0); /* RES is low half of the double width result, HIPART the high half. There was overflow if HIPART is different from RES < 0 ? -1 : 0. */ emit_cmp_and_jump_insns (signbit, hipart, EQ, NULL_RTX, mode, false, done_label, PROB_VERY_LIKELY); } else if (hmode != BLKmode && 2 * GET_MODE_PRECISION (hmode) == GET_MODE_PRECISION (mode)) { rtx_code_label *large_op0 = gen_label_rtx (); rtx_code_label *small_op0_large_op1 = gen_label_rtx (); rtx_code_label *one_small_one_large = gen_label_rtx (); rtx_code_label *both_ops_large = gen_label_rtx (); rtx_code_label *after_hipart_neg = gen_label_rtx (); rtx_code_label *after_lopart_neg = gen_label_rtx (); rtx_code_label *do_overflow = gen_label_rtx (); rtx_code_label *hipart_different = gen_label_rtx (); unsigned int hprec = GET_MODE_PRECISION (hmode); rtx hipart0 = expand_shift (RSHIFT_EXPR, mode, op0, hprec, NULL_RTX, 0); hipart0 = gen_lowpart (hmode, hipart0); rtx lopart0 = gen_lowpart (hmode, op0); rtx signbit0 = expand_shift (RSHIFT_EXPR, hmode, lopart0, hprec - 1, NULL_RTX, 0); rtx hipart1 = expand_shift (RSHIFT_EXPR, mode, op1, hprec, NULL_RTX, 0); hipart1 = gen_lowpart (hmode, hipart1); rtx lopart1 = gen_lowpart (hmode, op1); rtx signbit1 = expand_shift (RSHIFT_EXPR, hmode, lopart1, hprec - 1, NULL_RTX, 0); res = gen_reg_rtx (mode); /* True if op0 resp. op1 are known to be in the range of halfstype. */ bool op0_small_p = false; bool op1_small_p = false; /* True if op0 resp. op1 are known to have all zeros or all ones in the upper half of bits, but are not known to be op{0,1}_small_p. */ bool op0_medium_p = false; bool op1_medium_p = false; /* -1 if op{0,1} is known to be negative, 0 if it is known to be nonnegative, 1 if unknown. */ int op0_sign = 1; int op1_sign = 1; if (TREE_CODE (arg0) == SSA_NAME) { wide_int arg0_min, arg0_max; if (get_range_info (arg0, &arg0_min, &arg0_max) == VR_RANGE) { unsigned int mprec0 = wi::min_precision (arg0_min, SIGNED); unsigned int mprec1 = wi::min_precision (arg0_max, SIGNED); if (mprec0 <= hprec && mprec1 <= hprec) op0_small_p = true; else if (mprec0 <= hprec + 1 && mprec1 <= hprec + 1) op0_medium_p = true; if (!wi::neg_p (arg0_min, TYPE_SIGN (TREE_TYPE (arg0)))) op0_sign = 0; else if (wi::neg_p (arg0_max, TYPE_SIGN (TREE_TYPE (arg0)))) op0_sign = -1; } } if (TREE_CODE (arg1) == SSA_NAME) { wide_int arg1_min, arg1_max; if (get_range_info (arg1, &arg1_min, &arg1_max) == VR_RANGE) { unsigned int mprec0 = wi::min_precision (arg1_min, SIGNED); unsigned int mprec1 = wi::min_precision (arg1_max, SIGNED); if (mprec0 <= hprec && mprec1 <= hprec) op1_small_p = true; else if (mprec0 <= hprec + 1 && mprec1 <= hprec + 1) op1_medium_p = true; if (!wi::neg_p (arg1_min, TYPE_SIGN (TREE_TYPE (arg1)))) op1_sign = 0; else if (wi::neg_p (arg1_max, TYPE_SIGN (TREE_TYPE (arg1)))) op1_sign = -1; } } int smaller_sign = 1; int larger_sign = 1; if (op0_small_p) { smaller_sign = op0_sign; larger_sign = op1_sign; } else if (op1_small_p) { smaller_sign = op1_sign; larger_sign = op0_sign; } else if (op0_sign == op1_sign) { smaller_sign = op0_sign; larger_sign = op0_sign; } if (!op0_small_p) emit_cmp_and_jump_insns (signbit0, hipart0, NE, NULL_RTX, hmode, false, large_op0, PROB_UNLIKELY); if (!op1_small_p) emit_cmp_and_jump_insns (signbit1, hipart1, NE, NULL_RTX, hmode, false, small_op0_large_op1, PROB_UNLIKELY); /* If both op0 and op1 are sign extended from hmode to mode, the multiplication will never overflow. We can do just one hmode x hmode => mode widening multiplication. */ if (GET_CODE (lopart0) == SUBREG) { SUBREG_PROMOTED_VAR_P (lopart0) = 1; SUBREG_PROMOTED_SET (lopart0, 0); } if (GET_CODE (lopart1) == SUBREG) { SUBREG_PROMOTED_VAR_P (lopart1) = 1; SUBREG_PROMOTED_SET (lopart1, 0); } tree halfstype = build_nonstandard_integer_type (hprec, 0); ops.op0 = make_tree (halfstype, lopart0); ops.op1 = make_tree (halfstype, lopart1); ops.code = WIDEN_MULT_EXPR; ops.type = TREE_TYPE (arg0); rtx thisres = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL); emit_move_insn (res, thisres); emit_jump (done_label); emit_label (small_op0_large_op1); /* If op0 is sign extended from hmode to mode, but op1 is not, just swap the arguments and handle it as op1 sign extended, op0 not. */ rtx larger = gen_reg_rtx (mode); rtx hipart = gen_reg_rtx (hmode); rtx lopart = gen_reg_rtx (hmode); emit_move_insn (larger, op1); emit_move_insn (hipart, hipart1); emit_move_insn (lopart, lopart0); emit_jump (one_small_one_large); emit_label (large_op0); if (!op1_small_p) emit_cmp_and_jump_insns (signbit1, hipart1, NE, NULL_RTX, hmode, false, both_ops_large, PROB_UNLIKELY); /* If op1 is sign extended from hmode to mode, but op0 is not, prepare larger, hipart and lopart pseudos and handle it together with small_op0_large_op1. */ emit_move_insn (larger, op0); emit_move_insn (hipart, hipart0); emit_move_insn (lopart, lopart1); emit_label (one_small_one_large); /* lopart is the low part of the operand that is sign extended to mode, larger is the the other operand, hipart is the high part of larger and lopart0 and lopart1 are the low parts of both operands. We perform lopart0 * lopart1 and lopart * hipart widening multiplications. */ tree halfutype = build_nonstandard_integer_type (hprec, 1); ops.op0 = make_tree (halfutype, lopart0); ops.op1 = make_tree (halfutype, lopart1); rtx lo0xlo1 = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL); ops.op0 = make_tree (halfutype, lopart); ops.op1 = make_tree (halfutype, hipart); rtx loxhi = gen_reg_rtx (mode); rtx tem = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL); emit_move_insn (loxhi, tem); /* if (hipart < 0) loxhi -= lopart << (bitsize / 2); */ if (larger_sign == 0) emit_jump (after_hipart_neg); else if (larger_sign != -1) emit_cmp_and_jump_insns (hipart, const0_rtx, GE, NULL_RTX, hmode, false, after_hipart_neg, PROB_EVEN); tem = convert_modes (mode, hmode, lopart, 1); tem = expand_shift (LSHIFT_EXPR, mode, tem, hprec, NULL_RTX, 1); tem = expand_simple_binop (mode, MINUS, loxhi, tem, NULL_RTX, 1, OPTAB_DIRECT); emit_move_insn (loxhi, tem); emit_label (after_hipart_neg); /* if (lopart < 0) loxhi -= larger; */ if (smaller_sign == 0) emit_jump (after_lopart_neg); else if (smaller_sign != -1) emit_cmp_and_jump_insns (lopart, const0_rtx, GE, NULL_RTX, hmode, false, after_lopart_neg, PROB_EVEN); tem = expand_simple_binop (mode, MINUS, loxhi, larger, NULL_RTX, 1, OPTAB_DIRECT); emit_move_insn (loxhi, tem); emit_label (after_lopart_neg); /* loxhi += (uns) lo0xlo1 >> (bitsize / 2); */ tem = expand_shift (RSHIFT_EXPR, mode, lo0xlo1, hprec, NULL_RTX, 1); tem = expand_simple_binop (mode, PLUS, loxhi, tem, NULL_RTX, 1, OPTAB_DIRECT); emit_move_insn (loxhi, tem); /* if (loxhi >> (bitsize / 2) == (hmode) loxhi >> (bitsize / 2 - 1)) */ rtx hipartloxhi = expand_shift (RSHIFT_EXPR, mode, loxhi, hprec, NULL_RTX, 0); hipartloxhi = gen_lowpart (hmode, hipartloxhi); rtx lopartloxhi = gen_lowpart (hmode, loxhi); rtx signbitloxhi = expand_shift (RSHIFT_EXPR, hmode, lopartloxhi, hprec - 1, NULL_RTX, 0); emit_cmp_and_jump_insns (signbitloxhi, hipartloxhi, NE, NULL_RTX, hmode, false, do_overflow, PROB_VERY_UNLIKELY); /* res = (loxhi << (bitsize / 2)) | (hmode) lo0xlo1; */ rtx loxhishifted = expand_shift (LSHIFT_EXPR, mode, loxhi, hprec, NULL_RTX, 1); tem = convert_modes (mode, hmode, gen_lowpart (hmode, lo0xlo1), 1); tem = expand_simple_binop (mode, IOR, loxhishifted, tem, res, 1, OPTAB_DIRECT); if (tem != res) emit_move_insn (res, tem); emit_jump (done_label); emit_label (both_ops_large); /* If both operands are large (not sign extended from hmode), then perform the full multiplication which will be the result of the operation. The only cases which don't overflow are some cases where both hipart0 and highpart1 are 0 or -1. */ ops.code = MULT_EXPR; ops.op0 = make_tree (TREE_TYPE (arg0), op0); ops.op1 = make_tree (TREE_TYPE (arg0), op1); tem = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL); emit_move_insn (res, tem); if (!op0_medium_p) { tem = expand_simple_binop (hmode, PLUS, hipart0, const1_rtx, NULL_RTX, 1, OPTAB_DIRECT); emit_cmp_and_jump_insns (tem, const1_rtx, GTU, NULL_RTX, hmode, true, do_error, PROB_VERY_UNLIKELY); } if (!op1_medium_p) { tem = expand_simple_binop (hmode, PLUS, hipart1, const1_rtx, NULL_RTX, 1, OPTAB_DIRECT); emit_cmp_and_jump_insns (tem, const1_rtx, GTU, NULL_RTX, hmode, true, do_error, PROB_VERY_UNLIKELY); } /* At this point hipart{0,1} are both in [-1, 0]. If they are the same, overflow happened if res is negative, if they are different, overflow happened if res is positive. */ if (op0_sign != 1 && op1_sign != 1 && op0_sign != op1_sign) emit_jump (hipart_different); else if (op0_sign == 1 || op1_sign == 1) emit_cmp_and_jump_insns (hipart0, hipart1, NE, NULL_RTX, hmode, true, hipart_different, PROB_EVEN); emit_cmp_and_jump_insns (res, const0_rtx, LT, NULL_RTX, mode, false, do_error, PROB_VERY_UNLIKELY); emit_jump (done_label); emit_label (hipart_different); emit_cmp_and_jump_insns (res, const0_rtx, GE, NULL_RTX, mode, false, do_error, PROB_VERY_UNLIKELY); emit_jump (done_label); emit_label (do_overflow); /* Overflow, do full multiplication and fallthru into do_error. */ ops.op0 = make_tree (TREE_TYPE (arg0), op0); ops.op1 = make_tree (TREE_TYPE (arg0), op1); tem = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL); emit_move_insn (res, tem); } else { ops.code = MULT_EXPR; ops.type = TREE_TYPE (arg0); res = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL); emit_jump (done_label); } } emit_label (do_error); /* Expand the ubsan builtin call. */ push_temp_slots (); fn = ubsan_build_overflow_builtin (MULT_EXPR, gimple_location (stmt), TREE_TYPE (arg0), arg0, arg1); expand_normal (fn); pop_temp_slots (); do_pending_stack_adjust (); /* We're done. */ emit_label (done_label); if (lhs) emit_move_insn (target, res); }
int nds32_address_cost_impl (rtx address, enum machine_mode mode ATTRIBUTE_UNUSED, addr_space_t as ATTRIBUTE_UNUSED, bool speed) { rtx plus0, plus1; enum rtx_code code; code = GET_CODE (address); /* According to 'speed', goto suitable cost model section. */ if (speed) goto performance_cost; else goto size_cost; performance_cost: /* This is section for performance cost model. */ /* FALLTHRU, currently we use same cost model as size_cost. */ size_cost: /* This is section for size cost model. */ switch (code) { case POST_MODIFY: case POST_INC: case POST_DEC: /* We encourage that rtx contains POST_MODIFY/POST_INC/POST_DEC behavior. */ return 0; case SYMBOL_REF: /* We can have gp-relative load/store for symbol_ref. Have it 4-byte cost. */ return COSTS_N_INSNS (1); case CONST: /* It is supposed to be the pattern (const (plus symbol_ref const_int)). Have it 4-byte cost. */ return COSTS_N_INSNS (1); case REG: /* Simply return 4-byte costs. */ return COSTS_N_INSNS (1); case PLUS: /* We do not need to check if the address is a legitimate address, because this hook is never called with an invalid address. But we better check the range of const_int value for cost, if it exists. */ plus0 = XEXP (address, 0); plus1 = XEXP (address, 1); if (REG_P (plus0) && CONST_INT_P (plus1)) { /* If it is possible to be lwi333/swi333 form, make it 2-byte cost. */ if (satisfies_constraint_Iu05 (plus1)) return (COSTS_N_INSNS (1) - 2); else return COSTS_N_INSNS (1); } /* For other 'plus' situation, make it cost 4-byte. */ return COSTS_N_INSNS (1); default: break; } return COSTS_N_INSNS (4); }
static bool copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd) { bool anything_changed = false; rtx insn; for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn)) { int n_ops, i, alt, predicated; bool is_asm, any_replacements; rtx set; bool replaced[MAX_RECOG_OPERANDS]; bool changed = false; struct kill_set_value_data ksvd; if (!NONDEBUG_INSN_P (insn)) { if (DEBUG_INSN_P (insn)) { rtx loc = INSN_VAR_LOCATION_LOC (insn); if (!VAR_LOC_UNKNOWN_P (loc)) replace_oldest_value_addr (&INSN_VAR_LOCATION_LOC (insn), ALL_REGS, GET_MODE (loc), ADDR_SPACE_GENERIC, insn, vd); } if (insn == BB_END (bb)) break; else continue; } set = single_set (insn); extract_insn (insn); if (! constrain_operands (1)) fatal_insn_not_found (insn); preprocess_constraints (); alt = which_alternative; n_ops = recog_data.n_operands; is_asm = asm_noperands (PATTERN (insn)) >= 0; /* Simplify the code below by rewriting things to reflect matching constraints. Also promote OP_OUT to OP_INOUT in predicated instructions. */ predicated = GET_CODE (PATTERN (insn)) == COND_EXEC; for (i = 0; i < n_ops; ++i) { int matches = recog_op_alt[i][alt].matches; if (matches >= 0) recog_op_alt[i][alt].cl = recog_op_alt[matches][alt].cl; if (matches >= 0 || recog_op_alt[i][alt].matched >= 0 || (predicated && recog_data.operand_type[i] == OP_OUT)) recog_data.operand_type[i] = OP_INOUT; } /* Apply changes to earlier DEBUG_INSNs if possible. */ if (vd->n_debug_insn_changes) note_uses (&PATTERN (insn), cprop_find_used_regs, vd); /* For each earlyclobber operand, zap the value data. */ for (i = 0; i < n_ops; i++) if (recog_op_alt[i][alt].earlyclobber) kill_value (recog_data.operand[i], vd); /* Within asms, a clobber cannot overlap inputs or outputs. I wouldn't think this were true for regular insns, but scan_rtx treats them like that... */ note_stores (PATTERN (insn), kill_clobbered_value, vd); /* Kill all auto-incremented values. */ /* ??? REG_INC is useless, since stack pushes aren't done that way. */ for_each_rtx (&PATTERN (insn), kill_autoinc_value, vd); /* Kill all early-clobbered operands. */ for (i = 0; i < n_ops; i++) if (recog_op_alt[i][alt].earlyclobber) kill_value (recog_data.operand[i], vd); /* Special-case plain move instructions, since we may well be able to do the move from a different register class. */ if (set && REG_P (SET_SRC (set))) { rtx src = SET_SRC (set); unsigned int regno = REGNO (src); enum machine_mode mode = GET_MODE (src); unsigned int i; rtx new_rtx; /* If we are accessing SRC in some mode other that what we set it in, make sure that the replacement is valid. */ if (mode != vd->e[regno].mode) { if (hard_regno_nregs[regno][mode] > hard_regno_nregs[regno][vd->e[regno].mode]) goto no_move_special_case; /* And likewise, if we are narrowing on big endian the transformation is also invalid. */ if (hard_regno_nregs[regno][mode] < hard_regno_nregs[regno][vd->e[regno].mode] && (GET_MODE_SIZE (vd->e[regno].mode) > UNITS_PER_WORD ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN)) goto no_move_special_case; } /* If the destination is also a register, try to find a source register in the same class. */ if (REG_P (SET_DEST (set))) { new_rtx = find_oldest_value_reg (REGNO_REG_CLASS (regno), src, vd); if (new_rtx && validate_change (insn, &SET_SRC (set), new_rtx, 0)) { if (dump_file) fprintf (dump_file, "insn %u: replaced reg %u with %u\n", INSN_UID (insn), regno, REGNO (new_rtx)); changed = true; goto did_replacement; } /* We need to re-extract as validate_change clobbers recog_data. */ extract_insn (insn); if (! constrain_operands (1)) fatal_insn_not_found (insn); preprocess_constraints (); } /* Otherwise, try all valid registers and see if its valid. */ for (i = vd->e[regno].oldest_regno; i != regno; i = vd->e[i].next_regno) { new_rtx = maybe_mode_change (vd->e[i].mode, vd->e[regno].mode, mode, i, regno); if (new_rtx != NULL_RTX) { if (validate_change (insn, &SET_SRC (set), new_rtx, 0)) { ORIGINAL_REGNO (new_rtx) = ORIGINAL_REGNO (src); REG_ATTRS (new_rtx) = REG_ATTRS (src); REG_POINTER (new_rtx) = REG_POINTER (src); if (dump_file) fprintf (dump_file, "insn %u: replaced reg %u with %u\n", INSN_UID (insn), regno, REGNO (new_rtx)); changed = true; goto did_replacement; } /* We need to re-extract as validate_change clobbers recog_data. */ extract_insn (insn); if (! constrain_operands (1)) fatal_insn_not_found (insn); preprocess_constraints (); } } } no_move_special_case: any_replacements = false; /* For each input operand, replace a hard register with the eldest live copy that's in an appropriate register class. */ for (i = 0; i < n_ops; i++) { replaced[i] = false; /* Don't scan match_operand here, since we've no reg class information to pass down. Any operands that we could substitute in will be represented elsewhere. */ if (recog_data.constraints[i][0] == '\0') continue; /* Don't replace in asms intentionally referencing hard regs. */ if (is_asm && REG_P (recog_data.operand[i]) && (REGNO (recog_data.operand[i]) == ORIGINAL_REGNO (recog_data.operand[i]))) continue; if (recog_data.operand_type[i] == OP_IN) { if (recog_op_alt[i][alt].is_address) replaced[i] = replace_oldest_value_addr (recog_data.operand_loc[i], recog_op_alt[i][alt].cl, VOIDmode, ADDR_SPACE_GENERIC, insn, vd); else if (REG_P (recog_data.operand[i])) replaced[i] = replace_oldest_value_reg (recog_data.operand_loc[i], recog_op_alt[i][alt].cl, insn, vd); else if (MEM_P (recog_data.operand[i])) replaced[i] = replace_oldest_value_mem (recog_data.operand[i], insn, vd); } else if (MEM_P (recog_data.operand[i])) replaced[i] = replace_oldest_value_mem (recog_data.operand[i], insn, vd); /* If we performed any replacement, update match_dups. */ if (replaced[i]) { int j; rtx new_rtx; new_rtx = *recog_data.operand_loc[i]; recog_data.operand[i] = new_rtx; for (j = 0; j < recog_data.n_dups; j++) if (recog_data.dup_num[j] == i) validate_unshare_change (insn, recog_data.dup_loc[j], new_rtx, 1); any_replacements = true; } } if (any_replacements) { if (! apply_change_group ()) { for (i = 0; i < n_ops; i++) if (replaced[i]) { rtx old = *recog_data.operand_loc[i]; recog_data.operand[i] = old; } if (dump_file) fprintf (dump_file, "insn %u: reg replacements not verified\n", INSN_UID (insn)); } else changed = true; } did_replacement: if (changed) { anything_changed = true; /* If something changed, perhaps further changes to earlier DEBUG_INSNs can be applied. */ if (vd->n_debug_insn_changes) note_uses (&PATTERN (insn), cprop_find_used_regs, vd); } ksvd.vd = vd; ksvd.ignore_set_reg = NULL_RTX; /* Clobber call-clobbered registers. */ if (CALL_P (insn)) { unsigned int set_regno = INVALID_REGNUM; unsigned int set_nregs = 0; unsigned int regno; rtx exp; hard_reg_set_iterator hrsi; for (exp = CALL_INSN_FUNCTION_USAGE (insn); exp; exp = XEXP (exp, 1)) { rtx x = XEXP (exp, 0); if (GET_CODE (x) == SET) { rtx dest = SET_DEST (x); kill_value (dest, vd); set_value_regno (REGNO (dest), GET_MODE (dest), vd); copy_value (dest, SET_SRC (x), vd); ksvd.ignore_set_reg = dest; set_regno = REGNO (dest); set_nregs = hard_regno_nregs[set_regno][GET_MODE (dest)]; break; } } EXECUTE_IF_SET_IN_HARD_REG_SET (regs_invalidated_by_call, 0, regno, hrsi) if (regno < set_regno || regno >= set_regno + set_nregs) kill_value_regno (regno, 1, vd); /* If SET was seen in CALL_INSN_FUNCTION_USAGE, and SET_SRC of the SET isn't in regs_invalidated_by_call hard reg set, but instead among CLOBBERs on the CALL_INSN, we could wrongly assume the value in it is still live. */ if (ksvd.ignore_set_reg) note_stores (PATTERN (insn), kill_clobbered_value, vd); } /* Notice stores. */ note_stores (PATTERN (insn), kill_set_value, &ksvd); /* Notice copies. */ if (set && REG_P (SET_DEST (set)) && REG_P (SET_SRC (set))) copy_value (SET_DEST (set), SET_SRC (set), vd); if (insn == BB_END (bb)) break; } return anything_changed; }
static bool replace_oldest_value_addr (rtx *loc, enum reg_class cl, enum machine_mode mode, addr_space_t as, rtx insn, struct value_data *vd) { rtx x = *loc; RTX_CODE code = GET_CODE (x); const char *fmt; int i, j; bool changed = false; switch (code) { case PLUS: if (DEBUG_INSN_P (insn)) break; { rtx orig_op0 = XEXP (x, 0); rtx orig_op1 = XEXP (x, 1); RTX_CODE code0 = GET_CODE (orig_op0); RTX_CODE code1 = GET_CODE (orig_op1); rtx op0 = orig_op0; rtx op1 = orig_op1; rtx *locI = NULL; rtx *locB = NULL; enum rtx_code index_code = SCRATCH; if (GET_CODE (op0) == SUBREG) { op0 = SUBREG_REG (op0); code0 = GET_CODE (op0); } if (GET_CODE (op1) == SUBREG) { op1 = SUBREG_REG (op1); code1 = GET_CODE (op1); } if (code0 == MULT || code0 == SIGN_EXTEND || code0 == TRUNCATE || code0 == ZERO_EXTEND || code1 == MEM) { locI = &XEXP (x, 0); locB = &XEXP (x, 1); index_code = GET_CODE (*locI); } else if (code1 == MULT || code1 == SIGN_EXTEND || code1 == TRUNCATE || code1 == ZERO_EXTEND || code0 == MEM) { locI = &XEXP (x, 1); locB = &XEXP (x, 0); index_code = GET_CODE (*locI); } else if (code0 == CONST_INT || code0 == CONST || code0 == SYMBOL_REF || code0 == LABEL_REF) { locB = &XEXP (x, 1); index_code = GET_CODE (XEXP (x, 0)); } else if (code1 == CONST_INT || code1 == CONST || code1 == SYMBOL_REF || code1 == LABEL_REF) { locB = &XEXP (x, 0); index_code = GET_CODE (XEXP (x, 1)); } else if (code0 == REG && code1 == REG) { int index_op; unsigned regno0 = REGNO (op0), regno1 = REGNO (op1); if (REGNO_OK_FOR_INDEX_P (regno1) && regno_ok_for_base_p (regno0, mode, as, PLUS, REG)) index_op = 1; else if (REGNO_OK_FOR_INDEX_P (regno0) && regno_ok_for_base_p (regno1, mode, as, PLUS, REG)) index_op = 0; else if (regno_ok_for_base_p (regno0, mode, as, PLUS, REG) || REGNO_OK_FOR_INDEX_P (regno1)) index_op = 1; else if (regno_ok_for_base_p (regno1, mode, as, PLUS, REG)) index_op = 0; else index_op = 1; locI = &XEXP (x, index_op); locB = &XEXP (x, !index_op); index_code = GET_CODE (*locI); } else if (code0 == REG) { locI = &XEXP (x, 0); locB = &XEXP (x, 1); index_code = GET_CODE (*locI); } else if (code1 == REG) { locI = &XEXP (x, 1); locB = &XEXP (x, 0); index_code = GET_CODE (*locI); } if (locI) changed |= replace_oldest_value_addr (locI, INDEX_REG_CLASS, mode, as, insn, vd); if (locB) changed |= replace_oldest_value_addr (locB, base_reg_class (mode, as, PLUS, index_code), mode, as, insn, vd); return changed; } case POST_INC: case POST_DEC: case POST_MODIFY: case PRE_INC: case PRE_DEC: case PRE_MODIFY: return false; case MEM: return replace_oldest_value_mem (x, insn, vd); case REG: return replace_oldest_value_reg (loc, cl, insn, vd); default: break; } fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'e') changed |= replace_oldest_value_addr (&XEXP (x, i), cl, mode, as, insn, vd); else if (fmt[i] == 'E') for (j = XVECLEN (x, i) - 1; j >= 0; j--) changed |= replace_oldest_value_addr (&XVECEXP (x, i, j), cl, mode, as, insn, vd); } return changed; }
static unsigned int rest_of_handle_simplify_got (void) { df_ref ref; rtx use = NULL_RTX; int i, n_symbol, n_access = 0; struct got_access_info* got_accesses; htab_t var_table = htab_create (VAR_TABLE_SIZE, htab_hash_pointer, htab_eq_pointer, NULL); rtx pic_reg = targetm.got_access.get_pic_reg (); gcc_assert (pic_reg); ref = DF_REG_USE_CHAIN (REGNO (pic_reg)); got_accesses = XNEWVEC(struct got_access_info, DF_REG_USE_COUNT (REGNO (pic_reg))); /* Check if all uses of pic_reg are loading global address through the default method. */ while (ref) { rtx_insn* insn = DF_REF_INSN (ref); /* Check for the special USE insn, it is not a real usage of pic_reg. */ if (GET_CODE (PATTERN (insn)) == USE) use = insn; else { /* If an insn both set and use pic_reg, it is in the process of constructing the value of pic_reg. We should also ignore it. */ rtx set = single_set (insn); if (!(set && SET_DEST (set) == pic_reg)) { rtx offset_reg; rtx offset_insn; rtx symbol = targetm.got_access.loaded_global_var (insn, &offset_reg, &offset_insn); if (symbol) { rtx* slot = (rtx*) htab_find_slot (var_table, symbol, INSERT); if (*slot == HTAB_EMPTY_ENTRY) *slot = symbol; gcc_assert (set); got_accesses[n_access].symbol = symbol; got_accesses[n_access].offset_reg = offset_reg; got_accesses[n_access].address_reg = SET_DEST (set); got_accesses[n_access].load_insn = insn; got_accesses[n_access].offset_insn = offset_insn; n_access++; } else { /* This insn doesn't load a global address, but it has other unexpected usage of pic_reg, give up. */ free (got_accesses); htab_delete (var_table); return 0; } } } ref = DF_REF_NEXT_REG(ref); } /* Check if we can simplify it. */ n_symbol = htab_elements (var_table); gcc_assert (n_symbol <= n_access); if (!targetm.got_access.can_simplify_got_access (n_symbol, n_access)) { free (got_accesses); htab_delete (var_table); return 0; } /* Rewrite the global address loading insns. */ for (i=0; i<n_access; i++) targetm.got_access.load_global_address (got_accesses[i].symbol, got_accesses[i].offset_reg, got_accesses[i].address_reg, got_accesses[i].load_insn, got_accesses[i].offset_insn); /* Since there is no usage of pic_reg now, we can remove it. */ if (use) remove_insn (use); targetm.got_access.clear_pic_reg (); free (got_accesses); htab_delete (var_table); return 0; }
/* Recursive hash function for RTL X. */ static hashval_t rtx_hash (rtx x) { int i, j; enum rtx_code code; const char *fmt; hashval_t val = 0; if (x == 0) return val; code = GET_CODE (x); val += (int) code + 4095; /* Some RTL can be compared nonrecursively. */ switch (code) { case REG: return val + REGNO (x); case LABEL_REF: return iterative_hash_object (XEXP (x, 0), val); case SYMBOL_REF: return iterative_hash_object (XSTR (x, 0), val); case SCRATCH: case CONST_DOUBLE: case CONST_INT: case CONST_VECTOR: return val; default: break; } /* Hash the elements. */ fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { switch (fmt[i]) { case 'w': val += XWINT (x, i); break; case 'n': case 'i': val += XINT (x, i); break; case 'V': case 'E': val += XVECLEN (x, i); for (j = 0; j < XVECLEN (x, i); j++) val += rtx_hash (XVECEXP (x, i, j)); break; case 'e': val += rtx_hash (XEXP (x, i)); break; case 'S': case 's': val += htab_hash_string (XSTR (x, i)); break; case 'u': case '0': case 't': break; /* It is believed that rtx's at this level will never contain anything but integers and other rtx's, except for within LABEL_REFs and SYMBOL_REFs. */ default: abort (); } } return val; }
static void gen_int_relational (enum rtx_code code, rtx result, rtx cmp0, rtx cmp1, rtx destination) { machine_mode mode; int branch_p; mode = GET_MODE (cmp0); if (mode == VOIDmode) mode = GET_MODE (cmp1); /* Is this a branch or compare. */ branch_p = (destination != 0); /* Instruction set doesn't support LE or LT, so swap operands and use GE, GT. */ switch (code) { case LE: case LT: case LEU: case LTU: { rtx temp; code = swap_condition (code); temp = cmp0; cmp0 = cmp1; cmp1 = temp; break; } default: break; } if (branch_p) { rtx insn, cond, label; /* Operands must be in registers. */ if (!register_operand (cmp0, mode)) cmp0 = force_reg (mode, cmp0); if (!register_operand (cmp1, mode)) cmp1 = force_reg (mode, cmp1); /* Generate conditional branch instruction. */ cond = gen_rtx_fmt_ee (code, mode, cmp0, cmp1); label = gen_rtx_LABEL_REF (VOIDmode, destination); insn = gen_rtx_SET (pc_rtx, gen_rtx_IF_THEN_ELSE (VOIDmode, cond, label, pc_rtx)); emit_jump_insn (insn); } else { /* We can't have const_ints in cmp0, other than 0. */ if ((GET_CODE (cmp0) == CONST_INT) && (INTVAL (cmp0) != 0)) cmp0 = force_reg (mode, cmp0); /* If the comparison is against an int not in legal range move it into a register. */ if (GET_CODE (cmp1) == CONST_INT) { switch (code) { case EQ: case NE: case LE: case LT: case GE: case GT: if (!satisfies_constraint_K (cmp1)) cmp1 = force_reg (mode, cmp1); break; case LEU: case LTU: case GEU: case GTU: if (!satisfies_constraint_L (cmp1)) cmp1 = force_reg (mode, cmp1); break; default: gcc_unreachable (); } } /* Generate compare instruction. */ emit_move_insn (result, gen_rtx_fmt_ee (code, mode, cmp0, cmp1)); } }
static basic_block create_pre_exit (int n_entities, int *entity_map, const int *num_modes) { edge eg; edge_iterator ei; basic_block pre_exit; /* The only non-call predecessor at this stage is a block with a fallthrough edge; there can be at most one, but there could be none at all, e.g. when exit is called. */ pre_exit = 0; FOR_EACH_EDGE (eg, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) if (eg->flags & EDGE_FALLTHRU) { basic_block src_bb = eg->src; rtx_insn *last_insn; rtx ret_reg; gcc_assert (!pre_exit); /* If this function returns a value at the end, we have to insert the final mode switch before the return value copy to its hard register. */ if (EDGE_COUNT (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) == 1 && NONJUMP_INSN_P ((last_insn = BB_END (src_bb))) && GET_CODE (PATTERN (last_insn)) == USE && GET_CODE ((ret_reg = XEXP (PATTERN (last_insn), 0))) == REG) { int ret_start = REGNO (ret_reg); int nregs = hard_regno_nregs[ret_start][GET_MODE (ret_reg)]; int ret_end = ret_start + nregs; bool short_block = false; bool multi_reg_return = false; bool forced_late_switch = false; rtx_insn *before_return_copy; do { rtx_insn *return_copy = PREV_INSN (last_insn); rtx return_copy_pat, copy_reg; int copy_start, copy_num; int j; if (NONDEBUG_INSN_P (return_copy)) { /* When using SJLJ exceptions, the call to the unregister function is inserted between the clobber of the return value and the copy. We do not want to split the block before this or any other call; if we have not found the copy yet, the copy must have been deleted. */ if (CALL_P (return_copy)) { short_block = true; break; } return_copy_pat = PATTERN (return_copy); switch (GET_CODE (return_copy_pat)) { case USE: /* Skip USEs of multiple return registers. __builtin_apply pattern is also handled here. */ if (GET_CODE (XEXP (return_copy_pat, 0)) == REG && (targetm.calls.function_value_regno_p (REGNO (XEXP (return_copy_pat, 0))))) { multi_reg_return = true; last_insn = return_copy; continue; } break; case ASM_OPERANDS: /* Skip barrier insns. */ if (!MEM_VOLATILE_P (return_copy_pat)) break; /* Fall through. */ case ASM_INPUT: case UNSPEC_VOLATILE: last_insn = return_copy; continue; default: break; } /* If the return register is not (in its entirety) likely spilled, the return copy might be partially or completely optimized away. */ return_copy_pat = single_set (return_copy); if (!return_copy_pat) { return_copy_pat = PATTERN (return_copy); if (GET_CODE (return_copy_pat) != CLOBBER) break; else if (!optimize) { /* This might be (clobber (reg [<result>])) when not optimizing. Then check if the previous insn is the clobber for the return register. */ copy_reg = SET_DEST (return_copy_pat); if (GET_CODE (copy_reg) == REG && !HARD_REGISTER_NUM_P (REGNO (copy_reg))) { if (INSN_P (PREV_INSN (return_copy))) { return_copy = PREV_INSN (return_copy); return_copy_pat = PATTERN (return_copy); if (GET_CODE (return_copy_pat) != CLOBBER) break; } } } } copy_reg = SET_DEST (return_copy_pat); if (GET_CODE (copy_reg) == REG) copy_start = REGNO (copy_reg); else if (GET_CODE (copy_reg) == SUBREG && GET_CODE (SUBREG_REG (copy_reg)) == REG) copy_start = REGNO (SUBREG_REG (copy_reg)); else { /* When control reaches end of non-void function, there are no return copy insns at all. This avoids an ice on that invalid function. */ if (ret_start + nregs == ret_end) short_block = true; break; } if (!targetm.calls.function_value_regno_p (copy_start)) copy_num = 0; else copy_num = hard_regno_nregs[copy_start][GET_MODE (copy_reg)]; /* If the return register is not likely spilled, - as is the case for floating point on SH4 - then it might be set by an arithmetic operation that needs a different mode than the exit block. */ for (j = n_entities - 1; j >= 0; j--) { int e = entity_map[j]; int mode = targetm.mode_switching.needed (e, return_copy); if (mode != num_modes[e] && mode != targetm.mode_switching.exit (e)) break; } if (j >= 0) { /* __builtin_return emits a sequence of loads to all return registers. One of them might require another mode than MODE_EXIT, even if it is unrelated to the return value, so we want to put the final mode switch after it. */ if (multi_reg_return && targetm.calls.function_value_regno_p (copy_start)) forced_late_switch = true; /* For the SH4, floating point loads depend on fpscr, thus we might need to put the final mode switch after the return value copy. That is still OK, because a floating point return value does not conflict with address reloads. */ if (copy_start >= ret_start && copy_start + copy_num <= ret_end && OBJECT_P (SET_SRC (return_copy_pat))) forced_late_switch = true; break; } if (copy_num == 0) { last_insn = return_copy; continue; } if (copy_start >= ret_start && copy_start + copy_num <= ret_end) nregs -= copy_num; else if (!multi_reg_return || !targetm.calls.function_value_regno_p (copy_start)) break; last_insn = return_copy; } /* ??? Exception handling can lead to the return value copy being already separated from the return value use, as in unwind-dw2.c . Similarly, conditionally returning without a value, and conditionally using builtin_return can lead to an isolated use. */ if (return_copy == BB_HEAD (src_bb)) { short_block = true; break; } last_insn = return_copy; } while (nregs); /* If we didn't see a full return value copy, verify that there is a plausible reason for this. If some, but not all of the return register is likely spilled, we can expect that there is a copy for the likely spilled part. */ gcc_assert (!nregs || forced_late_switch || short_block || !(targetm.class_likely_spilled_p (REGNO_REG_CLASS (ret_start))) || (nregs != hard_regno_nregs[ret_start][GET_MODE (ret_reg)]) /* For multi-hard-register floating point values, sometimes the likely-spilled part is ordinarily copied first, then the other part is set with an arithmetic operation. This doesn't actually cause reload failures, so let it pass. */ || (GET_MODE_CLASS (GET_MODE (ret_reg)) != MODE_INT && nregs != 1)); if (!NOTE_INSN_BASIC_BLOCK_P (last_insn)) { before_return_copy = emit_note_before (NOTE_INSN_DELETED, last_insn); /* Instructions preceding LAST_INSN in the same block might require a different mode than MODE_EXIT, so if we might have such instructions, keep them in a separate block from pre_exit. */ src_bb = split_block (src_bb, PREV_INSN (before_return_copy))->dest; } else before_return_copy = last_insn; pre_exit = split_block (src_bb, before_return_copy)->src; } else { pre_exit = split_edge (eg); } } return pre_exit; }
static void gen_insn (rtx insn, int lineno) { struct pattern_stats stats; int i; /* See if the pattern for this insn ends with a group of CLOBBERs of (hard) registers or MATCH_SCRATCHes. If so, store away the information for later. */ if (XVEC (insn, 1)) { int has_hard_reg = 0; for (i = XVECLEN (insn, 1) - 1; i > 0; i--) { if (GET_CODE (XVECEXP (insn, 1, i)) != CLOBBER) break; if (REG_P (XEXP (XVECEXP (insn, 1, i), 0))) has_hard_reg = 1; else if (GET_CODE (XEXP (XVECEXP (insn, 1, i), 0)) != MATCH_SCRATCH) break; } if (i != XVECLEN (insn, 1) - 1) { struct clobber_pat *p; struct clobber_ent *link = XNEW (struct clobber_ent); int j; link->code_number = insn_code_number; /* See if any previous CLOBBER_LIST entry is the same as this one. */ for (p = clobber_list; p; p = p->next) { if (p->first_clobber != i + 1 || XVECLEN (p->pattern, 1) != XVECLEN (insn, 1)) continue; for (j = i + 1; j < XVECLEN (insn, 1); j++) { rtx old_rtx = XEXP (XVECEXP (p->pattern, 1, j), 0); rtx new_rtx = XEXP (XVECEXP (insn, 1, j), 0); /* OLD and NEW_INSN are the same if both are to be a SCRATCH of the same mode, or if both are registers of the same mode and number. */ if (! (GET_MODE (old_rtx) == GET_MODE (new_rtx) && ((GET_CODE (old_rtx) == MATCH_SCRATCH && GET_CODE (new_rtx) == MATCH_SCRATCH) || (REG_P (old_rtx) && REG_P (new_rtx) && REGNO (old_rtx) == REGNO (new_rtx))))) break; } if (j == XVECLEN (insn, 1)) break; } if (p == 0) { p = XNEW (struct clobber_pat); p->insns = 0; p->pattern = insn; p->first_clobber = i + 1; p->next = clobber_list; p->has_hard_reg = has_hard_reg; clobber_list = p; } link->next = p->insns; p->insns = link; }
static void gen_addr_rtx (enum machine_mode address_mode, rtx symbol, rtx base, rtx index, rtx step, rtx offset, rtx *addr, rtx **step_p, rtx **offset_p) { rtx act_elem; *addr = NULL_RTX; if (step_p) *step_p = NULL; if (offset_p) *offset_p = NULL; if (index) { act_elem = index; if (step) { act_elem = gen_rtx_MULT (address_mode, act_elem, step); if (step_p) *step_p = &XEXP (act_elem, 1); } *addr = act_elem; } if (base && base != const0_rtx) { if (*addr) *addr = simplify_gen_binary (PLUS, address_mode, base, *addr); else *addr = base; } if (symbol) { act_elem = symbol; if (offset) { act_elem = gen_rtx_PLUS (address_mode, act_elem, offset); if (offset_p) *offset_p = &XEXP (act_elem, 1); if (GET_CODE (symbol) == SYMBOL_REF || GET_CODE (symbol) == LABEL_REF || GET_CODE (symbol) == CONST) act_elem = gen_rtx_CONST (address_mode, act_elem); } if (*addr) *addr = gen_rtx_PLUS (address_mode, *addr, act_elem); else *addr = act_elem; } else if (offset) { if (*addr) { *addr = gen_rtx_PLUS (address_mode, *addr, offset); if (offset_p) *offset_p = &XEXP (*addr, 1); } else { *addr = offset; if (offset_p) *offset_p = addr; } } if (!*addr) *addr = const0_rtx; }
static void mark_all_labels (rtx f) { rtx insn; rtx prev_nonjump_insn = NULL; for (insn = f; insn; insn = NEXT_INSN (insn)) if (INSN_P (insn)) { mark_jump_label (PATTERN (insn), insn, 0); /* If the previous non-jump insn sets something to a label, something that this jump insn uses, make that label the primary target of this insn if we don't yet have any. That previous insn must be a single_set and not refer to more than one label. The jump insn must not refer to other labels as jump targets and must be a plain (set (pc) ...), maybe in a parallel, and may refer to the item being set only directly or as one of the arms in an IF_THEN_ELSE. */ if (! INSN_DELETED_P (insn) && JUMP_P (insn) && JUMP_LABEL (insn) == NULL) { rtx label_note = NULL; rtx pc = pc_set (insn); rtx pc_src = pc != NULL ? SET_SRC (pc) : NULL; if (prev_nonjump_insn != NULL) label_note = find_reg_note (prev_nonjump_insn, REG_LABEL_OPERAND, NULL); if (label_note != NULL && pc_src != NULL) { rtx label_set = single_set (prev_nonjump_insn); rtx label_dest = label_set != NULL ? SET_DEST (label_set) : NULL; if (label_set != NULL /* The source must be the direct LABEL_REF, not a PLUS, UNSPEC, IF_THEN_ELSE etc. */ && GET_CODE (SET_SRC (label_set)) == LABEL_REF && (rtx_equal_p (label_dest, pc_src) || (GET_CODE (pc_src) == IF_THEN_ELSE && (rtx_equal_p (label_dest, XEXP (pc_src, 1)) || rtx_equal_p (label_dest, XEXP (pc_src, 2)))))) { /* The CODE_LABEL referred to in the note must be the CODE_LABEL in the LABEL_REF of the "set". We can conveniently use it for the marker function, which requires a LABEL_REF wrapping. */ gcc_assert (XEXP (label_note, 0) == XEXP (SET_SRC (label_set), 0)); mark_jump_label_1 (label_set, insn, false, true); gcc_assert (JUMP_LABEL (insn) == XEXP (SET_SRC (label_set), 0)); } } } else if (! INSN_DELETED_P (insn)) prev_nonjump_insn = insn; } else if (LABEL_P (insn)) prev_nonjump_insn = NULL; /* If we are in cfglayout mode, there may be non-insns between the basic blocks. If those non-insns represent tablejump data, they contain label references that we must record. */ if (current_ir_type () == IR_RTL_CFGLAYOUT) { basic_block bb; rtx insn; FOR_EACH_BB (bb) { for (insn = bb->il.rtl->header; insn; insn = NEXT_INSN (insn)) if (INSN_P (insn)) { gcc_assert (JUMP_TABLE_DATA_P (insn)); mark_jump_label (PATTERN (insn), insn, 0); } for (insn = bb->il.rtl->footer; insn; insn = NEXT_INSN (insn)) if (INSN_P (insn)) { gcc_assert (JUMP_TABLE_DATA_P (insn)); mark_jump_label (PATTERN (insn), insn, 0); } } }
static void gen_exp (rtx x, enum rtx_code subroutine_type, char *used) { RTX_CODE code; int i; int len; const char *fmt; if (x == 0) { printf ("NULL_RTX"); return; } code = GET_CODE (x); switch (code) { case MATCH_OPERAND: case MATCH_DUP: if (used) { if (used[XINT (x, 0)]) { printf ("copy_rtx (operand%d)", XINT (x, 0)); return; } used[XINT (x, 0)] = 1; } printf ("operand%d", XINT (x, 0)); return; case MATCH_OP_DUP: printf ("gen_rtx_fmt_"); for (i = 0; i < XVECLEN (x, 1); i++) printf ("e"); printf (" (GET_CODE (operand%d), ", XINT (x, 0)); if (GET_MODE (x) == VOIDmode) printf ("GET_MODE (operand%d)", XINT (x, 0)); else printf ("%smode", GET_MODE_NAME (GET_MODE (x))); for (i = 0; i < XVECLEN (x, 1); i++) { printf (",\n\t\t"); gen_exp (XVECEXP (x, 1, i), subroutine_type, used); } printf (")"); return; case MATCH_OPERATOR: printf ("gen_rtx_fmt_"); for (i = 0; i < XVECLEN (x, 2); i++) printf ("e"); printf (" (GET_CODE (operand%d)", XINT (x, 0)); printf (", %smode", GET_MODE_NAME (GET_MODE (x))); for (i = 0; i < XVECLEN (x, 2); i++) { printf (",\n\t\t"); gen_exp (XVECEXP (x, 2, i), subroutine_type, used); } printf (")"); return; case MATCH_PARALLEL: case MATCH_PAR_DUP: printf ("operand%d", XINT (x, 0)); return; case MATCH_SCRATCH: gen_rtx_scratch (x, subroutine_type); return; case PC: printf ("pc_rtx"); return; case RETURN: printf ("ret_rtx"); return; case SIMPLE_RETURN: printf ("simple_return_rtx"); return; case CLOBBER: if (REG_P (XEXP (x, 0))) { printf ("gen_hard_reg_clobber (%smode, %i)", GET_MODE_NAME (GET_MODE (XEXP (x, 0))), REGNO (XEXP (x, 0))); return; } break; case CC0: printf ("cc0_rtx"); return; case CONST_INT: if (INTVAL (x) == 0) printf ("const0_rtx"); else if (INTVAL (x) == 1) printf ("const1_rtx"); else if (INTVAL (x) == -1) printf ("constm1_rtx"); else if (-MAX_SAVED_CONST_INT <= INTVAL (x) && INTVAL (x) <= MAX_SAVED_CONST_INT) printf ("const_int_rtx[MAX_SAVED_CONST_INT + (%d)]", (int) INTVAL (x)); else if (INTVAL (x) == STORE_FLAG_VALUE) printf ("const_true_rtx"); else { printf ("GEN_INT ("); printf (HOST_WIDE_INT_PRINT_DEC_C, INTVAL (x)); printf (")"); } return; case CONST_DOUBLE: case CONST_FIXED: case CONST_WIDE_INT: /* These shouldn't be written in MD files. Instead, the appropriate routines in varasm.c should be called. */ gcc_unreachable (); default: break; } printf ("gen_rtx_"); print_code (code); printf (" (%smode", GET_MODE_NAME (GET_MODE (x))); fmt = GET_RTX_FORMAT (code); len = GET_RTX_LENGTH (code); for (i = 0; i < len; i++) { if (fmt[i] == '0') break; printf (",\n\t"); switch (fmt[i]) { case 'e': case 'u': gen_exp (XEXP (x, i), subroutine_type, used); break; case 'i': printf ("%u", XINT (x, i)); break; case 's': printf ("\"%s\"", XSTR (x, i)); break; case 'E': { int j; printf ("gen_rtvec (%d", XVECLEN (x, i)); for (j = 0; j < XVECLEN (x, i); j++) { printf (",\n\t\t"); gen_exp (XVECEXP (x, i, j), subroutine_type, used); } printf (")"); break; } default: gcc_unreachable (); } } printf (")"); }
rtx copy_rtx (rtx orig) { rtx copy; int i, j; RTX_CODE code; const char *format_ptr; code = GET_CODE (orig); switch (code) { case REG: case DEBUG_EXPR: case VALUE: case CONST_INT: case CONST_DOUBLE: case CONST_FIXED: case CONST_VECTOR: case SYMBOL_REF: case CODE_LABEL: case PC: case CC0: case RETURN: case SIMPLE_RETURN: case SCRATCH: /* SCRATCH must be shared because they represent distinct values. */ return orig; case CLOBBER: if (REG_P (XEXP (orig, 0)) && REGNO (XEXP (orig, 0)) < FIRST_PSEUDO_REGISTER) return orig; break; case CONST: if (shared_const_p (orig)) return orig; break; /* A MEM with a constant address is not sharable. The problem is that the constant address may need to be reloaded. If the mem is shared, then reloading one copy of this mem will cause all copies to appear to have been reloaded. */ default: break; } /* Copy the various flags, fields, and other information. We assume that all fields need copying, and then clear the fields that should not be copied. That is the sensible default behavior, and forces us to explicitly document why we are *not* copying a flag. */ copy = shallow_copy_rtx (orig); /* We do not copy the USED flag, which is used as a mark bit during walks over the RTL. */ RTX_FLAG (copy, used) = 0; format_ptr = GET_RTX_FORMAT (GET_CODE (copy)); for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++) switch (*format_ptr++) { case 'e': if (XEXP (orig, i) != NULL) XEXP (copy, i) = copy_rtx (XEXP (orig, i)); break; case 'E': case 'V': if (XVEC (orig, i) != NULL) { XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i)); for (j = 0; j < XVECLEN (copy, i); j++) XVECEXP (copy, i, j) = copy_rtx (XVECEXP (orig, i, j)); } break; case 't': case 'w': case 'i': case 's': case 'S': case 'T': case 'u': case 'B': case '0': /* These are left unchanged. */ break; default: gcc_unreachable (); } return copy; }
void i386_pe_encode_section_info (tree decl, rtx rtl, int first) { default_encode_section_info (decl, rtl, first); if (first && TREE_CODE (decl) == FUNCTION_DECL) { tree type_attributes = TYPE_ATTRIBUTES (TREE_TYPE (decl)); tree newid = NULL_TREE; if (lookup_attribute ("stdcall", type_attributes)) newid = gen_stdcall_or_fastcall_suffix (decl, false); else if (lookup_attribute ("fastcall", type_attributes)) newid = gen_stdcall_or_fastcall_suffix (decl, true); if (newid != NULL_TREE) { rtx rtlname = XEXP (rtl, 0); if (GET_CODE (rtlname) == MEM) rtlname = XEXP (rtlname, 0); XSTR (rtlname, 0) = IDENTIFIER_POINTER (newid); /* These attributes must be present on first declaration, change_decl_assembler_name will warn if they are added later and the decl has been referenced, but duplicate_decls should catch the mismatch before this is called. */ change_decl_assembler_name (decl, newid); } } else if (TREE_CODE (decl) == VAR_DECL && lookup_attribute ("selectany", DECL_ATTRIBUTES (decl))) { if (DECL_INITIAL (decl) /* If an object is initialized with a ctor, the static initialization and destruction code for it is present in each unit defining the object. The code that calls the ctor is protected by a link-once guard variable, so that the object still has link-once semantics, */ || TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl))) make_decl_one_only (decl); else error ("%q+D:'selectany' attribute applies only to initialized objects", decl); } /* Mark the decl so we can tell from the rtl whether the object is dllexport'd or dllimport'd. tree.c: merge_dllimport_decl_attributes handles dllexport/dllimport override semantics. */ if (i386_pe_dllexport_p (decl)) i386_pe_mark_dllexport (decl); else if (i386_pe_dllimport_p (decl)) i386_pe_mark_dllimport (decl); /* It might be that DECL has been declared as dllimport, but a subsequent definition nullified that. Assert that tree.c: merge_dllimport_decl_attributes has removed the attribute before the RTL name was marked with the DLL_IMPORT_PREFIX. */ else gcc_assert (!((TREE_CODE (decl) == FUNCTION_DECL || TREE_CODE (decl) == VAR_DECL) && rtl != NULL_RTX && GET_CODE (rtl) == MEM && GET_CODE (XEXP (rtl, 0)) == MEM && GET_CODE (XEXP (XEXP (rtl, 0), 0)) == SYMBOL_REF && i386_pe_dllimport_name_p (XSTR (XEXP (XEXP (rtl, 0), 0), 0)))); }
int rtx_equal_p (const_rtx x, const_rtx y) { int i; int j; enum rtx_code code; const char *fmt; if (x == y) return 1; if (x == 0 || y == 0) return 0; code = GET_CODE (x); /* Rtx's of different codes cannot be equal. */ if (code != GET_CODE (y)) return 0; /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. (REG:SI x) and (REG:HI x) are NOT equivalent. */ if (GET_MODE (x) != GET_MODE (y)) return 0; /* MEMs referring to different address space are not equivalent. */ if (code == MEM && MEM_ADDR_SPACE (x) != MEM_ADDR_SPACE (y)) return 0; /* Some RTL can be compared nonrecursively. */ switch (code) { case REG: return (REGNO (x) == REGNO (y)); case LABEL_REF: return XEXP (x, 0) == XEXP (y, 0); case SYMBOL_REF: return XSTR (x, 0) == XSTR (y, 0); case DEBUG_EXPR: case VALUE: case SCRATCH: case CONST_DOUBLE: case CONST_INT: case CONST_FIXED: return 0; case DEBUG_IMPLICIT_PTR: return DEBUG_IMPLICIT_PTR_DECL (x) == DEBUG_IMPLICIT_PTR_DECL (y); case DEBUG_PARAMETER_REF: return DEBUG_PARAMETER_REF_DECL (x) == DEBUG_PARAMETER_REF_DECL (y); case ENTRY_VALUE: return rtx_equal_p (ENTRY_VALUE_EXP (x), ENTRY_VALUE_EXP (y)); default: break; } /* Compare the elements. If any pair of corresponding elements fail to match, return 0 for the whole thing. */ fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { switch (fmt[i]) { case 'w': if (XWINT (x, i) != XWINT (y, i)) return 0; break; case 'n': case 'i': if (XINT (x, i) != XINT (y, i)) { #ifndef GENERATOR_FILE if (((code == ASM_OPERANDS && i == 6) || (code == ASM_INPUT && i == 1)) && locator_eq (XINT (x, i), XINT (y, i))) break; #endif return 0; } break; case 'V': case 'E': /* Two vectors must have the same length. */ if (XVECLEN (x, i) != XVECLEN (y, i)) return 0; /* And the corresponding elements must match. */ for (j = 0; j < XVECLEN (x, i); j++) if (rtx_equal_p (XVECEXP (x, i, j), XVECEXP (y, i, j)) == 0) return 0; break; case 'e': if (rtx_equal_p (XEXP (x, i), XEXP (y, i)) == 0) return 0; break; case 'S': case 's': if ((XSTR (x, i) || XSTR (y, i)) && (! XSTR (x, i) || ! XSTR (y, i) || strcmp (XSTR (x, i), XSTR (y, i)))) return 0; break; case 'u': /* These are just backpointers, so they don't matter. */ break; case '0': case 't': break; /* It is believed that rtx's at this level will never contain anything but integers and other rtx's, except for within LABEL_REFs and SYMBOL_REFs. */ default: gcc_unreachable (); } } return 1; }
static bool lm32_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UNUSED, int *total, bool speed) { int code = GET_CODE (x); bool small_mode; const int arithmetic_latency = 1; const int shift_latency = 1; const int compare_latency = 2; const int multiply_latency = 3; const int load_latency = 3; const int libcall_size_cost = 5; /* Determine if we can handle the given mode size in a single instruction. */ small_mode = (mode == QImode) || (mode == HImode) || (mode == SImode); switch (code) { case PLUS: case MINUS: case AND: case IOR: case XOR: case NOT: case NEG: if (!speed) *total = COSTS_N_INSNS (LM32_NUM_REGS (mode)); else *total = COSTS_N_INSNS (arithmetic_latency + (LM32_NUM_REGS (mode) - 1)); break; case COMPARE: if (small_mode) { if (!speed) *total = COSTS_N_INSNS (1); else *total = COSTS_N_INSNS (compare_latency); } else { /* FIXME. Guessing here. */ *total = COSTS_N_INSNS (LM32_NUM_REGS (mode) * (2 + 3) / 2); } break; case ASHIFT: case ASHIFTRT: case LSHIFTRT: if (TARGET_BARREL_SHIFT_ENABLED && small_mode) { if (!speed) *total = COSTS_N_INSNS (1); else *total = COSTS_N_INSNS (shift_latency); } else if (TARGET_BARREL_SHIFT_ENABLED) { /* FIXME: Guessing here. */ *total = COSTS_N_INSNS (LM32_NUM_REGS (mode) * 4); } else if (small_mode && GET_CODE (XEXP (x, 1)) == CONST_INT) { *total = COSTS_N_INSNS (INTVAL (XEXP (x, 1))); } else { /* Libcall. */ if (!speed) *total = COSTS_N_INSNS (libcall_size_cost); else *total = COSTS_N_INSNS (100); } break; case MULT: if (TARGET_MULTIPLY_ENABLED && small_mode) { if (!speed) *total = COSTS_N_INSNS (1); else *total = COSTS_N_INSNS (multiply_latency); } else { /* Libcall. */ if (!speed) *total = COSTS_N_INSNS (libcall_size_cost); else *total = COSTS_N_INSNS (100); } break; case DIV: case MOD: case UDIV: case UMOD: if (TARGET_DIVIDE_ENABLED && small_mode) { if (!speed) *total = COSTS_N_INSNS (1); else { if (GET_CODE (XEXP (x, 1)) == CONST_INT) { int cycles = 0; unsigned HOST_WIDE_INT i = INTVAL (XEXP (x, 1)); while (i) { i >>= 2; cycles++; } if (IN_RANGE (i, 0, 65536)) *total = COSTS_N_INSNS (1 + 1 + cycles); else *total = COSTS_N_INSNS (2 + 1 + cycles); return true; } else if (GET_CODE (XEXP (x, 1)) == REG) { *total = COSTS_N_INSNS (1 + GET_MODE_SIZE (mode) / 2); return true; } else { *total = COSTS_N_INSNS (1 + GET_MODE_SIZE (mode) / 2); return false; } } }
hashval_t iterative_hash_rtx (const_rtx x, hashval_t hash) { enum rtx_code code; enum machine_mode mode; int i, j; const char *fmt; if (x == NULL_RTX) return hash; code = GET_CODE (x); hash = iterative_hash_object (code, hash); mode = GET_MODE (x); hash = iterative_hash_object (mode, hash); switch (code) { case REG: i = REGNO (x); return iterative_hash_object (i, hash); case CONST_INT: return iterative_hash_object (INTVAL (x), hash); case SYMBOL_REF: if (XSTR (x, 0)) return iterative_hash (XSTR (x, 0), strlen (XSTR (x, 0)) + 1, hash); return hash; case LABEL_REF: case DEBUG_EXPR: case VALUE: case SCRATCH: case CONST_DOUBLE: case CONST_FIXED: case DEBUG_IMPLICIT_PTR: case DEBUG_PARAMETER_REF: return hash; default: break; } fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) switch (fmt[i]) { case 'w': hash = iterative_hash_object (XWINT (x, i), hash); break; case 'n': case 'i': hash = iterative_hash_object (XINT (x, i), hash); break; case 'V': case 'E': j = XVECLEN (x, i); hash = iterative_hash_object (j, hash); for (j = 0; j < XVECLEN (x, i); j++) hash = iterative_hash_rtx (XVECEXP (x, i, j), hash); break; case 'e': hash = iterative_hash_rtx (XEXP (x, i), hash); break; case 'S': case 's': if (XSTR (x, i)) hash = iterative_hash (XSTR (x, 0), strlen (XSTR (x, 0)) + 1, hash); break; default: break; } return hash; }