📄 pa.h
字号:
is pushed. Arguments passed in registers are either 1 or 2 words long. The caller must make a distinction between calls to explicitly named functions and calls through pointers to functions -- the conventions are different! Calls through pointers to functions only use general registers for the first four argument words. Of course all this is different for the portable runtime model HP wants everyone to use for ELF. Ugh. Here's a quick description of how it's supposed to work. 1) callee side remains unchanged. It expects integer args to be in the integer registers, float args in the float registers and unnamed args in integer registers. 2) caller side now depends on if the function being called has a prototype in scope (rather than if it's being called indirectly). 2a) If there is a prototype in scope, then arguments are passed according to their type (ints in integer registers, floats in float registers, unnamed args in integer registers. 2b) If there is no prototype in scope, then floating point arguments are passed in both integer and float registers. egad. FYI: The portable parameter passing conventions are almost exactly like the standard parameter passing conventions on the RS6000. That's why you'll see lots of similar code in rs6000.h. */#define FUNCTION_ARG_PADDING(MODE, TYPE) function_arg_padding ((MODE), (TYPE))/* Do not expect to understand this without reading it several times. I'm tempted to try and simply it, but I worry about breaking something. */#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ (4 >= ((CUM).words + FUNCTION_ARG_SIZE ((MODE), (TYPE))) \ ? (!TARGET_PORTABLE_RUNTIME || (TYPE) == 0 \ || !FLOAT_MODE_P (MODE) || TARGET_SOFT_FLOAT \ || (CUM).nargs_prototype > 0) \ ? gen_rtx_REG ((MODE), \ (FUNCTION_ARG_SIZE ((MODE), (TYPE)) > 1 \ ? (((!(CUM).indirect \ || TARGET_PORTABLE_RUNTIME) \ && (MODE) == DFmode \ && ! TARGET_SOFT_FLOAT) \ ? ((CUM).words ? 38 : 34) \ : ((CUM).words ? 23 : 25)) \ : (((!(CUM).indirect \ || TARGET_PORTABLE_RUNTIME) \ && (MODE) == SFmode \ && ! TARGET_SOFT_FLOAT) \ ? (32 + 2 * (CUM).words) \ : (27 - (CUM).words - FUNCTION_ARG_SIZE ((MODE), \ (TYPE))))))\ /* We are calling a non-prototyped function with floating point \ arguments using the portable conventions. */ \ : gen_rtx_PARALLEL ((MODE), \ gen_rtvec \ (2, \ gen_rtx_EXPR_LIST (VOIDmode, \ gen_rtx_REG ((MODE), \ (FUNCTION_ARG_SIZE ((MODE), (TYPE)) > 1 \ ? ((CUM).words ? 38 : 34) \ : (32 + 2 * (CUM).words))), \ const0_rtx), \ gen_rtx_EXPR_LIST (VOIDmode, \ gen_rtx_REG ((MODE), \ (FUNCTION_ARG_SIZE ((MODE), (TYPE)) > 1 \ ? ((CUM).words ? 23 : 25) \ : (27 - (CUM).words - \ FUNCTION_ARG_SIZE ((MODE), \ (TYPE))))), \ const0_rtx))) \ /* Pass this parameter in the stack. */ \ : 0)/* For an arg passed partly in registers and partly in memory, this is the number of registers used. For args passed entirely in registers or entirely in memory, zero. */#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) 0/* If defined, a C expression that gives the alignment boundary, in bits, of an argument with the specified mode and type. If it is not defined, `PARM_BOUNDARY' is used for all arguments. */#define FUNCTION_ARG_BOUNDARY(MODE, TYPE) \ (((TYPE) != 0) \ ? (((int_size_in_bytes (TYPE)) + 3) / 4) * BITS_PER_WORD \ : ((GET_MODE_ALIGNMENT(MODE) <= PARM_BOUNDARY) \ ? PARM_BOUNDARY \ : GET_MODE_ALIGNMENT(MODE)))/* Arguments larger than eight bytes are passed by invisible reference */#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \ ((TYPE) && int_size_in_bytes (TYPE) > 8) #define FUNCTION_ARG_CALLEE_COPIES(CUM, MODE, TYPE, NAMED) \ ((TYPE) && int_size_in_bytes (TYPE) > 8)extern struct rtx_def *hppa_compare_op0, *hppa_compare_op1;extern enum cmp_type hppa_branch_type;/* Output the label for a function definition. */#ifndef HP_FP_ARG_DESCRIPTOR_REVERSED#define ASM_DOUBLE_ARG_DESCRIPTORS(FILE, ARG0, ARG1) \ do { fprintf (FILE, ",ARGW%d=FR", (ARG0)); \ fprintf (FILE, ",ARGW%d=FU", (ARG1));} while (0)#define DFMODE_RETURN_STRING ",RTNVAL=FU"#define SFMODE_RETURN_STRING ",RTNVAL=FR"#else#define ASM_DOUBLE_ARG_DESCRIPTORS(FILE, ARG0, ARG1) \ do { fprintf (FILE, ",ARGW%d=FU", (ARG0)); \ fprintf (FILE, ",ARGW%d=FR", (ARG1));} while (0)#define DFMODE_RETURN_STRING ",RTNVAL=FR"#define SFMODE_RETURN_STRING ",RTNVAL=FU"#endif#define ASM_OUTPUT_MI_THUNK(FILE, THUNK_FNDECL, DELTA, FUNCTION) \{ char *target_name = XSTR (XEXP (DECL_RTL (FUNCTION), 0), 0); \ STRIP_NAME_ENCODING (target_name, target_name); \ output_function_prologue (FILE, 0); \ if (VAL_14_BITS_P (DELTA)) \ fprintf (FILE, "\tb %s\n\tldo %d(%%r26),%%r26\n", target_name, DELTA); \ else \ fprintf (FILE, "\taddil L%%%d,%%r26\n\tb %s\n\tldo R%%%d(%%r1),%%r26\n", \ DELTA, target_name, DELTA); \ fprintf (FILE, "\n\t.EXIT\n\t.PROCEND\n"); \}/* NAME refers to the function's name. If we are placing each function into its own section, we need to switch to the section for this function. Note that the section name will have a "." prefix. */#define ASM_OUTPUT_FUNCTION_PREFIX(FILE, NAME) \ { \ char *name; \ STRIP_NAME_ENCODING (name, NAME); \ if (!TARGET_PORTABLE_RUNTIME && TARGET_GAS && in_section == in_text) \ fputs ("\t.NSUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=44,CODE_ONLY\n", FILE); \ else if (! TARGET_PORTABLE_RUNTIME && TARGET_GAS) \ fprintf (FILE, \ "\t.SUBSPA .%s\n", name); \ } #define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ do { tree fntype = TREE_TYPE (TREE_TYPE (DECL)); \ tree tree_type = TREE_TYPE (DECL); \ tree parm; \ int i; \ if (TREE_PUBLIC (DECL) || TARGET_GAS) \ { extern int current_function_varargs; \ if (TREE_PUBLIC (DECL)) \ { \ fputs ("\t.EXPORT ", FILE); \ assemble_name (FILE, NAME); \ fputs (",ENTRY,PRIV_LEV=3", FILE); \ } \ else \ { \ fputs ("\t.PARAM ", FILE); \ assemble_name (FILE, NAME); \ } \ if (TARGET_PORTABLE_RUNTIME) \ { \ fputs (",ARGW0=NO,ARGW1=NO,ARGW2=NO,ARGW3=NO,", FILE); \ fputs ("RTNVAL=NO\n", FILE); \ break; \ } \ for (parm = DECL_ARGUMENTS (DECL), i = 0; parm && i < 4; \ parm = TREE_CHAIN (parm)) \ { \ if (TYPE_MODE (DECL_ARG_TYPE (parm)) == SFmode \ && ! TARGET_SOFT_FLOAT) \ fprintf (FILE, ",ARGW%d=FR", i++); \ else if (TYPE_MODE (DECL_ARG_TYPE (parm)) == DFmode \ && ! TARGET_SOFT_FLOAT) \ { \ if (i <= 2) \ { \ if (i == 1) i++; \ ASM_DOUBLE_ARG_DESCRIPTORS (FILE, i++, i++); \ } \ else \ break; \ } \ else \ { \ int arg_size = \ FUNCTION_ARG_SIZE (TYPE_MODE (DECL_ARG_TYPE (parm)),\ DECL_ARG_TYPE (parm)); \ /* Passing structs by invisible reference uses \ one general register. */ \ if (arg_size > 2 \ || TREE_ADDRESSABLE (DECL_ARG_TYPE (parm))) \ arg_size = 1; \ if (arg_size == 2 && i <= 2) \ { \ if (i == 1) i++; \ fprintf (FILE, ",ARGW%d=GR", i++); \ fprintf (FILE, ",ARGW%d=GR", i++); \ } \ else if (arg_size == 1) \ fprintf (FILE, ",ARGW%d=GR", i++); \ else \ i += arg_size; \ } \ } \ /* anonymous args */ \ if ((TYPE_ARG_TYPES (tree_type) != 0 \ && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (tree_type)))\ != void_type_node)) \ || current_function_varargs) \ { \ for (; i < 4; i++) \ fprintf (FILE, ",ARGW%d=GR", i); \ } \ if (TYPE_MODE (fntype) == DFmode && ! TARGET_SOFT_FLOAT) \ fputs (DFMODE_RETURN_STRING, FILE); \ else if (TYPE_MODE (fntype) == SFmode && ! TARGET_SOFT_FLOAT) \ fputs (SFMODE_RETURN_STRING, FILE); \ else if (fntype != void_type_node) \ fputs (",RTNVAL=GR", FILE); \ fputs ("\n", FILE); \ }} while (0)/* This macro generates the assembly code for function entry. FILE is a stdio stream to output the code to. SIZE is an int: how many units of temporary storage to allocate. Refer to the array `regs_ever_live' to determine which registers to save; `regs_ever_live[I]' is nonzero if register number I is ever used in the function. This macro is responsible for knowing which registers should not be saved even if used. *//* On HP-PA, move-double insns between fpu and cpu need an 8-byte block of memory. If any fpu reg is used in the function, we allocate such a block here, at the bottom of the frame, just in case it's needed. If this function is a leaf procedure, then we may choose not to do a "save" insn. The decision about whether or not to do this is made in regclass.c. */#define FUNCTION_PROLOGUE(FILE, SIZE) \ output_function_prologue (FILE, SIZE)/* Output assembler code to FILE to increment profiler label # LABELNO for profiling a function entry. Because HPUX _mcount is so different, we actually emit the profiling code in function_prologue. This just stores LABELNO for that. */#define PROFILE_BEFORE_PROLOGUE#define FUNCTION_PROFILER(FILE, LABELNO) \{ extern int hp_profile_labelno; hp_profile_labelno = (LABELNO);}/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, the stack pointer does not matter. The value is tested only in functions that have frame pointers. No definition is equivalent to always zero. */extern int may_call_alloca;extern int current_function_pretend_args_size;#define EXIT_IGNORE_STACK \ (get_frame_size () != 0 \ || current_function_calls_alloca || current_function_outgoing_args_size)/* This macro generates the assembly code for function exit, on machines that need it. If FUNCTION_EPILOGUE is not defined then individual return instructions are generated for each return statement. Args are same as for FUNCTION_PROLOGUE. The function epilogue should not depend on the current stack pointer! It should use the frame pointer only. This is mandatory because of alloca; we also take advantage of it to omit stack adjustments before returning. *//* This declaration is needed due to traditional/ANSI incompatibilities which cannot be #ifdefed away because they occur inside of macros. Sigh. */extern union tree_node *current_function_decl;#define FUNCTION_EPILOGUE(FILE, SIZE) \ output_function_epilogue (FILE, SIZE)/* Output assembler code for a block containing the constant parts of a trampoline, leaving space for the variable parts.\ The trampoline sets the static chain pointer to STATIC_CHAIN_REGNUM and then branches to the specified routine. This code template is copied from text segment to stack location and then patched with INITIALIZE_TRAMPOLINE to contain valid values, and then entered as a subroutine. It is best to keep this as small as possible to avoid having to flush multiple lines in the cache. */#define TRAMPOLINE_TEMPLATE(FILE) \ { \ fputs ("\tldw 36(%r22),%r21\n", FILE); \ fputs ("\tbb,>=,n %r21,30,.+16\n", FILE); \ fputs ("\tdepi 0,31,2,%r21\n", FILE); \ fputs ("\tldw 4(%r21),%r19\n", FILE); \ fputs ("\tldw 0(%r21),%r21\n", FILE); \ fputs ("\tldsid (%r21),%r1\n", FILE); \ fputs ("\tmtsp %r1,%sr0\n", FILE); \ fputs ("\tbe 0(%sr0,%r21)\n", FILE); \ fputs ("\tldw 40(%r22),%r29\n", FILE); \ fputs ("\t.word 0\n", FILE); \ fputs ("\t.word 0\n", FILE); \ }/* Length in units of the trampoline for entering a nested function. Flush the cache entries corresponding to the first and last addresses of the trampoline. This is necessary as the trampoline may cross two cache lines. If the code part of the trampoline ever grows to > 32 bytes, then it will become necessary to hack on the cacheflush pattern in pa.md. */#define TRAMPOLINE_SIZE (11 * 4)/* Emit RTL insns to initialize the variable parts of a trampoline. FNADDR is an RTX for the address of the function's pure code. CXT is an RTX for the static chain value for the function. Move the function address to the trampoline template at offset 12. Move the static chain value to trampoline template at offset 16. */#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \{ \ rtx start_addr, end_addr; \ \ start_addr = memory_address (Pmode, plus_constant ((TRAMP), 36)); \ emit_move_insn (gen_rtx_MEM (Pmode, start_addr), (FNADDR)); \ start_addr = memory_address (Pmode, plus_constant ((TRAMP), 40)); \ emit_move_insn (gen_rtx_MEM (Pmode, start_addr), (CXT)); \ /* fdc and fic only use registers for the address to flush, \ they do not accept integer displacements. */ \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -