ROSE 编译器框架/程序翻译
ROSE 拥有高级中间表示,适合构建源到源的翻译器。这可以通过重新构建输入源代码的 AST,然后将转换后的 AST 反解析到输出源代码来实现。
官方教程: 第 32 章 ROSE 教程 的 AST 构造
许多初学者的疑问在阅读完本章后应该可以得到解答。
列表
- 代码生成: 代码片段(基于 SageBuilder 函数)、CUDA、PolyOpt、MPI、矢量化等。
- 循环转换: 交换、平铺、展开、折叠等。
- 内联: 请参见 ROSE 编译器框架/内联器
- 外联: 请参见 ROSE 编译器框架/外联器
- AutoPar,
- 自动调整(代码变体)等。
使用 ROSE 构建的翻译器被设计为像编译器一样(gcc、g++、gfortran 等,具体取决于输入文件类型)。
因此,翻译器的用户只需要更改输入文件的构建系统,就可以使用翻译器代替原始编译器。
主要文章位于 ROSE 编译器框架/处理编译指示
使用编译指示来指导翻译器通常很有用。
提供了一组解析器构建函数来帮助创建递归下降解析器
一旦您包含了头文件 AstFromString.h(位于 src/frontend/SageIII/astFromString 中),您就可以访问命名空间中定义的变量和函数。
有一个示例项目正在进行编译指示解析,并将结果保存到 AST 属性中。 https://github.com/rose-compiler/rose-develop/tree/master/projects/pragmaParsing
重构/构造 AST 的官方指南强烈建议使用 SageBuilder 和 SageInterface 命名空间中的辅助函数来创建 AST 部分并将其移动。这些辅助函数尝试在低级别更改中保持稳定,并足够智能,可以透明地设置许多边缘并维护符号表。
希望拥有更低级别控制的用户可能希望直接调用 AST 节点和符号表的成员函数来显式地操作 AST 中的边缘和符号。但这个过程非常繁琐且容易出错。
可能某些构建器函数尚未提供,尤其是对于 C++ 结构,例如模板声明等。我们正在积极地解决这个问题。在此期间,您可以直接使用 new 运算符和其他成员函数作为变通方法。
准备翻译器的输出
- 准备一个最简单的源文件 (b.c) 作为翻译器的示例输出
- 避免包含任何系统头文件
- 使用 ROSE_INSTALLATION_TREE/bin/dotGeneratorWholeASTGraph 为 b.c 生成完整的 AST,有关可视化 AST 的更多详细信息,请参见 如何可视化 AST。
- 研究 AST 节点类型及其父子关系的点图。
- 使用 SageInterface 或 SageBuilder 函数来重构源 AST 图,使其成为您想要生成的 AST 图
- 如果没有 SageBuilder 函数可以创建您想要的内容。您可能必须使用 new 运算符创建节点,并自行处理边缘和符号。
更多详细信息,请参见 如何创建一个翻译器
简单的先序遍历不适合构建翻译器,因为翻译器可能会更改遍历预期稍后访问的节点。从概念上讲,这本质上与 C++ 迭代器失效类似。
为了安全地转换 AST,建议使用由先序遍历生成的语句列表的反向迭代器。这与由后序遍历生成的列表不同。
例如,假设我们有一个子树:parent <child 1, child 2>,
- 先序遍历将生成一个列表:parent, child 1, child2
- 后序遍历将生成一个列表:child 1, child 2, parent。
- 先序遍历的反向迭代器将为您提供:child2, child 1 和 parent。根据我们的经验,使用此顺序进行转换是最安全的。
在 https://github.com/rose-compiler/rose/tree/master/tests/roseTests/astInterfaceTests 下有很多测试翻译器
其他示例
- 将一个复杂语句拆分成多个简单的语句:ROSE/projects/backstroke/ExtractFunctionArguments.C
请参见 转换跟踪
用于精确定位源代码结构的字符串。对于将循环、函数等传递给翻译器以进行处理很有用,更多内容请参见
断言失败: (expr->get_startOfConstruct() != NULL), 函数 unparseExpression,文件 ../../../ROSE/src/backend/unparser/languageIndependenceSupport/unparseLanguageIndependentConstructs.C,第 812 行。
void visitorTraversal::visit(SgNode* sgn){ SageBuilder::pushScopeStack(body); SgAssignOp* sao = isSgAssignOp(sgn); if(!sao) return; SgVarRefExp* svr = SageBuilder::buildVarRefExp("mami"); SgIntVal* siv = SageBuilder::buildIntVal(33); SgAssignOp* newsao = new SgAssignOp(svr, siv, NULL); SageInterface::replaceWithPattern(sao, newsao); SageBuilder::popScopeStack(); }
原因是: SgAssignOp* newsao = new SgAssignOp(svr, siv, NULL);
expr->get_startOfConstruct() != NULL 表明没有起始文件位置。SageBuilder 中有一个现有的函数可以构建 Assign Op 并处理许多细节,包括文件信息对象。否则,如果使用原始 new 运算符,则需要自己维护这些细节。