📄 calls.c
字号:
/* Convert function calls to rtl insns, for GNU C compiler. Copyright (C) 1989, 1992 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, 675 Mass Ave, Cambridge, MA 02139, USA. */#include "config.h"#include "rtl.h"#include "tree.h"#include "flags.h"#include "expr.h"#include "insn-flags.h"/* Decide whether a function's arguments should be processed from first to last or from last to first. */#ifdef STACK_GROWS_DOWNWARD#ifdef PUSH_ROUNDING#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; /* 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 EXPR_LIST if the arg is to be copied into multiple different 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};#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 void store_one_arg ();extern enum machine_mode mode_for_size ();/* Return 1 if EXP contains a call to the built-in function `alloca'. */static intcalls_alloca (exp) tree exp;{ register int i; int type = TREE_CODE_CLASS (TREE_CODE (exp)); int length = tree_code_length[(int) TREE_CODE (exp)]; /* Only expressions and references can contain calls. */ if (type != 'e' && type != '<' && type != '1' && type != '2' && type != 'r' && type != 'b') return 0; switch (TREE_CODE (exp)) { case CALL_EXPR: if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == FUNCTION_DECL) && DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) && (DECL_FUNCTION_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == BUILT_IN_ALLOCA)) return 1; /* Third operand is RTL. */ length = 2; break; case SAVE_EXPR: if (SAVE_EXPR_RTL (exp) != 0) return 0; break; case BLOCK: { register tree local; for (local = BLOCK_VARS (exp); local; local = TREE_CHAIN (local)) if (DECL_INITIAL (local) != 0 && calls_alloca (DECL_INITIAL (local))) return 1; } { register tree subblock; for (subblock = BLOCK_SUBBLOCKS (exp); subblock; subblock = TREE_CHAIN (subblock)) if (calls_alloca (subblock)) return 1; } return 0; case METHOD_CALL_EXPR: length = 3; break; case WITH_CLEANUP_EXPR: length = 1; break; case RTL_EXPR: return 0; } for (i = 0; i < length; i++) if (TREE_OPERAND (exp, i) != 0 && calls_alloca (TREE_OPERAND (exp, i))) 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. USE_INSNS points to a variable holding a chain of USE insns to which a USE of the static chain register should be added, if required. */rtxprepare_call_address (funexp, fndecl, use_insns) rtx funexp; tree fndecl; rtx *use_insns;{ 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) 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); /* Put the USE insn in the chain we were passed. It will later be output immediately in front of the CALL insn. */ push_to_sequence (*use_insns); emit_insn (gen_rtx (USE, VOIDmode, static_chain_rtx)); *use_insns = get_insns (); end_sequence (); } return funexp;}/* Generate instructions to call function FUNEXP, and optionally pop the results. The CALL_INSN is the first insn generated. FUNTYPE is the data type of the function, or, for a library call, the identifier for the name of the call. This is given to the macro RETURN_POPS_ARGS to determine whether this function pops its own args. 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. USE_INSNS is a chain of USE insns to be emitted immediately before the actual CALL insn. IS_CONST is true if this is a `const' call. */voidemit_call_1 (funexp, funtype, stack_size, struct_value_size, next_arg_reg, valreg, old_inhibit_defer_pop, use_insns, is_const) rtx funexp; tree funtype; int stack_size; int struct_value_size; rtx next_arg_reg; rtx valreg; int old_inhibit_defer_pop; rtx use_insns; 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 (funtype, stack_size) > 0 || stack_size == 0)) { rtx n_pop = GEN_INT (RETURN_POPS_ARGS (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#endif#endif#if defined (HAVE_call) && defined (HAVE_call_value) if (HAVE_call && HAVE_call_value) { if (valreg) emit_call_insn (gen_call_value (valreg, gen_rtx (MEM, FUNCTION_MODE, funexp), stack_size_rtx, next_arg_reg)); else emit_call_insn (gen_call (gen_rtx (MEM, FUNCTION_MODE, funexp), stack_size_rtx, next_arg_reg, struct_value_size_rtx)); } else#endif abort (); /* Find the CALL insn we just emitted and write the USE insns before it. */ for (call_insn = get_last_insn (); call_insn && GET_CODE (call_insn) != CALL_INSN; call_insn = PREV_INSN (call_insn)) ; if (! call_insn) abort (); /* Put the USE insns before the CALL. */ emit_insns_before (use_insns, call_insn); /* If this is a const call, then set the insn's unchanging bit. */ if (is_const) CONST_CALL_P (call_insn) = 1;#ifndef ACCUMULATE_OUTGOING_ARGS /* If returning from the subroutine does not automatically pop the args, we need an instruction to pop them sooner or later. Perhaps do it now; perhaps just record how much space to pop later. If returning from the subroutine does pop the args, indicate that the stack pointer will be changed. */ if (stack_size != 0 && RETURN_POPS_ARGS (funtype, stack_size) > 0) { if (!already_popped) emit_insn (gen_rtx (CLOBBER, VOIDmode, stack_pointer_rtx)); stack_size -= RETURN_POPS_ARGS (funtype, stack_size); stack_size_rtx = GEN_INT (stack_size); } if (stack_size != 0) { if (flag_defer_pop && inhibit_defer_pop == 0) pending_stack_adjust += stack_size; else adjust_stack (stack_size_rtx); }#endif inhibit_defer_pop = old_inhibit_defer_pop;}/* Generate all the code for a function call and return an rtx for its value. Store the value in TARGET (specified as an rtx) if convenient. If the value is stored in TARGET then TARGET is returned. If IGNORE is nonzero, then we ignore the value of the function call. */rtxexpand_call (exp, target, ignore) tree exp; rtx target; int ignore;{ /* List of actual parameters. */ tree actparms = TREE_OPERAND (exp, 1); /* RTX for the function to be called. */ rtx funexp; /* Tree node for the function to be called (not the address!). */ tree funtree; /* Data type of the function. */ tree funtype;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -