4.3.1.7.5.6.5.2. Handle attribute of format
For function of printf, scanf etc type, the real checking could be done only at site of invocation. In its declaraction, few checking can be taken as handle_format_attribute does. Now the argument args here is node of ATTR_PRINTF_1_0, and no_add_attrs passed in is 0.
2741 tree
2742 handle_format_attribute (tree *node, tree name ATTRIBUTE_UNUSED, in c-format.c
2743 tree args, int flags, bool *no_add_attrs)
2744 {
2745 tree type = *node;
2746 function_format_info info;
2747 tree argument;
2748
2749 if (!decode_format_attr (args, &info, 0))
2750 {
2751 *no_add_attrs = true;
2752 return NULL_TREE;
2753 }
The first part of job is to validate the attribute ATTR_PRINTF_1_0. The work is simple – just checks if the format required is valid, and the arguments carried are reasonable. Following types are defined to carry the needed information.
58 enum format_type { printf_format_type, asm_fprintf_format_type, in c-format.c
59 gcc_diag_format_type, gcc_cdiag_format_type,
60 gcc_cxxdiag_format_type,
61 scanf_format_type, strftime_format_type,
62 strfmon_format_type, format_type_error };
64 typedef struct function_format_info
65 {
66 enum format_type format_type; /* type of format (printf, scanf, etc.) */
67 unsigned HOST_WIDE_INT format_num; /* number of format argument */
68 unsigned HOST_WIDE_INT first_arg_num; /* number of first arg (zero for varargs) */
69 } function_format_info;
178 static bool
179 decode_format_attr (tree args, function_format_info *info, int validated_p) in c-format.c
180 {
181 tree format_type_id = TREE_VALUE (args);
182 tree format_num_expr = TREE_VALUE (TREE_CHAIN (args));
183 tree first_arg_num_expr
184 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
185
186 if (TREE_CODE (format_type_id) != IDENTIFIER_NODE)
187 {
188 if (validated_p)
189 abort ();
190 error ("unrecognized format specifier");
191 return false;
192 }
193 else
194 {
195 const char *p = IDENTIFIER_POINTER (format_type_id);
196
197 info->format_type = decode_format_type (p);
198
199 if (info->format_type == format_type_error)
200 {
201 if (validated_p)
202 abort ();
203 warning ("`%s' is an unrecognized format function type", p);
204 return false;
205 }
206 }
Validating the format type is simple and straight-forward. To carry information of all format types, GCC maintains a global array format_types which is indexed by format_type. Searching the name of format in format_types can give the answer.
1036 static enum format_type
1037 decode_format_type (const char *s) in c-format.c
1038 {
1039 int i;
1040 int slen;
1041 slen = strlen (s);
1042 for (i = 0; i < (int) format_type_error; i++)
1043 {
1044 int alen;
1045 if (!strcmp (s, format_types[i].name))
1046 break;
1047 alen = strlen (format_types[i].name);
1048 if (slen == alen + 4 && s[0] == '_' && s[1] == '_'
1049 && s[slen - 1] == '_' && s[slen - 2] == '_'
1050 && !strncmp (s + 2, format_types[i].name, alen))
1051 break;
1052 }
1053 return ((enum format_type) i);
1054 }
The requirement for the arguments of format attribute is that they must be constant, and format string should appear before arguments to be formatted (if any, remember 0 means not used).
decode_format_attr (continue)
208 if (!get_constant (format_num_expr, &info->format_num, validated_p))
209 {
210 error ("format string has invalid operand number");
211 return false;
212 }
213
214 if (!get_constant (first_arg_num_expr, &info->first_arg_num, validated_p))
215 {
216 error ("'...' has invalid operand number");
217 return false;
218 }
219
220 if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num)
221 {
222 if (validated_p)
223 abort ();
224 error ("format string arg follows the args to be formatted");
225 return false;
226 }
227
228 return true;
229 }
Now type below is builtin_types [BT_FN_INT_CONST_STRING_VALIST_ARG] – the type of builtin “__builtin_vprintf”. For the argument of format string, it should be const_string_type_node, which is created in c_common_nodes_and_builtins and is the node of “const char*”.
handle_format_attribute (continue)
2755 argument = TYPE_ARG_TYPES (type);
2756 if (argument)
2757 {
2758 if (!check_format_string (argument, info.format_num, flags,
2759 no_add_attrs))
2760 return NULL_TREE;
2761
2762 if (info.first_arg_num != 0)
2763 {
2764 unsigned HOST_WIDE_INT arg_num = 1;
2765
2766 /* Verify that first_arg_num points to the last arg,
2767 the ... */
2768 while (argument)
2769 arg_num++, argument = TREE_CHAIN (argument);
2770
2771 if (arg_num != info.first_arg_num)
2772 {
2773 if (!(flags & (int) ATTR_FLAG_BUILT_IN))
2774 error ("args to be formatted is not '...'");
2775 *no_add_attrs = true;
2776 return NULL_TREE;
2777 }
2778 }
2779 }
...
2816 return NULL_TREE;
2817 }
Nothing can be done here, except ensures that the format string is a real string!
121 static bool
122 check_format_string (tree argument, in c-format.c
123 unsigned HOST_WIDE_INT format_num, int flags, bool *no_add_attrs)
124 {
125 unsigned HOST_WIDE_INT i;
126
127 for (i = 1; i != format_num; i++)
128 {
129 if (argument == 0)
130 break;
131 argument = TREE_CHAIN (argument);
132 }
133
134 if (!argument
135 || TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE
136 || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (argument)))
137 != char_type_node))
138 {
139 if (!(flags & (int) ATTR_FLAG_BUILT_IN))
140 error ("format string arg not a string type");
141 *no_add_attrs = true;
142 return false;
143 }
144
145 return true;
146 }
4.3.1.7.5.6.5.3. Install attribute of format
Soon as the validation of the attribute format is verified, it should be added into the list of attribute of the interested node. Here notice that anode points to builtin_types [BT_FN_INT_CONST_STRING_VALIST_ARG], it is because the attribute applies to type.
decl_attributes (continue)
260 /* Layout the decl in case anything changed. */
261 if (spec->type_required && DECL_P (*node)
262 && (TREE_CODE (*node) == VAR_DECL
263 || TREE_CODE (*node) == PARM_DECL
264 || TREE_CODE (*node) == RESULT_DECL))
265 {
266 /* Force a recalculation of mode and size. */
267 DECL_MODE (*node) = VOIDmode;
268 DECL_SIZE (*node) = 0;
269 if (!DECL_USER_ALIGN (*node))
270 DECL_ALIGN (*node) = 0;
271
272 layout_decl (*node, 0);
273 }
274
275 if (!no_add_attrs)
276 {
277 tree old_attrs;
278 tree a;
279
280 if (DECL_P (*anode))
281 old_attrs = DECL_ATTRIBUTES (*anode);
282 else
283 old_attrs = TYPE_ATTRIBUTES (*anode);
284
285 for (a = lookup_attribute (spec->name, old_attrs);
286 a != NULL_TREE;
287 a = lookup_attribute (spec->name, TREE_CHAIN (a)))
288 {
289 if (simple_cst_equal (TREE_VALUE (a), args) == 1)
290 break;
291 }
292
293 if (a == NULL_TREE)
294 {
295 /* This attribute isn't already in the list. */
296 if (DECL_P (*anode))
297 DECL_ATTRIBUTES (*anode) = tree_cons (name, args, old_attrs);
298 else if (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
299 TYPE_ATTRIBUTES (*anode) = tree_cons (name, args, old_attrs);
300 else
301 *anode = build_type_attribute_variant (*anode,
302 tree_cons (name, args,
303 old_attrs));
304 }
305 }
306
307 if (fn_ptr_tmp)
308 {
309 /* Rebuild the function pointer type and put it in the
310 appropriate place. */
311 fn_ptr_tmp = build_pointer_type (fn_ptr_tmp);
312 if (DECL_P (*node))
313 TREE_TYPE (*node) = fn_ptr_tmp;
314 else if (TREE_CODE (*node) == POINTER_TYPE)
315 *node = fn_ptr_tmp;
316 else
317 abort ();
318 }
319 }
320
321 return returned_attrs;
322 }
The type applied with the attribute should be regarded as new type (at least from the point of C++). This new type is created by build_type_attribute_variant. Notice that anode will be updated by the type being created. Also see that arugment attribute is the updated attribute list at line 302.
2632 tree
2633 build_type_attribute_variant (tree ttype, tree attribute) in tree.c
2634 {
2635 if (! attribute_list_equal (TYPE_ATTRIBUTES (ttype), attribute))
2636 {
2637 unsigned int hashcode;
2638 tree ntype;
2639
2640 ntype = copy_node (ttype);
2641
2642 TYPE_POINTER_TO (ntype) = 0;
2643 TYPE_REFERENCE_TO (ntype) = 0;
2644 TYPE_ATTRIBUTES (ntype) = attribute;
2645
2646 /* Create a new main variant of TYPE. */
2647 TYPE_MAIN_VARIANT (ntype) = ntype;
2648 TYPE_NEXT_VARIANT (ntype) = 0;
2649 set_type_quals (ntype, TYPE_UNQUALIFIED);
2650
2651 hashcode = (TYPE_HASH (TREE_CODE (ntype))
2652 + TYPE_HASH (TREE_TYPE (ntype))
2653 + attribute_hash_list (attribute));
2654
2655 switch (TREE_CODE (ntype))
2656 {
2657 case FUNCTION_TYPE:
2658 hashcode += TYPE_HASH (TYPE_ARG_TYPES (ntype));
2659 break;
2660 case ARRAY_TYPE:
2661 hashcode += TYPE_HASH (TYPE_DOMAIN (ntype));
2662 break;
2663 case INTEGER_TYPE:
2664 hashcode += TYPE_HASH (TYPE_MAX_VALUE (ntype));
2665 break;
2666 case REAL_TYPE:
2667 hashcode += TYPE_HASH (TYPE_PRECISION (ntype));
2668 break;
2669 default:
2670 break;
2671 }
2672
2673 ntype = type_hash_canon (hashcode, ntype);
2674 ttype = build_qualified_type (ntype, TYPE_QUALS (ttype));
2675 }
2676
2677 return ttype;
2678 }
Then at line 307, fn_ptr_tmp is NULL
4.3.1.7.5.6.5.4. Handle attribute of nonnull
Attribute nonnull indicates the arguments (pointer) that should not be null. Also checking if the arguments are null or not only can be done at place of invocation of the function. All handle_nonnull_attributes can be done just verifies the attribute declaration is valid and in the function type declaration, corresponding argument is of pointer type.
Here argument node is the FUNCTION_TYPE created for attribute format in section Create Nodes For Builtin Function Types, because attribute nonnull requires type and function type. And args is ATTR_NOTHROW_NONNULL_1.
5305 static tree
5306 handle_nonnull_attribute (tree *node, tree name ATTRIBUTE_UNUSED, in c-common.c
5307 tree args, int flags ATTRIBUTE_UNUSED,
5308 bool *no_add_attrs)
5309 {
5310 tree type = *node;
5311 unsigned HOST_WIDE_INT attr_arg_num;
5312
5313 /* If no arguments are specified, all pointer arguments should be
5314 non-null. Verify a full prototype is given so that the arguments
5315 will have the correct types when we actually check them later. */
5316 if (! args)
5317 {
5318 if (! TYPE_ARG_TYPES (type))
5319 {
5320 error ("nonnull attribute without arguments on a non-prototype");
5321 *no_add_attrs = true;
5322 }
5323 return NULL_TREE;
5324 }
5325
5326 /* Argument list specified. Verify that each argument number references
5327 a pointer argument. */
5328 for (attr_arg_num = 1; args; args = TREE_CHAIN (args))
5329 {
5330 tree argument;
5331 unsigned HOST_WIDE_INT arg_num, ck_num;
5332
5333 if (! get_nonnull_operand (TREE_VALUE (args), &arg_num))
5334 {
5335 error ("nonnull argument has invalid operand number (arg %lu)",
5336 (unsigned long) attr_arg_num);
5337 *no_add_attrs = true;
5338 return NULL_TREE;
5339 }
5340
5341 argument = TYPE_ARG_TYPES (type);
5342 if (argument)
5343 {
5344 for (ck_num = 1; ; ck_num++)
5345 {
5346 if (! argument || ck_num == arg_num)
5347 break;
5348 argument = TREE_CHAIN (argument);
5349 }
5350
5351 if (! argument
5352 || TREE_CODE (TREE_VALUE (argument)) == VOID_TYPE)
5353 {
5354 error ("nonnull argument with out-of-range operand number (arg %lu, operand %lu)",
5355 (unsigned long) attr_arg_num, (unsigned long) arg_num);
5356 *no_add_attrs = true;
5357 return NULL_TREE;
5358 }
5359
5360 if (TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE)
5361 {
5362 error ("nonnull argument references non-pointer operand (arg %lu, operand %lu)",
5363 (unsigned long) attr_arg_num, (unsigned long) arg_num);
5364 *no_add_attrs = true;
5365 return NULL_TREE;
5366 }
5367 }
5368 }
5369
5370 return NULL_TREE;
5371 }
The arguments of the attribute if present should be integer constant and should be larger that 0. It is guaranteed by get_nonnull_operand. And the function also fetches the NO. of argument that claims nonnull (begin at 1).
5449 static bool
5450 get_nonnull_operand (tree arg_num_expr, unsigned HOST_WIDE_INT *valp) in c-common.c
5451 {
5452 /* Strip any conversions from the arg number and verify they
5453 are constants. */
5454 while (TREE_CODE (arg_num_expr) == NOP_EXPR
5455 || TREE_CODE (arg_num_expr) == CONVERT_EXPR
5456 || TREE_CODE (arg_num_expr) == NON_LVALUE_EXPR)
5457 arg_num_expr = TREE_OPERAND (arg_num_expr, 0);
5458
5459 if (TREE_CODE (arg_num_expr) != INTEGER_CST
5460 || TREE_INT_CST_HIGH (arg_num_expr) != 0)
5461 return false;
5462
5463 *valp = TREE_INT_CST_LOW (arg_num_expr);
5464 return true;
5465 }
Then according to the number of argument retrieved by get_nonnull_operand, now 1, handle_nonnull_attribute checks that the argument at that position is of pointer type (argument at position 1 of the type of vprintf is type builtin_types [BT_CONST_STRING] – “const char*”).
4.3.1.7.5.6.5.5. Install attribute of nonnull
Installing this attribute is just the same as that of attribute format. Now the FUNCTION_TYPE referred by vprintf has attribute list containing format and nonnull.
4.3.1.7.5.6.5.6. Handle attribute of nothrow
Attribute nothrow should be applied to FUNCTION_DECL, here parameter node is the FUNCTION_DECL created for __builtin_vprintf in section Create FUNCTION_DECL.
5470 static tree
5471 handle_nothrow_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, in c-common.c
5472 int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
5473 {
5474 if (TREE_CODE (*node) == FUNCTION_DECL)
5475 TREE_NOTHROW (*node) = 1;
5476 /* ??? TODO: Support types. */
5477 else
5478 {
5479 warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
5480 *no_add_attrs = true;
5481 }
5482
5483 return NULL_TREE;
5484 }
4.3.1.7.5.6.5.7. Install attribute of nothrow
The attribute nothrow as should be applied to declaration. So it should be integrated into the node of declaration (see line 297 in decl_attributes).
4.3.1.7.5.6.6. Off topic – compiling builtins
Builtin function is an interesting topic, here we just deviate the current task of the compiler – initializing alittle bit, to have a look at how will the compiler generate code for the builtin invocation. Later, we can see that for function call, the compiler will construct a CALL_EXPR, in which the operators include function called and the arguments list. After processing in the front-end, including possible optimization the front-end can take, the node of the expression containing this is handed to expand_expr_real to generate the rtx object for the back-end. In the function, we can see:
6262 rtx
6263 expand_expr_real (tree exp, rtx target, enum machine_mode tmode, in expr.c
6264 enum expand_modifier modifier, rtx *alt_rtl)
6265 {
…
6361 switch (code)
6362 {
…
7521 case CALL_EXPR:
7522 /* Check for a built-in function. */
7523 if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
7524 && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
7525 == FUNCTION_DECL)
7526 && DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
7527 {
7528 if (DECL_BUILT_IN_CLASS (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
7529 == BUILT_IN_FRONTEND)
7530 return (*lang_hooks.expand_expr) (exp, original_target,
7531 tmode, modifier,
7532 alt_rtl);
7533 else
7534 return expand_builtin (exp, target, subtarget, tmode, ignore);
7535 }
…
9118 }
…
9133 }
In expand_builtin, as we have known, for builtin having library function version, if the expanding function expand_XXX (XXX is the name of the __builtin version), then at last expand_builtin will invoke expand_call to build the RTX object of CALL, note that the subject of the CALL object is the identifier kept in field DECL_ASSEMBLE_NAME. And for builtin having library function version, it is the name of the library function. In current GCC, our example “__builtin_vprintf” is still not implemented, expand_builtin calls the library function directly.
Being a funny case, we now learn the implementation of __builtin_constant_p. This builtin deteremines whether its argument is a compiler-time constant, it hasn’t the library function peer, but it will never be fault in expansion. Along above call stack, when expand_builtin finds that the builtin has code of BUILT_IN_CONSTANT_P, expand_builtin_constant_p comes。
1146 static rtx
1147 expand_builtin_constant_p (tree arglist, enum machine_mode target_mode) in builtins.c
1148 {
1149 rtx tmp;
1150
1151 if (arglist == 0)
1152 return const0_rtx;
1153 arglist = TREE_VALUE (arglist);
1154
1155 /* We have taken care of the easy cases during constant folding. This
1156 case is not obvious, so emit (constant_p_rtx (ARGLIST)) and let CSE
1157 get a chance to see if it can deduce whether ARGLIST is constant.
1158 If CSE isn't going to run, of course, don't bother waiting. */
1159
1160 if (cse_not_expected)
1161 return const0_rtx;
1162
1163 current_function_calls_constant_p = 1;
1164
1165 tmp = expand_expr (arglist, NULL_RTX, VOIDmode, 0);
1166 tmp = gen_rtx_CONSTANT_P_RTX (target_mode, tmp);
1167 return tmp;
1168 }
At line 1166, the function builds a rtx object CONSTANT_P_RTX for __builtin_constant_p. Then in later processing in the back-end, as long as its argument is known as constant, the back-end will simplify it into const1_rtx. And if no simplication can be done, at last its argument will be regarded as non-constant at compile-time.