这是蓝图解析系列文章的第四部分,将介绍Statement优化和字节码生成
相关索引:南京周润发:UE4蓝图解析(一)zhuanlan.zhihu.com南京周润发:UE4蓝图解析(二)zhuanlan.zhihu.com南京周润发:UE4蓝图解析(三)zhuanlan.zhihu.com
对Statements进行优化
蓝图编译器会对Statements进行简单的优化,由FKismetFunctionContext::ResolveStatements()函数实现。
包括三个步骤:执行流程最终排序,处理对goto的补全,优化无用的jump
执行流程最终排序
此时,LinearExecutionList还只是根据数据引脚依赖进行深度优先拓扑排序生成的节点序列,与真正的节点执行序列还有一些差异,因此需要重新排序。
首先,需要把LinearExecutionList中未生成Statements的节点都去掉,比如K2Node_VariableGet节点和Comment节点,这些去掉的节点会以Terminal的形式参与代码生成。
排序好的节点会存入SortedLinearExecutionList数组中,刚开始时,数组中只有Function Entry一个节点,这比较好理解,因为Function Entry是函数的入口,必定是第一个节点。之后,程序会不断检查数组中的最后一个元素,如果该节点的最后一个Statement为KCST_UnconditionalGoto,那毫无疑问要将goto目标节点作为执行序列的下一个节点加入数组中。但当最后一个元素有多个output执行引脚,或者执行引脚连接了多个节点时,就会造成执行序列的分叉,参考ifthenelse节点。此时,需要把这些后继节点先加入NodesToStartNextChain容器中,然后采用“近似”深度优先的方式处理这些分叉的流程。
为什么是“近似”呢?因为这个执行序列依然只能保证一部分的顺序性,比如UnconditionalGoto指向的节点必定会紧挨着前一个节点,但多层分支直接的节点会形成穿插,所以这里所说的“最终排序”并不会真正意义上生成与逻辑顺序一致的顺序。
goto补全
首先,为什么需要对gotostatement进行补全呢?因为Statement::TargetLabel属性类型也为Statement,当对当前节点生成GotoStatement时,目标节点通常还没进行Statement生成,因此无法立即设置TargetLabel,只能先记下gotostatement和目标引脚的对应关系。因此,GotoStatement需要在此补全TargetLabel为目标节点的第一个Statement,并且设置目标Statement为JumpTarget。另外,如果goto的目标节点为NULL,则说明走到了一个执行流程的末尾,需要把Statement的类型设置为EndOfThread或者EndOfReturn。
优化无用的jump