Пример #1
0
def _InsUpdateDefUse(ins: ir.Ins, fun: ir.Fun, defs: Set[ir.Reg],
                     uses: Set[ir.Reg]):
    """Compute the set of defined and used registers for each instruction"""
    if ins.opcode.is_call():
        # note: a call instruction may have at most one used reg if opcode is JSR
        callee: ir.Fun = cfg.InsCallee(ins)
        assert isinstance(callee, ir.Fun)
        for cpu_reg in callee.cpu_live_out:
            # we intentionally iterate overall regs here
            for reg in fun.regs:
                if reg.cpu_reg is cpu_reg:
                    defs.add(reg)
                    uses.discard(reg)
        # for cpu_reg in callee.cpu_live_clobber:
        #     defs.add(cpu_reg)
        #     uses.discard(cpu_reg)
        # for cpu_reg in callee.cpu_live_in:
        #     uses.add(cpu_reg)

    num_defs = ins.opcode.def_ops_count()
    for n, reg in enumerate(ins.operands):
        if not isinstance(reg, ir.Reg): continue
        if n < num_defs:
            defs.add(reg)
            uses.discard(reg)
        else:
            uses.add(reg)
Пример #2
0
 def handle_call(self, ins, bbl, fun):
     callee: ir.Fun = cfg.InsCallee(ins)
     assert not self.pop_args, (f"unconsumed popargs from {self.callee} "
                                f"in {fun.name}:{bbl.name}: {self.pop_args}")
     self.pop_args = callee.output_types.copy()
     self.callee = f"{callee.name} results"
     self.push_args.reverse()
     assert self.push_args == callee.input_types, (f"parameter mismatch for {callee.name} "
                                                   f"in {fun.name}:{bbl.name} "
                                                   f"actual:{self.push_args} vs expected:{callee.input_types}")
     self.push_args = []
Пример #3
0
def _InsPushargConversionReverse(ins: ir.Ins, fun: ir.Fun,
                                 params: List[ir.CpuReg]) -> Optional[
    List[ir.Ins]]:
    """
    This pass converts pusharg reg -> mov arg_reg = reg

    Note:
         * params is passed around between calls to this function
         * pusharg's always precede calls or returns
    """
    if ins.opcode is o.PUSHARG:
        cpu_reg = params.pop(0)
        src = ins.operands[0]
        reg = fun.FindOrAddCpuReg(cpu_reg, src.kind)
        return [ir.Ins(o.MOV, [reg, src])]
    assert not params, f"params {params} should be empty at ins {ins} {ins.operands}"
    if ins.opcode.is_call():
        callee: ir.Fun = cfg.InsCallee(ins)
        assert isinstance(callee, ir.Fun)
        params += GetCpuRegsForSignature(callee.input_types)
    elif ins.opcode is o.RET:
        params += GetCpuRegsForSignature(fun.output_types)
    return None
Пример #4
0
def _InsPopargConversion(ins: ir.Ins, fun: ir.Fun,
                         params: List[ir.CpuReg]) -> Optional[List[ir.Ins]]:
    """
    This pass converts `poparg reg` -> `mov reg = arg_reg`

    it must used in a forward pass over the Bbl and will update `param`
    for use with the next Ins in the BBl. The initial value of `param`
    reflects the Fun's arguments.

    """
    if ins.opcode is o.POPARG:
        cpu_reg = params.pop(0)
        dst = ins.operands[0]
        # assert dst.kind == cpu_reg.kind
        reg = fun.FindOrAddCpuReg(cpu_reg, dst.kind)
        return [ir.Ins(o.MOV, [dst, reg])]

    assert not params, f"params {params} should be empty at ins {ins}"

    if ins.opcode.is_call():
        callee: ir.Fun = cfg.InsCallee(ins)
        assert isinstance(callee, ir.Fun)
        params += GetCpuRegsForSignature(callee.output_types)
    return None
