📄 calls.c
字号:
/* Convert function calls to rtl insns, for GNU C compiler. Copyright (C) 1989, 92, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.This file is part of GNU CC.GNU CC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2, or (at your option)any later version.GNU CC is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU CC; see the file COPYING. If not, write tothe Free Software Foundation, 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA. */#include "config.h"#include <stdio.h>#include "rtl.h"#include "tree.h"#include "flags.h"#include "expr.h"#include "regs.h"#ifdef __STDC__#include <stdarg.h>#else#include <varargs.h>#endif#include "insn-flags.h"/* Decide whether a function's arguments should be processed from first to last or from last to first. They should if the stack and args grow in opposite directions, but only if we have push insns. */#ifdef PUSH_ROUNDING#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)#define PUSH_ARGS_REVERSED /* If it's last to first */#endif#endif/* Like STACK_BOUNDARY but in units of bytes, not bits. */#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT)/* Data structure and subroutines used within expand_call. */struct arg_data{ /* Tree node for this argument. */ tree tree_value; /* Mode for value; TYPE_MODE unless promoted. */ enum machine_mode mode; /* Current RTL value for argument, or 0 if it isn't precomputed. */ rtx value; /* Initially-compute RTL value for argument; only for const functions. */ rtx initial_value; /* Register to pass this argument in, 0 if passed on stack, or an PARALLEL if the arg is to be copied into multiple non-contiguous registers. */ rtx reg; /* If REG was promoted from the actual mode of the argument expression, indicates whether the promotion is sign- or zero-extended. */ int unsignedp; /* Number of registers to use. 0 means put the whole arg in registers. Also 0 if not passed in registers. */ int partial; /* Non-zero if argument must be passed on stack. Note that some arguments may be passed on the stack even though pass_on_stack is zero, just because FUNCTION_ARG says so. pass_on_stack identifies arguments that *cannot* go in registers. */ int pass_on_stack; /* Offset of this argument from beginning of stack-args. */ struct args_size offset; /* Similar, but offset to the start of the stack slot. Different from OFFSET if this arg pads downward. */ struct args_size slot_offset; /* Size of this argument on the stack, rounded up for any padding it gets, parts of the argument passed in registers do not count. If REG_PARM_STACK_SPACE is defined, then register parms are counted here as well. */ struct args_size size; /* Location on the stack at which parameter should be stored. The store has already been done if STACK == VALUE. */ rtx stack; /* Location on the stack of the start of this argument slot. This can differ from STACK if this arg pads downward. This location is known to be aligned to FUNCTION_ARG_BOUNDARY. */ rtx stack_slot;#ifdef ACCUMULATE_OUTGOING_ARGS /* Place that this stack area has been saved, if needed. */ rtx save_area;#endif /* If an argument's alignment does not permit direct copying into registers, copy in smaller-sized pieces into pseudos. These are stored in a block pointed to by this field. The next field says how many word-sized pseudos we made. */ rtx *aligned_regs; int n_aligned_regs;};#ifdef ACCUMULATE_OUTGOING_ARGS/* A vector of one char per byte of stack space. A byte if non-zero if the corresponding stack location has been used. This vector is used to prevent a function call within an argument from clobbering any stack already set up. */static char *stack_usage_map;/* Size of STACK_USAGE_MAP. */static int highest_outgoing_arg_in_use;/* stack_arg_under_construction is nonzero when an argument may be initialized with a constructor call (including a C function that returns a BLKmode struct) and expand_call must take special action to make sure the object being constructed does not overlap the argument list for the constructor call. */int stack_arg_under_construction;#endifstatic int calls_function PROTO((tree, int));static int calls_function_1 PROTO((tree, int));static void emit_call_1 PROTO((rtx, tree, tree, int, int, rtx, rtx, int, rtx, int));static void store_one_arg PROTO ((struct arg_data *, rtx, int, int, tree, int));/* If WHICH is 1, return 1 if EXP contains a call to the built-in function `alloca'. If WHICH is 0, return 1 if EXP contains a call to any function. Actually, we only need return 1 if evaluating EXP would require pushing arguments on the stack, but that is too difficult to compute, so we just assume any function call might require the stack. */static tree calls_function_save_exprs;static intcalls_function (exp, which) tree exp; int which;{ int val; calls_function_save_exprs = 0; val = calls_function_1 (exp, which); calls_function_save_exprs = 0; return val;}static intcalls_function_1 (exp, which) tree exp; int which;{ register int i; enum tree_code code = TREE_CODE (exp); int type = TREE_CODE_CLASS (code); int length = tree_code_length[(int) code]; /* If this code is language-specific, we don't know what it will do. */ if ((int) code >= NUM_TREE_CODES) return 1; /* Only expressions and references can contain calls. */ if (type != 'e' && type != '<' && type != '1' && type != '2' && type != 'r' && type != 'b') return 0; switch (code) { case CALL_EXPR: if (which == 0) return 1; else if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == FUNCTION_DECL)) { tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); if ((DECL_BUILT_IN (fndecl) && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA) || (DECL_SAVED_INSNS (fndecl) && (FUNCTION_FLAGS (DECL_SAVED_INSNS (fndecl)) & FUNCTION_FLAGS_CALLS_ALLOCA))) return 1; } /* Third operand is RTL. */ length = 2; break; case SAVE_EXPR: if (SAVE_EXPR_RTL (exp) != 0) return 0; if (value_member (exp, calls_function_save_exprs)) return 0; calls_function_save_exprs = tree_cons (NULL_TREE, exp, calls_function_save_exprs); return (TREE_OPERAND (exp, 0) != 0 && calls_function_1 (TREE_OPERAND (exp, 0), which)); case BLOCK: { register tree local; for (local = BLOCK_VARS (exp); local; local = TREE_CHAIN (local)) if (DECL_INITIAL (local) != 0 && calls_function_1 (DECL_INITIAL (local), which)) return 1; } { register tree subblock; for (subblock = BLOCK_SUBBLOCKS (exp); subblock; subblock = TREE_CHAIN (subblock)) if (calls_function_1 (subblock, which)) return 1; } return 0; case METHOD_CALL_EXPR: length = 3; break; case WITH_CLEANUP_EXPR: length = 1; break; case RTL_EXPR: return 0; default: break; } for (i = 0; i < length; i++) if (TREE_OPERAND (exp, i) != 0 && calls_function_1 (TREE_OPERAND (exp, i), which)) return 1; return 0;}/* Force FUNEXP into a form suitable for the address of a CALL, and return that as an rtx. Also load the static chain register if FNDECL is a nested function. CALL_FUSAGE points to a variable holding the prospective CALL_INSN_FUNCTION_USAGE information. */rtxprepare_call_address (funexp, fndecl, call_fusage, reg_parm_seen) rtx funexp; tree fndecl; rtx *call_fusage; int reg_parm_seen;{ rtx static_chain_value = 0; funexp = protect_from_queue (funexp, 0); if (fndecl != 0) /* Get possible static chain value for nested function in C. */ static_chain_value = lookup_static_chain (fndecl); /* Make a valid memory address and copy constants thru pseudo-regs, but not for a constant address if -fno-function-cse. */ if (GET_CODE (funexp) != SYMBOL_REF) /* If we are using registers for parameters, force the function address into a register now. */ funexp = ((SMALL_REGISTER_CLASSES && reg_parm_seen) ? force_not_mem (memory_address (FUNCTION_MODE, funexp)) : memory_address (FUNCTION_MODE, funexp)); else {#ifndef NO_FUNCTION_CSE if (optimize && ! flag_no_function_cse)#ifdef NO_RECURSIVE_FUNCTION_CSE if (fndecl != current_function_decl)#endif funexp = force_reg (Pmode, funexp);#endif } if (static_chain_value != 0) { emit_move_insn (static_chain_rtx, static_chain_value); if (GET_CODE (static_chain_rtx) == REG) use_reg (call_fusage, static_chain_rtx); } return funexp;}/* Generate instructions to call function FUNEXP, and optionally pop the results. The CALL_INSN is the first insn generated. FNDECL is the declaration node of the function. This is given to the macro RETURN_POPS_ARGS to determine whether this function pops its own args. FUNTYPE is the data type of the function. This is given to the macro RETURN_POPS_ARGS to determine whether this function pops its own args. We used to allow an identifier for library functions, but that doesn't work when the return type is an aggregate type and the calling convention says that the pointer to this aggregate is to be popped by the callee. STACK_SIZE is the number of bytes of arguments on the stack, rounded up to STACK_BOUNDARY; zero if the size is variable. This is both to put into the call insn and to generate explicit popping code if necessary. STRUCT_VALUE_SIZE is the number of bytes wanted in a structure value. It is zero if this call doesn't want a structure value. NEXT_ARG_REG is the rtx that results from executing FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1) just after all the args have had their registers assigned. This could be whatever you like, but normally it is the first arg-register beyond those used for args in this call, or 0 if all the arg-registers are used in this call. It is passed on to `gen_call' so you can put this info in the call insn. VALREG is a hard register in which a value is returned, or 0 if the call does not return a value. OLD_INHIBIT_DEFER_POP is the value that `inhibit_defer_pop' had before the args to this call were processed. We restore `inhibit_defer_pop' to that value. CALL_FUSAGE is either empty or an EXPR_LIST of USE expressions that denote registers used by the called function. IS_CONST is true if this is a `const' call. */static voidemit_call_1 (funexp, fndecl, funtype, stack_size, struct_value_size, next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage, is_const) rtx funexp; tree fndecl; tree funtype; int stack_size; int struct_value_size; rtx next_arg_reg; rtx valreg; int old_inhibit_defer_pop; rtx call_fusage; int is_const;{ rtx stack_size_rtx = GEN_INT (stack_size); rtx struct_value_size_rtx = GEN_INT (struct_value_size); rtx call_insn; int already_popped = 0; /* Ensure address is valid. SYMBOL_REF is already valid, so no need, and we don't want to load it into a register as an optimization, because prepare_call_address already did it if it should be done. */ if (GET_CODE (funexp) != SYMBOL_REF) funexp = memory_address (FUNCTION_MODE, funexp);#ifndef ACCUMULATE_OUTGOING_ARGS#if defined (HAVE_call_pop) && defined (HAVE_call_value_pop) if (HAVE_call_pop && HAVE_call_value_pop && (RETURN_POPS_ARGS (fndecl, funtype, stack_size) > 0 || stack_size == 0)) { rtx n_pop = GEN_INT (RETURN_POPS_ARGS (fndecl, funtype, stack_size)); rtx pat; /* If this subroutine pops its own args, record that in the call insn if possible, for the sake of frame pointer elimination. */ if (valreg) pat = gen_call_value_pop (valreg, gen_rtx (MEM, FUNCTION_MODE, funexp), stack_size_rtx, next_arg_reg, n_pop); else pat = gen_call_pop (gen_rtx (MEM, FUNCTION_MODE, funexp), stack_size_rtx, next_arg_reg, n_pop); emit_call_insn (pat); already_popped = 1; } else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -