📄 integrate.c
字号:
} /* Make a binding contour to keep inline cleanups called at outer function-scope level from looking like they are shadowing parameter declarations. */ pushlevel (0); /* Make a fresh binding contour that we can easily remove. */ pushlevel (0); expand_start_bindings (0); if (GET_CODE (parm_insns) == NOTE && NOTE_LINE_NUMBER (parm_insns) > 0) { rtx note = emit_note (NOTE_SOURCE_FILE (parm_insns), NOTE_LINE_NUMBER (parm_insns)); if (note) RTX_INTEGRATED_P (note) = 1; } /* Expand the function arguments. Do this first so that any new registers get created before we allocate the maps. */ arg_vals = (rtx *) alloca (nargs * sizeof (rtx)); arg_trees = (tree *) alloca (nargs * sizeof (tree)); for (formal = DECL_ARGUMENTS (fndecl), actual = parms, i = 0; formal; formal = TREE_CHAIN (formal), actual = TREE_CHAIN (actual), i++) { /* Actual parameter, converted to the type of the argument within the function. */ tree arg = convert (TREE_TYPE (formal), TREE_VALUE (actual)); /* Mode of the variable used within the function. */ enum machine_mode mode = TYPE_MODE (TREE_TYPE (formal)); /* Where parameter is located in the function. */ rtx copy; /* Make sure this formal has some correspondence in the users code * before emitting any line notes for it. */ if (DECL_SOURCE_LINE (formal)) { rtx note = emit_note (DECL_SOURCE_FILE (formal), DECL_SOURCE_LINE (formal)); if (note) RTX_INTEGRATED_P (note) = 1; } arg_trees[i] = arg; loc = RTVEC_ELT (arg_vector, i); /* If this is an object passed by invisible reference, we copy the object into a stack slot and save its address. If this will go into memory, we do nothing now. Otherwise, we just expand the argument. */ if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER) { rtx stack_slot = assign_stack_temp (TYPE_MODE (TREE_TYPE (arg)), int_size_in_bytes (TREE_TYPE (arg)), 1); store_expr (arg, stack_slot, 0); arg_vals[i] = XEXP (stack_slot, 0); } else if (GET_CODE (loc) != MEM) /* The mode if LOC and ARG can differ if LOC was a variable that had its mode promoted via PROMOTED_MODE. */ arg_vals[i] = convert_to_mode (GET_MODE (loc), expand_expr (arg, NULL_RTX, mode, EXPAND_SUM), TREE_UNSIGNED (TREE_TYPE (formal))); else arg_vals[i] = 0; if (arg_vals[i] != 0 && (! TREE_READONLY (formal) /* If the parameter is not read-only, copy our argument through a register. Also, we cannot use ARG_VALS[I] if it overlaps TARGET in any way. In the inline function, they will likely be two different pseudos, and `safe_from_p' will make all sorts of smart assumptions about their not conflicting. But if ARG_VALS[I] overlaps TARGET, these assumptions are wrong, so put ARG_VALS[I] into a fresh register. */ || (target != 0 && (GET_CODE (arg_vals[i]) == REG || GET_CODE (arg_vals[i]) == SUBREG || GET_CODE (arg_vals[i]) == MEM) && reg_overlap_mentioned_p (arg_vals[i], target)))) arg_vals[i] = copy_to_mode_reg (GET_MODE (loc), arg_vals[i]); } /* Allocate the structures we use to remap things. */ map = (struct inline_remap *) alloca (sizeof (struct inline_remap)); map->fndecl = fndecl; map->reg_map = (rtx *) alloca (max_regno * sizeof (rtx)); bzero (map->reg_map, max_regno * sizeof (rtx)); map->label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx)); map->label_map -= min_labelno; map->insn_map = (rtx *) alloca (INSN_UID (header) * sizeof (rtx)); bzero (map->insn_map, INSN_UID (header) * sizeof (rtx)); map->min_insnno = 0; map->max_insnno = INSN_UID (header); /* const_equiv_map maps pseudos in our routine to constants, so it needs to be large enough for all our pseudos. This is the number we are currently using plus the number in the called routine, plus 15 for each arg, five to compute the virtual frame pointer, and five for the return value. This should be enough for most cases. We do not reference entries outside the range of the map. ??? These numbers are quite arbitrary and were obtained by experimentation. At some point, we should try to allocate the table after all the parameters are set up so we an more accurately estimate the number of pseudos we will need. */ map->const_equiv_map_size = max_reg_num () + (max_regno - FIRST_PSEUDO_REGISTER) + 15 * nargs + 10; map->const_equiv_map = (rtx *)alloca (map->const_equiv_map_size * sizeof (rtx)); bzero (map->const_equiv_map, map->const_equiv_map_size * sizeof (rtx)); map->const_age_map = (unsigned *)alloca (map->const_equiv_map_size * sizeof (unsigned)); bzero (map->const_age_map, map->const_equiv_map_size * sizeof (unsigned)); map->const_age = 0; /* Record the current insn in case we have to set up pointers to frame and argument memory blocks. */ map->insns_at_start = get_last_insn (); /* Update the outgoing argument size to allow for those in the inlined function. */ if (OUTGOING_ARGS_SIZE (header) > current_function_outgoing_args_size) current_function_outgoing_args_size = OUTGOING_ARGS_SIZE (header); /* If the inline function needs to make PIC references, that means that this function's PIC offset table must be used. */ if (FUNCTION_FLAGS (header) & FUNCTION_FLAGS_USES_PIC_OFFSET_TABLE) current_function_uses_pic_offset_table = 1; /* Process each argument. For each, set up things so that the function's reference to the argument will refer to the argument being passed. We only replace REG with REG here. Any simplifications are done via const_equiv_map. We make two passes: In the first, we deal with parameters that will be placed into registers, since we need to ensure that the allocated register number fits in const_equiv_map. Then we store all non-register parameters into their memory location. */ for (i = 0; i < nargs; i++) { rtx copy = arg_vals[i]; loc = RTVEC_ELT (arg_vector, i); /* There are three cases, each handled separately. */ if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER) { /* This must be an object passed by invisible reference (it could also be a variable-sized object, but we forbid inlining functions with variable-sized arguments). COPY is the address of the actual value (this computation will cause it to be copied). We map that address for the register, noting the actual address as an equivalent in case it can be substituted into the insns. */ if (GET_CODE (copy) != REG) { temp = copy_addr_to_reg (copy); if (CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy)) { map->const_equiv_map[REGNO (temp)] = copy; map->const_age_map[REGNO (temp)] = CONST_AGE_PARM; } copy = temp; } map->reg_map[REGNO (XEXP (loc, 0))] = copy; } else if (GET_CODE (loc) == MEM) { /* This is the case of a parameter that lives in memory. It will live in the block we allocate in the called routine's frame that simulates the incoming argument area. Do nothing now; we will call store_expr later. */ ; } else if (GET_CODE (loc) == REG) { /* This is the good case where the parameter is in a register. If it is read-only and our argument is a constant, set up the constant equivalence. If LOC is REG_USERVAR_P, the usual case, COPY must also have that flag set if it is a register. */ if ((GET_CODE (copy) != REG && GET_CODE (copy) != SUBREG) || (GET_CODE (copy) == REG && REG_USERVAR_P (loc) && ! REG_USERVAR_P (copy))) { temp = copy_to_mode_reg (GET_MODE (loc), copy); REG_USERVAR_P (temp) = REG_USERVAR_P (loc); if (CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy)) { map->const_equiv_map[REGNO (temp)] = copy; map->const_age_map[REGNO (temp)] = CONST_AGE_PARM; } copy = temp; } map->reg_map[REGNO (loc)] = copy; } else abort (); /* Free any temporaries we made setting up this parameter. */ free_temp_slots (); } /* Now do the parameters that will be placed in memory. */ for (formal = DECL_ARGUMENTS (fndecl), i = 0; formal; formal = TREE_CHAIN (formal), i++) { rtx copy = arg_vals[i]; loc = RTVEC_ELT (arg_vector, i); if (GET_CODE (loc) == MEM /* Exclude case handled above. */ && ! (GET_CODE (XEXP (loc, 0)) == REG && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER)) { rtx note = emit_note (DECL_SOURCE_FILE (formal), DECL_SOURCE_LINE (formal)); if (note) RTX_INTEGRATED_P (note) = 1; /* Compute the address in the area we reserved and store the value there. */ temp = copy_rtx_and_substitute (loc, map); subst_constants (&temp, NULL_RTX, map); apply_change_group (); if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0))) temp = change_address (temp, VOIDmode, XEXP (temp, 0)); store_expr (arg_trees[i], temp, 0); /* Free any temporaries we made setting up this parameter. */ free_temp_slots (); } } /* Deal with the places that the function puts its result. We are driven by what is placed into DECL_RESULT. Initially, we assume that we don't have anything special handling for REG_FUNCTION_RETURN_VALUE_P. */ map->inline_target = 0; loc = DECL_RTL (DECL_RESULT (fndecl)); if (TYPE_MODE (type) == VOIDmode) /* There is no return value to worry about. */ ; else if (GET_CODE (loc) == MEM) { if (! structure_value_addr || ! aggregate_value_p (DECL_RESULT (fndecl))) abort (); /* Pass the function the address in which to return a structure value. Note that a constructor can cause someone to call us with STRUCTURE_VALUE_ADDR, but the initialization takes place via the first parameter, rather than the struct return address. We have two cases: If the address is a simple register indirect, use the mapping mechanism to point that register to our structure return address. Otherwise, store the structure return value into the place that it will be referenced from. */ if (GET_CODE (XEXP (loc, 0)) == REG) { temp = force_reg (Pmode, structure_value_addr); map->reg_map[REGNO (XEXP (loc, 0))] = temp; if (CONSTANT_P (structure_value_addr) || (GET_CODE (structure_value_addr) == PLUS && XEXP (structure_value_addr, 0) == virtual_stack_vars_rtx && GET_CODE (XEXP (structure_value_addr, 1)) == CONST_INT)) { map->const_equiv_map[REGNO (temp)] = structure_value_addr; map->const_age_map[REGNO (temp)] = CONST_AGE_PARM; } } else { temp = copy_rtx_and_substitute (loc, map); subst_constants (&temp, NULL_RTX, map); apply_change_group (); emit_move_insn (temp, structure_value_addr); } } else if (ignore) /* We will ignore the result value, so don't look at its structure. Note that preparations for an aggregate return value do need to be made (above) even if it will be ignored. */ ; else if (GET_CODE (loc) == REG) { /* The function returns an object in a register and we use the return value. Set up our target for remapping. */ /* Machine mode function was declared to return. */ enum machine_mode departing_mode = TYPE_MODE (type); /* (Possibly wider) machine mode it actually computes (for the sake of callers that fail to declare it right). */ enum machine_mode arriving_mode = TYPE_MODE (TREE_TYPE (DECL_RESULT (fndecl))); rtx reg_to_map; /* Don't use MEMs as direct targets because on some machines substituting a MEM for a REG makes invalid insns. Let the combiner substitute the MEM if that is valid. */ if (target == 0 || GET_CODE (target) != REG || GET_MODE (target) != departing_mode) target = gen_reg_rtx (departing_mode); /* If function's value was promoted before return, avoid machine mode mismatch when we substitute INLINE_TARGET. But TARGET is what we will return to the caller. */ if (arriving_mode != departing_mode) reg_to_map = gen_rtx (SUBREG, arriving_mode, target, 0); else reg_to_map = target; /* Usually, the result value is the machine's return register. Sometimes it may be a pseudo. Handle both cases. */ if (REG_FUNCTION_VALUE_P (loc)) map->inline_target = reg_to_map; else map->reg_map[REGNO (loc)] = reg_to_map; } /* Make new label equivalences for the labels in the called function. */ for (i = min_labelno; i < max_labelno; i++) map->label_map[i] = gen_label_rtx (); /* Perform postincrements before actually calling the function. */ emit_queue (); /* Clean up stack so that variables might have smaller offsets. */ do_pending_stack_adjust (); /* Save a copy of the location of const_equiv_map for mark_stores, called via note_stores. */ global_const_equiv_map = map->const_equiv_map; /* Now copy the insns one by one. Do this in two passes, first the insns and then their REG_NOTES, just like save_for_inline. */ /* This loop is very similar to the loop in copy_loop_body in unroll.c. */ for (insn = insns; insn; insn = NEXT_INSN (insn)) { rtx copy, pattern; map->orig_asm_operands_vector = 0; switch (GET_CODE (insn)) { case INSN: pattern = PATTERN (insn); copy = 0; if (GET_CODE (pattern) == USE && GET_CODE (XEXP (pattern, 0)) == REG && REG_FUNCTION_VALUE_P (XEXP (pattern, 0))) /* The (USE (REG n)) at return from the function should be ignored since we are changing (REG n) into inline_target. */ break; /* Ignore setting a function value that we don't want to use. */ if (map->inline_target == 0 && GET_CODE (pattern) == SET && GET_CODE (SET_DEST (pattern)) == REG && REG_FUNCTION_VALUE_P (SET_DEST (pattern))) { if (volatile_refs_p (SET_SRC (pattern))) { /* If we must not delete the source, load it into a new temporary. */ copy = emit_insn (copy_rtx_and_substitute (pattern, map));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -