BURG(自底向上重写生成器)

概述

BURG 是编译器后端指令选择的经典工具。

它的核心思想是:给定一棵 IR 表达式树和一组带代价的机器指令模式,用动态规划找到覆盖整棵树的最小代价方案。

BURG 要解决什么问题?

问题场景

假设我们有以下 IR 表达式树(表示 (a + 5) * b):

其中:

  • a 是内存中的变量
  • b 是内存中的变量
  • 5 是常量

机器提供以下指令(每条指令 = 一个模式 + 代价):

规则 模式 指令 代价
R1 add(reg, reg) ADD rd, rs1, rs2 1
R2 add(reg, imm) ADDI rd, rs1, imm 1
R3 mul(reg, reg) MUL rd, rs1, rs2 3
R4 load(addr) LOAD rd, addr 2
R5 const(imm) LOADI rd, imm 1

目标:用这些指令覆盖整棵树,总代价最小。

BURG 的两步算法

Label 阶段(自底向上)

从叶子到根,给每个节点标注"用哪条规则、代价是多少"。

节点 a(叶子,内存变量):

text 复制代码
规则 R4:load(addr)
代价 = 2

标注:a 的最优代价 = 2(用 LOAD)

节点 5(叶子,常量):

text 复制代码
规则 R5:const(imm)
代价 = 1

标注:5 的最优代价 = 1(用 LOADI)

节点 +(内部节点,子节点是 a 和 5):

text 复制代码
候选规则 1:R1(add(reg, reg))
  需要 a 在寄存器中,5 在寄存器中
  代价 = cost(a) + cost(5) + 1 = 2 + 1 + 1 = 4

候选规则 2:R2(add(reg, imm))
  需要 a 在寄存器中,5 作为立即数
  代价 = cost(a) + 1 = 2 + 1 = 3

取最小:代价 = 3(用 ADDI)
标注:+ 的最优代价 = 3

节点 b(叶子,内存变量):

text 复制代码
规则 R4:load(addr)
代价 = 2

标注:b 的最优代价 = 2

节点 * (根节点,子节点是 + 和 b):

text 复制代码
候选规则:R3(mul(reg, reg))
  需要 + 的结果在寄存器中,b 在寄存器中
  代价 = cost(+) + cost(b) + 3 = 3 + 2 + 3 = 8

标注:* 的最优代价 = 8

Label 阶段结果汇总:

节点 最优规则 代价
a LOAD 2
5 LOADI 1
+ ADDI 3
b LOAD 2
* MUL 8

Select 阶段(自顶向下)

从根到叶,每步选择到达目标最便宜的路径。

根节点*:

text 复制代码
选择规则:R3(MUL)
需要:+ 的结果在寄存器中,b 在寄存器中

节点 +(被 * 需要,必须在寄存器中):

text 复制代码
选择规则:R2(ADDI)
需要:a 在寄存器中,5 作为立即数

节点 a(被 + 需要,必须在寄存器中):

text 复制代码
选择规则:R4(LOAD)
生成:LOAD r1, a

节点 5(被 + 作为立即数使用):

text 复制代码
不单独生成指令,直接作为 ADDI 的立即数

节点 b(被 * 需要,必须在寄存器中):

text 复制代码
选择规则:R4(LOAD)
生成:LOAD r2, b

节点 +(生成指令):

text 复制代码
生成:ADDI r1, r1, 5

节点 * (生成指令):

text 复制代码
生成:MUL r3, r1, r2

最终生成的汇编

assembly 复制代码
LOAD r1, a        ; 代价 2
ADDI r1, r1, 5    ; 代价 1
LOAD r2, b        ; 代价 2
MUL r3, r1, r2    ; 代价 3
; 总代价 = 8

验证:如果不用 ADDI 而用 ADD,需要额外加载 5 到寄存器:

assembly 复制代码
LOAD r1, a        ; 2
LOADI r4, 5       ; 1
ADD r1, r1, r4    ; 1
LOAD r2, b        ; 2
MUL r3, r1, r2    ; 3
; 总代价 = 9

BURG 选择了代价 8 的方案,比 9 更优。

BURG 与 Maximal Munch 的区别

