Studying note of GCC-3.4.6 source (101)

本文介绍了一种在编译器中缓存内联函数定义的方法,通过使用特定的数据结构来保存函数体,以便后续处理。重点讲解了如何为每个内联函数创建缓存,并在适当的时候将这些缓存插入到类的成员函数列表中。

5.12.3.2.1.1.3.4.3.            Cache body for inline function

Routine start_method creates nodes for the declarator of the method-declaration; after that if the method has default argument, this (these) argument(s) should be remembered by parser into field unparsed_functions_queues . If following token is “{”, the rule of function-definition applied, which defines the inline function.

 

cp_parser_save_member_function_body (continue)

 

14656   /* Remember it, if there default args to post process.  */

14657   cp_parser_save_default_args (parser, fn);

14658

14659   /* Create a token cache.  */

14660   cache = cp_token_cache_new ();

14661   /* Save away the tokens that make up the body of the

14662     function.  */

14663   cp_parser_cache_group (parser, cache, CPP_CLOSE_BRACE, /*depth=*/ 0);

14664   /* Handle function try blocks.  */

14665   while (cp_lexer_next_token_is_keyword (parser->lexer, RID_CATCH))

14666     cp_parser_cache_group (parser, cache, CPP_CLOSE_BRACE, /*depth=*/ 0);

14667

14668   /* Save away the inline definition; we will process it when the

14669     class is complete.  */

14670   DECL_PENDING_INLINE_INFO (fn) = cache;

14671   DECL_PENDING_INLINE_P (fn) = 1;

14672

14673   /* We need to know that this was defined in the class, so that

14674     friend templates are handled correctly.  */

14675   DECL_INITIALIZED_IN_CLASS_P (fn) = 1;

14676

14677   /* We're done with the inline definition.  */

14678   finish_method (fn);

14679

14680   /* Add FN to the queue of functions to be parsed later.  */

14681   TREE_VALUE (parser->unparsed_functions_queues)

14682     = tree_cons (NULL_TREE, fn,

14683                TREE_VALUE (parser->unparsed_functions_queues));

14684

14685   return fn;

14686 }

 

Inline function should be expanded at point of invocation, so its definition must be remembered by the parser. The parser uses blocks of type cp_token_block to cache the function definition. Every block is of size 512 bytes.

 

100    typedef struct cp_token_block GTY (())                                                     in parser.c

101    {

102      /* The tokens.  */

103      cp_token tokens[CP_TOKEN_BLOCK_NUM_TOKENS];

104      /* The number of tokens in this block.  */

105      size_t num_tokens;

106      /* The next token block in the chain.  */

107      struct cp_token_block *next;

108      /* The previous block in the chain.  */

109      struct cp_token_block *prev;

110     } cp_token_block;

 

112     typedef struct cp_token_cache GTY (())   

113     {

114       /* The first block in the cache. NULL if there are no tokens in the

115         cache.  */

116       cp_token_block *first;

117       /* The last block in the cache. NULL If there are no tokens in the

118         cache.  */

119       cp_token_block *last;

120    } cp_token_cache;

 

For every function, a cp_token_cache is associated which can chain variable number of blocks to hold the whole definition.

 

131    static cp_token_cache *

132    cp_token_cache_new (void)                                                                      in parser.c

133    {

134      return ggc_alloc_cleared (sizeof (cp_token_cache));

135    }

 

