GCC后端及汇编发布(4)

3.        genrecog 工具

3.1. 代码输出的准备

工具 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 ,然后其它。

3.2. 读入 rtl 形式的定义

3.2.1. 选择合适的模式

所有的工具都有这一步,不过对于除 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”

3.2.2. 验证模式的 RTL 模板

好了,现在看一下 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);

 

你可能感兴趣的:(汇编,struct,tree,gcc,null,constraints)