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

4.2.11.      更新用于函数调用的物理寄存器信息

回到backend_init,如果源代码要进行尺寸优化或者优化级别高于–O2flag_caller_saves将是true。那么init_caller_save被调用。在这里我们关注所有被函数调用使用的物理寄存器,并且它们没有被排除在跨函数调用外(由regclass.c中的fixed_regscall_used_regs等所指定)。

注意到对于这个评估,它也是在由创建伪函数上下文一节中所创建的伪函数上下文中执行。

 

111     void

112     init_caller_save (void)                                                                       in caller-save.c

113     {

114       rtx addr_reg;

115       int offset;

116       rtx address;

117       int i, j;

118       enum machine_mode mode;

119       rtx savepat, restpat;

120      rtx test_reg, test_mem;

121      rtx saveinsn, restinsn;

122   

123      /* First find all the registers that we need to deal with and all

124        the modes that they can have. If we can't find a mode to use,

125        we can't have the register live over calls.  */

126   

127      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)

128      {

129        if (call_used_regs[i] && ! call_fixed_regs[i])

130        {

131          for (j = 1; j <= MOVE_MAX_WORDS; j++)

132          {

133            regno_save_mode[i][j] = HARD_REGNO_CALLER_SAVE_MODE (i, j,

134                                                             VOIDmode);

135            if (regno_save_mode[i][j] == VOIDmode && j == 1)

136            {

137              call_fixed_regs[i] = 1;

138              SET_HARD_REG_BIT (call_fixed_reg_set, i);

139            }

140          }

141        }

142        else

143          regno_save_mode[i][1] = VOIDmode;

144    }

 

131MOVE_MAX_WORDS具有定义如下。

 

47      #define MOVE_MAX_WORDS (MOVE_MAX / UNITS_PER_WORD)  in caller-save.c

 

MOVE_MAX,对于x86机器,被定义为16,它是我们在一条合理的快速指令中,能在内存间所能移动的最大字节数。

 

1137   #define HARD_REGNO_CALLER_SAVE_MODE(REGNO, NREGS, MODE)/ in i386.h

1138     (CC_REGNO_P (REGNO) ? VOIDmode                                  /

1139      : (MODE) == VOIDmode && (NREGS) != 1 ? VOIDmode                   /

1140      : (MODE) == VOIDmode ? choose_hard_reg_mode ((REGNO), (NREGS), false)/

1141      : (MODE) == HImode && !TARGET_PARTIAL_REG_STALL ? SImode             /

1142      : (MODE) == QImode && (REGNO) >= 4 && !TARGET_64BIT ? SImode        /

1143      : (MODE))

 

HARD_REGNO_CALLER_SAVE_MODE尝试找出要求NREGS个连续的从REGNO编号开始的寄存器的模式,如果没有这样的模式,将返回VOIDmode。这个结果将被保存在regno_save_mode[REGNO][NREGS]。看到127行,循环控制变量j的初始值是1,因此regno_save_mode[REGNO][0]永远不会用到,一直为VOIDmode

根据init_reg_sets_16x86机器的寄存器分call_fixed_regs[REGNO]的值,如果是1,表示对应的寄存器有固定的用途,或者为函数调用机制所使用的寄存器,它不能在跨越多个调用的时段内保存数值,即便通过暂存和恢复的手段。并且回忆call_fixed_regscall_used_regs的子集call_used_regs还包括了调用间被破坏的寄存器这些寄存器通过暂存和恢复的手段可以跨越调用。

在为不同数据尺寸收集了有效模式后init_caller_save继续检查,那些我们可以不需要辅助寄存器(scratching register)或其他复杂性,而保存及恢复一个寄存器的条件。

 

init_caller_save (continue)

 

146    /* The following code tries to approximate the conditions under which

147       we can easily save and restore a register without scratch registers or

148       other complexities. It will usually work, except under conditions where

149       the validity of an insn operand is dependent on the address offset.

150       No such cases are currently known.

151   

152       We first find a typical offset from some BASE_REG_CLASS register.

153       This address is chosen by finding the first register in the class

154       and by finding the smallest power of two that is a valid offset from

155       that register in every mode we will use to save registers.  */

156   

157    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)

158       if (TEST_HARD_REG_BIT

159            (reg_class_contents

160            [(int) MODE_BASE_REG_CLASS (regno_save_mode [i][1])], i))

161         break;

162   

163    if (i == FIRST_PSEUDO_REGISTER)

164       abort ();

165   

166    addr_reg = gen_rtx_REG (Pmode, i);

167   

168    for (offset = 1 << (HOST_BITS_PER_INT / 2); offset; offset >>= 1)

169    {

170       address = gen_rtx_PLUS (Pmode, addr_reg, GEN_INT (offset));

171   

172       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)

173         if (regno_save_mode[i][1] != VOIDmode

174                && ! strict_memory_address_p (regno_save_mode[i][1], address))

175           break;

176   

177       if (i == FIRST_PSEUDO_REGISTER)

178         break;

179    }

180   

181    /* If we didn't find a valid address, we must use register indirect.  */

182    if (offset == 0)

183       address = addr_reg;

 

MODE_BASE_REG_CLASS(mode),对于x86机器,是GENERAL_REGS,当用作reg_class_contents的索引时,将返回代表eaxebxecxedxesiediebpespr8 ~ r15的位图。因此对于157行的FOR循环,i = 0 (eax) 将退出循环。这个寄存器在166行用于保存地址,然后在170行,进一步创建如下图中的地址。

