static void nds32_emit_mem_move (rtx src, rtx dst, enum machine_mode mode, int addr_offset) { gcc_assert (MEM_P (src) && MEM_P (dst)); rtx tmp_reg = gen_reg_rtx (mode); nds32_emit_load_store (tmp_reg, src, mode, addr_offset, /* load_p */ true); nds32_emit_load_store (tmp_reg, dst, mode, addr_offset, /* load_p */ false); }
rtx gen_lowpart_general (machine_mode mode, rtx x) { rtx result = gen_lowpart_common (mode, x); if (result) return result; /* Handle SUBREGs and hard REGs that were rejected by simplify_gen_subreg. */ else if (REG_P (x) || GET_CODE (x) == SUBREG) { result = gen_lowpart_common (mode, copy_to_reg (x)); gcc_assert (result != 0); return result; } else { /* The only additional case we can do is MEM. */ gcc_assert (MEM_P (x)); /* The following exposes the use of "x" to CSE. */ scalar_int_mode xmode; if (is_a <scalar_int_mode> (GET_MODE (x), &xmode) && GET_MODE_SIZE (xmode) <= UNITS_PER_WORD && TRULY_NOOP_TRUNCATION_MODES_P (mode, xmode) && !reload_completed) return gen_lowpart_general (mode, force_reg (xmode, x)); poly_int64 offset = byte_lowpart_offset (mode, GET_MODE (x)); return adjust_address (x, mode, offset); } }
rtx gen_lowpart_if_possible (machine_mode mode, rtx x) { rtx result = gen_lowpart_common (mode, x); if (result) return result; else if (MEM_P (x)) { /* This is the only other case we handle. */ poly_int64 offset = byte_lowpart_offset (mode, GET_MODE (x)); rtx new_rtx = adjust_address_nv (x, mode, offset); if (! memory_address_addr_space_p (mode, XEXP (new_rtx, 0), MEM_ADDR_SPACE (x))) return 0; return new_rtx; } else if (mode != GET_MODE (x) && GET_MODE (x) != VOIDmode && validate_subreg (mode, GET_MODE (x), x, subreg_lowpart_offset (mode, GET_MODE (x)))) return gen_lowpart_SUBREG (mode, x); else return 0; }
void symtab_node::make_decl_local (void) { rtx rtl, symbol; /* Avoid clearing comdat_groups on comdat-local decls. */ if (TREE_PUBLIC (decl) == 0) return; if (TREE_CODE (decl) == VAR_DECL) DECL_COMMON (decl) = 0; else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); DECL_COMDAT (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 void sdbout_toplevel_data (tree decl) { tree type = TREE_TYPE (decl); if (DECL_IGNORED_P (decl)) return; gcc_assert (TREE_CODE (decl) == VAR_DECL); gcc_assert (MEM_P (DECL_RTL (decl))); gcc_assert (DECL_INITIAL (decl)); PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); PUT_SDB_VAL (XEXP (DECL_RTL (decl), 0)); if (TREE_PUBLIC (decl)) { PUT_SDB_SCL (C_EXT); } else { PUT_SDB_SCL (C_STAT); } PUT_SDB_TYPE (plain_type (type)); PUT_SDB_ENDEF; }
/* Return true if X contains memory or some UNSPEC. We can not just check insn operands as memory or unspec might be not an operand itself but contain an operand. Insn with memory access is not profitable for rematerialization. Rematerialization of UNSPEC might result in wrong code generation as the UNPEC effect is unknown (e.g. generating a label). */ static bool bad_for_rematerialization_p (rtx x) { int i, j; const char *fmt; enum rtx_code code; if (MEM_P (x) || GET_CODE (x) == UNSPEC || GET_CODE (x) == UNSPEC_VOLATILE) return true; code = GET_CODE (x); fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'e') { if (bad_for_rematerialization_p (XEXP (x, i))) return true; } else if (fmt[i] == 'E') { for (j = XVECLEN (x, i) - 1; j >= 0; j--) if (bad_for_rematerialization_p (XVECEXP (x, i, j))) return true; } } return false; }
void split_quadword_operands (rtx * operands, rtx * low, int n ATTRIBUTE_UNUSED) { int i; /* Split operands. */ low[0] = low[1] = low[2] = 0; for (i = 0; i < 3; i++) { if (low[i]) /* it's already been figured out */; else if (MEM_P (operands[i]) && (GET_CODE (XEXP (operands[i], 0)) == POST_INC)) { rtx addr = XEXP (operands[i], 0); operands[i] = low[i] = gen_rtx_MEM (SImode, addr); if (which_alternative == 0 && i == 0) { addr = XEXP (operands[i], 0); operands[i+1] = low[i+1] = gen_rtx_MEM (SImode, addr); } } else { low[i] = operand_subword (operands[i], 0, 0, DImode); operands[i] = operand_subword (operands[i], 1, 0, DImode); } } }
static struct load * alloc_load (rtx set) { struct load **slot; rtx mem, reg; mem = SET_DEST (set); if (MEM_P (mem)) reg = SET_SRC (set); else { reg = mem; mem = SET_SRC (set); } slot = (struct load **) htab_find_slot_with_hash (htab_load, mem, load_rtx_hash (mem), INSERT); if (*slot == NULL) { *slot = (struct load*) xcalloc (1, sizeof (struct load)); (*slot)->mem = mem; } else (*slot)->reg_kill = 0; (*slot)->reg = reg; return *slot; }
bool nds32_expand_strlen (rtx result, rtx str, rtx target_char, rtx align ATTRIBUTE_UNUSED) { rtx base_reg, backup_base_reg; rtx ffb_result; rtx target_char_ptr, length; rtx loop_label, tmp; if (optimize_size || optimize < 3) return false; gcc_assert (MEM_P (str)); gcc_assert (CONST_INT_P (target_char) || REG_P (target_char)); base_reg = copy_to_mode_reg (SImode, XEXP (str, 0)); loop_label = gen_label_rtx (); ffb_result = gen_reg_rtx (Pmode); tmp = gen_reg_rtx (SImode); backup_base_reg = gen_reg_rtx (SImode); /* Emit loop version of strlen. move $backup_base, $base .Lloop: lmw.bim $tmp, [$base], $tmp, 0 ffb $ffb_result, $tmp, $target_char ! is there $target_char? beqz $ffb_result, .Lloop add $last_char_ptr, $base, $ffb_result sub $length, $last_char_ptr, $backup_base */ /* move $backup_base, $base */ emit_move_insn (backup_base_reg, base_reg); /* .Lloop: */ emit_label (loop_label); /* lmw.bim $tmp, [$base], $tmp, 0 */ emit_insn (gen_unaligned_load_update_base_w (base_reg, tmp, base_reg)); /* ffb $ffb_result, $tmp, $target_char ! is there $target_char? */ emit_insn (gen_unspec_ffb (ffb_result, tmp, target_char)); /* beqz $ffb_result, .Lloop */ emit_cmp_and_jump_insns (ffb_result, const0_rtx, EQ, NULL, SImode, 1, loop_label); /* add $target_char_ptr, $base, $ffb_result */ target_char_ptr = expand_binop (Pmode, add_optab, base_reg, ffb_result, NULL_RTX, 0, OPTAB_WIDEN); /* sub $length, $target_char_ptr, $backup_base */ length = expand_binop (Pmode, sub_optab, target_char_ptr, backup_base_reg, NULL_RTX, 0, OPTAB_WIDEN); emit_move_insn (result, length); return true; }
static bool varying_mem_p (const_rtx x) { subrtx_iterator::array_type array; FOR_EACH_SUBRTX (iter, array, x, NONCONST) if (MEM_P (*iter) && !MEM_READONLY_P (*iter)) return true; return false; }
static int try_apply_stack_adjustment (rtx insn, struct csa_reflist *reflist, HOST_WIDE_INT new_adjust, HOST_WIDE_INT delta) { struct csa_reflist *ml; rtx set; set = single_set_for_csa (insn); if (MEM_P (SET_DEST (set))) validate_change (insn, &SET_DEST (set), replace_equiv_address (SET_DEST (set), stack_pointer_rtx), 1); else validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (new_adjust), 1); for (ml = reflist; ml ; ml = ml->next) { rtx new_addr = plus_constant (Pmode, stack_pointer_rtx, ml->sp_offset - delta); rtx new_val; if (MEM_P (*ml->ref)) new_val = replace_equiv_address_nv (*ml->ref, new_addr); else if (GET_MODE (*ml->ref) == GET_MODE (stack_pointer_rtx)) new_val = new_addr; else new_val = lowpart_subreg (GET_MODE (*ml->ref), new_addr, GET_MODE (new_addr)); validate_change (ml->insn, ml->ref, new_val, 1); } if (apply_change_group ()) { /* Succeeded. Update our knowledge of the stack references. */ for (ml = reflist; ml ; ml = ml->next) ml->sp_offset -= delta; return 1; } else return 0; }
/* Auxiliary function for mem_read_insn_p. */ static void mark_mem_use (rtx *x, void *) { subrtx_iterator::array_type array; FOR_EACH_SUBRTX (iter, array, *x, NONCONST) if (MEM_P (*iter)) { mem_ref_p = true; break; } }
static bool interesting_load (rtx set) { rtx mem, reg; if (!set) return false; /* Handle store just like loads. */ mem = SET_DEST (set); if (MEM_P (mem)) reg = SET_SRC (set); else { reg = mem; mem = SET_SRC (set); } return MEM_P (mem) && !MEM_VOLATILE_P (mem) && REG_P (reg); }
static void sdbout_reg_parms (tree parms) { for (; parms; parms = TREE_CHAIN (parms)) if (DECL_NAME (parms)) { const char *name = IDENTIFIER_POINTER (DECL_NAME (parms)); /* Report parms that live in registers during the function but were passed in memory. */ if (REG_P (DECL_RTL (parms)) && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER && PARM_PASSED_IN_MEMORY (parms)) { if (name == 0 || *name == 0) name = gen_fake_label (); PUT_SDB_DEF (name); PUT_SDB_INT_VAL (DBX_REGISTER_NUMBER (REGNO (DECL_RTL (parms)))); PUT_SDB_SCL (C_REG); PUT_SDB_TYPE (plain_type (TREE_TYPE (parms))); PUT_SDB_ENDEF; } /* Report parms that live in memory but not where they were passed. */ else if (MEM_P (DECL_RTL (parms)) && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS && CONST_INT_P (XEXP (XEXP (DECL_RTL (parms), 0), 1)) && PARM_PASSED_IN_MEMORY (parms) && ! rtx_equal_p (DECL_RTL (parms), DECL_INCOMING_RTL (parms))) { #if 0 /* ??? It is not clear yet what should replace this. */ int offset = DECL_OFFSET (parms) / BITS_PER_UNIT; /* A parm declared char is really passed as an int, so it occupies the least significant bytes. On a big-endian machine those are not the low-numbered ones. */ if (BYTES_BIG_ENDIAN && offset != -1 && TREE_TYPE (parms) != DECL_ARG_TYPE (parms)) offset += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms))) - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms)))); if (INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) != offset) {...} #endif { if (name == 0 || *name == 0) name = gen_fake_label (); PUT_SDB_DEF (name); PUT_SDB_INT_VAL (DEBUGGER_AUTO_OFFSET (XEXP (DECL_RTL (parms), 0))); PUT_SDB_SCL (C_AUTO); PUT_SDB_TYPE (plain_type (TREE_TYPE (parms))); PUT_SDB_ENDEF; } } }
/* Make DECL local. FIXME: We shouldn't need to mess with rtl this early, but other code such as notice_global_symbol generates rtl. */ 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)) { /* It is possible that we are linking against library defining same COMDAT function. To avoid conflict we need to rename our local name of the function just in the case WHOPR partitioning decide to make it hidden to avoid cross partition references. */ if (flag_wpa) { const char *old_name; symtab_node node = symtab_get_node (decl); old_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); change_decl_assembler_name (decl, clone_function_name (decl, "local")); if (node->symbol.lto_file_data) lto_record_renamed_decl (node->symbol.lto_file_data, old_name, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); } DECL_SECTION_NAME (decl) = 0; DECL_COMDAT (decl) = 0; } DECL_COMDAT_GROUP (decl) = 0; DECL_WEAK (decl) = 0; DECL_EXTERNAL (decl) = 0; 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 stack_memref_p (rtx x) { if (!MEM_P (x)) return 0; x = XEXP (x, 0); if (x == stack_pointer_rtx) return 1; if (GET_CODE (x) == PLUS && XEXP (x, 0) == stack_pointer_rtx && CONST_INT_P (XEXP (x, 1))) return 1; return 0; }
static bool interesting_second_load (rtx set, struct load ***load, rtx insn) { rtx mem, reg; if (!set) return false; mem = SET_SRC (set); reg = SET_DEST (set); if (!MEM_P (mem) || MEM_VOLATILE_P (mem) || !REG_P (reg)) return false; *load = (struct load **) htab_find_slot_with_hash (htab_load, mem, load_rtx_hash (mem), NO_INSERT); if (!*load) return false; /* Don't work on cases that never happen: if there is no kill, we would have inherited the reload; if the store and load regs are the same we would need to find an available register. If the kill insn was already replaced by a move this information is stale, disregard it. */ if (rtx_equal_p (reg, (**load)->reg) || !(**load)->reg_kill || INSN_DELETED_P ((**load)->reg_kill) || reg_used_between_p (reg, PREV_INSN ((**load)->reg_kill), NEXT_INSN (insn)) || reg_set_between_p (reg, PREV_INSN ((**load)->reg_kill), insn)) { if (dump_file) { fputs ("\nCan't insert the move before the kill for this load:\n ", dump_file); print_inline_rtx (dump_file, insn, 2); fputs ("\n\n", dump_file); } return false; } return true; }
rtx gen_lowpart_general (enum machine_mode mode, rtx x) { rtx result = gen_lowpart_common (mode, x); if (result) return result; else if (REG_P (x)) { /* Must be a hard reg that's not valid in MODE. */ result = gen_lowpart_common (mode, copy_to_reg (x)); gcc_assert (result != 0); return result; } else { int offset = 0; /* The only additional case we can do is MEM. */ gcc_assert (MEM_P (x)); /* The following exposes the use of "x" to CSE. */ if (GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD && SCALAR_INT_MODE_P (GET_MODE (x)) && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode), GET_MODE_BITSIZE (GET_MODE (x))) && ! no_new_pseudos) return gen_lowpart_general (mode, force_reg (GET_MODE (x), x)); if (WORDS_BIG_ENDIAN) offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)); if (BYTES_BIG_ENDIAN) /* Adjust the address so that the address-after-the-data is unchanged. */ offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)) - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x)))); return adjust_address (x, mode, offset); } }
rtx gen_lowpart_general (machine_mode mode, rtx x) { rtx result = gen_lowpart_common (mode, x); if (result) return result; /* Handle SUBREGs and hard REGs that were rejected by simplify_gen_subreg. */ else if (REG_P (x) || GET_CODE (x) == SUBREG) { result = gen_lowpart_common (mode, copy_to_reg (x)); gcc_assert (result != 0); return result; } else { int offset = 0; /* The only additional case we can do is MEM. */ gcc_assert (MEM_P (x)); /* The following exposes the use of "x" to CSE. */ if (GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD && SCALAR_INT_MODE_P (GET_MODE (x)) && TRULY_NOOP_TRUNCATION_MODES_P (mode, GET_MODE (x)) && !reload_completed) return gen_lowpart_general (mode, force_reg (GET_MODE (x), x)); if (WORDS_BIG_ENDIAN) offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD) - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD)); if (BYTES_BIG_ENDIAN) /* Adjust the address so that the address-after-the-data is unchanged. */ offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)) - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x)))); return adjust_address (x, mode, offset); } }
void symtab_make_decl_local (tree decl) { rtx rtl, symbol; /* Avoid clearing DECL_COMDAT_GROUP on comdat-local decls. */ if (TREE_PUBLIC (decl) == 0) return; 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; DECL_DLLIMPORT_P (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 void expand_STORE_LANES (gimple stmt) { struct expand_operand ops[2]; tree type, lhs, rhs; rtx target, reg; lhs = gimple_call_lhs (stmt); rhs = gimple_call_arg (stmt, 0); type = TREE_TYPE (rhs); target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE); reg = expand_normal (rhs); gcc_assert (MEM_P (target)); PUT_MODE (target, TYPE_MODE (type)); create_fixed_operand (&ops[0], target); create_input_operand (&ops[1], reg, TYPE_MODE (type)); expand_insn (get_multi_vector_move (type, vec_store_lanes_optab), 2, ops); }
int c54x_expand_movqi(rtx ops[]) { int done = 0; int i; fprintf(stderr, "--->>>"); for(i=0; i < 2; i++) { print_rtl(stderr, ops[i]); } fprintf(stderr, "<<<---\n"); if(ACC_REG_P(ops[0])) { ops[0] = copy_rtx(ops[0]); PUT_MODE(ops[0], PSImode); fprintf(stderr, "+++"); print_rtl(stderr, ops[0]); fprintf(stderr, "+++\n"); done = 1; if(MEM_P(ops[1])) { emit_insn(gen_ldm(ops[0], ops[1])); } else if(REG_P(ops[1])) { emit_insn(gen_ldu(ops[0], ops[1])); } else if(CONSTANT_P(ops[1])) { emit_insn(gen_ld_const(ops[0], ops[1], gen_reg_rtx(QImode))); } else { done = 2; } } else if( (REG_P(ops[0]) && (GET_CODE(ops[1]) == MEM && REG_P(XEXP(ops[1],0)))) || (T_REG_P(ops[0]) && ARSP_REG_P(ops[1])) ) { done = 2; } return done; }
static void expand_MASK_STORE (gimple stmt) { struct expand_operand ops[3]; tree type, lhs, rhs, maskt; rtx mem, reg, mask; maskt = gimple_call_arg (stmt, 2); rhs = gimple_call_arg (stmt, 3); type = TREE_TYPE (rhs); lhs = fold_build2 (MEM_REF, type, gimple_call_arg (stmt, 0), gimple_call_arg (stmt, 1)); mem = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE); gcc_assert (MEM_P (mem)); mask = expand_normal (maskt); reg = expand_normal (rhs); create_fixed_operand (&ops[0], mem); create_input_operand (&ops[1], reg, TYPE_MODE (type)); create_input_operand (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt))); expand_insn (optab_handler (maskstore_optab, TYPE_MODE (type)), 3, ops); }
static void expand_MASK_LOAD (gimple stmt) { struct expand_operand ops[3]; tree type, lhs, rhs, maskt; rtx mem, target, mask; maskt = gimple_call_arg (stmt, 2); lhs = gimple_call_lhs (stmt); if (lhs == NULL_TREE) return; type = TREE_TYPE (lhs); rhs = fold_build2 (MEM_REF, type, gimple_call_arg (stmt, 0), gimple_call_arg (stmt, 1)); mem = expand_expr (rhs, NULL_RTX, VOIDmode, EXPAND_WRITE); gcc_assert (MEM_P (mem)); mask = expand_normal (maskt); target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE); create_output_operand (&ops[0], target, TYPE_MODE (type)); create_fixed_operand (&ops[1], mem); create_input_operand (&ops[2], mask, TYPE_MODE (TREE_TYPE (maskt))); expand_insn (optab_handler (maskload_optab, TYPE_MODE (type)), 3, ops); }
/* Return 1 if x is a valid address not using indexing. (This much is the easy part.) */ static int nonindexed_address_p (rtx x, int strict) { rtx xfoo0; if (REG_P (x)) { extern rtx *reg_equiv_mem; if (!reload_in_progress || reg_equiv_mem[REGNO (x)] == 0 || indirectable_address_p (reg_equiv_mem[REGNO (x)], strict)) return 1; } if (indirectable_constant_address_p (x)) return 1; if (indirectable_address_p (x, strict)) return 1; xfoo0 = XEXP (x, 0); if (MEM_P (x) && indirectable_address_p (xfoo0, strict)) return 1; if ((GET_CODE (x) == PRE_DEC || GET_CODE (x) == POST_INC) && BASE_REGISTER_P (xfoo0, strict)) return 1; return 0; }
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 find_call_stack_args (rtx_call_insn *call_insn, bool do_mark, bool fast, bitmap arg_stores) { rtx p; rtx_insn *insn, *prev_insn; bool ret; HOST_WIDE_INT min_sp_off, max_sp_off; bitmap sp_bytes; gcc_assert (CALL_P (call_insn)); if (!ACCUMULATE_OUTGOING_ARGS) return true; if (!do_mark) { gcc_assert (arg_stores); bitmap_clear (arg_stores); } min_sp_off = INTTYPE_MAXIMUM (HOST_WIDE_INT); max_sp_off = 0; /* First determine the minimum and maximum offset from sp for stored arguments. */ for (p = CALL_INSN_FUNCTION_USAGE (call_insn); p; p = XEXP (p, 1)) if (GET_CODE (XEXP (p, 0)) == USE && MEM_P (XEXP (XEXP (p, 0), 0))) { rtx mem = XEXP (XEXP (p, 0), 0), addr; HOST_WIDE_INT off = 0, size; if (!MEM_SIZE_KNOWN_P (mem)) return false; size = MEM_SIZE (mem); addr = XEXP (mem, 0); if (GET_CODE (addr) == PLUS && REG_P (XEXP (addr, 0)) && CONST_INT_P (XEXP (addr, 1))) { off = INTVAL (XEXP (addr, 1)); addr = XEXP (addr, 0); } if (addr != stack_pointer_rtx) { if (!REG_P (addr)) return false; /* If not fast, use chains to see if addr wasn't set to sp + offset. */ if (!fast) { df_ref use; struct df_link *defs; rtx set; FOR_EACH_INSN_USE (use, call_insn) if (rtx_equal_p (addr, DF_REF_REG (use))) break; if (use == NULL) return false; for (defs = DF_REF_CHAIN (use); defs; defs = defs->next) if (! DF_REF_IS_ARTIFICIAL (defs->ref)) break; if (defs == NULL) return false; set = single_set (DF_REF_INSN (defs->ref)); if (!set) return false; if (GET_CODE (SET_SRC (set)) != PLUS || XEXP (SET_SRC (set), 0) != stack_pointer_rtx || !CONST_INT_P (XEXP (SET_SRC (set), 1))) return false; off += INTVAL (XEXP (SET_SRC (set), 1)); } else return false;
static bool propagate_rtx_1 (rtx *px, rtx old_rtx, rtx new_rtx, int flags) { rtx x = *px, tem = NULL_RTX, op0, op1, op2; enum rtx_code code = GET_CODE (x); machine_mode mode = GET_MODE (x); machine_mode op_mode; bool can_appear = (flags & PR_CAN_APPEAR) != 0; bool valid_ops = true; if (!(flags & PR_HANDLE_MEM) && MEM_P (x) && !MEM_READONLY_P (x)) { /* If unsafe, change MEMs to CLOBBERs or SCRATCHes (to preserve whether they have side effects or not). */ *px = (side_effects_p (x) ? gen_rtx_CLOBBER (GET_MODE (x), const0_rtx) : gen_rtx_SCRATCH (GET_MODE (x))); return false; } /* If X is OLD_RTX, return NEW_RTX. But not if replacing only within an address, and we are *not* inside one. */ if (x == old_rtx) { *px = new_rtx; return can_appear; } /* If this is an expression, try recursive substitution. */ switch (GET_RTX_CLASS (code)) { case RTX_UNARY: op0 = XEXP (x, 0); op_mode = GET_MODE (op0); valid_ops &= propagate_rtx_1 (&op0, old_rtx, new_rtx, flags); if (op0 == XEXP (x, 0)) return true; tem = simplify_gen_unary (code, mode, op0, op_mode); break; case RTX_BIN_ARITH: case RTX_COMM_ARITH: op0 = XEXP (x, 0); op1 = XEXP (x, 1); valid_ops &= propagate_rtx_1 (&op0, old_rtx, new_rtx, flags); valid_ops &= propagate_rtx_1 (&op1, old_rtx, new_rtx, flags); if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1)) return true; tem = simplify_gen_binary (code, mode, op0, op1); break; case RTX_COMPARE: case RTX_COMM_COMPARE: op0 = XEXP (x, 0); op1 = XEXP (x, 1); op_mode = GET_MODE (op0) != VOIDmode ? GET_MODE (op0) : GET_MODE (op1); valid_ops &= propagate_rtx_1 (&op0, old_rtx, new_rtx, flags); valid_ops &= propagate_rtx_1 (&op1, old_rtx, new_rtx, flags); if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1)) return true; tem = simplify_gen_relational (code, mode, op_mode, op0, op1); break; case RTX_TERNARY: case RTX_BITFIELD_OPS: op0 = XEXP (x, 0); op1 = XEXP (x, 1); op2 = XEXP (x, 2); op_mode = GET_MODE (op0); valid_ops &= propagate_rtx_1 (&op0, old_rtx, new_rtx, flags); valid_ops &= propagate_rtx_1 (&op1, old_rtx, new_rtx, flags); valid_ops &= propagate_rtx_1 (&op2, old_rtx, new_rtx, flags); if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1) && op2 == XEXP (x, 2)) return true; if (op_mode == VOIDmode) op_mode = GET_MODE (op0); tem = simplify_gen_ternary (code, mode, op_mode, op0, op1, op2); break; case RTX_EXTRA: /* The only case we try to handle is a SUBREG. */ if (code == SUBREG) { op0 = XEXP (x, 0); valid_ops &= propagate_rtx_1 (&op0, old_rtx, new_rtx, flags); if (op0 == XEXP (x, 0)) return true; tem = simplify_gen_subreg (mode, op0, GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x)); } break; case RTX_OBJ: if (code == MEM && x != new_rtx) { rtx new_op0; op0 = XEXP (x, 0); /* There are some addresses that we cannot work on. */ if (!can_simplify_addr (op0)) return true; op0 = new_op0 = targetm.delegitimize_address (op0); valid_ops &= propagate_rtx_1 (&new_op0, old_rtx, new_rtx, flags | PR_CAN_APPEAR); /* Dismiss transformation that we do not want to carry on. */ if (!valid_ops || new_op0 == op0 || !(GET_MODE (new_op0) == GET_MODE (op0) || GET_MODE (new_op0) == VOIDmode)) return true; canonicalize_address (new_op0); /* Copy propagations are always ok. Otherwise check the costs. */ if (!(REG_P (old_rtx) && REG_P (new_rtx)) && !should_replace_address (op0, new_op0, GET_MODE (x), MEM_ADDR_SPACE (x), flags & PR_OPTIMIZE_FOR_SPEED)) return true; tem = replace_equiv_address_nv (x, new_op0); } else if (code == LO_SUM) { op0 = XEXP (x, 0); op1 = XEXP (x, 1); /* The only simplification we do attempts to remove references to op0 or make it constant -- in both cases, op0's invalidity will not make the result invalid. */ propagate_rtx_1 (&op0, old_rtx, new_rtx, flags | PR_CAN_APPEAR); valid_ops &= propagate_rtx_1 (&op1, old_rtx, new_rtx, flags); if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1)) return true; /* (lo_sum (high x) x) -> x */ if (GET_CODE (op0) == HIGH && rtx_equal_p (XEXP (op0, 0), op1)) tem = op1; else tem = gen_rtx_LO_SUM (mode, op0, op1); /* OP1 is likely not a legitimate address, otherwise there would have been no LO_SUM. We want it to disappear if it is invalid, return false in that case. */ return memory_address_p (mode, tem); } else if (code == REG) { if (rtx_equal_p (x, old_rtx)) { *px = new_rtx; return can_appear; } } break; default: break; } /* No change, no trouble. */ if (tem == NULL_RTX) return true; *px = tem; /* Allow replacements that simplify operations on a vector or complex value to a component. The most prominent case is (subreg ([vec_]concat ...)). */ if (REG_P (tem) && !HARD_REGISTER_P (tem) && (VECTOR_MODE_P (GET_MODE (new_rtx)) || COMPLEX_MODE_P (GET_MODE (new_rtx))) && GET_MODE (tem) == GET_MODE_INNER (GET_MODE (new_rtx))) return true; /* The replacement we made so far is valid, if all of the recursive replacements were valid, or we could simplify everything to a constant. */ return valid_ops || can_appear || CONSTANT_P (tem); }
static void combine_stack_adjustments_for_block (basic_block bb) { HOST_WIDE_INT last_sp_adjust = 0; rtx last_sp_set = NULL_RTX; rtx last2_sp_set = NULL_RTX; struct csa_reflist *reflist = NULL; rtx insn, next, set; struct record_stack_refs_data data; bool end_of_block = false; for (insn = BB_HEAD (bb); !end_of_block ; insn = next) { end_of_block = insn == BB_END (bb); next = NEXT_INSN (insn); if (! INSN_P (insn)) continue; set = single_set_for_csa (insn); if (set) { rtx dest = SET_DEST (set); rtx src = SET_SRC (set); /* Find constant additions to the stack pointer. */ if (dest == stack_pointer_rtx && GET_CODE (src) == PLUS && XEXP (src, 0) == stack_pointer_rtx && CONST_INT_P (XEXP (src, 1))) { HOST_WIDE_INT this_adjust = INTVAL (XEXP (src, 1)); /* If we've not seen an adjustment previously, record it now and continue. */ if (! last_sp_set) { last_sp_set = insn; last_sp_adjust = this_adjust; continue; } /* If not all recorded refs can be adjusted, or the adjustment is now too large for a constant addition, we cannot merge the two stack adjustments. Also we need to be careful to not move stack pointer such that we create stack accesses outside the allocated area. We can combine an allocation into the first insn, or a deallocation into the second insn. We can not combine an allocation followed by a deallocation. The only somewhat frequent occurrence of the later is when a function allocates a stack frame but does not use it. For this case, we would need to analyze rtl stream to be sure that allocated area is really unused. This means not only checking the memory references, but also all registers or global memory references possibly containing a stack frame address. Perhaps the best way to address this problem is to teach gcc not to allocate stack for objects never used. */ /* Combine an allocation into the first instruction. */ if (STACK_GROWS_DOWNWARD ? this_adjust <= 0 : this_adjust >= 0) { if (try_apply_stack_adjustment (last_sp_set, reflist, last_sp_adjust + this_adjust, this_adjust)) { /* It worked! */ maybe_move_args_size_note (last_sp_set, insn, false); delete_insn (insn); last_sp_adjust += this_adjust; continue; } } /* Otherwise we have a deallocation. Do not combine with a previous allocation. Combine into the second insn. */ else if (STACK_GROWS_DOWNWARD ? last_sp_adjust >= 0 : last_sp_adjust <= 0) { if (try_apply_stack_adjustment (insn, reflist, last_sp_adjust + this_adjust, -last_sp_adjust)) { /* It worked! */ maybe_move_args_size_note (insn, last_sp_set, true); delete_insn (last_sp_set); last_sp_set = insn; last_sp_adjust += this_adjust; free_csa_reflist (reflist); reflist = NULL; continue; } } /* Combination failed. Restart processing from here. If deallocation+allocation conspired to cancel, we can delete the old deallocation insn. */ if (last_sp_set) { if (last_sp_adjust == 0) { maybe_move_args_size_note (insn, last_sp_set, true); delete_insn (last_sp_set); } else last2_sp_set = last_sp_set; } free_csa_reflist (reflist); reflist = NULL; last_sp_set = insn; last_sp_adjust = this_adjust; continue; } /* Find a store with pre-(dec|inc)rement or pre-modify of exactly the previous adjustment and turn it into a simple store. This is equivalent to anticipating the stack adjustment so this must be an allocation. */ if (MEM_P (dest) && ((STACK_GROWS_DOWNWARD ? (GET_CODE (XEXP (dest, 0)) == PRE_DEC && last_sp_adjust == (HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (dest))) : (GET_CODE (XEXP (dest, 0)) == PRE_INC && last_sp_adjust == -(HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (dest)))) || ((STACK_GROWS_DOWNWARD ? last_sp_adjust >= 0 : last_sp_adjust <= 0) && GET_CODE (XEXP (dest, 0)) == PRE_MODIFY && GET_CODE (XEXP (XEXP (dest, 0), 1)) == PLUS && XEXP (XEXP (XEXP (dest, 0), 1), 0) == stack_pointer_rtx && GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1)) == CONST_INT && INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1)) == -last_sp_adjust)) && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx && !reg_mentioned_p (stack_pointer_rtx, src) && memory_address_p (GET_MODE (dest), stack_pointer_rtx) && try_apply_stack_adjustment (insn, reflist, 0, -last_sp_adjust)) { if (last2_sp_set) maybe_move_args_size_note (last2_sp_set, last_sp_set, false); else maybe_move_args_size_note (insn, last_sp_set, true); delete_insn (last_sp_set); free_csa_reflist (reflist); reflist = NULL; last_sp_set = NULL_RTX; last_sp_adjust = 0; continue; } } data.insn = insn; data.reflist = reflist; if (!CALL_P (insn) && last_sp_set && !for_each_rtx (&PATTERN (insn), record_stack_refs, &data)) { reflist = data.reflist; continue; } reflist = data.reflist; /* Otherwise, we were not able to process the instruction. Do not continue collecting data across such a one. */ if (last_sp_set && (CALL_P (insn) || reg_mentioned_p (stack_pointer_rtx, PATTERN (insn)))) { if (last_sp_set && last_sp_adjust == 0) { force_move_args_size_note (bb, last2_sp_set, last_sp_set); delete_insn (last_sp_set); } free_csa_reflist (reflist); reflist = NULL; last2_sp_set = NULL_RTX; last_sp_set = NULL_RTX; last_sp_adjust = 0; } } if (last_sp_set && last_sp_adjust == 0) { force_move_args_size_note (bb, last2_sp_set, last_sp_set); delete_insn (last_sp_set); } if (reflist) free_csa_reflist (reflist); }
void sdbout_symbol (tree decl, int local) { tree type = TREE_TYPE (decl); tree context = NULL_TREE; rtx value; int regno = -1; const char *name; /* If we are called before sdbout_init is run, just save the symbol for later. */ if (!sdbout_initialized) { preinit_symbols = tree_cons (0, decl, preinit_symbols); return; } sdbout_one_type (type); switch (TREE_CODE (decl)) { case CONST_DECL: /* Enum values are defined by defining the enum type. */ return; case FUNCTION_DECL: /* Don't mention a nested function under its parent. */ context = decl_function_context (decl); if (context == current_function_decl) return; /* Check DECL_INITIAL to distinguish declarations from definitions. Don't output debug info here for declarations; they will have a DECL_INITIAL value of 0. */ if (! DECL_INITIAL (decl)) return; if (!MEM_P (DECL_RTL (decl)) || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF) return; PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); PUT_SDB_VAL (XEXP (DECL_RTL (decl), 0)); PUT_SDB_SCL (TREE_PUBLIC (decl) ? C_EXT : C_STAT); break; case TYPE_DECL: /* Done with tagged types. */ if (DECL_NAME (decl) == 0) return; if (DECL_IGNORED_P (decl)) return; /* Don't output intrinsic types. GAS chokes on SDB .def statements that contain identifiers with embedded spaces (eg "unsigned long"). */ if (DECL_IS_BUILTIN (decl)) return; /* Output typedef name. */ if (template_name_p (DECL_NAME (decl))) PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); else PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_NAME (decl))); PUT_SDB_SCL (C_TPDEF); break; case PARM_DECL: /* Parm decls go in their own separate chains and are output by sdbout_reg_parms and sdbout_parms. */ gcc_unreachable (); case VAR_DECL: /* Don't mention a variable that is external. Let the file that defines it describe it. */ if (DECL_EXTERNAL (decl)) return; /* Ignore __FUNCTION__, etc. */ if (DECL_IGNORED_P (decl)) return; /* If there was an error in the declaration, don't dump core if there is no RTL associated with the variable doesn't exist. */ if (!DECL_RTL_SET_P (decl)) return; SET_DECL_RTL (decl, eliminate_regs (DECL_RTL (decl), VOIDmode, NULL_RTX)); #ifdef LEAF_REG_REMAP if (crtl->uses_only_leaf_regs) leaf_renumber_regs_insn (DECL_RTL (decl)); #endif value = DECL_RTL (decl); /* Don't mention a variable at all if it was completely optimized into nothingness. If DECL was from an inline function, then its rtl is not identically the rtl that was used in this particular compilation. */ if (REG_P (value)) { regno = REGNO (value); if (regno >= FIRST_PSEUDO_REGISTER) return; } else if (GET_CODE (value) == SUBREG) { while (GET_CODE (value) == SUBREG) value = SUBREG_REG (value); if (REG_P (value)) { if (REGNO (value) >= FIRST_PSEUDO_REGISTER) return; } regno = REGNO (alter_subreg (&value)); SET_DECL_RTL (decl, value); } /* Don't output anything if an auto variable gets RTL that is static. GAS version 2.2 can't handle such output. */ else if (MEM_P (value) && CONSTANT_P (XEXP (value, 0)) && ! TREE_STATIC (decl)) return; /* Emit any structure, union, or enum type that has not been output. This occurs for tag-less structs (et al) used to declare variables within functions. */ if (TREE_CODE (type) == ENUMERAL_TYPE || TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == QUAL_UNION_TYPE) { if (COMPLETE_TYPE_P (type) /* not a forward reference */ && KNOWN_TYPE_TAG (type) == 0) /* not yet declared */ sdbout_one_type (type); } /* Defer SDB information for top-level initialized variables! */ if (! local && MEM_P (value) && DECL_INITIAL (decl)) return; /* C++ in 2.3 makes nameless symbols. That will be fixed later. For now, avoid crashing. */ if (DECL_NAME (decl) == NULL_TREE) return; /* Record the name for, starting a symtab entry. */ if (local) name = IDENTIFIER_POINTER (DECL_NAME (decl)); else name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); if (MEM_P (value) && GET_CODE (XEXP (value, 0)) == SYMBOL_REF) { PUT_SDB_DEF (name); if (TREE_PUBLIC (decl)) { PUT_SDB_VAL (XEXP (value, 0)); PUT_SDB_SCL (C_EXT); } else { PUT_SDB_VAL (XEXP (value, 0)); PUT_SDB_SCL (C_STAT); } } else if (regno >= 0) { PUT_SDB_DEF (name); PUT_SDB_INT_VAL (DBX_REGISTER_NUMBER (regno)); PUT_SDB_SCL (C_REG); } else if (MEM_P (value) && (MEM_P (XEXP (value, 0)) || (REG_P (XEXP (value, 0)) && REGNO (XEXP (value, 0)) != HARD_FRAME_POINTER_REGNUM && REGNO (XEXP (value, 0)) != STACK_POINTER_REGNUM))) /* If the value is indirect by memory or by a register that isn't the frame pointer then it means the object is variable-sized and address through that register or stack slot. COFF has no way to represent this so all we can do is output the variable as a pointer. */ { PUT_SDB_DEF (name); if (REG_P (XEXP (value, 0))) { PUT_SDB_INT_VAL (DBX_REGISTER_NUMBER (REGNO (XEXP (value, 0)))); PUT_SDB_SCL (C_REG); } else { /* DECL_RTL looks like (MEM (MEM (PLUS (REG...) (CONST_INT...)))). We want the value of that CONST_INT. */ /* Encore compiler hates a newline in a macro arg, it seems. */ PUT_SDB_INT_VAL (DEBUGGER_AUTO_OFFSET (XEXP (XEXP (value, 0), 0))); PUT_SDB_SCL (C_AUTO); } /* Effectively do build_pointer_type, but don't cache this type, since it might be temporary whereas the type it points to might have been saved for inlining. */ /* Don't use REFERENCE_TYPE because dbx can't handle that. */ type = make_node (POINTER_TYPE); TREE_TYPE (type) = TREE_TYPE (decl); } else if (MEM_P (value) && ((GET_CODE (XEXP (value, 0)) == PLUS && REG_P (XEXP (XEXP (value, 0), 0)) && CONST_INT_P (XEXP (XEXP (value, 0), 1))) /* This is for variables which are at offset zero from the frame pointer. This happens on the Alpha. Non-frame pointer registers are excluded above. */ || (REG_P (XEXP (value, 0))))) { /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...))) or (MEM (REG...)). We want the value of that CONST_INT or zero. */ PUT_SDB_DEF (name); PUT_SDB_INT_VAL (DEBUGGER_AUTO_OFFSET (XEXP (value, 0))); PUT_SDB_SCL (C_AUTO); } else { /* It is something we don't know how to represent for SDB. */ return; } break; default: break; } PUT_SDB_TYPE (plain_type (type)); PUT_SDB_ENDEF; }