Routine cp_parser_cache_group is a recursive function, as a function can contain arbitrary number of pair of parenthesis or brace, recursive function can handle the case in much simpler form. Note that end will be either CPP_CLOSE_PAREN or CPP_CLOSE_BRACE, and first token consumed by the routine must be “{”.

 

15349 static void

15350 cp_parser_cache_group (cp_parser *parser,                                                in parser.c

15351                     cp_token_cache *cache,

15352                     enum cpp_ttype end,

15353                     unsigned depth)

15354 {

15355   while (true)

15356   {

15357     cp_token *token;

15358

15359     /* Abort a parenthesized expression if we encounter a brace.  */

15360     if ((end == CPP_CLOSE_PAREN || depth == 0)

15361         && cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))

15362       return ;

15363     /* If we've reached the end of the file, stop.  */

15364     if (cp_lexer_next_token_is (parser->lexer, CPP_EOF))

15365       return ;

15366     /* Consume the next token.  */

15367     token = cp_lexer_consume_token (parser->lexer);

15368      /* Add this token to the tokens we are saving.  */

15369     cp_token_cache_push_token (cache, token);

15370      /* See if it starts a new group.  */

15371     if (token->type == CPP_OPEN_BRACE)

15372     {

15373       cp_parser_cache_group (parser, cache, CPP_CLOSE_BRACE, depth + 1);

15374       if (depth == 0)

15375         return ;

15376     }

15377     else if (token->type == CPP_OPEN_PAREN)

15378       cp_parser_cache_group (parser, cache, CPP_CLOSE_PAREN, depth + 1);

15379     else if (token->type == end)

15380       return ;

15381   }

15382 }

 

Normally, matching close parenthesis or close brace satisfies condition at line 15379, and makes the function exit gracefully. However, condition at line 15360 is not the one described by the comment, semicolon within parenthesis pair instead of brace will hold. For example, inline function:

void m1() {

   for (int j = 0 ; j < 10; j++)

{

  …

}

}

The cache triggered by the open parenthesis contains only tokens in red, rest tokens ahead of the close parenthesis are saved in the cache started by the open brace at beginning of the functionbody.

 

139    static void

140    cp_token_cache_push_token (cp_token_cache *cache,                                 in parser.c

141                             cp_token *token)

142    {

143      cp_token_block *b = cache->last;

144   

145      /* See if we need to allocate a new token block.  */

146      if (!b || b->num_tokens == CP_TOKEN_BLOCK_NUM_TOKENS)

147      {

148        b = ggc_alloc_cleared (sizeof (cp_token_block));

149        b->prev = cache->last;

150        if (cache->last)

151        {

152          cache->last->next = b;

153          cache->last = b;

154        }

155        else

156          cache->first = cache->last = b;

157      }

158      /* Add this token to the current token block.  */

159      b->tokens[b->num_tokens++] = *token;

160    }

5.12.3.2.1.1.3.4.4.            Exit from the binding scope of the method

At time seeing the declarator of the method, the front-end pushes the binding scope of the method and makes it current ones, now we step over the closing brace for the function definition, it should pop the effective binding scope up.

 

11111  tree

11112  finish_method (tree decl)                                                                                 in decl.c

11113  {

11114    tree fndecl = decl;

11115    tree old_initial;

11116 

11117    tree link;

11118 

11119    if (decl == void_type_node)

11120     return decl;

11121

11122   old_initial = DECL_INITIAL (fndecl);

11123

11124   /* Undo the level for the parms (from start_method).

11125     This is like poplevel, but it causes nothing to be

11126     saved. Saving information here confuses symbol-table

11127     output routines. Besides, this information will

11128     be correctly output when this method is actually

11129     compiled.  */

11130

11131   /* Clear out the meanings of the local variables of this level;

11132     also record in each decl which block it belongs to.  */

11133

11134   for (link = current_binding_level ->names; link; link = TREE_CHAIN (link))

11135   {

11136     if (DECL_NAME (link) != NULL_TREE)

11137       pop_binding (DECL_NAME (link), link);

11138     my_friendly_assert (TREE_CODE (link) != FUNCTION_DECL, 163);

11139     DECL_CONTEXT (link) = NULL_TREE;

11140   }

11141

11142   poplevel (0, 0, 0);

11143

11144   DECL_INITIAL (fndecl) = old_initial;

11145

11146   /* We used to check if the context of FNDECL was different from

11147     current_class_type as another way to get inside here. This didn't work

11148     for String.cc in libg++.  */

11149   if (DECL_FRIEND_P (fndecl))

11150   {

11151     CLASSTYPE_INLINE_FRIENDS (current_class_type )

11152        = tree_cons (NULL_TREE, fndecl, CLASSTYPE_INLINE_FRIENDS (current_class_type ));

11153     decl = void_type_node;

11154   }

11155

11156   return decl;

11157 }

 

