| //===-- llvm/CodeGen/GlobalISel/ConstantFoldingMIRBuilder.h --*- C++ -*-==// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| /// \file |
| /// This file implements a version of MachineIRBuilder which does trivial |
| /// constant folding. |
| //===----------------------------------------------------------------------===// |
| #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" |
| #include "llvm/CodeGen/GlobalISel/Utils.h" |
| |
| namespace llvm { |
| |
| static Optional<APInt> ConstantFoldBinOp(unsigned Opcode, const unsigned Op1, |
| const unsigned Op2, |
| const MachineRegisterInfo &MRI) { |
| auto MaybeOp1Cst = getConstantVRegVal(Op1, MRI); |
| auto MaybeOp2Cst = getConstantVRegVal(Op2, MRI); |
| if (MaybeOp1Cst && MaybeOp2Cst) { |
| LLT Ty = MRI.getType(Op1); |
| APInt C1(Ty.getSizeInBits(), *MaybeOp1Cst, true); |
| APInt C2(Ty.getSizeInBits(), *MaybeOp2Cst, true); |
| switch (Opcode) { |
| default: |
| break; |
| case TargetOpcode::G_ADD: |
| return C1 + C2; |
| case TargetOpcode::G_AND: |
| return C1 & C2; |
| case TargetOpcode::G_ASHR: |
| return C1.ashr(C2); |
| case TargetOpcode::G_LSHR: |
| return C1.lshr(C2); |
| case TargetOpcode::G_MUL: |
| return C1 * C2; |
| case TargetOpcode::G_OR: |
| return C1 | C2; |
| case TargetOpcode::G_SHL: |
| return C1 << C2; |
| case TargetOpcode::G_SUB: |
| return C1 - C2; |
| case TargetOpcode::G_XOR: |
| return C1 ^ C2; |
| case TargetOpcode::G_UDIV: |
| if (!C2.getBoolValue()) |
| break; |
| return C1.udiv(C2); |
| case TargetOpcode::G_SDIV: |
| if (!C2.getBoolValue()) |
| break; |
| return C1.sdiv(C2); |
| case TargetOpcode::G_UREM: |
| if (!C2.getBoolValue()) |
| break; |
| return C1.urem(C2); |
| case TargetOpcode::G_SREM: |
| if (!C2.getBoolValue()) |
| break; |
| return C1.srem(C2); |
| } |
| } |
| return None; |
| } |
| |
| /// An MIRBuilder which does trivial constant folding of binary ops. |
| /// Calls to buildInstr will also try to constant fold binary ops. |
| class ConstantFoldingMIRBuilder |
| : public FoldableInstructionsBuilder<ConstantFoldingMIRBuilder> { |
| public: |
| // Pull in base class constructors. |
| using FoldableInstructionsBuilder< |
| ConstantFoldingMIRBuilder>::FoldableInstructionsBuilder; |
| // Unhide buildInstr |
| using FoldableInstructionsBuilder<ConstantFoldingMIRBuilder>::buildInstr; |
| |
| // Implement buildBinaryOp required by FoldableInstructionsBuilder which |
| // tries to constant fold. |
| MachineInstrBuilder buildBinaryOp(unsigned Opcode, unsigned Dst, |
| unsigned Src0, unsigned Src1) { |
| validateBinaryOp(Dst, Src0, Src1); |
| auto MaybeCst = ConstantFoldBinOp(Opcode, Src0, Src1, getMF().getRegInfo()); |
| if (MaybeCst) |
| return buildConstant(Dst, MaybeCst->getSExtValue()); |
| return buildInstr(Opcode).addDef(Dst).addUse(Src0).addUse(Src1); |
| } |
| |
| template <typename DstTy, typename UseArg1Ty, typename UseArg2Ty> |
| MachineInstrBuilder buildInstr(unsigned Opc, DstTy &&Ty, UseArg1Ty &&Arg1, |
| UseArg2Ty &&Arg2) { |
| unsigned Dst = getDestFromArg(Ty); |
| return buildInstr(Opc, Dst, getRegFromArg(std::forward<UseArg1Ty>(Arg1)), |
| getRegFromArg(std::forward<UseArg2Ty>(Arg2))); |
| } |
| |
| // Try to provide an overload for buildInstr for binary ops in order to |
| // constant fold. |
| MachineInstrBuilder buildInstr(unsigned Opc, unsigned Dst, unsigned Src0, |
| unsigned Src1) { |
| switch (Opc) { |
| default: |
| break; |
| case TargetOpcode::G_ADD: |
| case TargetOpcode::G_AND: |
| case TargetOpcode::G_ASHR: |
| case TargetOpcode::G_LSHR: |
| case TargetOpcode::G_MUL: |
| case TargetOpcode::G_OR: |
| case TargetOpcode::G_SHL: |
| case TargetOpcode::G_SUB: |
| case TargetOpcode::G_XOR: |
| case TargetOpcode::G_UDIV: |
| case TargetOpcode::G_SDIV: |
| case TargetOpcode::G_UREM: |
| case TargetOpcode::G_SREM: { |
| return buildBinaryOp(Opc, Dst, Src0, Src1); |
| } |
| } |
| return buildInstr(Opc).addDef(Dst).addUse(Src0).addUse(Src1); |
| } |
| |
| // Fallback implementation of buildInstr. |
| template <typename DstTy, typename... UseArgsTy> |
| MachineInstrBuilder buildInstr(unsigned Opc, DstTy &&Ty, |
| UseArgsTy &&... Args) { |
| auto MIB = buildInstr(Opc).addDef(getDestFromArg(Ty)); |
| addUsesFromArgs(MIB, std::forward<UseArgsTy>(Args)...); |
| return MIB; |
| } |
| }; |
| } // namespace llvm |