工具 genrecog 用于产生识别及转换 RTL 到汇编的转换引擎。在这里我们看到该工具如何从机器描述文件构建出这个引擎——我们以 i386 系统为例子,这个机器描述文件是 i386.md 。
2612 int
2613 main (int argc, char **argv) in genrecog.c
2614 {
2615 rtx desc;
2616 struct decision_head recog_tree, split_tree, peephole2_tree, h;
2617
2618 progname = "genrecog";
2619
2620 memset (&recog_tree, 0, sizeof recog_tree);
2621 memset (&split_tree, 0, sizeof split_tree);
2622 memset (&peephole2_tree, 0, sizeof peephole2_tree);
2623
2624 if (argc <= 1)
2625 fatal ("no input file name");
在 2616 行,结构体 decision_head 保存了转换引擎的决策树( decision tree )。它具有如下的定义。
73 struct decision_head in genrecog.c
74 {
75 struct decision *first;
76 struct decision *last;
77 };
而在上面 75 行, decision 的定义如下。
125 struct decision in genrecog.c
126 {
127 struct decision_head success; /* Nodes to test on success. */
128 struct decision *next; /* Node to test on failure. */
129 struct decision *prev; /* Node whose failure tests us. */
130 struct decision *afterward; /* Node to test on success,
131 but failure of successor nodes. */
132
133 const char *position; /* String denoting position in pattern. */
134
135 struct decision_test *tests; /* The tests for this node. */
136
137 int number; /* Node number, used for labels */
138 int subroutine_number; /* Number of subroutine this node starts */
139 int need_label; /* Label needs to be output. */
140 };
在 135 行, decision_test 保存了决策所需要的测试及其结果。
83 struct decision_test in genrecog.c
84 {
85 /* A linked list through the tests attached to a node. */
86 struct decision_test *next;
87
88 /* These types are roughly in the order in which we'd like to test them. */
89 enum decision_type
90 {
91 DT_mode, DT_code, DT_veclen,
92 DT_elt_zero_int, DT_elt_one_int, DT_elt_zero_wide, DT_elt_zero_wide_safe,
93 DT_veclen_ge, DT_dup, DT_pred, DT_c_test,
94 DT_accept_op, DT_accept_insn
95 } type;
96
97 union
98 {
99 enum machine_mode mode; /* Machine mode of node. */
100 RTX_CODE code; /* Code to test. */
101
102 struct
103 {
104 const char *name; /* Predicate to call. */
105 int index; /* Index into `preds' or -1. */
106 enum machine_mode mode; /* Machine mode for node. */
107 } pred;
108
109 const char *c_test; /* Additional test to perform. */
110 int veclen; /* Length of vector. */
111 int dup; /* Number of operand to compare against. */
112 HOST_WIDE_INT intval; /* Value for XINT for XWINT. */
113 int opno; /* Operand number matched. */
114
115 struct {
116 int code_number; /* Insn number matched. */
117 int lineno; /* Line number of the insn. */
118 int num_clobbers_to_add; /* Number of CLOBBERs to be added. */
119 } insn;
120 } u;
121 };
首先我们初始化 recog_tree , split_tree , peephole2_tree 及 h 的所有 decision_head 实例为 0 。然后需要初始化读入器来读入机器描述文件( md 文件)。
main (continued, genrecog)
2627 if (init_md_reader_args (argc, argv) != SUCCESS_EXIT_CODE)
2628 return (FATAL_EXIT_CODE);
2629
2630 next_insn_code = 0;
2631 next_index = 0;
2632
2633 write_header ();
在 2627 行,函数 init_md_reader_args 处理命令行选项。对于 genrecog 工具,它仅接受 -I 选项。这个选项显示了查找 md 文件的附加路径。然后该函数调用 init_md_reader 。这个函数我们已经在 genconditions工具 一节中看过。它从 md 文件中读入模式,构建相应的 rtx 对象并链入指定的链表。
注意到在 init_md_reader 的从 928 到 933 行, genrecog 现在把由 genconditions 构建的 insn-conditions 放入哈希表 condition_table 中。并且记住访问的次序是: define_attr , define_insn ,然后其它。
所有的工具都有这一步,不过对于除 genconditions 以外的其它工具,可以进行一些优化,因为它们可以,通过由 genconditions 产生的 insn_conditions ,过滤出不活动的模式。
main (continued, genrecog)
2635 /* Read the machine description. */
2636
2637 while (1)
2638 {
2639 desc = read_md_rtx (&pattern_lineno , &next_insn_code );
2640 if (desc == NULL)
2641 break ;
2642
2643 if (GET_CODE (desc) == DEFINE_INSN)
2644 {
2645 h = make_insn_sequence (desc, RECOG);
2646 merge_trees (&recog_tree, &h);
2647 }
2648 else if (GET_CODE (desc) == DEFINE_SPLIT)
2649 {
2650 h = make_insn_sequence (desc, SPLIT);
2651 merge_trees (&split_tree, &h);
2652 }
2653 else if (GET_CODE (desc) == DEFINE_PEEPHOLE2)
2654 {
2655 h = make_insn_sequence (desc, PEEPHOLE2);
2656 merge_trees (&peephole2_tree, &h);
2657 }
2658
2659 next_index ++;
2660 }
在 genconditions工具 一节我们已经碰到了 read_md_rtx ,现在我们仔细看一下它做什么。
965 rtx
966 read_md_rtx (int *lineno, int *seqnr) in gensupport.c
967 {
968 struct queue_elem **queue, *elem;
969 rtx desc;
970
971 discard:
972
973 /* Read all patterns from a given queue before moving on to the next. */
974 if (define_attr_queue != NULL)
975 queue = &define_attr_queue ;
976 else if (define_insn_queue != NULL)
977 queue = &define_insn_queue ;
978 else if (other_queue != NULL)
979 queue = &other_queue ;
980 else
981 return NULL_RTX;
982
983 elem = *queue;
984 *queue = elem->next;
985 desc = elem->data;
986 read_rtx_filename = elem->filename;
987 *lineno = elem->lineno;
988 *seqnr = sequence_num ;
989
990 free (elem);
991
992 /* Discard insn patterns which we know can never match (because
993 their C test is provably always false). If insn_elision is
994 false, our caller needs to see all the patterns. Note that the
995 elided patterns are never counted by the sequence numbering; it
996 it is the caller's responsibility, when insn_elision is false, not
997 to use elided pattern numbers for anything. */
998 switch (GET_CODE (desc))
999 {
1000 case DEFINE_INSN:
1001 case DEFINE_EXPAND:
1002 if (maybe_eval_c_test (XSTR (desc, 2)) != 0)
1003 sequence_num ++;
1004 else if (insn_elision )
1005 goto discard;
1006 break ;
1007
1008 case DEFINE_SPLIT:
1009 case DEFINE_PEEPHOLE:
1010 case DEFINE_PEEPHOLE2:
1011 if (maybe_eval_c_test (XSTR (desc, 1)) != 0)
1012 sequence_num ++;
1013 else if (insn_elision )
1014 goto discard;
1015 break ;
1016
1017 default :
1018 break ;
1019 }
1020
1021 return desc;
1022 }
在 genrecog 中, maybe_eval_c_test 可能为模式执行非平凡( non-trivial )的测试。
1067 int
1068 maybe_eval_c_test (const char *expr) in gensupport.c
1069 {
1070 const struct c_test *test;
1071 struct c_test dummy;
1072
1073 if (expr[0] == 0)
1074 return 1;
1075
1076 if (insn_elision_unavailable)
1077 return -1;
1078
1079 dummy.expr = expr;
1080 test = htab_find (condition_table , &dummy);
1081 if (!test)
1082 abort ();
1083
1084 return test->value;
1085 }
注意到在 1084 行, test 从 condtion_table 获取,其内容来自由 genconditions 产生的 insn_condition ,正如我们在 init_md_reader 看到那样。 test 的 value 在 genconditions工具 一节中确定。在 write_one_condition 的 157 行, value 由 MAYBE_EVAL(expr) 返回。 MAYBE_EVAL 也是由 genconditions 按以下方式定义:
123 puts ("/ in genconditions.c
124 /* If we don't have __builtin_constant_p, or it's not acceptable in/n/
125 array initializers, fall back to assuming that all conditions/n/
126 potentially vary at run time. It works in 3.0.1 and later; 3.0/n/
127 only when not optimizing. */ /n/
128 #if (GCC_VERSION >= 3001) || ((GCC_VERSION == 3000) && !__OPTIMIZE__)/n/
129 # define MAYBE_EVAL(expr) (__builtin_constant_p(expr) ? (int) (expr) : -1)/n/
130 #else /n/
131 # define MAYBE_EVAL(expr) -1/n/
132 #endif /n");
__builtin_constant_p 返回整数 1 ,如果该参数已知是一个编译时常量;返回 0 ,如果它不能确定为编译时常量。一个 0 返回值不表示这个值不是一个常量,仅是表明 GCC 不能证明,在给定的 -O 选项(优化)值下,它是一个常量。
因此在 read_md_rtx 的 1011 行,在工具处理这个机器描述文件过程中,跳过那些条件测试部分已知为 false 的模式。这可以节省处理时间,并减小产生出来文件的大小。而那些条件测试部分已知为 true ,或那些条件测试部分不能确信为常量的模式,为 read_md_rtx 返回,并将由 make_insn_sequence 处理。
2413 static struct decision_head
2414 make_insn_sequence (rtx insn, enum routine_type type) in genrecog.c
2415 {
2416 rtx x;
2417 const char *c_test = XSTR (insn, type == RECOG ? 2 : 1);
2418 int truth = maybe_eval_c_test (c_test);
2419 struct decision *last;
2420 struct decision_test *test, **place;
2421 struct decision_head head;
2422 char c_test_pos[2];
2423
2424 /* We should never see an insn whose C test is false at compile time. */
2425 if (truth == 0)
2426 abort ();
2427
2428 record_insn_name (next_insn_code , (type == RECOG? XSTR (insn, 0): NULL));
对于 define_insn 模式,模式名由函数 record_insn_name 记录。而对于其它匿名模式,该函数将为之构建一个 insn+’codeno’ 形式的名字。所有这些名字被保存在全局数组 insn_name_ptr 中。
2685 static void
2686 record_insn_name (int code, const char *name) in genrecog.c
2687 {
2688 static const char *last_real_name = "insn";
2689 static int last_real_code = 0;
2690 char *new;
2691
2692 if (insn_name_ptr_size <= code)
2693 {
2694 int new_size;
2695 new_size = (insn_name_ptr_size ? insn_name_ptr_size * 2 : 512);
2696 insn_name_ptr = xrealloc (insn_name_ptr , sizeof (char *) * new_size);
2697 memset (insn_name_ptr + insn_name_ptr_size , 0,
2698 sizeof (char *) * (new_size - insn_name_ptr_size ));
2699 insn_name_ptr_size = new_size;
2700 }
2701
2702 if (!name || name[0] == '/0')
2703 {
2704 new = xmalloc (strlen (last_real_name) + 10);
2705 sprintf (new, "%s+%d", last_real_name, code - last_real_code);
2706 }
2707 else
2708 {
2709 last_real_name = new = xstrdup (name);
2710 last_real_code = code;
2711 }
2712
2713 insn_name_ptr [code] = new;
2714 }
下面在 make_insn_sequence 中,模式 define_peephole2 描述了特定的指令序列,通过由指定的输出序列替代之,体现了优化的机会。这里我们跳过这个模式。
make_insn_sequence (continued)
2430 c_test_pos[0] = '/0';
2431 if (type == PEEPHOLE2)
2432 {
2433 int i, j;
2434
2435 /* peephole2 gets special treatment:
2436 - X always gets an outer parallel even if it's only one entry
2437 - we remove all traces of outer-level match_scratch and match_dup
2438 expressions here. */
2439 x = rtx_alloc (PARALLEL);
2440 PUT_MODE (x, VOIDmode);
2441 XVEC (x, 0) = rtvec_alloc (XVECLEN (insn, 0));
2442 for (i = j = 0; i < XVECLEN (insn, 0); i++)
2443 {
2444 rtx tmp = XVECEXP (insn, 0, i);
2445 if (GET_CODE (tmp) != MATCH_SCRATCH && GET_CODE (tmp) != MATCH_DUP)
2446 {
2447 XVECEXP (x, 0, j) = tmp;
2448 j++;
2449 }
2450 }
2451 XVECLEN (x, 0) = j;
2452
2453 c_test_pos[0] = 'A' + j - 1;
2454 c_test_pos[1] = '/0';
2455 }
2456 else if (XVECLEN (insn, type == RECOG) == 1)
2457 x = XVECEXP (insn, type == RECOG, 0);
2458 else
2459 {
2460 x = rtx_alloc (PARALLEL);
2461 XVEC (x, 0) = XVEC (insn, type == RECOG);
2462 PUT_MODE (x, VOIDmode);
2463 }
2464
2465 validate_pattern (x, insn, NULL_RTX, 0);
上面 , 获取了模式的 RTL 模板 ( template ) 部分 ( RTL 模板的定义参考 DEFINE_INSN模式的概览 一节)。对于那些具有多个指令的 RTL 模板, 2458 到 2463 行为之构建了一个 rtx 的 PARALLEL 对象。
在机器描述文件中的指令描述模式的 RTL 模板,在提取后,将由 validate_pattern 来验证有效性。这里我们再次使用上面的例子(注意:实际上这个例子将在 read_md_rtx 中被过滤掉,因为 maybe_eval_c_test 对其条件测试部分返回 0 。我们可以在 genextract工具 一节确认这一点。不过,这个事实并不影响它在这里作为例子)。
467 (define_insn "cmpdi_ccno_1_rex64" in i386.md
468 [(set (reg 17)
469 (compare (match_operand:DI 0 "nonimmediate_operand" "r,?mr")
470 (match_operand:DI 1 "const0_operand" "n,n")))]
471 "TARGET_64BIT && ix86_match_ccmode (insn, CCNOmode)"
472 "@
473 test{q}/t{%0, %0|%0, %0}
474 cmp{q}/t{%1, %0|%0, %1}"
475 [(set_attr "type" "test,icmp")
476 (set_attr "length_immediate" "0,1")
477 (set_attr "mode" "DI")])
定义中的 RTL 模板是:
[(set (reg 17)
(compare (match_operand:DI 0 "nonimmediate_operand" "r,?mr")
(match_operand:DI 1 "const0_operand" "n,n")))]
实际上,这里的这个模式已经被转换成 rtx 对象,如 图 1 所示。不过树的结构仍被保留。
从上面的模式我们可以看到,首先为了方便从模式构建 rtx 树,模式采用了后序记法( prefix notation )。其次, RTL 语言接近于汇编语言,它采用了容易转换为汇编的形式。因为它处于高级编程语言及汇编语言之间,其关键字更类似于编程语言,但行为上类似汇编,并且没有编程语言中 for 及 if 语句的对应物。其关键字可以粗略地分为:
赋值 - “set”
算术操作 - “plus, minus, mul, div, mod”
逻辑操作 - “and, or, xor, ior”
位操作 - “ashift, lshift”
函数调用 - “call”
函数退出 - “return”
操作符选择子 - “match_operand, match_dup, match_scratch”
操作数选择子 - “match_operator, match_op_dup”
类型转换 - “zero_extract, sign_extract, strict_low_part, strict_high_part”
取址 - “label_ref, symbol_ref”
比较 - “compare, test”
其它 - “clobber”
好了,现在看一下 validate_pattern 可以为我们做什么。
460 static void
461 validate_pattern (rtx pattern, rtx insn, rtx set, int set_code) in genrecog.c
462 {
463 const char *fmt;
464 RTX_CODE code;
465 size_t i, len;
466 int j;
467
468 code = GET_CODE (pattern);
469 switch (code)
470 {
471 case MATCH_SCRATCH:
472 return ;
473 case MATCH_DUP:
474 case MATCH_OP_DUP:
475 case MATCH_PAR_DUP:
476 if (find_operand (insn, XINT (pattern, 0), pattern) == pattern)
477 {
478 message_with_line (pattern_lineno ,
479 "operand %i duplicated before defined",
480 XINT (pattern, 0));
481 error_count ++;
482 }
483 break ;
484 case MATCH_INSN:
485 case MATCH_OPERAND:
486 case MATCH_OPERATOR:
487 {
…
635 }
636
637 case SET:
638 {
639 enum machine_mode dmode, smode;
640 rtx dest, src;
641
642 dest = SET_DEST (pattern);
643 src = SET_SRC (pattern);
644
645 /* STRICT_LOW_PART is a wrapper. Its argument is the real
646 destination, and it's mode should match the source. */
647 if (GET_CODE (dest) == STRICT_LOW_PART)
648 dest = XEXP (dest, 0);
649
650 /* Find the referent for a DUP. */
651
652 if (GET_CODE (dest) == MATCH_DUP
653 || GET_CODE (dest) == MATCH_OP_DUP
654 || GET_CODE (dest) == MATCH_PAR_DUP)
655 dest = find_operand (insn, XINT (dest, 0), NULL);
656
657 if (GET_CODE (src) == MATCH_DUP
658 || GET_CODE (src) == MATCH_OP_DUP
659 || GET_CODE (src) == MATCH_PAR_DUP)
660 src = find_operand (insn, XINT (src, 0), NULL);
661
662 dmode = GET_MODE (dest);
663 smode = GET_MODE (src);
664
665 /* The mode of an ADDRESS_OPERAND is the mode of the memory
666 reference, not the mode of the address. */
667 if (GET_CODE (src) == MATCH_OPERAND
668 && ! strcmp (XSTR (src, 1), "address_operand"))
669 ;
670
671 /* The operands of a SET must have the same mode unless one
672 is VOIDmode. */
673 else if (dmode != VOIDmode && smode != VOIDmode && dmode != smode)
674 {
675 message_with_line (pattern_lineno ,
676 "mode mismatch in set: %smode vs %smode",
677 GET_MODE_NAME (dmode), GET_MODE_NAME (smode));
678 error_count ++;
679 }
680
681 /* If only one of the operands is VOIDmode, and PC or CC0 is
682 not involved, it's probably a mistake. */
683 else if (dmode != smode
684 && GET_CODE (dest) != PC
685 && GET_CODE (dest) != CC0
686 && GET_CODE (src) != PC
687 && GET_CODE (src) != CC0
688 && GET_CODE (src) != CONST_INT)
689 {
690 const char *which;
691 which = (dmode == VOIDmode ? "destination" : "source");
692 message_with_line (pattern_lineno ,
693 "warning: %s missing a mode?", which);
694 }
695
696 if (dest != SET_DEST (pattern))
697 validate_pattern (dest, insn, pattern, '=');
698 validate_pattern (SET_DEST (pattern), insn, pattern, '=');
699 validate_pattern (SET_SRC (pattern), insn, NULL_RTX, 0);
700 return ;
701 }
对于我们的模式例子,它首先进入 637 行。在 642 及 643 行,获取了 insn 的,对应源及目的,的 rtx 对象。这里它们是:
目的: (reg 17)
源:
(compare (match_operand:DI 0 "nonimmediate_operand" "r,?mr")
(match_operand:DI 1 "const0_operand" "n,n"))
对于这些 rtx 对象,我们将跳过上面的 652 及 657 行。在 662 及 663 行获取了这些 rtx 对象的 mode 。不过在构建这些 rtx 对象时,没有给它们指定 mode ,因此这个值应该是 0 – 即 VOIDmode 。因而代码直接跑到 698 行,递归地验证“源”及“目的” rtx 对象。
validate_pattern (continued)
703 case CLOBBER:
704 validate_pattern (SET_DEST (pattern), insn, pattern, '=');
705 return ;
706
707 case ZERO_EXTRACT:
708 validate_pattern ( XEXP (pattern, 0), insn, set, set ? '+' : 0);
709 validate_pattern ( XEXP (pattern, 1), insn, NULL_RTX, 0);
710 validate_pattern ( XEXP (pattern, 2), insn, NULL_RTX, 0);
711 return ;
712
713 case STRICT_LOW_PART:
714 validate_pattern ( XEXP (pattern, 0), insn, set, set ? '+' : 0);
715 return ;
716
717 case LABEL_REF:
718 if (GET_MODE ( XEXP (pattern, 0)) != VOIDmode)
719 {
720 message_with_line (pattern_lineno ,
721 "operand to label_ref %smode not VOIDmode",
722 GET_MODE_NAME (GET_MODE (XEXP (pattern, 0))));
723 error_count ++;
724 }
725 break ;
726
727 default :
728 break ;
729 }
对于我们的“源”及“目的” rtx 对象,它们将进入上面代码的 727 行。然后它们的内容将被根据它们的 rtx 格式进行验证。
对于“目的”对象,格式是“ i00 ”,它将通过 746 行,但不做任何事。对于“源”对象,其格式是“ ee ”,它两次通过 737 行来处理其两个孩子。
validate_pattern (continued)
731 fmt = GET_RTX_FORMAT (code);
732 len = GET_RTX_LENGTH (code);
733 for (i = 0; i < len; i++)
734 {
735 switch (fmt[i])
736 {
737 case 'e': case 'u':
738 validate_pattern (XEXP (pattern, i), insn, NULL_RTX, 0);
739 break ;
740
741 case 'E':
742 for (j = 0; j < XVECLEN (pattern, i); j++)
743 validate_pattern (XVECEXP (pattern, i, j), insn, NULL_RTX, 0);
744 break ;
745
746 case 'i': case 'w': case '0': case 's':
747 break ;
748
749 default :
750 abort ();
751 }
752 }
753 }
我们只看第一个孩子。它是:“ match_operand:DI 0 "nonimmediate_operand" "r,?mr" ”。在进入 validate_pattern 之后,它将通过 485 行。下面的代码包含在 validate_pattern 中 484~486 行的 case 语句中。注意到下面, insn 仍然指向机器描述文件中的指令描述模式。这里是这个 define_insn 。
validate_pattern (continued)
488 const char *pred_name = XSTR (pattern, 1);
489 int allows_non_lvalue = 1, allows_non_const = 1;
490 int special_mode_pred = 0;
491 const char *c_test;
492
493 if (GET_CODE (insn) == DEFINE_INSN)
494 c_test = XSTR (insn, 2);
495 else
496 c_test = XSTR (insn, 1);
497
498 if (pred_name[0] != 0)
499 {
500 for (i = 0; i < NUM_KNOWN_PREDS; i++)
501 if (! strcmp (preds [i].name, pred_name))
502 break ;
503
504 if (i < NUM_KNOWN_PREDS)
505 {
506 int j;
507
508 allows_non_lvalue = allows_non_const = 0;
509 for (j = 0; preds [i].codes[j] != 0; j++)
510 {
511 RTX_CODE c = preds [i].codes[j];
512 if (c != LABEL_REF
513 && c != SYMBOL_REF
514 && c != CONST_INT
515 && c != CONST_DOUBLE
516 && c != CONST
517 && c != HIGH
518 && c != CONSTANT_P_RTX)
519 allows_non_const = 1;
520
521 if (c != REG
522 && c != SUBREG
523 && c != MEM
524 && c != ADDRESSOF
525 && c != CONCAT
526 && c != PARALLEL
527 && c != STRICT_LOW_PART)
528 allows_non_lvalue = 1;
529 }
530 }
531 else
532 {
533 #ifdef PREDICATE_CODES
534 /* If the port has a list of the predicates it uses but
535 omits one, warn. */
536 message_with_line (pattern_lineno ,
537 "warning: `%s' not in PREDICATE_CODES",
538 pred_name);
539 #endif
540 }
541
542 for (i = 0; i < NUM_SPECIAL_MODE_PREDS; ++i)
543 if (strcmp (pred_name, special_mode_pred_table [i]) == 0)
544 {
545 special_mode_pred = 1;
546 break ;
547 }
548 }
上面在 488 行,提取了“ nonimmediate_operand ”,它是匹配操作数的断言( predicate )。在 501 行的 preds 具有如下的定义。其 codes 域包含了一个 rtl 编码的列表,这些编码可能匹配一个定义在 recog.c 中的断言。
186 static const struct pred_table in genrecog.c
187 {
188 const char *const name;
189 const RTX_CODE codes[NUM_RTX_CODE];
190 } preds [] = {
191 {"general_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
192 LABEL_REF, SUBREG, REG, MEM, ADDRESSOF}},
193 #ifdef PREDICATE_CODES
194 PREDICATE_CODES
195 #endif
196 {"address_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
197 LABEL_REF, SUBREG, REG, MEM, ADDRESSOF,
198 PLUS, MINUS, MULT}},
199 {"register_operand", {SUBREG, REG, ADDRESSOF}},
200 {"pmode_register_operand", {SUBREG, REG, ADDRESSOF}},
201 {"scratch_operand", {SCRATCH, REG}},
202 {"immediate_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
203 LABEL_REF}},
204 {"const_int_operand", {CONST_INT}},
205 {"const_double_operand", {CONST_INT, CONST_DOUBLE}},
206 {"nonimmediate_operand", {SUBREG, REG, MEM, ADDRESSOF}},
207 {"nonmemory_operand", {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF,
208 LABEL_REF, SUBREG, REG, ADDRESSOF}},
209 {"push_operand", {MEM}},
210 {"pop_operand", {MEM}},
211 {"memory_operand", {SUBREG, MEM}},
212 {"indirect_operand", {SUBREG, MEM}},
213 {"comparison_operator", {EQ, NE, LE, LT, GE, GT, LEU, LTU, GEU, GTU,
214 UNORDERED, ORDERED, UNEQ, UNGE, UNGT, UNLE,
215 UNLT, LTGT}}
216 };
“ nonimmediate_operand ” 在 206 行。根据其内容, 521 行将被跳过。而 allows_non_const 在 519 行被设置。显然, allows_non_const 只是表示该断言是否接受非常量参数,而 allows_non_lvalue 显示该断言是否接受非左值参数。为某些需要特殊的处理的 mode 定义了 special_mode_pred_table 。它具有如下定义:
220 static const char *const special_mode_pred_table [] = { in genrecog.c
221 #ifdef SPECIAL_MODE_PREDICATES
222 SPECIAL_MODE_PREDICATES
223 #endif
224 "pmode_register_operand"
225 };
SPECIAL_MODE_PREDICATES 是特定于目标系统的。它记录了目标系统要求的特殊 mode 。对于 i386 系统,它有如下定义:
3000 #define SPECIAL_MODE_PREDICATES / in i386.c
3001 "ext_register_operand",
通过 special_mode_pred_table 没有发现匹配。我们将进入下面的 550 行。并注意到在 569 行, set 是 RTX_NULL 。
validate_pattern (continued)
550 if (code == MATCH_OPERAND)
551 {
552 const char constraints0 = XSTR (pattern, 2)[0];
553
554 /* In DEFINE_EXPAND, DEFINE_SPLIT, and DEFINE_PEEPHOLE2, we
555 don't use the MATCH_OPERAND constraint, only the predicate.
556 This is confusing to folks doing new ports, so help them
557 not make the mistake. */
558 if (GET_CODE (insn) == DEFINE_EXPAND
559 || GET_CODE (insn) == DEFINE_SPLIT
560 || GET_CODE (insn) == DEFINE_PEEPHOLE2)
561 {
562 if (constraints0)
563 message_with_line (pattern_lineno ,
564 "warning: constraints not supported in %s",
565 rtx_name [GET_CODE (insn)]);
566 }
567
568 /* A MATCH_OPERAND that is a SET should have an output reload. */
569 else if (set && constraints0)
570 {
571 if (set_code == '+')
572 {
573 if (constraints0 == '+')
574 ;
575 /* If we've only got an output reload for this operand,
576 we'd better have a matching input operand. */
577 else if (constraints0 == '='
578 && find_matching_operand (insn, XINT (pattern, 0)))
579 ;
580 else
581 {
582 message_with_line (pattern_lineno ,
583 "operand %d missing in-out reload",
584 XINT (pattern, 0));
585 error_count ++;
586 }
587 }
588 else if (constraints0 != '=' && constraints0 != '+')
589 {
590 message_with_line (pattern_lineno ,
591 "operand %d missing output reload",
592 XINT (pattern, 0));
593 error_count ++;
594 }
595 }
596 }
597
598 /* Allowing non-lvalues in destinations -- particularly CONST_INT --
599 while not likely to occur at runtime, results in less efficient
600 code from insn-recog.c. */
601 if (set
602 && pred_name[0] != '/0'
603 && allows_non_lvalue)
604 {
605 message_with_line (pattern_lineno ,
606 "warning: destination operand %d allows non-lvalue",
607 XINT (pattern, 0));
608 }
609
610 /* A modeless MATCH_OPERAND can be handy when we can
611 check for multiple modes in the c_test. In most other cases,
612 it is a mistake. Only DEFINE_INSN is eligible, since SPLIT
613 and PEEP2 can FAIL within the output pattern. Exclude
614 address_operand, since its mode is related to the mode of
615 the memory not the operand. Exclude the SET_DEST of a call
616 instruction, as that is a common idiom. */
617
618 if (GET_MODE (pattern) == VOIDmode
619 && code == MATCH_OPERAND
620 && GET_CODE (insn) == DEFINE_INSN
621 && allows_non_const
622 && ! special_mode_pred
623 && pred_name[0] != '/0'
624 && strcmp (pred_name, "address_operand") != 0
625 && strstr (c_test, "operands") == NULL
626 && ! (set
627 && GET_CODE (set) == SET
628 && GET_CODE (SET_SRC (set)) == CALL))
629 {
630 message_with_line (pattern_lineno ,
631 "warning: operand %d missing mode?",
632 XINT (pattern, 0));
633 }
634 return ;
上面在 552 行,提取了约束“ r,?mr ”。在 md 文件中,某些字符被用于修改约束。其中一个是‘ = ’,它意味着该操作数对于该指令是“只写的”:前一个值被输出数据所替代。另一个是‘ + ’,它表示该操作数是该指令可读写的。没有这两个修改符,其他所有的操作数都被假定为只读的。
对于我们的约束,它通过了验证。对于其它模式,也是类似的,你可以自己走一遍这个过程。
make_insn_sequence (continued)
2465 memset(&head, 0, sizeof (head));
2466 last = add_to_sequence (x, &head, "", type, 1);