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
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