干涉图着色

概述

干涉图着色是寄存器分配的核心算法。它的目标是:把程序中的虚拟寄存器(无限多个)映射到物理寄存器(数量有限,如 x86-64 只有 16 个通用寄存器)。

如果映射不了,就把一些变量溢出到栈上。

什么是干涉图?

核心概念:活跃区间(Live Range)

一个变量的活跃区间是从它被定义(def)到它最后一次被使用(use)之间的程序点。

c 复制代码
a = 1;        // a 被定义
b = 2;        // b 被定义
c = a + b;    // a 和 b 被使用,c 被定义
d = a + c;    // a 和 c 被使用,d 被定义
return d;     // d 被使用

活跃区间:

变量 定义点 最后使用点 活跃区间
a 第1行 第4行 1~4
b 第2行 第3行 2~3
c 第3行 第4行 3~4
d 第4行 第5行 4~5

干涉的定义

两个变量干涉(interfere):如果它们的活跃区间有重叠,就不能共用同一个物理寄存器。

从上面的例子:

  • a 和 b 干涉:a 活跃到第4行,b 活跃到第3行,重叠区间 2~3。
  • a 和 c 干涉:a 活跃到第4行,c 从第3行开始活跃,重叠区间 3~4。
  • b 和 c 干涉:b 活跃到第3行,c 从第3行开始活跃,重叠区间 3。
  • c 和 d 干涉:c 活跃到第4行,d 从第4行开始活跃,重叠区间 4。
  • a 和 d 不干涉 :a 在第4行之后不再活跃,d 从第4行开始活跃,没有重叠(严格来说,如果 a 在第4行被使用后死亡,d 在第4行被定义,它们可以共用寄存器)。

干涉图

节点:每个变量(虚拟寄存器)一个节点。

边 :如果两个变量干涉,就在它们之间连一条边

(实际图中 a-c, a-b, b-c, c-d 都有边;a-d 无边。)

图着色

问题转化

给干涉图的每个节点分配一种颜色(物理寄存器),使得相邻节点颜色不同。

如果 K = 可用物理寄存器数量:

  • 如果图能用 K 种颜色着色 → 所有变量都能分配到寄存器。
  • 如果不能用 K 种颜色着色 → 需要 溢出(spill) 一些变量到栈上。

例子:K = 3

假设有 3 个物理寄存器(r0, r1, r2)。

干涉图:

着色过程:

text 复制代码
步骤 1:选一个度数 < K 的节点。
        这里 d 的度数是 1(只连 c),1 < 3,可以简化。
        把 d 从图中移除,压入栈。
        栈:[d]

步骤 2:剩余图中,c 的度数是 2(连 a, b, d 已移除),2 < 3,可以简化。
        把 c 移除,压入栈。
        栈:[d, c]

步骤 3:剩余图中,a 的度数是 1(连 b),1 < 3,可以简化。
        把 a 移除,压入栈。
        栈:[d, c, a]

步骤 4:剩余图中,b 的度数是 0,可以简化。
        把 b 移除,压入栈。
        栈:[d, c, a, b]

步骤 5:栈非空,开始分配颜色(从栈顶弹出):
        b 弹出:分配 r0
        a 弹出:邻居 b 用了 r0,a 可以用 r1
        c 弹出:邻居 a 用了 r1,b 用了 r0,c 可以用 r2
        d 弹出:邻居 c 用了 r2,d 可以用 r0 或 r1

最终分配:
  a → r1
  b → r0
  c → r2
  d → r0

验证:

  • a 和 b 颜色不同(r1 vs r0)✅;
  • a 和 c 不同(r1 vs r2)✅;
  • b 和 c 不同(r0 vs r2)✅;
  • c 和 d 不同(r2 vs r0)✅。

如果 K = 2 呢?

只有 2 个物理寄存器,干涉图需要 3 种颜色(因为 a, b, c 两两干涉,形成三角形)。

三角形需要 3 种颜色。K=2 时无法着色,必须溢出一个变量。

溢出选择:选度数最高的节点(c 的度数是 2),把它溢出到栈上。