Our constructor only has an empty body, as result, finish_method and its callee only request leave_scope for the operation. Then seeing that the node of the scope is disconnected from the scopes chain, and linked into free_binding_level now.

(Click here for open )

Figure 72 : FUNCTION_DECL for ctor of “Lock” – leaving the scope

5.12.3.2.1.1.3.4.5.            Insert constructor into the class

Go back cp_parser_member_declaration , next finish_member_declaration finishes the method declaraction. Below, as the constructor declared within struct, in pushclass at line 5482, current_access_specifier is set as access_public_node .

 

2089   void

2090   finish_member_declaration (tree decl)                                                in semantics.c

2091   {

2092     if (decl == error_mark_node || decl == NULL_TREE)

2093       return ;

2094  

2095     if (decl == void_type_node)

2096       /* The COMPONENT was a friend, not a member, and so there's

2097         nothing for us to do.  */

2098       return ;

2099  

2100     /* We should see only one DECL at a time.  */

2101     my_friendly_assert (TREE_CHAIN (decl) == NULL_TREE, 0);

2102  

2103     /* Set up access control for DECL.  */

2104     TREE_PRIVATE (decl)

2105         = (current_access_specifier == access_private_node);

2106     TREE_PROTECTED (decl)

2107         = (current_access_specifier == access_protected_node);

2108     if (TREE_CODE (decl) == TEMPLATE_DECL)

2109     {

2110       TREE_PRIVATE (DECL_TEMPLATE_RESULT (decl)) = TREE_PRIVATE (decl);

2111       TREE_PROTECTED (DECL_TEMPLATE_RESULT (decl)) = TREE_PROTECTED (decl);

2112     }

2113  

2114     /* Mark the DECL as a member of the current class.  */

2115     DECL_CONTEXT (decl) = current_class_type ;

2116  

2117     /* [dcl.link]

2118  

2119       A C language linkage is ignored for the names of class members

2120       and the member function type of class member functions.  */

2121     if (DECL_LANG_SPECIFIC (decl) && DECL_LANGUAGE (decl) == lang_c)

2122       SET_DECL_LANGUAGE (decl, lang_cplusplus);

2123  

2124     /* Put functions on the TYPE_METHODS list and everything else on the

2125       TYPE_FIELDS list. Note that these are built up in reverse order.

2126       We reverse them (to obtain declaration order) in finish_struct.  */

2127     if (TREE_CODE (decl) == FUNCTION_DECL

2128         || DECL_FUNCTION_TEMPLATE_P (decl))

2129     {

2130       /* We also need to add this function to the

2131          CLASSTYPE_METHOD_VEC.  */

2132       add_method (current_class_type , decl, /*error_p=*/ 0);

2133  

2134       TREE_CHAIN (decl) = TYPE_METHODS (current_class_type );

2135       TYPE_METHODS (current_class_type ) = decl;

2136  

2137       maybe_add_class_template_decl_list (current_class_type , decl,

2138                                      /*friend_p=*/ 0);

2139     }

2140     /* Enter the DECL into the scope of the class.  */

2141     else if ((TREE_CODE (decl) == USING_DECL && TREE_TYPE (decl))

2142            || pushdecl_class_level (decl))

2143     {

          …

2172     }

2173   }

 

Now it can insert the method into the node of the containing class. In front-end, class keeps a vector to store all methods declared within. For constructor and destructor, they are commonly used methods, so they are placed at fix position (0 for constructor, 1 for destructor).

 

721    void

722    add_method (tree type, tree method, int error_p)                                        in class.c

