GCC's bacl-end & assemble emission (8)

3.4. Output insn-recog.c

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.

GCC's bacl-end & assemble emission (8)_第1张图片

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 .

GCC's bacl-end & assemble emission (8)_第2张图片

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.

你可能感兴趣的:(struct,gcc,tree,extension,output,subroutine)