溢出后,c 不再占寄存器,图变成:

text 复制代码
    a ─── b

这是二分图,可以用 2 种颜色着色。

从 IR 到寄存器分配

原始 IR

assembly 复制代码
mov $1, a      ; a = 1
mov $2, b      ; b = 2
add a, b       ; c = a + b
mov b, c       ; 保存 c
add a, c       ; d = a + c
mov c, d       ; 保存 d
ret d          ; return d

虚拟寄存器:a, b, c, d。

活跃区间

变量 定义 最后使用 活跃区间
a 第1行 第5行 1~5
b 第2行 第3行 2~3
c 第3行 第6行 3~6
d 第5行 第7行 5~7

干涉图

a 和 b 干涉(2~3 重叠)

a 和 c 干涉(3~5 重叠)

b 和 c 干涉(3 重叠)

c 和 d 干涉(5~6 重叠)

a 和 d 不干涉(a 在第5行之后死亡,d 从第5行开始)

用 3 个寄存器着色

如上所述:

text 复制代码
a → r1
b → r0
c → r2
d → r0

最终汇编

assembly 复制代码
mov $1, r1     ; a = 1
mov $2, r0     ; b = 2
add r1, r0     ; c = a + b(结果在 r0)
mov r0, r2     ; 保存 c 到 r2
add r1, r2     ; d = a + c(结果在 r2)
mov r2, r0     ; 保存 d 到 r0
ret r0         ; return d

注意:d 分配到了 r0,而 b 之前也用了 r0。因为 b 在第3行之后就不再活跃,r0 可以复用给 d。

代码

python 复制代码
def release_pressure(self, node, reg_class):
        """Remove some register pressure from the given node."""
        if node in self._num_blocked:
            self._num_blocked[node] -= self.q(node.reg_class, reg_class)
        
# ------ #

def enable_moves(self, nodes):
        for node in nodes:
            for move in self.NodeMoves(node):
                if move in self.activeMoves:
                    self.activeMoves.remove(move)
                    self.worklistMoves.add(move)

# ------ #

def decrement_degree(self, m):
        """If a node was lowered in degree, check if there are nodes that
        can be moved from the spill list to the freeze of simplify
        list
        """
        # This check was m.degree == self.K - 1
        if m in self.spill_worklist and self.is_colorable(m):
            self.enable_moves({m} | m.adjecent)
            self.spill_worklist.remove(m)
            if self.is_move_related(m):
                self.freeze_worklist.add(m)
            else:
                self.simplify_worklist.add(m)

# ------ #

def simplify(self):
        """Remove nodes from the graph"""
        n = self.simplify_worklist.pop()
        self.select_stack.append(n)

        if self.verbose:
            self.logger.debug("Simplify node %s", n)

        # Pop out of graph, we place it back later:
        self.frame.ig.mask_node(n)
        for m in n.adjecent:
            self.release_pressure(m, n.reg_class)
            self.decrement_degree(m)

# ------ #

def unlink_move(self, move):
        src = self.node(move.used_registers[0])
        dst = self.node(move.defined_registers[0])
        if move in src.moves:
            src.moves.remove(move)
        if move in dst.moves:
            dst.moves.remove(move)

# ------ #

def is_move_related(self, n):
        """Check if a node is used by move instructions"""
        return bool(self.NodeMoves(n))

# ------ #

def calc_num_blocked(self, node):
        """Calculate for the given node how many registers are blocked
        by it's adjecent nodes.

        This is an advanced form of a nodes degree, but then for
        register of different classes.
        """
        B = node.reg_class
        num_blocked = sum(self.q(B, j.reg_class) for j in node.adjecent)
        return num_blocked

# ------ #

def is_colorable(self, node) -> bool:
        """
        Helper function to determine whether a node is trivially
        colorable. This means: no matter the colors of the nodes neighbours,
        the node can be given a color.

        In case of one register class, this is: n.degree < self.K

        In case of more than one register class, somehow the worst case
        damage by all neighbours must be determined.

        We do this now with the pq-test.
        """
        if node.is_colored:
            return True

        if node in self._num_blocked:
            num_blocked = self._num_blocked[node]
        else:
            num_blocked = self.calc_num_blocked(node)
            self._num_blocked[node] = num_blocked
        # This invariant should hold:
        # assert num_blocked == self.calc_num_blocked(node)
        return num_blocked < self.K[node.reg_class]

