diff --git a/include/llvm/CodeGen/AsmPrinter.h b/include/llvm/CodeGen/AsmPrinter.h index b6056380916c..14bc08167825 100644 --- a/include/llvm/CodeGen/AsmPrinter.h +++ b/include/llvm/CodeGen/AsmPrinter.h @@ -631,6 +631,11 @@ class AsmPrinter : public MachineFunctionPass { /// inline asm. void EmitInlineAsm(const MachineInstr *MI) const; + /// Add inline assembly info to the diagnostics machinery, so we can + /// emit file and position info. Returns SrcMgr memory buffer position. + unsigned addInlineAsmDiagBuffer(StringRef AsmStr, + const MDNode *LocMDNode) const; + //===------------------------------------------------------------------===// // Internal Implementation Details //===------------------------------------------------------------------===// diff --git a/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp b/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp index 4159eb19423a..36604b46660b 100644 --- a/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp +++ b/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp @@ -71,6 +71,42 @@ static void srcMgrDiagHandler(const SMDiagnostic &Diag, void *diagInfo) { DiagInfo->DiagHandler(Diag, DiagInfo->DiagContext, LocCookie); } +unsigned AsmPrinter::addInlineAsmDiagBuffer(StringRef AsmStr, + const MDNode *LocMDNode) const { + if (!DiagInfo) { + DiagInfo = make_unique(); + + MCContext &Context = MMI->getContext(); + Context.setInlineSourceManager(&DiagInfo->SrcMgr); + + LLVMContext &LLVMCtx = MMI->getModule()->getContext(); + if (LLVMCtx.getInlineAsmDiagnosticHandler()) { + DiagInfo->DiagHandler = LLVMCtx.getInlineAsmDiagnosticHandler(); + DiagInfo->DiagContext = LLVMCtx.getInlineAsmDiagnosticContext(); + DiagInfo->SrcMgr.setDiagHandler(srcMgrDiagHandler, DiagInfo.get()); + } + } + + SourceMgr &SrcMgr = DiagInfo->SrcMgr; + + std::unique_ptr Buffer; + // The inline asm source manager will outlive AsmStr, so make a copy of the + // string for SourceMgr to own. + Buffer = MemoryBuffer::getMemBufferCopy(AsmStr, ""); + + // Tell SrcMgr about this buffer, it takes ownership of the buffer. + unsigned BufNum = SrcMgr.AddNewSourceBuffer(std::move(Buffer), SMLoc()); + + // Store LocMDNode in DiagInfo, using BufNum as an identifier. + if (LocMDNode) { + DiagInfo->LocInfos.resize(BufNum); + DiagInfo->LocInfos[BufNum - 1] = LocMDNode; + } + + return BufNum; +} + + /// EmitInlineAsm - Emit a blob of inline asm to the output streamer. void AsmPrinter::EmitInlineAsm(StringRef Str, const MCSubtargetInfo &STI, const MCTargetOptions &MCOptions, @@ -98,39 +134,11 @@ void AsmPrinter::EmitInlineAsm(StringRef Str, const MCSubtargetInfo &STI, return; } - if (!DiagInfo) { - DiagInfo = make_unique(); + unsigned BufNum = addInlineAsmDiagBuffer(Str, LocMDNode); + DiagInfo->SrcMgr.setIncludeDirs(MCOptions.IASSearchPaths); - MCContext &Context = MMI->getContext(); - Context.setInlineSourceManager(&DiagInfo->SrcMgr); - - LLVMContext &LLVMCtx = MMI->getModule()->getContext(); - if (LLVMCtx.getInlineAsmDiagnosticHandler()) { - DiagInfo->DiagHandler = LLVMCtx.getInlineAsmDiagnosticHandler(); - DiagInfo->DiagContext = LLVMCtx.getInlineAsmDiagnosticContext(); - DiagInfo->SrcMgr.setDiagHandler(srcMgrDiagHandler, DiagInfo.get()); - } - } - - SourceMgr &SrcMgr = DiagInfo->SrcMgr; - SrcMgr.setIncludeDirs(MCOptions.IASSearchPaths); - - std::unique_ptr Buffer; - // The inline asm source manager will outlive Str, so make a copy of the - // string for SourceMgr to own. - Buffer = MemoryBuffer::getMemBufferCopy(Str, ""); - - // Tell SrcMgr about this buffer, it takes ownership of the buffer. - unsigned BufNum = SrcMgr.AddNewSourceBuffer(std::move(Buffer), SMLoc()); - - // Store LocMDNode in DiagInfo, using BufNum as an identifier. - if (LocMDNode) { - DiagInfo->LocInfos.resize(BufNum); - DiagInfo->LocInfos[BufNum-1] = LocMDNode; - } - - std::unique_ptr Parser( - createMCAsmParser(SrcMgr, OutContext, *OutStreamer, *MAI, BufNum)); + std::unique_ptr Parser(createMCAsmParser( + DiagInfo->SrcMgr, OutContext, *OutStreamer, *MAI, BufNum)); // Do not use assembler-level information for parsing inline assembly. OutStreamer->setUseAssemblerInfoForParsing(false); @@ -519,6 +527,44 @@ void AsmPrinter::EmitInlineAsm(const MachineInstr *MI) const { MCOptions.SanitizeAddress = MF->getFunction().hasFnAttribute(Attribute::SanitizeAddress); + // Emit warnings if we use reserved registers on the clobber list, as + // that might give surprising results. + std::vector RestrRegs; + // Start with the first operand descriptor, and iterate over them. + for (unsigned I = InlineAsm::MIOp_FirstOperand, NumOps = MI->getNumOperands(); + I < NumOps; ++I) { + const MachineOperand &MO = MI->getOperand(I); + if (MO.isImm()) { + unsigned Flags = MO.getImm(); + if (InlineAsm::getKind(Flags) == InlineAsm::Kind_Clobber && + MF->getRegInfo().isReserved(MI->getOperand(I + 1).getReg())) { + const TargetRegisterInfo *TRI = MF->getSubtarget().getRegisterInfo(); + RestrRegs.push_back(TRI->getName(MI->getOperand(I + 1).getReg())); + } + // Skip to one before the next operand descriptor, if it exists. + I += InlineAsm::getNumOperandRegisters(Flags); + } + } + + if (!RestrRegs.empty()) { + unsigned BufNum = addInlineAsmDiagBuffer(OS.str(), LocMD); + auto &SrcMgr = DiagInfo->SrcMgr; + SMLoc Loc = SMLoc::getFromPointer( + SrcMgr.getMemoryBuffer(BufNum)->getBuffer().begin()); + + std::string Msg = "inline asm clobber list contains reserved registers: "; + for (auto I = RestrRegs.begin(), E = RestrRegs.end(); I != E; I++) { + if(I != RestrRegs.begin()) + Msg += ", "; + Msg += *I; + } + std::string Note = "Reserved registers on the clobber list may not be " + "preserved across the asm statement, and clobbering them may " + "lead to undefined behaviour."; + SrcMgr.PrintMessage(Loc, SourceMgr::DK_Warning, Msg); + SrcMgr.PrintMessage(Loc, SourceMgr::DK_Note, Note); + } + EmitInlineAsm(OS.str(), getSubtargetInfo(), MCOptions, LocMD, MI->getInlineAsmDialect()); diff --git a/test/CodeGen/AArch64/inline-asm-clobber.ll b/test/CodeGen/AArch64/inline-asm-clobber.ll new file mode 100644 index 000000000000..028cf0b6db12 --- /dev/null +++ b/test/CodeGen/AArch64/inline-asm-clobber.ll @@ -0,0 +1,8 @@ +; RUN: llc <%s -mtriple=aarch64-none-eabi 2>&1 | FileCheck %s + +; CHECK: warning: inline asm clobber list contains reserved registers: SP + +define void @foo() nounwind { + call void asm sideeffect "mov x7, #1", "~{x7},~{sp}"() + ret void +} diff --git a/test/CodeGen/ARM/inline-asm-clobber.ll b/test/CodeGen/ARM/inline-asm-clobber.ll new file mode 100644 index 000000000000..458949a5c944 --- /dev/null +++ b/test/CodeGen/ARM/inline-asm-clobber.ll @@ -0,0 +1,27 @@ +; RUN: llc <%s -mtriple=arm-none-eabi 2>&1 | FileCheck %s -check-prefix=CHECK + +; RUN: llc <%s -mtriple=arm-none-eabi -relocation-model=rwpi 2>&1 \ +; RUN: | FileCheck %s -check-prefix=RWPI + +; RUN: llc <%s -mtriple=arm-none-eabi --disable-fp-elim 2>&1 \ +; RUN: | FileCheck %s -check-prefix=NO_FP_ELIM + +; CHECK: warning: inline asm clobber list contains reserved registers: SP, PC +; CHECK: warning: inline asm clobber list contains reserved registers: R11 +; RWPI: warning: inline asm clobber list contains reserved registers: R9, SP, PC +; RWPI: warning: inline asm clobber list contains reserved registers: R11 +; NO_FP_ELIM: warning: inline asm clobber list contains reserved registers: R11, SP, PC +; NO_FP_ELIM: warning: inline asm clobber list contains reserved registers: R11 + +define void @foo() nounwind { + call void asm sideeffect "mov r7, #1", + "~{r9},~{r11},~{r12},~{lr},~{sp},~{pc},~{r10}"() + ret void +} + +define i32 @bar(i32 %i) { + %vla = alloca i32, i32 %i, align 4 + tail call void asm sideeffect "mov r7, #1", "~{r11}"() + %1 = load volatile i32, i32* %vla, align 4 + ret i32 %1 +} diff --git a/test/CodeGen/X86/inline-asm-clobber.ll b/test/CodeGen/X86/inline-asm-clobber.ll new file mode 100644 index 000000000000..1d354e743e51 --- /dev/null +++ b/test/CodeGen/X86/inline-asm-clobber.ll @@ -0,0 +1,8 @@ +; RUN: llc <%s -mtriple=x86_64-unknown-unknown -- 2>&1 | FileCheck %s + +; CHECK: warning: inline asm clobber list contains reserved registers: RSP, EBP + +define void @foo() nounwind { + call void asm sideeffect "mov $$0x12, %eax", "~{eax},~{rsp},~{esi},~{ebp}"() + ret void +}