Esempio n. 1
0
def _InsConstantFold(ins: ir.Ins, bbl: ir.Bbl, _fun: ir.Fun,
                     allow_conv_conversion: bool) -> Optional[List[ir.Ins]]:
    """
    Try combining the constant from ins_def with the instruction in ins

    Return 1 iff a change was made

    Note: None of the transformations must change the def register - otherwise
    the reaching_defs will be stale
    """

    ops = ins.operands
    kind = ins.opcode.kind
    if kind is o.OPC_KIND.COND_BRA:
        if not isinstance(ops[0], ir.Const) or not isinstance(
                ops[1], ir.Const):
            return None
        branch_taken = eval.EvaluatateCondBra(ins.opcode, ops[0], ops[1])
        target = ops[2]
        assert len(bbl.edge_out) == 2
        if branch_taken:
            succ_to_drop = bbl.edge_out[1] if bbl.edge_out[0] == target else \
                bbl.edge_out[0]
        else:
            succ_to_drop = target
        bbl.DelEdgeOut(succ_to_drop)
        return []
    elif kind is o.OPC_KIND.CMP:
        if not isinstance(ops[3], ir.Const) or not isinstance(
                ops[4], ir.Const):
            return None
        cmp_true = eval.EvaluatateCondBra(
            o.BEQ if ins.opcode is o.CMPEQ else o.BLT, ops[3], ops[4])
        if cmp_true:
            ins.Init(o.MOV, [ops[0], ops[1]])
        else:
            ins.Init(o.MOV, [ops[0], ops[2]])
    elif kind is o.OPC_KIND.ALU1:
        if not isinstance(ops[1], ir.Const):
            return None
        new_op = eval.EvaluatateALU1(ins.opcode, ops[1])
        ins.Init(o.MOV, [ops[0], new_op])
        return [ins]
    elif kind is o.OPC_KIND.ALU:
        if not isinstance(ops[1], ir.Const) or not isinstance(
                ops[2], ir.Const):
            return None
        new_op = eval.EvaluatateALU(ins.opcode, ops[1], ops[2])
        ins.Init(o.MOV, [ops[0], new_op])
        return [ins]
    elif ins.opcode is o.CONV:
        # TODO: this needs some  more thought generally but in
        # particular when we apply register widening
        # transformations, conv instructions end up being the only
        # ones with narrow width regs which simplifies
        # code generation. By allowing this to be converted into a
        # mov instruction we may leak the narrow register.
        if not allow_conv_conversion or not isinstance(ops[1], ir.Const):
            return None
        dst: ir.Reg = ops[0]
        src = ops[1]
        if not o.RegIsAddrInt(src.kind) or not o.RegIsAddrInt(dst.kind):
            return None
        new_val = eval.ConvertIntValue(dst.kind, src)
        ins.Init(o.MOV, [dst, new_val])
        return [ins]
    else:
        return None
Esempio n. 2
0
def _InsConstantFold(ins: ir.Ins, bbl: ir.Bbl, _fun: ir.Fun,
                     allow_conv_conversion: bool) -> Optional[List[ir.Ins]]:
    """
    Try combining the constant from ins_def with the instruction in ins

    Return 1 iff a change was made

    Note: None of the transformations must change the def register - otherwise
    the reaching_defs will be stale
    """

    ops = ins.operands
    kind = ins.opcode.kind
    if kind is o.OPC_KIND.COND_BRA:
        if not isinstance(ops[0], ir.Const) or not isinstance(
                ops[1], ir.Const):
            return None
        # TODO: implement this, needs access to BBL for CFG changes
        evaluator = _EVALUATORS_COND_BRA.get(ins.opcode)
        assert evaluator, f"Evaluator NYI for: {ins} {ins.operands}"
        branch_taken = evaluator(ops[0].value, ops[1].value)
        target = ops[2]
        assert len(bbl.edge_out) == 2
        if branch_taken:
            succ_to_drop = bbl.edge_out[1] if bbl.edge_out[0] == target else \
                bbl.edge_out[0]
        else:
            succ_to_drop = target
        bbl.DelEdgeOut(succ_to_drop)
        return []
    elif kind is o.OPC_KIND.ALU1:
        if not isinstance(ops[1], ir.Const):
            return None
        assert False, f"Evaluator NYI for ALU1: {ins} {ins.operands}"

    elif kind is o.OPC_KIND.ALU:
        if not isinstance(ops[1], ir.Const) or not isinstance(
                ops[2], ir.Const):
            return None
        evaluator = _EVALUATORS_ALU.get(ins.opcode)
        assert evaluator, f"Evaluator NYI for: {ins} {ins.operands}"
        val = ir.Const(ops[1].kind, evaluator(ops[1].value, ops[2].value))
        ins.opcode = o.MOV
        ins.operands.pop(-1)
        ins.operands[1] = val
        ins.operand_defs.pop(-1)
        ins.operand_defs[1] = ir.INS_INVALID
        return [ins]
    elif ins.opcode is o.CONV:
        # TODO: this needs some  more thought generally but in
        # particular when we apply register widening
        # transformations, conv instructions end up being the only
        # ones with narrow width regs which simplifies
        # code generation. By allowing this to be converted into a
        # mov instruction we may leak the narrow register.
        if not allow_conv_conversion or not isinstance(ops[1], ir.Const):
            return None
        dst: ir.Reg = ops[0]
        src = ops[1]
        if not o.RegIsAddrInt(src.kind) or not o.RegIsAddrInt(dst.kind):
            return None
        new_val = ConvertIntValue(dst.kind, src)
        ins.Init(o.MOV, [dst, new_val])
        return [ins]
    else:
        return None