# ------ #

def add_worklist(self, u):
        if (
            (u not in self.precolored)
            and (not self.is_move_related(u))
            and self.is_colorable(u)
        ):
            self.freeze_worklist.remove(u)
            self.simplify_worklist.add(u)

# ------ #

def coalesc(self):
        """Coalesc moves conservative.

        This means, merge the variables of a move into
        one variable, and delete the move. But do this
        only when no spill will occur.
        """
        # Remove the move from the worklist:
        m = self.worklistMoves.pop()
        x = self.node(m.defined_registers[0])
        y = self.node(m.used_registers[0])
        u, v = (y, x) if y in self.precolored else (x, y)
        if self.verbose:
            self.logger.debug("Coalescing %s which couples %s and %s", m, u, v)

        if u is v:
            # u is v, so we do 'mov x, x', which is redundant
            self.coalescedMoves.add(m)
            self.unlink_move(m)
            self.add_worklist(u)
            if self.verbose:
                self.logger.debug("Move was an identity move")
        elif (v in self.precolored) or self.has_edge(u, v):
            # Both u and v are precolored
            # or there is an interfering edge
            # between the two nodes:
            self.constrainedMoves.add(m)
            self.unlink_move(m)
            self.add_worklist(u)
            self.add_worklist(v)
            if self.verbose:
                self.logger.debug("Move is constrained!")
        elif (
            u.is_colored
            and issubclass(u.reg_class, v.reg_class)
            and all(self.ok(t, u) for t in v.adjecent)
        ) or ((not u.is_colored) and self.conservative(u, v)):
            # Check if v can be given the class of u, in other words:
            # is u a subclass of v?
            self.coalescedMoves.add(m)
            self.unlink_move(m)
            self.combine(u, v)
            self.add_worklist(u)
        else:
            self.logger.debug("Active move!")
            self.activeMoves.add(m)

# ------ #

def freeze_moves(self, u):
        """Freeze moves for node u"""
        for m in list(self.NodeMoves(u)):
            if m in self.activeMoves:
                self.activeMoves.remove(m)
            else:
                self.worklistMoves.remove(m)
            self.unlink_move(m)
            self.frozenMoves.add(m)
            # Check other part of the move for still being move related:
            src = self.node(m.used_registers[0])
            dst = self.node(m.defined_registers[0])
            v = src if u is dst else dst
            if (
                v not in self.precolored
                and not self.is_move_related(v)
                and self.is_colorable(v)
            ):
                assert v in self.freeze_worklist
                self.freeze_worklist.remove(v)
                self.simplify_worklist.add(v)

# ------ #

def select_spill(self):
        """Select potential spill node.

        Select a node to be spilled. This is optimistic,
        since this might be turned into a real spill.
        Continue nevertheless, to discover more potential
        spills, or we might be lucky and able to color the
        graph any ways.
        """

        # TODO: select a node which is certainly not a node that was
        # introduced during spilling?
        # Select to be spilled variable:
        # Select node with the lowest priority:
        p = []
        for n in self.spill_worklist:
            assert not n.is_colored
            d = sum(len(self.frame.ig.defs(t)) for t in n.temps)
            u = sum(len(self.frame.ig.uses(t)) for t in n.temps)
            priority = (u + d) / n.degree
            self.logger.debug("%s has spill priority=%s", n, priority)
            p.append((n, priority))
        node = min(p, key=lambda x: x[1])[0]

        # Potential spill node, place in simplify worklist:
        self.spill_worklist.remove(node)
        self.simplify_worklist.add(node)
        self.freeze_moves(node)

# ------ #

