Index: include/llvm/CodeGen/SelectionDAGISel.h =================================================================== --- include/llvm/CodeGen/SelectionDAGISel.h +++ include/llvm/CodeGen/SelectionDAGISel.h @@ -80,12 +80,12 @@ virtual SDNode *Select(SDNode *N) = 0; /// SelectInlineAsmMemoryOperand - Select the specified address as a target - /// addressing mode, according to the specified constraint code. If this does + /// addressing mode, according to the specified constraint. If this does /// not match or is not implemented, return true. The resultant operands /// (which will appear in the machine instruction) should be added to the /// OutOps vector. virtual bool SelectInlineAsmMemoryOperand(const SDValue &Op, - char ConstraintCode, + unsigned ConstraintID, std::vector &OutOps) { return true; } Index: include/llvm/IR/InlineAsm.h =================================================================== --- include/llvm/IR/InlineAsm.h +++ include/llvm/IR/InlineAsm.h @@ -189,7 +189,20 @@ // These are helper methods for dealing with flags in the INLINEASM SDNode // in the backend. - + // + // The encoding of the flag word is currently: + // Bit 31 - Set if the flags represent a matching operand. + // Clear if they represent a register class. + // If bits 2-0 are Kind_Mem: + // Bits 31 - 0 + // Bits 30-16 - A Constraint_* value representing the constraint code. + // Else if bit 31 is set: + // Bits 30-16 - The operand number to match. + // Else if bit 31 is clear: + // Bits 30-16 - The register class for the constraint. + // Bits 15-3 - The number of operands to the SDNode associated with this + // inline assembly operand. + // Bits 2-0 - The kind of operand (see the Kind_* values). enum : uint32_t { // Fixed operands on an INLINEASM SDNode. Op_InputChain = 0, @@ -220,6 +233,27 @@ Kind_Imm = 5, // Immediate. Kind_Mem = 6, // Memory operand, "m". + // Memory constraint codes. + // These could be tablegenerated but there's little need to do that since + // there's plenty of space in the encoding to support the union of all + // constraint codes. + Constraint_Unknown = 0, + Constraint_i, + Constraint_m, + Constraint_o, + Constraint_v, + Constraint_Q, + Constraint_R, + Constraint_S, + Constraint_T, + Constraint_X, + Constraint_Uv, + Constraint_Z, + Constraint_Zy, + Constraint_ZC, + Constraints_Max = Constraint_ZC, + Constraints_ShiftAmount = 16, + Flag_MatchingOperand = 0x80000000 }; @@ -252,6 +286,14 @@ return InputFlag | (RC << 16); } + /// Augment an existing flag word returned by getFlagWord with the constraint + /// code for a memory constraint. + static unsigned getFlagWordForMem(unsigned InputFlag, unsigned Constraint) { + assert(Constraint <= 0x7fff && "Too large memory constraint ID"); + assert((InputFlag & ~0xffff) == 0 && "High bits already contain data"); + return InputFlag | (Constraint << Constraints_ShiftAmount); + } + static unsigned getKind(unsigned Flags) { return Flags & 7; } @@ -265,6 +307,10 @@ static bool isClobberKind(unsigned Flag) { return getKind(Flag) == Kind_Clobber; } + static unsigned getMemoryConstraintID(unsigned Flag) { + assert(isMemKind(Flag)); + return (Flag >> Constraints_ShiftAmount) & 0x7fff; + } /// getNumOperandRegisters - Extract the number of registers field from the /// inline asm operand flag. Index: include/llvm/Target/TargetLowering.h =================================================================== --- include/llvm/Target/TargetLowering.h +++ include/llvm/Target/TargetLowering.h @@ -39,6 +39,7 @@ #include #include #include +#include "llvm/Support/Debug.h" namespace llvm { class CallInst; @@ -2594,6 +2595,18 @@ getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, const std::string &Constraint, MVT VT) const; + /// Convert a memory constraint (e.g. "m") into an InlineAsm::Constraint_* + /// value. + /// Unsupported constraints should return InlineAsm::Constraint_Unknown. + virtual unsigned getInlineAsmMemConstraint(const std::string Constraint) const { + if (Constraint == "m") return InlineAsm::Constraint_m; + if (Constraint == "o") return InlineAsm::Constraint_o; + if (Constraint == "v") return InlineAsm::Constraint_v; + if (Constraint == "X") return InlineAsm::Constraint_X; + errs() << "Unknown Constraint " << Constraint << "\n"; + return InlineAsm::Constraint_Unknown; + } + /// Try to replace an X constraint, which matches anything, with another that /// has more specific requirements based on the type of the corresponding /// operand. This returns null if there is no replacement to make. Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6592,10 +6592,15 @@ // Memory output, or 'other' output (e.g. 'X' constraint). assert(OpInfo.isIndirect && "Memory output must be indirect operand"); + unsigned ConstraintID = + TLI.getInlineAsmMemConstraint(OpInfo.ConstraintCode); + assert(ConstraintID != InlineAsm::Constraint_Unknown && + "Unknown memory constraint"); + // Add information to the INLINEASM node to know about this output. unsigned OpFlags = InlineAsm::getFlagWord(InlineAsm::Kind_Mem, 1); - AsmNodeOperands.push_back(DAG.getTargetConstant(OpFlags, - TLI.getPointerTy())); + OpFlags = InlineAsm::getFlagWordForMem(OpFlags, ConstraintID); + AsmNodeOperands.push_back(DAG.getTargetConstant(OpFlags, MVT::i32)); AsmNodeOperands.push_back(OpInfo.CallOperand); break; } @@ -6738,10 +6743,15 @@ assert(InOperandVal.getValueType() == TLI.getPointerTy() && "Memory operands expect pointer values"); + unsigned ConstraintID = + TLI.getInlineAsmMemConstraint(OpInfo.ConstraintCode); + assert(ConstraintID != InlineAsm::Constraint_Unknown && + "Unknown memory constraint"); + // Add information to the INLINEASM node to know about this input. unsigned ResOpType = InlineAsm::getFlagWord(InlineAsm::Kind_Mem, 1); - AsmNodeOperands.push_back(DAG.getTargetConstant(ResOpType, - TLI.getPointerTy())); + ResOpType = InlineAsm::getFlagWordForMem(ResOpType, ConstraintID); + AsmNodeOperands.push_back(DAG.getTargetConstant(ResOpType, MVT::i32)); AsmNodeOperands.push_back(InOperandVal); break; } Index: lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -1780,7 +1780,9 @@ "Memory operand with multiple values?"); // Otherwise, this is a memory operand. Ask the target to select it. std::vector SelOps; - if (SelectInlineAsmMemoryOperand(InOps[i+1], 'm', SelOps)) + if (SelectInlineAsmMemoryOperand(InOps[i+1], + InlineAsm::getMemoryConstraintID(Flags), + SelOps)) report_fatal_error("Could not match memory address. Inline asm" " failure!"); Index: lib/Target/AArch64/AArch64ISelDAGToDAG.cpp =================================================================== --- lib/Target/AArch64/AArch64ISelDAGToDAG.cpp +++ lib/Target/AArch64/AArch64ISelDAGToDAG.cpp @@ -65,7 +65,7 @@ /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for /// inline asm expressions. bool SelectInlineAsmMemoryOperand(const SDValue &Op, - char ConstraintCode, + unsigned ConstraintID, std::vector &OutOps) override; SDNode *SelectMLAV64LaneV128(SDNode *N); @@ -211,8 +211,10 @@ } bool AArch64DAGToDAGISel::SelectInlineAsmMemoryOperand( - const SDValue &Op, char ConstraintCode, std::vector &OutOps) { - assert(ConstraintCode == 'm' && "unexpected asm memory constraint"); + const SDValue &Op, unsigned ConstraintCode, std::vector &OutOps) { + // FIXME: All memory constraints currently behave like 'm'. + // This is presumably wrong but it preserves the existing behaviour. + // Require the address to be in a register. That is safe for all AArch64 // variants and it is hard to do anything much smarter without knowing // how the operand is used. Index: lib/Target/AArch64/AArch64ISelLowering.h =================================================================== --- lib/Target/AArch64/AArch64ISelLowering.h +++ lib/Target/AArch64/AArch64ISelLowering.h @@ -461,6 +461,12 @@ std::vector &Ops, SelectionDAG &DAG) const override; + unsigned getInlineAsmMemConstraint(const std::string Constraint) const override { + if (Constraint == "Q") + return InlineAsm::Constraint_Q; + return TargetLowering::getInlineAsmMemConstraint(Constraint); + } + bool isUsedByReturnOnly(SDNode *N, SDValue &Chain) const override; bool mayBeEmittedAsTailCall(CallInst *CI) const override; bool getIndexedAddressParts(SDNode *Op, SDValue &Base, SDValue &Offset, Index: lib/Target/ARM/ARMISelDAGToDAG.cpp =================================================================== --- lib/Target/ARM/ARMISelDAGToDAG.cpp +++ lib/Target/ARM/ARMISelDAGToDAG.cpp @@ -257,7 +257,7 @@ /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for /// inline asm expressions. - bool SelectInlineAsmMemoryOperand(const SDValue &Op, char ConstraintCode, + bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) override; // Form pairs of consecutive R, S, D, or Q registers. @@ -3472,9 +3472,11 @@ bool ARMDAGToDAGISel:: -SelectInlineAsmMemoryOperand(const SDValue &Op, char ConstraintCode, +SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) { - assert(ConstraintCode == 'm' && "unexpected asm memory constraint"); + // FIXME: All memory constraints currently behave like 'm'. + // This is presumably wrong but it preserves the existing behaviour. + // Require the address to be in a register. That is safe for all ARM // variants and it is hard to do anything much smarter without knowing // how the operand is used. Index: lib/Target/ARM/ARMISelLowering.h =================================================================== --- lib/Target/ARM/ARMISelLowering.h +++ lib/Target/ARM/ARMISelLowering.h @@ -340,6 +340,14 @@ const std::string &Constraint, MVT VT) const override; + unsigned getInlineAsmMemConstraint(const std::string Constraint) const override { + if (Constraint == "Q") + return InlineAsm::Constraint_Q; + if (Constraint == "Uv") + return InlineAsm::Constraint_Uv; + return TargetLowering::getInlineAsmMemConstraint(Constraint); + } + /// LowerAsmOperandForConstraint - Lower the specified operand into the Ops /// vector. If it is invalid, don't add anything to Ops. If hasMemory is /// true it means one of the asm constraint of the inline asm instruction Index: lib/Target/Hexagon/HexagonISelDAGToDAG.cpp =================================================================== --- lib/Target/Hexagon/HexagonISelDAGToDAG.cpp +++ lib/Target/Hexagon/HexagonISelDAGToDAG.cpp @@ -95,7 +95,7 @@ /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for /// inline asm expressions. bool SelectInlineAsmMemoryOperand(const SDValue &Op, - char ConstraintCode, + unsigned ConstraintID, std::vector &OutOps) override; bool SelectAddr(SDNode *Op, SDValue Addr, SDValue &Base, SDValue &Offset); @@ -1529,15 +1529,16 @@ bool HexagonDAGToDAGISel:: -SelectInlineAsmMemoryOperand(const SDValue &Op, char ConstraintCode, +SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) { + // FIXME: All memory constraints currently behave like 'm'. + // This is presumably wrong but it preserves the existing behaviour. + SDValue Op0, Op1; - switch (ConstraintCode) { - case 'o': // Offsetable. - case 'v': // Not offsetable. - default: return true; - case 'm': // Memory. + switch (ConstraintID) { + default: + case InlineAsm::Constraint_m: // Memory. if (!SelectAddr(Op.getNode(), Op, Op0, Op1)) return true; break; Index: lib/Target/MSP430/MSP430ISelDAGToDAG.cpp =================================================================== --- lib/Target/MSP430/MSP430ISelDAGToDAG.cpp +++ lib/Target/MSP430/MSP430ISelDAGToDAG.cpp @@ -104,7 +104,7 @@ bool MatchWrapper(SDValue N, MSP430ISelAddressMode &AM); bool MatchAddressBase(SDValue N, MSP430ISelAddressMode &AM); - bool SelectInlineAsmMemoryOperand(const SDValue &Op, char ConstraintCode, + bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) override; // Include the pieces autogenerated from the target description. @@ -280,12 +280,14 @@ } bool MSP430DAGToDAGISel:: -SelectInlineAsmMemoryOperand(const SDValue &Op, char ConstraintCode, +SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) { + // FIXME: All memory constraints currently behave like 'm'. + // This is presumably wrong but it preserves the existing behaviour. SDValue Op0, Op1; - switch (ConstraintCode) { - default: return true; - case 'm': // memory + switch (ConstraintID) { + default: + case InlineAsm::Constraint_m: // memory if (!SelectAddr(Op, Op0, Op1)) return true; break; Index: lib/Target/Mips/MipsAsmPrinter.cpp =================================================================== --- lib/Target/Mips/MipsAsmPrinter.cpp +++ lib/Target/Mips/MipsAsmPrinter.cpp @@ -438,7 +438,7 @@ // Print out an operand for an inline asm expression. bool MipsAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, - unsigned AsmVariant,const char *ExtraCode, + unsigned AsmVariant, const char *ExtraCode, raw_ostream &O) { // Does this asm operand have a single letter operand modifier? if (ExtraCode && ExtraCode[0]) { @@ -540,18 +540,23 @@ unsigned OpNum, unsigned AsmVariant, const char *ExtraCode, raw_ostream &O) { - int Offset = 0; + const MachineOperand &BaseMO = MI->getOperand(OpNum); + const MachineOperand &OffsetMO = MI->getOperand(OpNum+1); + assert(OffsetMO.isImm() && "unexpected inline asm memory operand"); + int Offset = OffsetMO.getImm(); + // Currently we are expecting either no ExtraCode or 'D' if (ExtraCode) { if (ExtraCode[0] == 'D') - Offset = 4; + Offset += 4; + // FIXME: M = high order + // FIXME: L = low order else return true; // Unknown modifier. } - const MachineOperand &MO = MI->getOperand(OpNum); - assert(MO.isReg() && "unexpected inline asm memory operand"); - O << Offset << "($" << MipsInstPrinter::getRegisterName(MO.getReg()) << ")"; + assert(BaseMO.isReg() && "unexpected inline asm memory operand"); + O << Offset << "($" << MipsInstPrinter::getRegisterName(BaseMO.getReg()) << ")"; return false; } Index: lib/Target/Mips/MipsISelDAGToDAG.h =================================================================== --- lib/Target/Mips/MipsISelDAGToDAG.h +++ lib/Target/Mips/MipsISelDAGToDAG.h @@ -125,7 +125,7 @@ virtual void processFunctionAfterISel(MachineFunction &MF) = 0; bool SelectInlineAsmMemoryOperand(const SDValue &Op, - char ConstraintCode, + unsigned ConstraintID, std::vector &OutOps) override; }; } Index: lib/Target/Mips/MipsISelDAGToDAG.cpp =================================================================== --- lib/Target/Mips/MipsISelDAGToDAG.cpp +++ lib/Target/Mips/MipsISelDAGToDAG.cpp @@ -230,9 +230,11 @@ } bool MipsDAGToDAGISel:: -SelectInlineAsmMemoryOperand(const SDValue &Op, char ConstraintCode, +SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) { - assert(ConstraintCode == 'm' && "unexpected asm memory constraint"); + // All memory constraints can at least manage plain addresses but + // MipsDAGToDAGISel subclasses should be more specific. OutOps.push_back(Op); + OutOps.push_back(CurDAG->getTargetConstant(0, MVT::i32)); return false; } Index: lib/Target/Mips/MipsISelLowering.h =================================================================== --- lib/Target/Mips/MipsISelLowering.h +++ lib/Target/Mips/MipsISelLowering.h @@ -21,6 +21,7 @@ #include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/SelectionDAG.h" #include "llvm/IR/Function.h" +#include "llvm/Support/Debug.h" #include "llvm/Target/TargetLowering.h" #include #include @@ -494,6 +495,14 @@ const std::string &Constraint, MVT VT) const override; + unsigned getInlineAsmMemConstraint(const std::string Constraint) const override { + if (Constraint == "R") + return InlineAsm::Constraint_R; + if (Constraint == "ZC") + return InlineAsm::Constraint_ZC; + return TargetLowering::getInlineAsmMemConstraint(Constraint); + } + /// LowerAsmOperandForConstraint - Lower the specified operand into the Ops /// vector. If it is invalid, don't add anything to Ops. If hasMemory is /// true it means one of the asm constraint of the inline asm instruction Index: lib/Target/Mips/MipsISelLowering.cpp =================================================================== --- lib/Target/Mips/MipsISelLowering.cpp +++ lib/Target/Mips/MipsISelLowering.cpp @@ -3163,6 +3163,10 @@ return C_Memory; } } + + if (Constraint == "ZC") + return C_Memory; + return TargetLowering::getConstraintType(Constraint); } Index: lib/Target/Mips/MipsSEISelDAGToDAG.h =================================================================== --- lib/Target/Mips/MipsSEISelDAGToDAG.h +++ lib/Target/Mips/MipsSEISelDAGToDAG.h @@ -111,6 +111,10 @@ // Insert instructions to initialize the global base register in the // first MBB of the function. void initGlobalBaseReg(MachineFunction &MF); + + bool SelectInlineAsmMemoryOperand(const SDValue &Op, + unsigned ConstraintID, + std::vector &OutOps) override; }; FunctionPass *createMipsSEISelDag(MipsTargetMachine &TM); Index: lib/Target/Mips/MipsSEISelDAGToDAG.cpp =================================================================== --- lib/Target/Mips/MipsSEISelDAGToDAG.cpp +++ lib/Target/Mips/MipsSEISelDAGToDAG.cpp @@ -916,6 +916,35 @@ return std::make_pair(false, nullptr); } +bool MipsSEDAGToDAGISel:: +SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, + std::vector &OutOps) { + SDValue Base, Offset, Alias; + + switch (ConstraintID) { + case InlineAsm::Constraint_ZC: + // FIXME: This implementation is actually wrong but it's good enough to test + // that constraints other than 'm' work. + // It should be selecting between different sized immediates according to + // the subtarget. + if (this->selectAddrRegImm12(Op, Base, Offset)) { + OutOps.push_back(Base); + OutOps.push_back(Offset); + return false; + } + + // FIXME: All other memory constraints currently behave like 'm'. + // This is presumably wrong but it preserves the existing behaviour. + default: + case InlineAsm::Constraint_m: + break; + } + + OutOps.push_back(Op); + OutOps.push_back(CurDAG->getTargetConstant(0, MVT::i32)); + return false; +} + FunctionPass *llvm::createMipsSEISelDag(MipsTargetMachine &TM) { return new MipsSEDAGToDAGISel(TM); } Index: lib/Target/NVPTX/NVPTXISelDAGToDAG.h =================================================================== --- lib/Target/NVPTX/NVPTXISelDAGToDAG.h +++ lib/Target/NVPTX/NVPTXISelDAGToDAG.h @@ -48,7 +48,7 @@ const NVPTXSubtarget *Subtarget; bool SelectInlineAsmMemoryOperand(const SDValue &Op, - char ConstraintCode, + unsigned ConstraintID, std::vector &OutOps) override; private: // Include the pieces autogenerated from the target description. Index: lib/Target/NVPTX/NVPTXISelDAGToDAG.cpp =================================================================== --- lib/Target/NVPTX/NVPTXISelDAGToDAG.cpp +++ lib/Target/NVPTX/NVPTXISelDAGToDAG.cpp @@ -5044,12 +5044,13 @@ /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for /// inline asm expressions. bool NVPTXDAGToDAGISel::SelectInlineAsmMemoryOperand( - const SDValue &Op, char ConstraintCode, std::vector &OutOps) { + const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) { + // FIXME: All memory constraints currently behave like 'm'. + // This is presumably wrong but it preserves the existing behaviour. SDValue Op0, Op1; - switch (ConstraintCode) { + switch (ConstraintID) { default: - return true; - case 'm': // memory + case InlineAsm::Constraint_m: // memory if (SelectDirectAddr(Op, Op0)) { OutOps.push_back(Op0); OutOps.push_back(CurDAG->getTargetConstant(0, MVT::i32)); Index: lib/Target/PowerPC/PPCISelDAGToDAG.cpp =================================================================== --- lib/Target/PowerPC/PPCISelDAGToDAG.cpp +++ lib/Target/PowerPC/PPCISelDAGToDAG.cpp @@ -186,7 +186,7 @@ /// register can be improved, but it is wrong to substitute Reg+Reg for /// Reg in an asm, because the load or store opcode would have to change. bool SelectInlineAsmMemoryOperand(const SDValue &Op, - char ConstraintCode, + unsigned ConstraintID, std::vector &OutOps) override { // We need to make sure that this one operand does not end up in r0 // (because we might end up lowering this as 0(%op)). Index: lib/Target/PowerPC/PPCISelLowering.h =================================================================== --- lib/Target/PowerPC/PPCISelLowering.h +++ lib/Target/PowerPC/PPCISelLowering.h @@ -514,6 +514,14 @@ const std::string &Constraint, MVT VT) const override; + unsigned getInlineAsmMemConstraint(const std::string Constraint) const override { + if (Constraint == "Z") + return InlineAsm::Constraint_Z; + if (Constraint == "Zy") + return InlineAsm::Constraint_Zy; + return TargetLowering::getInlineAsmMemConstraint(Constraint); + } + /// getByValTypeAlignment - Return the desired alignment for ByVal aggregate /// function arguments in the caller parameter area. This is the actual /// alignment, not its logarithm. Index: lib/Target/Sparc/SparcISelDAGToDAG.cpp =================================================================== --- lib/Target/Sparc/SparcISelDAGToDAG.cpp +++ lib/Target/Sparc/SparcISelDAGToDAG.cpp @@ -50,7 +50,7 @@ /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for /// inline asm expressions. bool SelectInlineAsmMemoryOperand(const SDValue &Op, - char ConstraintCode, + unsigned ConstraintID, std::vector &OutOps) override; const char *getPassName() const override { @@ -195,12 +195,14 @@ /// inline asm expressions. bool SparcDAGToDAGISel::SelectInlineAsmMemoryOperand(const SDValue &Op, - char ConstraintCode, + unsigned ConstraintID, std::vector &OutOps) { + // FIXME: All memory constraints currently behave like 'm'. + // This is presumably wrong but it preserves the existing behaviour. SDValue Op0, Op1; - switch (ConstraintCode) { - default: return true; - case 'm': // memory + switch (ConstraintID) { + default: + case InlineAsm::Constraint_m: // memory if (!SelectADDRrr(Op, Op0, Op1)) SelectADDRri(Op, Op0, Op1); break; Index: lib/Target/SystemZ/SystemZISelDAGToDAG.cpp =================================================================== --- lib/Target/SystemZ/SystemZISelDAGToDAG.cpp +++ lib/Target/SystemZ/SystemZISelDAGToDAG.cpp @@ -328,7 +328,7 @@ // Override SelectionDAGISel. SDNode *Select(SDNode *Node) override; - bool SelectInlineAsmMemoryOperand(const SDValue &Op, char ConstraintCode, + bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) override; // Include the pieces autogenerated from the target description. @@ -1129,9 +1129,11 @@ bool SystemZDAGToDAGISel:: SelectInlineAsmMemoryOperand(const SDValue &Op, - char ConstraintCode, + unsigned ConstraintID, std::vector &OutOps) { - assert(ConstraintCode == 'm' && "Unexpected constraint code"); + // FIXME: All memory constraints currently behave like 'm'. + // This is presumably wrong but it preserves the existing behaviour. + // Accept addresses with short displacements, which are compatible // with Q, R, S and T. But keep the index operand for future expansion. SDValue Base, Disp, Index; Index: lib/Target/SystemZ/SystemZISelLowering.h =================================================================== --- lib/Target/SystemZ/SystemZISelLowering.h +++ lib/Target/SystemZ/SystemZISelLowering.h @@ -224,6 +224,19 @@ getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, const std::string &Constraint, MVT VT) const override; + + unsigned getInlineAsmMemConstraint(const std::string Constraint) const override { + if (Constraint == "Q") + return InlineAsm::Constraint_Q; + if (Constraint == "R") + return InlineAsm::Constraint_R; + if (Constraint == "S") + return InlineAsm::Constraint_S; + if (Constraint == "T") + return InlineAsm::Constraint_T; + return TargetLowering::getInlineAsmMemConstraint(Constraint); + } + TargetLowering::ConstraintType getConstraintType(const std::string &Constraint) const override; TargetLowering::ConstraintWeight Index: lib/Target/X86/X86ISelDAGToDAG.cpp =================================================================== --- lib/Target/X86/X86ISelDAGToDAG.cpp +++ lib/Target/X86/X86ISelDAGToDAG.cpp @@ -228,7 +228,7 @@ /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for /// inline asm expressions. bool SelectInlineAsmMemoryOperand(const SDValue &Op, - char ConstraintCode, + unsigned ConstraintID, std::vector &OutOps) override; void EmitSpecialCodeForMain(); @@ -2814,14 +2814,14 @@ } bool X86DAGToDAGISel:: -SelectInlineAsmMemoryOperand(const SDValue &Op, char ConstraintCode, +SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) { + // FIXME: All memory constraints currently behave like 'm'. + // This is presumably wrong but it preserves the existing behaviour. SDValue Op0, Op1, Op2, Op3, Op4; - switch (ConstraintCode) { - case 'o': // offsetable ?? - case 'v': // not offsetable ?? - default: return true; - case 'm': // memory + switch (ConstraintID) { + default: + case InlineAsm::Constraint_m: // memory if (!SelectAddr(nullptr, Op, Op0, Op1, Op2, Op3, Op4)) return true; break; Index: lib/Target/X86/X86ISelLowering.h =================================================================== --- lib/Target/X86/X86ISelLowering.h +++ lib/Target/X86/X86ISelLowering.h @@ -704,6 +704,14 @@ const std::string &Constraint, MVT VT) const override; + unsigned getInlineAsmMemConstraint(const std::string Constraint) const override { + // FIXME: Should 'i' really be handled here? It's not a memory constraint + // but tests fail if we don't handle it here. + if (Constraint == "i") + return InlineAsm::Constraint_i; + return TargetLowering::getInlineAsmMemConstraint(Constraint); + } + /// Return true if the addressing mode represented /// by AM is legal for this target, for a load/store of the specified type. bool isLegalAddressingMode(const AddrMode &AM, Type *Ty) const override; Index: lib/Target/XCore/XCoreISelDAGToDAG.cpp =================================================================== --- lib/Target/XCore/XCoreISelDAGToDAG.cpp +++ lib/Target/XCore/XCoreISelDAGToDAG.cpp @@ -65,7 +65,7 @@ // Complex Pattern Selectors. bool SelectADDRspii(SDValue Addr, SDValue &Base, SDValue &Offset); - bool SelectInlineAsmMemoryOperand(const SDValue &Op, char ConstraintCode, + bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) override; const char *getPassName() const override { @@ -108,12 +108,14 @@ } bool XCoreDAGToDAGISel:: -SelectInlineAsmMemoryOperand(const SDValue &Op, char ConstraintCode, +SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) { + // FIXME: All memory constraints currently behave like 'm'. + // This is presumably wrong but it preserves the existing behaviour. SDValue Reg; - switch (ConstraintCode) { - default: return true; - case 'm': // Memory. + switch (ConstraintID) { + default: + case InlineAsm::Constraint_m: // Memory. switch (Op.getOpcode()) { default: return true; case XCoreISD::CPRelativeWrapper: Index: test/CodeGen/Mips/inlineasm_constraint_ZC.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/inlineasm_constraint_ZC.ll @@ -0,0 +1,43 @@ +; RUN: llc -march=mipsel < %s | FileCheck %s + +@data = global [2048 x i32] zeroinitializer + +define void @ZC_no_offset(i32* %p) nounwind { +entry: + ; CHECK-LABEL: ZC_no_offset: + + ; Check ZC on a plain pointer + call void asm sideeffect "lw $$1, $0", "*^ZC,~{$1}"(i32* getelementptr inbounds ([2048 x i32]* @data, i32 0, i32 0)) + ; CHECK: lw $[[BASEPTR:[0-9]+]], %got(data)($1) + ; CHECK: #APP + ; CHECK: lw ${{[0-9]+}}, 0($[[BASEPTR]]) + ; CHECK: #NO_APP + ret void +} + +define void @ZC_in_range_offset(i32* %p) nounwind { +entry: + ; CHECK-LABEL: ZC_in_range_offset: + + ; Check ZC on an offset pointer + call void asm sideeffect "lw $$1, $0", "*^ZC,~{$1}"(i32* getelementptr inbounds ([2048 x i32]* @data, i32 0, i32 1)) + ; CHECK: lw $[[BASEPTR:[0-9]+]], %got(data)($1) + ; CHECK: #APP + ; CHECK: lw ${{[0-9]+}}, 4($[[BASEPTR]]) + ; CHECK: #NO_APP + ret void +} + +define void @ZC_out_of_range_offset(i32* %p) nounwind { +entry: + ; CHECK-LABEL: ZC_out_of_range_offset: + + ; Check ZC on an offset pointer + call void asm sideeffect "lw $$1, $0", "*^ZC,~{$1}"(i32* getelementptr inbounds ([2048 x i32]* @data, i32 0, i32 1024)) + ; CHECK: lw $[[BASEPTR:[0-9]+]], %got(data)($1) + ; CHECK: addiu $[[PTR:[0-9]+]], $[[BASEPTR]], 4096 + ; CHECK: #APP + ; CHECK: lw ${{[0-9]+}}, 0($[[PTR]]) + ; CHECK: #NO_APP + ret void +}