Next: , Up: Loop Analysis and Representation


14.1 循环表示

这章描述了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函数来分析循环。该函数的参数是一个标记集,使用整数位掩码表示。这些标记指定了循环结构体的其它哪些属性将在之后被计算/赋予,并且保留:

这些属性也可以在之后使用函数create_preheaders, force_single_succ_latches,mark_irreducible_loopsrecord_loop_exits来求得/赋予。

循环结构体占用的内存应该在loop_optimizer_finalize函数中被释放。

CFG操作函数通常不更新循环结构体。在GIMPLE上,如果设置了current_loops,则cleanup_tree_cfg_loop可以被用来在清除CFG的同时,更新循环结构体。