📄 function.c
字号:
}/* Free all temporary slots used in T, an RTL_EXPR node. */voidfree_temps_for_rtl_expr (t) tree t;{ struct temp_slot *p; for (p = temp_slots; p; p = p->next) if (p->rtl_expr == t) p->in_use = 0; combine_temp_slots ();}/* Mark all temporaries ever allocated in this function as not suitable for reuse until the current level is exited. */voidmark_all_temps_used (){ struct temp_slot *p; for (p = temp_slots; p; p = p->next) { p->in_use = p->keep = 1; p->level = MIN (p->level, temp_slot_level); }}/* Push deeper into the nesting level for stack temporaries. */voidpush_temp_slots (){ temp_slot_level++;}/* Pop a temporary nesting level. All slots in use in the current level are freed. */voidpop_temp_slots (){ struct temp_slot *p; for (p = temp_slots; p; p = p->next) if (p->in_use && p->level == temp_slot_level && p->rtl_expr == 0) p->in_use = 0; combine_temp_slots (); temp_slot_level--;}/* Initialize temporary slots. */voidinit_temp_slots (){ /* We have not allocated any temporaries yet. */ temp_slots = 0; temp_slot_level = 0; target_temp_slot_level = 0;}/* Retroactively move an auto variable from a register to a stack slot. This is done when an address-reference to the variable is seen. */voidput_var_into_stack (decl) tree decl;{ register rtx reg; enum machine_mode promoted_mode, decl_mode; struct function *function = 0; tree context; int can_use_addressof; if (output_bytecode) return; context = decl_function_context (decl); /* Get the current rtl used for this object and it's original mode. */ reg = TREE_CODE (decl) == SAVE_EXPR ? SAVE_EXPR_RTL (decl) : DECL_RTL (decl); /* No need to do anything if decl has no rtx yet since in that case caller is setting TREE_ADDRESSABLE and a stack slot will be assigned when the rtl is made. */ if (reg == 0) return; /* Get the declared mode for this object. */ decl_mode = (TREE_CODE (decl) == SAVE_EXPR ? TYPE_MODE (TREE_TYPE (decl)) : DECL_MODE (decl)); /* Get the mode it's actually stored in. */ promoted_mode = GET_MODE (reg); /* If this variable comes from an outer function, find that function's saved context. */ if (context != current_function_decl && context != inline_function_decl) for (function = outer_function_chain; function; function = function->next) if (function->decl == context) break; /* If this is a variable-size object with a pseudo to address it, put that pseudo into the stack, if the var is nonlocal. */ if (DECL_NONLOCAL (decl) && GET_CODE (reg) == MEM && GET_CODE (XEXP (reg, 0)) == REG && REGNO (XEXP (reg, 0)) > LAST_VIRTUAL_REGISTER) { reg = XEXP (reg, 0); decl_mode = promoted_mode = GET_MODE (reg); } can_use_addressof = (function == 0 /* FIXME make it work for promoted modes too */ && decl_mode == promoted_mode#ifdef NON_SAVING_SETJMP && ! (NON_SAVING_SETJMP && current_function_calls_setjmp)#endif ); /* If we can't use ADDRESSOF, make sure we see through one we already generated. */ if (! can_use_addressof && GET_CODE (reg) == MEM && GET_CODE (XEXP (reg, 0)) == ADDRESSOF) reg = XEXP (XEXP (reg, 0), 0); /* Now we should have a value that resides in one or more pseudo regs. */ if (GET_CODE (reg) == REG) { /* If this variable lives in the current function and we don't need to put things in the stack for the sake of setjmp, try to keep it in a register until we know we actually need the address. */ if (can_use_addressof) gen_mem_addressof (reg, decl); else put_reg_into_stack (function, reg, TREE_TYPE (decl), promoted_mode, decl_mode, TREE_SIDE_EFFECTS (decl), 0); } else if (GET_CODE (reg) == CONCAT) { /* A CONCAT contains two pseudos; put them both in the stack. We do it so they end up consecutive. */ enum machine_mode part_mode = GET_MODE (XEXP (reg, 0)); tree part_type = TREE_TYPE (TREE_TYPE (decl));#ifdef FRAME_GROWS_DOWNWARD /* Since part 0 should have a lower address, do it second. */ put_reg_into_stack (function, XEXP (reg, 1), part_type, part_mode, part_mode, TREE_SIDE_EFFECTS (decl), 0); put_reg_into_stack (function, XEXP (reg, 0), part_type, part_mode, part_mode, TREE_SIDE_EFFECTS (decl), 0);#else put_reg_into_stack (function, XEXP (reg, 0), part_type, part_mode, part_mode, TREE_SIDE_EFFECTS (decl), 0); put_reg_into_stack (function, XEXP (reg, 1), part_type, part_mode, part_mode, TREE_SIDE_EFFECTS (decl), 0);#endif /* Change the CONCAT into a combined MEM for both parts. */ PUT_CODE (reg, MEM); MEM_VOLATILE_P (reg) = MEM_VOLATILE_P (XEXP (reg, 0)); /* The two parts are in memory order already. Use the lower parts address as ours. */ XEXP (reg, 0) = XEXP (XEXP (reg, 0), 0); /* Prevent sharing of rtl that might lose. */ if (GET_CODE (XEXP (reg, 0)) == PLUS) XEXP (reg, 0) = copy_rtx (XEXP (reg, 0)); } else return; if (flag_check_memory_usage) emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3, XEXP (reg, 0), ptr_mode, GEN_INT (GET_MODE_SIZE (GET_MODE (reg))), TYPE_MODE (sizetype), GEN_INT (MEMORY_USE_RW), TYPE_MODE (integer_type_node));}/* Subroutine of put_var_into_stack. This puts a single pseudo reg REG into the stack frame of FUNCTION (0 means the current function). DECL_MODE is the machine mode of the user-level data type. PROMOTED_MODE is the machine mode of the register. VOLATILE_P is nonzero if this is for a "volatile" decl. */static voidput_reg_into_stack (function, reg, type, promoted_mode, decl_mode, volatile_p, original_regno) struct function *function; rtx reg; tree type; enum machine_mode promoted_mode, decl_mode; int volatile_p; int original_regno;{ rtx new = 0; int regno = original_regno; if (regno == 0) regno = REGNO (reg); if (function) { if (regno < function->max_parm_reg) new = function->parm_reg_stack_loc[regno]; if (new == 0) new = assign_outer_stack_local (decl_mode, GET_MODE_SIZE (decl_mode), 0, function); } else { if (regno < max_parm_reg) new = parm_reg_stack_loc[regno]; if (new == 0) new = assign_stack_local (decl_mode, GET_MODE_SIZE (decl_mode), 0); } PUT_MODE (reg, decl_mode); XEXP (reg, 0) = XEXP (new, 0); /* `volatil' bit means one thing for MEMs, another entirely for REGs. */ MEM_VOLATILE_P (reg) = volatile_p; PUT_CODE (reg, MEM); /* If this is a memory ref that contains aggregate components, mark it as such for cse and loop optimize. */ MEM_IN_STRUCT_P (reg) = AGGREGATE_TYPE_P (type); /* Now make sure that all refs to the variable, previously made when it was a register, are fixed up to be valid again. */ if (function) { struct var_refs_queue *temp; /* Variable is inherited; fix it up when we get back to its function. */ push_obstacks (function->function_obstack, function->function_maybepermanent_obstack); /* See comment in restore_tree_status in tree.c for why this needs to be on saveable obstack. */ temp = (struct var_refs_queue *) savealloc (sizeof (struct var_refs_queue)); temp->modified = reg; temp->promoted_mode = promoted_mode; temp->unsignedp = TREE_UNSIGNED (type); temp->next = function->fixup_var_refs_queue; function->fixup_var_refs_queue = temp; pop_obstacks (); } else /* Variable is local; fix it up now. */ fixup_var_refs (reg, promoted_mode, TREE_UNSIGNED (type));}static voidfixup_var_refs (var, promoted_mode, unsignedp) rtx var; enum machine_mode promoted_mode; int unsignedp;{ tree pending; rtx first_insn = get_insns (); struct sequence_stack *stack = sequence_stack; tree rtl_exps = rtl_expr_chain; /* Must scan all insns for stack-refs that exceed the limit. */ fixup_var_refs_insns (var, promoted_mode, unsignedp, first_insn, stack == 0); /* Scan all pending sequences too. */ for (; stack; stack = stack->next) { push_to_sequence (stack->first); fixup_var_refs_insns (var, promoted_mode, unsignedp, stack->first, stack->next != 0); /* Update remembered end of sequence in case we added an insn at the end. */ stack->last = get_last_insn (); end_sequence (); } /* Scan all waiting RTL_EXPRs too. */ for (pending = rtl_exps; pending; pending = TREE_CHAIN (pending)) { rtx seq = RTL_EXPR_SEQUENCE (TREE_VALUE (pending)); if (seq != const0_rtx && seq != 0) { push_to_sequence (seq); fixup_var_refs_insns (var, promoted_mode, unsignedp, seq, 0); end_sequence (); } }}/* REPLACEMENTS is a pointer to a list of the struct fixup_replacement and X is some part of an insn. Return a struct fixup_replacement whose OLD value is equal to X. Allocate a new structure if no such entry exists. */static struct fixup_replacement *find_fixup_replacement (replacements, x) struct fixup_replacement **replacements; rtx x;{ struct fixup_replacement *p; /* See if we have already replaced this. */ for (p = *replacements; p && p->old != x; p = p->next) ; if (p == 0) { p = (struct fixup_replacement *) oballoc (sizeof (struct fixup_replacement)); p->old = x; p->new = 0; p->next = *replacements; *replacements = p; } return p;}/* Scan the insn-chain starting with INSN for refs to VAR and fix them up. TOPLEVEL is nonzero if this chain is the main chain of insns for the current function. */static voidfixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel) rtx var; enum machine_mode promoted_mode; int unsignedp; rtx insn; int toplevel;{ rtx call_dest = 0; while (insn) { rtx next = NEXT_INSN (insn); rtx note; if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') { /* If this is a CLOBBER of VAR, delete it. If it has a REG_LIBCALL note, delete the REG_LIBCALL and REG_RETVAL notes too. */ if (GET_CODE (PATTERN (insn)) == CLOBBER && XEXP (PATTERN (insn), 0) == var) { if ((note = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) != 0) /* The REG_LIBCALL note will go away since we are going to turn INSN into a NOTE, so just delete the corresponding REG_RETVAL note. */ remove_note (XEXP (note, 0), find_reg_note (XEXP (note, 0), REG_RETVAL, NULL_RTX)); /* In unoptimized compilation, we shouldn't call delete_insn except in jump.c doing warnings. */ PUT_CODE (insn, NOTE); NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; NOTE_SOURCE_FILE (insn) = 0; } /* The insn to load VAR from a home in the arglist is now a no-op. When we see it, just delete it. */ else if (toplevel && GET_CODE (PATTERN (insn)) == SET && SET_DEST (PATTERN (insn)) == var /* If this represents the result of an insn group, don't delete the insn. */ && find_reg_note (insn, REG_RETVAL, NULL_RTX) == 0 && rtx_equal_p (SET_SRC (PATTERN (insn)), var)) { /* In unoptimized compilation, we shouldn't call delete_insn except in jump.c doing warnings. */ PUT_CODE (insn, NOTE); NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; NOTE_SOURCE_FILE (insn) = 0; if (insn == last_parm_insn) last_parm_insn = PREV_INSN (next); } else { struct fixup_replacement *replacements = 0; rtx next_insn = NEXT_INSN (insn); if (SMALL_REGISTER_CLASSES) { /* If the insn that copies the results of a CALL_INSN into a pseudo now references VAR, we have to use an intermediate pseudo since we want the life of the return value register to be only a single insn. If we don't use an intermediate pseudo, such things as address computations to make the address of VAR valid if it is not can be placed between the CALL_INSN and INSN. To make sure this doesn't happen, we record the destination of the CALL_INSN and see if the next insn uses both that and VAR. */ if (call_dest != 0 && GET_CODE (insn) == INSN
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -