Studying note of GCC-3.4.6 source (8)

1.3. Create nodes for non-unary expression

build can be used to create tree node for expression. But it can only accept nodes of expression, and composes them into a more complex one. It can’t be used to create the first level expression (that is, according to the semantics components analyized by parser, constructs the node of expression. These procedures require far more complex functions to do the handling. We will see some code scrap later). Anyway, build is still used extensively, since in C/C++, the expression of expressions due most to their ability of nesting and the places they can appear. And constructing complex expression from simple ones is the task of the function.

1.3.1. Create tree node

As we have seen, the program will be transformed into a tree which is semantic equivalence, the nodes of the tree are various of which each corresponds to certain syntax component. Here build will create node of tree_exp for expressoions.

1.3.1.1.    Type of tree_exp

In front-end, experssion is an instance of tree_exp as following.

 

852  struct tree_exp GTY(())                                                                                     in tree.h

853  {

854    struct tree_common common;

855    int complexity;

856    tree GTY ((special ("tree_exp"),

857              desc ("TREE_CODE ((tree) &%0)")))

858      operands[1];

859  };

 

tree_exp is one type of tree nodes, so it will be created by make_node as other tree nodes.

 

2295 tree

2296 build (enum tree_code code, tree tt, ...)                                                              in tree.c

2297 {

2298   tree t;

2299   int length;

2300   int i;

2301   int fro;

2302   int constant;

2303   va_list p;

2304   tree node;

2305

2306   va_start (p, tt);

2307

2308   t = make_node (code);

2309   length = TREE_CODE_LENGTH (code);

2310   TREE_TYPE (t) = tt;

 

Notice that va_start at line 2306 is not that we use for programing. In GCC, it is the so-called builtin function. We will see detail of builtin function later.

1.3.1.2.    Operand pretreatment

Function build accepts variable number of parameters, so first it needs decide the number of parameter passed in. This number is determined by the type of expression (via tree code).

 

build (continued)

 

2312   /* Below, we automatically set TREE_SIDE_EFFECTS and TREE_READONLY for the

2313     result based on those same flags for the arguments. But if the

2314     arguments aren't really even `tree' expressions, we shouldn't be trying

2315     to do this.  */

2316   fro = first_rtl_op (code);

2317

2318   /* Expressions without side effects may be constant if their

2319     arguments are as well.  */

2320   constant = (TREE_CODE_CLASS (code) == '<'

2321                || TREE_CODE_CLASS (code) == '1'

2322                || TREE_CODE_CLASS (code) == '2'

2323                || TREE_CODE_CLASS (code) == 'c');

 

At line 2316 above, first_rtl_op can find out the number of operands required by the creating expression.

 

1448 int

1449 first_rtl_op (enum tree_code code)                                                                    in tree.c

1450 {

1451   switch (code)

1452   {

1453     case SAVE_EXPR:

1454       return 2;

1455     case GOTO_SUBROUTINE_EXPR:

1456     case RTL_EXPR:

1457       return 0;

1458     case WITH_CLEANUP_EXPR:

1459       return 2;

1460     default:

1461       return TREE_CODE_LENGTH (code);

1462   }

1463 }

 

Line 2320, comparison expression (class “<”), unary arithmetic expression (class “1”), binary arithmetic expression (for example, shift, bit operation, etc. class “2”), constant expression (class “c”) are expressions without side effect, so long as their arguments are without side effect, and are constant if the arguments are constant.

 

build (continued)

 

2325   if (length == 2)

2326   {

2327     /* This is equivalent to the loop below, but faster.  */

2328     tree arg0 = va_arg (p, tree);

2329     tree arg1 = va_arg (p, tree);

2330

2331     TREE_OPERAND (t, 0) = arg0;

2332     TREE_OPERAND (t, 1) = arg1;

2333     TREE_READONLY (t) = 1;

2334     if (arg0 && fro > 0)

2335     {

2336       if (TREE_SIDE_EFFECTS (arg0))

2337         TREE_SIDE_EFFECTS (t) = 1;

2338       if (!TREE_READONLY (arg0))

2339         TREE_READONLY (t) = 0;

2340       if (!TREE_CONSTANT (arg0))

2341         constant = 0;

2342     }

2343

2344     if (arg1 && fro > 1)

2345     {

2346       if (TREE_SIDE_EFFECTS (arg1))

2347         TREE_SIDE_EFFECTS (t) = 1;

2348       if (!TREE_READONLY (arg1))

2349         TREE_READONLY (t) = 0;

2350       if (!TREE_CONSTANT (arg1))

2351         constant = 0;

2352     }

2353   }

2354   else if (length == 1)

2355   {

2356     tree arg0 = va_arg (p, tree);

2357

2358     /* The only one-operand cases we handle here are those with side-effects.

2359       Others are handled with build1. So don't bother checked if the

2360       arg has side-effects since we'll already have set it.

2361

2362       ??? This really should use build1 too.  */

2363     if (TREE_CODE_CLASS (code) != 's')

2364       abort ();

2365     TREE_OPERAND (t, 0) = arg0;

2366   }

2367   else

2368   {

2369     for (i = 0; i < length; i++)

2370     {

2371       tree operand = va_arg (p, tree);

2372

2373       TREE_OPERAND (t, i) = operand;

2374       if (operand && fro > i)

2375       {

2376         if (TREE_SIDE_EFFECTS (operand))

2377           TREE_SIDE_EFFECTS (t) = 1;

2378         if (!TREE_CONSTANT (operand))

2379           constant = 0;

2380       }

2381     }

2382   }

2383   va_end (p);

1.3.2. Handling of CALL_EXPR

For expressoion other than CALL_EXPR, the object is ready. But CALL_EXPR requests further treatment to detect the real effect of side effect. Because the side effect of CALL_EXPR depends on the function it invokes. And the fresh created CALL_EXPR node should always have TREE_SIDE_EFFECTS return 0. So it needs extra code to step into the invoked function to determine the real side effect.

 

build (continued)

 

2385   TREE_CONSTANT (t) = constant;

2386   

2387   if (code == CALL_EXPR && !TREE_SIDE_EFFECTS (t))

2388   {

2389     /* Calls have side-effects, except those to const or

2390       pure functions.  */

2391     i = call_expr_flags (t);

2392     if (!(i & (ECF_CONST | ECF_PURE)))

2393       TREE_SIDE_EFFECTS (t) = 1;

2394

2395     /* And even those have side-effects if their arguments do.  */

2396     else for (node = TREE_OPERAND (t, 1); node; node = TREE_CHAIN (node))

2397     if (TREE_SIDE_EFFECTS (TREE_VALUE (node)))

2398     {

2399       TREE_SIDE_EFFECTS (t) = 1;

2400       break;

2401     }

2402   }

2403

2404   return t;

2405 }

1.3.2.1.    Determine function called

1.3.2.1.1.            The CALL_EXPR

First see what is CALL_EXPR.

CALL_EXPR[2]

²        The node is used to represent calls to functions, including non-static member functions. The first operand is a pointer to the function to call; it is always an expression whose type is a POINTER_TYPE. The second argument is a TREE_LIST. The arguments to the call appear left-to-right in the list. The TREE_VALUE of each list node contains the expression corresponding to that argument. (The value of TREE_PURPOSE for these nodes is unspecified, and should be ignored.) For non-static member functions, there will be an operand corresponding to the this pointer. There will always be expressions corresponding to all of the arguments, even if the function is declared with default arguments and some arguments are not explicitly provided at the call sites.

 

751  int

752  call_expr_flags (tree t)                                                                               in calls.c

753  {

754    int flags;

755    tree decl = get_callee_fndecl (t);

756 

757    if (decl)

758      flags = flags_from_decl_or_type (decl);

759   else

760    {

761      t = TREE_TYPE (TREE_OPERAND (t, 0));

762      if (t && TREE_CODE (t) == POINTER_TYPE)

763        flags = flags_from_decl_or_type (TREE_TYPE (t));

764      else

765        flags = 0;

766    }

767 

768    return flags;

769  }

 

