4.3.1.7.5.6.5.2. 处理format属性
对于printf,scanf等类型的函数,真正的检查要在调用处进行。在其声明中,只进行少许检查,由handle_format_attribute 执行。现在参数args是节点ATTR_PRINTF_1_0,而传入的no_add_attrs是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 }
第一步是验证属性ATTR_PRINTF_1_0。工作很简单——检查要求的格式是有效的,所带的实参是合理的。编译器定义了下面的类型来携带所需要的信息。
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 }
验证格式的类型简单明了。为了保存所有格式类型的信息,GCC维护了一个全局数组format_types,该数组以format_type为索引。在format_types中找出格式所对应的名字就能得到答案。
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 }
而对于格式属性的参数的要求是,它们必须是常量,而且格式字符串必须出现在要被格式的参数之前(如果有的话,记住0表示不使用)。
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 }
现在下面的type是builtin_types [BT_FN_INT_CONST_STRING_VALIST_ARG]——内建函数“__builtin_vprintf”的类型。对于对应格式字符串的参数,它应该是const_string_type_node,这个类型在c_common_nodes_and_builtins中创建,是代表“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 }
这里除了确认格式字符串真的是字符串外,没有别的事可做。
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. 设置format属性
一旦格式属性的有效性得到验证,该属性就应该被加入到相应节点的attribute链表中。注意在这里anode指向builtin_types [BT_FN_INT_CONST_STRING_VALIST_ARG],因为这个属性是应用于类型的。
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 }
应用了属性的类型应视为一个新的类型(至少从C++语言的角度来说)。这个新类型由build_type_attribute_variant来构建。注意到anode将被这个将要创建的节点所更新。同时看到参数attribute是在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 }
而在307行,若fn_ptr_tmp不为NULL,表明
4.3.1.7.5.6.5.4. 处理nonnull属性
属性nonnull表示参数(指针)不能是NULL。同样,检实参是否为空,只能在函数调用处进行。handle_nonnull_attributes所能做的,只是验证属性的声明是有效的,并且在函数类型声明中,相应的参数是指针类型。
在这里参数node是,在设置format属性中,为格式属性所构建的FUNCTION_TYPE节点,因为属性nonnull要求的是类型或函数类型。而args是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 }
属性的参数如果出现,必须是整型常量而且大于0。这由get_nonnull_operand来保证。而且该函数还会获取声明不为空的参数的序号(从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 }
然后根据由get_nonnull_operand所获取的参数的序号,这里为1, handle_nonnull_attribute检查在那个位置的参数是否为指针类型(vprintf类型的参数1是类型builtin_types [BT_CONST_STRING]——“const char*”)。
4.3.1.7.5.6.5.5. 设置nonnull属性
设置这个属性和设置格式属性一样。现在所对应的FUNCTION_TYPE的属性链表中包含了format及nonnull。
4.3.1.7.5.6.5.6. 处理nothrow属性
属性nothrow应该应用于FUNCTION_DECL,此处参数node是为__builtin_vprintf在创建FUNCTION_DECL节点中所构建的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. 设置nothrow属性
属性nothrow应该应用于FUNCTION_DECL,因此它被整合入代表该声明的节点(参见decl_attributes的297行)。
4.3.1.7.5.6.6. 题外话 —— 编译内建函数
内建函数是一个很有趣的话题,在此我们稍稍偏离一下编译器目前的任务——完成初始化,看一下如何编译内建函数的调用。在后面我们会看到,对于函数调用,编译器将为之构建一个CALL_EXPR的节点,其中的操作数是调用的函数及实参列表。在经过前端一系列的处理,包括前端所能做的优化后,这个CALL_EXPR所在的表达式节点,被交给expand_expr_real来为后端产生其所期望的输入——RTX对象。在这个函数中,可以看到这样的片段:
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 }
在expand_builtin中,正如我们已经了解的,对于有库函数版本的内建函数,如果展开函数expand_XXX(XXX为__builtin版本的内建函数名)不能给出满意的结果,那么expand_builtin最后将调用expand_call来构建RTX的CALL对象,注意这个CALL对象的对象,将是保存在DECL_ASSEMBLE_NAME域的标识符,对于有库函数版本的内建函数,这就是库函数名。在当前版本的GCC中,我们的例子“__builtin_vprintf”实际上还没有实现,expand_builtin直截了当地为其调用库函数。
作为一个有趣的例子,我们看一下__builtin_constant_p的实现。这个内建函数判定其参数是否是一个编译时常量,它没有对应的库函数,不过它的展开不可能失败。沿上面所示的调用路径,当expand_builtin看到内建函数的编码为BUILT_IN_CONSTANT_P时,函数expand_builtin_constant_p粉墨登场。
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 }
在1166行,该函数为__builtin_constant_p构建了一个RTX对象——CONSTANT_P_RTX。这是一个特殊的RTX对象,在随后的后端的处理中,只要能判定其参数是常量,就将其简化为const1_rtx。如果得不到简化,这个CONSTANT_P_RTX对象的参数,最终会被认为是一个非编译时常量。