Index: include/llvm/CodeGen/AsmPrinter.h =================================================================== --- include/llvm/CodeGen/AsmPrinter.h +++ include/llvm/CodeGen/AsmPrinter.h @@ -48,6 +48,7 @@ class GlobalObject; class GlobalValue; class GlobalVariable; +class InlineAsmDataflowChecker; class MachineBasicBlock; class MachineConstantPoolValue; class MachineDominatorTree; @@ -625,7 +626,8 @@ EmitInlineAsm(StringRef Str, const MCSubtargetInfo &STI, const MCTargetOptions &MCOptions, const MDNode *LocMDNode = nullptr, - InlineAsm::AsmDialect AsmDialect = InlineAsm::AD_ATT) const; + InlineAsm::AsmDialect AsmDialect = InlineAsm::AD_ATT, + InlineAsmDataflowChecker *Checker = nullptr) const; /// This method formats and emits the specified machine instruction that is an /// inline asm. Index: include/llvm/IR/InlineAsm.h =================================================================== --- include/llvm/IR/InlineAsm.h +++ include/llvm/IR/InlineAsm.h @@ -272,6 +272,11 @@ return Kind | (NumOps << 3); } + static bool isRegKind(unsigned Flag) { + return getKind(Flag) >= Kind_RegUse && + getKind(Flag) <= Kind_RegDefEarlyClobber; + } + static bool isRegUseKind(unsigned Flag){ return getKind(Flag) == Kind_RegUse;} static bool isRegDefKind(unsigned Flag){ return getKind(Flag) == Kind_RegDef;} static bool isImmKind(unsigned Flag) { return getKind(Flag) == Kind_Imm; } static bool isMemKind(unsigned Flag) { return getKind(Flag) == Kind_Mem; } Index: include/llvm/MC/MCInst.h =================================================================== --- include/llvm/MC/MCInst.h +++ include/llvm/MC/MCInst.h @@ -42,6 +42,7 @@ kInst ///< Sub-instruction operand. }; MachineOperandType Kind = kInvalid; + unsigned ParsedOperandNum = ~0U; union { unsigned RegVal; @@ -113,6 +114,14 @@ InstVal = Val; } + unsigned getParsedOpNum() const { + return ParsedOperandNum; + } + + void setParsedOpNum(unsigned Num) { + ParsedOperandNum = Num; + } + static MCOperand createReg(unsigned Reg) { MCOperand Op; Op.Kind = kRegister; Index: include/llvm/MC/MCInstrDesc.h =================================================================== --- include/llvm/MC/MCInstrDesc.h +++ include/llvm/MC/MCInstrDesc.h @@ -596,6 +596,25 @@ /// register, either explicitly or implicitly. bool hasDefOfPhysReg(const MCInst &MI, unsigned Reg, const MCRegisterInfo &RI) const; + + // Returns true if the operand at the given index is a definition. + bool operandIsDef(unsigned OpIdx) const { + // Regular definition. + if (OpIdx < NumDefs) + return true; + + // Optional definition. + if (OpIdx < NumOperands && + OpInfo[OpIdx].isOptionalDef()) + return true; + + // Varidaic definition. + if (OpIdx >= (NumOperands - 1) && variadicOpsAreDefs()) + return true; + + // Everything else is a use. + return false; + } }; } // end namespace llvm Index: include/llvm/MC/MCParser/MCInlineAsmDataflowChecker.h =================================================================== --- /dev/null +++ include/llvm/MC/MCParser/MCInlineAsmDataflowChecker.h @@ -0,0 +1,317 @@ +//===- llvm/MC/MCInlineAsmDataflowChecker.h ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// TODO +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MC_MCINLINEASMDATAFLOWCHECKER_H +#define LLVM_MC_MCINLINEASMDATAFLOWCHECKER_H + +#include "llvm/ADT/IndexedMap.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCInstrDesc.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/SMLoc.h" +#include "llvm/Support/SourceMgr.h" + +namespace llvm { + +class MCParsedAsmOperand; +class MCInstrInfo; +class MCTargetAsmParser; + +using OperandVector = SmallVectorImpl>; + +class InlineAsmDataflowChecker { + enum OperandKind { + Input, + Output, + InputOutput, + Memory, + Immediate, + Tied, + None, + }; + + static const char *getOperandKindName(OperandKind K) { + switch (K) { + case Input: return "Input"; + case Output: return "Output"; + case InputOutput: return "InputOutput"; + case Memory: return "Memory"; + case Immediate: return "Immediate"; + case Tied: return "Tied"; + case None: return "None"; + } + llvm_unreachable(""); + } + + struct OperandInfo { + OperandKind Kind; + bool IsEarlyClobber; + bool HasBeenWritten; + bool HasBeenWrittenInThisInst; + bool HasBeenRead; + unsigned TiedOpIdx; + bool AppearsInString; + bool PhysicalRegister; + + bool IsRegInput() const { return Kind == Input || Kind == InputOutput; } + bool IsRegOutput() const { return Kind == Output || Kind == InputOutput; } + bool IsTied() const { return TiedOpIdx != ~0U; } + + OperandInfo(OperandKind Kind, bool EC) + : Kind(Kind), IsEarlyClobber(EC), HasBeenWritten(false), + HasBeenWrittenInThisInst(false), HasBeenRead(false), TiedOpIdx(~0U), + AppearsInString(false), PhysicalRegister(false) {} + + OperandInfo(unsigned TiedIdx) + : Kind(Tied), IsEarlyClobber(false), HasBeenWritten(false), + HasBeenWrittenInThisInst(false), HasBeenRead(false), + TiedOpIdx(TiedIdx), AppearsInString(false), PhysicalRegister(false) {} + + void RecordRead() { + HasBeenRead = true; + } + void RecordWrite() { + if (IsEarlyClobber) + HasBeenWritten = true; + else + HasBeenWrittenInThisInst = true; + } + void FinishWrites() { + HasBeenWritten |= HasBeenWrittenInThisInst; + HasBeenWrittenInThisInst = false; + } + + void print(raw_ostream &OS) const { + OS << " <" << getOperandKindName(Kind); + if (IsTied()) + OS << " to " << TiedOpIdx; + if (IsEarlyClobber) + OS << " EC"; + if (HasBeenWritten) + OS << " Written"; + if (HasBeenWrittenInThisInst) + OS << " WrittenThisInst"; + if (HasBeenRead) + OS << " Read"; + OS << ">"; + } + }; + + struct PhysRegInfo { + unsigned RegNum; + + // If this is a physical register used in a constraint (exposed as register + // variables in C/C++), these are the indexes of the inline asm operands. + // Otherwise (and if only an input or output operand operand is used), + // these are -1. + unsigned InputOperandIdx; + unsigned OutputOperandIdx; + + bool HasBeenWritten; + + PhysRegInfo(unsigned RegNum) + : RegNum(RegNum), InputOperandIdx(~0U), OutputOperandIdx(~0U), + HasBeenWritten(false) {} + + bool IsClobber() const { + // We represent a clobber as a PhysRegInfo with neither operand index + // set. It doesn't make sense for clobbers a register which is used in a + // physical register constraint, and the frontend should reject that. + return InputOperandIdx == ~0U && OutputOperandIdx == ~0U; + } + + void dump(const MCRegisterInfo *RI) const { + dbgs() << "getName(RegNum); + if (InputOperandIdx != ~0U) + dbgs() << " input " << InputOperandIdx; + if (OutputOperandIdx != ~0U) + dbgs() << " output " << OutputOperandIdx; + if (IsClobber()) + dbgs() << " clobber"; + if (HasBeenWritten) + dbgs() << " written"; + dbgs() << ">"; + } + }; + + struct MCOpInfo { + MCPhysReg Reg; + bool IsDef; + bool IsEarlyClobber; + MCOpInfo(MCPhysReg Reg, bool IsDef, bool IsEarlyClobber) + : Reg(Reg), IsDef(IsDef), IsEarlyClobber(IsEarlyClobber) {} + }; + + struct OperandGroup { + SmallVector InlineAsmOps; + MCParsedAsmOperand *ParsedOp; + SMLoc ParsedOpLoc; + // Second element of the pair is true for defs, false for uses. + SmallVector MCOps; + }; + void checkOperandGroup(OperandGroup &G, SMLoc Loc); + + bool IsActive = false; + MCTargetAsmParser *TAP = nullptr; + const MCInstrInfo *MII = nullptr; + const MCRegisterInfo *RI = nullptr; + + // Has a non-earlyclobber inline asm output been written to? + bool OutputOperandWritten; + bool OutputOperandWrittenThisInst; + SMLoc FirstOutputLoc; + + void FinishWrites() { + for (unsigned i = 0; i < Operands.size(); ++i) + Operands[i].FinishWrites(); + OutputOperandWritten |= OutputOperandWrittenThisInst; + OutputOperandWrittenThisInst = false; + } + + bool MayLoad, MayStore; + + + // Info about operands to the inline asm block. + // TODO make IndexedMap iterable? + IndexedMap Operands; + // TODO This could be a map indexed by register number + SmallVector PhysRegs; + + OperandInfo &getOperand(unsigned OpNum) { + OperandInfo &Info = Operands[OpNum]; + if (Info.IsTied()) + return Operands[Info.TiedOpIdx]; + return Info; + } + + // Pair of (source offset, operand num), sorted by source offset. + SmallVector, 8> OperandLocs; + SMLoc BaseLoc; + + PhysRegInfo *FindPhysReg(unsigned Reg) { + for (auto &P : PhysRegs) + if (Reg == P.RegNum || (RI && RI->regsOverlap(Reg, P.RegNum))) + return &P; + return nullptr; + } + +public: + InlineAsmDataflowChecker() : + Operands(OperandInfo(None, false)) { + OutputOperandWritten = false; + OutputOperandWrittenThisInst = false; + MayLoad = MayStore = false; + FirstOutputLoc = SMLoc(); + } + + bool isActive() const { return IsActive; } + + // Before parsing + void addOutputOp(unsigned OpNum) { + DEBUG_WITH_TYPE("asm-dataflow", dbgs() << "Add output " << OpNum << "\n"); + Operands.grow(OpNum); + Operands[OpNum] = OperandInfo(Output, false); + } + void addOutputEarlyClobberOp(unsigned OpNum) { + DEBUG_WITH_TYPE("asm-dataflow", dbgs() + << "Add output EC " << OpNum << "\n"); + Operands.grow(OpNum); + Operands[OpNum] = OperandInfo(Output, true); + } + void addInputOp(unsigned OpNum) { + DEBUG_WITH_TYPE("asm-dataflow", dbgs() << "Add input " << OpNum << "\n"); + Operands.grow(OpNum); + Operands[OpNum] = OperandInfo(Input, false); + } + void addTiedOp(unsigned NewOpNum, unsigned TiedOpNum) { + DEBUG_WITH_TYPE("asm-dataflow", dbgs() << "Add tied " << NewOpNum << ", " + << TiedOpNum << "\n"); + Operands.grow(NewOpNum); + Operands[NewOpNum] = OperandInfo(TiedOpNum); + assert(Operands[TiedOpNum].Kind == Output); + Operands[TiedOpNum].Kind = InputOutput; + } + void addImmediateOp(unsigned OpNum) { + DEBUG_WITH_TYPE("asm-dataflow", dbgs() << "Add immediate " << OpNum << "\n"); + Operands.grow(OpNum); + Operands[OpNum] = OperandInfo(Immediate, false); + } + void addMemoryOp(unsigned OpNum) { + DEBUG_WITH_TYPE("asm-dataflow", dbgs() << "Add memory " << OpNum << "\n"); + Operands.grow(OpNum); + Operands[OpNum] = OperandInfo(Memory, false); + } + void addClobber(unsigned RegNum) { + DEBUG_WITH_TYPE("asm-dataflow", dbgs() << "Add clobber " << RegNum << "\n"); + PhysRegs.emplace_back(RegNum); + } + void addPhysRegOp(unsigned RegNum, unsigned OpNum) { + DEBUG_WITH_TYPE("asm-dataflow", dbgs() << "Add phys reg " << OpNum << ", " + << RegNum << "\n"); + getOperand(OpNum).PhysicalRegister = true; + if (!FindPhysReg(RegNum)) + PhysRegs.emplace_back(RegNum); + PhysRegInfo *P = FindPhysReg(RegNum); + assert(P && "Failed to add physical register!"); + if (getOperand(OpNum).IsRegInput()) { + assert(P->InputOperandIdx == ~0U); + P->InputOperandIdx = OpNum; + } + if (getOperand(OpNum).IsRegOutput()) { + assert(P->OutputOperandIdx == ~0U); + P->OutputOperandIdx = OpNum; + } + } + void addMayLoad() { MayLoad = true; } + void addMayStore() { MayStore = true; } + + // Record that inline assembly operand OpNum was used in the inlie assembly + // string, at offset Offset into the final assembly string. + void addOpLoc(unsigned Offset, unsigned OpNum) { + DEBUG_WITH_TYPE("asm-dataflow", + dbgs() << "Operand " << OpNum << " at offset " << Offset << "\n"); + assert(Operands.inBounds(OpNum)); + if (!OperandLocs.empty()) + assert(Offset >= OperandLocs.back().first); + OperandLocs.push_back(std::make_pair(Offset, OpNum)); + } + + unsigned getLocOffset(SMLoc Loc, SourceMgr &SrcMgr) { + if (SrcMgr.FindBufferContainingLoc(BaseLoc) != + SrcMgr.FindBufferContainingLoc(Loc)) + return ~0U; + return (unsigned)(Loc.getPointer() - BaseLoc.getPointer() + 1); + } + + void transferInlineAsmOps(MCParsedAsmOperand *Op, SMLoc EndLoc, SourceMgr &SrcMgr); + + void setBaseLoc(SMLoc Loc) { + BaseLoc = Loc; + } + + // While parsing + void start(MCTargetAsmParser *TAP, const MCInstrInfo *MII, + const MCRegisterInfo *RI, SMLoc BaseLoc); + void stop(); + void checkInst(MCInst &Inst, const OperandVector &Operands); + void checkEarlyClobberMCOps(MCInst &Inst, + const OperandVector &ParsedOperands); + void + iterateOperandGroups(const MCInst &Inst, const OperandVector &Operands, + std::function Callback); +}; + +} // namespace llvm + +#endif // LLVM_MC_MCINLINEASMDATAFLOWCHECKER_H Index: include/llvm/MC/MCParser/MCParsedAsmOperand.h =================================================================== --- include/llvm/MC/MCParser/MCParsedAsmOperand.h +++ include/llvm/MC/MCParser/MCParsedAsmOperand.h @@ -31,6 +31,10 @@ /// MS-style inline assembly. std::string Constraint; + /// Operand numbers of inline asm operands which were expanded then parsed + /// into this operand. + SmallVector InlineAsmOperands; + protected: // This only seems to need to be movable (by ARMOperand) but ARMOperand has // lots of members and MSVC doesn't support defaulted move ops, so to avoid @@ -41,6 +45,7 @@ MCParsedAsmOperand &operator=(const MCParsedAsmOperand &) = default; public: + virtual ~MCParsedAsmOperand() = default; void setConstraint(StringRef C) { Constraint = C.str(); } @@ -52,6 +57,11 @@ virtual StringRef getSymName() { return StringRef(); } virtual void *getOpDecl() { return nullptr; } + void addInlineAsmOpNum(unsigned OpNum) { InlineAsmOperands.push_back(OpNum); } + const SmallVectorImpl &getInlineAsmOpNums() const { + return InlineAsmOperands; + } + /// isToken - Is this a token operand? virtual bool isToken() const = 0; /// isImm - Is this an immediate operand? Index: include/llvm/MC/MCParser/MCTargetAsmParser.h =================================================================== --- include/llvm/MC/MCParser/MCTargetAsmParser.h +++ include/llvm/MC/MCParser/MCTargetAsmParser.h @@ -10,14 +10,20 @@ #ifndef LLVM_MC_MCPARSER_MCTARGETASMPARSER_H #define LLVM_MC_MCPARSER_MCTARGETASMPARSER_H +#include "llvm/ADT/IndexedMap.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringRef.h" +#include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCParsedAsmOperand.h" #include "llvm/MC/MCParser/MCAsmParserExtension.h" +#include "llvm/MC/MCParser/MCInlineAsmDataflowChecker.h" #include "llvm/MC/MCTargetOptions.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/SMLoc.h" +#include "llvm/Support/SourceMgr.h" #include #include @@ -352,6 +358,9 @@ const MCInstrInfo &MII; + // TODO this should probably use a std::unique_ptr to make ownership explicit. + InlineAsmDataflowChecker *InlineAsmDataflow; + public: MCTargetAsmParser(const MCTargetAsmParser &) = delete; MCTargetAsmParser &operator=(const MCTargetAsmParser &) = delete; @@ -490,6 +499,32 @@ MCContext &Ctx) { return nullptr; } + + // If true, this register can be used in inline assembly without it being an + // operand or clobber. For example, the stack pointer can used to create a + // new stack allocation (as long as it is reset before leaving the assembly + // block). + virtual bool isRegPredictableInInlineAsm(MCPhysReg Reg) const { + return false; + } + + InlineAsmDataflowChecker *getInlineAsmDataflow() const { + // Might be nullptr + return InlineAsmDataflow; + } + void startInlineAsmDataflow(InlineAsmDataflowChecker *Checker) { + assert(!InlineAsmDataflow && "already have one of those"); + assert(Checker); + InlineAsmDataflow = Checker; + Checker->start(this, &MII, getContext().getRegisterInfo(), + getParser().getTok().getLoc()); + } + void endInlineAsmDataflow() { + assert(InlineAsmDataflow && InlineAsmDataflow->isActive() && + "data-flow checking is not running"); + InlineAsmDataflow->stop(); + InlineAsmDataflow = nullptr; + } }; } // end namespace llvm Index: include/llvm/MC/MCRegisterInfo.h =================================================================== --- include/llvm/MC/MCRegisterInfo.h +++ include/llvm/MC/MCRegisterInfo.h @@ -470,6 +470,9 @@ bool isSuperOrSubRegisterEq(unsigned RegA, unsigned RegB) const { return isSubRegisterEq(RegA, RegB) || isSuperRegister(RegA, RegB); } + + /// Returns true if the two registers are equal or alias each other. + bool regsOverlap(unsigned regA, unsigned regB) const; }; //===----------------------------------------------------------------------===// Index: lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp @@ -111,7 +111,8 @@ void AsmPrinter::EmitInlineAsm(StringRef Str, const MCSubtargetInfo &STI, const MCTargetOptions &MCOptions, const MDNode *LocMDNode, - InlineAsm::AsmDialect Dialect) const { + InlineAsm::AsmDialect Dialect, + InlineAsmDataflowChecker *Checker) const { assert(!Str.empty() && "Can't emit empty inline asm block"); // Remember if the buffer is nul terminated or not so we can avoid a copy. @@ -165,11 +166,15 @@ TAP->SetFrameRegister(TRI->getFrameRegister(*MF)); } + if (Checker) + TAP->startInlineAsmDataflow(Checker); emitInlineAsmStart(); // Don't implicitly switch to the text section before the asm. int Res = Parser->Run(/*NoInitialTextSection*/ true, /*NoFinalize*/ true); emitInlineAsmEnd(STI, &TAP->getSTI()); + if (Checker) + TAP->endInlineAsmDataflow(); if (Res && !DiagInfo->DiagHandler) report_fatal_error("Error parsing inline asm\n"); @@ -290,10 +295,56 @@ OS << "\n\t.att_syntax\n" << (char)0; // null terminate string. } +static void InitChecker(const MachineInstr *MI, + InlineAsmDataflowChecker &Checker) { + unsigned OpNo = InlineAsm::MIOp_FirstOperand; + unsigned SeqOpNo = 0; + + unsigned ExtraInfo = MI->getOperand(InlineAsm::MIOp_ExtraInfo).getImm(); + if (ExtraInfo & InlineAsm::Extra_MayLoad) + Checker.addMayLoad(); + if (ExtraInfo & InlineAsm::Extra_MayStore) + Checker.addMayStore(); + + while (OpNo < MI->getNumOperands() && MI->getOperand(OpNo).isImm()) { + unsigned OpFlags = MI->getOperand(OpNo).getImm(); + unsigned TiedOpNum; + bool CheckPhysReg = false; + if (InlineAsm::isClobberKind(OpFlags)) { + Checker.addClobber(MI->getOperand(OpNo+1).getReg()); + } else if (InlineAsm::isUseOperandTiedToDef(OpFlags, TiedOpNum)) { + Checker.addTiedOp(SeqOpNo, TiedOpNum); + } else if (InlineAsm::isRegUseKind(OpFlags)) { + Checker.addInputOp(SeqOpNo); + CheckPhysReg = true; + } else if (InlineAsm::isRegDefKind(OpFlags)) { + Checker.addOutputOp(SeqOpNo); + CheckPhysReg = true; + } else if (InlineAsm::isRegDefEarlyClobberKind(OpFlags)) { + Checker.addOutputEarlyClobberOp(SeqOpNo); + CheckPhysReg = true; + } else if (InlineAsm::isImmKind(OpFlags)) { + Checker.addImmediateOp(SeqOpNo); + } else if (InlineAsm::isMemKind(OpFlags)) { + Checker.addMemoryOp(SeqOpNo); + } else { + llvm_unreachable("don't know how to handle this operand"); + } + + unsigned RC; + if (CheckPhysReg && !InlineAsm::hasRegClassConstraint(OpFlags, RC)) + Checker.addPhysRegOp(MI->getOperand(OpNo+1).getReg(), SeqOpNo); + + OpNo += InlineAsm::getNumOperandRegisters(OpFlags) + 1; + SeqOpNo++; + } +} + static void EmitGCCInlineAsmStr(const char *AsmStr, const MachineInstr *MI, MachineModuleInfo *MMI, int InlineAsmVariant, int AsmPrinterVariant, AsmPrinter *AP, - unsigned LocCookie, raw_ostream &OS) { + unsigned LocCookie, raw_svector_ostream &OS, + InlineAsmDataflowChecker &Checker) { int CurVariant = -1; // The number of the {.|.|.} region we are in. const char *LastEmitted = AsmStr; // One past the last character emitted. unsigned NumOperands = MI->getNumOperands(); @@ -419,7 +470,7 @@ bool Error = false; // Scan to find the machine operand number for the operand. - for (; Val; --Val) { + for (int i = Val; i; --i) { if (OpNo >= MI->getNumOperands()) break; unsigned OpFlags = MI->getOperand(OpNo).getImm(); OpNo += InlineAsm::getNumOperandRegisters(OpFlags) + 1; @@ -432,6 +483,8 @@ MI->getOperand(OpNo).isMetadata()) { Error = true; } else { + Checker.addOpLoc(OS.str().size(), Val); + unsigned OpFlags = MI->getOperand(OpNo).getImm(); ++OpNo; // Skip over the ID number. @@ -513,16 +566,20 @@ SmallString<256> StringData; raw_svector_ostream OS(StringData); + InlineAsmDataflowChecker Checker; + InitChecker(MI, Checker); + // The variant of the current asmprinter. int AsmPrinterVariant = MAI->getAssemblerDialect(); InlineAsm::AsmDialect InlineAsmVariant = MI->getInlineAsmDialect(); AsmPrinter *AP = const_cast(this); if (InlineAsmVariant == InlineAsm::AD_ATT) EmitGCCInlineAsmStr(AsmStr, MI, MMI, InlineAsmVariant, AsmPrinterVariant, - AP, LocCookie, OS); + AP, LocCookie, OS, Checker); else EmitMSInlineAsmStr(AsmStr, MI, MMI, InlineAsmVariant, AP, LocCookie, OS); + // Reset SanitizeAddress based on the function's attribute. MCTargetOptions MCOptions = TM.Options.MCOptions; MCOptions.SanitizeAddress = @@ -567,7 +624,7 @@ } EmitInlineAsm(OS.str(), getSubtargetInfo(), MCOptions, LocMD, - MI->getInlineAsmDialect()); + MI->getInlineAsmDialect(), &Checker); // Emit the #NOAPP end marker. This has to happen even if verbose-asm isn't // enabled, so we use emitRawComment. Index: lib/MC/MCParser/AsmParser.cpp =================================================================== --- lib/MC/MCParser/AsmParser.cpp +++ lib/MC/MCParser/AsmParser.cpp @@ -857,6 +857,9 @@ // Prime the lexer. Lex(); + if (auto *DF = getTargetParser().getInlineAsmDataflow()) + DF->setBaseLoc(Lexer.getTok().getLoc()); + HadError = false; AsmCond StartingCondState = TheCondState; SmallVector AsmStrRewrites; Index: lib/MC/MCParser/CMakeLists.txt =================================================================== --- lib/MC/MCParser/CMakeLists.txt +++ lib/MC/MCParser/CMakeLists.txt @@ -7,6 +7,7 @@ MCAsmLexer.cpp MCAsmParser.cpp MCAsmParserExtension.cpp + MCInlineAsmDataflowChecker.cpp MCTargetAsmParser.cpp WasmAsmParser.cpp Index: lib/MC/MCParser/MCInlineAsmDataflowChecker.cpp =================================================================== --- /dev/null +++ lib/MC/MCParser/MCInlineAsmDataflowChecker.cpp @@ -0,0 +1,421 @@ +//===-- MCInlineAsmDataflowChecker.cpp ------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCParser/MCInlineAsmDataflowChecker.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "asm-dataflow" + +using namespace llvm; + +void InlineAsmDataflowChecker::transferInlineAsmOps(MCParsedAsmOperand *Op, + SMLoc EndLoc, + SourceMgr &SrcMgr) { + unsigned StartOffset = getLocOffset(Op->getStartLoc(), SrcMgr); + unsigned EndOffset = getLocOffset(EndLoc, SrcMgr); + LLVM_DEBUG(dbgs() << "Checking parsed operand "; Op->print(dbgs()); + dbgs() << " between offsets " << StartOffset << " and " + << EndOffset << "\n"); + DEBUG_WITH_TYPE("asm-dataflow-operands", + TAP->getParser().Warning(BaseLoc, "operand here", + SMRange(Op->getStartLoc(), EndLoc))); + + // TODO should just bail + assert(StartOffset != ~0U && EndOffset != ~0U && + "Operand in differnt buffer"); + auto Comp = [](const std::pair &P, const unsigned &L) { + return P.first < L; + }; + auto First = std::lower_bound(OperandLocs.begin(), OperandLocs.end(), + StartOffset, Comp); + auto Last = + std::lower_bound(OperandLocs.begin(), OperandLocs.end(), EndOffset, Comp); + for (auto It = First; It != Last; ++It) { + Op->addInlineAsmOpNum(It->second); + LLVM_DEBUG(dbgs() << " Adding inline asm operand " << It->second << "\n"); + } +} + +// Called after all information from the MachineInstr and assembly string has +// been added, before the assembly parser starts. +void InlineAsmDataflowChecker::start(MCTargetAsmParser *TAP, + const MCInstrInfo *MII, + const MCRegisterInfo *RI, SMLoc BaseLoc) { + assert(!IsActive); + assert(TAP); + assert(MII); + assert(RI); + + this->TAP = TAP; + this->MII = MII; + this->RI = RI; + + // Fix up physical registers which were added as outputs, but then later + // tied to an input. + for (PhysRegInfo &P : PhysRegs) + if (P.OutputOperandIdx != ~0U && + getOperand(P.OutputOperandIdx).Kind == InputOutput) + P.InputOperandIdx = P.OutputOperandIdx; + + IsActive = true; + + if (DebugFlag && isCurrentDebugType(DEBUG_TYPE)) { + dbgs() << "Physical regs:"; + for (PhysRegInfo &P : PhysRegs) { + dbgs() << " "; + P.dump(RI); + } + + dbgs() << "\nInline asm operands:"; + for (unsigned i = 0; i < Operands.size(); ++i) + Operands[i].print(dbgs()); + + dbgs() << "\n"; + } +} + +void InlineAsmDataflowChecker::stop() { + // Check that every operand is used in the string. We treat a pair of tied + // operands as one, and ignore operands with a physical register constraint + // because they could be used using the literal register name, or even + // implicitly (e.g. by a function call). + for (auto OpLoc : OperandLocs) + getOperand(OpLoc.second).AppearsInString = true; + + for (unsigned OpIdx = 0, E = Operands.size(); OpIdx < E; ++OpIdx) { + OperandInfo Op = Operands[OpIdx]; + if (Op.IsTied()) + continue; + if (Op.PhysicalRegister) + continue; + if (!Op.AppearsInString) + TAP->Warning(BaseLoc, + "operand " + Twine(OpIdx) + " is unused in inline assembly"); + } +} + +void InlineAsmDataflowChecker::checkOperandGroup(OperandGroup &G, SMLoc InstLoc) { + SMLoc Loc = G.ParsedOpLoc.isValid() ? G.ParsedOpLoc : InstLoc; + // First, figure out if this is a use or definition operand. + bool IsUse = false; + bool IsDef = false; + for (auto MCOp : G.MCOps) { + IsUse |= !MCOp.IsDef; + IsDef |= MCOp.IsDef; + } + + // If we don't have any uses or defs, there's nothing we can check. + if (!IsUse && !IsDef) { + LLVM_DEBUG(dbgs() << "No uses/defs, bailing\n"); + return; + } + + // If we have a use and a def, and more than one inline asm op, we can't tell + // which inline asm op is being read/written, so bail out. + if (IsUse && IsDef && G.InlineAsmOps.size() > 1) { + LLVM_DEBUG(dbgs() << "Too many uses/defs, bailing\n"); + return; + } + + if (G.InlineAsmOps.empty()) { + // We have no inline asm operands (even after matching physical register + // constraints), so the only way for this to be legal is for this to be a + // clobbered register. + auto CheckMCReg = [&](MCPhysReg Reg, bool IsDef) { + LLVM_DEBUG(dbgs() << "checking reg " << RI->getName(Reg) << "\n"); + PhysRegInfo *P = FindPhysReg(Reg); + // Only check for clobbers here, physical register constraints will + // have been converted to an inline asm operand. + if (P && P->IsClobber()) { + if (!IsDef && !P->HasBeenWritten) { + // Trying to read a clobbered register, which has unpredictable + // value. + TAP->Warning(Loc, Twine("read from clobbered register ") + + RI->getName(Reg) + ", value is undefined"); + } else if (IsDef) { + // Writing to a clobbered register is fine. + P->HasBeenWritten = true; + } + } else if (!TAP->isRegPredictableInInlineAsm(Reg)) { + TAP->Warning( + Loc, Twine(IsUse ? "read from " : "write to ") + RI->getName(Reg) + + ", which is not an inline assembly operand or clobber"); + } + }; + for (auto MCOp : G.MCOps) + CheckMCReg(MCOp.Reg, IsDef); + } else { + // We have inline asm operands, so check that they are being used + // correctly. + // TODO: Currently, this assumes that a parsed operand is built entirely + // from inline asm operands. However, some parsed operand classes (LDM/STM + // list, register-shifted-register, register-offset memory, ...) have + // multiple registers, so it's possible to mix inline asm operands and + // hard-coded registers in one parsed operand. We don't currently diagnose + // cases line this. Maybe we could start splitting groups further based on + // the actual physical registers used? This will make the warnings + // dependent on register allocation choices, but they will never be worse + // than not doing this. + for (OperandInfo *InlineAsmOp : G.InlineAsmOps) { + // Write to an input-only operand. + if (InlineAsmOp->Kind == Input && IsDef) + TAP->Warning(Loc, "write to an input-only inline assembly operand"); + + // Read from an output only operand (this is valid if we've already + // written it, though). + if ((InlineAsmOp->Kind == Output && !InlineAsmOp->HasBeenWritten) && IsUse) + TAP->Warning(Loc, "instruction reads from an output inline assembly " + "operand (without having written to it first)"); + + // Read after write of a non-earlyclobber operand. + if (InlineAsmOp->Kind == Input && OutputOperandWritten) { + TAP->Warning(Loc, "read from an inline assembly operand after first " + "output operand written, suggest adding " + "early-clobber modifier to output operand"); + TAP->Note(FirstOutputLoc, "output operand written here"); + } + + if (IsDef) + InlineAsmOp->RecordWrite(); + if (IsUse) + InlineAsmOp->RecordRead(); + + // Record the fact that an output-only inline asm operand has been + // written, after which all input-only operands are invalidated. We + // ignore earlyclobber outputs here, because they can't be co-allocated + // with an input operand. We also treat tied input/output operands the + // same way. + if (IsDef && + !(InlineAsmOp->IsEarlyClobber || InlineAsmOp->Kind == InputOutput)) { + if (!FirstOutputLoc.isValid()) + FirstOutputLoc = Loc; + OutputOperandWrittenThisInst = true; + } + } + } +} + +// Sort the three kinds of operands into groups, each of which contains +// operands which refer to the same part of the instruction. +void InlineAsmDataflowChecker::iterateOperandGroups( + const MCInst &Inst, const OperandVector &ParsedOperands, + std::function Callback) { + const MCInstrDesc &MCID = MII->get(Inst.getOpcode()); + SmallSet UsedMCOps; + + // Start with parsed operands. + for (unsigned ParsedOpNum = 0; ParsedOpNum < ParsedOperands.size(); + ++ParsedOpNum) { + LLVM_DEBUG(dbgs() << "Start group in pass 1:\n"); + OperandGroup G; + + G.ParsedOp = ParsedOperands[ParsedOpNum].get(); + G.ParsedOpLoc = ParsedOperands[ParsedOpNum]->getStartLoc(); + LLVM_DEBUG(dbgs() << " Parsed operand: "; G.ParsedOp->print(dbgs()); + dbgs() << "\n"); + + // Find the (zero or more) inline asm operands which contributed to this + // parsed operand. + for (unsigned InlineAsmOpNum : G.ParsedOp->getInlineAsmOpNums()) { + OperandInfo &InlineAsmOp = getOperand(InlineAsmOpNum); + LLVM_DEBUG(dbgs() << " Inline asm operand: " + << getOperandKindName(InlineAsmOp.Kind) << " " + << InlineAsmOpNum << "\n"); + G.InlineAsmOps.push_back(&InlineAsmOp); + } + + // Find the MCOperands which were created from this parsed operand. + for (unsigned MCOpNum = 0; MCOpNum < Inst.getNumOperands(); ++MCOpNum) { + const MCOperand &MCOp = Inst.getOperand(MCOpNum); + if (MCOp.getParsedOpNum() != ParsedOpNum || !MCOp.isReg() || !MCOp.getReg()) + continue; + + MCPhysReg Reg = MCOp.getReg(); + bool IsDef = MCID.operandIsDef(MCOpNum); + bool IsEC = MCID.getOperandConstraint(MCOpNum, MCOI::EARLY_CLOBBER) != -1; + G.MCOps.emplace_back(Reg, IsDef, IsEC); + LLVM_DEBUG(dbgs() << " MC operand: " << (IsDef ? "def" : "use") << " " + << RI->getName(Reg) << "\n"); + + // Find operands where a physical register constraint (register variable + // in C/C++) was used as an operand to the inline assembly, but the + // literal register name was used in the assembly string rather than the + // operand template. I'm not certain that this should be allowed, but I'm + // leaning towards allowing it after seeing some more complex examples + // using LDM/STM where seeing the actual register names makes things + // clearer. + PhysRegInfo *P = FindPhysReg(Reg); + if (!IsDef && P && P->InputOperandIdx != ~0U) { + LLVM_DEBUG( + dbgs() << " Inline asm operand (physical): " + << getOperandKindName(getOperand(P->InputOperandIdx).Kind) + << " " << P->InputOperandIdx << "\n"); + G.InlineAsmOps.push_back(&Operands[P->InputOperandIdx]); + } else if (IsDef && P && P->OutputOperandIdx != ~0U) { + LLVM_DEBUG( + dbgs() << " Inline asm operand (physical): " + << getOperandKindName(getOperand(P->OutputOperandIdx).Kind) + << " " << P->OutputOperandIdx << "\n"); + G.InlineAsmOps.push_back(&getOperand(P->OutputOperandIdx)); + } + + UsedMCOps.insert(MCOpNum); + } + + Callback(G, Inst.getLoc()); + } + + // Create operand groups for any MC operands which weren't used above. + for (unsigned MCOpNum = 0; MCOpNum < Inst.getNumOperands(); ++MCOpNum) { + const MCOperand &MCOp = Inst.getOperand(MCOpNum); + if (!MCOp.isReg() || !MCOp.getReg() || UsedMCOps.count(MCOpNum)) + continue; + + MCPhysReg Reg = MCOp.getReg(); + bool IsDef = MCID.operandIsDef(MCOpNum); + + LLVM_DEBUG(dbgs() << "Start group in pass 2:\n"); + OperandGroup G; + bool IsEC = MCID.getOperandConstraint(MCOpNum, MCOI::EARLY_CLOBBER) != -1; + G.MCOps.emplace_back(Reg, IsDef, IsEC); + LLVM_DEBUG(dbgs() << " MC operand: " << (IsDef ? "def" : "use") << " " + << RI->getName(Reg) << "\n"); + + // Check if there is a physical register inline assembly constraint + // matching this register, and if so add it to the group. + PhysRegInfo *P = FindPhysReg(Reg); + if (!IsDef && P && P->InputOperandIdx != ~0U) { + LLVM_DEBUG(dbgs() << " Inline asm operand: " + << getOperandKindName(getOperand(P->InputOperandIdx).Kind) + << " " << P->InputOperandIdx << "\n"); + G.InlineAsmOps.push_back(&getOperand(P->InputOperandIdx)); + } else if (IsDef && P && P->OutputOperandIdx != ~0U) { + LLVM_DEBUG( + dbgs() << " Inline asm operand: " + << getOperandKindName(getOperand(P->OutputOperandIdx).Kind) + << " " << P->OutputOperandIdx << "\n"); + G.InlineAsmOps.push_back(&getOperand(P->OutputOperandIdx)); + } + + Callback(G, Inst.getLoc()); + } + + // Check implicit uses, which can match physical register inline asm + // operands, but never symbolic ones. + for (const MCPhysReg *ImpUse = MCID.getImplicitUses(); ImpUse && *ImpUse; ++ImpUse) { + LLVM_DEBUG(dbgs() << "Implicit use of " << RI->getName(*ImpUse) << "\n"); + OperandGroup G; + G.MCOps.emplace_back(*ImpUse, false, false); + Callback(G, Inst.getLoc()); + } + for (const MCPhysReg *ImpDef = MCID.getImplicitDefs(); ImpDef && *ImpDef; ++ImpDef) { + LLVM_DEBUG(dbgs() << "Implicit def of " << RI->getName(*ImpDef) << "\n"); + OperandGroup G; + // TODO: can implicit defs ever be early-clobber? + G.MCOps.emplace_back(*ImpDef, true, false); + Callback(G, Inst.getLoc()); + } +} + +void InlineAsmDataflowChecker::checkInst(MCInst &Inst, + const OperandVector &ParsedOperands) { + assert(isActive()); + + // We have three different kinds of "operand" which we care about here: + // - Inline assembly operands. These are written by the user in the source, + // to tell the compiler which values are inputs and outputs of the assembly + // block. The AsmPrinter will have given us all of the information we need + // about these while it was generating the assembly string. + // - Parsed operands. + // - MCOperands. The final operands as present in the MCInst. + + // Strategy: + // - I ops know what operations are legal. + // - P ops know which I ops (0 or more) they match. + // - M ops know what operation they are, and which P op (0 or 1) they match. + // + // - Gather the operands into groups: + // - Loop over P ops, create a group for each one, including the matching I + // and M ops. + // - Loop over M ops + // - If this is a physical I op, create a group with them + // - Else, create a group with just the M op + // - For each group: + // - + + const MCInstrDesc &MCID = MII->get(Inst.getOpcode()); + + LLVM_DEBUG(dbgs() << "Instruction: "; + Inst.dump_pretty(dbgs(), MII->getName(Inst.getOpcode())); + dbgs() << "\n"); + LLVM_DEBUG(dbgs() << "Parsed operands:"; + for (auto &Op : ParsedOperands) { + dbgs() << " "; + Op->print(dbgs()); + } + dbgs() << "\n";); + + // Check for operands which are actually early-clobber but were marked by the + // user as output-only. This isn't a problem by itself, because these are two + // slightly different concepts of early-clobber (applying to a single + // instruction vs the whole inline asm block). However, they do invalidate + // any input-only inline asm operands before they are read by this + // instruction, not just later ones, so we need to check them before checking + // any input operands. + LLVM_DEBUG(dbgs() << "Checking for early-clobber operands\n"); + iterateOperandGroups(Inst, ParsedOperands, [&](OperandGroup &G, SMLoc L) { + // Must have at least an MC operand and an inline asm operand. + if (G.MCOps.empty() || G.InlineAsmOps.empty()) + return; + + // All MC ops must be early-clobber definitions. + for (MCOpInfo &MCOp : G.MCOps) + if (!MCOp.IsDef || !MCOp.IsEarlyClobber) + return; + + // All inline asm ops must be output-only, but not early-clobber. + // - An input matched against an output MC operand will be diagnosed later. + // - In input-output or early-clobber operand can't be co-allocated with + // input-only inline asm operands, so doesn't invalidate them. + for (OperandInfo *InlineAsmOp : G.InlineAsmOps) + if (InlineAsmOp->Kind != Output || InlineAsmOp->IsEarlyClobber) + return; + + LLVM_DEBUG(dbgs() << "found an early-clobber output operand, invalidating " + "inputs for this instruction\n"); + OutputOperandWritten = true; + }); + + // Check the validity of all operands of this instruction. + LLVM_DEBUG(dbgs() << "Main operand checks\n"); + iterateOperandGroups(Inst, ParsedOperands, [&](OperandGroup &G, SMLoc L) { + checkOperandGroup(G, L); + }); + + // Report loads/stores without proper operands/clobbers. This is a lot looser + // than it could be, because we've thrown away the information about which + // memory operands are input/outputs by this point. + // TODO: Could we pass that info through further? + // TODO: Can we check anything with the HasSideEffect property? + if (MCID.mayLoad() && !MayLoad) + TAP->Warning(Inst.getLoc(), "load instruction in inline assembly without " + "appropriate operand/clobber"); + if (MCID.mayStore() && !MayStore) + TAP->Warning(Inst.getLoc(), "store instruction in inline assembly without " + "appropriate operand/clobber"); + + // Mark inline assembly operands as having been written. + for (unsigned i = 0; i < Operands.size(); ++i) + Operands[i].FinishWrites(); + OutputOperandWritten |= OutputOperandWrittenThisInst; + + return; +} Index: lib/MC/MCParser/MCTargetAsmParser.cpp =================================================================== --- lib/MC/MCParser/MCTargetAsmParser.cpp +++ lib/MC/MCParser/MCTargetAsmParser.cpp @@ -15,7 +15,7 @@ MCTargetAsmParser::MCTargetAsmParser(MCTargetOptions const &MCOptions, const MCSubtargetInfo &STI, const MCInstrInfo &MII) - : MCOptions(MCOptions), STI(&STI), MII(MII) {} + : MCOptions(MCOptions), STI(&STI), MII(MII), InlineAsmDataflow(nullptr) {} MCTargetAsmParser::~MCTargetAsmParser() = default; Index: lib/MC/MCRegisterInfo.cpp =================================================================== --- lib/MC/MCRegisterInfo.cpp +++ lib/MC/MCRegisterInfo.cpp @@ -133,3 +133,21 @@ : Twine(RegNum))); return I->second; } + +bool MCRegisterInfo::regsOverlap(unsigned regA, unsigned regB) const { + if (regA == regB) + return true; + + // Regunits are numerically ordered. Find a common unit. + MCRegUnitIterator RUA(regA, this); + MCRegUnitIterator RUB(regB, this); + do { + if (*RUA == *RUB) + return true; + if (*RUA < *RUB) + ++RUA; + else + ++RUB; + } while (RUA.isValid() && RUB.isValid()); + return false; +} Index: lib/Target/ARM/ARMISelLowering.cpp =================================================================== --- lib/Target/ARM/ARMISelLowering.cpp +++ lib/Target/ARM/ARMISelLowering.cpp @@ -13798,7 +13798,7 @@ } } if (StringRef("{cc}").equals_lower(Constraint)) - return std::make_pair(unsigned(ARM::CPSR), &ARM::CCRRegClass); + return std::make_pair(unsigned(ARM::CondCodes), &ARM::ClobberCCRRegClass); return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); } Index: lib/Target/ARM/ARMInstrThumb2.td =================================================================== --- lib/Target/ARM/ARMInstrThumb2.td +++ lib/Target/ARM/ARMInstrThumb2.td @@ -4823,14 +4823,14 @@ // FIXME: LSL #0 in the shift should allow SP to be used as either the // source or destination (but not both). def t2MOVsi: t2AsmPseudo<"mov${p} $Rd, $shift", - (ins rGPR:$Rd, t2_so_reg:$shift, pred:$p)>; + (ins t2_so_reg:$shift, pred:$p), (outs rGPR:$Rd)>; def t2MOVSsi: t2AsmPseudo<"movs${p} $Rd, $shift", - (ins rGPR:$Rd, t2_so_reg:$shift, pred:$p)>; + (ins t2_so_reg:$shift, pred:$p), (outs rGPR:$Rd)>; def t2MOVsr: t2AsmPseudo<"mov${p} $Rd, $shift", - (ins rGPR:$Rd, so_reg_reg:$shift, pred:$p)>; + (ins so_reg_reg:$shift, pred:$p), (outs rGPR:$Rd)>; def t2MOVSsr: t2AsmPseudo<"movs${p} $Rd, $shift", - (ins rGPR:$Rd, so_reg_reg:$shift, pred:$p)>; + (ins so_reg_reg:$shift, pred:$p), (outs rGPR:$Rd)>; // Aliases for the above with the .w qualifier def : t2InstAlias<"mov${p}.w $Rd, $shift", @@ -4848,15 +4848,15 @@ // LDR(literal) w/ alternate [pc, #imm] syntax. def t2LDRpcrel : t2AsmPseudo<"ldr${p} $Rt, $addr", - (ins GPR:$Rt, t2ldr_pcrel_imm12:$addr, pred:$p)>; + (ins t2ldr_pcrel_imm12:$addr, pred:$p), (outs GPR:$Rt)>; def t2LDRBpcrel : t2AsmPseudo<"ldrb${p} $Rt, $addr", - (ins GPRnopc:$Rt, t2ldr_pcrel_imm12:$addr, pred:$p)>; + (ins t2ldr_pcrel_imm12:$addr, pred:$p), (outs GPRnopc:$Rt)>; def t2LDRHpcrel : t2AsmPseudo<"ldrh${p} $Rt, $addr", - (ins GPRnopc:$Rt, t2ldr_pcrel_imm12:$addr, pred:$p)>; + (ins t2ldr_pcrel_imm12:$addr, pred:$p), (outs GPRnopc:$Rt)>; def t2LDRSBpcrel : t2AsmPseudo<"ldrsb${p} $Rt, $addr", - (ins GPRnopc:$Rt, t2ldr_pcrel_imm12:$addr, pred:$p)>; + (ins t2ldr_pcrel_imm12:$addr, pred:$p), (outs GPRnopc:$Rt)>; def t2LDRSHpcrel : t2AsmPseudo<"ldrsh${p} $Rt, $addr", - (ins GPRnopc:$Rt, t2ldr_pcrel_imm12:$addr, pred:$p)>; + (ins t2ldr_pcrel_imm12:$addr, pred:$p), (outs GPRnopc:$Rt)>; // Version w/ the .w suffix. def : t2InstAlias<"ldr${p}.w $Rt, $addr", (t2LDRpcrel GPR:$Rt, t2ldr_pcrel_imm12:$addr, pred:$p), 0>; @@ -4875,7 +4875,7 @@ // Pseudo instruction ldr Rt, =immediate def t2LDRConstPool : t2AsmPseudo<"ldr${p} $Rt, $immediate", - (ins GPR:$Rt, const_pool_asm_imm:$immediate, pred:$p)>; + (ins const_pool_asm_imm:$immediate, pred:$p), (outs GPR:$Rt)>; // Version w/ the .w suffix. def : t2InstAlias<"ldr${p}.w $Rt, $immediate", (t2LDRConstPool GPRnopc:$Rt, Index: lib/Target/ARM/ARMRegisterInfo.td =================================================================== --- lib/Target/ARM/ARMRegisterInfo.td +++ lib/Target/ARM/ARMRegisterInfo.td @@ -191,6 +191,18 @@ def FPINST : ARMReg<9, "fpinst">; def FPINST2 : ARMReg<10, "fpinst2">; +// Pseudo-register which aliases all special registers, used for the "cc" +// inline assembly clobber. +def CondCodes : ARMReg<0, "cc"> { + let Aliases = [CPSR, APSR, APSR_NZCV, SPSR, FPSCR, FPSCR_NZCV, ITSTATE, + FPSID, MVFR2, MVFR1, MVFR0, FPEXC, FPINST, FPINST2]; +} + +def ClobberCCR : RegisterClass<"ARM", [i32], 32, (add CondCodes)> { + let CopyCost = -1; // Don't allow copying of status registers. + let isAllocatable = 0; +} + // Register classes. // // pc == Program Counter Index: lib/Target/ARM/AsmParser/ARMAsmParser.cpp =================================================================== --- lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -635,6 +635,17 @@ void doBeforeLabelEmit(MCSymbol *Symbol) override; void onLabelParsed(MCSymbol *Symbol) override; + + bool isRegPredictableInInlineAsm(MCPhysReg Reg) const override { + // SP can be safely used in inline asm, as long as its value is restored by + // the end of the assembly block. + if (Reg == ARM::SP) + return true; + + // TODO: r9 in RWPI mode, r8 in PIXO mode + + return false; + } }; /// ARMOperand - Instances of this class represent a parsed ARM machine @@ -3461,6 +3472,7 @@ // The source register for the shift has already been added to the // operand list, so we need to pop it off and combine it into the shifted // register operand instead. + S = Operands.back()->getStartLoc(); std::unique_ptr PrevOp( (ARMOperand *)Operands.pop_back_val().release()); if (!PrevOp->isReg()) @@ -3550,6 +3562,9 @@ const AsmToken &ExclaimTok = Parser.getTok(); if (ExclaimTok.is(AsmToken::Exclaim)) { + if (auto *DF = getInlineAsmDataflow()) + DF->transferInlineAsmOps(Operands.back().get(), Parser.getTok().getLoc(), + getSourceManager()); Operands.push_back(ARMOperand::CreateToken(ExclaimTok.getString(), ExclaimTok.getLoc())); Parser.Lex(); // Eat exclaim token @@ -3576,6 +3591,9 @@ SMLoc E = Parser.getTok().getEndLoc(); Parser.Lex(); // Eat right bracket token. + if (auto *DF = getInlineAsmDataflow()) + DF->transferInlineAsmOps(Operands.back().get(), Parser.getTok().getLoc(), + getSourceManager()); Operands.push_back(ARMOperand::CreateVectorIndex(MCE->getValue(), SIdx, E, getContext())); @@ -3875,6 +3893,9 @@ // The ARM system instruction variants for LDM/STM have a '^' token here. if (Parser.getTok().is(AsmToken::Caret)) { + if (auto *DF = getInlineAsmDataflow()) + DF->transferInlineAsmOps(Operands.back().get(), Parser.getTok().getLoc(), + getSourceManager()); Operands.push_back(ARMOperand::CreateToken("^",Parser.getTok().getLoc())); Parser.Lex(); // Eat '^' token. } @@ -5093,6 +5114,9 @@ // If there's a pre-indexing writeback marker, '!', just add it as a token // operand. It's rather odd, but syntactically valid. if (Parser.getTok().is(AsmToken::Exclaim)) { + if (auto *DF = getInlineAsmDataflow()) + DF->transferInlineAsmOps(Operands.back().get(), + Parser.getTok().getLoc(), getSourceManager()); Operands.push_back(ARMOperand::CreateToken("!",Parser.getTok().getLoc())); Parser.Lex(); // Eat the '!'. } @@ -5150,6 +5174,9 @@ // If there's a pre-indexing writeback marker, '!', just add it as a token // operand. if (Parser.getTok().is(AsmToken::Exclaim)) { + if (auto *DF = getInlineAsmDataflow()) + DF->transferInlineAsmOps(Operands.back().get(), + Parser.getTok().getLoc(), getSourceManager()); Operands.push_back(ARMOperand::CreateToken("!",Parser.getTok().getLoc())); Parser.Lex(); // Eat the '!'. } @@ -5201,6 +5228,9 @@ // If there's a pre-indexing writeback marker, '!', just add it as a token // operand. if (Parser.getTok().is(AsmToken::Exclaim)) { + if (auto *DF = getInlineAsmDataflow()) + DF->transferInlineAsmOps(Operands.back().get(), + Parser.getTok().getLoc(), getSourceManager()); Operands.push_back(ARMOperand::CreateToken("!",Parser.getTok().getLoc())); Parser.Lex(); // Eat the '!'. } @@ -5245,6 +5275,9 @@ // If there's a pre-indexing writeback marker, '!', just add it as a token // operand. if (Parser.getTok().is(AsmToken::Exclaim)) { + if (auto *DF = getInlineAsmDataflow()) + DF->transferInlineAsmOps(Operands.back().get(), Parser.getTok().getLoc(), + getSourceManager()); Operands.push_back(ARMOperand::CreateToken("!",Parser.getTok().getLoc())); Parser.Lex(); // Eat the '!'. } @@ -5489,6 +5522,10 @@ // '!' Token operand. Handle that here. For example, the compatibility // alias for 'srsdb sp!, #imm' is 'srsdb #imm!'. if (Parser.getTok().is(AsmToken::Exclaim)) { + if (auto *DF = getInlineAsmDataflow()) + DF->transferInlineAsmOps(Operands.back().get(), + Parser.getTok().getLoc(), + getSourceManager()); Operands.push_back(ARMOperand::CreateToken(Parser.getTok().getString(), Parser.getTok().getLoc())); Parser.Lex(); // Eat exclaim token @@ -6207,12 +6244,18 @@ if (parseOperand(Operands, Mnemonic)) { return true; } + if (auto *DF = getInlineAsmDataflow()) + DF->transferInlineAsmOps(Operands.back().get(), Parser.getTok().getLoc(), + getSourceManager()); while (parseOptionalToken(AsmToken::Comma)) { // Parse and remember the operand. if (parseOperand(Operands, Mnemonic)) { return true; } + if (auto *DF = getInlineAsmDataflow()) + DF->transferInlineAsmOps(Operands.back().get(), + Parser.getTok().getLoc(), getSourceManager()); } } @@ -9299,6 +9342,10 @@ return true; } + Inst.setLoc(IDLoc); + if (auto *DF = getInlineAsmDataflow()) + DF->checkInst(Inst, Operands); + { // processInstruction() updates inITBlock state, we need to save it away bool wasInITBlock = inITBlock(); Index: test/CodeGen/ARM/inline-asm-dataflow.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/inline-asm-dataflow.ll @@ -0,0 +1,641 @@ +; RUN: llc -mtriple armv7a--none-eabi < %s 2>&1 | FileCheck %s +; RUN: llc -mtriple thumbv7a--none-eabi < %s 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=THUMB + +; Nothing wring with this, operands used for all inputs and outputs. +define i32 @correct(i32 %a, i32 %b) { +entry: + %0 = tail call i32 asm sideeffect "add $0, $1, $2", "=r,r,r"(i32 %a, i32 %b), !srcloc !0 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Read from hard-coded register, without operand or clobber. +define i32 @hardcoded_input(i32 %a) { +entry: +; CHECK: warning: read from R1, which is not an inline assembly operand or clobber +; CHECK: note: !srcloc = 1 + %0 = tail call i32 asm sideeffect "add $0, $1, r1", "=r,r"(i32 %a), !srcloc !1 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Write to hard-coded register, without operand or clobber. +define i32 @hardcoded_output(i32 %a, i32 %b) { +entry: +; CHECK: warning: write to R0, which is not an inline assembly operand or clobber +; CHECK: note: !srcloc = 2 + tail call void asm sideeffect "add r0, $0, $1", "r,r"(i32 %a, i32 %b), !srcloc !2 + ret i32 0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Write to an operand which is declared as input-only. +define i32 @write_input(i32 %a, i32 %b) { +entry: +; CHECK: warning: write to an input-only inline assembly operand +; CHECK: note: !srcloc = 3 + tail call void asm sideeffect "add $2, $0, $1", "r,r,r"(i32 %a, i32 %b, i32 undef), !srcloc !3 + ret i32 undef +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Read an output-only operand, without writing to it first. +define i32 @read_output(i32 %a, i32 %b) { +entry: +; CHECK: warning: instruction reads from an output inline assembly operand (without having written to it first) +; CHECK: note: !srcloc = 4 + %0 = tail call { i32, i32 } asm sideeffect "add $0, $1, $2", "=r,=r,r"(i32 %b), !srcloc !4 + %asmresult = extractvalue { i32, i32 } %0, 0 + ret i32 %asmresult +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Read an output-only operand, having written to it first. +define i32 @write_read_output(i32 %a, i32 %b) { +entry: + %0 = tail call { i32, i32 } asm sideeffect "mov $1, $2; mov $0, $1", "=r,=r,r"(i32 %b), !srcloc !5 + %asmresult = extractvalue { i32, i32 } %0, 0 + ret i32 %asmresult +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Read an input-only operand after having written an output-only operand. It is +; possible that these two operands get allocated to the same register, so the +; input value will be clobbered before it can be read. +define i32 @read_after_write(i32 %a, i32 %b, i32 %c) { +entry: +; CHECK: warning: read from an inline assembly operand after first output operand written, suggest adding early-clobber modifier to output operand +; CHECK: note: !srcloc = 6 +; CHECK: note: output operand written here +; CHECK: note: !srcloc = 6 + %0 = tail call i32 asm sideeffect "add $0, $1, $2; add $0, $0, $3", "=r,r,r,r"(i32 %a, i32 %b, i32 %c), !srcloc !6 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; The correct way to write the above function is to add the "&" (early-clobber) +; constraint modifier to the output operand, which prevents the compiler from +; sharing its register with an input operand. +define i32 @read_after_write_earlyclobber(i32 %a, i32 %b, i32 %c) { +entry: + %0 = tail call i32 asm sideeffect "add $0, $1, $2; add $0, $0, $3", "=&r,r,r,r"(i32 %a, i32 %b, i32 %c) #1, !srcloc !7 + ret i32 %0 +} + + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Writing a clobbered register is allowed. +define i32 @write_clobber(i32 %a, i32 %b) { +entry: + tail call void asm sideeffect "add r4, $0, $1", "r,r,~{r4}"(i32 %a, i32 %b), !srcloc !8 + ret i32 undef +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Reading from a clobbered register is not allowed, the value is unspecified. +define i32 @read_clobber(i32 %a, i32 %b) { +entry: +; CHECK: warning: read from clobbered register R4, value is undefined +; CHECK: note: !srcloc = 9 + %0 = tail call i32 asm sideeffect "add $0, $1, r4", "=r,r,~{r4}"(i32 %a), !srcloc !9 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Reading from a clobbered register which we have previously written to is +; allowed. +define i32 @write_read_clobber(i32 %a, i32 %b) { +entry: + %0 = tail call i32 asm sideeffect "mov r4, #42; add $0, $1, r4", "=r,r,~{r4}"(i32 %a), !srcloc !10 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Load instruction, but inline asm has no memory operands or clobber. +define i32 @read_without_mem_op(i32 %a, i32 %b) { +entry: + %a.addr = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 +; CHECK: warning: load instruction in inline assembly without appropriate operand/clobber +; CHECK: note: !srcloc = 11 + %0 = call i32 asm sideeffect "ldr $0, [$1]", "=r,r"(i32* nonnull %a.addr), !srcloc !11 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Load instruction, with a memory input operand, so OK. +define i32 @read_with_input_mem_op(i32 %a, i32 %b) { +entry: + %a.addr = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + %0 = call i32 asm sideeffect "ldr $0, $1", "=r,*m"(i32* nonnull %a.addr), !srcloc !12 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Load instruction, with only an output memory operand. +define i32 @_Z23read_with_output_mem_opii(i32 %a, i32 %b) { +entry: + %a.addr = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 +; CHECK: warning: load instruction in inline assembly without appropriate operand/clobber +; CHECK: note: !srcloc = 13 + %0 = call i32 asm sideeffect "ldr $0, $1", "=r,=*m"(i32* nonnull %a.addr), !srcloc !13 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Load instruction, with a memory clobber, so OK. +define dso_local i32 @read_with_mem_clobber(i32 %a) { +entry: + %a.addr = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + %0 = call i32 asm sideeffect "ldr $0, [$1]", "=r,r,~{memory}"(i32* nonnull %a.addr), !srcloc !14 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Store instruction, but inline asm has no memory operands or clobber. +define dso_local i32 @write_without_mem_op(i32 %a) { +entry: + %x = alloca i32, align 4 + %0 = bitcast i32* %x to i8* +; CHECK: warning: store instruction in inline assembly without appropriate operand/clobber +; CHECK: note: !srcloc = 15 + call void asm sideeffect "str $1, [$0]", "r,r"(i32* nonnull %x, i32 %a), !srcloc !15 + %1 = load i32, i32* %x, align 4 + ret i32 %1 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Store instruction with output memory operand. +define i32 @write_with_output_mem_op(i32 %a) { +entry: + %x = alloca i32, align 4 + %0 = bitcast i32* %x to i8* + call void asm sideeffect "str $1, $0", "=*m,r"(i32* nonnull %x, i32 %a), !srcloc !16 + %1 = load i32, i32* %x, align 4 + ret i32 %1 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Store instruction, with only an input memory operand. +define i32 @write_with_input_mem_op(i32 %a) { +entry: + %x = alloca i32, align 4 + %0 = bitcast i32* %x to i8* +; CHECK: warning: store instruction in inline assembly without appropriate operand/clobber +; CHECK: note: !srcloc = 17 + call void asm sideeffect "str $1, $0", "*m,r"(i32* nonnull %x, i32 %a), !srcloc !17 + %1 = load i32, i32* %x, align 4 + ret i32 %1 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Store instruction, with a memory clobber, so OK. +define i32 @write_with_mem_clobber(i32 %a) { +entry: + %x = alloca i32, align 4 + %0 = bitcast i32* %x to i8* + call void asm sideeffect "str $1, [$0]", "r,r,~{memory}"(i32* nonnull %x, i32 %a), !srcloc !18 + %1 = load i32, i32* %x, align 4 + ret i32 %1 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Reading CPSR without the "cc" clobber. +define i32 @read_cpsr_without_clobber(i32 %a, i32 %b) { +entry: +; CHECK: warning: read from CPSR, which is not an inline assembly operand or clobber +; CHECK: note: !srcloc = 19 + tail call void asm sideeffect "beq foo", ""(), !srcloc !19 + ret i32 undef +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Reading CPSR with the "cc" clobber, value is unpredictable. +define i32 @read_cpsr_with_clobber(i32 %a, i32 %b) { +entry: +; CHECK: warning: read from clobbered register CPSR, value is undefined +; CHECK: note: !srcloc = 20 + tail call void asm sideeffect "beq foo", "~{cc}"(), !srcloc !20 + ret i32 undef +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Writing CPSR without the "cc" clobber. +define i32 @write_cpsr_without_clobber(i32 %a, i32 %b) { +entry: +; CHECK: warning: write to CPSR, which is not an inline assembly operand or clobber +; CHECK: note: !srcloc = 21 + %0 = tail call i32 asm sideeffect "adds $0, $1, $2", "=r,r,r"(i32 %a, i32 %b), !srcloc !21 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Writing CPSR without the "cc" clobber. +define i32 @write_cpsr_with_clobber(i32 %a, i32 %b) { +entry: + %0 = tail call i32 asm sideeffect "adds $0, $1, $2", "=r,r,r,~{cc}"(i32 %a, i32 %b), !srcloc !22 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Writing then reading CPSR, with the "cc" clobber. +define i32 @write_read_cpsr_with_clobber(i32 %a, i32 %b) { +entry: + %0 = tail call i32 asm sideeffect "adds $0, $1, $2; it eq; addeq $0, $0, #1", "=r,r,r,~{cc}"(i32 %a, i32 %b), !srcloc !23 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; The SP has a predictable value (not the exact value, but the semantics of the +; stack it points into), so we allow the user to read and write it without +; clobbering it. We just have to trust the user to use the stack memory sanely, +; and put the value of SP back before leaving the assembly block. +define i32 @internal_stack_allocation() { +entry: + tail call void asm sideeffect "sub sp, sp, #8; add sp, sp, #8", ""(), !srcloc !24 + ret i32 0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Create new space on the stack, use load/store instructions on that memory +; region. This currently requires the "memory" clobber to avoid warnings, +; though one could reasonably argue that this is a false positive in this case +; because the memory isn't otherwise visible to the compiler. +define i32 @sp_relative_mem_access_with_mem_clobber() { +entry: + %0 = tail call i32 asm sideeffect "sub sp, sp, #8; mov r4, #42; str r4, [sp]; ldr $0, [sp]; add sp, sp, #8", "=r,~{r4},~{memory}"(), !srcloc !25 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; If a physical register constraint is used (local named register variables in +; C/C++), then it is valid to use the literal register name in the assembly +; string, and all of the normal operand checks are still performed. + +; Physical register used correctly as input operand. +define i32 @register_variable_input(i32 %a, i32 %b) { +entry: + %0 = tail call i32 asm sideeffect "add $0, $1, r4", "=r,r,{r4}"(i32 %a, i32 %b), !srcloc !26 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Physical register used correctly as output operand. +define i32 @register_variable_output(i32 %a, i32 %b) { +entry: + %0 = tail call i32 asm sideeffect "add r4, $1, $2", "={r4},r,r"(i32 %a, i32 %b), !srcloc !27 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Input-only physical register is written to. This is invalid because the +; compiler could re-use the register as an output. +define i32 @register_variable_input_write(i32 %a, i32 %b) { +entry: +; CHECK: warning: write to R4, which is not an inline assembly operand or clobber +; CHECK: note: !srcloc = 28 + tail call void asm sideeffect "add r4, $0, $1", "r,{r4}"(i32 %a, i32 %b), !srcloc !28 + ret i32 %b +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Output-only physical register is read from. This is invalid because the +; compiler could re-use the register as an input. +define i32 @register_variable_output_read(i32 %a, i32 %b) { +entry: +; CHECK: warning: read from R4, which is not an inline assembly operand or clobber +; CHECK: note: !srcloc = 29 + %0 = tail call i32 asm sideeffect "add $0, r4, $1", "={r4},r"(i32 %a), !srcloc !29 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; The same physical register is used as both an input and an output, so reading +; and writing it are both valid. +define i32 @register_variable_input_output(i32 %a, i32 %b) { +entry: + %0 = tail call i32 asm "add r4, r4, $1", "={r4},r,{r4}"(i32 %a, i32 %b), !srcloc !30 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; The rule about reading an input after writing an output still applies if the +; input is a physical register. +define i32 @register_variable_input_ec(i32 %a, i32 %b, i32 %c) { +entry: +; CHECK: warning: read from an inline assembly operand after first output operand written, suggest adding early-clobber modifier to output operand +; CHECK: note: !srcloc = 31 +; CHECK: note: output operand written here +; CHECK: note: !srcloc = 31 + %0 = tail call i32 asm sideeffect "add $0, r4, $2\0Aadd $0, $0, $3\0A", "=r,{r4},r,r"(i32 %a, i32 %b, i32 %c), !srcloc !31 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; The rule about reading an input after writing an output still applies if the +; output is a physical register. +define dso_local i32 @register_variable_output_ec(i32 %a, i32 %b, i32 %c) local_unnamed_addr #0 { +entry: +; CHECK: warning: read from an inline assembly operand after first output operand written, suggest adding early-clobber modifier to output operand +; CHECK: note: !srcloc = 32 +; CHECK: note: output operand written here +; CHECK: note: !srcloc = 32 + %0 = tail call i32 asm sideeffect "add r4, $1, $2\0Aadd r4, $0, $3\0A", "={r4},r,r,r"(i32 %a, i32 %b, i32 %c), !srcloc !32 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Unused input operand. +define i32 @unused_input(i32 %a, i32 %b) { +entry: +; CHECK: warning: operand 0 is unused in inline assembly +; CHECK: note: !srcloc = 33 + tail call void asm sideeffect "// nothing here", "r"(i32 %a) #2, !srcloc !33 + ret i32 undef +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Unused output operand +define i32 @unused_output(i32 %a, i32 %b) { +entry: +; CHECK: warning: operand 0 is unused in inline assembly +; CHECK: note: !srcloc = 34 + %0 = tail call i32 asm sideeffect "// nothing here", "=r"() #2, !srcloc !34 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Some operands uses, some not. +define i32 @multiple_unused(i32 %a, i32 %b) { +entry: +; CHECK: warning: operand 1 is unused in inline assembly +; CHECK: note: !srcloc = 35 +; CHECK: warning: operand 3 is unused in inline assembly +; CHECK: note: !srcloc = 35 + %0 = tail call { i32, i32 } asm sideeffect "mov $0, $2", "=r,=r,r,r"(i32 %a, i32 %b) #2, !srcloc !35 + %asmresult1 = extractvalue { i32, i32 } %0, 1 + ret i32 %asmresult1 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Unused physical register operands are allowed, because they could be used +; implictly (e.g. by a function call). +define void @unused_physical_input(i32 %a) { +entry: + tail call void asm sideeffect "// nothing here", "{r4}"(i32 %a), !srcloc !36 + ret void +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Function Attrs: nounwind readnone +define i32 @unused_physical_output() { +entry: + %0 = tail call i32 asm "// nothing here", "={r4}"(), !srcloc !37 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; If the actual operand to an instruction is early-clobber, then using an +; output operand there invalidates all input operands for that instruction, not +; just later ones. +define i32 @early_clobber_instruction(i32 %z, i32 %v, i32* %p) { +entry: +; CHECK: warning: read from an inline assembly operand after first output operand written, suggest adding early-clobber modifier to output operand +; CHECK: note: !srcloc = 38 +; CHECK: note: output operand written here +; CHECK: note: !srcloc = 38 +; CHECK: warning: read from an inline assembly operand after first output operand written, suggest adding early-clobber modifier to output operand +; CHECK: note: !srcloc = 38 +; CHECK: note: output operand written here +; CHECK: note: !srcloc = 38 + %0 = tail call i32 asm "strex $0, $1, [$2]", "=r,r,r,~{memory}"(i32 %v, i32* %p), !srcloc !38 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; The solution, as before, is to mark the output operand is early-clobber. +define i32 @early_clobber_instruction_with_ec_operand(i32 %v, i32* %p) { +entry: + %0 = tail call i32 asm "strex $0, $1, [$2]", "=&r,r,r,~{memory}"(i32 %v, i32* %p), !srcloc !39 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +define void @fpscr_without_clobber(float %a, float %b) { +entry: +; CHECK: warning: write to FPSCR_NZCV, which is not an inline assembly operand or clobber +; CHECK: note: !srcloc = 40 + tail call void asm sideeffect "vcmp.f32 $0, $1", "w,w"(float %a, float %b), !srcloc !40 + ret void +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +define void @fpscr_with_clobber(float %a, float %b) { +entry: + tail call void asm sideeffect "vcmp.f32 $0, $1", "w,w,~{cc}"(float %a, float %b), !srcloc !41 + ret void +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +define void @itstate_without_clobber(float %a, float %b) { +entry: +; THUMB: warning: write to ITSTATE, which is not an inline assembly operand or clobber +; THUMB: note: !srcloc = 42 + tail call void asm sideeffect "it eq", ""(), !srcloc !42 + ret void +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +define void @itstate_with_clobber(float %a, float %b) { +entry: + tail call void asm sideeffect "it eq", "~{cc}"(), !srcloc !43 + ret void +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; Memory operands with writeback are both read and written, so need to be +; input-output operands. +; FIXME: This doesn't currently work correctly for pre-increment operands, +; because the instruction matcher can't cope with tied operands where one +; parser operand corresponds to multiple MCOperands. +define void @post_inc_writeback_correct(i32* %a) { +entry: + %0 = tail call i32* asm sideeffect "ldr r6, [$0], #4", "=r,0,~{memory},~{r6}"(i32* %a), !srcloc !44 + ret void +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +define void @post_inc_writeback_input(i32* %a) { +entry: +; CHECK: warning: write to an input-only inline assembly operand +; CHECK: note: !srcloc = 45 + tail call void asm sideeffect "ldr r6, [$0], #4", "r,~{memory},~{r6}"(i32* %a), !srcloc !45 + ret void +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +define void @post_inc_writeback_output() { +entry: +; CHECK: warning: instruction reads from an output inline assembly operand (without having written to it first) +; CHECK: note: !srcloc = 46 + %0 = tail call i32* asm sideeffect "ldr r6, [$0], #4", "=r,~{memory},~{r6}"(), !srcloc !46 + ret void +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +; This instruction is a pseudo-instruction expanded in the assembly parser, +; which previously did not have the output operands defined correctly. +define i32 @mov_shift_alias(i32 %a) { +entry: + %0 = tail call i32 asm "mov $0, $1, asr #31", "=r,r"(i32 %a), !srcloc !47 + ret i32 %0 +} + +; CHECK-NOT: warning: +; CHECK-NOT: note: + +!0 = !{i32 0} +!1 = !{i32 1} +!2 = !{i32 2} +!3 = !{i32 3} +!4 = !{i32 4} +!5 = !{i32 5} +!6 = !{i32 6} +!7 = !{i32 7} +!8 = !{i32 8} +!9 = !{i32 9} +!10 = !{i32 10} +!11 = !{i32 11} +!12 = !{i32 12} +!13 = !{i32 13} +!14 = !{i32 14} +!15 = !{i32 15} +!16 = !{i32 16} +!17 = !{i32 17} +!18 = !{i32 18} +!19 = !{i32 19} +!20 = !{i32 20} +!21 = !{i32 21} +!22 = !{i32 22} +!23 = !{i32 23} +!24 = !{i32 24} +!25 = !{i32 25} +!26 = !{i32 26} +!27 = !{i32 27} +!28 = !{i32 28} +!29 = !{i32 29} +!30 = !{i32 30} +!31 = !{i32 31} +!32 = !{i32 32} +!33 = !{i32 33} +!34 = !{i32 34} +!35 = !{i32 35} +!36 = !{i32 36} +!37 = !{i32 37} +!38 = !{i32 38} +!39 = !{i32 39} +!40 = !{i32 40} +!41 = !{i32 41} +!42 = !{i32 42} +!43 = !{i32 43} +!44 = !{i32 44} +!45 = !{i32 45} +!46 = !{i32 46} +!47 = !{i32 47} Index: utils/TableGen/AsmMatcherEmitter.cpp =================================================================== --- utils/TableGen/AsmMatcherEmitter.cpp +++ utils/TableGen/AsmMatcherEmitter.cpp @@ -1976,6 +1976,7 @@ CvtOS << " }\n"; } CvtOS << " unsigned OpIdx;\n"; + CvtOS << " unsigned LastMCOpNum = 0;\n"; CvtOS << " Inst.setOpcode(Opcode);\n"; CvtOS << " for (const uint8_t *p = Converter; *p; p+= 2) {\n"; if (HasOptionalOperands) { @@ -2249,7 +2250,11 @@ } // Finish up the converter driver function. - CvtOS << " }\n }\n}\n\n"; + CvtOS << " }\n"; + CvtOS << " for (; LastMCOpNum < Inst.getNumOperands(); ++LastMCOpNum)\n"; + CvtOS << " if (*p != CVT_Tied)\n"; + CvtOS << " Inst.getOperand(LastMCOpNum).setParsedOpNum(OpIdx);\n"; + CvtOS << " }\n}\n\n"; // Finish up the operand number lookup function. OpOS << " }\n }\n}\n\n";