def rewrite_program(self, node):
        """Rewrite program by creating a load and a store for each use"""
        # Generate spill code:
        self.logger.debug(f"Placing {node} on stack")
        if self.verbose:
            self.reporter.message(f"Placing {node} on stack")

        size = node.reg_class.bitsize // 8
        alignment = size
        slot = self.frame.alloc(size, alignment)
        self.logger.debug("Allocating stack slot %s", slot)

        # TODO: maybe break-up coalesced node before doing this?
        for tmp in node.temps:
            instructions = OrderedSet(
                self.frame.ig.uses(tmp) + self.frame.ig.defs(tmp)
            )
            for instruction in instructions:
                if self.verbose:
                    self.reporter.message(
                        f"Updating instruction: {instruction}"
                    )

                vreg2 = self.frame.new_reg(type(tmp))
                self.logger.debug("tmp: %s, new: %s", tmp, vreg2)
                if self.verbose:
                    self.reporter.message(f"Replace {tmp} by {vreg2}")
                instruction.replace_register(tmp, vreg2)

                if instruction.reads_register(vreg2):
                    code = self.spill_gen.gen_load(self.frame, vreg2, slot)
                    if self.verbose:
                        self.reporter.message(
                            f"Load code before: {list(map(str, code))}"
                        )
                    self.frame.insert_code_before(instruction, code)

                if instruction.writes_register(vreg2):
                    code = self.spill_gen.gen_store(self.frame, vreg2, slot)
                    if self.verbose:
                        self.reporter.message(
                            f"Store code after: {list(map(str, code))}"
                        )
                    self.frame.insert_code_after(instruction, code)

                if self.verbose:
                    self.reporter.dump_frame(self.frame)

# ------ #

def remove_redundant_moves(self):
        """Remove coalesced moves"""
        for move in self.coalescedMoves:
            self.frame.instructions.remove(move)

# ------ #

def apply_colors(self):
    """Assign colors to registers"""
    # Apply all colors:
    for node in self.frame.ig:
        assert node.reg is not None
        for reg in node.temps:
            if reg.is_colored:
                assert reg.color == node.reg.color
            else:
                reg.set_color(node.reg.color)

            # Mark the register as used in this frame:
            self.frame.used_regs.add(node.reg.get_real())
            # TODO:
            # if self.frame.debug_db:
            #    self.frame.debug_db.map(
            #        reg, self.arch.get_register(node.reg))

# ------ #

def alloc_frame(self, frame: Frame):
        """Do iterated register allocation for a single frame.

        This is the entry function for the register allocator and drives
        through all stages of the algorithm.

        Args:
            frame: The frame to perform register allocation on.
        """
        spill_rounds = 0

        self.logger.debug("Starting iterative coloring")
        while True:
            self.init_data(frame)

            # Process all work lists:
            while True:
                # self.check_invariants()

                # Run one of the possible steps:
                if self.simplify_worklist:
                    self.simplify()
                elif self.worklistMoves:
                    self.coalesc()
                elif self.freeze_worklist:
                    self.freeze()
                elif self.spill_worklist:
                    self.select_spill()
                else:
                    break

            self.logger.debug("Now assinging colors")
            spilled_nodes = self.assign_colors()
            if spilled_nodes:
                spill_rounds += 1

                self.logger.debug("Spilling round %s", spill_rounds)
                max_spill_rounds = 30
                if spill_rounds > max_spill_rounds:
                    raise RuntimeError(
                        f"Give up after {max_spill_rounds} spill rounds!"
                    )

                # Rewrite program now.
                for node in spilled_nodes:
                    self.rewrite_program(node)

                if self.verbose:
                    self.reporter.message("Rewrote program with spilling")
                    self.reporter.dump_frame(self.frame)

            else:
                # Done!
                break

        self.remove_redundant_moves()
        self.apply_colors()

release_pressure

python 复制代码
def release_pressure(self, node, reg_class):
    """Remove some register pressure from the given node."""
    if node in self._num_blocked:
        self._num_blocked[node] -= self.q(node.reg_class, reg_class)

作用:

  • 当某个节点被简化(从图中移除)时,减少它邻居的"阻塞计数"。
  • _num_blocked:记录每个节点被多少寄存器阻塞(用于多寄存器类的情况)。q(B, j):计算寄存器类 B 和 j 之间的阻塞数量。

