使用 Sethi-Ullman 算法的表达式的代码生成器

2024-04-27

Give a AST tree http://en.wikipedia.org/wiki/Abstract_syntax_tree,我想生成一种类似汇编的语言。我正在尝试使用塞西-乌尔曼 http://en.wikipedia.org/wiki/Sethi%E2%80%93Ullman_algorithm算法,但我对算法实现有一些疑问。

当我的寄存器用完时我该怎么办?

目前我执行以下操作:

Emit a push REG where REG是右子树的寄存器,评估左子树,得到一个空闲寄存器分配作为右子树的寄存器,然后发出一个POP REG操作地点REG也是右子树的寄存器。

我该如何实现免费注册的功能呢?目前我正在使用这样的实现而不是基于堆栈的:

enum Reg { Reg_r0, Reg_r1 };
Reg regs[] = { Reg_r0, Reg_r1 };
    Reg getreg() {
             static int c;
        if(c == sizeof(regs) / sizeof(int))
        c = 0;
        return regs[c++];
    }

这是一个伪代码(来自 C 语言)如何根据我未设置的内容(包括label()功能)

// K is the number of registers available
int K = 2;
void gen(AST ast) {
    if(ast.left != null && ast.right != null) {
        int l = ast.left.n;
        int r = ast.right.n;
        
        if(l >= K && r >= K) {
            gen(ast.right);
            ast.n -= 1;
            emit_operation(PUSH, ast.right.reg);
            gen(ast.left);
            ast.reg = getreg();
            emit_operation(POP, ast.right.reg);
        } else if(l >= r) {
            gen(ast.left);
            gen(ast.right);
            ast.n -= 1;
        } else if(l < r) {
            gen(ast.right);
            gen(ast.left);
            ast.n -= 1;
        }
        
        ast.reg = getreg();
        Reg r1 = ast.left.reg;
        Reg r2 = ast.right.reg;
        emit_operation(ast.type, r1, r2);
    } else if(ast.type == Type_id || ast.type == Type_number) {
        ast.n += 1;
        ast.reg = getreg();
        emit_load(ast);
    } else {
        print("gen() error");
        // error
    }
}

// ershov numbers
void label(AST ast) {
    if(ast == null)
        return;
    
    label(ast.left);
    label(ast.right);
    
    if(ast.type == Type_id || ast.type == Type_number)
        ast.n = 1;
    // ast has two childrens
    else if(ast.left not null && ast.right not null) {      
        int l = ast.left.n;
        int r = ast.right.n;
        
        if(l == r)
            ast.n = 1 + l;
        else
            ast.n = max(1, l, r);
    }
    // ast has one child
    else if(ast.left not null && ast.right is null)
        ast.n = ast.left.n;
    else
        print("label() error!");
}

EDIT:如果需要更多背景信息来理解这一点,请告诉我。


Sethi-Ullman 实际上只是一个调度算法,而不是一个寄存器分配算法,所以它只是告诉你order在其中执行操作以最小化所需寄存器的数量;它没有告诉你which寄存器使用在哪里。

因此,您需要将其与寄存器分配策略结合起来——通常只是一个贪婪的分配器。接下来的问题是,如果寄存器用完该怎么办——是内联插入溢出,还是中止并执行其他操作?

为了与您的调度和指令生成内联进行简单的贪婪分配(您似乎正在使用简单的gen递归过程),您需要跟踪在任何给定时间正在使用哪些寄存器。最简单的方法是添加一个额外的in_usegen 函数的参数:

typedef unsigned RegSet; /* using a simple bitmask for a set -- assuming that
                          * unsigned is big enough to have a bit per register */