Above at line 755, get_callee_fndecl is used to determine the address of the function called. As description of CALL_EXPR above reveals, the first operand is a pointer to the function called (that is the address of the function), and it is always an expression having type of POINTER_TYPE. So we need first see what is FUNCTION_DECL, the object pointed by the pointer.

1.3.2.1.2.            Find out function called

What is FUNCTION_DECL

FUNCTION_DECL[2]

²        A function is represented by a FUNCTION_DECL node. A set of overloaded functions is sometimes represented by a OVERLOAD node.

An OVERLOAD node is not a declaration, so none of the DECL_* macros should be used on an OVERLOAD. An OVERLOAD node is similar to a TREE_LIST. Use OVL_CURRENT to get the function associated with an OVERLOAD node; use OVL_NEXT to get the next OVERLOAD node in the list of overloaded functions. The macros OVL_CURRENT and OVL_NEXT are actually polymorphic; you can use them to work with FUNCTION_DECL nodes as well as with overloads. In the case of a FUNCTION_DECL, OVL_CURRENT will always return the function itself, and OVL_NEXT will always be NULL_TREE.

To determine the scope of a function, you can use the DECL_CONTEXT macro. This macro will return the class (either a RECORD_TYPE or a UNION_TYPE) or namespace (a NAMESPACE_DECL) of which the function is a member. For a virtual function, this macro returns the class in which the function was actually defined, not the base class in which the virtual declaration occurred.

If a friend function is defined in a class scope, the DECL_FRIEND_CONTEXT macro can be used to determine the class in which it was defined. For example, in class C { friend void f() {} }; the DECL_CONTEXT for f will be the global_namespace, but the DECL_FRIEND_CONTEXT will be the RECORD_TYPE for C.

In C, the DECL_CONTEXT for a function maybe another function. This representation indicates that the GNU nested function extension is in use. For details on the semantics of nested functions, see the GCC Manual[6]. The nested function can refer to local variables in its containing function. Such references are not explicitly marked in the tree structure; back ends must look at the DECL_CONTEXT for the referenced VAR_DECL. If the DECL_CONTEXT for the referenced VAR_DECL is not the same as the function currently being processed, and neither DECL_EXTERNAL nor DECL_STATIC hold, then the reference is to a local variable in a containing function, and the back end must take appropriate action.

At line 4482 below, STRIP_NOPS strips topmost NON_LVALUE_EXPR and NOP_EXPR nodes that don't change the machine mode (or more precise, skips off these nodes and steps into their operand in turn). For normal function invocation, after STRIP_NOP, it maybe encounters FUNCTION_DECL.

DECL_P at line 4485 if nonzero means addr is declaration. If addr is a declaration but not FUNCTION_DECL, it is assumed as funciton pointer, and if it is read-only and non-volatile; its initial value should contain the address we expect.

Then at line 4492, we expect to reach ADDR_EXPR statement of FUNCTION_DECL. The contained FUNCTION_DECL is what we find.

 

4468 tree

4469 get_callee_fndecl (tree call)                                                                                     in tree.c

4470 {

4471   tree addr;

4472

4473   /* It's invalid to call this function with anything but a

4474     CALL_EXPR.  */

4475   if (TREE_CODE (call) != CALL_EXPR)

4476     abort ();

4477

4478   /* The first operand to the CALL is the address of the function

4479     called.  */

4480   addr = TREE_OPERAND (call, 0);

4481

4482   STRIP_NOPS (addr);

4483

4484   /* If this is a readonly function pointer, extract its initial value.  */

4485   if (DECL_P (addr) && TREE_CODE (addr) != FUNCTION_DECL

4486       && TREE_READONLY (addr) && ! TREE_THIS_VOLATILE (addr)

4487       && DECL_INITIAL (addr))

4488     addr = DECL_INITIAL (addr);

4489

4490   /* If the address is just `&f' for some function `f', then we know

4491     that `f' is being called.  */

4492   if (TREE_CODE (addr) == ADDR_EXPR

4493       && TREE_CODE (TREE_OPERAND (addr, 0)) == FUNCTION_DECL)

4494     return TREE_OPERAND (addr, 0);

4495   

4496   /* We couldn't figure out what was being called. Maybe the front

4497     end has some idea.  */

4498   return (*lang_hooks.lang_get_callee_fndecl) (call);

4499 }

 