723    {

724      int using;

725      int len;

726      int slot;

727      tree method_vec;

728      int template_conv_p;

729   

730      if (method == error_mark_node)

731        return ;

732     

733      using = (DECL_CONTEXT (method) != type);

734      template_conv_p = (TREE_CODE (method) == TEMPLATE_DECL

735                         && DECL_TEMPLATE_CONV_FN_P (method));

736   

737      if (!CLASSTYPE_METHOD_VEC (type))

738        /* Make a new method vector. We start with 8 entries. We must

739          allocate at least two (for constructors and destructors), and

740          we're going to end up with an assignment operator at some point

741          as well.

742          

743          We could use a TREE_LIST for now, and convert it to a TREE_VEC

744          in finish_struct, but we would probably waste more memory

745          making the links in the list than we would by over-allocating

746          the size of the vector here. Furthermore, we would complicate

747          all the code that expects this to be a vector.  */

748        CLASSTYPE_METHOD_VEC (type) = make_tree_vec (8);

749   

750      method_vec = CLASSTYPE_METHOD_VEC (type);

751      len = TREE_VEC_LENGTH (method_vec);

752   

753      /* Constructors and destructors go in special slots.  */

754      if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (method))

755        slot = CLASSTYPE_CONSTRUCTOR_SLOT;

756      else if (DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (method))

757      {

758        slot = CLASSTYPE_DESTRUCTOR_SLOT;

759        TYPE_HAS_DESTRUCTOR (type) = 1;

760         

761        if (TYPE_FOR_JAVA (type))

762          error (DECL_ARTIFICIAL (method)

763               ? "Java class '%T' cannot have an implicit non-trivial destructor"

764               : "Java class '%T' cannot have a destructor",

765                 DECL_CONTEXT (method));

766      }

767      else

768      {

          …

866      }

867         

868      if (processing_template_decl )

869        /* TYPE is a template class. Don't issue any errors now; wait

870          until instantiation time to complain.  */

871         ;

872      else

873      {

          …

949      }

950   

951      /* Actually insert the new method.  */

952      TREE_VEC_ELT (method_vec, slot)

953         = build_overload (method, TREE_VEC_ELT (method_vec, slot));

954   

955      /* Add the new binding.  */

956      if (!DECL_CONSTRUCTOR_P (method)

957         && !DECL_DESTRUCTOR_P (method))

958        push_class_level_binding (DECL_NAME (method),

959                              TREE_VEC_ELT (method_vec, slot));

960    }

 

Further, for class template declaration, maybe_add_class_template_decl_list also records the method in decl_list of the containing template class.

(Click here for open )

Figure 73 : Insert default constructor into class

 

内容概要:本文系统介绍了算术优化算法(AOA)的基本原理、核心思想及Python实现方法,并通过图像分割的实际案例展示了其应用价值。AOA是一种基于种群的元启发式算法,其核心思想来源于四则运算,利用乘除运算进行全局勘探,加减运算进行局部开发,通过数学优化器加速函数(MOA)和数学优化概率(MOP)动态控制搜索过程,在全局探索与局部开发之间实现平衡。文章详细解析了算法的初始化、勘探与开发阶段的更新策略,并提供了完整的Python代码实现,结合Rastrigin函数进行测试验证。进一步地,以Flask框架搭建前后端分离系统,将AOA应用于图像分割任务,展示了其在实际工程中的可行性与高效性。最后,通过收敛速度、寻优精度等指标评估算法性能,并提出自适应参数调整、模型优化和并行计算等改进策略。; 适合人群:具备一定Python编程基础和优化算法基础知识的高校学生、科研人员及工程技术人员,尤其适合从事人工智能、图像处理、智能优化等领域的从业者;; 使用场景及目标:①理解元启发式算法的设计思想与实现机制;②掌握AOA在函数优化、图像分割等实际问题中的建模与求解方法;③学习如何将优化算法集成到Web系统中实现工程化应用;④为算法性能评估与改进提供实践参考; 阅读建议:建议读者结合代码逐行调试,深入理解算法流程中MOA与MOP的作用机制,尝试在不同测试函数上运行算法以观察性能差异,并可进一步扩展图像分割模块,引入更复杂的预处理或后处理技术以提升分割效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值