void gen(AST *ast, RegSet in_use) {
    if(ast->left != 0 && ast->right != 0) {
        if (ast->left->n >= ast->right->n) {
            gen(ast->left, in_use);
            gen(ast->right, in_use | (1 << ast->left->reg));
        } else {
            gen(ast->right, in_use);
            gen(ast->left, in_use | (1 << ast->right->reg)); }
        ast->reg = ast->left->reg
        emit_operation(ast->type, ast->left->reg, ast->right->reg);
    } else if(ast->type == Type_id || ast->type == Type_number) {
        ast->reg = pick_unused_register(in_use);
        emit_load(ast);
    } else ....

请注意,您需要单独的递归过程来计算n对于每个节点(Sethi-Ullman 本质上需要两次遍历,第一次遍历计算自下而上n第二次遍历使用自上而下的值)。

现在上面的代码根本不处理寄存器耗尽的问题。为此,您需要插入一些溢出物。一种方法是在进行递归调用之前检测所有寄存器是否都在使用中,然后溢出,然后恢复:

void gen(AST *ast, RegSet in_use) {
    if(ast->left != 0 && ast->right != 0) {
        Reg spill = NoRegister; /* no spill yet */
        AST *do1st, *do2nd;     /* what order to generate children */
        if (ast->left->n >= ast->right->n) {
            do1st = ast->left;
            do2nd = ast->right;
        } else {
            do1st = ast->right;
            do2nd = ast->left; }
        gen(do1st, in_use);
        in_use |= 1 << do1st->reg;
        if (all_used(in_use)) {
            spill = pick_register_other_than(do1st->reg);
            in_use &= ~(1 << spill);
            emit_operation(PUSH, spill); }
        gen(do2nd, in_use);
        ast->reg = ast->left->reg
        emit_operation(ast->type, ast->left->reg, ast->right->reg);
        if (spill != NoRegister)
            emit_operation(POP, spill);
    } else ...

当然,事实证明这并不是非常有效——通常最好早点溢出并稍后再填充,但只有当你知道你的寄存器即将用完时。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Sethi-Ullman 算法的表达式的代码生成器 的相关文章

  • SharpZipLib - 将文件夹/目录添加到 zip 存档

    通过示例 我很好地掌握了如何提取 zip 文件 几乎在每个示例中 识别 ZipEntry 是否为目录的方法如下 string directoryName Path GetDirectoryName theEntry Name string
  • 解析嵌套括号内包含的值

    我只是在开玩笑 奇怪地发现在简单的递归函数中解析嵌套括号有点棘手 例如 如果程序的目的是查找用户详细信息 它可能来自 name surname age to Bob Builder age 然后到Bob Builder 20 这是一个用于在
  • 哪种数据聚类算法适合检测时间序列事件中未知数量的聚类?

    这是我的场景 考虑在不同地点和时间发生的一组事件 例如 考虑有人在高空记录暴风雨期间城市中的雷击 就我的目的而言 闪电是瞬时的 只能击中某些位置 例如高层建筑 还可以想象每次雷击都有一个唯一的 ID 以便以后可以参考该雷击 这个城市大约有1
  • 如何从RichTextBox中获取显示的文本?

    如何获得显示的RichTextBox 中的文本 我的意思是 如果 RichTextBox 滚动到末尾 我只想接收那些对我来说可见的行 P S 获得第一个显示的字符串就足够了 您想使用 RichTextBox GetCharIndexFrom
  • 如何使用 MVVM 更新 WPF 中编辑的数据? [复制]

    这个问题在这里已经有答案了 我正在为聊天应用程序构建 UI 设计 在尝试更新所选联系人的消息时遇到问题 选择现有联系人 选择编辑选项 然后编辑其属性 例如用户名和图像 后 唯一进行的更改是联系人的用户名和图像 我仍然想更改 MessageM
  • C# 中四舍五入到偶数

    我没有看到 Math Round 的预期结果 return Math Round 99 96535789 2 MidpointRounding ToEven returning 99 97 据我了解 MidpointRounding ToE
  • Qt 计算和比较密码哈希

    目前正在 Qt 中为测验程序构建面向 Web 的身份验证服务 据我了解 在数据库中存储用户密码时 必须对其进行隐藏 以防落入坏人之手 流行的方法似乎是添加的过程Salt https en wikipedia org wiki Salt cr
  • 矩阵向量变换

    我正在编写一个代码来制作软件蒙皮器 骨骼 皮肤动画 并且我正处于 优化 阶段 蒙皮器工作得很好 并且在 Core 上 1 09 毫秒内对 4900 个三角形网格与 22 个骨骼进行蒙皮Duo 2 Ghz 笔记本 我需要知道的是 1 有人可以
  • 有没有办法使用 i387 fsqrt 指令获得正确的舍入?

    有没有办法使用 i387 fsqrt 指令获得正确的舍入 除了改变精确模式在 x87 控制字中 我知道这是可能的 但这不是一个合理的解决方案 因为它存在令人讨厌的重入型问题 如果 sqrt 操作中断 精度模式将出错 我正在处理的问题如下 x
  • 是否有像 gccxml 这样的用于生成包装器的 C 标头解析器工具?

    我需要为一种新的编程语言编写一些 C 标头包装器 并且想要类似 gccxml 的东西 但不完全依赖 gcc 以及它在 Windows 系统上带来的问题 只需要读C而不是C 只要有完整的文档记录 任何格式的输出都可以 Linux Solari
  • 手动将 ClientBase 集合类型从 Array[] 更改为 List<>

    我将自己的 WCF 代理与 Client Base 一起使用 我想做一些类似于 svc util 中的 ct 属性的操作 并告诉代理返回 List 集合类型 我不能使用 List 因为实体由 nhibernate 管理 所以我必须使用 IL
  • 编译器错误“错误:在文件范围内可变地修改了‘字符串’”

    考虑 include
  • 在简单注入器中注册具有多个构造函数和字符串依赖项的类型

    我正在尝试弄清楚如何使用 Simple Injector 我在项目中使用了它 注册简单服务及其组件没有任何问题 但是 当组件具有两个以上实现接口的构造函数时 我想使用依赖注入器 public DAL IDAL private Logger
  • dropdownlist DataTextField 由属性组成?

    有没有一种方法可以通过 C 使 asp net 中的下拉列表的 datatextfield 属性由对象的多个属性组成 public class MyObject public int Id get set public string Nam
  • 错误左值需要作为赋值C++的左操作数

    整个程序基本上只允许用户移动光标 如果用户位于给定的坐标范围 2 2 内 则允许用户键入输入 我刚刚提供了一些我认为足以解决问题的代码 我不知道是什么导致了这个问题 你能解释一下为什么会发生吗 void goToXY int int 创建一
  • 设计 Javascript 前端 <-> C++ 后端通信

    在我最近的将来 我将不得不制作一个具有 C 后端和 Web 前端的系统 要求 目前 我对此了解不多 我认为前端将触发数据传输 而不是后端 所以不需要类似 Comet 的东西 由于在该领域的经验可能很少 我非常感谢您对我所做的设计决策的评论
  • 不兼容的类型 - 是因为数组已经是指针吗?

    在下面的代码中 我创建一个基于书籍结构的对象 并让它保存多个 书籍 我设置的是一个数组 即定义 启动的对象 然而 每当我去测试我对指针的了解 实践有帮助 并尝试创建一个指向创建的对象的指针时 它都会给我错误 C Users Justin D
  • 相当于 C# 中 Java 的“ByteBuffer.putType()”

    我正在尝试通过从 Java 移植代码来格式化 C 中的字节数组 在 Java 中 使用方法 buf putInt value buf putShort buf putDouble 等等 但我不知道如何将其移植到 C 我尝试过 MemoryS
  • 如何访问窗口?

    我正在尝试使用其句柄访问特定窗口 即System IntPtr value Getting the process of Visual Studio program var process Process GetProcessesByNam
  • C# 粘贴到文本框时检查剪贴板中的字符

    有没有一些方法可以在粘贴到文本框 C 之前仅检查剪贴板中的字符 Ctrl V 和右键单击 gt 粘贴 但不使用 MaskedTextbox 在文本框文本更改中添加规则以仅接受数字 例如 private string value privat

随机推荐