4. genemit 工具
4.1. 代码输出的准备
这个工具从机器描述文件生成文件 insn-emit.c 。 Insn-emit.c 定义了,从机器描述文件中的模式定义生成的,以给定操作数作为参数的模式匹配函数。
775 int
776 main (int argc, char **argv) in genemit.c
777 {
778 rtx desc;
779
780 progname = "genemit";
781
782 if (argc <= 1)
783 fatal ("no input file name");
784
785 if (init_md_reader_args (argc, argv) != SUCCESS_EXIT_CODE)
786 return (FATAL_EXIT_CODE);
787
788 /* Assign sequential codes to all entries in the machine description
789 i n parallel with the tables in insn-output.c. */
790
791 insn_code_number = 0;
792 insn_index_number = 0;
793
794 printf ("/* Generated automatically by the program `genemit'/n/
795 from the machine description file `md'. *//n/n");
796
797 printf ("#include /"config.h/"/n");
798 printf ("#include /"system.h/"/n");
799 printf ("#include /"coretypes.h/"/n");
800 printf ("#include /"tm.h/"/n");
801 printf ("#include /"rtl.h/"/n");
802 printf ("#include /"tm_p.h/"/n");
803 printf ("#include /"function.h/"/n");
804 printf ("#include /"expr.h/"/n");
805 printf ("#include /"optabs.h/"/n");
806 printf ("#include /"real.h/"/n");
807 printf ("#include /"flags.h/"/n");
808 printf ("#include /"output.h/"/n");
809 printf ("#include /"insn-config.h/"/n");
810 printf ("#include /"hard-reg-set.h/"/n");
811 printf ("#include /"recog.h/"/n");
812 printf ("#include /"resource.h/"/n");
813 printf ("#include /"reload.h/"/n");
814 printf ("#include /"toplev.h/"/n");
815 printf ("#include /"ggc.h/"/n/n");
816 printf ("#define FAIL return (end_sequence (), _val)/n");
817 printf ("#define DONE return (_val = get_insns (), end_sequence (), _val)/n/n");
开始看起来都是类似的。在 785 行, init_md_reader_args 读入机器描述文件,并构建对应的 rtx 对象。我们以 genconditions工具 中的 define_insn_and_split 为例,它将被分裂成两个 rtx 对象。
4225 (define_insn_and_split "*fix_truncsi_1" in i386.md
4226 [(set (match_operand:SI 0 "nonimmediate_operand" "=m,?r")
4227 (fix:SI (match_operand 1 "register_operand" "f,f")))]
4228 "TARGET_80387 && FLOAT_MODE_P (GET_MODE (operands[1]))
4229 && !reload_completed && !reload_in_progress
4230 && !SSE_FLOAT_MODE_P (GET_MODE (operands[1]))"
4231 "#"
4232 "&& 1"
4233 [(const_int 0)]
4234 {
4235 ix86_optimize_mode_switching = 1;
4236 operands[2] = assign_386_stack_local (HImode, 1);
4237 operands[3] = assign_386_stack_local (HImode, 2);
4238 if (memory_operand (operands[0], VOIDmode))
4239 emit_insn (gen_fix_truncsi_memory (operands[0], operands[1],
4240 operands[2], operands[3]));
4241 else
4242 {
4243 operands[4] = assign_386_stack_local (SImode, 0);
4244 emit_insn (gen_fix_truncsi_nomemory (operands[0], operands[1],
4245 operands[2], operands[3],
4246 operands[4]));
4247 }
4248 DONE;
4249 }
4250 [(set_attr "type" "fistp")
4251 (set_attr "mode" "SI")])
那么在下面,在 main 中我们将处理这个 rtx 对象。
main (continued)
819 /* Read the machine description. */
820
821 while (1)
822 {
823 int line_no;
824
825 desc = read_md_rtx (&line_no, &insn_code_number);
826 if (desc == NULL)
827 break ;
828
829 switch (GET_CODE (desc))
830 {
831 case DEFINE_INSN:
832 gen_insn (desc, line_no);
833 break ;
834
835 case DEFINE_EXPAND:
836 printf ("/* %s:%d *//n", read_rtx_filename, line_no);
837 gen_expand (desc);
838 break ;
839
840 case DEFINE_SPLIT:
841 printf ("/* %s:%d *//n", read_rtx_filename, line_no);
842 gen_split (desc);
843 break ;
844
845 case DEFINE_PEEPHOLE2:
846 printf ("/* %s:%d *//n", read_rtx_filename, line_no);
847 gen_split (desc);
848 break ;
849
850 default :
851 break ;
852 }
853 ++insn_index_number;
854 }
855
856 /* Write out the routines to add CLOBBERs to a pattern and say whether they
857 clobber a hard reg. */
858 output_add_clobbers ();
859 output_added_clobbers_hard_reg_p ();
860
861 fflush (stdout);
862 return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE);
863 }
4.1. 为 define_insn 产生代码
对于我们 define_insn_and_split 模式的例子,其 define_insn 部分如下。
图 29 : genemit - define_insn_and_split 模式 – insn 部分
这是一个 define_insn 的 rtx 对象,因此 gen_insn 将被调用。
287 static void
288 gen_ins n (rtx insn, int lineno) in genemit.c
289 {
290 int operands;
291 int i;
292
293 /* See if the pattern for this insn ends with a group of CLOBBERs of (hard)
294 registers or MATCH_SCRATCHes. If so, store away the information for
295 later. */
296
297 if (XVEC (insn, 1))
298 {
299 int has_hard_reg = 0;
300
301 for (i = XVECLEN (insn, 1) - 1; i > 0; i--)
302 {
303 if (GET_CODE (XVECEXP (insn, 1, i)) != CLOBBER)
304 break ;
305
306 if (GET_CODE (XEXP (XVECEXP (insn, 1, i), 0)) == REG)
307 has_hard_reg = 1;
308 else if (GET_CODE (XEXP (XVECEXP (insn, 1, i), 0)) != MATCH_SCRATCH)
309 break ;
310 }
对于我们上面的例子,它在 304 行跳出循环,因为这个模式不破坏任何寄存器。
gen_insn (continued)
312 if (i != XVECLEN (insn, 1) - 1)
313 {
314 struct clobber_pat *p;
315 struct clobber_ent *link = xmalloc (sizeof (struct clobber_ent));
316 int j;
317
318 link->code_number = insn_code_number ;
319
320 /* See if any previous CLOBBER_LIST entry is the same as this
321 one. */
322
323 for (p = clobber_list ; p; p = p->next)
324 {
325 if (p->first_clobber != i + 1
326 || XVECLEN (p->pattern, 1) != XVECLEN (insn, 1))
327 continue ;
328
329 for (j = i + 1; j < XVECLEN (insn, 1); j++)
330 {
331 rtx old = XEXP (XVECEXP (p->pattern, 1, j), 0);
332 rtx new = XEXP (XVECEXP (insn, 1, j), 0);
333
334 /* OLD and NEW are the same if both are to be a SCRATCH
335 of the same mode,
336 or if both are registers of the same mode and number. */
337 if (! (GET_MODE (old) == GET_MODE (new)
338 && ((GET_CODE (old) == MATCH_SCRATCH
339 && GET_CODE (new) == MATCH_SCRATCH)
340 || (GET_CODE (old) == REG && GET_CODE (new) == REG
341 && REGNO (old) == REGNO (new)))))
342 break ;
343 }
344
345 if (j == XVECLEN (insn, 1))
346 break ;
347 }
348
349 if (p == 0)
350 {
351 p = xmalloc (sizeof (struct clobber_pat));
352
353 p->insns = 0;
354 p->pattern = insn;
355 p->first_clobber = i + 1;
356 p->next = clobber_list ;
357 p->has_hard_reg = has_hard_reg;
358 clobber_list = p;
359 }
360
361 link->next = p->insns;
362 p->insns = link;
363 }
364 }
上面。对于将破坏寄存器的模式,这个相关的信息必须被保存在 clobber_list 中。它具有如下的定义:
42 struct clobber_pat in genemit.c
43 {
44 struct clobber_ent *insns;
45 rtx pattern;
46 int first_clobber;
47 struct clobber_pat *next;
48 int has_hard_reg;
49 } *clobber_list;
50
51 /* Records one insn that uses the clobber list. */
52
54 {
55 int code_number; /* Counts only insns. */
56 struct clobber_ent *next;
57 };
clobber_pat 为具有相同形式的模式保存 clobber 信息的记录,换而言之,对于两个具有不同形式的模式,它们将属于不同的 clobber_pat 实例。因此 has_hard_reg , first_clobber 对于 clobber_ent 中的所有成员都是有意义的,其中 first_clobber 表示在哪一步这个指定的寄存器将被破坏,而 has_hard_reg 指出是否涉及真实的寄存器。
在 clobber_ent 中的 code_number 区分模式,因为它来自 insn_code_number ,对于指定的模式它是唯一且不变的。显然, clobber_ent 是与模式对应的。对于我们的例子,没有收集 clobber 信息。
gen_insn (continued)
366 /* Don't mention instructions whose names are the null string
367 or begin with '*'. They are in the machine description just
368 to be recognized. */
369 if (XSTR (insn, 0)[0] == 0 || XSTR (insn, 0)[0] == '*')
370 return ;
371
372 printf ("/* %s:%d *//n", read_rtx_filename , lineno);
373
374 /* Find out how many operands this function has. */
375 operands = max_operand_vec (insn, 1);
376 if (max_dup_opno >= operands)
377 fatal ("match_dup operand number has no match_operand");
378 /* Output the function name and argument declarations. */
379 printf ("rtx/ngen_%s (", XSTR (insn, 0));
380 if (operands)
381 for (i = 0; i < operands; i++)
382 if (i)
383 printf (",/n/trtx operand%d ATTRIBUTE_UNUSED", i);
384 else
385 printf ("rtx operand%d ATTRIBUTE_UNUSED", i);
386 else
387 printf ("void");
388 printf (")/n");
389 printf ("{/n");
不过,我们的例子在其名字的开头有“ * ”,这意味着这个指令模式不用于产生 RTL 代码,并且这样的一个名字仅用于在 RTL 转储( dump )中识别该指令。因此在真实世界里,它将在 370 行返回,而不做任何事。
现在,我们假定这个‘ * ’ 压根没有出现过。在 372 行,给出我们来自那里的信息。在 375 行, max_operand_vec 找出在该模式中操作数的个数。这个函数相当简单。对于我们的例子, max_opno 将是 2 。
108 static int
109 max_operand_vec (rtx insn, int arg) in genemit.c
110 {
111 int len = XVECLEN (insn, arg);
112 int i;
113
114 max_opno = -1;
115 max_dup_opno = -1;
116 max_scratch_opno = -1;
117
118 for (i = 0; i < len; i++)
119 max_operand_1 (XVECEXP (insn, arg, i));
120
121 return max_opno + 1;
122 }
72 static void
73 max_operand_1 (rtx x) in genemit.c
74 {
75 RTX_CODE code;
76 int i;
77 int len;
78 const char *fmt;
79
80 if (x == 0)
81 return ;
82
83 code = GET_CODE (x);
84
85 if (code == MATCH_OPERAND || code == MATCH_OPERATOR
86 || code == MATCH_PARALLEL)
87 max_opno = MAX (max_opno , XINT (x, 0));
88 if (code == MATCH_DUP || code == MATCH_OP_DUP || code == MATCH_PAR_DUP)
89 max_dup_opno = MAX (max_dup_opno , XINT (x, 0));
90 if (code == MATCH_SCRATCH)
91 max_scratch_opno = MAX (max_scratch_opno , XINT (x, 0));
92
93 fmt = GET_RTX_FORMAT (code);
94 len = GET_RTX_LENGTH (code);
95 for (i = 0; i < len; i++)
96 {
97 if (fmt[i] == 'e' || fmt[i] == 'u')
98 max_operand_1 (XEXP (x, i));
99 else if (fmt[i] == 'E')
100 {
101 int j;
102 for (j = 0; j < XVECLEN (x, i); j++)
103 max_operand_1 (XVECEXP (x, i, j));
104 }
105 }
106 }
那么,从上面的代码片段,我们将得到如下函数的定义(假定模式名字是 fix_truncsi_1 ,而不是 *fix_truncsi_1 )。
rtx
gen_fix_truncsi_1(rtx operand0 ATTRIBUTE_UNUSED,
rtx operand1 ATTRIBUTE_UNUSED,
rtx operand2 ATTRIBUTE_UNUSED)
{
gen_insn (continued)
392 /* Output code to construct and return the rtl for the instruction body. */
393
394 if (XVECLEN (insn, 1) == 1)
395 {
396 printf (" return ");
397 gen_exp (XVECEXP (insn, 1, 0), DEFINE_INSN, NULL);
398 printf (";/n}/n/n");
399 }
400 else
401 {
402 printf (" return gen_rtx_PARALLEL (VOIDmode, gen_rtvec (%d",
403 XVECLEN (insn, 1));
404
405 for (i = 0; i < XVECLEN (insn, 1); i++)
406 {
407 printf (",/n/t/t");
408 gen_exp (XVECEXP (insn, 1, i), DEFINE_INSN, NULL);
409 }
410 printf ("));/n}/n/n");
411 }
412 }
在 define_pattern 的 rtx 格式中, RTL 模板具有格式‘ E ’—— rtx 对象的向量。在上面 394 行的 XVECLEN 给出了这个向量的大小,而在 408 行 XVECEXP 给出该向量中的元素。对于 gen_exp 的参数 x ,它指向以下的 RTL 模板。
4225 [(set (match_operand:SI 0 "nonimmediate_operand" "=m,?r")
4226 (fix:SI (match_operand 1 "register_operand" "f,f")))]
148 static void
149 gen_exp (rtx x, enum rtx_code subroutine_type, char *used) in genemit.c
150 {
151 RTX_CODE code;
152 int i;
153 int len;
154 const char *fmt;
155
156 if (x == 0)
157 {
158 printf ("NULL_RTX");
159 return ;
160 }
161
162 code = GET_CODE (x);
163
164 switch (code)
165 {
166 case MATCH_OPERAND:
167 case MATCH_DUP:
168 if (used)
169 {
170 if (used[XINT (x, 0)])
171 {
172 printf ("copy_rtx (operand%d)", XINT (x, 0));
173 return ;
174 }
175 used[XINT (x, 0)] = 1;
176 }
177 printf ("operand%d", XINT (x, 0));
178 return ;
179
180 case MATCH_OP_DUP:
181 printf ("gen_rtx (GET_CODE (operand%d), ", XINT (x, 0));
182 if (GET_MODE (x) == VOIDmode)
183 printf ("GET_MODE (operand%d)", XINT (x, 0));
184 else
185 printf ("%smode", GET_MODE_NAME (GET_MODE (x)));
186 for (i = 0; i < XVECLEN (x, 1); i++)
187 {
188 printf (",/n/t/t");
189 gen_exp (XVECEXP (x, 1, i), subroutine_type, used);
190 }
191 printf (")");
192 return ;
193
194 case MATCH_OPERATOR:
195 printf ("gen_rtx (GET_CODE (operand%d)", XINT (x, 0));
196 printf (", %smode", GET_MODE_NAME (GET_MODE (x)));
197 for (i = 0; i < XVECLEN (x, 2); i++)
198 {
199 printf (",/n/t/t");
200 gen_exp (XVECEXP (x, 2, i), subroutine_type, used);
201 }
202 printf (")");
203 return ;
204
205 case MATCH_PARALLEL:
206 case MATCH_PAR_DUP:
207 printf ("operand%d", XINT (x, 0));
208 return ;
209
210 case MATCH_SCRATCH:
211 gen_rtx_scratch (x, subroutine_type);
212 return ;
213
214 case ADDRESS:
215 fatal ("ADDRESS expression code used in named instruction pattern");
216
217 case PC:
218 printf ("pc_rtx");
219 return ;
220
221 case CC0:
222 printf ("cc0_rtx");
223 return ;
224
225 case CONST_INT:
226 if (INTVAL (x) == 0)
227 printf ("const0_rtx");
228 else if (INTVAL (x) == 1)
229 printf ("const1_rtx");
230 else if (INTVAL (x) == -1)
231 printf ("constm1_rtx");
232 else if (INTVAL (x) == STORE_FLAG_VALUE)
233 printf ("const_true_rtx");
234 else
235 {
236 printf ("GEN_INT (");
237 printf (HOST_WIDE_INT_PRINT_DEC_C, INTVAL (x));
238 printf (")");
239 }
240 return ;
241
242 case CONST_DOUBLE:
243 /* These shouldn't be written in MD files. Instead, the appropriate
244 routines in varasm.c should be called. */
245 abort ();
246
247 default :
248 break ;
249 }
250
251 printf ("gen_rtx_");
252 print_code (code);
253 printf (" (%smode", GET_MODE_NAME (GET_MODE (x)));
对于我们这里的例子,编码是 SET ,因此我们直接跑到 251 行。因为这个 set 模式没有带 mode 信息,这个 mode 默认的是 VIODmode 。现在我们得到:
rtx
gen_fix_truncsi_1(rtx operand0 ATTRIBUTE_UNUSED,
rtx operand1 ATTRIBUTE_UNUSED,
rtx operand2 ATTRIBUTE_UNUSED)
{
return gen_rtx_set (VOIDmode,
对于 set 模式,其 rtl 格式是“ ee ”,因此在 263 行 gen_exp 递归入其孩子。
gen_exp (continued)
255 fmt = GET_RTX_FORMAT (code);
256 len = GET_RTX_LENGTH (code);
257 for (i = 0; i < len; i++)
258 {
259 if (fmt[i] == '0')
260 break ;
261 printf (",/n/t");
262 if (fmt[i] == 'e' || fmt[i] == 'u')
263 gen_exp (XEXP (x, i), subroutine_type, used);
264 else if (fmt[i] == 'i')
265 printf ("%u", XINT (x, i));
266 else if (fmt[i] == 's')
267 printf ("/"%s/"", XSTR (x, i));
268 else if (fmt[i] == 'E')
269 {
270 int j;
271 printf ("gen_rtvec (%d", XVECLEN (x, i));
272 for (j = 0; j < XVECLEN (x, i); j++)
273 {
274 printf (",/n/t/t");
275 gen_exp (XVECEXP (x, i, j), subroutine_type, used);
276 }
277 printf (")");
278 }
279 else
280 abort ();
281 }
282 printf (")");
283 }
这个 set 模式的第一个孩子是 match_operand ,我们将进入 166 行。注意到 gen_exp 的参数 used 是 null ,因此我们得到:
rtx
gen_fix_truncsi_1(rtx operand0 ATTRIBUTE_UNUSED,
rtx operand1 ATTRIBUTE_UNUSED,
rtx operand2 ATTRIBUTE_UNUSED)
{
return gen_rtx_set (VOIDmode,
operand0
模式 set 的第二个孩子是 fix ,其 rtl 格式是‘ e ’。再次,我们直接跳到 251 行。并且也将使用 gen_exp 递归其第一个孩子 match_operand 。因此,对于这个 set 模式,我们得到以下代码片段。
rtx
gen_fix_truncsi_1(rtx operand0 ATTRIBUTE_UNUSED,
rtx operand1 ATTRIBUTE_UNUSED,
rtx operand2 ATTRIBUTE_UNUSED)
{
return gen_rtx_SET (VOIDmode,
operand0,
gen_rtx_FIX (SImode,
operand1));
}