enable_moves

python 复制代码
def enable_moves(self, nodes):
    for node in nodes:
        for move in self.NodeMoves(node):
            if move in self.activeMoves:
                self.activeMoves.remove(move)
                self.worklistMoves.add(move)

作用:把与给定节点相关的、之前被"冻结"的 move 指令重新激活,放回工作队列。

背景:在寄存器分配中,move 指令(如 mov a, b)如果两个操作数能分配到同一寄存器,就可以删除。但有时因为干涉关系,暂时不能合并,就把 move 冻结。当图变化后,可能又可以合并了,就重新激活。

decrement_degree

python 复制代码
def decrement_degree(self, m):
    if m in self.spill_worklist and self.is_colorable(m):
        self.enable_moves({m} | m.adjecent)
        self.spill_worklist.remove(m)
        if self.is_move_related(m):
            self.freeze_worklist.add(m)
        else:
            self.simplify_worklist.add(m)

作用:当一个节点的度数降低后,检查它是否从"可能溢出"变成"可着色"。

逻辑:

  • 如果 m 在 spill 列表中,且现在可着色了 → 从 spill 列表移到 freeze 或 simplify 列表。
  • 如果 m 与 move 相关 → 移到 freeze 列表(先尝试合并 move)。
  • 否则 → 移到 simplify 列表(直接简化)。

simplify

python 复制代码
def simplify(self):
    """Remove nodes from the graph"""
    n = self.simplify_worklist.pop()
    self.select_stack.append(n)

    self.frame.ig.mask_node(n)
    for m in n.adjecent:
        self.release_pressure(m, n.reg_class)
        self.decrement_degree(m)

作用:从图中移除一个度数 < K 的节点,压入栈。

步骤:

  • 从 simplify 工作队列弹出一个节点 n。
  • 把 n 压入 select_stack(后面着色时用)。
  • 在干涉图中"遮蔽"n(暂时移除)。
  • 对每个邻居 m:减少阻塞计数,降低度数。
python 复制代码
def unlink_move(self, move):
    src = self.node(move.used_registers[0])
    dst = self.node(move.defined_registers[0])
    if move in src.moves:
        src.moves.remove(move)
    if move in dst.moves:
        dst.moves.remove(move)

作用:从源节点和目标节点的 move 列表中移除这条 move 指令。

python 复制代码
def is_move_related(self, n):
    """Check if a node is used by move instructions"""
    return bool(self.NodeMoves(n))

作用:检查节点 n 是否与任何 move 指令相关。

calc_num_blocked

python 复制代码
def calc_num_blocked(self, node):
    B = node.reg_class
    num_blocked = sum(self.q(B, j.reg_class) for j in node.adjecent)
    return num_blocked

作用:计算节点 node 被多少寄存器阻塞。

公式:对所有邻居 j,累加 q(B, j.reg_class)。q(B, j):寄存器类 B 被寄存器类 j 阻塞的数量。

在单寄存器类情况下,q(B, j) = 1(每个邻居阻塞一个寄存器)。

is_colorable

python 复制代码
def is_colorable(self, node) -> bool:
    if node.is_colored:
        return True

    if node in self._num_blocked:
        num_blocked = self._num_blocked[node]
    else:
        num_blocked = self.calc_num_blocked(node)
        self._num_blocked[node] = num_blocked
    return num_blocked < self.K[node.reg_class]

作用:判断节点是否"可着色"。

条件:

  • 如果已经着色 → 可着色。
  • 否则,计算 num_blocked,如果 < K(可用寄存器数量)→ 可着色。

直觉:如果邻居占用的寄存器数量小于 K,那么至少还有一个寄存器可用。

add_worklist

python 复制代码
def add_worklist(self, u):
    if (
        (u not in self.precolored)
        and (not self.is_move_related(u))
        and self.is_colorable(u)
    ):
        self.freeze_worklist.remove(u)
        self.simplify_worklist.add(u)