如我们在什么所见,regno_save_mode[REGNO][1] ,如果不是VOIDmode,则表示了编号为REGNO的寄存器所能支持的模式。而strict_memory_address_p则检查这个模式是否能支持地址。实际上strict_memory_address_p调用GO_IF_LEGITIMATE_ADDRESS,而这个宏则调用legitimate_address_p

GCC-3.4.6源代码学习笔记(50)_第1张图片

31:地址表达式的rtx对象

浏览legitimate_address_p,已知在上图中,base指向REGdisp指向const_int,该函数将检查保存base的寄存器(eax)及disp是否有效。对于上图中所展示的地址, strict_memory_address_p将返回true。注意到在strict_memory_address_p中,在regno_save_mode[REGNO][1]中的模式在处理中没有用到,仅地址表达式被评估,因此在172行的FOR循环中,寄存器0 ~ FIRST_PSEUDO_REGISTER被成功处理。

 

init_caller_save (continue)

 

185    /* Next we try to form an insn to save and restore the register. We

186       see if such an insn is recognized and meets its constraints.

187   

188       To avoid lots of unnecessary RTL allocation, we construct all the RTL

189       once, then modify the memory and register operands in-place.  */

190   

191    test_reg = gen_rtx_REG (VOIDmode, 0);

192    test_mem = gen_rtx_MEM (VOIDmode, address);

193    savepat = gen_rtx_SET (VOIDmode, test_mem, test_reg);

194    restpat = gen_rtx_SET (VOIDmode, test_reg, test_mem);

195   

196    saveinsn = gen_rtx_INSN (VOIDmode, 0, 0, 0, 0, 0, savepat, -1, 0, 0);

197    restinsn = gen_rtx_INSN (VOIDmode, 0, 0, 0, 0, 0, restpat, -1, 0, 0);

198   

199    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)

200       for (mode = 0 ; mode < MAX_MACHINE_MODE; mode++)

201         if (HARD_REGNO_MODE_OK (i, mode))

202         {

203           int ok;

204   

205           /* Update the register number and modes of the register

206             and memory operand.  */

207           REGNO (test_reg) = i;

208           PUT_MODE (test_reg, mode);

209           PUT_MODE (test_mem, mode);

210   

211            /* Force re-recognition of the modified insns.  */

212           INSN_CODE (saveinsn) = -1;

213           INSN_CODE (restinsn) = -1;

214   

215           reg_save_code[i][mode] = recog_memoized (saveinsn);

216           reg_restore_code[i][mode] = recog_memoized (restinsn);

217   

218           /* Now extract both insns and see if we can meet their

219             constraints.  */

220           ok = (reg_save_code[i][mode] != -1

221               && reg_restore_code[i][mode] != -1);

222           if (ok)

223           {

224             extract_insn (saveinsn);

225             ok = constrain_operands (1);

226             extract_insn (restinsn);

227             ok &= constrain_operands (1);

228           }

229   

230           if (! ok)

231           {

232             reg_save_code[i][mode] = -1;

233             reg_restore_code[i][mode] = -1;

234           }

235         }

236         else

237         {

238           reg_save_code[i][mode] = -1;

239           reg_restore_code[i][mode] = -1;

240         }

241   

242    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)

243       for (j = 1; j <= MOVE_MAX_WORDS; j++)

244         if (reg_save_code [i][regno_save_mode[i][j]] == -1)

245         {

246           regno_save_mode[i][j] = VOIDmode;

247           if (j == 1)

248           {

249             call_fixed_regs[i] = 1;

250             SET_HARD_REG_BIT (call_fixed_reg_set, i);

251           }

252         }

253    }

 

接下来,只要内存地址是有效的,如果寄存器可以通过一个简单的SET后端指令(见193194行)保存符合其最大机器模式的数据,那么该寄存器可用于调用者保存(caller-save)。我们将这些指令的INSN_CODE记录在reg_save_code[REGNO][mode]中(recog_memoized如果能识别该指令,将返回其INSN_CODE)。另外,reg_restore_code[REGNO][mode] 有可能不能被识别,因为在我们产生这些指令时,这些地址可能不是有效的。

为此,需要生成评估所用的rtx对象。 上面,在196197行,gen_rtx_INSN创建了如下2INSN对象。

 

 

32:寄存器->内存,初始rtx对象

 

33:内存->寄存器,初始rtx对象

199行的FOR循环,遍历了所有可用的寄存器及机器模式,检查将寄存器保存入内存,及从内存恢复寄存器是否可行。从207213行,重置了寄存器编号,机器模式,及指令码。注意到在212213行,指令码被设置为-1,在recog_memoized中,在215216行,这个值将触发对recog的调用。这个函数如果能识别该INSN对象,将返回指令码;否则返回-1

如果指定的寄存器及机器模式被recog所识别,那么在224~227行通过extract_insn constrain_operands检查该INSN对象的有效性。接着在242行,进一步根据reg_save_code[REGNO][1]更新call_fixed_regs

 

4.2.12.      清除伪函数上下文

在此,后端的初始化已经完成,现在需要清除伪函数上下文。

 

6861 void

6862 expand_dummy_function_end (void)                                                          in function.c

6863 {

6864   /* End any sequences that failed to be closed due to syntax errors.  */

6865   while (in_sequence_p ())

6866     end_sequence ();

6867

6868   /* Outside function body, can't compute type's actual size

6869     until next function's body starts.  */

6870  

6871     free_after_parsing (cfun);

6872     free_after_compilation (cfun);

6873     cfun = 0;

6874   }

 

这里free_after_parsing不做任何事情,而free_after_compilation将重置cfun的大部分域。

 

你可能感兴趣的:(优化,function,Class,Parsing,Allocation,compilation)