Graphvizによるファンクション・コールの視覚化
ちなみに、このプロファイル関数 (__cyg_profile_function_enter/exit()) は決め打ちで埋め込まれるので、C++ の場合は extern "C" で宣言しなければならない。
How to use '-finstrument-functions' in C++ programs ?
この機能の実装は、単に関数呼び出しの RTX を直接展開することで行われているようだ。
[tree-ssa][rfc] -finstrument-functions at tree level
gcc-4.3.2/gcc/builtins.c
- static rtx
- expand_builtin_profile_func (bool exitp)
- {
- rtx this, which;
- this = DECL_RTL (current_function_decl);
- gcc_assert (MEM_P (this));
- this = XEXP (this, 0);
- if (exitp)
- which = profile_function_exit_libfunc;
- else
- which = profile_function_entry_libfunc;
- emit_library_call (which, LCT_NORMAL, VOIDmode, 2, this, Pmode,
- expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
- 0),
- Pmode);
- return const0_rtx;
- }
gcc-4.3.2/gcc/libfuncs.h
- #define profile_function_exit_libfunc (libfunc_table[LTI_profile_function_exit])
gcc-4.3.2/gcc/optabs.c
- rtx libfunc_table[LTI_MAX];
- rtx
- init_one_libfunc (const char *name)
- {
- rtx symbol;
- /* Create a FUNCTION_DECL that can be passed to
- targetm.encode_section_info. */
- /* ??? We don't have any type information except for this is
- a function. Pretend this is "int foo()". */
- tree decl = build_decl (FUNCTION_DECL, get_identifier (name),
- build_function_type (integer_type_node, NULL_TREE));
- DECL_ARTIFICIAL (decl) = 1;
- DECL_EXTERNAL (decl) = 1;
- TREE_PUBLIC (decl) = 1;
- symbol = XEXP (DECL_RTL (decl), 0);
- /* Zap the nonsensical SYMBOL_REF_DECL for this. What we're left with
- are the flags assigned by targetm.encode_section_info. */
- SET_SYMBOL_REF_DECL (symbol, 0);
- return symbol;
- }
単に external でかつ artificial な関数呼び出しを作っているようだ。
- abort_libfunc = init_one_libfunc ("abort");
- memcpy_libfunc = init_one_libfunc ("memcpy");
- memmove_libfunc = init_one_libfunc ("memmove");
- memcmp_libfunc = init_one_libfunc ("memcmp");
- memset_libfunc = init_one_libfunc ("memset");
- setbits_libfunc = init_one_libfunc ("__setbits");
- #ifndef DONT_USE_BUILTIN_SETJMP
- setjmp_libfunc = init_one_libfunc ("__builtin_setjmp");
- longjmp_libfunc = init_one_libfunc ("__builtin_longjmp");
- #else
- setjmp_libfunc = init_one_libfunc ("setjmp");
- longjmp_libfunc = init_one_libfunc ("longjmp");
- #endif
- unwind_sjlj_register_libfunc = init_one_libfunc ("_Unwind_SjLj_Register");
- unwind_sjlj_unregister_libfunc
- = init_one_libfunc ("_Unwind_SjLj_Unregister");
- /* For function entry/exit instrumentation. */
- profile_function_entry_libfunc
- = init_one_libfunc ("__cyg_profile_func_enter");
- profile_function_exit_libfunc
- = init_one_libfunc ("__cyg_profile_func_exit");
- gcov_flush_libfunc = init_one_libfunc ("__gcov_flush");
gcc-4.3.2/gcc/calls.h
- /* Output a library call to function FUN (a SYMBOL_REF rtx)
- (emitting the queue unless NO_QUEUE is nonzero),
- for a value of mode OUTMODE,
- with NARGS different arguments, passed as alternating rtx values
- and machine_modes to convert them to.
- FN_TYPE should be LCT_NORMAL for `normal' calls, LCT_CONST for `const'
- calls, LCT_PURE for `pure' calls, LCT_CONST_MAKE_BLOCK for `const' calls
- which should be enclosed in REG_LIBCALL/REG_RETVAL notes,
- LCT_PURE_MAKE_BLOCK for `purep' calls which should be enclosed in
- REG_LIBCALL/REG_RETVAL notes with extra (use (memory (scratch)),
- or other LCT_ value for other types of library calls. */
- void
- emit_library_call (rtx orgfun, enum libcall_type fn_type,
- enum machine_mode outmode, int nargs, ...)
- {
- va_list p;
- va_start (p, nargs);
- emit_library_call_value_1 (0, orgfun, NULL_RTX, fn_type, outmode, nargs, p);
- va_end (p);
- }
同ファイル内の emit_library_call_value_1 は非常に巨大。
- /* Output a library call to function FUN (a SYMBOL_REF rtx).
- The RETVAL parameter specifies whether return value needs to be saved, other
- parameters are documented in the emit_library_call function below. */
- static rtx
- emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
- enum libcall_type fn_type,
- enum machine_mode outmode, int nargs, va_list p)
- {
- /* Total size in bytes of all the stack-parms scanned so far. */
- struct args_size args_size;
- /* Size of arguments before any adjustments (such as rounding). */
- struct args_size original_args_size;
- int argnum;
- rtx fun;
- int inc;
- int count;
- rtx argblock = 0;
- CUMULATIVE_ARGS args_so_far;
- struct arg
- {
- rtx value;
- enum machine_mode mode;
- rtx reg;
- int partial;
- struct locate_and_pad_arg_data locate;
- rtx save_area;
- };
- struct arg *argvec;
- int old_inhibit_defer_pop = inhibit_defer_pop;
- rtx call_fusage = 0;
- rtx mem_value = 0;
- rtx valreg;
- int pcc_struct_value = 0;
- int struct_value_size = 0;
- int flags;
- int reg_parm_stack_space = 0;
- int needed;
- rtx before_call;
- tree tfom; /* type_for_mode (outmode, 0) */
- #ifdef REG_PARM_STACK_SPACE
- /* Define the boundary of the register parm stack space that needs to be
- save, if any. */
- int low_to_save, high_to_save;
- rtx save_area = 0; /* Place that it is saved. */
- #endif
- /* Size of the stack reserved for parameter registers. */
- int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
- char *initial_stack_usage_map = stack_usage_map;
- char *stack_usage_map_buf = NULL;
- rtx struct_value = targetm.calls.struct_value_rtx (0, 0);
- #ifdef REG_PARM_STACK_SPACE
- reg_parm_stack_space = REG_PARM_STACK_SPACE ((tree) 0);
- #endif
- /* By default, library functions can not throw. */
- flags = ECF_NOTHROW;
- switch (fn_type)
- {
- case LCT_NORMAL:
- break;
- case LCT_CONST:
- flags |= ECF_CONST;
- break;
- case LCT_PURE:
- flags |= ECF_PURE;
- break;
- case LCT_CONST_MAKE_BLOCK:
- flags |= ECF_CONST | ECF_LIBCALL_BLOCK;
- break;
- case LCT_PURE_MAKE_BLOCK:
- flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
- break;
- case LCT_NORETURN:
- flags |= ECF_NORETURN;
- break;
- case LCT_THROW:
- flags = ECF_NORETURN;
- break;
- case LCT_RETURNS_TWICE:
- flags = ECF_RETURNS_TWICE;
- break;
- }
- fun = orgfun;
- /* Ensure current function's preferred stack boundary is at least
- what we need. */
- if (cfun->preferred_stack_boundary < PREFERRED_STACK_BOUNDARY)
- cfun->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
- /* If this kind of value comes back in memory,
- decide where in memory it should come back. */
- if (outmode != VOIDmode)
- {
- tfom = lang_hooks.types.type_for_mode (outmode, 0);
- if (aggregate_value_p (tfom, 0))
- {
- #ifdef PCC_STATIC_STRUCT_RETURN
- rtx pointer_reg
- = hard_function_value (build_pointer_type (tfom), 0, 0, 0);
- mem_value = gen_rtx_MEM (outmode, pointer_reg);
- pcc_struct_value = 1;
- if (value == 0)
- value = gen_reg_rtx (outmode);
- #else /* not PCC_STATIC_STRUCT_RETURN */
- struct_value_size = GET_MODE_SIZE (outmode);
- if (value != 0 && MEM_P (value))
- mem_value = value;
- else
- mem_value = assign_temp (tfom, 0, 1, 1);
- #endif
- /* This call returns a big structure. */
- flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
- }
- }
- else
- tfom = void_type_node;
- /* ??? Unfinished: must pass the memory address as an argument. */
- /* Copy all the libcall-arguments out of the varargs data
- and into a vector ARGVEC.
- Compute how to pass each argument. We only support a very small subset
- of the full argument passing conventions to limit complexity here since
- library functions shouldn't have many args. */
- argvec = alloca ((nargs + 1) * sizeof (struct arg));
- memset (argvec, 0, (nargs + 1) * sizeof (struct arg));
- #ifdef INIT_CUMULATIVE_LIBCALL_ARGS
- INIT_CUMULATIVE_LIBCALL_ARGS (args_so_far, outmode, fun);
- #else
- INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun, 0, nargs);
- #endif
- args_size.constant = 0;
- args_size.var = 0;
- count = 0;
- /* Now we are about to start emitting insns that can be deleted
- if a libcall is deleted. */
- if (flags & ECF_LIBCALL_BLOCK)
- start_sequence ();
- push_temp_slots ();
- /* If there's a structure value address to be passed,
- either pass it in the special place, or pass it as an extra argument. */
- if (mem_value && struct_value == 0 && ! pcc_struct_value)
- {
- rtx addr = XEXP (mem_value, 0);
- nargs++;
- /* Make sure it is a reasonable operand for a move or push insn. */
- if (!REG_P (addr) && !MEM_P (addr)
- && ! (CONSTANT_P (addr) && LEGITIMATE_CONSTANT_P (addr)))
- addr = force_operand (addr, NULL_RTX);
- argvec[count].value = addr;
- argvec[count].mode = Pmode;
- argvec[count].partial = 0;
- argvec[count].reg = FUNCTION_ARG (args_so_far, Pmode, NULL_TREE, 1);
- gcc_assert (targetm.calls.arg_partial_bytes (&args_so_far, Pmode,
- NULL_TREE, 1) == 0);
- locate_and_pad_parm (Pmode, NULL_TREE,
- #ifdef STACK_PARMS_IN_REG_PARM_AREA
- 1,
- #else
- argvec[count].reg != 0,
- #endif
- 0, NULL_TREE, &args_size, &argvec[count].locate);
- if (argvec[count].reg == 0 || argvec[count].partial != 0
- || reg_parm_stack_space > 0)
- args_size.constant += argvec[count].locate.size.constant;
- FUNCTION_ARG_ADVANCE (args_so_far, Pmode, (tree) 0, 1);
- count++;
- }
- for (; count < nargs; count++)
- {
- rtx val = va_arg (p, rtx);
- enum machine_mode mode = va_arg (p, enum machine_mode);
- /* We cannot convert the arg value to the mode the library wants here;
- must do it earlier where we know the signedness of the arg. */
- gcc_assert (mode != BLKmode
- && (GET_MODE (val) == mode || GET_MODE (val) == VOIDmode));
- /* Make sure it is a reasonable operand for a move or push insn. */
- if (!REG_P (val) && !MEM_P (val)
- && ! (CONSTANT_P (val) && LEGITIMATE_CONSTANT_P (val)))
- val = force_operand (val, NULL_RTX);
- if (pass_by_reference (&args_so_far, mode, NULL_TREE, 1))
- {
- rtx slot;
- int must_copy
- = !reference_callee_copied (&args_so_far, mode, NULL_TREE, 1);
- /* loop.c won't look at CALL_INSN_FUNCTION_USAGE of const/pure
- functions, so we have to pretend this isn't such a function. */
- if (flags & ECF_LIBCALL_BLOCK)
- {
- rtx insns = get_insns ();
- end_sequence ();
- emit_insn (insns);
- }
- flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
- /* If this was a CONST function, it is now PURE since
- it now reads memory. */
- if (flags & ECF_CONST)
- {
- flags &= ~ECF_CONST;
- flags |= ECF_PURE;
- }
- if (GET_MODE (val) == MEM && !must_copy)
- slot = val;
- else
- {
- slot = assign_temp (lang_hooks.types.type_for_mode (mode, 0),
- 0, 1, 1);
- emit_move_insn (slot, val);
- }
- call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_USE (VOIDmode, slot),
- call_fusage);
- if (must_copy)
- call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_CLOBBER (VOIDmode,
- slot),
- call_fusage);
- mode = Pmode;
- val = force_operand (XEXP (slot, 0), NULL_RTX);
- }
- argvec[count].value = val;
- argvec[count].mode = mode;
- argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
- argvec[count].partial
- = targetm.calls.arg_partial_bytes (&args_so_far, mode, NULL_TREE, 1);
- locate_and_pad_parm (mode, NULL_TREE,
- #ifdef STACK_PARMS_IN_REG_PARM_AREA
- 1,
- #else
- argvec[count].reg != 0,
- #endif
- argvec[count].partial,
- NULL_TREE, &args_size, &argvec[count].locate);
- gcc_assert (!argvec[count].locate.size.var);
- if (argvec[count].reg == 0 || argvec[count].partial != 0
- || reg_parm_stack_space > 0)
- args_size.constant += argvec[count].locate.size.constant;
- FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree) 0, 1);
- }
- /* If this machine requires an external definition for library
- functions, write one out. */
- assemble_external_libcall (fun);
- original_args_size = args_size;
- args_size.constant = (((args_size.constant
- + stack_pointer_delta
- + STACK_BYTES - 1)
- / STACK_BYTES
- * STACK_BYTES)
- - stack_pointer_delta);
- args_size.constant = MAX (args_size.constant,
- reg_parm_stack_space);
- if (!OUTGOING_REG_PARM_STACK_SPACE)
- args_size.constant -= reg_parm_stack_space;
- if (args_size.constant > current_function_outgoing_args_size)
- current_function_outgoing_args_size = args_size.constant;
- if (ACCUMULATE_OUTGOING_ARGS)
- {
- /* Since the stack pointer will never be pushed, it is possible for
- the evaluation of a parm to clobber something we have already
- written to the stack. Since most function calls on RISC machines
- do not use the stack, this is uncommon, but must work correctly.
- Therefore, we save any area of the stack that was already written
- and that we are using. Here we set up to do this by making a new
- stack usage map from the old one.
- Another approach might be to try to reorder the argument
- evaluations to avoid this conflicting stack usage. */
- needed = args_size.constant;
- /* Since we will be writing into the entire argument area, the
- map must be allocated for its entire size, not just the part that
- is the responsibility of the caller. */
- if (!OUTGOING_REG_PARM_STACK_SPACE)
- needed += reg_parm_stack_space;
- #ifdef ARGS_GROW_DOWNWARD
- highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
- needed + 1);
- #else
- highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
- needed);
- #endif
- stack_usage_map_buf = XNEWVEC (char, highest_outgoing_arg_in_use);
- stack_usage_map = stack_usage_map_buf;
- if (initial_highest_arg_in_use)
- memcpy (stack_usage_map, initial_stack_usage_map,
- initial_highest_arg_in_use);
- if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
- memset (&stack_usage_map[initial_highest_arg_in_use], 0,
- highest_outgoing_arg_in_use - initial_highest_arg_in_use);
- needed = 0;
- /* We must be careful to use virtual regs before they're instantiated,
- and real regs afterwards. Loop optimization, for example, can create
- new libcalls after we've instantiated the virtual regs, and if we
- use virtuals anyway, they won't match the rtl patterns. */
- if (virtuals_instantiated)
- argblock = plus_constant (stack_pointer_rtx, STACK_POINTER_OFFSET);
- else
- argblock = virtual_outgoing_args_rtx;
- }
- else
- {
- if (!PUSH_ARGS)
- argblock = push_block (GEN_INT (args_size.constant), 0, 0);
- }
- /* If we push args individually in reverse order, perform stack alignment
- before the first push (the last arg). */
- if (argblock == 0 && PUSH_ARGS_REVERSED)
- anti_adjust_stack (GEN_INT (args_size.constant
- - original_args_size.constant));
- if (PUSH_ARGS_REVERSED)
- {
- inc = -1;
- argnum = nargs - 1;
- }
- else
- {
- inc = 1;
- argnum = 0;
- }
- #ifdef REG_PARM_STACK_SPACE
- if (ACCUMULATE_OUTGOING_ARGS)
- {
- /* The argument list is the property of the called routine and it
- may clobber it. If the fixed area has been used for previous
- parameters, we must save and restore it. */
- save_area = save_fixed_argument_area (reg_parm_stack_space, argblock,
- &low_to_save, &high_to_save);
- }
- #endif
- /* Push the args that need to be pushed. */
- /* ARGNUM indexes the ARGVEC array in the order in which the arguments
- are to be pushed. */
- for (count = 0; count < nargs; count++, argnum += inc)
- {
- enum machine_mode mode = argvec[argnum].mode;
- rtx val = argvec[argnum].value;
- rtx reg = argvec[argnum].reg;
- int partial = argvec[argnum].partial;
- int lower_bound = 0, upper_bound = 0, i;
- if (! (reg != 0 && partial == 0))
- {
- if (ACCUMULATE_OUTGOING_ARGS)
- {
- /* If this is being stored into a pre-allocated, fixed-size,
- stack area, save any previous data at that location. */
- #ifdef ARGS_GROW_DOWNWARD
- /* stack_slot is negative, but we want to index stack_usage_map
- with positive values. */
- upper_bound = -argvec[argnum].locate.offset.constant + 1;
- lower_bound = upper_bound - argvec[argnum].locate.size.constant;
- #else
- lower_bound = argvec[argnum].locate.offset.constant;
- upper_bound = lower_bound + argvec[argnum].locate.size.constant;
- #endif
- i = lower_bound;
- /* Don't worry about things in the fixed argument area;
- it has already been saved. */
- if (i < reg_parm_stack_space)
- i = reg_parm_stack_space;
- while (i < upper_bound && stack_usage_map[i] == 0)
- i++;
- if (i < upper_bound)
- {
- /* We need to make a save area. */
- unsigned int size
- = argvec[argnum].locate.size.constant * BITS_PER_UNIT;
- enum machine_mode save_mode
- = mode_for_size (size, MODE_INT, 1);
- rtx adr
- = plus_constant (argblock,
- argvec[argnum].locate.offset.constant);
- rtx stack_area
- = gen_rtx_MEM (save_mode, memory_address (save_mode, adr));
- if (save_mode == BLKmode)
- {
- argvec[argnum].save_area
- = assign_stack_temp (BLKmode,
- argvec[argnum].locate.size.constant,
- 0);
- emit_block_move (validize_mem (argvec[argnum].save_area),
- stack_area,
- GEN_INT (argvec[argnum].locate.size.constant),
- BLOCK_OP_CALL_PARM);
- }
- else
- {
- argvec[argnum].save_area = gen_reg_rtx (save_mode);
- emit_move_insn (argvec[argnum].save_area, stack_area);
- }
- }
- }
- emit_push_insn (val, mode, NULL_TREE, NULL_RTX, PARM_BOUNDARY,
- partial, reg, 0, argblock,
- GEN_INT (argvec[argnum].locate.offset.constant),
- reg_parm_stack_space,
- ARGS_SIZE_RTX (argvec[argnum].locate.alignment_pad));
- /* Now mark the segment we just used. */
- if (ACCUMULATE_OUTGOING_ARGS)
- for (i = lower_bound; i < upper_bound; i++)
- stack_usage_map[i] = 1;
- NO_DEFER_POP;
- if (flags & ECF_CONST)
- {
- rtx use;
- /* Indicate argument access so that alias.c knows that these
- values are live. */
- if (argblock)
- use = plus_constant (argblock,
- argvec[argnum].locate.offset.constant);
- else
- /* When arguments are pushed, trying to tell alias.c where
- exactly this argument is won't work, because the
- auto-increment causes confusion. So we merely indicate
- that we access something with a known mode somewhere on
- the stack. */
- use = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx,
- gen_rtx_SCRATCH (Pmode));
- use = gen_rtx_MEM (argvec[argnum].mode, use);
- use = gen_rtx_USE (VOIDmode, use);
- call_fusage = gen_rtx_EXPR_LIST (VOIDmode, use, call_fusage);
- }
- }
- }
- /* If we pushed args in forward order, perform stack alignment
- after pushing the last arg. */
- if (argblock == 0 && !PUSH_ARGS_REVERSED)
- anti_adjust_stack (GEN_INT (args_size.constant
- - original_args_size.constant));
- if (PUSH_ARGS_REVERSED)
- argnum = nargs - 1;
- else
- argnum = 0;
- fun = prepare_call_address (fun, NULL, &call_fusage, 0, 0);
- /* Now load any reg parms into their regs. */
- /* ARGNUM indexes the ARGVEC array in the order in which the arguments
- are to be pushed. */
- for (count = 0; count < nargs; count++, argnum += inc)
- {
- enum machine_mode mode = argvec[argnum].mode;
- rtx val = argvec[argnum].value;
- rtx reg = argvec[argnum].reg;
- int partial = argvec[argnum].partial;
- /* Handle calls that pass values in multiple non-contiguous
- locations. The PA64 has examples of this for library calls. */
- if (reg != 0 && GET_CODE (reg) == PARALLEL)
- emit_group_load (reg, val, NULL_TREE, GET_MODE_SIZE (mode));
- else if (reg != 0 && partial == 0)
- emit_move_insn (reg, val);
- NO_DEFER_POP;
- }
- /* Any regs containing parms remain in use through the call. */
- for (count = 0; count < nargs; count++)
- {
- rtx reg = argvec[count].reg;
- if (reg != 0 && GET_CODE (reg) == PARALLEL)
- use_group_regs (&call_fusage, reg);
- else if (reg != 0)
- {
- int partial = argvec[count].partial;
- if (partial)
- {
- int nregs;
- gcc_assert (partial % UNITS_PER_WORD == 0);
- nregs = partial / UNITS_PER_WORD;
- use_regs (&call_fusage, REGNO (reg), nregs);
- }
- else
- use_reg (&call_fusage, reg);
- }
- }
- /* Pass the function the address in which to return a structure value. */
- if (mem_value != 0 && struct_value != 0 && ! pcc_struct_value)
- {
- emit_move_insn (struct_value,
- force_reg (Pmode,
- force_operand (XEXP (mem_value, 0),
- NULL_RTX)));
- if (REG_P (struct_value))
- use_reg (&call_fusage, struct_value);
- }
- /* Don't allow popping to be deferred, since then
- cse'ing of library calls could delete a call and leave the pop. */
- NO_DEFER_POP;
- valreg = (mem_value == 0 && outmode != VOIDmode
- ? hard_libcall_value (outmode) : NULL_RTX);
- /* Stack must be properly aligned now. */
- gcc_assert (!(stack_pointer_delta
- & (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT - 1)));
- before_call = get_last_insn ();
- /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which
- will set inhibit_defer_pop to that value. */
- /* The return type is needed to decide how many bytes the function pops.
- Signedness plays no role in that, so for simplicity, we pretend it's
- always signed. We also assume that the list of arguments passed has
- no impact, so we pretend it is unknown. */
- emit_call_1 (fun, NULL,
- get_identifier (XSTR (orgfun, 0)),
- build_function_type (tfom, NULL_TREE),
- original_args_size.constant, args_size.constant,
- struct_value_size,
- FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
- valreg,
- old_inhibit_defer_pop + 1, call_fusage, flags, & args_so_far);
- /* For calls to `setjmp', etc., inform function.c:setjmp_warnings
- that it should complain if nonvolatile values are live. For
- functions that cannot return, inform flow that control does not
- fall through. */
- if (flags & ECF_NORETURN)
- {
- /* The barrier note must be emitted
- immediately after the CALL_INSN. Some ports emit more than
- just a CALL_INSN above, so we must search for it here. */
- rtx last = get_last_insn ();
- while (!CALL_P (last))
- {
- last = PREV_INSN (last);
- /* There was no CALL_INSN? */
- gcc_assert (last != before_call);
- }
- emit_barrier_after (last);
- }
- /* Now restore inhibit_defer_pop to its actual original value. */
- OK_DEFER_POP;
- /* If call is cse'able, make appropriate pair of reg-notes around it.
- Test valreg so we don't crash; may safely ignore `const'
- if return type is void. Disable for PARALLEL return values, because
- we have no way to move such values into a pseudo register. */
- if (flags & ECF_LIBCALL_BLOCK)
- {
- rtx insns;
- if (valreg == 0)
- {
- insns = get_insns ();
- end_sequence ();
- emit_insn (insns);
- }
- else
- {
- rtx note = 0;
- rtx temp;
- int i;
- if (GET_CODE (valreg) == PARALLEL)
- {
- temp = gen_reg_rtx (outmode);
- emit_group_store (temp, valreg, NULL_TREE,
- GET_MODE_SIZE (outmode));
- valreg = temp;
- }
- temp = gen_reg_rtx (GET_MODE (valreg));
- /* Construct an "equal form" for the value which mentions all the
- arguments in order as well as the function name. */
- for (i = 0; i < nargs; i++)
- note = gen_rtx_EXPR_LIST (VOIDmode, argvec[i].value, note);
- note = gen_rtx_EXPR_LIST (VOIDmode, fun, note);
- insns = get_insns ();
- end_sequence ();
- if (flags & ECF_PURE)
- note = gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_USE (VOIDmode,
- gen_rtx_MEM (BLKmode,
- gen_rtx_SCRATCH (VOIDmode))),
- note);
- emit_libcall_block (insns, temp, valreg, note);
- valreg = temp;
- }
- }
- pop_temp_slots ();
- /* Copy the value to the right place. */
- if (outmode != VOIDmode && retval)
- {
- if (mem_value)
- {
- if (value == 0)
- value = mem_value;
- if (value != mem_value)
- emit_move_insn (value, mem_value);
- }
- else if (GET_CODE (valreg) == PARALLEL)
- {
- if (value == 0)
- value = gen_reg_rtx (outmode);
- emit_group_store (value, valreg, NULL_TREE, GET_MODE_SIZE (outmode));
- }
- else
- {
- /* Convert to the proper mode if PROMOTE_MODE has been active. */
- if (GET_MODE (valreg) != outmode)
- {
- int unsignedp = TYPE_UNSIGNED (tfom);
- gcc_assert (targetm.calls.promote_function_return (tfom));
- gcc_assert (promote_mode (tfom, outmode, &unsignedp, 0)
- == GET_MODE (valreg));
- valreg = convert_modes (outmode, GET_MODE (valreg), valreg, 0);
- }
- if (value != 0)
- emit_move_insn (value, valreg);
- else
- value = valreg;
- }
- }
- if (ACCUMULATE_OUTGOING_ARGS)
- {
- #ifdef REG_PARM_STACK_SPACE
- if (save_area)
- restore_fixed_argument_area (save_area, argblock,
- high_to_save, low_to_save);
- #endif
- /* If we saved any argument areas, restore them. */
- for (count = 0; count < nargs; count++)
- if (argvec[count].save_area)
- {
- enum machine_mode save_mode = GET_MODE (argvec[count].save_area);
- rtx adr = plus_constant (argblock,
- argvec[count].locate.offset.constant);
- rtx stack_area = gen_rtx_MEM (save_mode,
- memory_address (save_mode, adr));
- if (save_mode == BLKmode)
- emit_block_move (stack_area,
- validize_mem (argvec[count].save_area),
- GEN_INT (argvec[count].locate.size.constant),
- BLOCK_OP_CALL_PARM);
- else
- emit_move_insn (stack_area, argvec[count].save_area);
- }
- highest_outgoing_arg_in_use = initial_highest_arg_in_use;
- stack_usage_map = initial_stack_usage_map;
- }
- if (stack_usage_map_buf)
- free (stack_usage_map_buf);
- return value;
- }
0 件のコメント:
コメントを投稿