Now we can output the file named insn-recog.c. It begins by write_subroutines . The parameter head points to 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 }
Above, at line 2345, subroutine_number is set in break_out_subroutines at line 1614, the nonzero value means the sequence number of the node is the muliple of 100 (SUBROUTINE_THRESHOLD), and new subroutine needs created for following nodes. We can note that for 100th , 200th and so on nodes, write_subroutine will be invoked at line 2346, and for other nodes the function will be skipped. For our example, as SUBROUTINE_THRESHOLD is assumed as 4, the nodes invoking write_subroutine are in red in following figure.
figure 27 : output insn-recog.c, figure 1
Notice that the invocation of write_subroutine is depth first.
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 just creates the common part of the function. For our example, following statements will be written into the file. Note that max_depth records the longest length of position, which is 3 here.
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;
Above in the source code produced, recog_data is defined in recog.h, and is initialized by function extract-insn , which analyzes rtx object of instruction and fills in recog_data . Output the major body of the output function is the task of write_tree . Notice that for the top invocation, the parameter initial is 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 }
As at the top level, the parameter initial is 1, code begins at line 2265 is run at first. And change_state will be invoked in turn, notice that the first parameter of the function is null for this invocation.
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 }
From above, we know that the position field of decision node records the depth of the node in the tree. The longer the length of position , the deeper it buries. For the content of the position , digitals are for sequence of normal instruction matching, while lower case alphabits are for sequence of parallel instructions matching (parallel instructions means all instructions take effect at the same time not in sequence), and upper case alphabits are for sequence of peepholes matching (peepholes stands for an opportunity of optimization by replacing certain sequence of instructions by other sequence of instrucitons).
In this function, from line 1680 to 1689, just checks the depth needs to go down from oldpos to newpos . If we fail to move to the new state, branch to node pointed by afterward field if it is not null. Failure to move to the new state can only occur if we are trying to match multiple instruction and we try to step past the end of the stream. For our example, after adjusting to the right position, we can get following generated code fragment.
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);
Then write_tree_1 is invoked to output the major body of 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 }
At line 2204, write_switch does nothing for our current node. It just returns p . So write_node at line 2210 above will be invoked.
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 }
The first decision_test nodes belonging to our current node is DT_code, so first invocation of is_unconditional returns 0 as it is the unexpected nodes.
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 }
Then back in write_node , at line 2165, write_cond helps to fill in the testing in the IF statement.
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 }
Continue the decision_test nodes belonged to our current node, when finishing node of DT_c_test, we can get following code fragment.
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)))
For the third node of type DT_accept_insn, in write_node at line 2172, is_unconditional returns 1, then write_action finds out the action to be taken. Notice that for this time invocation, parameter success points to node of poiston “11”, uncond is 1, depth is 2, test points to the forth decision_test node, which is DT_accept_insn now, and subroutine_type is 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 }
For our example, now we can get following code fragment.
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` ;
}
Then we return back to write_tree at line 2270. write_tree is invoked recursively for the following decision nodes. As for node in interested, it hasn’t following nodes, we further return back to write_subroutine , and we can get following output.
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;
}
Then back in write_tree , next output recog_2 as following (we take position as the code number, for case of duplicate position , append size number at the tail of position to form the unique code number).
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;
For the node following the node having subroutine_number of 2, write_switch is not trivial. Let’s see write_switch closely.
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 }
To see the conditional test at line 2200 clearly, we rewrite it as following:
if (!(p->next
&& !p->tests->next
&& p->next->tests->type == type
&& !p->next->tests->next
&& !nodes_identical_1 (p->tests, p->next->tests)))
See that write_switch handles sibling nodes having decision_test of the same type. For sibling nodes of other cases, they will be treated by write_node one by one in write_tree_1 .
figure 28 : output insn-recog.c, figure 2
For nodes in interested, the test present by figure 28 is passed, write_switch will output switch case statement for the nodes, and we can get following code fragment.
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;
}
Then when returning back into write_switch from write_switch , for the node undergoing handling, at line 2205, p will not be equal to next , write_afterward will be invoked at line 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 }
For the case, afterward is null and subroutine_number is not 0. Then we can get following code fragment.
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;
After that we return back to write_tree , at line 2272, it recurses with nodes following the node of position 10 and size of 4 in figure 27 . And note that the node has need_label as 1. We copy the related code of write_tree below.
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 }
Now we get following code fragment for the branch containing that node.
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;
For another branch in figure 27 , we can get following code fragment.
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;
}
After finishing these two branches, we will return from write_subroutines back to process_tree . At line 2607, write_subroutine will be invoked for the root of the tree. Notice that the root of the tree does not join the number counting, and haven’t subroutine_number assigned (keeps 0). So we will get following function:
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;
}
Now we understand what recog does, and how it does. For split and peephole2, the whole produce processing are very similar. And they also work in the similar way.