现在我们可以输出名为 insn-recog.c 的文件了。这由 write_subroutines 开始,参数 head 指向 recog_tree 。
2336 static void
2337 write_subroutines (struct decision_head *head, enum routine_type type) in genrecog.c
2338 {
2339 struct decision *p;
2340
2341 for (p = head->first; p ; p = p->next)
2342 if (p->success.first)
2343 write_subroutines (&p->success, type);
2344
2345 if (head->first->subroutine_number > 0)
2346 write_subroutine (head, type);
2347 }
上面,在 2345 行,在 break_out_subroutines 的 1614 行设置 subroutine_number ,非 0 值表示该节点的序号是 100 ( SUBROUTINE_THRESHOLD )的倍数,并且要为接下来的节点构建新的例程。注意到对于第 100 , 200 依此类推的节点, write_subroutine 将 2346 行被调用,而对于其它节点,这个函数将被跳过。对于我们的例子,因为 SUBROUTINE_THRESHOLD 被假定为 4 ,调用 write_subroutine 的节点在下图中被标记成红色。
图 27 :输出 insn-recog.c ,图 1
注意到 write_subroutine 的调用是深度优先的。
2281 static void
2282 write_subroutine (struct decision_head *head, enum routine_type type) in genrecog.c
2283 {
2284 int subfunction = head->first ? head->first->subroutine_number : 0;
2285 const char *s_or_e;
2286 char extension[32];
2287 int i;
2288
2289 s_or_e = subfunction ? "static " : "";
2290
2291 if (subfunction)
2292 sprintf (extension, "_%d", subfunction);
2293 else if (type == RECOG)
2294 extension[0] = '/0';
2295 else
2296 strcpy (extension, "_insns");
2297
2298 switch (type)
2299 {
2300 case RECOG:
2301 printf ("%sint/n/
2302 recog%s (rtx x0 ATTRIBUTE_UNUSED,/n/trtx insn ATTRIBUTE_UNUSED,/n/tint *pnum_clobbers ATTRIBUTE_UNUSED)/n", s_or_e, extension);
2303 break ;
2304 case SPLIT:
2305 printf ("%srtx/n/
2306 split%s (rtx x0 ATTRIBUTE_UNUSED, rtx insn ATTRIBUTE_UNUSED)/n",
2307 s_or_e, extension);
2308 break ;
2309 case PEEPHOLE2:
2310 printf ("%srtx/n/
2311 peephole2%s (rtx x0 ATTRIBUTE_UNUSED,/n/trtx insn ATTRIBUTE_UNUSED,
2312 /n/tint *_pmatch_len ATTRIBUTE_UNUSED)/n", s_or_e, extension);
2313 break ;
2314 }
2315
2316 printf ("{/n rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];/n");
2317 for (i = 1; i <= max_depth ; i++)
2318 printf (" rtx x%d ATTRIBUTE_UNUSED;/n", i);
2319
2320 printf (" %s tem ATTRIBUTE_UNUSED;/n", IS_SPLIT (type) ? "rtx" : "int");
2321
2322 if (!subfunction)
2323 printf (" recog_data.insn = NULL_RTX;/n");
2324
2325 if (head->first)
2326 write_tree (head, "", type, 1);
2327 else
2328 printf (" goto ret0;/n");
2329
2330 printf (" ret0:/n return %d;/n}/n/n", IS_SPLIT (type) ? 0 : -1);
2331 }
write_subroutine 只是创建了该输出函数的公共部分。对于我们的例子,下面的语句被写入这个文件。注意到 max_depth 记录了 position 的最大长度 , ,这里是 3 。
int
recog_1 (rtx x0 ATTRIBUTE_UNUSED,
rtx insn ATTRIBUTE_UNUSED,
int *pnum_clobbers ATTRIBUTE_UNUSED)
{
rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];
rtx x1 ATTRIBUTE_UNUSED;
rtx x2 ATTRIBUTE_UNUSED;
rtx x3 ATTRIBUTE_UNUSED;
int tem ATTRIBUTE_UNUSED;
在上面产生的源代码中, recog_data 被定义在 recog.h 里,并由函数 extract-insn 来初始化,该函数分析指令的 rtx 对象,并填入 recog_data 。输出输出函数的主体是 write_tree 的钩子。注意到对于最上层的调用,参数 initial 是 1 。
2224 static void
2225 write_tree (struct decision_head *head, const char *prevpos, in genrecog.c
2226 enum routine_type type, int initial)
2227 {
2228 struct decision *p = head->first;
2229
2230 putchar ('/n');
2231 if (p->need_label)
2232 OUTPUT_LABEL (" ", p->number);
2233
2234 if (! initial && p->subroutine_number > 0)
2235 {
2236 static const char * const name_prefix [] = {
2237 "recog", "split", "peephole2"
2238 };
2239
2240 static const char * const call_suffix [] = {
2241 ", pnum_clobbers", "", ", _pmatch_len"
2242 };
2243
2244 /* This node has been broken out into a separate subroutine.
2245 Call it, test the result, and branch accordingly. */
2246
2247 if (p->afterward)
2248 {
2249 printf (" tem = %s_%d (x0, insn%s);/n",
2250 name_prefix [type], p->subroutine_number, call_suffix [type]);
2251 if (IS_SPLIT (type))
2252 printf (" if (tem != 0)/n return tem;/n");
2253 else
2254 printf (" if (tem >= 0)/n return tem;/n");
2255
2256 change_state (p->position, p->afterward->position, NULL, " ");
2257 printf (" goto L%d;/n", p->afterward->number);
2258 }
2259 else
2260 {
2261 printf (" return %s_%d (x0, insn%s);/n",
2262 name_prefix [type], p->subroutine_number, call_suffix [type]);
2263 }
2264 }
2265 else
2266 {
2267 int depth = strlen (p->position);
2268
2269 change_state (prevpos, p->position, head->last->afterward, " ");
2270 write_tree_1 (head, depth, type);
2271
2272 for (p = head->first; p; p = p->next)
2273 if (p->success.first)
2274 write_tree (&p->success, p->position, type, 0);
2275 }
2276 }
因为在最上层的调用中,参数 initial 是 1 ,在 2265 行开始的代码首先运行。然后调用 change_state ,注意到对于这次调用,该函数的第一个参数是 null 。
1670 static void
1671 change_state (const char *oldpos, const char *newpos, in genrecog.c
1672 struct decision *afterward, const char *indent)
1673 {
1674 int odepth = strlen (oldpos);
1675 int ndepth = strlen (newpos);
1676 int depth;
1677 int old_has_insn, new_has_insn;
1678
1679 /* Pop up as many levels as necessary. */
1680 for (depth = odepth; strncmp (oldpos, newpos, depth) != 0; --depth)
1681 continue ;
1682
1683 /* Hunt for the last [A-Z] in both strings. */
1684 for (old_has_insn = odepth - 1; old_has_insn >= 0; --old_has_insn)
1685 if (ISUPPER (oldpos[old_has_insn]))
1686 break ;
1687 for (new_has_insn = ndepth - 1; new_has_insn >= 0; --new_has_insn)
1688 if (ISUPPER (newpos[new_has_insn]))
1689 break ;
1690
1691 /* Go down to desired level. */
1692 while (depth < ndepth)
1693 {
1694 /* It's a different insn from the first one. */
1695 if (ISUPPER (newpos[depth]))
1696 {
1697 /* We can only fail if we're moving down the tree. */
1698 if (old_has_insn >= 0 && oldpos[old_has_insn] >= newpos[depth])
1699 {
1700 printf ("%stem = peep2_next_insn (%d);/n",
1701 indent, newpos[depth] - 'A');
1702 }
1703 else
1704 {
1705 printf ("%stem = peep2_next_insn (%d);/n",
1706 indent, newpos[depth] - 'A');
1707 printf ("%sif (tem == NULL_RTX)/n", indent);
1708 if (afterward)
1709 printf ("%s goto L%d;/n", indent, afterward->number);
1710 else
1711 printf ("%s goto ret0;/n", indent);
1712 }
1713 printf ("%sx%d = PATTERN (tem);/n", indent, depth + 1);
1714 }
1715 else if (ISLOWER (newpos[depth]))
1716 printf ("%sx%d = XVECEXP (x%d, 0, %d);/n",
1717 indent, depth + 1, depth, newpos[depth] - 'a');
1718 else
1719 printf ("%sx%d = XEXP (x%d, %c);/n",
1720 indent, depth + 1, depth, newpos[depth]);
1721 ++depth;
1722 }
1723 }
从上面,我们知道 decision 节点的 position 域记录了节点在树中的深度。 Position 的长度越长,它埋藏得越深。对于 position 的内容而言,数字用作普通指令匹配的序列号,而小写字母用作并行( parallel )指令匹配的序列号(并行指令表示使用的指令同时起作用,而不是依次),而大写字母用作窥孔( peephole )匹配的序列号(窥孔代表一个优化机会——特定的指令序列被另一个指令序列所代替)。
在这个函数中,从 1680 到 1689 行,只是检查从 oldpos 到 newpos 所需要下降的深度。如果我们不能移到新的状态,就转入由 afterward 指向的分支,如果它不是 null 。转移到新状态失败,仅发生在,如果我们正在尝试匹配多个指令,并且我们尝试越过这个流的末尾。对于我们的例子,在调整到正确的位置之后,我们可以得到以下的代码片断。
int
recog_1 (rtx x0 ATTRIBUTE_UNUSED,
rtx insn ATTRIBUTE_UNUSED,
int *pnum_clobbers ATTRIBUTE_UNUSED)
{
rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];
rtx x1 ATTRIBUTE_UNUSED;
rtx x2 ATTRIBUTE_UNUSED;
rtx x3 ATTRIBUTE_UNUSED;
int tem ATTRIBUTE_UNUSED;
x1 = XEXP(x0, 1);
x2 = XEXP(x1, 1);
然后 write_tree_1 被调用来输出 recog_1 的主体部分。
2190 static void
2191 write_tree_1 (struct decision_head *head, int depth, in genrecog.c
2192 enum routine_type subroutine_type)
2193 {
2194 struct decision *p, *next;
2195 int uncond = 0;
2196
2197 for (p = head->first; p ; p = next)
2198 {
2199 /* The label for the first element was printed in write_tree. */
2200 if (p != head->first && p->need_label)
2201 OUTPUT_LABEL (" ", p->number);
2202
2203 /* Attempt to write a switch statement for a whole sequence. */
2204 next = write_switch (p, depth);
2205 if (p != next)
2206 uncond = 0;
2207 else
2208 {
2209 /* Failed -- fall back and write one node. */
2210 uncond = write_node (p, depth, subroutine_type);
2211 next = p->next;
2212 }
2213 }
2214
2215 /* Finished with this chain. Close a fallthru path by branching
2216 to the afterward node. */
2217 if (! uncond)
2218 write_afterward (head->last, head->last->afterward, " ");
2219 }
在 2204 行,对于我们当前的节点, write_switch 不做任何事。它仅是返回 p 。因此在 2210 行的 write_node 被调用。
2153 static int
2154 write_node (struct decision *p, int depth, in genrecog.c
2155 enum routine_type subroutine_type)
2156 {
2157 struct decision_test *test, *last_test;
2158 int uncond;
2159
2160 last_test = test = p->tests;
2161 uncond = is_unconditional (test, subroutine_type);
2162 if (uncond == 0)
2163 {
2164 printf (" if (");
2165 write_cond (test, depth, subroutine_type);
2166
2167 while ((test = test->next) != NULL)
2168 {
2169 int uncond2;
2170
2171 last_test = test;
2172 uncond2 = is_unconditional (test, subroutine_type);
2173 if (uncond2 != 0)
2174 break ;
2175
2176 printf ("/n && ");
2177 write_cond (test, depth, subroutine_type);
2178 }
2179
2180 printf (")/n");
2181 }
2182
2183 write_action (p, last_test, depth, uncond, p->success.first, subroutine_type);
2184
2185 return uncond > 0;
2186 }
属于我们当前节点的第一个 decision_test 节点是 DT_code ,因此对 is_unconditional 的第一次调用返回 0 ,因为它不是期望的节点。
2126 static int
2127 is_unconditional (struct decision_test *t, enum routine_type subroutine_type) in genrecog.c
2128 {
2129 if (t->type == DT_accept_op)
2130 return 1;
2131
2132 if (t->type == DT_accept_insn)
2133 {
2134 switch (subroutine_type)
2135 {
2136 case RECOG:
2137 return (t->u.insn.num_clobbers_to_add == 0);
2138 case SPLIT:
2139 return 1;
2140 case PEEPHOLE2:
2141 return -1;
2142 default :
2143 abort ();
2144 }
2145 }
2146
2147 return 0;
2148 }
那么回到 write_node ,在 2165 行, write_cond 协助填入 IF 语句的测试部分。
1965 static void
1966 write_cond (struct decision_test *p, int depth, in genrecog.c
1967 enum routine_type subroutine_type)
1968 {
1969 switch (p->type)
1970 {
1971 case DT_mode:
1972 printf ("GET_MODE (x%d) == %smode", depth, GET_MODE_NAME (p->u.mode));
1973 break ;
1974
1975 case DT_code:
1976 printf ("GET_CODE (x%d) == ", depth);
1977 print_code (p->u.code);
1978 break ;
1979
1980 case DT_veclen:
1981 printf ("XVECLEN (x%d, 0) == %d", depth, p->u.veclen);
1982 break ;
1983
1984 case DT_elt_zero_int:
1985 printf ("XINT (x%d, 0) == %d", depth, (int) p->u.intval);
1986 break ;
1987
1988 case DT_elt_one_int:
1989 printf ("XINT (x%d, 1) == %d", depth, (int) p->u.intval);
1990 break ;
1991
1992 case DT_elt_zero_wide:
1993 case DT_elt_zero_wide_safe:
1994 printf ("XWINT (x%d, 0) == ", depth);
1995 print_host_wide_int (p->u.intval);
1996 break ;
1997
1998 case DT_veclen_ge:
1999 printf ("XVECLEN (x%d, 0) >= %d", depth, p->u.veclen);
2000 break ;
2001
2002 case DT_dup:
2003 printf ("rtx_equal_p (x%d, operands[%d])", depth, p->u.dup);
2004 break ;
2005
2006 case DT_pred:
2007 printf ("%s (x%d, %smode)", p->u.pred.name, depth,
2008 GET_MODE_NAME (p->u.pred.mode));
2009 break ;
2010
2011 case DT_c_test:
2012 printf ("(%s)", p->u.c_test);
2013 break ;
2014
2015 case DT_accept_insn:
2016 switch (subroutine_type)
2017 {
2018 case RECOG:
2019 if (p->u.insn.num_clobbers_to_add == 0)
2020 abort ();
2021 printf ("pnum_clobbers != NULL");
2022 break ;
2023
2024 default :
2025 abort ();
2026 }
2027 break ;
2028
2029 default :
2030 abort ();
2031 }
2032 }
继续属于我们当前节点的 decision_test 节点,当完成 DT_c_test 节点的处理,我们可以得到下面的代码片段。
int
recog_1 (rtx x0 ATTRIBUTE_UNUSED,
rtx insn ATTRIBUTE_UNUSED,
int *pnum_clobbers ATTRIBUTE_UNUSED)
{
rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];
rtx x1 ATTRIBUTE_UNUSED;
rtx x2 ATTRIBUTE_UNUSED;
rtx x3 ATTRIBUTE_UNUSED;
int tem ATTRIBUTE_UNUSED;
x1 = XEXP (x0, 1);
x2 = XEXP(x1, 0);
if (GET_CODE (x2) == CONST
&& XWINT(x2, 0) == 0
&& (TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode)))
对于第三个类型为 DT_accept_insn 的节点,在 write_node 的 2172 行, is_unconditional 返回 1 ,接着 write_action 找出需要执行的行动( action )。注意到对于此次调用,参数 success 指向位置为“ 11 ”的节点, uncond 是 1 , depth 是 2 , test 指向第四个 decision_test 节点——是 DT_accept_insn ,而 subroutine_type 是 RECOG 。
2038 static void
2039 write_action (struct decision *p, struct decision_test *test, in genrecog.c
2040 int depth, int uncond, struct decision *success,
2041 enum routine_type subroutine_type)
2042 {
2043 const char *indent;
2044 int want_close = 0;
2045
2046 if (uncond)
2047 indent = " ";
2048 else if (test->type == DT_accept_op || test->type == DT_accept_insn)
2049 {
2050 fputs (" {/n", stdout);
2051 indent = " ";
2052 want_close = 1;
2053 }
2054 else
2055 indent = " ";
2056
2057 if (test->type == DT_accept_op)
2058 {
2059 printf("%soperands[%d] = x%d;/n", indent, test->u.opno, depth);
2060
2061 /* Only allow DT_accept_insn to follow. */
2062 if (test->next)
2063 {
2064 test = test->next;
2065 if (test->type != DT_accept_insn)
2066 abort ();
2067 }
2068 }
2069
2070 /* Sanity check that we're now at the end of the list of tests. */
2071 if (test->next)
2072 abort ();
2073
2074 if (test->type == DT_accept_insn)
2075 {
2076 switch (subroutine_type)
2077 {
2078 case RECOG:
2079 if (test->u.insn.num_clobbers_to_add != 0)
2080 printf ("%s*pnum_clobbers = %d;/n",
2081 indent, test->u.insn.num_clobbers_to_add);
2082 printf ("%sreturn %d;/n", indent, test->u.insn.code_number);
2083 break ;
2084
2085 case SPLIT:
2086 printf ("%sreturn gen_split_%d (operands);/n",
2087 indent, test->u.insn.code_number);
2088 break ;
2089
2090 case PEEPHOLE2:
2091 {
2092 int match_len = 0, i;
2093
2094 for (i = strlen (p->position) - 1; i >= 0; --i)
2095 if (ISUPPER (p->position[i]))
2096 {
2097 match_len = p->position[i] - 'A';
2098 break ;
2099 }
2100 printf ("%s*_pmatch_len = %d;/n", indent, match_len);
2101 printf ("%stem = gen_peephole2_%d (insn, operands);/n",
2102 indent, test->u.insn.code_number);
2103 printf ("%sif (tem != 0)/n%s return tem;/n", indent, indent);
2104 }
2105 break ;
2106
2107 default :
2108 abort ();
2109 }
2110 }
2111 else
2112 {
2113 printf("%sgoto L%d;/n", indent, success->number);
2114 success->need_label = 1;
2115 }
2116
2117 if (want_close)
2118 fputs (" }/n", stdout);
2119 }
对于我们的例子,现在我们得到以下代码片段。
int
recog_1 (rtx x0 ATTRIBUTE_UNUSED,
rtx insn ATTRIBUTE_UNUSED,
int *pnum_clobbers ATTRIBUTE_UNUSED)
{
rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];
rtx x1 ATTRIBUTE_UNUSED;
rtx x2 ATTRIBUTE_UNUSED;
rtx x3 ATTRIBUTE_UNUSED;
int tem ATTRIBUTE_UNUSED;
x1 = XEXP(x0, 1);
x2 = XEXP(x1, 1);
if (GET_CODE (x2) == CONST
&& XWINT(x2, 0) == 0
&& (TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode)))
{
return `code-number` ;
}
那么回到 write_tree 的 2270 行。对于接下来的 decision 节点, write_tree 被递归调用。因为对于感兴趣的节点,它没有跟随的节点,我们进一步返回到 write_subroutine ,并得到如下输出。
int
recog_1 (rtx x0 ATTRIBUTE_UNUSED,
rtx insn ATTRIBUTE_UNUSED,
int *pnum_clobbers ATTRIBUTE_UNUSED)
{
rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];
rtx x1 ATTRIBUTE_UNUSED;
rtx x2 ATTRIBUTE_UNUSED;
rtx x3 ATTRIBUTE_UNUSED;
int tem ATTRIBUTE_UNUSED;
x1 = XEXP(x0, 1);
x2 = XEXP(x1, 1);
if (GET_CODE (x2) == CONST
&& XWINT(x2, 0) == 0
&& (TARGET_64BIT && ix86_match_ccmode (insn, CCGOCmode)))
{
return `code-number` ;
}
goto ret0;
ret0:
return -1;
}
那么回到 write_tree ,接着输出如下的 recog_2 (我们把 position 用作编码的号码,对于 position 重复的情形,在 position 的末尾加上数字 size 来构成唯一的编码号码)。
int
recog_2 (rtx x0 ATTRIBUTE_UNUSED,
rtx insn ATTRIBUTE_UNUSED,
int *pnum_clobbers ATTRIBUTE_UNUSED)
{
rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];
rtx x1 ATTRIBUTE_UNUSED;
rtx x2 ATTRIBUTE_UNUSED;
rtx x3 ATTRIBUTE_UNUSED;
int tem ATTRIBUTE_UNUSED;
x1 = XEXP(x0, 1);
x2 = XEXP(x1, 0);
if (GET_MODE (x2) == DI_mode)
goto L104;
goto ret0;
对于跟在具有 subroutine_number 为 2 的节点后的节点, write_switch 不是无关紧要的。让我们仔细看一下 write_switch 。
2190 static struct decision *
2191 write_switch (struct decision *start, int depth) in genrecog.c
2192 {
2193 struct decision *p = start;
2194 enum decision_type type = p->tests->type;
2195 struct decision *needs_label = NULL;
2196
2197 /* If we have two or more nodes in sequence that test the same one
2198 thing, we may be able to use a switch statement. */
2199
2200 if (!p->next
2201 || p->tests->next
2202 || p->next->tests->type != type
2203 || p->next->tests->next
2204 || nodes_identical_1 (p->tests, p->next->tests))
2205 return p;
2206
2207 /* DT_code is special in that we can do interesting things with
2208 known predicates at the same time. */
2209 if (type == DT_code)
2210 {
2211 char codemap[NUM_RTX_CODE];
2212 struct decision *ret;
2213 RTX_CODE code;
2214
2215 memset (codemap, 0, sizeof (codemap));
2216
2217 printf (" switch (GET_CODE (x%d))/n {/n", depth);
2218 code = p->tests->u.code;
2219 do
2220 {
2221 if (p != start && p->need_label && needs_label == NULL)
2222 needs_label = p;
2223
2224 printf (" case ");
2225 print_code (code);
2226 printf (":/n goto L%d;/n", p->success.first->number);
2227 p->success.first->need_label = 1;
2228
2229 codemap[code] = 1;
2230 p = p->next;
2231 }
2232 while (p
2233 && ! p->tests->next
2234 && p->tests->type == DT_code
2235 && ! codemap[code = p->tests->u.code]);
2236
2237 /* If P is testing a predicate that we know about and we haven't
2238 seen any of the codes that are valid for the predicate, we can
2239 write a series of "case" statement, one for each possible code.
2240 Since we are already in a switch, these redundant tests are very
2241 cheap and will reduce the number of predicates called. */
2242
2243 /* Note that while we write out cases for these predicates here,
2244 we don't actually write the test here, as it gets kinda messy.
2245 It is trivial to leave this to later by telling our caller that
2246 we only processed the CODE tests. */
2247 if (needs_label != NULL)
2248 ret = needs_label;
2249 else
2250 ret = p;
2251
2252 while (p && p->tests->type == DT_pred
2253 && p->tests->u.pred.index >= 0)
2254 {
2255 const RTX_CODE *c;
2256
2257 for (c = &preds [p->tests->u.pred.index].codes[0]; *c ; ++c)
2258 if (codemap[(int) *c] != 0)
2259 goto pred_done;
2260
2261 for (c = &preds [p->tests->u.pred.index].codes[0]; *c ; ++c)
2262 {
2263 printf (" case ");
2264 print_code (*c);
2265 printf (":/n");
2266 codemap[(int) *c] = 1;
2267 }
2268
2269 printf (" goto L%d;/n", p->number);
2270 p->need_label = 1;
2271 p = p->next;
2272 }
2273
2274 pred_done:
2275 /* Make the default case skip the predicates we managed to match. */
2276
2277 printf (" default:/n");
2278 if (p != ret)
2279 {
2280 if (p)
2281 {
2282 printf (" goto L%d;/n", p->number);
2283 p->need_label = 1;
2284 }
2285 else
2286 write_afterward (start, start->afterward, " ");
2287 }
2288 else
2289 printf (" break;/n");
2290 printf (" }/n");
2291
2292 return ret;
2293 }
2294 else if (type == DT_mode
2295 || type == DT_veclen
2296 || type == DT_elt_zero_int
2297 || type == DT_elt_one_int
2298 || type == DT_elt_zero_wide_safe)
2299 {
2300 const char *indent = "";
2301
2302 /* We cast switch parameter to integer, so we must ensure that the value
2303 fits. */
2304 if (type == DT_elt_zero_wide_safe)
2305 {
2306 indent = " ";
2307 printf(" if ((int) XWINT (x%d, 0) == XWINT (x%d, 0))/n", depth, depth);
2308 }
2309 printf ("%s switch (", indent);
2310 switch (type)
2311 {
2312 case DT_mode:
2313 printf ("GET_MODE (x%d)", depth);
2314 break ;
2315 case DT_veclen:
2316 printf ("XVECLEN (x%d, 0)", depth);
2317 break ;
2318 case DT_elt_zero_int:
2319 printf ("XINT (x%d, 0)", depth);
2320 break ;
2321 case DT_elt_one_int:
2322 printf ("XINT (x%d, 1)", depth);
2323 break ;
2324 case DT_elt_zero_wide_safe:
2325 /* Convert result of XWINT to int for portability since some C
2326 compilers won't do it and some will. */
2327 printf ("(int) XWINT (x%d, 0)", depth);
2328 break ;
2329 default :
2330 abort ();
2331 }
2332 printf (")/n%s {/n", indent);
2333
2334 do
2335 {
2336 /* Merge trees will not unify identical nodes if their
2337 sub-nodes are at different levels. Thus we must check
2338 for duplicate cases. */
2339 struct decision *q;
2340 for (q = start; q != p; q = q->next)
2341 if (nodes_identical_1 (p->tests, q->tests))
2342 goto case_done;
2343
2344 if (p != start && p->need_label && needs_label == NULL)
2345 needs_label = p;
2346
2347 printf ("%s case ", indent);
2348 switch (type)
2349 {
2350 case DT_mode:
2351 printf ("%smode", GET_MODE_NAME (p->tests->u.mode));
2352 break ;
2353 case DT_veclen:
2354 printf ("%d", p->tests->u.veclen);
2355 break ;
2356 case DT_elt_zero_int:
2357 case DT_elt_one_int:
2358 case DT_elt_zero_wide:
2359 case DT_elt_zero_wide_safe:
2360 print_host_wide_int (p->tests->u.intval);
2361 break ;
2362 default :
2363 abort ();
2364 }
2365 printf (":/n%s goto L%d;/n", indent, p->success.first->number);
2366 p->success.first->need_label = 1;
2367
2368 p = p->next;
2369 }
2370 while (p && p->tests->type == type && !p->tests->next);
2371
2372 case_done:
2373 printf ("%s default:/n%s break;/n%s }/n",
2374 indent, indent, indent);
2375
2376 return needs_label != NULL ? needs_label : p;
2377 }
2378 else
2379 {
2380 /* None of the other tests are amenable. */
2381 return p;
2382 }
2383 }
为了使 2200 行的条件测试更清楚些,我们把它重写如下:
if (!(p->next
&& !p->tests->next
&& p->next->tests->type == type
&& !p->next->tests->next
&& !nodes_identical_1 (p->tests, p->next->tests)))
看到 write_switch 处理具有相同 decision_test 类型的兄弟节点。对于其他情形的兄弟节点,它们则由 write_node 通过 write_tree_1 来一个个处理。
图 28 :输出 insn-recog.c ,图 2
对于感兴趣的节点,通过由 图 28 表示的测试后, write_switch 将为这些节点输出 switch case 语句,因此我们可以得到如下的代码片段。
int
recog_2 (rtx x0 ATTRIBUTE_UNUSED,
rtx insn ATTRIBUTE_UNUSED,
int *pnum_clobbers ATTRIBUTE_UNUSED)
{
rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];
rtx x1 ATTRIBUTE_UNUSED;
rtx x2 ATTRIBUTE_UNUSED;
rtx x3 ATTRIBUTE_UNUSED;
int tem ATTRIBUTE_UNUSED;
x1 = XEXP(x0, 1);
x2 = XEXP(x1, 0);
if (GET_MODE (x2) == DI_mode)
goto L104;
goto ret0;
L104:
switch (GET_CODE (x2))
{
case MATCH_OPERAND:
goto L103;
case MINUS:
goto L100;
default:
break;
}
那么当从 write_switch 返回 write_switch 时,对于正在处理的节点,在 2205 行, p 将不等于 next , write_afterward 将在 2218 行被调用。
1738 static void
1739 write_afterward (struct decision *start, struct decision *afterward, in genrecog.c
1740 const char *indent)
1741 {
1742 if (!afterward || start->subroutine_number > 0)
1743 printf("%sgoto ret0;/n", indent);
1744 else
1745 {
1746 change_state (start->position, afterward->position, NULL, indent);
1747 printf ("%sgoto L%d;/n", indent, afterward->number);
1748 }
1749 }
这时, afterward 是 null ,而 subroutine_number 不是 0 。那么我们得到下面的代码片段。
int
recog_2 (rtx x0 ATTRIBUTE_UNUSED,
rtx insn ATTRIBUTE_UNUSED,
int *pnum_clobbers ATTRIBUTE_UNUSED)
{
rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];
rtx x1 ATTRIBUTE_UNUSED;
rtx x2 ATTRIBUTE_UNUSED;
rtx x3 ATTRIBUTE_UNUSED;
int tem ATTRIBUTE_UNUSED;
x1 = XEXP(x0, 1);
x2 = XEXP(x1, 0);
if (GET_MODE (x2) == DI_mode)
goto L104;
goto ret0;
L104:
switch (GET_CODE (x2))
{
case MATCH_OPERAND:
goto L103;
case MINUS:
goto L100;
default:
break;
}
goto ret0;
随后我们回到 write_tree ,在 2272 行,对 图 27 中,跟在 position 为 10 , size 为 4 的节点后的节点,递归调用该函数。注意到这个节点具有为 1 的 need_label 。我们在下面显示 write_tree 中相关的代码。
2224 static void
2225 write_tree (struct decision_head *head, const char *prevpos, in genrecog.c
2226 enum routine_type type, int initial)
2227 {
2228 struct decision *p = head->first;
2229
2230 putchar ('/n');
2231 if (p->need_label)
2232 OUTPUT_LABEL (" ", p->number);
2233
2234 if (! initial && p->subroutine_number > 0)
2235 {
…
2264 }
2265 else
2266 {
2267 int depth = strlen (p->position);
2268
2269 change_state (prevpos, p->position, head->last->afterward, " ");
2270 write_tree_1 (head, depth, type);
2271
2272 for (p = head->first; p; p = p->next)
2273 if (p->success.first)
2274 write_tree (&p->success, p->position, type, 0);
2275 }
2276 }
现在对于包含这个节点的分支,我们得到如下的代码片段。
int
recog_2 (rtx x0 ATTRIBUTE_UNUSED,
rtx insn ATTRIBUTE_UNUSED,
int *pnum_clobbers ATTRIBUTE_UNUSED)
{
rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];
rtx x1 ATTRIBUTE_UNUSED;
rtx x2 ATTRIBUTE_UNUSED;
rtx x3 ATTRIBUTE_UNUSED;
int tem ATTRIBUTE_UNUSED;
x1 = XEXP(x0, 1);
x2 = XEXP(x1, 0);
if (GET_MODE (x2) == DI_mode)
goto L104;
goto ret0;
L104:
switch (GET_CODE (x2))
{
case MATCH_OPERAND:
goto L103;
case MINUS:
goto L100;
default:
break;
}
goto ret0;
L103:
if (nonimmediate_operand (x2, SUBREG))
{
operands[0] = x2;
goto L112;
}
goto ret0;
L112:
x2 = XEXP (x1, 1);
if (const0_operand (x2, CONST_INT))
{
operands[1] = x2;
goto L111;
}
goto ret0;
L111:
if (TARGET_64BIT && ix86_match_ccmode (insn, CCNomode))
{
return `code-number` ;
}
goto ret0;
对于 图 27 中的另一个分支,则可以得到如下代码片段。
int
recog_2 (rtx x0 ATTRIBUTE_UNUSED,
rtx insn ATTRIBUTE_UNUSED,
int *pnum_clobbers ATTRIBUTE_UNUSED)
{
rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];
rtx x1 ATTRIBUTE_UNUSED;
rtx x2 ATTRIBUTE_UNUSED;
rtx x3 ATTRIBUTE_UNUSED;
int tem ATTRIBUTE_UNUSED;
x1 = XEXP(x0, 1);
x2 = XEXP(x1, 0);
if (GET_MODE (x2) == DI_mode)
goto L104;
goto ret0;
L104:
switch (GET_CODE (x2))
{
case MATCH_OPERAND:
goto L103;
case MINUS:
goto L100;
default:
break;
}
goto ret0;
L103:
if (nonimmediate_operand (x2, SUBREG))
{
operands[0] = x2;
goto L112;
}
goto ret0;
L112:
x2 = XEXP (x1, 1);
if (const0_operand (x2, CONST_INT))
{
operands[1] = x2;
goto L111;
}
goto ret0;
L111:
if (TARGET_64BIT && ix86_match_ccmode (insn, CCNomode))
{
return `code-number` ;
}
goto ret0;
L100:
x2 = XEXP (x1, 0);
x3 = XEXP (x2, 0);
if (nonimmediate_operand (x3, SUBREG))
{
operands[0] = x3;
goto L101:
}
goto ret0;
L101:
x3 = XEXP (x2, 1);
if (x86_64_general_operand (x3, CONST_INT))
{
operands[1] = x3;
goto L11;
}
goto ret0;
L11:
return recog_1 (x0, insn, pnum_clobbers);
ret0:
return -1;
}
在完成了这两个分支之后,我们将从 write_subroutines 返回到 process_tree 。在 2607 行, write_subroutine 将为根节点所调用。注意到根节点不计入节点数目,因此没有分配 subroutine_number (保持为 0 )。因而我们将得到下面的函数。
static int
recog (rtx x0 ATTRIBUTE_UNUSED,
rtx insn ATTRIBUTE_UNUSED,
int *pnum_clobbers ATTRIBUTE_UNUSED)
{
rtx * const operands ATTRIBUTE_UNUSED = &recog_data.operand[0];
rtx x1 ATTRIBUTE_UNUSED;
rtx x2 ATTRIBUTE_UNUSED;
rtx x3 ATTRIBUTE_UNUSED;
int tem ATTRIBUTE_UNUSED;
recog_data.insn = NULL_RTX;
if (GET_CODE (x0) == SET)
goto L3;
goto ret0;
L3:
x1 = XEXP (x0, 0);
if (GET_CODE (x1) == REG &&
XINT (x0, 0) == 17)
{
goto L1:
}
goto ret0;
L1:
x1 = XEXP (x0, 1);
if (GET_CODE (x1) == COMPARE)
{
goto L101:
}
goto ret0;
L101:
return recog_2 (x0, insn, pnum_clobbers);
ret0:
return -1;
}
现在我们了解了 recog 做什么用,并且它如何起作用。对于 split 及 peephole2 ,整个产生的过程非常类似。而且它们也以相似的方式工作。