5.13.5.2.2.1.1.3. Emit assembler code
Back output_constant_def_contents , if the variable has alignment bigger than 1 byte, it needs invoke ASM_OUTPUT_ALIGN to output this requirement, here what output is logarithm of base 2 of the alignemnt. For x86, the macro has definition:
54 #define ASM_OUTPUT_ALIGN (FILE,LOG) / in att.h
55 if ((LOG)!=0) fprintf ((FILE), "/t.align %d/n", 1<<(LOG))
In build_constant_desc previous, it makes the internal label for constant, now needs output this label just before the constant. Here it is done by following macro.
151 #ifndef ASM_OUTPUT_LABEL in default.h
152 #define ASM_OUTPUT_LABEL (FILE,NAME) /
153 do { assemble_name ((FILE), (NAME)); fputs (":/n", (FILE)); } while (0)
154 #endif
Note, at line 153, “:/n” is appended after the label.
1699 void
1700 assemble_name (FILE *file, const char *name) in varasm.c
1701 {
1702 const char *real_name;
1703 tree id;
1704
1705 real_name = (* targetm .strip_name_encoding) (name);
1706
1707 id = maybe_get_identifier (real_name);
1708 if (id)
1709 mark_referenced (id);
1710
1711 if (name[0] == '*')
1712 fputs (&name[1], file);
1713 else
1714 ASM_OUTPUT_LABELREF (file, name);
1715 }
At line 1705, default hook strip_name_encoding does anything but discard the '*' marker at head. Then maybe_get_identifier tries to get identifer of name , and returns NULL if it is not an identifier.
129 tree
130 maybe_get_identifier (const char *text) in stringpool.c
131 {
132 hashnode ht_node;
133
134 ht_node = ht_lookup (ident_hash , (const unsigned char *) text,
135 strlen (text), HT_NO_INSERT);
136 if (ht_node)
137 return HT_IDENT_TO_GCC_IDENT (ht_node);
138
139 return NULL_TREE;
140 }
If name corresponds to an identifier, it should mark it as being referenced. Below flag cgraph_global_info_ready is nonzero when whole unit has been analyzed, no doubt at here, it is still zero.
1671 void
1672 mark_referenced (tree id) in varasm.c
1673 {
1674 if (!TREE_SYMBOL_REFERENCED (id))
1675 {
1676 struct cgraph_node *node;
1677 struct cgraph_varpool_node *vnode;
1678
1679 if (!cgraph_global_info_ready )
1680 {
1681 node = cgraph_node_for_identifier (id);
1682 if (node)
1683 cgraph_mark_needed_node (node);
1684 }
1685
1686 vnode = cgraph_varpool_node_for_identifier (id);
1687 if (vnode)
1688 cgraph_varpool_mark_needed_node (vnode);
1689 }
1690 TREE_SYMBOL_REFERENCED (id) = 1;
1691 }
An identifier may be name of a function or a variable; so cgraph_node_for_identifier at line 1681 retrieves the cgraph_node for FUNCTION_DECL of the name; and at line 1686, cgraph_varpool_node_for_identifier retrieves the cgraph_varpool_node for VAR_DECL of the name.
133 struct cgraph_node *
134 cgraph_node_for_identifier (tree id) in cgraph.c
135 {
136 struct cgraph_node **slot;
137
138 if (TREE_CODE (id) != IDENTIFIER_NODE)
139 abort ();
140
141 if (!cgraph_hash )
142 return NULL;
143
144 slot = (struct cgraph_node **)
145 htab_find_slot_with_hash (cgraph_hash , id,
146 IDENTIFIER_HASH_VALUE (id), NO_INSERT);
147 if (!slot)
148 return NULL;
149 return *slot;
150 }
If an identifier is not global visible, it won’t be recorded by cgraph_hash or cgraph_varpool_hash . And if an identifier is global visible, it needs be marked as needed, so later optimization will not delete it.
Queue cgraph_nodes_queue records the functions in used. And similarly queue cgraph_varpool_nodes_queue records the global variables in used.
547 struct cgraph_varpool_node *
548 cgraph_varpool_node_for_identifier (tree id) in cgraph.c
549 {
550 struct cgraph_varpool_node **slot;
551
552 if (TREE_CODE (id) != IDENTIFIER_NODE)
553 abort ();
554
555 if (!cgraph_varpool_hash )
556 return NULL;
557
558 slot = (struct cgraph_varpool_node **)
559 htab_find_slot_with_hash (cgraph_varpool_hash , id,
560 IDENTIFIER_HASH_VALUE (id), NO_INSERT);
561 if (!slot)
562 return NULL;
563 return *slot;
564 }
Back assemble_name , can see that the internal names generated by the front-end itself has ‘*’ at head, it just outputs these names without ‘*’. While other names are output by ASM_OUTPUT_LABELREF, which on our taget has following definition.
158 #ifndef ASM_OUTPUT_LABELREF in default.h
159 #define ASM_OUTPUT_LABELREF (FILE,NAME) asm_fprintf ((FILE), "%U%s", (NAME))
160 #endif
In function asm_fprintf , format “%U” means prints the value of user_label_prefix which is “” for the target.
Back output_constant_def_contents again, at last invokes output_constant to output the content of the constants.
3730 void
3731 output_constant (tree exp, unsigned HOST_WIDE_INT size, unsigned int align) in varasm.c
3732 {
3733 enum tree_code code;
3734 unsigned HOST_WIDE_INT thissize;
3735
3736 /* Some front-ends use constants other than the standard language-independent
3737 varieties, but which may still be output directly. Give the front-end a
3738 chance to convert EXP to a language-independent representation. */
3739 exp = (*lang_hooks .expand_constant) (exp);
3740
3741 if (size == 0 || flag_syntax_only )
3742 return ;
3743
3744 /* Eliminate any conversions since we'll be outputting the underlying
3745 constant. */
3746 while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR
3747 || TREE_CODE (exp) == NON_LVALUE_EXPR
3748 || TREE_CODE (exp) == VIEW_CONVERT_EXPR)
3749 exp = TREE_OPERAND (exp, 0);
3750
3751 code = TREE_CODE (TREE_TYPE (exp));
3752 thissize = int_size_in_bytes (TREE_TYPE (exp));
3753
3754 /* Allow a constructor with no elements for any data type.
3755 This means to fill the space with zeros. */
3756 if (TREE_CODE (exp) == CONSTRUCTOR && CONSTRUCTOR_ELTS (exp) == 0)
3757 {
3758 assemble_zeros (size);
3759 return ;
3760 }
Again, above at line 3739, hook of C++ cplus_expand_constant changes pointer-to-member constant into more appropriate form (PLUS_EXPR or another PTRMEM_CST).
5.13.5.2.2.1.1.3.1. Content of 0
Below function can leave size bytes 0 in assembler code.
1155 void
1156 assemble_zeros (unsigned HOST_WIDE_INT size) in varasm.c
1157 {
1158 /* Do no output if -fsyntax-only. */
1159 if (flag_syntax_only )
1160 return ;
1161
1162 #ifdef ASM_NO_SKIP_IN_TEXT
1163 /* The `space' pseudo in the text section outputs nop insns rather than 0s,
1164 so we must output 0s explicitly in the text section. */
1165 if (ASM_NO_SKIP_IN_TEXT && in_text_section ())
1166 {
1167 unsigned HOST_WIDE_INT i;
1168 for (i = 0; i < size; i++)
1169 assemble_integer (const0_rtx, 1, BITS_PER_UNIT, 1);
1170 }
1171 else
1172 #endif
1173 if (size > 0)
1174 ASM_OUTPUT_SKIP (asm_out_file , size);
1175 }
If data section and text section reqiure different treatment, it needs define macro ASM_NO_SKIP_IN_TEXT; otherwise needs ASM_OUTPUT_SKIP. In x86/Linux, it needs the differentiated treatment, so ASM_OUTPUT_SKIP is defined as (it is first defined in att.h, but overwritten in below file):
104 #undef ASM_OUTPUT_SKIP i n elfos.h
105 #define ASM_OUTPUT_SKIP(FILE, SIZE) /
106 fprintf ((FILE), "%s"HOST_WIDE_INT_PRINT_UNSIGNED"/n",/
107 SKIP_ASM_OP, (SIZE))
Above, SKIP_ASM_OP is “/t.zero/t”, and HOST_WIDE_INT_PRINT_UNSIGNED in fact is” llu”, as in C++ adjancent strings will be concatenated, so at line 106, the second parameter of fprintf is “%llu/n”.
If it can’t leave the required bytes by changing the location counter, it needs to output the data via assemble_integer . We will see this function shortly.
5.13.5.2.2.1.1.3.2. Integer
x86/Linux doesn’t use function descriptor, so macro ASM_OUTPUT_FDESC is not defined. And if found FDESC_EXPR, it will invoke abort at line 3769 to exit the compilation.
output_constant (continue)
3762 if (TREE_CODE (exp) == FDESC_EXPR)
3763 {
3764 #ifdef ASM_OUTPUT_FDESC
3765 HOST_WIDE_INT part = tree_low_cst (TREE_OPERAND (exp, 1), 0);
3766 tree decl = TREE_OPERAND (exp, 0);
3767 ASM_OUTPUT_FDESC (asm_out_file , decl, part);
3768 #else
3769 abort ();
3770 #endif
3771 return ;
3772 }
3773
3774 /* Now output the underlying data. If we've handling the padding, return.
3775 Otherwise, break and ensure THISSIZE is the size written. */
3776 switch (code)
3777 {
3778 case CHAR_TYPE:
3779 case BOOLEAN_TYPE:
3780 case INTEGER_TYPE:
3781 case ENUMERAL_TYPE:
3782 case POINTER_TYPE:
3783 case REFERENCE_TYPE:
3784 case OFFSET_TYPE:
3785 if (! assemble_integer (expand_expr (exp, NULL_RTX, VOIDmode,
3786 EXPAND_INITIALIZER),
3787 size, align, 0))
3788 error ("initializer for integer value is too complicated");
3789 break ;
At line 3785, expand_expr transforms the given frond-end node into RTL node, it is a complex function, we don't see it temperarily. For scalar constant (non-VECTOR type), will all be built with RTL node of CONST_DOUBLE; and arithmetic experssion also has corresponding RTL node.
1873 bool
1874 assemble_integer (rtx x, unsigned int size, unsigned int align, int force) in varasm.c
1875 {
1876 int aligned_p;
1877
1878 aligned_p = (align >= MIN (size * BITS_PER_UNIT, BIGGEST_ALIGNMENT));
1879
1880 /* See if the target hook can handle this kind of object. */
1881 if ((*targetm .asm_out.integer ) (x, size, aligned_p))
1882 return true;
1883
1884 /* If the object is a multi-byte one, try splitting it up. Split
1885 it into words it if is multi-word, otherwise split it into bytes. */
1886 if (size > 1)
1887 {
1888 enum machine_mode omode, imode;
1889 unsigned int subalign;
1890 unsigned int subsize, i;
1891
1892 subsize = size > UNITS_PER_WORD? UNITS_PER_WORD : 1;
1893 subalign = MIN (align, subsize * BITS_PER_UNIT);
1894 omode = mode_for_size (subsize * BITS_PER_UNIT, MODE_INT, 0);
1895 imode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
1896
1897 for (i = 0; i < size; i += subsize)
1898 {
1899 rtx partial = simplify_subreg (omode, x, imode, i);
1900 if (!partial || !assemble_integer (partial, subsize, subalign, 0))
1901 break ;
1902 }
1903 if (i == size)
1904 return true;
1905
1906 /* If we've printed some of it, but not all of it, there's no going
1907 back now. */
1908 if (i > 0)
1909 abort ();
1910 }
1911
1912 if (force)
1913 abort ();
1914
1915 return false;
1916 }
At line 1881 the hook, for our target machine, is bound with below funciton.
1859 bool
1860 default_assemble_integer (rtx x ATTRIBUTE_UNUSED, in varasm.c
1861 unsigned int size ATTRIBUTE_UNUSED,
1862 int aligned_p ATTRIBUTE_UNUSED)
1863 {
1864 const char *op = integer_asm_op (size, aligned_p);
1865 return op && (assemble_integer_with_op (op, x), true);
1866 }
1819 const char *
1820 integer_asm_op (int size, int aligned_p) in varasm.c
1821 {
1822 struct asm_int_op *ops;
1823
1824 if (aligned_p)
1825 ops = &targetm .asm_out.aligned_op;
1826 else
1827 ops = &targetm .asm_out.unaligned_op;
1828
1829 switch (size)
1830 {
1831 case 1:
1832 return targetm .asm_out.byte_op;
1833 case 2:
1834 return ops->hi;
1835 case 4:
1836 return ops->si;
1837 case 8:
1838 return ops->di;
1839 case 16:
1840 return ops->ti;
1841 default :
1842 return NULL;
1843 }
1844 }
For x86/Linux, hooks of unsigned, singed are equal: HI mode uses ASM_SHORT, SI mode uses ASM_LONG, and DI mode uses ASM_QUAD. Their definitions are:
30 #define ASM_SHORT "/t.value/t" in att.h
31 #define ASM_LONG "/t.long/t"
32 #define ASM_QUAD "/t.quad/t" /* Should not be used for 32bit compilation. */
While for byte_op hook, in most targets, it uses the default definition:
35 #define TARGET_ASM_BYTE_OP "/t.byte/t" in target-def.h
If the hook is not NULL, then executes below function (by C++’s optimization rule, once op at line 1865 is NULL, it won’t call assemble_integer_with_op ).
1849 void
1850 assemble_integer_with_op (const char *op, rtx x) in varasm.c
1851 {
1852 fputs (op, asm_out_file );
1853 output_addr_const (asm_out_file , x);
1854 fputc ('/n', asm_out_file );
1855 }
The content of op is first output, which declares the size of the constant. Then below function handles the content of the constant. Note that LABEL_REF represents user-defined label, its operand is CODE_LABEL. Besides, CODE_LABEL is also used for extra and no user-defined label for (conditional) jump by the compiler. See that these labels have name of form “*LLn ” (name of constant has format “*LLCn ” instead).
3163 void
3164 output_addr_const (FILE *file, rtx x) in final.c
3165 {
3166 char buf[256];
3167
3168 restart:
3169 switch (GET_CODE (x))
3170 {
3171 case PC:
3172 putc ('.', file);
3173 break ;
3174
3175 case SYMBOL_REF:
3176 #ifdef ASM_OUTPUT_SYMBOL_REF
3177 ASM_OUTPUT_SYMBOL_REF (file, x);
3178 #else
3179 assemble_name (file, XSTR (x, 0));
3180 #endif
3181 break ;
3182
3183 case LABEL_REF:
3184 x = XEXP (x, 0);
3185 /* Fall through. */
3186 case CODE_LABEL:
3187 ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x));
3188 #ifdef ASM_OUTPUT_LABEL_REF
3189 ASM_OUTPUT_LABEL_REF (file, buf);
3190 #else
3191 assemble_name (file, buf);
3192 #endif
3193 break ;
3194
3195 case CONST_INT:
3196 fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
3197 break ;
3198
3199 case CONST:
3200 /* This used to output parentheses around the expression,
3201 but that does not work on the 386 (either ATT or BSD assembler). */
3202 output_addr_const (file, XEXP (x, 0));
3203 break ;
3204
3205 case CONST_DOUBLE:
3206 if (GET_MODE (x) == VOIDmode)
3207 {
3208 /* We can use %d if the number is one word and positive. */
3209 if (CONST_DOUBLE_HIGH (x))
3210 fprintf (file, HOST_WIDE_INT_PRINT_DOUBLE_HEX,
3211 CONST_DOUBLE_HIGH (x), CONST_DOUBLE_LOW (x));
3212 else if (CONST_DOUBLE_LOW (x) < 0)
3213 fprintf (file, HOST_WIDE_INT_PRINT_HEX, CONST_DOUBLE_LOW (x));
3214 else
3215 fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x));
3216 }
3217 else
3218 /* We can't handle floating point constants;
3219 PRINT_OPERAND must handle them. */
3220 output_operand_lossage ("floating constant misused");
3221 break ;
3222
3223 case PLUS:
3224 /* Some assemblers need integer constants to appear last (eg masm). */
3225 if (GET_CODE (XEXP (x, 0)) == CONST_INT)
3226 {
3227 output_addr_const (file, XEXP (x, 1));
3228 if (INTVAL (XEXP (x, 0)) >= 0)
3229 fprintf (file, "+");
3230 output_addr_const (file, XEXP (x, 0));
3231 }
3232 else
3233 {
3234 output_addr_const (file, XEXP (x, 0));
3235 if (GET_CODE (XEXP (x, 1)) != CONST_INT
3236 || INTVAL (XEXP (x, 1)) >= 0)
3237 fprintf (file, "+");
3238 output_addr_const (file, XEXP (x, 1));
3239 }
3240 break ;
3241
3242 case MINUS:
3243 /* Avoid outputting things like x-x or x+5-x,
3244 since some assemblers can't handle that. */
3245 x = simplify_subtraction (x);
3246 if (GET_CODE (x) != MINUS)
3247 goto restart;
3248
3249 output_addr_const (file, XEXP (x, 0));
3250 fprintf (file, "-");
3251 if ((GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) >= 0)
3252 || GET_CODE (XEXP (x, 1)) == PC
3253 || GET_CODE (XEXP (x, 1)) == SYMBOL_REF)
3254 output_addr_const (file, XEXP (x, 1));
3255 else
3256 {
3257 fputs (targetm .asm_out.open_paren, file);
3258 output_addr_const (file, XEXP (x, 1));
3259 fputs (targetm .asm_out.close_paren, file);
3260 }
3261 break ;
3262
3263 case ZERO_EXTEND:
3264 case SIGN_EXTEND:
3265 case SUBREG:
3266 output_addr_const (file, XEXP (x, 0));
3267 break ;
3268
3269 default :
3270 #ifdef OUTPUT_ADDR_CONST_EXTRA
3271 OUTPUT_ADDR_CONST_EXTRA (file, x, fail);
3272 break ;
3273
3274 fail:
3275 #endif
3276 output_operand_lossage ("invalid expression as operand");
3277 }
3278 }
Pay attention to comment at line 3243, remember that expression in RTX form uses prefix (Polish) notation, so “x+5-x” would be “-(+(x, 5), x)” --- the outmost node is MINUS.
2857 rtx
2858 simplify_subtraction (rtx x) in varasm.c
2859 {
2860 struct rtx_const val0, val1;
2861
2862 decode_rtx_const (GET_MODE (x), XEXP (x, 0), &val0);
2863 decode_rtx_const (GET_MODE (x), XEXP (x, 1), &val1);
2864
2865 if (val0.kind >= RTX_INT
2866 && val0.kind == val1.kind
2867 && val0.un.addr.base == val1.un.addr.base
2868 && val0.un.addr.symbol == val1.un.addr.symbol)
2869 return GEN_INT (val0.un.addr.offset - val1.un.addr.offset);
2870
2871 return x;
2872 }
In RTX form, “x+5” is a CONST node (because it is a rvalue, which content can’t be modified), in which ‘x” is a SYMBOL_REF having assemble name (mangled name), and ‘5’ is a CONST_INT. See that as RTX form is much closer assemble than the front-end’s intermediate tree form, at here there is no DECL, TYPE and such nodes.
2029 enum kind { RTX_UNKNOWN, RTX_DOUBLE, RTX_VECTOR, RTX_INT, RTX_UNSPEC };
2030 struct rtx_const GTY(()) in varasm.c
2031 {
2032 ENUM_BITFIELD(kind) kind : 16;
2033 ENUM_BITFIELD(machine_mode) mode : 16;
2034 union rtx_const_un {
2035 REAL_VALUE_TYPE GTY ((tag ("4"))) du;
2036 struct rtx_const_u_addr {
2037 rtx base;
2038 const char *symbol;
2039 HOST_WIDE_INT offset;
2040 } GTY ((tag ("1"))) addr;
2041 struct rtx_const_u_di {
2042 HOST_WIDE_INT high;
2043 HOST_WIDE_INT low;
2044 } GTY ((tag ("0"))) di;
2045
2046 /* The max vector size we have is 16 wide; two variants for
2047 integral and floating point vectors. */
2048 struct rtx_const_int_vec {
2049 HOST_WIDE_INT high;
2050 HOST_WIDE_INT low;
2051 } GTY ((tag ("2"))) int_vec[16];
2052
2053 REAL_VALUE_TYPE GTY ((tag ("3"))) fp_vec[8];
2054
2055 } GTY ((desc ("%1.kind >= RTX_INT"), descbits ("1"))) un;
2056 };
To simplify the expression, it should express RTL node of different type in the same way, for example, in “x+5-x”, “x+5” is a CONST, and “x” is SYMBOL_REF, to simplify this expression, it must represent “x+5” and “x” in the same way to make further processing possible.
The way uses above structure rtx_const, it classifies RTL nodes into 5 kinds which is described by enumerator kind. The transformation is done by below function.
2679 static void
2680 decode_rtx_const (enum machine_mode mode, rtx x, struct rtx_const *value) in varasm.c
2681 {
2682 /* Clear the whole structure, including any gaps. */
2683 memset (value, 0, sizeof (struct rtx_const));
2684
2685 value->kind = RTX_INT; /* Most usual kind. */
2686 value->mode = mode;
2687
2688 switch (GET_CODE (x))
2689 {
2690 case CONST_DOUBLE:
2691 value->kind = RTX_DOUBLE;
2692 if (GET_MODE (x) != VOIDmode)
2693 {
2694 const REAL_VALUE_TYPE *r = CONST_DOUBLE_REAL_VALUE (x);
2695
2696 value->mode = GET_MODE (x);
2697
2698 /* Copy the REAL_VALUE_TYPE by members so that we don't
2699 copy garbage from the original structure into our
2700 carefully cleaned hashing structure. */
2701 value->un.du.class = r->class;
2702 value->un.du.sign = r->sign;
2703 switch (r->class)
2704 {
2705 case rvc_zero:
2706 case rvc_inf:
2707 break ;
2708 case rvc_normal:
2709 value->un.du.exp = r->exp;
2710 /* Fall through. */
2711 case rvc_nan:
2712 memcpy (value->un.du.sig, r->sig, sizeof (r->sig));
2713 break ;
2714 default :
2715 abort ();
2716 }
2717 }
2718 else
2719 {
2720 value->un.di.low = CONST_DOUBLE_LOW (x);
2721 value->un.di.high = CONST_DOUBLE_HIGH (x);
2722 }
2723 break ;
2724
2725 case CONST_VECTOR:
2726 {
2727 int units, i;
2728
2729 units = CONST_VECTOR_NUNITS (x);
2730 value->kind = RTX_VECTOR;
2731 value->mode = mode;
2732
2733 if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
2734 {
2735 for (i = 0; i < units; ++i)
2736 {
2737 rtx elt = CONST_VECTOR_ELT (x, i);
2738 if (GET_CODE (elt) == CONST_INT)
2739 {
2740 value->un.int_vec[i].low = INTVAL (elt);
2741 value->un.int_vec[i].high = 0;
2742 }
2743 else
2744 {
2745 value->un.int_vec[i].low = CONST_DOUBLE_LOW (elt);
2746 value->un.int_vec[i].high = CONST_DOUBLE_HIGH (elt);
2747 }
2748 }
2749 }
2750 else if (GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
2751 {
2752 for (i = 0; i < units; ++i)
2753 {
2754 const REAL_VALUE_TYPE *r
2755 = CONST_DOUBLE_REAL_VALUE (CONST_VECTOR_ELT (x, i));
2756 REAL_VALUE_TYPE *d = &value->un.fp_vec[i];
2757
2758 /* Copy the REAL_VALUE_TYPE by members so that we don't
2759 copy garbage from the original structure into our
2760 carefully cleaned hashing structure. */
2761 d->class = r->class;
2762 d->sign = r->sign;
2763 switch (r->class)
2764 {
2765 case rvc_zero:
2766 case rvc_inf:
2767 break ;
2768 case rvc_normal:
2769 d->exp = r->exp;
2770 /* Fall through. */
2771 case rvc_nan:
2772 memcpy (d->sig, r->sig, sizeof (r->sig));
2773 break ;
2774 default :
2775 abort ();
2776 }
2777 }
2778 }
2779 else
2780 abort ();
2781 }
2782 break ;
2783
2784 case CONST_INT:
2785 value->un.addr.offset = INTVAL (x);
2786 break ;
2787
2788 case SYMBOL_REF:
2789 case LABEL_REF:
2790 case PC:
2791 value->un.addr.base = x;
2792 break ;
2793
2794 case CONST:
2795 x = XEXP (x, 0);
2796 if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
2797 {
2798 value->un.addr.base = XEXP (x, 0);
2799 value->un.addr.offset = INTVAL (XEXP (x, 1));
2800 }
2801 else if (GET_CODE (x) == MINUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
2802 {
2803 value->un.addr.base = XEXP (x, 0);
2804 value->un.addr.offset = - INTVAL (XEXP (x, 1));
2805 }
2806 else
2807 {
2808 value->un.addr.base = x;
2809 value->un.addr.offset = 0;
2810 }
2811 break ;
2812
2813 default :
2814 value->kind = RTX_UNKNOWN;
2815 break ;
2816 }
2817
2818 if (value->kind == RTX_INT && value->un.addr.base != 0
2819 && GET_CODE (value->un.addr.base) == UNSPEC)
2820 {
2821 /* For a simple UNSPEC, the base is set to the
2822 operand, the kind field is set to the index of
2823 the unspec expression.
2824 Together with the code below, in case that
2825 the operand is a SYMBOL_REF or LABEL_REF,
2826 the address of the string or the code_label
2827 is taken as base. */
2828 if (XVECLEN (value->un.addr.base, 0) == 1)
2829 {
2830 value->kind = RTX_UNSPEC + XINT (value->un.addr.base, 1);
2831 value->un.addr.base = XVECEXP (value->un.addr.base, 0, 0);
2832 }
2833 }
2834
2835 if (value->kind >= RTX_INT && value->un.addr.base != 0)
2836 switch (GET_CODE (value->un.addr.base))
2837 {
2838 case SYMBOL_REF:
2839 /* Use the string's address, not the SYMBOL_REF's address,
2840 for the sake of addresses of library routines. */
2841 value->un.addr.symbol = XSTR (value->un.addr.base, 0);
2842 value->un.addr.base = NULL_RTX;
2843 break ;
2844
2845 case LABEL_REF:
2846 /* For a LABEL_REF, compare labels. */
2847 value->un.addr.base = XEXP (value->un.addr.base, 0);
2848
2849 default :
2850 break ;
2851 }
2852 }
First at line 2685, value ’s kind is set as RTX_INT, which is the most possible value; kind is then will be overwritten at line 2691, 2730, 2814 and 2830. So expression in from “x+5” would have kind of RTX_INT, similarly are ‘x’ (SYMBOL_REF), and “goto label1” (LABEL_REF).
At line 2819, RTL node UNSPEC is also from CONST node, it represents a machine specifies operation. Its 1st operand is a vector of operands being used by the operation so that any needed reloads can be done; 2nd operand is a unique value saying which of a number of machine-specific operations is to be performed. So line 2830 makes these operations matches kind one for one.
Back simplify_subtraction , see that only kind of RTX_INT, RTX_UNSPEC or following (machine specifies operation) gets handled. Others, especial RTX_CONST, are ignore, it is because if RTX_CONST node’s content has optimization opportunity, it should have been processed by constant folding before.
Above is the handling of integer target can support efficiently, for other integer, in assemble_integer at line 1881, the hook returns false. For x86/Linux, the integer has TI mode (16 bytes, in 64 bits arithmetic operation, the intermediate value is of TI mode, usually saved by SSE register, but if spill happens, it will invoke assemble_ineger to push into the stack). The handling of data of TI mode is to split it into 4 words (4 bytes), then for immediate number (CONST_INT, CONST_DOUBLE etc), which is done by simplify_immed_subreg , then output by assemble_ineger . The treatment in simplify_immed_subreg is not complex and we have seen before, so skip it here.