📄 calls.c
字号:
NULL_RTX, BITS_PER_UNIT); seq = get_insns (); end_sequence (); emit_insns_before (seq, first_insn); emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX); } }#endif /* If the result is equivalent to TARGET, return TARGET to simplify checks in store_expr. They can be equivalent but not equal in the case of a function that returns BLKmode. */ if (temp != target && rtx_equal_p (temp, target)) return target; return temp; } /* If inlining failed, mark FNDECL as needing to be compiled separately after all. If function was declared inline, give a warning. */ if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline && optimize > 0 && ! TREE_ADDRESSABLE (fndecl)) { warning_with_decl (fndecl, "inlining failed in call to `%s'"); warning ("called from here"); } mark_addressable (fndecl); } /* When calling a const function, we must pop the stack args right away, so that the pop is deleted or moved with the call. */ if (is_const) NO_DEFER_POP; function_call_count++; if (fndecl && DECL_NAME (fndecl)) name = IDENTIFIER_POINTER (DECL_NAME (fndecl));#if 0 /* Unless it's a call to a specific function that isn't alloca, if it has one argument, we must assume it might be alloca. */ may_be_alloca = (!(fndecl != 0 && strcmp (name, "alloca")) && actparms != 0 && TREE_CHAIN (actparms) == 0);#else /* We assume that alloca will always be called by name. It makes no sense to pass it as a pointer-to-function to anything that does not understand its behavior. */ may_be_alloca = (name && ((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6 && name[0] == 'a' && ! strcmp (name, "alloca")) || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16 && name[0] == '_' && ! strcmp (name, "__builtin_alloca"))));#endif /* See if this is a call to a function that can return more than once or a call to longjmp. */ returns_twice = 0; is_longjmp = 0; if (name != 0 && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 15) { char *tname = name; /* Disregard prefix _, __ or __x. */ if (name[0] == '_') { if (name[1] == '_' && name[2] == 'x') tname += 3; else if (name[1] == '_') tname += 2; else tname += 1; } if (tname[0] == 's') { returns_twice = ((tname[1] == 'e' && (! strcmp (tname, "setjmp") || ! strcmp (tname, "setjmp_syscall"))) || (tname[1] == 'i' && ! strcmp (tname, "sigsetjmp")) || (tname[1] == 'a' && ! strcmp (tname, "savectx"))); if (tname[1] == 'i' && ! strcmp (tname, "siglongjmp")) is_longjmp = 1; } else if ((tname[0] == 'q' && tname[1] == 's' && ! strcmp (tname, "qsetjmp")) || (tname[0] == 'v' && tname[1] == 'f' && ! strcmp (tname, "vfork"))) returns_twice = 1; else if (tname[0] == 'l' && tname[1] == 'o' && ! strcmp (tname, "longjmp")) is_longjmp = 1; } if (may_be_alloca) current_function_calls_alloca = 1; /* Don't let pending stack adjusts add up to too much. Also, do all pending adjustments now if there is any chance this might be a call to alloca. */ if (pending_stack_adjust >= 32 || (pending_stack_adjust > 0 && may_be_alloca)) do_pending_stack_adjust (); /* Operand 0 is a pointer-to-function; get the type of the function. */ funtype = TREE_TYPE (TREE_OPERAND (exp, 0)); if (TREE_CODE (funtype) != POINTER_TYPE) abort (); funtype = TREE_TYPE (funtype); /* Push the temporary stack slot level so that we can free any temporaries we make. */ push_temp_slots (); /* Start updating where the next arg would go. On some machines (such as the PA) indirect calls have a different calling convention than normal calls. The last argument in INIT_CUMULATIVE_ARGS tells the backend if this is an indirect call or not. */ INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, (fndecl == 0)); /* If struct_value_rtx is 0, it means pass the address as if it were an extra parameter. */ if (structure_value_addr && struct_value_rtx == 0) { /* If structure_value_addr is a REG other than virtual_outgoing_args_rtx, we can use always use it. If it is not a REG, we must always copy it into a register. If it is virtual_outgoing_args_rtx, we must copy it to another register in some cases. */ rtx temp = (GET_CODE (structure_value_addr) != REG#ifdef ACCUMULATE_OUTGOING_ARGS || (stack_arg_under_construction && structure_value_addr == virtual_outgoing_args_rtx)#endif ? copy_addr_to_reg (structure_value_addr) : structure_value_addr); actparms = tree_cons (error_mark_node, make_tree (build_pointer_type (TREE_TYPE (funtype)), temp), actparms); structure_value_addr_parm = 1; } /* Count the arguments and set NUM_ACTUALS. */ for (p = actparms, i = 0; p; p = TREE_CHAIN (p)) i++; num_actuals = i; /* Compute number of named args. Normally, don't include the last named arg if anonymous args follow. We do include the last named arg if STRICT_ARGUMENT_NAMING is defined. (If no anonymous args follow, the result of list_length is actually one too large. This is harmless.) If SETUP_INCOMING_VARARGS is defined and STRICT_ARGUMENT_NAMING is not, this machine will be able to place unnamed args that were passed in registers into the stack. So treat all args as named. This allows the insns emitting for a specific argument list to be independent of the function declaration. If SETUP_INCOMING_VARARGS is not defined, we do not have any reliable way to pass unnamed args in registers, so we must force them into memory. */#if !defined(SETUP_INCOMING_VARARGS) || defined(STRICT_ARGUMENT_NAMING) if (TYPE_ARG_TYPES (funtype) != 0) n_named_args = (list_length (TYPE_ARG_TYPES (funtype))#ifndef STRICT_ARGUMENT_NAMING /* Don't include the last named arg. */ - 1#endif /* Count the struct value address, if it is passed as a parm. */ + structure_value_addr_parm); else#endif /* If we know nothing, treat all args as named. */ n_named_args = num_actuals; /* Make a vector to hold all the information about each arg. */ args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data)); bzero ((char *) args, num_actuals * sizeof (struct arg_data)); args_size.constant = 0; args_size.var = 0; /* In this loop, we consider args in the order they are written. We fill up ARGS from the front or from the back if necessary so that in any case the first arg to be pushed ends up at the front. */#ifdef PUSH_ARGS_REVERSED i = num_actuals - 1, inc = -1; /* In this case, must reverse order of args so that we compute and push the last arg first. */#else i = 0, inc = 1;#endif /* I counts args in order (to be) pushed; ARGPOS counts in order written. */ for (p = actparms, argpos = 0; p; p = TREE_CHAIN (p), i += inc, argpos++) { tree type = TREE_TYPE (TREE_VALUE (p)); int unsignedp; enum machine_mode mode; args[i].tree_value = TREE_VALUE (p); /* Replace erroneous argument with constant zero. */ if (type == error_mark_node || TYPE_SIZE (type) == 0) args[i].tree_value = integer_zero_node, type = integer_type_node; /* If TYPE is a transparent union, pass things the way we would pass the first field of the union. We have already verified that the modes are the same. */ if (TYPE_TRANSPARENT_UNION (type)) type = TREE_TYPE (TYPE_FIELDS (type)); /* Decide where to pass this arg. args[i].reg is nonzero if all or part is passed in registers. args[i].partial is nonzero if part but not all is passed in registers, and the exact value says how many words are passed in registers. args[i].pass_on_stack is nonzero if the argument must at least be computed on the stack. It may then be loaded back into registers if args[i].reg is nonzero. These decisions are driven by the FUNCTION_... macros and must agree with those made by function.c. */ /* See if this argument should be passed by invisible reference. */ if ((TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST && contains_placeholder_p (TYPE_SIZE (type))) || TREE_ADDRESSABLE (type)#ifdef FUNCTION_ARG_PASS_BY_REFERENCE || FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, TYPE_MODE (type), type, argpos < n_named_args)#endif ) { /* If we're compiling a thunk, pass through invisible references instead of making a copy. */ if (current_function_is_thunk#ifdef FUNCTION_ARG_CALLEE_COPIES || (FUNCTION_ARG_CALLEE_COPIES (args_so_far, TYPE_MODE (type), type, argpos < n_named_args) /* If it's in a register, we must make a copy of it too. */ /* ??? Is this a sufficient test? Is there a better one? */ && !(TREE_CODE (args[i].tree_value) == VAR_DECL && REG_P (DECL_RTL (args[i].tree_value))) && ! TREE_ADDRESSABLE (type))#endif ) { args[i].tree_value = build1 (ADDR_EXPR, build_pointer_type (type), args[i].tree_value); type = build_pointer_type (type); } else { /* We make a copy of the object and pass the address to the function being called. */ rtx copy; if (TYPE_SIZE (type) == 0 || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST || (flag_stack_check && ! STACK_CHECK_BUILTIN && (TREE_INT_CST_HIGH (TYPE_SIZE (type)) != 0 || (TREE_INT_CST_LOW (TYPE_SIZE (type)) > STACK_CHECK_MAX_VAR_SIZE * BITS_PER_UNIT)))) { /* This is a variable-sized object. Make space on the stack for it. */ rtx size_rtx = expr_size (TREE_VALUE (p)); if (old_stack_level == 0) { emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); old_pending_adj = pending_stack_adjust; pending_stack_adjust = 0; } copy = gen_rtx (MEM, BLKmode, allocate_dynamic_stack_space (size_rtx, NULL_RTX, TYPE_ALIGN (type))); } else { int size = int_size_in_bytes (type); copy = assign_stack_temp (TYPE_MODE (type), size, 0); } MEM_IN_STRUCT_P (copy) = AGGREGATE_TYPE_P (type); store_expr (args[i].tree_value, copy, 0); is_const = 0; args[i].tree_value = build1 (ADDR_EXPR, build_pointer_type (type), make_tree (type, copy)); type = build_pointer_type (type); } } mode = TYPE_MODE (type); unsignedp = TREE_UNSIGNED (type);#ifdef PROMOTE_FUNCTION_ARGS mode = promote_mode (type, mode, &unsignedp, 1);#endif args[i].unsignedp = unsignedp; args[i].mode = mode; args[i].reg = FUNCTION_ARG (args_so_far, mode, type, argpos < n_named_args);#ifdef FUNCTION_ARG_PARTIAL_NREGS if (args[i].reg) args[i].partial = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, type, argpos < n_named_args);#endif args[i].pass_on_stack = MUST_PASS_IN_STACK (mode, type); /* If FUNCTION_ARG returned a (parallel [(expr_list (nil) ...) ...]), it means that we are to pass this arg in the register(s) designated by the PARALLEL, but also to pass it in the stack. */ if (args[i].reg && GET_CODE (args[i].reg) == PARALLEL && XEXP (XVECEXP (args[i].reg, 0, 0), 0) == 0) args[i].pass_on_stack = 1; /* If this is an addressable type, we must preallocate the stack since we must evaluate the object into its final location. If this is to be passed in both registers and the stack, it is simpler to preallocate. */ if (TREE_ADDRESSABLE (type) || (args[i].pass_on_stack && args[i].reg != 0)) must_preallocate = 1; /* If this is an addressable type, we cannot pre-evaluate it. Thus, we cannot consider this function call constant. */ if (TREE_ADDRESSABLE (type)) is_const = 0; /* Compute the stack-size of this argument. */ if (args[i].reg == 0 || args[i].partial != 0#ifdef REG_PARM_STACK_SPACE || reg_parm_stack_space > 0#endif || args[i].pass_on_stack) locate_and_pad_parm (mode, type,#ifdef STACK_PARMS_IN_REG_PARM_AREA 1,#else args[i].reg != 0,#endif fndecl, &args_size, &args[i].offset, &args[i].size);#ifndef ARGS_GROW_DOWNWARD args[i].slot_offset = args_size;#endif#ifndef REG_PARM_STACK_SPACE /* If a part of the arg was put into registers, don't include that part in the amount pushed. */ if (! args[i].pass_on_stack) args[i].size.constant -= ((args[i].partial * UNITS_PER_WORD) / (PARM_BOUNDARY / BITS_PER_UNIT) * (PARM_BOUNDARY / BITS_PER_UNIT));#endif /* Update ARGS_SIZE, the total stack space for args so far. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -