GCC-3.4.6源代码学习笔记(45)

4.2.3.3.    支持自动增/减寄存器的信息

回到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)表明,除了保存数据的寄存器,它需要至少分配一个寄存器用于重装的目的。特别地,如果拷贝XMODE模式、CLASS类别的寄存器,要求一个中间寄存器,应该定义SECONDARY_INPUT_RELOAD_CLASS为返回最大的寄存器类别,其中所有的寄存器可用作中间寄存器或空闲寄存器。

如果拷贝MODE模式、CLASS类别的寄存器至X要求一个中间寄存器或空闲寄存器,应该定义SECONDARY_OUTPUT_RELOAD_CLASS为返回所要求的最大寄存器类别。如果对输入及输出重装的要求是一样的,应该使用宏SECONDARY_RELOAD_CLASS,而不是把这2个宏定义为一样。

这些宏返回的值通常是GENERAL_REGS。如果不需要备用(spare)寄存器,则返回NO_REGS;比如,如果XMODE模式、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 }

4.2.4. 初始化伪栈框引用

回到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

4.2.5. 准备别名分析

接下来在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个窗口已经足够,它们作为循环缓存来处理。寄存器窗口起初为BerkeleyRISC机器设计。更多寄存器窗口的细节可以参考p240 1]】。

GCC的源代码中,可以看到IA64SparcXtensa具有寄存器窗口。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位的ABIx86机器可以使用axdxcxxmm0 ~ 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_rtxhard_frame_pointer_rtxarg_pointer_rtxstack_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_REGNUMspARG_POINTER_REGNUMarg(伪寄存器),HARD_FRAME_POINTER_REGNUMbp,而 FRAME_POINTER_REGNUMframe(伪寄存器)。为了保存位置无关代码(PIC)中访问数据项的基址,x86,在重装遍完成前,使用bx寄存器。

gen_rtx_REG返回的rtx对象init_alias_once2751行,传递给gen_rtx_ADDRESS毫无疑问gen_rtx_ADDRESS是与目标机器相关的。对于x86机器,它被定义为gen_rtx_fmt_ecode参数为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

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值