If we are still unable to determine the function invoked at line 4492 above, function pointer lang_get_callee_fndecl in lang_hooks will be invoked. For default, function lhd_return_null_tree will be invoked and does nothing. Front-end can define funciton appropriate for its purpose. For C/C++, default function will be used.

1.3.2.1.3.            Detect function attributes
1.3.2.1.3.1.      Fetch data describing function called

Function flags_from_decl_or_type detects function’s attributes. GCC will create a graph to describe the callee and caller during compilation.

 

698  int

699  flags_from_decl_or_type (tree exp)                                                             in calls.c

700  {

701    int flags = 0;

702    tree type = exp;

703 

704    if (DECL_P (exp))

705    {

706      struct cgraph_rtl_info *i = cgraph_rtl_info (exp);

707      type = TREE_TYPE (exp);

1.3.2.1.3.2.      Graph of relation between caller and callee

1.3.2.1.3.2.1.              Structure of graph nodes

Above at line 706, function cgraph_rtl_info fetches the graph node of the function via cgraph_node. If decl is the function currently processing (current_function_decl below if non-null, always points to the function currently under compilation), the corresponding node of cgraph_rtl_info. Otherwise, the function must be assembled already.

 

348  struct cgraph_rtl_info *

349  cgraph_rtl_info (tree decl)                                                                           in cgraph.c

350  {

351    struct cgraph_node *node;

352    if (TREE_CODE (decl) != FUNCTION_DECL)

353      abort ();

354    node = cgraph_node (decl);

355    if (decl != current_function_decl

356        && !TREE_ASM_WRITTEN (node->decl))

357      return NULL;

358    return &node->rtl;

359  }

 

Struct cgraph_node below acts as a node within the graph is used to describe the invocation relationship for the function. Since GCC supports nested function, fieds origin, nested, next_nested are used for the purpose and describes the level and position of the function.

 

85    struct cgraph_node GTY((chain_next ("%h.next"), chain_prev ("%h.previous")))    in cgraph.h

86    {

87      tree decl;

88      struct cgraph_edge *callees;

89      struct cgraph_edge *callers;

90      struct cgraph_node *next;

91      struct cgraph_node *previous;

92      /* For nested functions points to function the node is nested in.  */

93      struct cgraph_node *origin;

94      /* Points to first nested function, if any.  */

95      struct cgraph_node *nested;

96      /* Pointer to the next function with same origin, if any.  */

97      struct cgraph_node *next_nested;

98      /* Pointer to the next function in cgraph_nodes_queue.  */

99      struct cgraph_node *next_needed;

100    PTR GTY ((skip (""))) aux;

101 

102    struct cgraph_local_info local;

103    struct cgraph_global_info global;

104    struct cgraph_rtl_info rtl;

105    /* Unique id of the node.  */

106    int uid;

107    /* Set when function must be output - it is externally visible

108      or it's address is taken.  */

109    bool needed;

110    /* Set when function is reachable by call from other function

111      that is either reachable or needed.  */

112    bool reachable;

113    /* Set once the function has been instantiated and its callee

114      lists created.  */

115    bool analyzed;

116    /* Set when function is scheduled to be assembled.  */

117    bool output;

118  };

 

In above structure, cgraph_edge is used as edge to connect graph nodes. It saves all information of the function being caller and callee.

 

120  struct cgraph_edge GTY(())                                                                         in cgraph.h

121  {

122    struct cgraph_node *caller;

123    struct cgraph_node *callee;

124    struct cgraph_edge *next_caller;

125    struct cgraph_edge *next_callee;

126    /* When NULL, inline this call. When non-NULL, points to the explanation

127      why function was not inlined.  */

128    const char *inline_failed;

129  };

 

The relation among callers and callees can be built into graph by cgraph nodes and cgraph edges as following figure demonstrated. In the figure, N1 invokes N3 first, then N3 calls N4, when N4 returns, N2 calls N3, then N3 invokes N5 in turn. First nodes N1 to N5 are connected via next according to the order of creation (same is previous). In cgrap_node of N3, field caller points to Edge1-3 (the first caller). Field caller in Edge1-3 points to N1, and callee points to N3; and at the same time, next_caller points to Edge2-3 (the second caller). Next, in cgrap_node of N3, field callee points to Edge3-4 (the first callee), and next_callee of Edge3-4 points to Edge3-5 (the second callee); and if N3 continues to invoke other function, then next_callee of Edge3-5 points to corresponding cgraph_edge. And to N5, Edge3-5 is the caller, its next_caller points to cgraph_edge that invokes N5 next

Studying note of GCC-3.4.6 source (8)_第1张图片

Figure 1 example of graph of caller and callee

 

In cgraph_node definition, at line 102, struct cgraph_local_info contains information about the function collected locally. It is available after function is analyzed.

 

28    struct cgraph_local_info GTY(())                                                                 in cgraph.h

29    {

30      /* Size of the function before inlining.  */

31      int self_insns;

32   

33      /* Set when function function is visible in current compilation unit only

34        and it's address is never taken.  */

35      bool local;

36      /* Set once it has been finalized so we consider it to be output.  */

37      bool finalized;

38   

39      /* False when there something makes inlining impossible (such as va_arg).  */

40      bool inlinable;

41      /* True when function should be inlined independently on it's size.  */

42      bool disregard_inline_limits;

43     /* True when the function has been originally extern inline, but it is

44        redefined now.  */

45      bool redefined_extern_inline;

46    };

 

Then at line 103 above, struct cgraph_global_info contains information about the function that needs to be computed globally once compilation is finished. It is available only with switch -funit-at-time.

 

51    struct cgraph_global_info GTY(())                                                                      in cgraph.h

52    {

53      /* Estimated size of the function after inlining.  */

54      int insns;

55   

56      /* Number of times given function will be cloned during output.  */

57      int cloned_times;

58   

59      /* Set when the function will be inlined exactly once.  */

60      bool inline_once;

61   

62     /* Set to true for all reachable functions before inlining is decided.

63        Once we inline all calls to the function and the function is local,

64        it is set to false.  */

65      bool will_be_output;

66   

67      /* Set iff at least one of the caller edges has inline_call flag set.  */

68      bool inlined;

69    };

 

And at line104, structure cgraph_rtl_info contains information about the function that is propagated by the RTL backend. It is available only for functions that have been already assembled.

 

74    struct cgraph_rtl_info GTY(())                                                                    in cgraph.h

75    {

76       bool const_function;

77       bool pure_function;

78       int preferred_incoming_stack_boundary;

79    };

1.3.1.1.1.1.1.              Creation of cgaph_node

Function cgraph_node creates object of cgraph_node. All cgraph_nodes are linked by a double link.

 

95    struct cgraph_node *

96    cgraph_node (tree decl)                                                                                     in cgraph.c

97    {

98      struct cgraph_node *node;

99      struct cgraph_node **slot;

100 

101    if (TREE_CODE (decl) != FUNCTION_DECL)

102      abort ();

103 

104    if (!cgraph_hash)

105      cgraph_hash = htab_create_ggc (10, hash_node, eq_node, NULL);

106 

107    slot = (struct cgraph_node **)

108      htab_find_slot_with_hash (cgraph_hash, DECL_ASSEMBLER_NAME (decl),

109                             IDENTIFIER_HASH_VALUE

110                               (DECL_ASSEMBLER_NAME (decl)), INSERT);

111    if (*slot)

112      return *slot;

113    node = ggc_alloc_cleared (sizeof (*node));

114    node->decl = decl;

115    node->next = cgraph_nodes;

116    node->uid = cgraph_max_uid++;

117    if (cgraph_nodes)

118      cgraph_nodes->previous = node;

119    node->previous = NULL;

120    cgraph_nodes = node;

121    cgraph_n_nodes++;

122    *slot = node;

123    if (DECL_CONTEXT (decl) && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)

124    {

125      node->origin = cgraph_node (DECL_CONTEXT (decl));

126      node->next_nested = node->origin->nested;

127      node->origin->nested = node;

128    }

129    return node;

130  }

 

At the same time all cgraph_nodes are saved into hashtable to ensure one node per function.

1.3.1.1.1.2.      Determine attributes

As mentioned above, data cgraph_rtl_info is available only after the function being assembled (otherwise, function cgraph_rtl_info returns NULL). If it is usable, it is the most correct data (see assemble is ready).

 

flags_from_decl_or_type (continued)

 

698      if (i)

699      {

700        if (i->pure_function)

701          flags |= ECF_PURE | ECF_LIBCALL_BLOCK;

702        if (i->const_function)

703          flags |= ECF_CONST | ECF_LIBCALL_BLOCK;

704      }

705 

706      /* The function exp may have the `malloc' attribute.  */

707      if (DECL_IS_MALLOC (exp))

708        flags |= ECF_MALLOC;

709 

710      /* The function exp may have the `pure' attribute.  */

711      if (DECL_IS_PURE (exp))

712        flags |= ECF_PURE | ECF_LIBCALL_BLOCK;

713 

714      if (TREE_NOTHROW (exp))

715        flags |= ECF_NOTHROW;

716 

717      if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))