作用:如果节点 u 不是预着色的、不与 move 相关、且可着色,就从 freeze 列表移到 simplify 列表。

coalesc

python 复制代码
def coalesc(self):
    """Coalesc moves conservative."""
    m = self.worklistMoves.pop()
    x = self.node(m.defined_registers[0])
    y = self.node(m.used_registers[0])
    u, v = (y, x) if y in self.precolored else (x, y)
    ...

作用:尝试合并 move 指令的两个操作数(mov x, y 合并为同一个寄存器)。

逻辑:

  • 如果 u is v → 冗余 move,删除。
  • 如果 v 是预着色的,或 u 和 v 之间有干涉边 → 不能合并,标记为 constrained。
  • 如果 u 已着色且 v 的邻居都能接受 u 的寄存器类,或保守合并成功 → 合并。
  • 否则 → 标记为 active move。

freeze_moves

python 复制代码
def freeze_moves(self, u):
    """Freeze moves for node u"""
    for m in list(self.NodeMoves(u)):
        if m in self.activeMoves:
            self.activeMoves.remove(m)
        else:
            self.worklistMoves.remove(m)
        self.unlink_move(m)
        self.frozenMoves.add(m)
        ...

作用:冻结与节点 u 相关的所有 move 指令。

逻辑:

  • 从 active 或 worklist 中移除 move。
  • 从源/目标节点的 move 列表中移除。
  • 加入 frozen 集合。
  • 检查 move 另一端的节点是否还能简化。

select_spill

python 复制代码
def select_spill(self):
    """Select potential spill node."""
    p = []
    for n in self.spill_worklist:
        d = sum(len(self.frame.ig.defs(t)) for t in n.temps)
        u = sum(len(self.frame.ig.uses(t)) for t in n.temps)
        priority = (u + d) / n.degree
        p.append((n, priority))
    node = min(p, key=lambda x: x[1])[0]

    self.spill_worklist.remove(node)
    self.simplify_worklist.add(node)
    self.freeze_moves(node)

作用:从 spill 列表中选择一个节点溢出。

优先级公式:

priority=use 次数+def 次数度数 priority = \frac{use\ 次数 + def\ 次数}{度数} priority=度数use 次数+def 次数

  • 度数高 → 优先级低(更倾向于溢出)。
  • 使用次数多 → 优先级高(更倾向于保留)。

选择优先级最低的节点溢出。

注意:这是"乐观"溢出------先标记为可能溢出,继续着色,可能最后不需要真正溢出。

rewrite_program

python 复制代码
def rewrite_program(self, node):
    """Rewrite program by creating a load and a store for each use"""
    size = node.reg_class.bitsize // 8
    alignment = size
    slot = self.frame.alloc(size, alignment)

    for tmp in node.temps:
        instructions = OrderedSet(
            self.frame.ig.uses(tmp) + self.frame.ig.defs(tmp)
        )
        for instruction in instructions:
            vreg2 = self.frame.new_reg(type(tmp))
            instruction.replace_register(tmp, vreg2)

            if instruction.reads_register(vreg2):
                code = self.spill_gen.gen_load(self.frame, vreg2, slot)
                self.frame.insert_code_before(instruction, code)

            if instruction.writes_register(vreg2):
                code = self.spill_gen.gen_store(self.frame, vreg2, slot)
                self.frame.insert_code_after(instruction, code)

作用:真正执行溢出------为每个被溢出的变量分配栈槽,在每次使用前插入 load,每次定义后插入 store。

步骤:

  • 分配栈槽(大小 = 寄存器位宽 / 8)。
  • 对每个临时变量:
    • 创建新的虚拟寄存器 vreg2。
    • 替换指令中的 tmp 为 vreg2。
    • 如果指令读 vreg2 → 在指令前插入 load。
    • 如果指令写 vreg2 → 在指令后插入 store。

remove_redundant_moves

python 复制代码
def remove_redundant_moves(self):
    """Remove coalesced moves"""
    for move in self.coalescedMoves:
        self.frame.instructions.remove(move)

作用:删除所有已合并的 move 指令(它们变成冗余的)。