Пример #5
0
def _InsUpdateLiveness(ins: ir.Ins, fun: ir.Fun,
                       live_out: Set[ir.Reg]) -> bool:
    """Similar to _InsUpdateDefUse but also checks if the instruction is useless"""
    if ins.opcode.is_call():
        # note: a call instruction may have at most one used reg if opcode is JSR
        callee: ir.Fun = cfg.InsCallee(ins)
        assert isinstance(callee, ir.Fun)
        for cpu_reg in callee.cpu_live_out:
            for reg in fun.regs:
                if reg.cpu_reg is cpu_reg:
                    live_out.discard(reg)

    # TODO: take cpu_live_in into account, otherwise we cannot eliminate useless code
    #       after pusharg and poparg convdersions
    #         live_out.discard(cpu_reg)
    #     for cpu_reg in callee.cpu_live_clobber:
    #         live_out.discard(cpu_reg)
    #     for cpu_reg in callee.cpu_live_in:
    #         live_out.add(cpu_reg)

    is_live = ins.opcode.has_side_effect()
    num_defs = ins.opcode.def_ops_count()
    for n, reg in enumerate(ins.operands):
        if not isinstance(reg, ir.Reg): continue

        if n < num_defs:
            if reg in live_out:
                is_live = True
            live_out.discard(reg)
        else:
            # all defs precede the uses, so if the ins is not live at this point,
            # we can skip the
            if not is_live:
                break
            live_out.add(reg)
    return is_live
Пример #6
0
def BblGetLiveRanges(bbl: ir.Bbl, fun: ir.Fun, live_out: Set[ir.Reg],
                     emit_uses: bool) -> List[LiveRange]:
    """ LiveRanges are use to do register allocation

    Note: function call handling is quite adhoc and likely has bugs
    """
    out = []
    bbl_size = len(bbl.inss)

    last_use: Dict[ir.Reg, LiveRange] = {}
    last_call_pos = -1
    last_call_cpu_live_in = []

    def initialize_lr(pos: int, reg: ir.Reg) -> LiveRange:
        lr = LiveRange(-1, pos, reg, 1)
        last_use[reg] = lr
        out.append(lr)
        return lr

    def finalize_lr(lr: LiveRange, def_pos: int):
        lr.def_pos = def_pos
        if (last_call_pos != -1 and last_call_pos != AFTER_BBL
                and last_call_pos < lr.last_use_pos):
            lr.flags |= LiveRangeFlag.LAC
        del last_use[lr.reg]

    # handle live ranges that extend passed the bbl
    for reg in live_out:
        initialize_lr(AFTER_BBL, reg)

    for pos, ins in enumerate(reversed(bbl.inss)):
        pos = bbl_size - 1 - pos
        if ins.opcode is o.RET:
            if fun.cpu_live_out:
                last_call_cpu_live_in = fun.cpu_live_out
                last_call_pos = AFTER_BBL
        elif ins.opcode.is_call():
            callee: ir.Fun = cfg.InsCallee(ins)
            assert isinstance(callee, ir.Fun)
            # This complication only applies after we have (partial) reg allocation
            # Finalize live ranges using the results of the call
            if callee.cpu_live_out:
                # Note, destructive list iteration -> `list(...)` is necessary
                for reg, lr in list(last_use.items()):
                    if reg.HasCpuReg() and reg.cpu_reg in callee.cpu_live_out:
                        finalize_lr(lr, pos)
            last_call_cpu_live_in = callee.cpu_live_in
            last_call_pos = pos  # setting this after dealing with cpu_live_out seems right

        num_defs = ins.opcode.def_ops_count()
        uses = []
        for n, reg in enumerate(ins.operands):
            if not isinstance(reg, ir.Reg): continue
            if n < num_defs:
                lr = last_use.get(reg)
                if lr:
                    finalize_lr(lr, pos)
                else:
                    last_use_pos = NO_USE
                    # Note: likely this makes some assumptions about the adjacency
                    # of these instruction and the call: We assume this cannot be LAC!
                    if reg.HasCpuReg(
                    ) and reg.cpu_reg in last_call_cpu_live_in:
                        last_use_pos = last_call_pos
                    out.append(LiveRange(pos, last_use_pos, reg, 0))
            else:
                lr = last_use.get(reg)
                if lr:
                    lr.num_uses += 1
                else:
                    lr = initialize_lr(pos, reg)
                if lr not in uses:
                    uses.append(lr)
        if emit_uses and uses:
            # Note "pos, pos" ensure that this record will come before
            #       a regular record after sorting
            out.append(LiveRange(pos, pos, ir.REG_INVALID, 0, uses))

    for lr in list(last_use.values()):
        finalize_lr(lr, BEFORE_BBL)
    return out
