回到init_regs,其次调用init_reg_autoinc。对于x86机器这个函数将对所有的寄存器设置forbidden_inc_dec_class,因为x86没有任何自增取址(auto increment addressing)。在下面的init_reg_autoinc中,FORBIDDEN_INC_DEC_CLASSES被定义,如果定义了宏SECONDARY_INPUT_RELOAD_CLASS或者SECONDARY_OUTPUT_RELOAD_CLASS。对于x86机器,SECONDARY_OUTPUT_RELOAD_CLASS被定义为:
1560 #define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, OUT) /
1561 (((CLASS) == GENERAL_REGS || (CLASS) == LEGACY_REGS /
1562 || (CLASS) == INDEX_REGS) && !TARGET_64BIT && (MODE) == QImode /
1563 ? Q_REGS : NO_REGS)
这些宏的含义由【2】给出如下:
-- Macro: SECONDARY_RELOAD_CLASS (CLASS, MODE, X) -- Macro: SECONDARY_INPUT_RELOAD_CLASS (CLASS, MODE, X) -- Macro: SECONDARY_OUTPUT_RELOAD_CLASS (CLASS, MODE, X) 许多机器具有某些寄存器,它们不能与内存甚至其他类型寄存器直接交互。一个例子就是在大多数机器上的MQ寄存器,它只能与通用寄存器直接交互,内存则不可。某些机器允许将在所有寄存器及内存间移动数据,但要求一个空闲寄存器(scratch register)用于向内存存入(比如,在编译PIC时,那些在RT机器上的符号化地址,及在SPARC机器上的某些符号化地址)。在某些情况下,同时要求中间(intermediate)寄存器及空闲(scratch)寄存器。 应该定义这些宏,以对重装阶段(reload phase)表明,除了保存数据的寄存器,它需要至少分配一个寄存器用于重装的目的。特别地,如果拷贝X至MODE模式、CLASS类别的寄存器,要求一个中间寄存器,应该定义SECONDARY_INPUT_RELOAD_CLASS为返回最大的寄存器类别,其中所有的寄存器可用作中间寄存器或空闲寄存器。 如果拷贝MODE模式、CLASS类别的寄存器至X要求一个中间寄存器或空闲寄存器,应该定义SECONDARY_OUTPUT_RELOAD_CLASS为返回所要求的最大寄存器类别。如果对输入及输出重装的要求是一样的,应该使用宏SECONDARY_RELOAD_CLASS,而不是把这2个宏定义为一样。 这些宏返回的值通常是GENERAL_REGS。如果不需要备用(spare)寄存器,则返回NO_REGS;比如,如果X和MODE模式、CLASS类别的寄存器间,可以直接拷贝而不需要空闲寄存器。如果寄存器总是能与内存直接交互(返回NO_REGS),不需要定义这些宏。 |
* 编译器的重装遍在完成寄存器分配后运行。它检查每个insn(中间形式的指令)是否有效(要求在寄存器中的操作数确实在合适类别的寄存器中);并且对于无效的insn,通过将其所需的值临时拷贝入寄存器,来修复。
1133 static void
1134 init_reg_autoinc (void) in regclass.c
1135 {
1136 #ifdef FORBIDDEN_INC_DEC_CLASSES
1137 int i;
1138
1139 for (i = 0; i < N_REG_CLASSES; i++)
1140 {
1141 rtx r = gen_rtx_raw_REG (VOIDmode, 0);
1142 enum machine_mode m;
1143 int j;
1144
1145 for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
1146 if (TEST_HARD_REG_BIT (reg_class_contents[i], j))
1147 {
1148 REGNO (r) = j;
1149
1150 for (m = VOIDmode; (int) m < (int) MAX_MACHINE_MODE;
1151 m = (enum machine_mode) ((int) m + 1))
1152 if (HARD_REGNO_MODE_OK (j, m))
1153 {
1154 PUT_MODE (r, m);
1155
1156 /* If a register is not directly suitable for an
1157 auto-increment or decrement addressing mode and
1158 requires secondary reloads, disallow its class from
1159 being used in such addresses. */
1160
1161 if ((0
1162 #ifdef SECONDARY_RELOAD_CLASS
1163 || (SECONDARY_RELOAD_CLASS (MODE_BASE_REG_CLASS (VOIDmode), m, r)
1164 != NO_REGS)
1165 #else
1166 #ifdef SECONDARY_INPUT_RELOAD_CLASS
1167 || (SECONDARY_INPUT_RELOAD_CLASS (MODE_BASE_REG_CLASS (VOIDmode), m, r)
1168 != NO_REGS)
1169 #endif
1170 #ifdef SECONDARY_OUTPUT_RELOAD_CLASS
1171 || (SECONDARY_OUTPUT_RELOAD_CLASS (MODE_BASE_REG_CLASS (VOIDmode), m, r)
1172 != NO_REGS)
1173 #endif
1174 #endif
1175 )
1176 && ! auto_inc_dec_reg_p (r, m))
1177 forbidden_inc_dec_class[i] = 1;
1178 }
1179 }
1180 }
1181 #endif /* FORBIDDEN_INC_DEC_CLASSES */
1182 }
上面的MODE_BASE_REG_CLASS,对于x86机器一直返回GENERAL_REGS。而且在auto_inc_dec_reg_p中,下面的宏HAVE_*默认地被定义为0,对于x86机器,这个函数也一直返回0。
2106 static int
2107 auto_inc_dec_reg_p (rtx reg, enum machine_mode mode) in regclass.c
2108 {
2109 if (HAVE_POST_INCREMENT
2110 && memory_address_p (mode, gen_rtx_POST_INC (Pmode, reg)))
2111 return 1;
2112
2113 if (HAVE_POST_DECREMENT
2114 && memory_address_p (mode, gen_rtx_POST_DEC (Pmode, reg)))
2115 return 1;
2116
2117 if (HAVE_PRE_INCREMENT
2118 && memory_address_p (mode, gen_rtx_PRE_INC (Pmode, reg)))
2119 return 1;
2120
2121 if (HAVE_PRE_DECREMENT
2122 && memory_address_p (mode, gen_rtx_PRE_DEC (Pmode, reg)))
2123 return 1;
2124
2125 return 0;
2126 }
回到backend_init,在完成寄存器集的初始化后,调用init_fake_stack_mems来初始化用在memory_move_secondary_cost中用于测试目的的伪栈框引用。
574 void
575 init_fake_stack_mems (void) in regclass.c
576 {
577 #ifdef HAVE_SECONDARY_RELOADS
578 {
579 int i;
580
581 for (i = 0; i < MAX_MACHINE_MODE; i++)
582 top_of_stack[i] = gen_rtx_MEM (i, stack_pointer_rtx);
583 }
584 #endif
585 }
gen_rtx_MEM具有如下定义,它产生内存引用的rtx对象。
600 rtx
601 gen_rtx_MEM (enum machine_mode mode, rtx addr) in emit-rtl.c
602 {
603 rtx rt = gen_rtx_raw_MEM (mode, addr);
604
605 /* This field is not cleared by the mere allocation of the rtx, so
606 we clear it here. */
607 MEM_ATTRS (rt) = 0;
608
609 return rt;
610 }
gen_rtx_raw_MEM由工具gengenrtl根据文件rtl.def产生。它被放在生成的文件genrtl.h中。gen_rtx_raw_MEM调用gen_rtx_fmt_e0来创建所需的rtx对象。该函数的名字表明,该rx对象有2个子对象,一个为rtx表达式,另一个为NULL_RTX。
下面的MEM_ATTRS用内存属性来替换rtx对象的第二个子对象。不过这里它依然是null。
1163 #define MEM_ATTRS(RTX) X0MEMATTR (RTX, 1) in rtl.h
533 #define X0MEMATTR(RTX, N) (RTL_CHECKC1 (RTX, N, MEM).rtmem) in rtl.h
接下来在backend_init中,调用init_alias_once,为编译期间的别名分析,设立必需的数据结构。
backend_init (continue)
4509 init_alias_once ();
4510 init_loop ();
4511 init_reload ();
4512 init_function_once ();
4513 init_varasm_once ();
4514
4515 /* The following initialization functions need to generate rtl, so
4516 provide a dummy function context for them. */
4517 init_dummy_function_start ();
4518 init_expmed ();
4519 if (flag_caller_saves)
4520 init_caller_save ();
4521 expand_dummy_function_end ();
4522 }
下面的函数创建了static_reg_base_value,这个数据包含了可用于地址参数(指针)的寄存器以及特殊用途的寄存器(例如,栈指针,参数指针,栈框指针)。
2736 void
2737 init_alias_once (void) in alias.c
2738 {
2739 int i;
2740
2741 #ifndef OUTGOING_REGNO
2742 #define OUTGOING_REGNO(N) N
2743 #endif
2744 for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
2745 /* Check whether this register can hold an incoming pointer
2746 argument. FUNCTION_ARG_REGNO_P tests outgoing register
2747 numbers, so translate if necessary due to register windows. */
2748 if (FUNCTION_ARG_REGNO_P (OUTGOING_REGNO (i))
2749 && HARD_REGNO_MODE_OK (i, Pmode))
2750 static_reg_base_value[i]
2751 = gen_rtx_ADDRESS (VOIDmode, gen_rtx_REG (Pmode, i));
2752
2753 static_reg_base_value[STACK_POINTER_REGNUM]
2754 = gen_rtx_ADDRESS (Pmode, stack_pointer_rtx);
2755 static_reg_base_value[ARG_POINTER_REGNUM]
2756 = gen_rtx_ADDRESS (Pmode, arg_pointer_rtx);
2757 static_reg_base_value[FRAME_POINTER_REGNUM]
2758 = gen_rtx_ADDRESS (Pmode, frame_pointer_rtx);
2759 #if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
2760 static_reg_base_value[HARD_FRAME_POINTER_REGNUM]
2761 = gen_rtx_ADDRESS (Pmode, hard_frame_pointer_rtx);
2762 #endif
2763 }
上面,OUTGOING_REGNO特别为机载寄存器窗口(machine incorporating register windows)而定义。寄存器窗口的基本思想是提供一大组物理寄存器,其中的大部分被组织成若干组部分重合的窗口。以Sparc为例,一小部分寄存器(r0 ~ r7)总是引用相同的位置,它们在全局范围使用,但是其他(r8 ~ r31)则被解释为相对于当前活动的窗口。在一个子例程调用中,硬件移动到不同的窗口。为了便利参数的传递,旧的和新的窗口部分重叠:在调用者窗口顶部的少数寄存器(r24 ~ r31)与被调用者窗口底部的少数寄存器(r8 ~ r15)相同。编译器把仅在当前子例程中使用的值放入窗口的中部。在实际应用上,对于典型的程序,8个窗口已经足够,它们作为循环缓存来处理。寄存器窗口起初为Berkeley的RISC机器设计。更多寄存器窗口的细节可以参考p240 【1]】。
在GCC的源代码中,可以看到IA64,Sparc,Xtensa具有寄存器窗口。OUTGOING_REGNO (IN)将返回,在被调用函数所见的寄存器编号IN,所对应的在调用函数中所见的寄存器编号。对于x86机器,宏OUTGOING_REGNO (i)定义为i(定义在上面的2742行),表明寄存器编号i不是传入寄存器(inbound register)。
图19:寄存器窗口
在2748行,FUNCTION_ARG_REGNO_P检查是否某个寄存器可以用于传递函数参数。对于x86机器,则有:
1744 /* 1 if N is a possible register number for function argument passing. */
1745 #define FUNCTION_ARG_REGNO_P(N) ix86_function_arg_regno_p (N) in i386.h
1825 bool
1826 ix86_function_arg_regno_p (int regno) in i386.c
1827 {
1828 int i;
1829 if (!TARGET_64BIT)
1830 return (regno < REGPARM_MAX
1831 || (TARGET_SSE && SSE_REGNO_P (regno) && !fixed_regs[regno]));
...
1841 }
REGPARM_MAX定义了可以通过寄存器传入的最大参数数目。对于x86机器,它的定义如下。
2551 #define REGPARM_MAX (TARGET_64BIT ? 6 : 3) in i386.h
GCC定义了一个全局整型变量target_flags。它是一个比特旗标,用以表示为之编译的机器的亚型。比特位通过宏TARGET_*来测试,这些宏定义在`目标机器`.h file,并且这些比特位由–m*编译选项设置。上面的TARGET_SSE检查机器是否支持sse指令集。
从这个函数,可以看到对于32位的ABI,x86机器可以使用ax,dx,cx及xmm0 ~ xmm7 来传递函数参数。如果寄存器可以用于传递函数参数,并且它兼容于Pmode(可为目标机器保存地址),需要为static_reg_base_value创建唯一的rtx对象。static_reg_base_value将代表间接内存访问的基址。
535 rtx
536 gen_rtx_REG (enum machine_mode mode, unsigned int regno) in emit-rtl.c
537 {
538 /* In case the MD file explicitly references the frame pointer, have
539 all such references point to the same frame pointer. This is
540 used during frame pointer elimination to distinguish the explicit
541 references to these registers from pseudos that happened to be
542 assigned to them.
543
544 If we have eliminated the frame pointer or arg pointer, we will
545 be using it as a normal register, for example as a spill
546 register. In such cases, we might be accessing it in a mode that
547 is not Pmode and therefore cannot use the pre-allocated rtx.
548
549 Also don't do this when we are making new REGs in reload, since
550 we don't want to get confused with the real pointers. */
551
552 if (mode == Pmode && !reload_in_progress)
553 {
554 if (regno == FRAME_POINTER_REGNUM
555 && (!reload_completed || frame_pointer_needed))
556 return frame_pointer_rtx;
557 #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
558 if (regno == HARD_FRAME_POINTER_REGNUM
559 && (!reload_completed || frame_pointer_needed))
560 return hard_frame_pointer_rtx;
561 #endif
562 #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM && HARD_FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
563 if (regno == ARG_POINTER_REGNUM)
564 return arg_pointer_rtx;
565 #endif
566 #ifdef RETURN_ADDRESS_POINTER_REGNUM
567 if (regno == RETURN_ADDRESS_POINTER_REGNUM)
568 return return_address_pointer_rtx;
569 #endif
570 if (regno == (unsigned) PIC_OFFSET_TABLE_REGNUM
571 && fixed_regs[PIC_OFFSET_TABLE_REGNUM])
572 return pic_offset_table_rtx;
573 if (regno == STACK_POINTER_REGNUM)
574 return stack_pointer_rtx;
575 }
576
577 #if 0
578 /* If the per-function register table has been set up, try to re-use
579 an existing entry in that table to avoid useless generation of RTL.
580
581 This code is disabled for now until we can fix the various backends
582 which depend on having non-shared hard registers in some cases. Long
583 term we want to re-enable this code as it can significantly cut down
584 on the amount of useless RTL that gets generated.
585
586 We'll also need to fix some code that runs after reload that wants to
587 set ORIGINAL_REGNO. */
588
589 if (cfun
590 && cfun->emit
591 && regno_reg_rtx
592 && regno < FIRST_PSEUDO_REGISTER
593 && reg_raw_mode[regno] == mode)
594 return regno_reg_rtx[regno];
595 #endif
596
597 return gen_raw_REG (mode, regno);
598 }
在上面的函数中,frame_pointer_rtx,hard_frame_pointer_rtx,arg_pointer_rtx及stack_pointer_rtx都是global_rtx的成员,它们由函数init_emit_once初始化。另,在init_emit_once中,亦创建了编号小于第一个伪寄存器号的寄存器对象,因此留意上面被禁掉的代码及注释。最后的返回语句会再次创建已经存在的rtx寄存器对象。
为了理解上面提及的寄存器,参考下面有关的定义。
1151 /* Register to use for pushing function arguments. */ in i386.h
1152 #define STACK_POINTER_REGNUM 7
1153
1154 /* Base register for access to local variables of the function. */
1155 #define HARD_FRAME_POINTER_REGNUM 6
1156
1157 /* Base register for access to local variables of the function. */
1158 #define FRAME_POINTER_REGNUM 20
1198 /* Base register for access to arguments of the function. */
1199 #define ARG_POINTER_REGNUM 16
1206 /* Register to hold the addressing base for position independent
1207 code access to data items. We don't use PIC pointer for 64bit
1208 mode. Define the regnum to dummy value to prevent gcc from
1209 pessimizing code dealing with EBX.
1210
1211 To avoid clobbering a call-saved register unnecessarily, we renumber
1212 the pic register when possible. The change is visible after the
1213 prologue has been emitted. */
1214
1215 #define REAL_PIC_OFFSET_TABLE_REGNUM 3
1216
1217 #define PIC_OFFSET_TABLE_REGNUM /
1218 (TARGET_64BIT || !flag_pic ? INVALID_REGNUM /
1219 : reload_completed ? REGNO (pic_offset_table_rtx) /
1220 : REAL_PIC_OFFSET_TABLE_REGNUM)
在x86中,STACK_POINTER_REGNUM为sp,ARG_POINTER_REGNUM为arg(伪寄存器),HARD_FRAME_POINTER_REGNUM为bp,而 FRAME_POINTER_REGNUM是frame(伪寄存器)。为了保存位置无关代码(PIC)中访问数据项的基址,x86,在重装遍完成前,使用bx寄存器。
由gen_rtx_REG返回的rtx对象,在init_alias_once的2751行,传递给gen_rtx_ADDRESS。毫无疑问gen_rtx_ADDRESS是与目标机器相关的。对于x86机器,它被定义为gen_rtx_fmt_e,code参数为ADDRESS。其名字中的‘e’表示所生成的rtx表达式的内容是另一个表达式。
231 rtx
232 gen_rtx_fmt_e (RTX_CODE code, enum machine_mode mode, in genrtl.c
233 rtx arg0)
234 {
235 rtx rt;
236 rt = rtx_alloc (code);
237
238 PUT_MODE (rt, mode);
239 XEXP (rt, 0) = arg0;
240
241 return rt;
242 }
XEXP类似于X0EXP,除了他所期望的类型。对于X0EXP,这个类型是‘0’,表示未使用(或者用于与阶段相关的方式(used in a phase-dependent manner))。
508 #define XEXP(RTX, N) (RTL_CHECK2 (RTX, N, 'e', 'u').rtx) in rtl.h
316 #define RTL_CHECK2(RTX, N, C1, C2) __extension__ /
317 (*({ rtx const _rtx = (RTX); const int _n = (N); /
318 const enum rtx_code _code = GET_CODE (_rtx); /
319 if (_n < 0 || _n >= GET_RTX_LENGTH (_code)) /
320 rtl_check_failed_bounds (_rtx, _n, __FILE__, __LINE__, /
321 __FUNCTION__); /
322 if (GET_RTX_FORMAT(_code)[_n] != C1 /
323 && GET_RTX_FORMAT(_code)[_n] != C2) /
324 rtl_check_failed_type2 (_rtx, _n, C1, C2, __FILE__, __LINE__, /
325 __FUNCTION__); /
326 &_rtx->u.fld[_n]; }))Initialize Rtx Objects for Aliasing Code