Index: include/llvm/CodeGen/GlobalISel/InstructionSelector.h =================================================================== --- include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -63,6 +63,8 @@ bool isOperandImmEqual(const MachineOperand &MO, int64_t Value, const MachineRegisterInfo &MRI) const; + + bool isObviouslySafeToFold(MachineInstr &MI) const; }; } // End namespace llvm. Index: include/llvm/Target/GlobalISel/SelectionDAGCompat.td =================================================================== --- include/llvm/Target/GlobalISel/SelectionDAGCompat.td +++ include/llvm/Target/GlobalISel/SelectionDAGCompat.td @@ -25,6 +25,8 @@ SDNode Node = node; } +def : GINodeEquiv; +def : GINodeEquiv; def : GINodeEquiv; def : GINodeEquiv; def : GINodeEquiv; Index: lib/CodeGen/GlobalISel/InstructionSelector.cpp =================================================================== --- lib/CodeGen/GlobalISel/InstructionSelector.cpp +++ lib/CodeGen/GlobalISel/InstructionSelector.cpp @@ -65,3 +65,8 @@ } return true; } + +bool InstructionSelector::isObviouslySafeToFold(MachineInstr &MI) const { + return !MI.mayLoadOrStore() && !MI.hasUnmodeledSideEffects() && + MI.implicit_operands().begin() == MI.implicit_operands().end(); +} Index: lib/Target/AArch64/AArch64InstructionSelector.cpp =================================================================== --- lib/Target/AArch64/AArch64InstructionSelector.cpp +++ lib/Target/AArch64/AArch64InstructionSelector.cpp @@ -752,42 +752,6 @@ // operands to use appropriate classes. return constrainSelectedInstRegOperands(I, TII, TRI, RBI); } - case TargetOpcode::G_MUL: { - // Reject the various things we don't support yet. - if (unsupportedBinOp(I, RBI, MRI, TRI)) - return false; - - const unsigned DefReg = I.getOperand(0).getReg(); - const RegisterBank &RB = *RBI.getRegBank(DefReg, MRI, TRI); - - if (RB.getID() != AArch64::GPRRegBankID) { - DEBUG(dbgs() << "G_MUL on bank: " << RB << ", expected: GPR\n"); - return false; - } - - unsigned ZeroReg; - unsigned NewOpc; - if (Ty.isScalar() && Ty.getSizeInBits() <= 32) { - NewOpc = AArch64::MADDWrrr; - ZeroReg = AArch64::WZR; - } else if (Ty == LLT::scalar(64)) { - NewOpc = AArch64::MADDXrrr; - ZeroReg = AArch64::XZR; - } else { - DEBUG(dbgs() << "G_MUL has type: " << Ty << ", expected: " - << LLT::scalar(32) << " or " << LLT::scalar(64) << '\n'); - return false; - } - - I.setDesc(TII.get(NewOpc)); - - I.addOperand(MachineOperand::CreateReg(ZeroReg, /*isDef=*/false)); - - // Now that we selected an opcode, we need to constrain the register - // operands to use appropriate classes. - return constrainSelectedInstRegOperands(I, TII, TRI, RBI); - } - case TargetOpcode::G_FADD: case TargetOpcode::G_FSUB: case TargetOpcode::G_FMUL: Index: test/CodeGen/AArch64/GlobalISel/arm64-instructionselect-muladd.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/arm64-instructionselect-muladd.mir @@ -0,0 +1,51 @@ +# RUN: llc -O0 -mtriple=aarch64-linux-gnu -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s + +--- | + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + + define void @SMADDLrrr_gpr() { ret void } +... + +--- +# => (SMADDLrrr:i64 GPR32:i32:$Rn, GPR32:i32:$Rm, GPR64:i64:$Ra) + +# CHECK-LABEL: name: SMADDLrrr_gpr +name: SMADDLrrr_gpr +legalized: true +regBankSelected: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr64 } +# CHECK-NEXT: - { id: 1, class: gpr32 } +# CHECK-NEXT: - { id: 2, class: gpr32 } +# CHECK-NEXT: - { id: 3, class: gpr64 } +# CHECK-NEXT: - { id: 4, class: gpr64 } +# CHECK-NEXT: - { id: 5, class: gpr64 } +# CHECK-NEXT: - { id: 6, class: gpr64 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + - { id: 2, class: gpr } + - { id: 3, class: gpr } + - { id: 4, class: gpr } + - { id: 5, class: gpr } + - { id: 6, class: gpr } + +# CHECK: body: +# CHECK: %0 = COPY %x0 +# CHECK: %1 = COPY %w1 +# CHECK: %2 = COPY %w2 +# CHECK: %6 = SMADDLrrr %1, %2, %0 +body: | + bb.0: + liveins: %x0, %w1, %w2 + + %0(s64) = COPY %x0 + %1(s32) = COPY %w1 + %2(s32) = COPY %w2 + %3(s64) = G_SEXT %1 + %4(s64) = G_SEXT %2 + %5(s64) = G_MUL %3, %4 + %6(s64) = G_ADD %0, %5 +... + Index: test/CodeGen/AArch64/GlobalISel/arm64-instructionselect-sext.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/arm64-instructionselect-sext.mir @@ -0,0 +1,58 @@ +# RUN: llc -O0 -mtriple=aarch64-linux-gnu -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s + +--- | + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + + define void @sext_s16_s32_gpr() { ret void } + define void @sext_s32_s64_gpr() { ret void } + +... + +--- +# CHECK-LABEL: name: sext_s16_s32_gpr +name: sext_s16_s32_gpr +legalized: true +regBankSelected: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr32 } +# CHECK-NEXT: - { id: 1, class: gpr32 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + +# CHECK: body: +# CHECK: %0 = COPY %w0 +# CHECK: %1 = SBFMWri %0, 0, 15 +body: | + bb.0: + liveins: %w0 + + %0(s16) = COPY %w0 + %1(s32) = G_SEXT %0 +... + +--- +# CHECK-LABEL: name: sext_s32_s64_gpr +name: sext_s32_s64_gpr +legalized: true +regBankSelected: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr32 } +# CHECK-NEXT: - { id: 1, class: gpr64 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + +# CHECK: body: +# CHECK: %0 = COPY %w0 +# CHECK: %2 = SUBREG_TO_REG 0, %0, {{[0-9]+}} +# CHECK: %1 = SBFMXri %2, 0, 31 +body: | + bb.0: + liveins: %w0 + + %0(s32) = COPY %w0 + %1(s64) = G_SEXT %0 +... Index: test/CodeGen/AArch64/GlobalISel/arm64-instructionselect-xor.mir =================================================================== --- test/CodeGen/AArch64/GlobalISel/arm64-instructionselect-xor.mir +++ test/CodeGen/AArch64/GlobalISel/arm64-instructionselect-xor.mir @@ -1,9 +1,4 @@ -# RUN: llc -O0 -mtriple=aarch64-apple-ios -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s -check-prefix=CHECK -check-prefix=IOS -# RUN: llc -O0 -mtriple=aarch64-linux-gnu -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s -check-prefix=CHECK -check-prefix=LINUX-DEFAULT -# RUN: llc -O0 -mtriple=aarch64-linux-gnu -relocation-model=pic -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s -check-prefix=CHECK -check-prefix=LINUX-PIC - -# Test the instruction selector. -# As we support more instructions, we need to split this up. +# RUN: llc -O0 -mtriple=aarch64-linux-gnu -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s --- | target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" Index: test/CodeGen/AArch64/GlobalISel/arm64-instructionselect-zext.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/arm64-instructionselect-zext.mir @@ -0,0 +1,58 @@ +# RUN: llc -O0 -mtriple=aarch64-linux-gnu -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s + +--- | + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + + define void @zext_s16_s32_gpr() { ret void } + define void @zext_s32_s64_gpr() { ret void } + +... + +--- +# CHECK-LABEL: name: zext_s16_s32_gpr +name: zext_s16_s32_gpr +legalized: true +regBankSelected: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr32 } +# CHECK-NEXT: - { id: 1, class: gpr32 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + +# CHECK: body: +# CHECK: %0 = COPY %w0 +# CHECK: %1 = UBFMWri %0, 0, 15 +body: | + bb.0: + liveins: %w0 + + %0(s16) = COPY %w0 + %1(s32) = G_ZEXT %0 +... + +--- +# CHECK-LABEL: name: zext_s32_s64_gpr +name: zext_s32_s64_gpr +legalized: true +regBankSelected: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr32 } +# CHECK-NEXT: - { id: 1, class: gpr64 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + +# CHECK: body: +# CHECK: %0 = COPY %w0 +# CHECK: %2 = SUBREG_TO_REG 0, %0, {{[0-9]+}} +# CHECK: %1 = UBFMXri %2, 0, 31 +body: | + bb.0: + liveins: %w0 + + %0(s32) = COPY %w0 + %1(s64) = G_ZEXT %0 +... Index: test/TableGen/GlobalISelEmitter.td =================================================================== --- test/TableGen/GlobalISelEmitter.td +++ test/TableGen/GlobalISelEmitter.td @@ -48,6 +48,87 @@ def ADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2), [(set GPR32:$dst, (add GPR32:$src1, GPR32:$src2))]>; +//===- Test a nested instruction match. -----------------------------------===// + +// CHECK-LABEL: if ([&]() { +// CHECK-NEXT: MachineInstr &MI0 = I; +// CHECK-NEXT: if (MI0.getNumOperands() < 3) +// CHECK-NEXT: return false; +// CHECK-NEXT: if (!MI0.getOperand(1).isReg()) +// CHECK-NEXT: return false; +// CHECK-NEXT: MachineInstr &MI1 = *MRI.getVRegDef(MI0.getOperand(1).getReg()); +// CHECK-NEXT: if (MI1.getNumOperands() < 3) +// CHECK-NEXT: return false; +// CHECK-NEXT: if ((MI0.getOpcode() == TargetOpcode::G_MUL) && +// CHECK-NEXT: ((/* dst */ (MRI.getType(MI0.getOperand(0).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(0).getReg(), MRI, TRI))))) && +// CHECK-NEXT: ((/* Operand 1 */ (MRI.getType(MI0.getOperand(1).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: (((MI1.getOpcode() == TargetOpcode::G_ADD) && +// CHECK-NEXT: ((/* Operand 0 */ (MRI.getType(MI1.getOperand(0).getReg()) == (LLT::scalar(32))))) && +// CHECK-NEXT: ((/* src1 */ (MRI.getType(MI1.getOperand(1).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI1.getOperand(1).getReg(), MRI, TRI))))) && +// CHECK-NEXT: ((/* src2 */ (MRI.getType(MI1.getOperand(2).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI1.getOperand(2).getReg(), MRI, TRI)))))) +// CHECK-NEXT: ))) && +// CHECK-NEXT: ((/* src3 */ (MRI.getType(MI0.getOperand(2).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(2).getReg(), MRI, TRI)))))) { +// CHECK-NEXT: if (!isObviouslySafeToFold(MI1)) return false; +// CHECK-NEXT: // (mul:i32 (add:i32 GPR32:i32:$src1, GPR32:i32:$src2), GPR32:i32:$src3) => (MULADD:i32 GPR32:i32:$src1, GPR32:i32:$src2, GPR32:i32:$src3) +// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::MULADD)); +// CHECK-NEXT: MIB.add(MI0.getOperand(0)/*dst*/); +// CHECK-NEXT: MIB.add(MI1.getOperand(1)/*src1*/); +// CHECK-NEXT: MIB.add(MI1.getOperand(2)/*src2*/); +// CHECK-NEXT: MIB.add(MI0.getOperand(2)/*src3*/); +// CHECK-NEXT: MIB.setMemRefs(I.memoperands_begin(), I.memoperands_end()); +// CHECK-NEXT: I.eraseFromParent(); +// CHECK-NEXT: MachineInstr &NewI = *MIB; +// CHECK-NEXT: constrainSelectedInstRegOperands(NewI, TII, TRI, RBI); +// CHECK-NEXT: return true; +// CHECK-NEXT: } + +// We also get a second rule by commutativity. +// CHECK-LABEL: if ([&]() { +// CHECK-NEXT: MachineInstr &MI0 = I; +// CHECK-NEXT: if (MI0.getNumOperands() < 3) +// CHECK-NEXT: return false; +// CHECK-NEXT: if (!MI0.getOperand(2).isReg()) +// CHECK-NEXT: return false; +// CHECK-NEXT: MachineInstr &MI1 = *MRI.getVRegDef(MI0.getOperand(2).getReg()); +// CHECK-NEXT: if (MI1.getNumOperands() < 3) +// CHECK-NEXT: return false; +// CHECK-NEXT: if ((MI0.getOpcode() == TargetOpcode::G_MUL) && +// CHECK-NEXT: ((/* dst */ (MRI.getType(MI0.getOperand(0).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(0).getReg(), MRI, TRI))))) && +// CHECK-NEXT: ((/* src3 */ (MRI.getType(MI0.getOperand(1).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(1).getReg(), MRI, TRI))))) && +// CHECK-NEXT: ((/* Operand 2 */ (MRI.getType(MI0.getOperand(2).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: (((MI1.getOpcode() == TargetOpcode::G_ADD) && +// CHECK-NEXT: ((/* Operand 0 */ (MRI.getType(MI1.getOperand(0).getReg()) == (LLT::scalar(32))))) && +// CHECK-NEXT: ((/* src1 */ (MRI.getType(MI1.getOperand(1).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI1.getOperand(1).getReg(), MRI, TRI))))) && +// CHECK-NEXT: ((/* src2 */ (MRI.getType(MI1.getOperand(2).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI1.getOperand(2).getReg(), MRI, TRI)))))) +// CHECK-NEXT: )))) { +// CHECK-NEXT: if (!isObviouslySafeToFold(MI1)) return false; +// CHECK-NEXT: // (mul:i32 GPR32:i32:$src3, (add:i32 GPR32:i32:$src1, GPR32:i32:$src2)) => (MULADD:i32 GPR32:i32:$src1, GPR32:i32:$src2, GPR32:i32:$src3) +// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::MULADD)); +// CHECK-NEXT: MIB.add(MI0.getOperand(0)/*dst*/); +// CHECK-NEXT: MIB.add(MI1.getOperand(1)/*src1*/); +// CHECK-NEXT: MIB.add(MI1.getOperand(2)/*src2*/); +// CHECK-NEXT: MIB.add(MI0.getOperand(1)/*src3*/); +// CHECK-NEXT: MIB.setMemRefs(I.memoperands_begin(), I.memoperands_end()); +// CHECK-NEXT: I.eraseFromParent(); +// CHECK-NEXT: MachineInstr &NewI = *MIB; +// CHECK-NEXT: constrainSelectedInstRegOperands(NewI, TII, TRI, RBI); +// CHECK-NEXT: return true; +// CHECK-NEXT: } + +def MULADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3), + [(set GPR32:$dst, + (mul (add GPR32:$src1, GPR32:$src2), GPR32:$src3))]>; + +//===- Test another simple pattern with regclass operands. ----------------===// + // CHECK-LABEL: if ([&]() { // CHECK-NEXT: MachineInstr &MI0 = I; // CHECK-NEXT: if (MI0.getNumOperands() < 3) Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ utils/TableGen/GlobalISelEmitter.cpp @@ -150,6 +150,7 @@ //===- Matchers -----------------------------------------------------------===// +class OperandMatcher; class MatchAction; /// Generates code to check that a match rule matches. @@ -253,6 +254,7 @@ /// are represented by a virtual register defined by a G_CONSTANT instruction. enum PredicateKind { OPM_ComplexPattern, + OPM_Instruction, OPM_Int, OPM_LLT, OPM_RegBank, @@ -268,6 +270,23 @@ PredicateKind getKind() const { return Kind; } + /// Return the OperandMatcher for the specified operand or nullptr if there + /// isn't one by that name in this operand predicate matcher. + /// + /// InstructionOperandMatcher is the only subclass that can return non-null + /// for this. + virtual const OperandMatcher * + getOperand(const StringRef SymbolicName) const { + assert(!SymbolicName.empty() && "Cannot lookup unnamed operand"); + return nullptr; + } + + /// Emit C++ statements to capture instructions into local variables. + /// + /// Only InstructionOperandMatcher needs to do anything for this method. + virtual void emitCxxCaptureStmtsForOperand(raw_ostream &OS, RuleMatcher &Rule, + StringRef Expr) const {} + /// Emit a C++ expression that checks the predicate for the given operand. virtual void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule, StringRef OperandExpr) const = 0; @@ -418,8 +437,27 @@ return (InsnVarName + ".getOperand(" + llvm::to_string(OpIdx) + ")").str(); } + const OperandMatcher *getOperand(const StringRef DesiredSymbolicName) const { + assert(!DesiredSymbolicName.empty() && "Cannot lookup unnamed operand"); + if (DesiredSymbolicName == SymbolicName) + return this; + for (const auto &OP : predicates()) { + const OperandMatcher *MaybeOperand = OP->getOperand(DesiredSymbolicName); + if (MaybeOperand) + return MaybeOperand; + } + return nullptr; + } + InstructionMatcher &getInstructionMatcher() const { return Insn; } + /// Emit C++ statements to capture instructions into local variables. + void emitCxxCaptureStmtsForOperand(raw_ostream &OS, RuleMatcher &Rule, + StringRef OperandExpr) const { + for (const auto &Predicate : predicates()) + Predicate->emitCxxCaptureStmtsForOperand(OS, Rule, OperandExpr); + } + /// Emit a C++ expression that tests whether the instruction named in /// InsnVarName matches all the predicate and all the operands. void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule, @@ -577,14 +615,13 @@ llvm_unreachable("Failed to lookup operand"); } - const OperandMatcher *getOperand(StringRef SymbolicName) const { + const OperandMatcher *getOperand(const StringRef SymbolicName) const { assert(!SymbolicName.empty() && "Cannot lookup unnamed operand"); - const auto &I = std::find_if(Operands.begin(), Operands.end(), - [&SymbolicName](const OperandMatcher &X) { - return X.getSymbolicName() == SymbolicName; - }); - if (I != Operands.end()) - return &*I; + for (const auto &Operand : Operands) { + const OperandMatcher *OM = Operand.getOperand(SymbolicName); + if (OM) + return OM; + } return nullptr; } @@ -596,6 +633,11 @@ } unsigned getNumOperands() const { return Operands.size(); } + OperandVec::iterator operands_begin() { return Operands.begin(); } + OperandVec::iterator operands_end() { return Operands.end(); } + iterator_range operands() { + return make_range(operands_begin(), operands_end()); + } OperandVec::const_iterator operands_begin() const { return Operands.begin(); } OperandVec::const_iterator operands_end() const { return Operands.end(); } iterator_range operands() const { @@ -606,6 +648,10 @@ void emitCxxCaptureStmts(raw_ostream &OS, RuleMatcher &Rule, StringRef Expr) { OS << "if (" << Expr << ".getNumOperands() < " << getNumOperands() << ")\n" << " return false;\n"; + for (const auto &Operand : Operands) { + Operand.emitCxxCaptureStmtsForOperand(OS, Rule, + Operand.getOperandExpr(Expr)); + } } /// Emit a C++ expression that tests whether the instruction named in @@ -663,6 +709,55 @@ } }; +/// Generates code to check that the operand is a register defined by an +/// instruction that matches the given instruction matcher. +/// +/// For example, the pattern: +/// (set $dst, (G_MUL (G_ADD $src1, $src2), $src3)) +/// would use an InstructionOperandMatcher for operand 1 of the G_MUL to match +/// the: +/// (G_ADD $src1, $src2) +/// subpattern. +class InstructionOperandMatcher : public OperandPredicateMatcher { +protected: + std::unique_ptr InsnMatcher; + +public: + InstructionOperandMatcher() + : OperandPredicateMatcher(OPM_Instruction), + InsnMatcher(new InstructionMatcher()) {} + + static bool classof(const OperandPredicateMatcher *P) { + return P->getKind() == OPM_Instruction; + } + + InstructionMatcher &getInsnMatcher() const { return *InsnMatcher; } + + const OperandMatcher * + getOperand(const StringRef SymbolicName) const override { + assert(!SymbolicName.empty() && "Cannot lookup unnamed operand"); + return InsnMatcher->getOperand(SymbolicName); + } + + void emitCxxCaptureStmtsForOperand(raw_ostream &OS, RuleMatcher &Rule, + StringRef OperandExpr) const override { + OS << "if (!" << OperandExpr + ".isReg())\n" + << " return false;\n"; + std::string InsnVarName = Rule.defineInsnVar( + OS, *InsnMatcher, + ("*MRI.getVRegDef(" + OperandExpr + ".getReg())").str()); + InsnMatcher->emitCxxCaptureStmts(OS, Rule, InsnVarName); + } + + void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule, + StringRef OperandExpr) const override { + OperandExpr = Rule.getInsnVarName(*InsnMatcher); + OS << "("; + InsnMatcher->emitCxxPredicateExpr(OS, Rule, OperandExpr); + OS << ")\n"; + } +}; + //===- Actions ------------------------------------------------------------===// void OperandPlaceholder::emitCxxValueExpr(raw_ostream &OS) const { switch (Kind) { @@ -918,6 +1013,55 @@ getInsnVarName(*Matchers.front())); OS << ") {\n"; + // We must also check if it's safe to fold the matched instructions. + if (InsnVariableNames.size() >= 2) { + for (const auto &Pair : InsnVariableNames) { + // Skip the root node since it isn't moving anywhere. Everything else is + // sinking to meet it. + if (Pair.first == Matchers.front().get()) + continue; + + // Reject the difficult cases until we have a more accurate check. + OS << " if (!isObviouslySafeToFold(" << Pair.second + << ")) return false;\n"; + + // FIXME: Emit checks to determine it's _actually_ safe to fold and/or + // account for unsafe cases. + // + // Example: + // MI1--> %0 = ... + // %1 = ... %0 + // MI0--> %2 = ... %0 + // It's not safe to erase MI1. We currently handle this by not + // erasing %0 (even when it's dead). + // + // Example: + // MI1--> %0 = load volatile @a + // %1 = load volatile @a + // MI0--> %2 = ... %0 + // It's not safe to sink %0's def past %1. We currently handle + // this by rejecting all loads. + // + // Example: + // MI1--> %0 = load @a + // %1 = store @a + // MI0--> %2 = ... %0 + // It's not safe to sink %0's def past %1. We currently handle + // this by rejecting all loads. + // + // Example: + // G_CONDBR %cond, @BB1 + // BB0: + // MI1--> %0 = load @a + // G_BR @BB1 + // BB1: + // MI0--> %2 = ... %0 + // It's not always safe to sink %0 across control flow. In this + // case it may introduce a memory fault. We currentl handle this + // by rejecting all loads. + } + } + for (const auto &MA : Actions) { MA->emitCxxActionStmts(OS, *this, "I"); } @@ -1101,8 +1245,6 @@ return true; } } - - return failedImport("Src child operand is an unsupported type"); } auto OpTyOrNone = MVTToLLT(ChildTypes.front().getConcrete()); @@ -1110,6 +1252,19 @@ return failedImport("Src operand has an unsupported type"); OM.addPredicate(*OpTyOrNone); + // Check for nested instructions. + if (!SrcChild->isLeaf()) { + // Map the node to a gMIR instruction. + InstructionOperandMatcher &InsnOperand = + OM.addPredicate(); + auto InsnMatcherOrError = + importSelDAGMatcher(InsnOperand.getInsnMatcher(), SrcChild); + if (auto Error = InsnMatcherOrError.takeError()) + return std::move(Error); + + return true; + } + // Check for constant immediates. if (auto *ChildInt = dyn_cast(SrcChild->getLeafValue())) { OM.addPredicate(ChildInt->getValue()); @@ -1270,6 +1425,7 @@ if (!isTrivialOperatorNode(Src)) return failedImport("Src pattern root isn't a trivial operator"); + // Start with the defined operands (i.e., the results of the root operator). Record *DstOp = Dst->getOperator(); if (!DstOp->isSubClassOf("Instruction")) return failedImport("Pattern operator isn't an instruction");