https://github.com/GkvJwa updated https://github.com/llvm/llvm-project/pull/193007
>From 09d0243ddf02f323be8bacc78ee570b32603f7e4 Mon Sep 17 00:00:00 2001 From: GkvJwa <[email protected]> Date: Sun, 6 Sep 2026 19:47:02 +0800 Subject: [PATCH] [CodeView] Add line entries for instructions in MS inline asm Teach Clang to preserve per-line source locations for MS inline asm in srcloc metadata, and teach the CodeView path to emit those locations while the integrated assembler parses the inline asm body. This allows debuggers such as WinDbg to step through individual instructions inside __asm blocks instead of stepping over the whole block Fixes #91861 --- clang/include/clang/AST/Stmt.h | 11 +- clang/lib/AST/Stmt.cpp | 36 ++++- clang/lib/CodeGen/CGStmt.cpp | 132 ++++++++++++++++-- clang/test/CodeGen/inline-asm-codeview.c | 75 ++++++++++ clang/test/CodeGen/ms-inline-asm-codeview.cpp | 32 +++++ llvm/docs/LangRef.md | 37 +++++ llvm/include/llvm/CodeGen/AsmPrinterHandler.h | 5 + llvm/include/llvm/CodeGen/MachineInstr.h | 5 + llvm/include/llvm/IR/InlineAsm.h | 5 + llvm/include/llvm/MC/MCStreamer.h | 11 ++ .../AsmPrinter/AsmPrinterInlineAsm.cpp | 104 +++++++++++++- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 8 ++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h | 4 + llvm/lib/CodeGen/MachineInstr.cpp | 4 + llvm/lib/CodeGen/MachineModuleInfo.cpp | 9 +- llvm/lib/IR/InlineAsm.cpp | 28 ++++ llvm/lib/MC/MCAsmStreamer.cpp | 2 + llvm/lib/MC/MCStreamer.cpp | 7 + .../COFF/gnu-inline-asm-source-loc.ll | 96 +++++++++++++ .../COFF/ms-inline-asm-source-loc.ll | 51 +++++++ 20 files changed, 635 insertions(+), 27 deletions(-) create mode 100644 clang/test/CodeGen/inline-asm-codeview.c create mode 100644 clang/test/CodeGen/ms-inline-asm-codeview.cpp create mode 100644 llvm/test/DebugInfo/COFF/gnu-inline-asm-source-loc.ll create mode 100644 llvm/test/DebugInfo/COFF/ms-inline-asm-source-loc.ll diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 5d27ded64082d..8f2247dac60a1 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -3538,11 +3538,16 @@ class GCCAsmStmt : public AsmStmt { /// true, otherwise return false. This handles canonicalization and /// translation of strings from GCC syntax to LLVM IR syntax, and handles //// flattening of named references like %[foo] to Operand AsmStringPiece's. - unsigned AnalyzeAsmString(SmallVectorImpl<AsmStringPiece> &Pieces, - const ASTContext &C, unsigned &DiagOffs) const; + unsigned + AnalyzeAsmString(SmallVectorImpl<AsmStringPiece> &Pieces, const ASTContext &C, + unsigned &DiagOffs, + SmallVectorImpl<unsigned> *SourceOffsets = nullptr) const; /// Assemble final IR asm string. - std::string generateAsmString(const ASTContext &C) const; + /// If requested, map each output byte to an offset in getAsmString(). + std::string + generateAsmString(const ASTContext &C, + SmallVectorImpl<unsigned> *SourceOffsets = nullptr) const; //===--- Output operands ---===// diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index 15d0e6435aaf3..49a8e128c0666 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -32,6 +32,7 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Token.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" @@ -667,19 +668,25 @@ int GCCAsmStmt::getNamedOperand(StringRef SymbolicName) const { /// AnalyzeAsmString - Analyze the asm string of the current asm, decomposing /// it into pieces. If the asm string is erroneous, emit errors and return /// true, otherwise return false. -unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces, - const ASTContext &C, unsigned &DiagOffs) const { +unsigned +GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece> &Pieces, + const ASTContext &C, unsigned &DiagOffs, + SmallVectorImpl<unsigned> *SourceOffsets) const { std::string Str = getAsmString(); const char *StrStart = Str.data(); const char *StrEnd = Str.data() + Str.size(); const char *CurPtr = StrStart; + if (SourceOffsets) + SourceOffsets->clear(); // "Simple" inline asms have no constraints or operands, just convert the asm // string to escape $'s. if (isSimple()) { std::string Result; for (; CurPtr != StrEnd; ++CurPtr) { + if (SourceOffsets) + SourceOffsets->append(*CurPtr == '$' ? 2 : 1, CurPtr - StrStart); switch (*CurPtr) { case '$': Result += "$$"; @@ -710,6 +717,25 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces, return 0; } + // Track the bytes produced by this conversion, including escapes and + // operand references. Flushing a literal into Pieces does not change its + // length; operand pieces contribute their LLVM template spelling. + unsigned SourceOffset = CurPtr - StrStart; + size_t OldStringSize = CurStringPiece.size(); + size_t OldPiecesSize = Pieces.size(); + auto RecordOffsets = llvm::scope_exit([&] { + if (!SourceOffsets) + return; + size_t NewSize = CurStringPiece.size(); + for (size_t I = OldPiecesSize; I < Pieces.size(); ++I) { + const auto &Piece = Pieces[I]; + NewSize += Piece.isString() + ? Piece.getString().size() + : llvm::utostr(Piece.getOperandNo()).size() + + (Piece.getModifier() ? 5 : 1); + } + SourceOffsets->append(NewSize - OldStringSize, SourceOffset); + }); char CurChar = *CurPtr++; switch (CurChar) { case '$': CurStringPiece += "$$"; continue; @@ -869,12 +895,14 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces, } /// Assemble final IR asm string (GCC-style). -std::string GCCAsmStmt::generateAsmString(const ASTContext &C) const { +std::string +GCCAsmStmt::generateAsmString(const ASTContext &C, + SmallVectorImpl<unsigned> *SourceOffsets) const { // Analyze the asm string to decompose it into its pieces. We know that Sema // has already done this, so it is guaranteed to be successful. SmallVector<GCCAsmStmt::AsmStringPiece, 4> Pieces; unsigned DiagOffs; - AnalyzeAsmString(Pieces, C, DiagOffs); + AnalyzeAsmString(Pieces, C, DiagOffs, SourceOffsets); std::string AsmString; for (const auto &Piece : Pieces) { diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index bf6e6eb50f555..f78bedfa9a9fd 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -26,6 +26,7 @@ #include "clang/Basic/PrettyStackTrace.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Lex/Token.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallSet.h" @@ -2629,16 +2630,48 @@ CodeGenFunction::EmitAsmInput(const TargetInfo::ConstraintInfo &Info, InputExpr->getExprLoc()); } +static llvm::MDNode * +getAsmSrcLocInfo(ArrayRef<SourceLocation> SourceLocs, CodeGenFunction &CGF, + ArrayRef<std::pair<unsigned, SourceLocation>> DebugLocs = {}) { + SmallVector<llvm::Metadata *, 8> Locs; + llvm::LLVMContext &Ctx = CGF.getLLVMContext(); + + for (SourceLocation Loc : SourceLocs) { + llvm::Constant *RawLoc = + llvm::ConstantInt::get(CGF.Int64Ty, Loc.getRawEncoding()); + Locs.push_back(llvm::ConstantAsMetadata::get(RawLoc)); + } + + if (CGF.getDebugInfo() && CGF.CGM.getCodeGenOpts().EmitCodeView && + !DebugLocs.empty()) { + SmallVector<llvm::Metadata *, 16> Entries; + Entries.push_back(llvm::MDString::get(Ctx, "inlineasm.dbg.offset")); + llvm::Type *Int32Ty = llvm::Type::getInt32Ty(Ctx); + for (auto [Offset, Loc] : DebugLocs) { + llvm::DebugLoc DL = CGF.SourceLocToDebugLoc(Loc); + Entries.push_back(llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(Int32Ty, Offset))); + Entries.push_back(llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(Int32Ty, DL ? DL.getLine() : 0))); + Entries.push_back(llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(Int32Ty, DL ? DL.getCol() : 0))); + } + Locs.push_back(llvm::MDNode::get(Ctx, Entries)); + } + + return llvm::MDNode::get(Ctx, Locs); +} + /// getAsmSrcLocInfo - Return the !srcloc metadata node to attach to an inline /// asm call instruction. The !srcloc MDNode contains a list of constant /// integers which are the source locations of the start of each line in the /// asm. -static llvm::MDNode *getAsmSrcLocInfo(const StringLiteral *Str, - CodeGenFunction &CGF) { - SmallVector<llvm::Metadata *, 8> Locs; +static llvm::MDNode *getGCCAsmSrcLocInfo(const GCCAsmStmt &S, + const StringLiteral *Str, + CodeGenFunction &CGF) { + SmallVector<SourceLocation, 8> Locs; // Add the location of the first line to the MDNode. - Locs.push_back(llvm::ConstantAsMetadata::get(llvm::ConstantInt::get( - CGF.Int64Ty, Str->getBeginLoc().getRawEncoding()))); + Locs.push_back(Str->getBeginLoc()); StringRef StrVal = Str->getString(); if (!StrVal.empty()) { const SourceManager &SM = CGF.CGM.getContext().getSourceManager(); @@ -2649,15 +2682,85 @@ static llvm::MDNode *getAsmSrcLocInfo(const StringLiteral *Str, // Add the location of the start of each subsequent line of the asm to the // MDNode. for (unsigned i = 0, e = StrVal.size() - 1; i != e; ++i) { - if (StrVal[i] != '\n') continue; + if (StrVal[i] != '\n') + continue; SourceLocation LineLoc = Str->getLocationOfByte( i + 1, SM, LangOpts, CGF.getTarget(), &StartToken, &ByteOffset); - Locs.push_back(llvm::ConstantAsMetadata::get( - llvm::ConstantInt::get(CGF.Int64Ty, LineLoc.getRawEncoding()))); + Locs.push_back(LineLoc); } } - return llvm::MDNode::get(CGF.getLLVMContext(), Locs); + SmallVector<std::pair<unsigned, SourceLocation>, 16> DebugLocs; + if (CGF.getDebugInfo() && CGF.CGM.getCodeGenOpts().EmitCodeView) { + SmallVector<unsigned, 64> SourceOffsets; + std::string Template = + S.generateAsmString(CGF.getContext(), &SourceOffsets); + assert(Template.size() == SourceOffsets.size()); + const SourceManager &SM = CGF.CGM.getContext().getSourceManager(); + unsigned StartToken = 0, ByteOffset = 0; + // Record word starts and punctuation without assuming a target-specific + // statement separator. In particular, ';' need not introduce a new line. + // Positions refer to the LLVM template, after Clang's escape conversion. + auto IsWordChar = [](char C) { + return llvm::isAlnum(C) || C == '_' || C == '.'; + }; + for (unsigned I = 0; I < Template.size(); ++I) { + if (llvm::isSpace(Template[I]) || + (I && SourceOffsets[I] == SourceOffsets[I - 1]) || + (I && IsWordChar(Template[I]) && IsWordChar(Template[I - 1]))) + continue; + SourceLocation Loc = + Str->getLocationOfByte(SourceOffsets[I], SM, CGF.CGM.getLangOpts(), + CGF.getTarget(), &StartToken, &ByteOffset); + DebugLocs.emplace_back(I, Loc); + } + } + return getAsmSrcLocInfo(Locs, CGF, DebugLocs); +} + +static llvm::MDNode *getMSAsmSrcLocInfo(const MSAsmStmt &S, StringRef AsmString, + CodeGenFunction &CGF) { + SmallVector<SourceLocation, 8> Locs; + + MSAsmStmt &NonConstS = const_cast<MSAsmStmt &>(S); + ArrayRef<Token> AsmToks(NonConstS.getAsmToks(), NonConstS.getNumAsmToks()); + bool IsNewStatement = true; + for (const Token &Tok : AsmToks) { + if (!IsNewStatement && (Tok.is(tok::kw_asm) || Tok.isAtStartOfLine())) + IsNewStatement = true; + + if (IsNewStatement) { + if (Tok.is(tok::kw_asm)) + continue; + + Locs.push_back(Tok.getLocation()); + IsNewStatement = false; + continue; + } + + if (!Tok.is(tok::kw_asm)) + IsNewStatement = false; + } + + if (Locs.empty()) + Locs.push_back(S.getAsmLoc()); + + size_t ExpectedLocs = AsmString.empty() ? 1 : AsmString.count('\n') + 1; + if (Locs.size() > ExpectedLocs) + Locs.truncate(ExpectedLocs); + while (Locs.size() < ExpectedLocs) + Locs.push_back(Locs.back()); + + SmallVector<std::pair<unsigned, SourceLocation>, 8> DebugLocs; + unsigned Offset = 0; + for (SourceLocation Loc : Locs) { + DebugLocs.emplace_back(Offset, Loc); + size_t End = AsmString.find('\n', Offset); + if (End == StringRef::npos) + break; + Offset = End + 1; + } + return getAsmSrcLocInfo(Locs, CGF, DebugLocs); } namespace clang { @@ -3311,15 +3414,14 @@ void AsmConstraintsInfo::UpdateAsmCallInst( if (const auto *gccAsmStmt = dyn_cast<GCCAsmStmt>(&S); gccAsmStmt && (SL = dyn_cast<StringLiteral>(gccAsmStmt->getAsmStringExpr()))) { - Result.setMetadata("srcloc", getAsmSrcLocInfo(SL, CGF)); + Result.setMetadata("srcloc", getGCCAsmSrcLocInfo(*gccAsmStmt, SL, CGF)); + } else if (const auto *msAsmStmt = dyn_cast<MSAsmStmt>(&S)) { + Result.setMetadata("srcloc", + getMSAsmSrcLocInfo(*msAsmStmt, AsmString, CGF)); } else { // At least put the line number on MS inline asm blobs and GCC asm constexpr // strings. - llvm::Constant *Loc = - llvm::ConstantInt::get(CGF.Int64Ty, S.getAsmLoc().getRawEncoding()); - Result.setMetadata("srcloc", - llvm::MDNode::get(getLLVMContext(), - llvm::ConstantAsMetadata::get(Loc))); + Result.setMetadata("srcloc", getAsmSrcLocInfo({S.getAsmLoc()}, CGF)); } // Make inline-asm calls Key for the debug info feature Key Instructions. diff --git a/clang/test/CodeGen/inline-asm-codeview.c b/clang/test/CodeGen/inline-asm-codeview.c new file mode 100644 index 0000000000000..b929810d6dd49 --- /dev/null +++ b/clang/test/CodeGen/inline-asm-codeview.c @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 -triple i386-pc-windows-msvc -gcodeview \ +// RUN: -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple i386-pc-windows-msvc -gcodeview \ +// RUN: -debug-info-kind=limited -S -o - %s | FileCheck %s --check-prefix=ASM +// RUN: %clang_cc1 -triple i386-pc-windows-msvc -emit-llvm -o - %s \ +// RUN: | FileCheck %s --check-prefix=NO-DEBUG --implicit-check-not=inlineasm.dbg.offset + +// The concatenated template has no newlines. Each instruction nevertheless +// belongs to a different source line. Named operands, %% and $ change length +// when converted to an LLVM asm template. +#line 100 "inline-asm-codeview.c" +void gnu(int x) { + asm volatile( + "movl %[value], %%eax;" + "addl $1, %%eax;" + "incl %%eax" + : + : [value] "r"(x) + : "eax", "cc"); +} + +// ASM-LABEL: _gnu: +// ASM: #APP +// ASM: .cv_loc 0 1 102 8 +// ASM-NEXT: movl +// ASM-NEXT: .cv_loc 0 1 103 8 +// ASM-NEXT: addl $1, %eax +// ASM-NEXT: .cv_loc 0 1 104 8 +// ASM-NEXT: incl %eax +// ASM: #NO_APP + +// CHECK: call void asm sideeffect "movl $0, %eax;addl $$1, %eax;incl %eax" +// CHECK-SAME: !srcloc ![[SRC:[0-9]+]] +// NO-DEBUG: call void asm sideeffect +// NO-DEBUG-SAME: !srcloc + +// Cover dialect alternatives in the frontend mapping. +#line 200 "inline-asm-codeview.c" +void variants(int x) { + asm volatile( + "{incl %0|inc %0};" + "nop" + : "+r"(x)); +} + +// CHECK: call i32 asm sideeffect "$(incl $0$|inc $0$);nop" +// CHECK-SAME: !srcloc ![[VAR_SRC:[0-9]+]] + +// Basic GNU asm also distinguishes instructions on the same source line. +#line 300 "inline-asm-codeview.c" +void basic(void) { + asm("nop;nop"); +} + +// CHECK: call void asm sideeffect "nop;nop" +// CHECK-SAME: !srcloc ![[BASIC_SRC:[0-9]+]] +// ASM-LABEL: _basic: +// ASM: #APP +// ASM: .cv_loc 2 1 301 8 +// ASM-NEXT: nop +// ASM-NEXT: .cv_loc 2 1 301 12 +// ASM-NEXT: nop +// ASM: #NO_APP + +// CHECK: ![[SRC]] = !{i64 {{[0-9]+}}, ![[LOCS:[0-9]+]]} +// CHECK: ![[LOCS]] = !{!"inlineasm.dbg.offset", i32 0, i32 102, i32 8, +// CHECK-SAME: i32 14, i32 103, i32 8, +// CHECK-SAME: i32 29, i32 104, i32 8, +// CHECK: ![[VAR_SRC]] = !{i64 {{[0-9]+}}, ![[VAR_LOCS:[0-9]+]]} +// CHECK: ![[VAR_LOCS]] = !{!"inlineasm.dbg.offset", +// CHECK-SAME: i32 2, i32 202, i32 9, +// CHECK-SAME: i32 11, i32 202, i32 17, +// CHECK-SAME: i32 20, i32 203, i32 8} +// CHECK: ![[BASIC_SRC]] = !{i64 {{[0-9]+}}, ![[BASIC_LOCS:[0-9]+]]} +// CHECK: ![[BASIC_LOCS]] = !{!"inlineasm.dbg.offset", i32 0, i32 301, i32 8, i32 3, i32 301, i32 11, i32 4, i32 301, i32 12} diff --git a/clang/test/CodeGen/ms-inline-asm-codeview.cpp b/clang/test/CodeGen/ms-inline-asm-codeview.cpp new file mode 100644 index 0000000000000..a24dc8965422b --- /dev/null +++ b/clang/test/CodeGen/ms-inline-asm-codeview.cpp @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -triple i386-pc-windows-msvc -fasm-blocks -gcodeview \ +// RUN: -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s + +#line 100 "t.cpp" +int a, b; + +int main(int argc, char **argv) +{ + __asm + { + lea eax, a + mov dword ptr [eax], 1 + + lea ebx, b + mov dword ptr [ebx], 1 + + mov eax, [eax] + add [ebx], eax + + inc eax + + imul dword ptr [ebx] + mov [ebx], eax + } + + return 0; +} + +// CHECK: call i32 asm sideeffect inteldialect +// CHECK-SAME: !srcloc ![[SRCLOC:[0-9]+]] +// CHECK: ![[SRCLOC]] = !{i64 {{[0-9]+}}, i64 {{[0-9]+}}, i64 {{[0-9]+}}, i64 {{[0-9]+}}, i64 {{[0-9]+}}, i64 {{[0-9]+}}, i64 {{[0-9]+}}, i64 {{[0-9]+}}, i64 {{[0-9]+}}, ![[DBGLOCS:[0-9]+]]} +// CHECK: ![[DBGLOCS]] = !{!"inlineasm.dbg.offset", i32 0, i32 106, i32 9, i32 12, i32 107, i32 9, i32 38, i32 109, i32 9, i32 51, i32 110, i32 9, i32 77, i32 112, i32 9, i32 93, i32 113, i32 9, i32 109, i32 115, i32 9, i32 118, i32 117, i32 9, i32 140, i32 118, i32 9} diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md index 14caff88243c1..b76d2929ffe29 100644 --- a/llvm/docs/LangRef.md +++ b/llvm/docs/LangRef.md @@ -6530,6 +6530,43 @@ in the IR. If the MDNode contains multiple constants, the code generator will use the one that corresponds to the line of the asm that the error occurs on. +An optional final nested node describes source locations within the inline +assembly template. It starts with the string `"inlineasm.dbg.offset"`, followed +by one or more triples of `i32` values: a byte offset, a source line number, +and a source column number. Offsets are zero-based, strictly increasing, and +refer to bytes in the decoded LLVM IR template string, **before** operand +substitution and dialect-alternative selection. For example, `\0A` counts as +one byte and `$0` counts as two bytes. Offsets must lie within the template. + +Each entry supplies the source location from its offset up to the next entry, +or the end of the template for the last entry. Positions before the first +entry have no per-instruction source location. Source lines and columns are +one-based; line zero means no source location is available, and column zero +means the column is unknown. The scope and inlining context come from the +call instruction's `!dbg` attachment, which is required to use these entries. +Locations must refer to the source file associated with that scope. + +The code generator maps parsed instructions back to template positions, +accounting for operand substitution, escaped characters, dialect alternatives, +and any synthetic assembly syntax directives. Thus, instructions separated +by `;` can have different source locations even without a newline in the +template. The preceding location cookies keep their diagnostic meaning and +are indexed by assembly line, independently of this nested node. Existing +`!srcloc` nodes containing only cookies remain supported. + +```llvm +call void asm sideeffect "nop;nop", ""(), !dbg !40, !srcloc !42 +... +!42 = !{i64 1234567, !43} +!43 = !{!"inlineasm.dbg.offset", i32 0, i32 10, i32 5, + i32 4, i32 11, i32 5} +``` + +This representation is independent of the source assembly syntax and debug +information format. Currently, CodeView consumes these locations when the +integrated assembler parses inline assembly. Other debug information formats +continue to use the call instruction's ordinary debug location. + (metadata)= ## Metadata diff --git a/llvm/include/llvm/CodeGen/AsmPrinterHandler.h b/llvm/include/llvm/CodeGen/AsmPrinterHandler.h index ab737fa00ce14..ed15f6c23fe6d 100644 --- a/llvm/include/llvm/CodeGen/AsmPrinterHandler.h +++ b/llvm/include/llvm/CodeGen/AsmPrinterHandler.h @@ -20,6 +20,7 @@ namespace llvm { class AsmPrinter; +class DILocation; class MachineBasicBlock; class MachineFunction; class MachineInstr; @@ -72,6 +73,10 @@ class LLVM_ABI AsmPrinterHandler { /// Process beginning of an instruction. virtual void beginInstruction(const MachineInstr *MI) {} + /// Process beginning of an instruction parsed from an inline asm blob. + virtual void beginInlineAsmInstruction(const MachineInstr *MI, + const DILocation *Loc) {} + /// Process end of an instruction. virtual void endInstruction() {} diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h index b04018e43bbe0..b73535f3a0b1b 100644 --- a/llvm/include/llvm/CodeGen/MachineInstr.h +++ b/llvm/include/llvm/CodeGen/MachineInstr.h @@ -586,6 +586,11 @@ class MachineInstr /// the loc cookie from it. LLVM_ABI const MDNode *getLocCookieMD() const; + /// For inline asm, get the nested source location metadata in !srcloc, if + /// present. Entries contain template byte offsets and source line/column + /// numbers. + LLVM_ABI const MDNode *getInlineAsmSourceLocMD() const; + /// Emit an error referring to the source location of this instruction. This /// should only be used for inline assembly that is somehow impossible to /// compile. Other errors should have been handled much earlier. diff --git a/llvm/include/llvm/IR/InlineAsm.h b/llvm/include/llvm/IR/InlineAsm.h index 564f2e7df2dd3..f991523572ca4 100644 --- a/llvm/include/llvm/IR/InlineAsm.h +++ b/llvm/include/llvm/IR/InlineAsm.h @@ -29,11 +29,16 @@ namespace llvm { class Error; class FunctionType; +class MDNode; class PointerType; template <class ConstantClass> class ConstantUniqueMap; class InlineAsm final : public Value { public: + /// Return a well-formed inlineasm.dbg.offset node appended to !srcloc. + /// Each entry is an i32 template byte offset, source line and source column. + LLVM_ABI static const MDNode *getSourceLocMetadata(const MDNode *LocMD); + enum AsmDialect { AD_ATT, AD_Intel diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h index 614b8d79c5da4..2f11374e2a74f 100644 --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -31,6 +31,7 @@ #include "llvm/TargetParser/ARMTargetParser.h" #include <cassert> #include <cstdint> +#include <functional> #include <memory> #include <optional> #include <string> @@ -247,6 +248,10 @@ class LLVM_ABI MCStreamer { /// locations for diagnostics. const SMLoc *StartTokLocPtr = nullptr; + /// Callback used by inline asm parsing to give AsmPrinter a chance to emit + /// source location directives immediately before each parsed instruction. + std::function<void(SMLoc)> InlineAsmSourceLocCallback; + /// The next unique ID to use when creating a WinCFI-related section (.pdata /// or .xdata). This ID ensures that we have a one-to-one mapping from /// code section to unwind info section, which MSVC's incremental linker @@ -293,6 +298,8 @@ class LLVM_ABI MCStreamer { virtual void emitRawTextImpl(StringRef String); + void emitInlineAsmSourceLoc(SMLoc Loc); + /// Returns true if the .cv_loc directive is in the right section. bool checkCVLocSection(unsigned FuncId, SMLoc Loc); @@ -315,6 +322,10 @@ class LLVM_ABI MCStreamer { return StartTokLocPtr ? *StartTokLocPtr : SMLoc(); } + void setInlineAsmSourceLocCallback(std::function<void(SMLoc)> Callback) { + InlineAsmSourceLocCallback = std::move(Callback); + } + void setLFIRewriter(std::unique_ptr<MCLFIRewriter> Rewriter); MCLFIRewriter *getLFIRewriter() { return LFIRewriter.get(); } diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp index b3bcfc08643f1..72ce04b872c17 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp @@ -10,11 +10,14 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/AsmPrinterHandler.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineModuleInfo.h" @@ -22,6 +25,7 @@ #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/InlineAsm.h" #include "llvm/IR/LLVMContext.h" @@ -131,16 +135,62 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI, Parser->setTargetParser(*TAP); emitInlineAsmStart(); + const MDNode *InlineAsmSourceLocs = + MI ? InlineAsm::getSourceLocMetadata(LocMDNode) : nullptr; + if (InlineAsmSourceLocs) { + SmallVector<unsigned, 16> Offsets; + for (unsigned I = 1; I < InlineAsmSourceLocs->getNumOperands(); I += 3) + Offsets.push_back( + mdconst::extract<ConstantInt>(InlineAsmSourceLocs->getOperand(I)) + ->getZExtValue()); + // The offsets have already been remapped to the parser's input buffer. + // Keep the callback's lookup table alive until parsing finishes. + OutStreamer->setInlineAsmSourceLocCallback( + [this, &SrcMgr, BufNum, Offsets = std::move(Offsets), + InlineAsmSourceLocs, MI](SMLoc Loc) { + if (!Loc.isValid() || SrcMgr.FindBufferContainingLoc(Loc) != BufNum) + return; + + size_t Offset = Loc.getPointer() - + SrcMgr.getMemoryBuffer(BufNum)->getBufferStart(); + auto It = llvm::upper_bound(Offsets, Offset); + if (It == Offsets.begin()) + return; + unsigned LocIdx = (It - Offsets.begin() - 1) * 3 + 2; + + const auto *Line = mdconst::dyn_extract<ConstantInt>( + InlineAsmSourceLocs->getOperand(LocIdx)); + const auto *Column = mdconst::dyn_extract<ConstantInt>( + InlineAsmSourceLocs->getOperand(LocIdx + 1)); + if (!Line || !Column || Line->isZero()) + return; + + DebugLoc BaseDL = MI->getDebugLoc(); + if (!BaseDL) + return; + + auto *DIL = DILocation::get( + MI->getMF()->getFunction().getContext(), Line->getZExtValue(), + Column->getZExtValue(), BaseDL->getScope(), + BaseDL->getInlinedAt(), BaseDL->isImplicitCode(), + BaseDL->getAtomGroup(), BaseDL->getAtomRank()); + for (auto &Handler : Handlers) + Handler->beginInlineAsmInstruction(MI, DIL); + }); + } // Don't implicitly switch to the text section before the asm. (void)Parser->Run(/*NoInitialTextSection*/ true, /*NoFinalize*/ true); + if (InlineAsmSourceLocs) + OutStreamer->setInlineAsmSourceLocCallback(nullptr); emitInlineAsmEnd(STI, &TAP->getSTI(), MI); } static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI, MachineModuleInfo *MMI, const MCAsmInfo &MAI, AsmPrinter *AP, uint64_t LocCookie, - raw_ostream &OS) { + raw_ostream &OS, + SmallVectorImpl<unsigned> *SourceOffsets) { bool InputIsIntelDialect = MI->getInlineAsmDialect() == InlineAsm::AD_Intel; if (InputIsIntelDialect) { @@ -163,6 +213,15 @@ static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI, OS << '\t'; while (*LastEmitted) { + // Synthetic text (e.g. .intel_syntax) has no template position. Literal + // bytes map one-to-one; all bytes of a substituted operand map to its '$'. + if (SourceOffsets) + SourceOffsets->resize(OS.tell(), ~0U); + unsigned TemplateOffset = LastEmitted - AsmStr; + auto RecordOffsets = llvm::scope_exit([&] { + if (SourceOffsets) + SourceOffsets->resize(OS.tell(), TemplateOffset); + }); switch (*LastEmitted) { default: { // Not a special case, emit the string section literally. @@ -170,8 +229,12 @@ static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI, while (*LiteralEnd && *LiteralEnd != '{' && *LiteralEnd != '|' && *LiteralEnd != '}' && *LiteralEnd != '$' && *LiteralEnd != '\n') ++LiteralEnd; - if (CurVariant == -1 || CurVariant == AsmPrinterVariant) + if (CurVariant == -1 || CurVariant == AsmPrinterVariant) { OS.write(LastEmitted, LiteralEnd - LastEmitted); + if (SourceOffsets) + for (const char *P = LastEmitted; P != LiteralEnd; ++P) + SourceOffsets->push_back(P - AsmStr); + } LastEmitted = LiteralEnd; break; } @@ -365,7 +428,42 @@ void AsmPrinter::emitInlineAsm(const MachineInstr *MI) { raw_svector_ostream OS(StringData); AsmPrinter *AP = const_cast<AsmPrinter*>(this); - EmitInlineAsmStr(AsmStr, MI, MMI, MAI, AP, LocCookie, OS); + const MDNode *SourceLocs = MI->getInlineAsmSourceLocMD(); + SmallVector<unsigned, 256> SourceOffsets; + EmitInlineAsmStr(AsmStr, MI, MMI, MAI, AP, LocCookie, OS, + SourceLocs ? &SourceOffsets : nullptr); + + if (SourceLocs) { + // Convert template offsets to offsets in StringData, following the + // selected dialect alternative and the actual operand spellings. + SmallVector<Metadata *, 16> Entries; + Entries.push_back(SourceLocs->getOperand(0)); + unsigned Next = 1, Current = 0, Previous = 0; + for (unsigned I = 0; I < SourceOffsets.size(); ++I) { + unsigned Offset = SourceOffsets[I]; + if (Offset == ~0U) + continue; + while (Next < SourceLocs->getNumOperands() && + mdconst::extract<ConstantInt>(SourceLocs->getOperand(Next)) + ->getZExtValue() <= Offset) { + Current = Next; + Next += 3; + } + if (!Current || Current == Previous) + continue; + Entries.push_back(ConstantAsMetadata::get(ConstantInt::get( + Type::getInt32Ty(MF->getFunction().getContext()), I))); + Entries.push_back(SourceLocs->getOperand(Current + 1)); + Entries.push_back(SourceLocs->getOperand(Current + 2)); + Previous = Current; + } + SmallVector<Metadata *, 8> Locs; + for (unsigned I = 0; I + 1 < LocMD->getNumOperands(); ++I) + Locs.push_back(LocMD->getOperand(I)); + if (Entries.size() > 1) + Locs.push_back(MDNode::get(MF->getFunction().getContext(), Entries)); + LocMD = MDNode::get(MF->getFunction().getContext(), Locs); + } // Emit warnings if we use reserved registers on the clobber list, as // that might lead to undefined behaviour. diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp index 84ea5e349f01d..3615d9cee670a 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -3177,6 +3177,14 @@ void CodeViewDebug::beginInstruction(const MachineInstr *MI) { maybeRecordLocation(DL, Asm->MF); } +void CodeViewDebug::beginInlineAsmInstruction(const MachineInstr *MI, + const DILocation *Loc) { + if (!Asm || !CurFn || !MI || !Loc) + return; + + maybeRecordLocation(DebugLoc(Loc), Asm->MF); +} + MCSymbol *CodeViewDebug::beginCVSubsection(DebugSubsectionKind Kind) { MCSymbol *BeginLabel = MMI->getContext().createTempSymbol(), *EndLabel = MMI->getContext().createTempSymbol(); diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h index e28e0ab6da36b..bf55d73468473 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h @@ -548,6 +548,10 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase { /// Process beginning of an instruction. void beginInstruction(const MachineInstr *MI) override; + + /// Process beginning of an instruction parsed from an inline asm blob. + void beginInlineAsmInstruction(const MachineInstr *MI, + const DILocation *Loc) override; }; template <> struct DenseMapInfo<CodeViewDebug::LocalVarDef> { diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp index e94de37261c53..cb61b9af50e5e 100644 --- a/llvm/lib/CodeGen/MachineInstr.cpp +++ b/llvm/lib/CodeGen/MachineInstr.cpp @@ -2352,6 +2352,10 @@ const MDNode *MachineInstr::getLocCookieMD() const { return nullptr; } +const MDNode *MachineInstr::getInlineAsmSourceLocMD() const { + return InlineAsm::getSourceLocMetadata(getLocCookieMD()); +} + void MachineInstr::emitInlineAsmError(const Twine &Msg) const { assert(isInlineAsm()); const MDNode *LocMD = getLocCookieMD(); diff --git a/llvm/lib/CodeGen/MachineModuleInfo.cpp b/llvm/lib/CodeGen/MachineModuleInfo.cpp index dd9defdec276b..94d71ff83972c 100644 --- a/llvm/lib/CodeGen/MachineModuleInfo.cpp +++ b/llvm/lib/CodeGen/MachineModuleInfo.cpp @@ -11,7 +11,9 @@ #include "llvm/CodeGen/Passes.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/InlineAsm.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" #include "llvm/InitializePasses.h" #include "llvm/Target/TargetLoweringObjectFile.h" @@ -192,10 +194,13 @@ static uint64_t getLocCookie(const SMDiagnostic &SMD, const SourceMgr &SrcMgr, uint64_t LocCookie = 0; if (LocInfo) { unsigned ErrorLine = SMD.getLineNo() - 1; - if (ErrorLine >= LocInfo->getNumOperands()) + unsigned NumRawLocs = LocInfo->getNumOperands(); + if (InlineAsm::getSourceLocMetadata(LocInfo)) + --NumRawLocs; + if (ErrorLine >= NumRawLocs) ErrorLine = 0; - if (LocInfo->getNumOperands() != 0) + if (NumRawLocs != 0) if (const ConstantInt *CI = mdconst::dyn_extract<ConstantInt>(LocInfo->getOperand(ErrorLine))) LocCookie = CI->getZExtValue(); diff --git a/llvm/lib/IR/InlineAsm.cpp b/llvm/lib/IR/InlineAsm.cpp index 922081468a775..356e9af8f1cc9 100644 --- a/llvm/lib/IR/InlineAsm.cpp +++ b/llvm/lib/IR/InlineAsm.cpp @@ -14,8 +14,10 @@ #include "ConstantsContext.h" #include "LLVMContextImpl.h" #include "llvm/ADT/StringRef.h" +#include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Metadata.h" #include "llvm/IR/Value.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" @@ -27,6 +29,32 @@ using namespace llvm; +const MDNode *InlineAsm::getSourceLocMetadata(const MDNode *LocMD) { + if (!LocMD || LocMD->getNumOperands() < 2) + return nullptr; + const auto *Locs = + dyn_cast_or_null<MDNode>(LocMD->getOperand(LocMD->getNumOperands() - 1)); + if (!Locs || Locs->getNumOperands() < 4 || + (Locs->getNumOperands() - 1) % 3 != 0) + return nullptr; + const auto *Tag = dyn_cast_or_null<MDString>(Locs->getOperand(0)); + if (!Tag || Tag->getString() != "inlineasm.dbg.offset") + return nullptr; + uint64_t Previous = 0; + for (unsigned I = 1; I < Locs->getNumOperands(); ++I) { + const auto *Value = mdconst::dyn_extract<ConstantInt>(Locs->getOperand(I)); + if (!Value || Value->getBitWidth() != 32) + return nullptr; + if ((I - 1) % 3 == 0) { + uint64_t Offset = Value->getZExtValue(); + if (I != 1 && Offset <= Previous) + return nullptr; + Previous = Offset; + } + } + return Locs; +} + InlineAsm::InlineAsm(FunctionType *FTy, const std::string &asmString, const std::string &constraints, bool hasSideEffects, bool isAlignStack, AsmDialect asmDialect, bool canThrow) diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index 2dd964edb738a..7378430f735e9 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -2629,6 +2629,8 @@ void MCAsmStreamer::emitInstruction(const MCInst &Inst, if (LFIRewriter && LFIRewriter->rewriteInst(Inst, *this, STI)) return; + emitInlineAsmSourceLoc(getStartTokLoc()); + if (CurFrag) { MCSection *Sec = getCurrentSectionOnly(); Sec->setHasInstructions(true); diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp index 1d51fbc46a43c..f116c26270260 100644 --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -1399,7 +1399,14 @@ void MCStreamer::visitUsedExpr(const MCExpr &Expr) { } } +void MCStreamer::emitInlineAsmSourceLoc(SMLoc Loc) { + if (InlineAsmSourceLocCallback) + InlineAsmSourceLocCallback(Loc); +} + void MCStreamer::emitInstruction(const MCInst &Inst, const MCSubtargetInfo &) { + emitInlineAsmSourceLoc(getStartTokLoc()); + // Scan for values. for (unsigned i = Inst.getNumOperands(); i--;) if (Inst.getOperand(i).isExpr()) diff --git a/llvm/test/DebugInfo/COFF/gnu-inline-asm-source-loc.ll b/llvm/test/DebugInfo/COFF/gnu-inline-asm-source-loc.ll new file mode 100644 index 0000000000000..9f8a775616dd0 --- /dev/null +++ b/llvm/test/DebugInfo/COFF/gnu-inline-asm-source-loc.ll @@ -0,0 +1,96 @@ +; RUN: llc -mtriple=i386-windows-msvc -filetype=asm < %s | FileCheck %s --check-prefixes=CHECK,ATT +; RUN: llc -mtriple=i386-windows-msvc -x86-asm-syntax=intel -filetype=asm < %s | FileCheck %s --check-prefixes=CHECK,INTEL +; RUN: llc -mtriple=i386-windows-msvc -filetype=obj < %s | llvm-readobj --codeview - | FileCheck %s --check-prefix=OBJ + +; Check that object emission puts each location at the corresponding encoded +; instruction, not merely at successive byte addresses. +; OBJ: FunctionLineTable [ +; OBJ-NEXT: Name: _gnu +; OBJ: LineNumberStart: 5 +; OBJ: +0x[[#%x,START:]] [ +; OBJ-NEXT: LineNumberStart: 10 +; OBJ: +0x[[#%x,START + 2]] [ +; OBJ-NEXT: LineNumberStart: 11 +; OBJ: +0x[[#%x,START + 5]] [ +; OBJ-NEXT: LineNumberStart: 12 + +; Substituting $0 grows the string, while unescaping $$ shrinks it. The +; instructions share an asm line but originate on different source lines. +; CHECK-LABEL: _gnu: +; CHECK: #APP +; CHECK: .cv_loc 0 1 10 3 +; ATT-NEXT: movl %ecx, %eax +; INTEL-NEXT: mov eax, ecx +; CHECK-NEXT: .cv_loc 0 1 11 5 +; ATT-NEXT: addl $1, %eax +; INTEL-NEXT: add eax, 1 +; CHECK-NEXT: .cv_loc 0 1 12 7 +; ATT-NEXT: incl %eax +; INTEL-NEXT: inc eax +; CHECK: #NO_APP + +define void @gnu(i32 %x) !dbg !8 { + call void asm sideeffect "movl $0, %eax;addl $$1, %eax;incl %eax", "{ecx},~{eax},~{flags}"(i32 %x), !srcloc !12, !dbg !13 + ret void, !dbg !13 +} + +; Both dialect alternatives have locations. Test selecting either alternative +; and the common instruction that follows it, including the Intel prefix. +; CHECK-LABEL: _variants: +; CHECK: #APP +; CHECK: .cv_loc 1 1 21 3 +; ATT-NEXT: incl %eax +; INTEL-NEXT: inc eax +; CHECK-NEXT: .cv_loc 1 1 23 3 +; CHECK-NEXT: nop +; CHECK: #NO_APP +; CHECK: #APP +; CHECK: .cv_loc 1 1 22 3 +; ATT-NEXT: incl %eax +; INTEL-NEXT: inc eax +; CHECK-NEXT: .cv_loc 1 1 23 3 +; CHECK-NEXT: nop +; CHECK: #NO_APP + +define void @variants() !dbg !20 { + call void asm sideeffect "$(incl %eax$|inc eax$); nop", "~{eax},~{flags}"(), !srcloc !22, !dbg !21 + call void asm sideeffect inteldialect "$(incl %eax$|inc eax$); nop", "~{eax},~{flags}"(), !srcloc !22, !dbg !21 + ret void, !dbg !21 +} + +; An invalid (unsorted) location table must not suppress the ordinary !dbg +; location or crash while looking up offsets. +; CHECK-LABEL: _fallback: +; CHECK: .cv_loc 2 1 30 3 +; CHECK: #APP +; CHECK-NOT: .cv_loc +; CHECK: nop +; CHECK-NOT: .cv_loc +; CHECK: #NO_APP + +define void @fallback() !dbg !30 { + call void asm sideeffect "nop", ""(), !srcloc !32, !dbg !31 + ret void, !dbg !31 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4} +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +!1 = !DIFile(filename: "inline-asm-codeview.c", directory: "/") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !DISubroutineType(types: !6) +!6 = !{null} +!8 = distinct !DISubprogram(name: "gnu", scope: !1, file: !1, line: 1, type: !5, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!12 = !{i64 1234, !14} +!13 = !DILocation(line: 5, column: 3, scope: !8) +!14 = !{!"inlineasm.dbg.offset", i32 0, i32 10, i32 3, i32 14, i32 11, i32 5, i32 29, i32 12, i32 7} +!20 = distinct !DISubprogram(name: "variants", scope: !1, file: !1, line: 20, type: !5, scopeLine: 20, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!21 = !DILocation(line: 20, column: 3, scope: !20) +!22 = !{i64 1234, !23} +!23 = !{!"inlineasm.dbg.offset", i32 2, i32 21, i32 3, i32 13, i32 22, i32 3, i32 24, i32 23, i32 3} +!30 = distinct !DISubprogram(name: "fallback", scope: !1, file: !1, line: 30, type: !5, scopeLine: 30, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!31 = !DILocation(line: 30, column: 3, scope: !30) +!32 = !{i64 1234, !33} +!33 = !{!"inlineasm.dbg.offset", i32 1, i32 31, i32 3, i32 0, i32 32, i32 3} diff --git a/llvm/test/DebugInfo/COFF/ms-inline-asm-source-loc.ll b/llvm/test/DebugInfo/COFF/ms-inline-asm-source-loc.ll new file mode 100644 index 0000000000000..b6307c0dfd7de --- /dev/null +++ b/llvm/test/DebugInfo/COFF/ms-inline-asm-source-loc.ll @@ -0,0 +1,51 @@ +; RUN: llc -mtriple=i386-windows-msvc -x86-asm-syntax=intel -filetype=asm < %s | FileCheck %s + +; CHECK: #APP +; CHECK: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 6 9 +; CHECK-NEXT: {{[ \t]*}}lea eax, [a] +; CHECK-NEXT: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 7 9 +; CHECK-NEXT: {{[ \t]*}}mov dword ptr [eax], 1 +; CHECK: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 9 9 +; CHECK-NEXT: {{[ \t]*}}lea ebx, [b] +; CHECK: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 10 9 +; CHECK-NEXT: {{[ \t]*}}mov dword ptr [ebx], 1 +; CHECK: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 12 9 +; CHECK-NEXT: {{[ \t]*}}mov eax, dword ptr [eax] +; CHECK: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 13 9 +; CHECK-NEXT: {{[ \t]*}}add dword ptr [ebx], eax +; CHECK: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 15 9 +; CHECK-NEXT: {{[ \t]*}}inc eax +; CHECK: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 17 9 +; CHECK-NEXT: {{[ \t]*}}imul dword ptr [ebx] +; CHECK: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 18 9 +; CHECK-NEXT: {{[ \t]*}}mov dword ptr [ebx], eax +; CHECK: #NO_APP + +target triple = "i386-pc-windows-msvc" + +@a = dso_local global i32 0 +@b = dso_local global i32 0 + +define dso_local i32 @main() !dbg !8 { +entry: + ; The inline asm string contains multi-byte instructions and blank lines. + call void asm sideeffect inteldialect "lea eax, a\0A\09mov dword ptr [eax], 1\0A\0A\09lea ebx, b\0A\09mov dword ptr [ebx], 1\0A\0A\09mov eax, [eax]\0A\09add [ebx], eax\0A\0A\09inc eax\0A\0A\09imul dword ptr [ebx]\0A\09mov [ebx], eax", "~{dirflag},~{fpsr},~{flags}"(), !srcloc !12, !dbg !13 + ret i32 0, !dbg !15 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +!1 = !DIFile(filename: "t.cpp", directory: "/") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !DISubroutineType(types: !6) +!6 = !{!7} +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!12 = !{i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, !14} +!13 = !DILocation(line: 4, column: 5, scope: !8) +!14 = !{!"inlineasm.dbg.offset", i32 0, i32 6, i32 9, i32 12, i32 7, i32 9, i32 37, i32 9, i32 9, i32 49, i32 10, i32 9, i32 74, i32 12, i32 9, i32 90, i32 13, i32 9, i32 107, i32 15, i32 9, i32 117, i32 17, i32 9, i32 139, i32 18, i32 9} +!15 = !DILocation(line: 8, column: 3, scope: !8) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
