Next: Loop querying, Up: Loop Analysis and Representation
这章描述了GCC中循环的表示,以及可以用来构建,修改和分析这些表示的函数。大多数接口和数据结构都在cfgloop.h中声明。目前,只是由处理循环的优化过程来分析这些循环结构和更新这些信息,不过正在做一些努力,使得其在大多数优化过程中都可用。
通常,一个自然的循环会具有一个入口块(header),以及可能多个的从循环内部通向header的回边(latch)。如果多个循环共享单个 header,或者在循环中间有个分支跳转,则可能会出现带有多个latch的循环。然而GCC中对循环的表示只允许具有单个latch。在循环分析过程中,为了消除循环结构的歧义,这样的循环的header会被拆分,并创建前向的块。基于profile信息的heuristic,以及循环中的归纳变量的结构被用来判定latches是否与子循环相关,还是与单个循环中的控制流相关。这意味着分析有时候会改变CFG,并且如果你在一个优化过程的中间运行了该分析,则必须能够处理新的块。可以通过传递LOOPS_MAY_HAVE_MULTIPLE_LATCHES
标记来避免CFG改变,但是要注意,对于具有多个latch边的循环,大多其它的循环操作函数将无法正确工作(只有查询块成员与循环和子循环关系的,或者枚举和测试循环出口的函数能够工作)。
循环体是由header支配的一组基本块,并且可以通过回边沿着CFG中边的方向达到。循环使用树的层次结构来组织,直接包含在循环L中的所有循环在树中都为L的子节点。该树由struct loops
结构体表示。该树的根是一个假循环,包含了函数中的所有块。每个循环都由struct loop
结构体表示。每个循环都被赋予一个索引(struct loop
结构体的num
域),并且指向循环的指针被存在struct loops
结构体中的larray
向量的对应域里。索引不必是连续的,larray
中可能会有空项(NULL
),是由删除循环产生的。而且不保证索引的数字与循环和子循环有关系。循环的索引不会改变。
不要直接访问larray
域中的项。函数get_loop
返回给定索引的循环描述。number_of_loops
函数返回函数中的循环数目。要遍历所有的循环,使用FOR_EACH_LOOP
宏。宏的标记参数用来决定遍历的方向和要访问的循环集。不管循环树是否变化,以及在遍历过程中循环是否被移除,每个循环都保证只被访问一次。新创建的循环将不会被访问到,如果需要访问,这必须在它们创建之后单独进行。FOR_EACH_LOOP
宏会分配临时变量,如果使用break或者goto终止了FOR_EACH_LOOP
,它们将不会被释放;因此必须使用FOR_EACH_LOOP_BREAK
宏。
每个基本块包含了对其所属的最内层循环的引用(loop_father
)。基于这个原因,对每个CFG只可能有一个struct loops
结构体在同一时间被初始化。全局变量current_loops
包含了struct loops
结构体。许多循环操作函数都假设dominance信息是最新的。
通过loop_optimizer_init
函数来分析循环。该函数的参数是一个标记集,使用整数位掩码表示。这些标记指定了循环结构体的其它哪些属性将在之后被计算/赋予,并且保留:
LOOPS_MAY_HAVE_MULTIPLE_LATCHES
: 如果设置了该标记,循环分析将不会改变CFG,特别的,具有多个回边的循环将不会被消除歧义。如果循环具有多个回边,它的回边块被设为NULL。对于这种形式,大多循环操作函数将无法工作。
LOOPS_HAVE_PREHEADERS
: 创建前驱块的方法为,每个循环只有一个入口边,另外,这个入口边的源块只有一个后继。这就创建了一个自然的位置,使得代码能够被移出循环,并且保证循环的入口边由它的直接外循环进来。
LOOPS_HAVE_SIMPLE_LATCHES
: 创建前驱块,从而使得每个循环的回边块只有一个后继。这就保证了循环的回边不属于任何子循环,并且使得对循环的操作变得非常容易。许多循环操作函数都假设循环是处于这种形式的。注意使用该标记时,其中没有任何控制流,且只有一个出口的“正常”循环,将包括两个基本块。
LOOPS_HAVE_MARKED_IRREDUCIBLE_REGIONS
: 强连通组件中的基本块和边,如果不是自然循环(具有多个入口块),将被BB_IRREDUCIBLE_LOOP
和EDGE_IRREDUCIBLE_LOOP
标记。在这样的不可消减区域中的块和边,如果属于自然循环的,则不被标记(但是会为进入和离开该区域的入口边和出口边做标记)。
LOOPS_HAVE_RECORDED_EXITS
: 为每个循环记录并更新出口列表。这使得一些函数(如get_loop_exit_edges
)更加有效。一些函数(如single_exit
)只有在出口列表被记录的情况下才能用。
这些属性也可以在之后使用函数create_preheaders
, force_single_succ_latches
,mark_irreducible_loops
和record_loop_exits
来求得/赋予。
循环结构体占用的内存应该在loop_optimizer_finalize
函数中被释放。
CFG操作函数通常不更新循环结构体。在GIMPLE上,如果设置了current_loops
,则cleanup_tree_cfg_loop
可以被用来在清除CFG的同时,更新循环结构体。