apply_colors

python 复制代码
def apply_colors(self):
    """Assign colors to registers"""
    for node in self.frame.ig:
        assert node.reg is not None
        for reg in node.temps:
            if reg.is_colored:
                assert reg.color == node.reg.color
            else:
                reg.set_color(node.reg.color)
            self.frame.used_regs.add(node.reg.get_real())

作用:把干涉图节点的颜色应用到实际的虚拟寄存器上。

alloc_frame(主循环)

python 复制代码
def alloc_frame(self, frame: Frame):
    spill_rounds = 0

    while True:
        self.init_data(frame)

        while True:
            if self.simplify_worklist:
                self.simplify()
            elif self.worklistMoves:
                self.coalesc()
            elif self.freeze_worklist:
                self.freeze()
            elif self.spill_worklist:
                self.select_spill()
            else:
                break

        spilled_nodes = self.assign_colors()
        if spilled_nodes:
            spill_rounds += 1
            if spill_rounds > 30:
                raise RuntimeError(...)
            for node in spilled_nodes:
                self.rewrite_program(node)
        else:
            break

    self.remove_redundant_moves()
    self.apply_colors()

作用:寄存器分配的主循环。

流程:

  • 初始化数据(构建干涉图)。
  • 反复执行:
    • 如果 simplify 队列非空 → simplify(移除低度数节点)。
    • 否则如果 move 队列非空 → coalesc(尝试合并 move)。
    • 否则如果 freeze 队列非空 → freeze(冻结 move)。
    • 否则如果 spill 队列非空 → select_spill(选择溢出)。
    • 否则 → 跳出,开始着色。
  • 着色(assign_colors)。
  • 如果有溢出节点 → 重写程序(插入 load/store),增加溢出轮次,重新开始。
  • 如果无溢出 → 完成。
  • 删除冗余 move,应用颜色。

总结

概念 含义
干涉图 节点 = 变量,边 = 活跃区间重叠
图着色 给节点分配颜色(物理寄存器),相邻节点颜色不同
K 种颜色 K = 可用物理寄存器数量
可着色 度数 < K 的节点一定可着色
溢出 无法着色时,把变量放到栈上
简化 移除度数 < K 的节点,压入栈
合并(coalesc) 尝试把 move 的两个操作数合并到同一寄存器
冻结(freeze) 暂时放弃合并 move
乐观溢出 先标记为可能溢出,继续着色,可能最后不需要

一句话:干涉图着色就是把变量分配问题转化为图着色问题。节点是变量,边是活跃区间重叠。

用 K 种颜色着色,K 是物理寄存器数量。如果着不了色,就溢出到栈上。

代码实现了一个完整的迭代寄存器分配器,包括简化、合并、冻结、溢出、重写程序等步骤。

相关推荐
小蒋观天下1 小时前
两轮车检测AI摄像头行业竞争格局与企业突围路径解析
大数据·人工智能·安全·计算机视觉·ai大模型
工业设备方案笔记1 小时前
IBOX3576 vs PICO-PC RK3588S:AI边缘计算盒子/主板如何选?
人工智能
FluxArt1 小时前
GPT Image 2.5 商品换颜色:Flux Art 当前电商工具实操
人工智能·gpt
白露与泡影1 小时前
Jev 爆火之后,给企业应用配一个 AI 决策模型
大数据·人工智能·深度学习
zhongerzixunshi1 小时前
DLMM 企业数字化转型的“度量衡”与导航仪
大数据·人工智能
xiaoye-duck1 小时前
《Linux 网络编程》深入理解 IO 多路复用:select服务器完善、poll 接口详解与服务端改造实战
linux·网络
55873 生态系统1 小时前
价值005+55873 价值定义篇:文化资产实体价值体系重构
人工智能·55873全域文明生态体系·55873操作系统·全域文明生态系统·55873技术底层
Jlzn88881 小时前
深圳CCS产线AOI漏焊检出怎么拆解检测逻辑?
人工智能
找了一圈尾巴1 小时前
Agent 运行时架构发展
人工智能·架构