Пример #7
0
def BblGetLiveRanges(bbl: ir.Bbl, fun: ir.Fun,
                     live_out: Set[ir.Reg]) -> List[LiveRange]:
    """ LiveRanges are use to do register allocation

    Note: function call handling is quite adhoc and likely has bugs.
    The output contains the following special LiveRanges
    * LRs without a last_use if the register is used outside the Bbl (based on live_out)
    * LRs without a def of the register is defined outside the Bbl
    * use-def LRs contain the LRs of all the used regs in the instruction at point p.
                     (def=p last_use=p,  reg=REG_INVALID)
    """
    out = []
    bbl_size = len(bbl.inss)

    last_use: Dict[ir.Reg, LiveRange] = {}
    last_call_pos = -1
    last_call_cpu_live_in = []

    def initialize_lr(pos: int, reg: ir.Reg) -> LiveRange:
        lr = LiveRange(-1, pos, reg, 1)
        last_use[reg] = lr
        out.append(lr)
        return lr

    def finalize_lr(lr: LiveRange, def_pos: int):
        lr.def_pos = def_pos
        if (last_call_pos != -1 and last_call_pos != AFTER_BBL
                and last_call_pos < lr.last_use_pos):
            lr.flags |= LiveRangeFlag.LAC
        del last_use[lr.reg]

    # handle live ranges that extend passed the bbl
    for reg in live_out:
        if reg.IsSpilled(): continue
        initialize_lr(AFTER_BBL, reg)

    for pos, ins in enumerate(reversed(bbl.inss)):
        pos = bbl_size - 1 - pos
        if ins.opcode is o.RET:
            if fun.cpu_live_out:
                last_call_cpu_live_in = fun.cpu_live_out
                last_call_pos = AFTER_BBL
        elif ins.opcode.is_call():
            callee: ir.Fun = cfg.InsCallee(ins)
            assert isinstance(callee, ir.Fun)
            # This complication only applies after we have (partial) reg allocation
            # Finalize live ranges using the results of the call
            if callee.cpu_live_out:
                # Note, destructive list iteration -> `list(...)` is necessary
                for reg, lr in list(last_use.items()):
                    if reg.HasCpuReg() and reg.cpu_reg in callee.cpu_live_out:
                        finalize_lr(lr, pos)
            last_call_cpu_live_in = callee.cpu_live_in
            last_call_pos = pos  # setting this after dealing with cpu_live_out seems right

        num_defs = ins.opcode.def_ops_count()
        uses = []
        for n, reg in enumerate(ins.operands):
            if not isinstance(reg, ir.Reg): continue
            if reg.IsSpilled(): continue
            if n < num_defs:  # define reg
                if n == 0 and ir.REG_FLAG.TWO_ADDRESS in reg.flags and reg == ins.operands[
                        1]:
                    continue
                lr = last_use.get(reg)
                if lr:
                    finalize_lr(lr, pos)
                else:
                    last_use_pos = NO_USE
                    # Note: likely this makes some assumptions about the adjacency
                    # of these instruction and the call: We assume this cannot be LAC!
                    if reg.HasCpuReg(
                    ) and reg.cpu_reg in last_call_cpu_live_in:
                        last_use_pos = last_call_pos
                    #elif ins.opcode is o.NOP1:
                    #    # assert False, f"found nop1 {ins.operands}"
                    #    last_use_pos = n - 1
                    out.append(LiveRange(pos, last_use_pos, reg, 0))
            else:  # used reg
                lr = last_use.get(reg)
                if lr:
                    # make meaning of num_uses more precise
                    lr.num_uses += 1
                else:
                    lr = initialize_lr(pos, reg)
                if lr not in uses:
                    uses.append(lr)
        if uses:
            # Note "pos, pos" ensure that this record will come before
            #       a regular record after sorting
            out.append(LiveRange(pos, pos, ir.REG_INVALID, 0, uses))

    for lr in list(last_use.values()):
        finalize_lr(lr, BEFORE_BBL)
    return out