718        flags |= ECF_LIBCALL_BLOCK;

719    }

720 

721    if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))

722      flags |= ECF_CONST;

723 

724    if (TREE_THIS_VOLATILE (exp))

725      flags |= ECF_NORETURN;

726 

727    /* Mark if the function returns with the stack pointer depressed. We

728      cannot consider it pure or constant in that case.  */

729    if (TREE_CODE (type) == FUNCTION_TYPE && TYPE_RETURNS_STACK_DEPRESSED (type))

730    {

731      flags |= ECF_SP_DEPRESSED;

732      flags &= ~(ECF_PURE | ECF_CONST | ECF_LIBCALL_BLOCK);

733    }

734 

735    return flags;

736  }

 

Above, following flags are used to indicate the attribution.

ECF_CONST, if nonzero, this is a call to a const function.

ECF_NORETURN, if nonzero, this is a call to a volatile function.

ECF_MALLOC, if nonzero, this is a call to malloc or a related function.

ECF_MAY_BE_ALLOCA, if nonzero, it is plausible that this is a call to alloca.

ECF_NOTHROW, if nonzero, this is a call to a function that won't throw an exception.

ECF_RETURNS_TWICE, if nonzero, this is a call to setjmp or a related function.

ECF_LONGJMP, if nonzero, this is a call to longjmp.

ECF_FORK_OR_EXEC, ECF_SIBCALL, if nonzero, this is a syscall that makes a new process in the image of the current one.

ECF_PURE, if nonzero, this is a call to pure function. In [6], there is a description of pure function as below:

Many functions have no effects except the return value and their return value depends only on the parameters and/or global variables. Such a function can be subject to common subexpression elimination and loop optimization just as an arithmetic operator would be. These functions should be declared with the attribute pure. Some of common examples of pure functions are strlen or memcmp.

ECF_SP_DEPRESSED, if nonzero, this is a call to a function that returns with the stack pointer depressed (make it possible for a called function to return an object whose size is unknown to the caller, essentially for Ada).

ECF_ALWAYS_RETURN, if nonzero, this call is known to always return.

ECF_LIBCALL_BLOCK, creates libcall block around the call (emit_libcall_block).

Then back build, side_effects_flag field of the tree_exp object is set accordingly. Note that if the function is neither pure nor constant, it is considered as having side-effect (i.e, can’t change its time of invocation arbitrarily).

 

 

 

你可能感兴趣的:(Studying note of GCC-3.4.6 source (8))