GCC-3.4.6源代码学习笔记(61)

4.3.1.7.5.6.5.2.        处理format属性

对于printfscanf等类型的函数,真正的检查要在调用处进行。在其声明中,只进行少许检查,由handle_format_attribute 执行。现在参数args是节点ATTR_PRINTF_1_0,而传入的no_add_attrs0

 

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    }

 

现在下面的typebuiltin_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要求的是类型或函数类型。而argsATTR_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的属性链表中包含了formatnonnull

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_attributes297行)。

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_XXXXXX__builtin版本的内建函数名)不能给出满意的结果,那么expand_builtin最后将调用expand_call来构建RTXCALL对象,注意这个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对象的参数,最终会被认为是一个非编译时常量。

你可能感兴趣的:(GCC-3.4.6源代码学习笔记(61))