1.一种基于LLVM的IEC61131‑3工业PLC语言编译器,其特征在于,采用前后端分离的设计,即前端为ieclang,后端为LLVM,包括以下步骤:(1)将IEC61131‑3源码作为输入,通过应用词法分析器flex,将其分解为标记流token;
应用词法分析器,包括以下步骤:
通过定义正则表达式识别输入文本中的各种标记tokens,包括:关键字、标识符、运算符和其他符号;
读取词法规则文件即.ll文件,生成对应的 C 语言代码文件即lex.yy.c;
读取C 语言代码文件,将其分解成标记,并为每个标记执行相应的动作包括:调用特定的函数或返回标记类型;
(2)将标记流token通过语法分析bison,构建抽象语法树AST;应用语法分析器,包括以下步骤:通过定义上下文无关文法Context‑Free Grammar,解析输入的标记流,确保其符合特定的语法规则;
读取语法规则文件.yy 文件,生成对应的 C 语言代码文件;
接收词法分析器提供的标记流,根据定义的语法规则构建解析树或抽象语法树;
(3)填充全局符号表,并与抽象语法树一起传输至下一阶段;填充全局符号表,包括以下步骤:填充功能表:包含所有全局声明函数 POU 的符号表;
填充功能块类型表:包含所有全局声明的函数块 POU 的符号表;
填充程序类型表:包含所有全局声明的程序 POU 的符号表;
填充总类型表:包含所有用户声明的数据类型定义的符号表;
填充枚举值表:包含为枚举类型声明的所有标识符的符号表;
(4)执行语义分析,包括类型检查,生成带有附加语义信息的抽象语法树并传输至下一阶段;包括:在数组范围检查类中实现数组范围检查;
在CASE元素检查类中检查CASE语句中的元素;
在常量折叠类中进行常量折叠:遍历程序的抽象语法树AST,评估常量表达式,并将其结果替换到相应的位置;
在外部检查类中确保外部变量声明与全局变量声明的一致性;通过查找变量的声明和类型,并进行比较;如果发现不一致,会记录错误;
在声明检查类中协调整个声明检查过程;递归处理配置和资源声明,并在需要时使用 外部检查类 进行具体的变量检查;
在填充枚举值类中将枚举值插入符号表;通过检查枚举值是否已存在来避免重复定义,并在发现重复时报告错误;
在枚举声明检查类中协调整个枚举声明检查过程;通过访问库、数据类型、函数、功能块、程序、配置和资源声明,填充和检查相应的符号表;
在填充候补数据类型类中实现分析每个表达式/字段的数据类型的功能并且标注抽象语法树;
在指令控制流分析类中实现了对指令控制流的分析,有效地处理函数、功能块和程序中的控制流,构建出反映实际控制流的指令链接关系图;
在赋值检查类中检查赋值操作是否合法;确保左值lvalue符合特定的条件:不能对控制变量、输出变量、常量、表达式进行赋值;
在寻找向前依赖类中标识给定符号的前向依赖关系;它检查特定标识符是否已声明;
在移除向前依赖类中移除库中的前向依赖关系,创建一个新的没有前向依赖关系的库树;
在组织单元计数类中计算库中的程序组织单元POU的数量;它被实现为单例模式,以确保在整个程序运行期间只存在一个实例;
在缩小候选数据类型类中实现将每个表达式/字段使用的所有数据类型缩减为单一数据类型的功能并标注抽象语法树;
在打印数据类型类中实现抽象语法树中每个有数据类型的对象都将正确填写变量 "datatype"的功能,如果分析的 IEC 61131‑3 源代码包含语义数据类型不兼容错误,则打印错误信息;
(5)解析带有附加语义信息的抽象语法树,生成C语言代码并传输给下一阶段;至此,ieclang前端工作结束;包括:遍历AST节点:遍历AST中的各个节点,根据节点类型生成相应的C语言代码;以下是处理不同类型节点的具体步骤:处理变量声明:为变量声明分配内存空间,并将其添加到符号表中;
处理常量和变量引用:对于常量,直接生成相应的C语言常量值;对于变量引用,从符号表中获取变量的地址,并生成加载指令;
处理算术运算:根据AST节点中的运算符,生成相应的C语言指令;
处理控制流:处理条件语句和循环语句时,生成相应的基本块和跳转指令;
处理函数定义和调用:生成函数定义的入口和返回指令,以及函数调用指令;
采用LLVM编译器作为后端,包括以下步骤:
(6)C语言编译器接收到C语言代码后进行编译,输出相应的可执行文件;包括:预处理:处理预处理指令,生成预处理后的源代码文件;
编译:将预处理后的源代码转换为汇编代码文件;
汇编:将汇编代码转换为目标文件;
链接:将目标文件和库文件链接在一起,生成可执行文件;
(7)LLVM编译器接收到C语言代码后进行编译,输出X86、PowerPC目标代码;包括:将C语言代码转换为LLVM 中间表示;
对LLVM 中间表示进行优化;使用LLVM的opt工具进行各种代码优化:执行Mem2Reg、Constant Folding、Dead Code Elimination、Inlining、Loop Unrolling优化passes,对中间表示进行优化,每个pass优化中间表示后都会将中间表示传递到下个优化pass中;将优化后的LLVM 中间表示转换为目标机器代码;包括指令选择、寄存器分配和代码生成:指令选择:将中间表示中的高级操作映射到目标架构的指令集上;这个过程选择最适合目标体系结构的指令,以便在生成的目标代码中实现所需的操作;
寄存器分配:将虚拟寄存器映射到目标架构的物理寄存器上;包括全局寄存器分配和局部寄存器分配;全局寄存器分配考虑整个函数或模块的寄存器需求,而局部寄存器分配则在基本块或最小的代码单元范围内完成;
指令调度:对指令序列进行重新排序,以最大程度地利用处理器的流水线和其他并行执行资源;指令调度可分为静态调度和动态调度,静态调度在编译期间完成,而动态调度由处理器硬件执行;
生成目标代码:根据经过选择、分配和调度的指令序列,生成目标机器代码或者汇编代码;其中,考虑目标体系结构的特性和限制,以确保生成的代码在目标环境中正确运行。