Studying note of GCC-3.4.6 source (61)

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.

 

你可能感兴趣的:(function,String,tree,null,attributes,Types)