算法 策略 最优性
Maximal Munch 从根到叶,每次选覆盖最多节点的瓦片 贪心,不一定最优
BURG 从叶到根计算代价,再从根到叶选择 动态规划,保证最优

Maximal Munch 可能错过的例子

假设树是 (a + 5) * b,但规则是:

规则 模式 代价
R1 mul(add(reg, imm), reg) 4(一条指令完成加法和乘法)
R2 add(reg, imm) 1
R3 mul(reg, reg) 3

Maximal Munch :从根 * 开始,最大瓦片是 R1(覆盖 * + + + 5),代价 4。

然后处理 a(LOAD,代价 2)和 b(LOAD,代价 2)。

总代价 = 4 + 2 + 2 = 8。

BURG:会计算所有可能组合,发现 R1 的总代价 8,而 R2 + R3 的总代价是 1 + 3 + 2 + 2 = 8。两者相同。

但如果 R1 的代价是 5,Maximal Munch 会选 R1(代价 5+2+2=9),而 BURG 会选 R2+R3(代价 8),更优。

结论:Maximal Munch 贪心,可能不是最优;BURG 用动态规划,保证最优。

BURG 的输入与输出

输入:模式描述

BURG 的输入是一个 .burg 文件,描述每条指令的模式和代价:

burg 复制代码
%term ADD=1 MUL=2 ADDI=3 LOAD=4 LOADI=5
%start reg

reg: ADD(reg, reg)    1
reg: ADDI(reg, imm)   1
reg: MUL(reg, reg)    3
reg: LOAD(addr)       2
reg: LOADI(imm)       1
  • %term:定义终结符(操作符)
  • %start reg:目标非终结符reg: ADD(reg, reg) 1:规则
  • "reg → ADD(reg, reg)",代价 1

输出:匹配器

BURG 生成一个 C/C++ 函数,输入是 IR 树,输出是最优覆盖方案:

c 复制代码
int burg_label(Node *node);   // Label 阶段
void burg_select(Node *node, int goal);  // Select 阶段

BURG 的完整工作流程

text 复制代码
输入:
  IR 树 + 模式描述(.burg 文件)

BURG 生成器:
  解析 .burg 文件
  生成状态机(自动机)
  生成 label() 和 select() 函数

编译器使用:
  ① 对 IR 树调用 label() → 每个节点标注最优规则和代价
  ② 对 IR 树调用 select() → 自顶向下选择规则,生成指令
  ③ 输出汇编代码

总结

概念 含义
BURG Bottom-Up Rewrite Generator,自底向上重写生成器
输入 模式描述(.burg 文件),每条模式有代价
输出 匹配器(label + select 函数)
Label 自底向上遍历,给每个节点标注最优规则和代价
Select 自顶向下遍历,每步选择到达目标最便宜的路径
核心算法 动态规划,保证找到最小代价覆盖
与 Maximal Munch 的区别 Maximal Munch 贪心,BURG 动态规划保证最优

一句话:BURG 是编译器指令选择的经典工具。

它把"用机器指令覆盖 IR 树"转化为"动态规划求最小代价覆盖",通过 Label(自底向上计算代价)和 Select(自顶向下选择规则)两步,保证生成的指令序列总代价最小。

相关推荐
198******126341 小时前
Work Agent深度解读:AI长程任务执行的机制与落地形态
人工智能
m4Rk_1 小时前
【论文阅读】Agent 记忆机制(90):HyperMem——用超图建模长期记忆中的高阶关联
论文阅读·人工智能·学习·开源·github
仍然.1 小时前
Redis---主从复制
java·数据库·redis
言乐61 小时前
逻辑回归与利弊
人工智能·算法·机器学习·数据挖掘·逻辑回归
墨心@1 小时前
AI Agent 学习总结
人工智能·自然语言处理·agent·harness·datawhale共学
Είναι η κοπέλα1 小时前
WSL2 部署 AI 开发环境:内核级 Linux + GPU 直通
linux·运维·人工智能
袖清暮雨1 小时前
机器学习之线性回归
人工智能·机器学习·线性回归
海绵宝宝转agent1 小时前
Leetcode100 二叉树的中序遍历
java·算法
其实防守也摸鱼1 小时前
网安自测题:掌握核心知识点的实用练习
linux·运维·服务器·前端·数据库·sql·xss