[clang] [llvm] [CodeView] Add line entries for instructions in MS inline asm (PR #193007)

via cfe-commits cfe-commits at lists.llvm.org
Sat Aug 22 06:57:58 PDT 2026


https://github.com/GkvJwa updated https://github.com/llvm/llvm-project/pull/193007

>From 0a085591e6263df6f9b2a6a2200c8aff3ba5d7c4 Mon Sep 17 00:00:00 2001
From: GkvJwa <gkvjwa at gmail.com>
Date: Wed, 19 Aug 2026 23:51:55 +0800
Subject: [PATCH 1/2] [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/lib/CodeGen/CGStmt.cpp                  | 94 ++++++++++++++++---
 clang/test/CodeGen/ms-inline-asm-codeview.cpp | 15 +++
 llvm/include/llvm/CodeGen/AsmPrinterHandler.h |  5 +
 llvm/include/llvm/CodeGen/MachineInstr.h      |  5 +
 llvm/include/llvm/MC/MCStreamer.h             | 11 +++
 .../AsmPrinter/AsmPrinterInlineAsm.cpp        | 44 +++++++++
 llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 13 +++
 llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h   |  4 +
 llvm/lib/CodeGen/MachineInstr.cpp             | 32 +++++++
 llvm/lib/CodeGen/MachineModuleInfo.cpp        | 21 ++++-
 llvm/lib/MC/MCAsmStreamer.cpp                 |  2 +
 llvm/lib/MC/MCStreamer.cpp                    |  7 ++
 .../DebugInfo/COFF/inline-asm-source-loc.ll   | 34 +++++++
 13 files changed, 270 insertions(+), 17 deletions(-)
 create mode 100644 clang/test/CodeGen/ms-inline-asm-codeview.cpp
 create mode 100644 llvm/test/DebugInfo/COFF/inline-asm-source-loc.ll

diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index bf6e6eb50f555..5c6605d0ed8e4 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,43 @@ CodeGenFunction::EmitAsmInput(const TargetInfo::ConstraintInfo &Info,
                             InputExpr->getExprLoc());
 }
 
+static llvm::MDNode *getAsmSrcLocInfo(ArrayRef<SourceLocation> SourceLocs,
+                                      CodeGenFunction &CGF) {
+  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) {
+    SmallVector<llvm::Metadata *, 16> DebugLocs;
+    DebugLocs.push_back(llvm::MDString::get(Ctx, "inlineasm.dbg.line"));
+    llvm::Type *Int32Ty = llvm::Type::getInt32Ty(Ctx);
+    for (SourceLocation Loc : SourceLocs) {
+      llvm::DebugLoc DL = CGF.SourceLocToDebugLoc(Loc);
+      DebugLocs.push_back(llvm::ConstantAsMetadata::get(
+          llvm::ConstantInt::get(Int32Ty, DL ? DL.getLine() : 0)));
+      DebugLocs.push_back(llvm::ConstantAsMetadata::get(
+          llvm::ConstantInt::get(Int32Ty, DL ? DL.getCol() : 0)));
+    }
+    Locs.push_back(llvm::MDNode::get(Ctx, DebugLocs));
+  }
+
+  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 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 +2677,51 @@ 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);
+  return getAsmSrcLocInfo(Locs, CGF);
+}
+
+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());
+
+  return getAsmSrcLocInfo(Locs, CGF);
 }
 
 namespace clang {
@@ -3311,15 +3375,15 @@ 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(SL, CGF));
+  } else if (const auto *msAsmStmt = dyn_cast<MSAsmStmt>(&S)) {
+    Result.setMetadata(
+        "srcloc",
+        getMSAsmSrcLocInfo(*msAsmStmt, msAsmStmt->getAsmString(), 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/ms-inline-asm-codeview.cpp b/clang/test/CodeGen/ms-inline-asm-codeview.cpp
new file mode 100644
index 0000000000000..04c6f15b566ad
--- /dev/null
+++ b/clang/test/CodeGen/ms-inline-asm-codeview.cpp
@@ -0,0 +1,15 @@
+// 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"
+void f() {
+  __asm {
+    nop
+    nop
+  }
+}
+
+// CHECK: call void asm sideeffect inteldialect
+// CHECK-SAME: !srcloc ![[SRCLOC:[0-9]+]]
+// CHECK: ![[SRCLOC]] = !{i64 {{[0-9]+}}, i64 {{[0-9]+}}, ![[DBGLOCS:[0-9]+]]}
+// CHECK: ![[DBGLOCS]] = !{!"inlineasm.dbg.line", i32 102, i32 5, i32 103, i32 5}
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..18c909ca9139e 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 line metadata in !srcloc, if
+  /// present. Operand 0 is a tag, and subsequent operands are line/column pairs
+  /// for inline asm source lines.
+  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/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..9109f7d362c21 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
@@ -15,6 +15,7 @@
 #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 +23,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,9 +133,51 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
   Parser->setTargetParser(*TAP);
 
   emitInlineAsmStart();
+  const MDNode *InlineAsmSourceLocs =
+      MI ? MI->getInlineAsmSourceLocMD() : nullptr;
+  if (InlineAsmSourceLocs) {
+    unsigned SyntheticLineOffset =
+        TM.getTargetTriple().isX86() && Dialect == InlineAsm::AD_Intel ? 1 : 0;
+    OutStreamer->setInlineAsmSourceLocCallback(
+        [this, &SrcMgr, BufNum, SyntheticLineOffset, InlineAsmSourceLocs,
+         MI](SMLoc Loc) {
+          if (!Loc.isValid() || SrcMgr.FindBufferContainingLoc(Loc) != BufNum)
+            return;
+
+          unsigned ParserLine = SrcMgr.FindLineNumber(Loc, BufNum);
+          if (ParserLine <= SyntheticLineOffset)
+            return;
+
+          unsigned SourceLine = ParserLine - SyntheticLineOffset;
+          unsigned LocIdx = (SourceLine - 1) * 2 + 1;
+          if (LocIdx + 1 >= InlineAsmSourceLocs->getNumOperands())
+            return;
+
+          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);
 }
 
diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
index 84ea5e349f01d..124b71fe2bad3 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
@@ -3149,6 +3149,11 @@ static bool isUsableDebugLoc(DebugLoc DL) {
 void CodeViewDebug::beginInstruction(const MachineInstr *MI) {
   DebugHandlerBase::beginInstruction(MI);
 
+  if (MI->isInlineAsm() && MI->getInlineAsmSourceLocMD()) {
+    PrevInstBB = MI->getParent();
+    return;
+  }
+
   // Ignore DBG_VALUE and DBG_LABEL locations and function prologue.
   if (!Asm || !CurFn || MI->isDebugInstr() ||
       MI->getFlag(MachineInstr::FrameSetup))
@@ -3177,6 +3182,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 1067f186208c4..4414998ecf067 100644
--- a/llvm/lib/CodeGen/MachineInstr.cpp
+++ b/llvm/lib/CodeGen/MachineInstr.cpp
@@ -2352,6 +2352,38 @@ const MDNode *MachineInstr::getLocCookieMD() const {
   return nullptr;
 }
 
+static bool isInlineAsmSourceLocMD(const MDNode *Locs) {
+  if (!Locs || Locs->getNumOperands() < 3 || Locs->getNumOperands() % 2 == 0)
+    return false;
+
+  const auto *Tag = dyn_cast_or_null<MDString>(Locs->getOperand(0));
+  if (!Tag || Tag->getString() != "inlineasm.dbg.line")
+    return false;
+
+  return true;
+}
+
+const MDNode *MachineInstr::getInlineAsmSourceLocMD() const {
+  for (unsigned I = getNumOperands(); I != 0; --I) {
+    if (!getOperand(I - 1).isMetadata())
+      continue;
+
+    const MDNode *LocMD = getOperand(I - 1).getMetadata();
+    if (isInlineAsmSourceLocMD(LocMD))
+      return LocMD;
+
+    if (!LocMD)
+      continue;
+    for (const MDOperand &Op : LocMD->operands()) {
+      const auto *Locs = dyn_cast_or_null<MDNode>(Op);
+      if (isInlineAsmSourceLocMD(Locs))
+        return Locs;
+    }
+  }
+
+  return nullptr;
+}
+
 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..4e66721265cdb 100644
--- a/llvm/lib/CodeGen/MachineModuleInfo.cpp
+++ b/llvm/lib/CodeGen/MachineModuleInfo.cpp
@@ -12,6 +12,7 @@
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DiagnosticInfo.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"
@@ -21,6 +22,19 @@
 using namespace llvm;
 using namespace llvm::dwarf;
 
+static bool isInlineAsmSourceLocMD(const MDNode *LocInfo) {
+  if (!LocInfo || LocInfo->getNumOperands() == 0)
+    return false;
+
+  const auto *Locs = dyn_cast_or_null<MDNode>(
+      LocInfo->getOperand(LocInfo->getNumOperands() - 1));
+  if (!Locs || Locs->getNumOperands() < 3 || Locs->getNumOperands() % 2 == 0)
+    return false;
+
+  const auto *Tag = dyn_cast_or_null<MDString>(Locs->getOperand(0));
+  return Tag && Tag->getString() == "inlineasm.dbg.line";
+}
+
 // Out of line virtual method.
 MachineModuleInfoImpl::~MachineModuleInfoImpl() = default;
 
@@ -192,10 +206,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 (isInlineAsmSourceLocMD(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/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/inline-asm-source-loc.ll b/llvm/test/DebugInfo/COFF/inline-asm-source-loc.ll
new file mode 100644
index 0000000000000..dda873b91d35e
--- /dev/null
+++ b/llvm/test/DebugInfo/COFF/inline-asm-source-loc.ll
@@ -0,0 +1,34 @@
+; RUN: llc -mtriple=i386-windows-msvc -filetype=asm < %s | FileCheck %s
+
+; CHECK: #APP
+; CHECK: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 6 9
+; CHECK-NEXT: {{[ \t]*}}nop
+; CHECK-NEXT: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 7 9
+; CHECK-NEXT: {{[ \t]*}}nop
+; CHECK: #NO_APP
+
+target triple = "i386-pc-windows-msvc"
+
+define dso_local i32 @main() !dbg !8 {
+entry:
+  ; The inline asm string is two source instructions: "nop\n\tnop".
+  call void asm sideeffect inteldialect "nop\0A\09nop", "~{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, !14}
+!13 = !DILocation(line: 4, column: 5, scope: !8)
+!14 = !{!"inlineasm.dbg.line", i32 6, i32 9, i32 7, i32 9}
+!15 = !DILocation(line: 8, column: 3, scope: !8)

>From 9d54325e7ed7e80c5856a20064f58f34c0cbdfb9 Mon Sep 17 00:00:00 2001
From: GkvJwa <gkvjwa at gmail.com>
Date: Sat, 22 Aug 2026 21:57:38 +0800
Subject: [PATCH 2/2] Add test

---
 clang/test/CodeGen/ms-inline-asm-codeview.cpp | 32 +++++++++++++++----
 .../DebugInfo/COFF/inline-asm-source-loc.ll   | 29 +++++++++++++----
 2 files changed, 48 insertions(+), 13 deletions(-)

diff --git a/clang/test/CodeGen/ms-inline-asm-codeview.cpp b/clang/test/CodeGen/ms-inline-asm-codeview.cpp
index 04c6f15b566ad..268eb8db04674 100644
--- a/clang/test/CodeGen/ms-inline-asm-codeview.cpp
+++ b/clang/test/CodeGen/ms-inline-asm-codeview.cpp
@@ -2,14 +2,32 @@
 // RUN:   -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s
 
 #line 100 "t.cpp"
-void f() {
-  __asm {
-    nop
-    nop
-  }
+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 void asm sideeffect inteldialect
 // CHECK-SAME: !srcloc ![[SRCLOC:[0-9]+]]
-// CHECK: ![[SRCLOC]] = !{i64 {{[0-9]+}}, i64 {{[0-9]+}}, ![[DBGLOCS:[0-9]+]]}
-// CHECK: ![[DBGLOCS]] = !{!"inlineasm.dbg.line", i32 102, i32 5, i32 103, i32 5}
+// 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.line", i32 106, i32 9, i32 107, i32 9, i32 109, i32 9, i32 110, i32 9, i32 112, i32 9, i32 113, i32 9, i32 115, i32 9, i32 117, i32 9, i32 118, i32 9}
diff --git a/llvm/test/DebugInfo/COFF/inline-asm-source-loc.ll b/llvm/test/DebugInfo/COFF/inline-asm-source-loc.ll
index dda873b91d35e..f92ebfaf86cfc 100644
--- a/llvm/test/DebugInfo/COFF/inline-asm-source-loc.ll
+++ b/llvm/test/DebugInfo/COFF/inline-asm-source-loc.ll
@@ -2,17 +2,34 @@
 
 ; CHECK: #APP
 ; CHECK: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 6 9
-; CHECK-NEXT: {{[ \t]*}}nop
+; CHECK-NEXT: {{[ \t]*}}lea eax, a
 ; CHECK-NEXT: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 7 9
-; CHECK-NEXT: {{[ \t]*}}nop
+; 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, [eax]
+; CHECK: {{[ \t]*}}.cv_loc{{[ \t]+}}0 1 13 9
+; CHECK-NEXT: {{[ \t]*}}add [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 [ebx], eax
 ; CHECK: #NO_APP
 
 target triple = "i386-pc-windows-msvc"
 
+ at a = dso_local global i32 0
+ at b = dso_local global i32 0
+
 define dso_local i32 @main() !dbg !8 {
 entry:
-  ; The inline asm string is two source instructions: "nop\n\tnop".
-  call void asm sideeffect inteldialect "nop\0A\09nop", "~{dirflag},~{fpsr},~{flags}"(), !srcloc !12, !dbg !13
+  ; 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
 }
 
@@ -28,7 +45,7 @@ entry:
 !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, !14}
+!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.line", i32 6, i32 9, i32 7, i32 9}
+!14 = !{!"inlineasm.dbg.line", i32 6, i32 9, i32 7, i32 9, i32 0, i32 0, i32 9, i32 9, i32 10, i32 9, i32 0, i32 0, i32 12, i32 9, i32 13, i32 9, i32 0, i32 0, i32 15, i32 9, i32 0, i32 0, i32 17, i32 9, i32 18, i32 9}
 !15 = !DILocation(line: 8, column: 3, scope: !8)



More information about the cfe-commits mailing list