Index: include/llvm/CodeGen/GlobalISel/GISelAccessor.h =================================================================== --- include/llvm/CodeGen/GlobalISel/GISelAccessor.h +++ include/llvm/CodeGen/GlobalISel/GISelAccessor.h @@ -17,6 +17,7 @@ namespace llvm { class CallLowering; +class MachineLegalizeInfo; class RegisterBankInfo; /// The goal of this helper class is to gather the accessor to all @@ -27,6 +28,9 @@ struct GISelAccessor { virtual ~GISelAccessor() {} virtual const CallLowering *getCallLowering() const { return nullptr;} + virtual const MachineLegalizeInfo *getMachineLegalizeInfo() const { + return nullptr; + } virtual const RegisterBankInfo *getRegBankInfo() const { return nullptr;} }; } // End namespace llvm; Index: include/llvm/CodeGen/GlobalISel/LegalizeMachineIR.h =================================================================== --- /dev/null +++ include/llvm/CodeGen/GlobalISel/LegalizeMachineIR.h @@ -0,0 +1,92 @@ +//== llvm/CodeGen/GlobalISel/LegalizeMachineIR.h --------------- -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file A pass to convert the target-illegal operations created by IR -> MIR +/// translation into ones the target expects to be able to select. This may +/// occur in multiple phases, for example G_ADD <2 x i8> -> G_ADD <2 x i16> -> +/// G_ADD <4 x i16>. +/// +/// The LegalizeHelper class is where most of the work happens, and is designed +/// to be callable from other passes that find themselves with an illegal +/// instruction. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_GLOBALISEL_LEGALIZEMACHINEIR_H +#define LLVM_CODEGEN_GLOBALISEL_LEGALIZEMACHINEIR_H + +#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/CodeGen/MachineFunctionPass.h" + +namespace llvm { +// Forward declarations. +class MachineLegalizeInfo; +class MachineRegisterInfo; + +class LegalizeHelper { +public: + LegalizeHelper(MachineFunction &MF); + + /// Replace MI by a sequence of legal instructions that can implement the same + /// operation. Note that this means MI may be deleted, so any iterator steps + /// should be performed before calling this function. + bool legalizeInstr(MachineInstr &MI); + + /// Legalize an instruction by reducing the width of the underlying scalar + /// type. + void narrowScalar(MachineInstr &MI, Type *NarrowTy); + + /// Legalize an instruction by performing the operation on a wider scalar type + /// (for example a 16-bit addition can be safely performed at 32-bits + /// precision, ignoring the unused bits). + void widenScalar(MachineInstr &MI, Type *WideTy); + + /// Legalize a vector instruction by splitting into multiple components, each + /// acting on the same scalar type as the original but with fewer elements. + void fewerElementsVector(MachineInstr &MI, Type *NarrowTy); + + /// Legalize a vector instruction by increasing the number of vector elements + /// involved and ignoring the added elements later. + void moreElementsVector(MachineInstr &MI, Type *WideTy); + +private: + + /// Helper function to split a wide generic register into bitwise blocks with + /// the given Type (which implies the number of blocks needed). The generic + /// registers created are appended to Ops, starting at bit 0 of Reg. + void extractParts(unsigned Reg, Type *Ty, int NumParts, + SmallVectorImpl &Ops); + + MachineIRBuilder MIRBuilder; + const MachineLegalizeInfo &MLI; + MachineRegisterInfo &MRI; +}; + +class LegalizeMachineIR : public MachineFunctionPass { +public: + static char ID; + +private: + + /// Initialize the field members using \p MF. + void init(MachineFunction &MF); + +public: + // Ctor, nothing fancy. + LegalizeMachineIR(); + + const char *getPassName() const override { + return "LegalizeMachineIR"; + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} // End namespace llvm. + +#endif Index: include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h =================================================================== --- include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h +++ include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h @@ -140,6 +140,76 @@ /// /// \return The newly created instruction. MachineInstr *buildInstr(unsigned Opcode); + + /// Build and insert \p Res = G_ADD \p Ty \p Op0, \p Op1 + /// + /// G_ADD sets \p Res to the sum of integer parameters \p Op0 and \p Op1, + /// truncated to their width. + /// + /// \pre setBasicBlock or setMI must have been called. + /// + /// \return The newly created instruction. + MachineInstr *buildAdd(Type *Ty, unsigned Res, unsigned Op0, unsigned Op1); + + /// Build and insert \p Res, \p CarryOut = G_ADDC \p Ty \p Op0, \p Op1 + /// + /// G_ADDC sets \p Res to \p Op0 + \p Op1 (truncated to the bit width) and + /// sets \p CarryOut to 1 if the result overflowed in 2s-complement + /// arithmetic. + /// + /// \pre setBasicBlock or setMI must have been called. + /// + /// \return The newly created instruction. + MachineInstr *buildAddc(Type *Ty, unsigned Res, unsigned CarryOut, + unsigned Op0, unsigned Op1); + + /// Build and insert \p Res, \p CarryOut = G_ADDE \p Ty \p Op0, \p Op1, + /// \p CarryIn + /// + /// G_ADDE sets \p Res to \p Op0 + \p Op1 + \p CarryIn (truncated to the bit + /// width) and sets \p CarryOut to 1 if the result overflowed in 2s-complement + /// arithmetic. + /// + /// \pre setBasicBlock or setMI must have been called. + /// + /// \return The newly created instruction. + MachineInstr *buildAdde(Type *Ty, unsigned Res, unsigned CarryOut, + unsigned Op0, unsigned Op1, unsigned CarryIn); + + /// Build and insert \p Res = G_ANYEXTEND \p Ty \p Op0 + /// + /// G_ANYEXTEND produces a register of the specified width, with bits 0 to + /// sizeof(\p Ty) * 8 set to \p Op. The remaining bits are unspecified + /// (i.e. this is neither zero nor sign-extension). For a vector register, + /// each element is extended individually. + /// + /// \pre setBasicBlock or setMI must have been called. + /// + /// \return The newly created instruction. + MachineInstr *buildAnyExtend(Type *Ty, unsigned Res, unsigned Op); + + /// Build and insert \p Res = G_EXTRACT \p Ty \p Op0, \p Idx + /// + /// If \p Ty has size N bits, G_EXTRACT sets \p Res to bits + /// [Idx * N, Idx * (N+1)) of \p Op. + /// + /// \pre setBasicBlock or setMI must have been called. + /// + /// \return The newly created instruction. + MachineInstr *buildExtract(Type *Ty, unsigned Res, unsigned Op, int Idx); + + /// Build and insert \p Res = G_SEQUENCE \p Ty \p Ops[0], ... + /// + /// G_SEQUENCE concatenates each element in Ops into a single register, where + /// Ops[0] starts at bit 0 of \p Res. + /// + /// \pre setBasicBlock or setMI must have been called. + /// + /// \return The newly created instruction. + MachineInstr *buildSequence(Type *Ty, unsigned Res, + SmallVectorImpl &Ops); + + MachineInstr *buildTruncate(Type *Ty, unsigned Res, unsigned Op); }; } // End namespace llvm. Index: include/llvm/CodeGen/GlobalISel/MachineLegalizeInfo.h =================================================================== --- /dev/null +++ include/llvm/CodeGen/GlobalISel/MachineLegalizeInfo.h @@ -0,0 +1,117 @@ +//==-- llvm/CodeGen/GlobalISel/MachineLegalizeInfo.h -------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// Interface for Targets to specify which operations they can successfully +/// select and how the others should be expanded most efficiently. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_GLOBALISEL_MACHINELEGALIZEINFO_H +#define LLVM_CODEGEN_GLOBALISEL_MACHINELEGALIZEINFO_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/CodeGen/ValueTypes.h" + +#include +#include + +namespace llvm { +class Type; +class VectorType; +class MachineInstr; +class LLVMContext; + +/// Holds all the information related to register banks. +class MachineLegalizeInfo { +public: + enum LegalizeAction : std::uint8_t { + Legal, + NarrowScalar, + WidenScalar, + FewerElements, + MoreElements, + Libcall, + Custom, + }; + + MachineLegalizeInfo(LLVMContext &Ctx); + + /// Compute any ancilliary tables needed to quickly decide how an operation + /// should be handled. This must be called after all "set*Action"methods but + /// before any query is made or incorrect results may be returned. + void computeTables(); + + /// Set how a specific G_* operation should be legalized on a given type. + void setAction(unsigned Opcode, Type *Ty, LegalizeAction Action) { + Actions[std::make_pair(Opcode, Ty)] = Action; + } + + /// More friendly way to set an action for common types that have an EVT + /// representation. + void setAction(unsigned Opcode, EVT Ty, LegalizeAction Action) { + setAction(Opcode, Ty.getTypeForEVT(Ctx), Action); + } + + /// If an operation on a given vector type (say ) isn't explicitly + /// specified, we proceed in 2 stages. First we legalize the underlying scalar + /// (so that there's at least one legal vector with that scalar), then we + /// adjust the number of elements in the vector so that it is legal. The + /// desired action in the first step is controlled by this function. + void setScalarInVectorAction(unsigned Opcode, Type *ScalarTy, + LegalizeAction Action) { + ScalarInVectorActions[std::make_pair(Opcode, ScalarTy)] = Action; + } + + /// Helper function because EVTs are easier to work with. + void setScalarInVectorAction(unsigned Opcode, EVT Ty, LegalizeAction Action) { + setScalarInVectorAction(Opcode, Ty.getTypeForEVT(Ctx), Action); + } + + std::pair getAction(unsigned Opcode, Type *Ty) const; + std::pair getAction(MachineInstr &MI) const; + + /// Iterate the given function (typically something like doubling the width) + /// on Ty until we find a legal type for this operation. + Type *findLegalType(unsigned Opcode, Type *Ty, + std::function NextType) const { + LegalizeAction Action; + do { + Ty = NextType(Ty); + auto ActionIt = Actions.find(std::make_pair(Opcode, Ty)); + if (ActionIt == Actions.end()) + Action = DefaultActions.find(Opcode)->second; + else + Action = ActionIt->second; + } while(Action != Legal); + return Ty; + } + + /// Find what type it's actually OK to perform the given operation on, given + /// the general approach we've decided to take. + Type *findLegalType(unsigned Opcode, Type *Ty, LegalizeAction Action) const; + + bool isLegal(MachineInstr &MI) const; + +protected: + // Subclasses may want a context to construct fully general llvm::Type + // instances for complex situations. + LLVMContext &Ctx; + +private: + typedef DenseMap, LegalizeAction> ActionMap; + + ActionMap Actions; + ActionMap ScalarInVectorActions; + DenseMap, unsigned> MaxLegalVectorElts; + DenseMap DefaultActions; +}; + +} // End namespace llvm. + +#endif Index: include/llvm/CodeGen/TargetPassConfig.h =================================================================== --- include/llvm/CodeGen/TargetPassConfig.h +++ include/llvm/CodeGen/TargetPassConfig.h @@ -217,6 +217,8 @@ /// LLVM code to machine instructions with possibly generic opcodes. virtual bool addIRTranslator() { return true; } + virtual bool addLegalizeMachineIR() { return true; } + /// This method may be implemented by targets that want to run passes /// immediately before the register bank selection. virtual void addPreRegBankSelect() {} Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -186,6 +186,9 @@ void initializeLoopSimplifyCFGLegacyPassPass(PassRegistry&); void initializeLoopSimplifyPass(PassRegistry&); void initializeLoopStrengthReducePass(PassRegistry&); +void initializeGlobalMergePass(PassRegistry&); +void initializeLegalizeMachineIRPass(PassRegistry&); +void initializeLoopRerollPass(PassRegistry&); void initializeLoopUnrollPass(PassRegistry&); void initializeLoopUnswitchPass(PassRegistry&); void initializeLoopVectorizePass(PassRegistry&); Index: include/llvm/Target/GenericOpcodes.td =================================================================== --- include/llvm/Target/GenericOpcodes.td +++ include/llvm/Target/GenericOpcodes.td @@ -13,8 +13,31 @@ //===----------------------------------------------------------------------===// //------------------------------------------------------------------------------ +// Unary ops. +//------------------------------------------------------------------------------ + +// Extend the underlying scalar type of an operation, leaving the high bits +// unspecified. +def G_ANYEXTEND : Instruction { + let OutOperandList = (outs unknown:$dst); + let InOperandList = (ins unknown:$src); + let AsmString = ""; + let hasSideEffects = 0; +} + +// Truncate the underlying scalar type of an operation. This is equivalent to +// G_EXTRACT for scalar types, but acts elementwise on vectors. +def G_TRUNCATE : Instruction { + let OutOperandList = (outs unknown:$dst); + let InOperandList = (ins unknown:$src); + let AsmString = ""; + let hasSideEffects = 0; +} + +//------------------------------------------------------------------------------ // Binary ops. //------------------------------------------------------------------------------ + // Generic addition. def G_ADD : Instruction { let OutOperandList = (outs unknown:$dst); @@ -24,6 +47,38 @@ let isCommutable = 1; } +// Generic addition producing a carry flag. +def G_ADDC : Instruction { + let OutOperandList = (outs unknown:$dst, unknown:$carry); + let InOperandList = (ins unknown:$src1, unknown:$src2); + let AsmString = ""; + let hasSideEffects = 0; + let isCommutable = 1; +} + +// Generic addition consuming and producing a carry flag. + +// FIXME: I think I'm going to merge this with G_ADDC (so what is now "G_ADDC a, +// b" would instead be "G_ADDC a, b, 0"). That needs G_IMMEDIATE or similar +// though and would bloat the initial patch. +def G_ADDE : Instruction { + let OutOperandList = (outs unknown:$dst, unknown:$carry_out); + let InOperandList = (ins unknown:$src1, unknown:$src2, unknown:$carry_in); + let AsmString = ""; + let hasSideEffects = 0; + let isCommutable = 1; +} + +// Extract bits of the specified size, starting from block given by $idx. This +// will almost certainly be mapped to a sub-register COPY after register banks +// have been selected. +def G_EXTRACT : Instruction { + let OutOperandList = (outs unknown:$dst); + let InOperandList = (ins unknown:$src, unknown:$idx); + let AsmString = ""; + let hasSideEffects = 0; +} + // Generic bitwise or. def G_OR : Instruction { let OutOperandList = (outs unknown:$dst); @@ -34,8 +89,23 @@ } //------------------------------------------------------------------------------ +// Variadic ops +//------------------------------------------------------------------------------ + +// Combine a sequence of generic vregs into a single larger value (starting at +// bit 0). +def G_SEQUENCE : Instruction { + let OutOperandList = (outs unknown:$dst); + let InOperandList = (ins variable_ops); + let AsmString = ""; + let hasSideEffects = 0; +} + + +//------------------------------------------------------------------------------ // Branches. //------------------------------------------------------------------------------ + // Generic unconditional branch. def G_BR : Instruction { let OutOperandList = (outs); Index: include/llvm/Target/TargetOpcodes.def =================================================================== --- include/llvm/Target/TargetOpcodes.def +++ include/llvm/Target/TargetOpcodes.def @@ -150,18 +150,43 @@ HANDLE_TARGET_OPCODE(G_ADD, 24) HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPCODE_START, G_ADD) -/// Generic Bitwise-OR instruction. -HANDLE_TARGET_OPCODE(G_OR, 25) +/// Generic ADD instruction, producing two results: the sum and a carry flag. +HANDLE_TARGET_OPCODE(G_ADDC, 25) + +/// Generic ADD instruction, consuming the normal operands plus a carry flag, +/// and similarly producing the result and a carry flag. +HANDLE_TARGET_OPCODE(G_ADDE, 26) /// Generic BRANCH instruction. This is an unconditional branch. -HANDLE_TARGET_OPCODE(G_BR, 26) +HANDLE_TARGET_OPCODE(G_BR, 27) + +/// Generic Bitwise-OR instruction. +HANDLE_TARGET_OPCODE(G_OR, 28) + +/// Generic extend instruction, where the high bits introduced take unspecified +/// values (any_ext). +HANDLE_TARGET_OPCODE(G_ANYEXTEND, 29) + +/// Generic instruction to extract a block of bits from the register given +/// (typically a sub-register COPY after instruction selection). +HANDLE_TARGET_OPCODE(G_EXTRACT, 30) + +/// Generic instruction to paste a variable number of components together into a +/// larger register. +HANDLE_TARGET_OPCODE(G_SEQUENCE, 31) + +/// Generic instruction to discard the high bits of a register. This differs +/// from (G_EXTRACT val, 0) on its action on vectors: G_TRUNCATE will truncate +/// each element individually, G_EXTRACT will typically discard the high +/// elements of the vector. +HANDLE_TARGET_OPCODE(G_TRUNCATE, 32) // TODO: Add more generic opcodes as we move along. /// Marker for the end of the generic opcode. /// This is used to check if an opcode is in the range of the /// generic opcodes. -HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPCODE_END, G_BR) +HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPCODE_END, G_TRUNCATE) /// BUILTIN_OP_END - This must be the last enum value in this list. /// The target-specific post-isel opcode values start here. Index: include/llvm/Target/TargetSubtargetInfo.h =================================================================== --- include/llvm/Target/TargetSubtargetInfo.h +++ include/llvm/Target/TargetSubtargetInfo.h @@ -27,6 +27,7 @@ class DataLayout; class MachineFunction; class MachineInstr; +class MachineLegalizeInfo; class RegisterBankInfo; class SDep; class SUnit; @@ -94,6 +95,10 @@ return nullptr; } + virtual const MachineLegalizeInfo *getMachineLegalizeInfo() const { + return nullptr; + } + /// getRegisterInfo - If register information is available, return it. If /// not, return null. /// Index: lib/CodeGen/GlobalISel/CMakeLists.txt =================================================================== --- lib/CodeGen/GlobalISel/CMakeLists.txt +++ lib/CodeGen/GlobalISel/CMakeLists.txt @@ -1,7 +1,9 @@ # List of all GlobalISel files. set(GLOBAL_ISEL_FILES IRTranslator.cpp + LegalizeMachineIR.cpp MachineIRBuilder.cpp + MachineLegalizeInfo.cpp RegBankSelect.cpp RegisterBank.cpp RegisterBankInfo.cpp Index: lib/CodeGen/GlobalISel/GlobalISel.cpp =================================================================== --- lib/CodeGen/GlobalISel/GlobalISel.cpp +++ lib/CodeGen/GlobalISel/GlobalISel.cpp @@ -25,6 +25,7 @@ void llvm::initializeGlobalISel(PassRegistry &Registry) { initializeIRTranslatorPass(Registry); + initializeLegalizeMachineIRPass(Registry); initializeRegBankSelectPass(Registry); } #endif // LLVM_BUILD_GLOBAL_ISEL Index: lib/CodeGen/GlobalISel/IRTranslator.cpp =================================================================== --- lib/CodeGen/GlobalISel/IRTranslator.cpp +++ lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -1,4 +1,4 @@ -//===-- llvm/CodeGen/GlobalISel/IRTranslator.cpp - IRTranslator --*- C++ -*-==// +//===--- lib/CodeGen/GlobalISel/IRTranslator.cpp - IRTranslator --*- C++ -*-==// // // The LLVM Compiler Infrastructure // Index: lib/CodeGen/GlobalISel/LegalizeMachineIR.cpp =================================================================== --- /dev/null +++ lib/CodeGen/GlobalISel/LegalizeMachineIR.cpp @@ -0,0 +1,186 @@ +//===-- llvm/CodeGen/GlobalISel/LegalizeMachineIR.cpp - LegalizeMachineIR -===// +// +// 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 the LegalizeHelper class to legalize individual +/// instructions and the LegalizeMachineIR wrapper pass for the primary +/// legalization. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/GlobalISel/LegalizeMachineIR.h" +#include "llvm/CodeGen/GlobalISel/MachineLegalizeInfo.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetSubtargetInfo.h" + +#define DEBUG_TYPE "legalize-mir" + +using namespace llvm; + +LegalizeHelper::LegalizeHelper(MachineFunction &MF) + : MLI(*MF.getSubtarget().getMachineLegalizeInfo()), MRI(MF.getRegInfo()) { + MIRBuilder.setMF(MF); +} + +void LegalizeHelper::extractParts(unsigned Reg, Type *Ty, int NumParts, + SmallVectorImpl &VRegs) { + unsigned Size = Ty->getPrimitiveSizeInBits(); + for (int i = 0; i < NumParts; ++i) { + unsigned PartReg = MRI.createGenericVirtualRegister(Size); + MIRBuilder.buildExtract(Ty, PartReg, Reg, i); + VRegs.push_back(PartReg); + } +} + +bool LegalizeHelper::legalizeInstr(MachineInstr &MI) { + auto Action = MLI.getAction(MI); + switch (Action.first) { + case MachineLegalizeInfo::Legal: + return false; + case MachineLegalizeInfo::NarrowScalar: + narrowScalar(MI, Action.second); + break; + case MachineLegalizeInfo::WidenScalar: + widenScalar(MI, Action.second); + break; + case MachineLegalizeInfo::FewerElements: + fewerElementsVector(MI, Action.second); + break; + default: + llvm_unreachable("don't know how to do that yet"); + } + + return true; +} + +void LegalizeHelper::narrowScalar(MachineInstr &MI, Type *NarrowTy) { + switch (MI.getOpcode()) { + default: + llvm_unreachable("Cannot legalize this instruction"); + case TargetOpcode::G_ADD: { + // Expand in terms of carry-setting/consuming G_ADDC/G_ADDE instructions. + unsigned NarrowSize = NarrowTy->getPrimitiveSizeInBits(); + int NumParts = MI.getType()->getPrimitiveSizeInBits() / NarrowSize; + + MIRBuilder.setInstr(MI, false); + + SmallVector Src1Regs, Src2Regs, DstRegs; + extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, Src1Regs); + extractParts(MI.getOperand(2).getReg(), NarrowTy, NumParts, Src2Regs); + + unsigned DstReg = MRI.createGenericVirtualRegister(NarrowSize); + unsigned CarryIn = MRI.createGenericVirtualRegister(1); + MIRBuilder.buildAddc(NarrowTy, DstReg, CarryIn, Src1Regs[0], Src2Regs[0]); + DstRegs.push_back(DstReg); + + for (int i = 1; i < NumParts; ++i) { + unsigned DstReg = MRI.createGenericVirtualRegister(NarrowSize); + unsigned CarryOut = MRI.createGenericVirtualRegister(1); + + MIRBuilder.buildAdde(NarrowTy, DstReg, CarryOut, + Src1Regs[i], Src2Regs[i], CarryIn); + + DstRegs.push_back(DstReg); + CarryIn = CarryOut; + } + MIRBuilder.buildSequence(MI.getType(), MI.getOperand(0).getReg(), DstRegs); + MI.eraseFromParent(); + break; + } + } +} + +void LegalizeHelper::widenScalar(MachineInstr &MI, Type *WideTy) { + switch (MI.getOpcode()) { + default: + llvm_unreachable("Cannot legalize this instruction"); + case TargetOpcode::G_ADD: { + // Perform operation at larger width (any extension is fine here, high bits + // don't affect the result) and then truncate the result back to the + // original type. + unsigned WideSize = WideTy->getPrimitiveSizeInBits(); + + MIRBuilder.setInstr(MI, false); + + unsigned Src1Ext = MRI.createGenericVirtualRegister(WideSize); + unsigned Src2Ext = MRI.createGenericVirtualRegister(WideSize); + MIRBuilder.buildAnyExtend(WideTy, Src1Ext, MI.getOperand(1).getReg()); + MIRBuilder.buildAnyExtend(WideTy, Src2Ext, MI.getOperand(2).getReg()); + + unsigned DstExt = MRI.createGenericVirtualRegister(WideSize); + MIRBuilder.buildAdd(WideTy, DstExt, Src1Ext, Src2Ext); + + MIRBuilder.buildTruncate(MI.getType(), MI.getOperand(0).getReg(), DstExt); + MI.eraseFromParent(); + break; + } + } +} + +void LegalizeHelper::fewerElementsVector(MachineInstr &MI, Type *NarrowTy) { + switch (MI.getOpcode()) { + default: + llvm_unreachable("Cannot legalize this instruction"); + case TargetOpcode::G_ADD: { + unsigned NarrowSize = NarrowTy->getPrimitiveSizeInBits(); + int NumParts = MI.getType()->getPrimitiveSizeInBits() / NarrowSize; + + MIRBuilder.setInstr(MI, false); + + SmallVector Src1Regs, Src2Regs, DstRegs; + extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, Src1Regs); + extractParts(MI.getOperand(2).getReg(), NarrowTy, NumParts, Src2Regs); + + for (int i = 0; i < NumParts; ++i) { + unsigned DstReg = MRI.createGenericVirtualRegister(NarrowSize); + MIRBuilder.buildAdd(NarrowTy, DstReg, Src1Regs[i], Src2Regs[i]); + DstRegs.push_back(DstReg); + } + + MIRBuilder.buildSequence(MI.getType(), MI.getOperand(0).getReg(), DstRegs); + MI.eraseFromParent(); + break; + } + } +} + +char LegalizeMachineIR::ID = 0; +INITIALIZE_PASS(LegalizeMachineIR, "legalize-mir", + "Legalize the Machine IR a function's Machine IR", false, + false); + +LegalizeMachineIR::LegalizeMachineIR() : MachineFunctionPass(ID) { + initializeLegalizeMachineIRPass(*PassRegistry::getPassRegistry()); +} + +void LegalizeMachineIR::init(MachineFunction &MF) { +} + +bool LegalizeMachineIR::runOnMachineFunction(MachineFunction &MF) { + DEBUG(dbgs() << "Legalize Machine IR for: " << MF.getName() << '\n'); + init(MF); + LegalizeHelper Helper(MF); + + // FIXME: an instruction may need more than one pass before it is legal. For + // example on most architectures <3 x i3> is doubly-illegal. It would + // typically proceed along a path like: <3 x i3> -> <3 x i8> -> <8 x i8>. We + // probably want a worklist of instructions rather than naive iterate until + // convergence for performance reasons. + bool Changed = false; + MachineBasicBlock::iterator NextMI; + for (auto &MBB : MF) + for (auto MI = MBB.begin(); MI != MBB.end(); MI = NextMI) { + // Get the next Instruction before we try to legalize, because there's a + // good chance MI will be deleted. + NextMI = std::next(MI); + Changed |= Helper.legalizeInstr(*MI); + } + + return Changed; +} Index: lib/CodeGen/GlobalISel/MachineIRBuilder.cpp =================================================================== --- lib/CodeGen/GlobalISel/MachineIRBuilder.cpp +++ lib/CodeGen/GlobalISel/MachineIRBuilder.cpp @@ -66,6 +66,7 @@ assert(!isPreISelGenericOpcode(Opcode) && "Generic instruction must have a type"); getMBB().insert(getInsertPt(), NewMI); + setInstr(*NewMI, false); return NewMI; } @@ -102,3 +103,71 @@ MachineInstrBuilder(getMF(), NewMI).addMBB(&BB); return NewMI; } + +MachineInstr *MachineIRBuilder::buildAdd(Type *Ty, unsigned Res, unsigned Op0, + unsigned Op1) { + return buildInstr(TargetOpcode::G_ADD, Ty, Res, Op0, Op1); +} + +MachineInstr *MachineIRBuilder::buildAddc(Type *Ty, unsigned Res, + unsigned CarryOut, unsigned Op0, + unsigned Op1) { + MachineInstr *NewMI = buildInstr(TargetOpcode::G_ADDC, Ty); + MachineInstrBuilder(getMF(), NewMI) + .addReg(Res, RegState::Define) + .addReg(CarryOut, RegState::Define) + .addReg(Op0) + .addReg(Op1); + return NewMI; +} + +MachineInstr *MachineIRBuilder::buildAdde(Type *Ty, unsigned Res, + unsigned CarryOut, unsigned Op0, + unsigned Op1, unsigned CarryIn) { + MachineInstr *NewMI = buildInstr(TargetOpcode::G_ADDE, Ty); + MachineInstrBuilder(getMF(), NewMI) + .addReg(Res, RegState::Define) + .addReg(CarryOut, RegState::Define) + .addReg(Op0) + .addReg(Op1) + .addReg(CarryIn); + return NewMI; +} + +MachineInstr *MachineIRBuilder::buildAnyExtend(Type *Ty, unsigned Res, + unsigned Op) { + MachineInstr *NewMI = buildInstr(TargetOpcode::G_ANYEXTEND, Ty); + MachineInstrBuilder(getMF(), NewMI) + .addReg(Res, RegState::Define) + .addReg(Op); + return NewMI; +} + +MachineInstr *MachineIRBuilder::buildExtract(Type *Ty, unsigned Res, + unsigned Op, int Idx) { + MachineInstr *NewMI = buildInstr(TargetOpcode::G_EXTRACT, Ty); + MachineInstrBuilder(getMF(), NewMI) + .addReg(Res, RegState::Define) + .addReg(Op) + .addImm(Idx); + return NewMI; +} + +MachineInstr *MachineIRBuilder::buildSequence(Type *Ty, unsigned Res, + SmallVectorImpl &Ops) { + MachineInstr *NewMI = buildInstr(TargetOpcode::G_SEQUENCE, Ty); + auto MIB = MachineInstrBuilder(getMF(), NewMI); + MIB.addReg(Res, RegState::Define); + for (auto Op : Ops) + MIB.addReg(Op); + return NewMI; +} + +MachineInstr *MachineIRBuilder::buildTruncate(Type *Ty, unsigned Res, + unsigned Op) { + MachineInstr *NewMI = buildInstr(TargetOpcode::G_TRUNCATE, Ty); + MachineInstrBuilder(getMF(), NewMI) + .addReg(Res, RegState::Define) + .addReg(Op); + return NewMI; +} Index: lib/CodeGen/GlobalISel/MachineLegalizeInfo.cpp =================================================================== --- /dev/null +++ lib/CodeGen/GlobalISel/MachineLegalizeInfo.cpp @@ -0,0 +1,151 @@ +//===---- lib/CodeGen/GlobalISel/MachineLegalizeInfo.cpp - IRTranslator ----==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implement an interface to specify and query how an illegal operation on a +// given type should be expanded. +// +// Issues to be resolved: +// + Make it fast. +// + Support weird types like i3, <7 x i3>, ... +// + Operations with more than one type (ICMP, CMPXCHG, intrinsics, ...) +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/ValueTypes.h" +#include "llvm/CodeGen/GlobalISel/MachineLegalizeInfo.h" +#include "llvm/IR/Type.h" +#include "llvm/Target/TargetOpcodes.h" +using namespace llvm; + +MachineLegalizeInfo::MachineLegalizeInfo(LLVMContext &Ctx) : Ctx(Ctx) { + // FIXME: these two can be legalized to the fundamental load/store Jakob + // proposed. Once loads & stores are supported. + DefaultActions[TargetOpcode::G_ANYEXTEND] = Legal; + DefaultActions[TargetOpcode::G_TRUNCATE] = Legal; + + DefaultActions[TargetOpcode::G_ADD] = NarrowScalar; +} + +void MachineLegalizeInfo::computeTables() { + for (auto &Op : Actions) { + VectorType *Ty = dyn_cast(Op.first.second); + if (!Ty) + continue; + + auto &Entry = MaxLegalVectorElts[std::make_pair(Op.first.first, + Ty->getElementType())]; + Entry = std::max(Entry, Ty->getNumElements()); + } +} + +// FIXME: inefficient implementation for now. Without ComputeValueVTs we're +// probably going to need specialized lookup structures for various types before +// we have any hope of doing well with something like <13 x i3>. Even the common +// cases should do better than what we have now. +std::pair +MachineLegalizeInfo::getAction(unsigned Opcode, Type *Ty) const { + // These *have* to be implemented for now, they're the fundamental basis of + // how everything else is transformed. + + // FIXME: the long-term plan calls for expansion in terms of load/store (if + // they're not legal). + if (Opcode == TargetOpcode::G_SEQUENCE || Opcode == TargetOpcode::G_EXTRACT) + return std::make_pair(Legal, Ty); + + auto ActionIt = Actions.find(std::make_pair(Opcode, Ty)); + if (ActionIt != Actions.end()) + return std::make_pair(ActionIt->second, + findLegalType(Opcode, Ty, ActionIt->second)); + else if (!Ty->isVectorTy()) { + auto DefaultAction = DefaultActions.find(Opcode); + if (DefaultAction != DefaultActions.end() && DefaultAction->second == Legal) + return std::make_pair(Legal, Ty); + + assert(DefaultAction->second == NarrowScalar && "unexpected default"); + return std::make_pair(NarrowScalar, + findLegalType(Opcode, Ty, NarrowScalar)); + } + + VectorType *VTy = cast(Ty); + Type *EltTy = VTy->getElementType(); + unsigned NumElts = VTy->getNumElements(); + + auto ScalarAction = ScalarInVectorActions.find(std::make_pair(Opcode, EltTy)); + if (ScalarAction != ScalarInVectorActions.end() && + ScalarAction->second != Legal) + return std::make_pair(ScalarAction->second, + findLegalType(Opcode, EltTy, ScalarAction->second)); + else if (ScalarAction != ScalarInVectorActions.end()) { + assert(ScalarAction->second == FewerElements); + return std::make_pair(FewerElements, + findLegalType(Opcode, VTy, FewerElements)); + } + + // The element type is legal in principle, but the number of elements is + // wrong. + auto MaxLegalElts = MaxLegalVectorElts.lookup(std::make_pair(Opcode, EltTy)); + if (MaxLegalElts > NumElts) + return std::make_pair(MoreElements, VectorType::get(EltTy, MaxLegalElts)); + else if (MaxLegalElts == 0) + return std::make_pair(FewerElements, EltTy); + + return std::make_pair(FewerElements, findLegalType(Opcode, Ty, FewerElements)); +} + +std::pair +MachineLegalizeInfo::getAction(MachineInstr &MI) const { + return getAction(MI.getOpcode(), MI.getType()); +} + +bool MachineLegalizeInfo::isLegal(MachineInstr &MI) const { + return getAction(MI).first == Legal; +} + +Type *MachineLegalizeInfo::findLegalType(unsigned Opcode, Type *Ty, + LegalizeAction Action) const { + switch(Action) { + default: + llvm_unreachable("Cannot find legal type"); + case Legal: + return Ty; + case NarrowScalar: { + assert(Ty->isIntegerTy()); + unsigned Width = Ty->getPrimitiveSizeInBits(); + return findLegalType(Opcode, Ty, [&](Type *Ty) -> Type * { + Width /= 2; + return IntegerType::get(Ctx, Width); + }); + } + case WidenScalar: { + assert(Ty->isIntegerTy()); + unsigned Width = Ty->getPrimitiveSizeInBits(); + return findLegalType(Opcode, Ty, [&](Type *Ty) -> Type * { + Width *= 2; + return IntegerType::get(Ctx, Width); + }); + } + case FewerElements: { + Type *EltTy = Ty->getVectorElementType(); + unsigned NumElts = Ty->getVectorNumElements(); + return findLegalType(Opcode, Ty, [&](Type *Ty) -> Type * { + NumElts /= 2; + return VectorType::get(EltTy, NumElts); + }); + } + case MoreElements: { + Type *EltTy = Ty->getVectorElementType(); + unsigned NumElts = Ty->getVectorNumElements(); + return findLegalType(Opcode, Ty, [&](Type *Ty) -> Type * { + NumElts *= 2; + return VectorType::get(EltTy, NumElts); + }); + } + } +} Index: lib/CodeGen/LLVMTargetMachine.cpp =================================================================== --- lib/CodeGen/LLVMTargetMachine.cpp +++ lib/CodeGen/LLVMTargetMachine.cpp @@ -160,6 +160,8 @@ if (PassConfig->addIRTranslator()) return nullptr; + PassConfig->addLegalizeMachineIR(); + // Before running the register bank selector, ask the target if it // wants to run some passes. PassConfig->addPreRegBankSelect(); Index: lib/Target/AArch64/AArch64MachineLegalizeInfo.h =================================================================== --- /dev/null +++ lib/Target/AArch64/AArch64MachineLegalizeInfo.h @@ -0,0 +1,30 @@ +//===- AArch64MachineLegalizeInfo --------------------------------*- 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 declares the targeting of the MachineLegalizeInfo class for +/// AArch64. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_AARCH64_AARCH64MACHINELEGALIZEINFO_H +#define LLVM_LIB_TARGET_AARCH64_AARCH64MACHINELEGALIZEINFO_H + +#include "llvm/CodeGen/GlobalISel/MachineLegalizeInfo.h" + +namespace llvm { + +class LLVMContext; + +/// This class provides the information for the target register banks. +class AArch64MachineLegalizeInfo : public MachineLegalizeInfo { +public: + AArch64MachineLegalizeInfo(LLVMContext &Ctx); +}; +} // End llvm namespace. +#endif Index: lib/Target/AArch64/AArch64MachineLegalizeInfo.cpp =================================================================== --- /dev/null +++ lib/Target/AArch64/AArch64MachineLegalizeInfo.cpp @@ -0,0 +1,42 @@ +//===- AArch64MachineLegalizeInfo.cpp -------------------------------*- 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 the targeting of the MachineLegalizeInfo class for +/// AArch64. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "AArch64MachineLegalizeInfo.h" +#include "llvm/CodeGen/ValueTypes.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/Target/TargetOpcodes.h" + +using namespace llvm; + +#ifndef LLVM_BUILD_GLOBAL_ISEL +#error "You shouldn't build this" +#endif + +AArch64MachineLegalizeInfo::AArch64MachineLegalizeInfo(LLVMContext &Ctx) + : MachineLegalizeInfo(Ctx) { + // We have 32-bit and 64-bit scalar adds... + setAction(TargetOpcode::G_ADD, MVT::i8, WidenScalar); + setAction(TargetOpcode::G_ADD, MVT::i16, WidenScalar); + setAction(TargetOpcode::G_ADD, MVT::i32, Legal); + setAction(TargetOpcode::G_ADD, MVT::i64, Legal); + + // ... and + setScalarInVectorAction(TargetOpcode::G_ADD, MVT::i8, Legal); + setScalarInVectorAction(TargetOpcode::G_ADD, MVT::i16, Legal); + setScalarInVectorAction(TargetOpcode::G_ADD, MVT::i32, Legal); + + setAction(TargetOpcode::G_ADD, MVT::v2i64, Legal); + computeTables(); +} Index: lib/Target/AArch64/AArch64Subtarget.h =================================================================== --- lib/Target/AArch64/AArch64Subtarget.h +++ lib/Target/AArch64/AArch64Subtarget.h @@ -145,6 +145,7 @@ return &getInstrInfo()->getRegisterInfo(); } const CallLowering *getCallLowering() const override; + const MachineLegalizeInfo *getMachineLegalizeInfo() const override; const RegisterBankInfo *getRegBankInfo() const override; const Triple &getTargetTriple() const { return TargetTriple; } bool enableMachineScheduler() const override { return true; } Index: lib/Target/AArch64/AArch64Subtarget.cpp =================================================================== --- lib/Target/AArch64/AArch64Subtarget.cpp +++ lib/Target/AArch64/AArch64Subtarget.cpp @@ -91,6 +91,11 @@ return GISel->getCallLowering(); } +const MachineLegalizeInfo *AArch64Subtarget::getMachineLegalizeInfo() const { + assert(GISel && "Access to GlobalISel APIs not set"); + return GISel->getMachineLegalizeInfo(); +} + const RegisterBankInfo *AArch64Subtarget::getRegBankInfo() const { assert(GISel && "Access to GlobalISel APIs not set"); return GISel->getRegBankInfo(); Index: lib/Target/AArch64/AArch64TargetMachine.cpp =================================================================== --- lib/Target/AArch64/AArch64TargetMachine.cpp +++ lib/Target/AArch64/AArch64TargetMachine.cpp @@ -12,11 +12,13 @@ #include "AArch64.h" #include "AArch64CallLowering.h" +#include "AArch64MachineLegalizeInfo.h" #include "AArch64RegisterBankInfo.h" #include "AArch64TargetMachine.h" #include "AArch64TargetObjectFile.h" #include "AArch64TargetTransformInfo.h" #include "llvm/CodeGen/GlobalISel/IRTranslator.h" +#include "llvm/CodeGen/GlobalISel/LegalizeMachineIR.h" #include "llvm/CodeGen/GlobalISel/RegBankSelect.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/RegAllocRegistry.h" @@ -195,10 +197,14 @@ namespace { struct AArch64GISelActualAccessor : public GISelAccessor { std::unique_ptr CallLoweringInfo; + std::unique_ptr MachineLegalizeInfo; std::unique_ptr RegBankInfo; const CallLowering *getCallLowering() const override { return CallLoweringInfo.get(); } + const class MachineLegalizeInfo *getMachineLegalizeInfo() const override { + return MachineLegalizeInfo.get(); + } const RegisterBankInfo *getRegBankInfo() const override { return RegBankInfo.get(); } @@ -233,6 +239,8 @@ new AArch64GISelActualAccessor(); GISel->CallLoweringInfo.reset( new AArch64CallLowering(*I->getTargetLowering())); + GISel->MachineLegalizeInfo.reset( + new AArch64MachineLegalizeInfo(F.getContext())); GISel->RegBankInfo.reset( new AArch64RegisterBankInfo(*I->getRegisterInfo())); #endif @@ -276,6 +284,7 @@ bool addInstSelector() override; #ifdef LLVM_BUILD_GLOBAL_ISEL bool addIRTranslator() override; + bool addLegalizeMachineIR() override; bool addRegBankSelect() override; #endif bool addILPOpts() override; @@ -375,6 +384,10 @@ addPass(new IRTranslator()); return false; } +bool AArch64PassConfig::addLegalizeMachineIR() { + addPass(new LegalizeMachineIR()); + return false; +} bool AArch64PassConfig::addRegBankSelect() { addPass(new RegBankSelect()); return false; Index: lib/Target/AArch64/CMakeLists.txt =================================================================== --- lib/Target/AArch64/CMakeLists.txt +++ lib/Target/AArch64/CMakeLists.txt @@ -17,6 +17,7 @@ # List of all GlobalISel files. set(GLOBAL_ISEL_FILES AArch64CallLowering.cpp + AArch64MachineLegalizeInfo.cpp AArch64RegisterBankInfo.cpp ) Index: test/CodeGen/AArch64/GlobalISel/legalize-add.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/legalize-add.mir @@ -0,0 +1,100 @@ +# RUN: llc -O0 -run-pass=legalize-mir -global-isel %s -o - 2>&1 | FileCheck %s +# REQUIRES: global-isel + +--- | + ; ModuleID = 'generic-virtual-registers-type-error.mir' + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + target triple = "aarch64-apple-ios" + define void @test_scalar_add_big() { + entry: + ret void + } + define void @test_scalar_add_small() { + entry: + ret void + } + define void @test_vector_add() { + entry: + ret void + } +... + +--- +# Check that we assign a relevant register bank for %0. +# Based on the type i32, this should be gpr. +name: test_scalar_add_big +isSSA: true +registers: + - { id: 0, class: _ } + - { id: 1, class: _ } + - { id: 2, class: _ } +body: | + bb.0.entry: + liveins: %x0, %x1, %x2, %x3 + ; CHECK-LABEL: name: test_scalar_add_big + ; CHECK-DAG: [[LHS_LO:%.*]](64) = G_EXTRACT i64 %0, 0 + ; CHECK-DAG: [[LHS_HI:%.*]](64) = G_EXTRACT i64 %0, 1 + ; CHECK-DAG: [[RHS_LO:%.*]](64) = G_EXTRACT i64 %1, 0 + ; CHECK-DAG: [[RHS_HI:%.*]](64) = G_EXTRACT i64 %1, 1 + ; CHECK: [[RES_LO:%.*]](64), [[CARRY:%.*]](1) = G_ADDC i64 [[LHS_LO]], [[RHS_LO]] + ; CHECK: [[RES_HI:%.*]](64), {{%.*}}(1) = G_ADDE i64 [[LHS_HI]], [[RHS_HI]], [[CARRY]] + ; CHECK: %2(128) = G_SEQUENCE i128 [[RES_LO]], [[RES_HI]] + + %0(128) = G_SEQUENCE i128 %x0, %x1 + %1(128) = G_SEQUENCE i128 %x2, %x3 + %2(128) = G_ADD i128 %0, %1 + %x0 = G_EXTRACT i64 %2, 0 + %x1 = G_EXTRACT i64 %2, 1 +... + +--- +# Check that we assign a relevant register bank for %0. +# Based on the type i32, this should be gpr. +name: test_scalar_add_small +isSSA: true +registers: + - { id: 0, class: _ } + - { id: 1, class: _ } + - { id: 2, class: _ } +body: | + bb.0.entry: + liveins: %x0, %x1, %x2, %x3 + ; CHECK-LABEL: name: test_scalar_add_small + ; CHECK-DAG: [[LHS:%.*]](32) = G_ANYEXTEND i32 %0 + ; CHECK-DAG: [[RHS:%.*]](32) = G_ANYEXTEND i32 %1 + ; CHECK: [[RES:%.*]](32) = G_ADD i32 [[LHS]], [[RHS]] + ; CHECK: %2(8) = G_TRUNCATE i8 [[RES]] + + %0(8) = G_TRUNCATE i8 %x0 + %1(8) = G_TRUNCATE i8 %x1 + %2(8) = G_ADD i8 %0, %1 + %x0 = G_ANYEXTEND i64 %2 +... + +--- +# Check that we assign a relevant register bank for %0. +# Based on the type i32, this should be gpr. +name: test_vector_add +isSSA: true +registers: + - { id: 0, class: _ } + - { id: 1, class: _ } + - { id: 2, class: _ } +body: | + bb.0.entry: + liveins: %q0, %q1, %q2, %q3 + ; CHECK-DAG: name: test_vector_add + ; CHECK-DAG: [[LHS_LO:%.*]](128) = G_EXTRACT <2 x i64> %0, 0 + ; CHECK-DAG: [[LHS_HI:%.*]](128) = G_EXTRACT <2 x i64> %0, 1 + ; CHECK-DAG: [[RHS_LO:%.*]](128) = G_EXTRACT <2 x i64> %1, 0 + ; CHECK-DAG: [[RHS_HI:%.*]](128) = G_EXTRACT <2 x i64> %1, 1 + ; CHECK: [[RES_LO:%.*]](128) = G_ADD <2 x i64> [[LHS_LO]], [[RHS_LO]] + ; CHECK: [[RES_HI:%.*]](128) = G_ADD <2 x i64> [[LHS_HI]], [[RHS_HI]] + ; CHECK: %2(256) = G_SEQUENCE <4 x i64> [[RES_LO]], [[RES_HI]] + + %0(256) = G_SEQUENCE <4 x i64> %q0, %q1 + %1(256) = G_SEQUENCE <4 x i64> %q2, %q3 + %2(256) = G_ADD <4 x i64> %0, %1 + %q0 = G_EXTRACT <2 x i64> %2, 0 + %q1 = G_EXTRACT <2 x i64> %2, 1 +... Index: test/TableGen/trydecode-emission.td =================================================================== --- test/TableGen/trydecode-emission.td +++ test/TableGen/trydecode-emission.td @@ -36,8 +36,8 @@ // CHECK: /* 0 */ MCD::OPC_ExtractField, 4, 4, // Inst{7-4} ... // CHECK-NEXT: /* 3 */ MCD::OPC_FilterValue, 0, 14, 0, // Skip to: 21 // CHECK-NEXT: /* 7 */ MCD::OPC_CheckField, 2, 2, 0, 5, 0, // Skip to: 18 -// CHECK-NEXT: /* 13 */ MCD::OPC_TryDecode, 28, 0, 0, 0, // Opcode: InstB, skip to: 18 -// CHECK-NEXT: /* 18 */ MCD::OPC_Decode, 27, 1, // Opcode: InstA +// CHECK-NEXT: /* 13 */ MCD::OPC_TryDecode, {{[0-9]+}}, 0, 0, 0, // Opcode: InstB, skip to: 18 +// CHECK-NEXT: /* 18 */ MCD::OPC_Decode, {{[0-9]+}}, 1, // Opcode: InstA // CHECK-NEXT: /* 21 */ MCD::OPC_Fail, // CHECK: if (DecodeInstB(MI, insn, Address, Decoder) == MCDisassembler::Fail) { DecodeComplete = false; return MCDisassembler::Fail; } Index: test/TableGen/trydecode-emission2.td =================================================================== --- test/TableGen/trydecode-emission2.td +++ test/TableGen/trydecode-emission2.td @@ -35,9 +35,9 @@ // CHECK-NEXT: /* 7 */ MCD::OPC_ExtractField, 5, 3, // Inst{7-5} ... // CHECK-NEXT: /* 10 */ MCD::OPC_FilterValue, 0, 22, 0, // Skip to: 36 // CHECK-NEXT: /* 14 */ MCD::OPC_CheckField, 0, 2, 3, 5, 0, // Skip to: 25 -// CHECK-NEXT: /* 20 */ MCD::OPC_TryDecode, 28, 0, 0, 0, // Opcode: InstB, skip to: 25 +// CHECK-NEXT: /* 20 */ MCD::OPC_TryDecode, {{[0-9]+}}, 0, 0, 0, // Opcode: InstB, skip to: 25 // CHECK-NEXT: /* 25 */ MCD::OPC_CheckField, 3, 2, 0, 5, 0, // Skip to: 36 -// CHECK-NEXT: /* 31 */ MCD::OPC_TryDecode, 27, 1, 0, 0, // Opcode: InstA, skip to: 36 +// CHECK-NEXT: /* 31 */ MCD::OPC_TryDecode, {{[0-9]+}}, 1, 0, 0, // Opcode: InstA, skip to: 36 // CHECK-NEXT: /* 36 */ MCD::OPC_Fail, // CHECK: if (DecodeInstB(MI, insn, Address, Decoder) == MCDisassembler::Fail) { DecodeComplete = false; return MCDisassembler::Fail; } Index: test/TableGen/trydecode-emission3.td =================================================================== --- test/TableGen/trydecode-emission3.td +++ test/TableGen/trydecode-emission3.td @@ -37,8 +37,8 @@ // CHECK: /* 0 */ MCD::OPC_ExtractField, 4, 4, // Inst{7-4} ... // CHECK-NEXT: /* 3 */ MCD::OPC_FilterValue, 0, 14, 0, // Skip to: 21 // CHECK-NEXT: /* 7 */ MCD::OPC_CheckField, 2, 2, 0, 5, 0, // Skip to: 18 -// CHECK-NEXT: /* 13 */ MCD::OPC_TryDecode, 28, 0, 0, 0, // Opcode: InstB, skip to: 18 -// CHECK-NEXT: /* 18 */ MCD::OPC_Decode, 27, 1, // Opcode: InstA +// CHECK-NEXT: /* 13 */ MCD::OPC_TryDecode, {{[0-9]+}}, 0, 0, 0, // Opcode: InstB, skip to: 18 +// CHECK-NEXT: /* 18 */ MCD::OPC_Decode, {{[0-9]+}}, 1, // Opcode: InstA // CHECK-NEXT: /* 21 */ MCD::OPC_Fail, // CHECK: if (DecodeInstBOp(MI, tmp, Address, Decoder) == MCDisassembler::Fail) { DecodeComplete = false; return MCDisassembler::Fail; }