Skip to content

[X86][WinEH] Insert nop after unwinding inline assembly#176393

Merged
nikic merged 1 commit intollvm:mainfrom
nikic:wineh-inlinasm-nop
Jan 19, 2026
Merged

[X86][WinEH] Insert nop after unwinding inline assembly#176393
nikic merged 1 commit intollvm:mainfrom
nikic:wineh-inlinasm-nop

Conversation

@nikic
Copy link
Copy Markdown
Contributor

@nikic nikic commented Jan 16, 2026

As discussed on #144745, insert a nop after unwinding inline assembly, as it may end on a call.

While the change itself is trivial, I ended up having to do two infrastructure changes:

  • The unwind flag needs to be propagated to ExtraInfo of the MachineInstr.
  • The MachineInstr needs to be passed through to emitInlineAsmEnd(), and the method needs to be non-const.

Fixes #157073.

@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Jan 16, 2026

@llvm/pr-subscribers-backend-aarch64
@llvm/pr-subscribers-backend-mips

@llvm/pr-subscribers-llvm-globalisel

Author: Nikita Popov (nikic)

Changes

As discussed on #144745, insert a nop after unwinding inline assembly, as it may end on a call.

While the change itself is trivial, I ended up having to do two infrastructure changes:

  • The unwind flag needs to be propagated to ExtraInfo of the MachineInstr.
  • The MachineInstr needs to be passed through to emitInlineAsmEnd(), and the method needs to be non-const.

Fixes #157073.


Full diff: https://github.com/llvm/llvm-project/pull/176393.diff

15 Files Affected:

  • (modified) llvm/include/llvm/CodeGen/AsmPrinter.h (+8-7)
  • (modified) llvm/include/llvm/IR/InlineAsm.h (+1)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp (+8-6)
  • (modified) llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp (+2)
  • (modified) llvm/lib/CodeGen/MachineInstr.cpp (+2)
  • (modified) llvm/lib/CodeGen/MachineVerifier.cpp (+2-2)
  • (modified) llvm/lib/CodeGen/SelectionDAG/FastISel.cpp (+2)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (+2)
  • (modified) llvm/lib/Target/ARM/ARMAsmPrinter.cpp (+2-1)
  • (modified) llvm/lib/Target/ARM/ARMAsmPrinter.h (+2-1)
  • (modified) llvm/lib/Target/Mips/MipsAsmPrinter.cpp (+2-1)
  • (modified) llvm/lib/Target/Mips/MipsAsmPrinter.h (+2-1)
  • (modified) llvm/lib/Target/X86/X86AsmPrinter.h (+4)
  • (modified) llvm/lib/Target/X86/X86MCInstLower.cpp (+12)
  • (modified) llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll (+2)
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 7f99e81e7d1a0..707dd5ddcf515 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -908,7 +908,8 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   /// \p EndInfo   - the final subtarget info after parsing the inline asm,
   ///                or NULL if the value is unknown.
   virtual void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                const MCSubtargetInfo *EndInfo) const;
+                                const MCSubtargetInfo *EndInfo,
+                                const MachineInstr *MI);
 
   /// This emits visibility information about symbol, if this is supported by
   /// the target.
@@ -942,15 +943,15 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   void emitFunctionPrefix(ArrayRef<const Constant *> Prefix);
 
   /// Emit a blob of inline asm to the output streamer.
-  void
-  emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
-                const MCTargetOptions &MCOptions,
-                const MDNode *LocMDNode = nullptr,
-                InlineAsm::AsmDialect AsmDialect = InlineAsm::AD_ATT) const;
+  void emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
+                     const MCTargetOptions &MCOptions,
+                     const MDNode *LocMDNode = nullptr,
+                     InlineAsm::AsmDialect AsmDialect = InlineAsm::AD_ATT,
+                     const MachineInstr *MI = nullptr);
 
   /// This method formats and emits the specified machine instruction that is an
   /// inline asm.
-  void emitInlineAsm(const MachineInstr *MI) const;
+  void emitInlineAsm(const MachineInstr *MI);
 
   /// Add inline assembly info to the diagnostics machinery, so we can
   /// emit file and position info. Returns SrcMgr memory buffer position.
diff --git a/llvm/include/llvm/IR/InlineAsm.h b/llvm/include/llvm/IR/InlineAsm.h
index 96887d129a69f..735953ca36bc2 100644
--- a/llvm/include/llvm/IR/InlineAsm.h
+++ b/llvm/include/llvm/IR/InlineAsm.h
@@ -220,6 +220,7 @@ class InlineAsm final : public Value {
     Extra_MayLoad = 8,
     Extra_MayStore = 16,
     Extra_IsConvergent = 32,
+    Extra_MayUnwind = 64,
   };
 
   // Inline asm operands map to multiple SDNode / MachineInstr operands.
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
index 69c2950711c7c..ae9fff80133d0 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
@@ -73,7 +73,8 @@ unsigned AsmPrinter::addInlineAsmDiagBuffer(StringRef AsmStr,
 void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
                                const MCTargetOptions &MCOptions,
                                const MDNode *LocMDNode,
-                               InlineAsm::AsmDialect Dialect) const {
+                               InlineAsm::AsmDialect Dialect,
+                               const MachineInstr *MI) {
   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.
@@ -93,7 +94,7 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
       !OutStreamer->isIntegratedAssemblerRequired()) {
     emitInlineAsmStart();
     OutStreamer->emitRawText(Str);
-    emitInlineAsmEnd(STI, nullptr);
+    emitInlineAsmEnd(STI, nullptr, MI);
     return;
   }
 
@@ -135,7 +136,7 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
   // Don't implicitly switch to the text section before the asm.
   (void)Parser->Run(/*NoInitialTextSection*/ true,
                     /*NoFinalize*/ true);
-  emitInlineAsmEnd(STI, &TAP->getSTI());
+  emitInlineAsmEnd(STI, &TAP->getSTI(), MI);
 }
 
 static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
@@ -336,7 +337,7 @@ static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
 
 /// This method formats and emits the specified machine instruction that is an
 /// inline asm.
-void AsmPrinter::emitInlineAsm(const MachineInstr *MI) const {
+void AsmPrinter::emitInlineAsm(const MachineInstr *MI) {
   assert(MI->isInlineAsm() && "printInlineAsm only works on inline asms");
 
   // Disassemble the AsmStr, printing out the literal pieces, the operands, etc.
@@ -417,7 +418,7 @@ void AsmPrinter::emitInlineAsm(const MachineInstr *MI) const {
   }
 
   emitInlineAsm(StringData, getSubtargetInfo(), TM.Options.MCOptions, LocMD,
-                MI->getInlineAsmDialect());
+                MI->getInlineAsmDialect(), MI);
 
   // Emit the #NOAPP end marker.  This has to happen even if verbose-asm isn't
   // enabled, so we use emitRawComment.
@@ -518,4 +519,5 @@ bool AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
 void AsmPrinter::emitInlineAsmStart() const {}
 
 void AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                  const MCSubtargetInfo *EndInfo) const {}
+                                  const MCSubtargetInfo *EndInfo,
+                                  const MachineInstr *MI) {}
diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
index 2927b075fc360..41b6489064efd 100644
--- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
@@ -51,6 +51,8 @@ class ExtraFlags {
       Flags |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       Flags |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      Flags |= InlineAsm::Extra_MayUnwind;
     if (CB.isConvergent())
       Flags |= InlineAsm::Extra_IsConvergent;
     Flags |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp
index 41b897c3607c1..afdc72244bc86 100644
--- a/llvm/lib/CodeGen/MachineInstr.cpp
+++ b/llvm/lib/CodeGen/MachineInstr.cpp
@@ -1937,6 +1937,8 @@ void MachineInstr::print(raw_ostream &OS, ModuleSlotTracker &MST,
       OS << " [isconvergent]";
     if (ExtraInfo & InlineAsm::Extra_IsAlignStack)
       OS << " [alignstack]";
+    if (ExtraInfo & InlineAsm::Extra_MayUnwind)
+      OS << " [unwind]";
     if (getInlineAsmDialect() == InlineAsm::AD_ATT)
       OS << " [attdialect]";
     if (getInlineAsmDialect() == InlineAsm::AD_Intel)
diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index 5ce7606b051a5..9e64a19f1d017 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -955,8 +955,8 @@ void MachineVerifier::verifyInlineAsm(const MachineInstr *MI) {
     report("Asm flags must be an immediate", MI);
   // Allowed flags are Extra_HasSideEffects = 1, Extra_IsAlignStack = 2,
   // Extra_AsmDialect = 4, Extra_MayLoad = 8, and Extra_MayStore = 16,
-  // and Extra_IsConvergent = 32.
-  if (!isUInt<6>(MI->getOperand(1).getImm()))
+  // and Extra_IsConvergent = 32, Extra_MayUnwind = 64.
+  if (!isUInt<7>(MI->getOperand(1).getImm()))
     report("Unknown asm flags", &MI->getOperand(1), 1);
 
   static_assert(InlineAsm::MIOp_FirstOperand == 2, "Asm format changed");
diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
index 03727b77b3e1a..dd5587625e2aa 100644
--- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
@@ -1162,6 +1162,8 @@ bool FastISel::selectCall(const User *I) {
       ExtraInfo |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       ExtraInfo |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      ExtraInfo |= InlineAsm::Extra_MayUnwind;
     if (Call->isConvergent())
       ExtraInfo |= InlineAsm::Extra_IsConvergent;
     ExtraInfo |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 27131e14141cc..7471a6c856bcf 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -10078,6 +10078,8 @@ class ExtraFlags {
       Flags |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       Flags |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      Flags |= InlineAsm::Extra_MayUnwind;
     if (Call.isConvergent())
       Flags |= InlineAsm::Extra_IsConvergent;
     Flags |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
index 458a3f0ced070..e7d30aec14860 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
@@ -519,7 +519,8 @@ static bool isThumb(const MCSubtargetInfo& STI) {
 }
 
 void ARMAsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                     const MCSubtargetInfo *EndInfo) const {
+                                     const MCSubtargetInfo *EndInfo,
+                                     const MachineInstr *MI) {
   // If either end mode is unknown (EndInfo == NULL) or different than
   // the start mode, then restore the start mode.
   const bool WasThumb = isThumb(StartInfo);
diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.h b/llvm/lib/Target/ARM/ARMAsmPrinter.h
index 12e20d758e415..1d35b3c445958 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.h
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.h
@@ -83,7 +83,8 @@ class LLVM_LIBRARY_VISIBILITY ARMAsmPrinter : public AsmPrinter {
                              const char *ExtraCode, raw_ostream &O) override;
 
   void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                        const MCSubtargetInfo *EndInfo) const override;
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
 
   void emitJumpTableAddrs(const MachineInstr *MI);
   void emitJumpTableInsts(const MachineInstr *MI);
diff --git a/llvm/lib/Target/Mips/MipsAsmPrinter.cpp b/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
index 9d8b9f86daf7a..a0e5872da5e6e 100644
--- a/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
+++ b/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
@@ -810,7 +810,8 @@ void MipsAsmPrinter::emitInlineAsmStart() const {
 }
 
 void MipsAsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                      const MCSubtargetInfo *EndInfo) const {
+                                      const MCSubtargetInfo *EndInfo,
+                                      const MachineInstr *MI) {
   OutStreamer->addBlankLine();
   getTargetStreamer().emitDirectiveSetPop();
 }
diff --git a/llvm/lib/Target/Mips/MipsAsmPrinter.h b/llvm/lib/Target/Mips/MipsAsmPrinter.h
index 8b2fb32dc552d..6d45ad551db72 100644
--- a/llvm/lib/Target/Mips/MipsAsmPrinter.h
+++ b/llvm/lib/Target/Mips/MipsAsmPrinter.h
@@ -83,7 +83,8 @@ class LLVM_LIBRARY_VISIBILITY MipsAsmPrinter : public AsmPrinter {
   void emitInlineAsmStart() const override;
 
   void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                        const MCSubtargetInfo *EndInfo) const override;
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
 
   void emitJumpTableEntry(const MachineJumpTableInfo &MJTI,
                           const MachineBasicBlock *MBB,
diff --git a/llvm/lib/Target/X86/X86AsmPrinter.h b/llvm/lib/Target/X86/X86AsmPrinter.h
index e02b5562d3b5e..705bb9a4aacfb 100644
--- a/llvm/lib/Target/X86/X86AsmPrinter.h
+++ b/llvm/lib/Target/X86/X86AsmPrinter.h
@@ -172,6 +172,10 @@ class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter {
 
   void emitInstruction(const MachineInstr *MI) override;
 
+  void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
+
   void emitBasicBlockEnd(const MachineBasicBlock &MBB) override;
 
   bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
diff --git a/llvm/lib/Target/X86/X86MCInstLower.cpp b/llvm/lib/Target/X86/X86MCInstLower.cpp
index 2287a921a19c0..1dd702069843b 100644
--- a/llvm/lib/Target/X86/X86MCInstLower.cpp
+++ b/llvm/lib/Target/X86/X86MCInstLower.cpp
@@ -2627,6 +2627,18 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) {
   EmitAndCountInstruction(TmpInst);
 }
 
+void X86AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                                     const MCSubtargetInfo *EndInfo,
+                                     const MachineInstr *MI) {
+  if (MI) {
+    // If unwinding inline asm ends on a call, wineh may require insertion of
+    // a nop.
+    unsigned ExtraInfo = MI->getOperand(InlineAsm::MIOp_ExtraInfo).getImm();
+    if (ExtraInfo & InlineAsm::Extra_MayUnwind)
+      maybeEmitNopAfterCallForWindowsEH(MI);
+  }
+}
+
 void X86AsmPrinter::emitCallInstruction(const llvm::MCInst &MCI) {
   // Stackmap shadows cannot include branch targets, so we can count the bytes
   // in a call towards the shadow, but must ensure that the no thread returns
diff --git a/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll b/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
index 5a6aeb6020344..e2959718b6068 100644
--- a/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
+++ b/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
@@ -15,6 +15,7 @@ entry:
 
 ; CHECK-LABEL: .Ltmp0:
 ; CHECK: callq  trap
+; CHECK: nop
 ; CHECK-LABEL: .Ltmp1:
 
   invoke void asm sideeffect unwind "call trap", "~{dirflag},~{fpsr},~{flags}"()
@@ -27,6 +28,7 @@ except:
 
 ; CHECK-LABEL: "?dtor$2@?0?test@4HA":
 ; CHECK: callq	printf
+; CHECK: nop
 
   %0 = cleanuppad within none []
   call void (ptr, ...) @printf(ptr @str) [ "funclet"(token %0) ]

@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Jan 16, 2026

@llvm/pr-subscribers-backend-arm

Author: Nikita Popov (nikic)

Changes

As discussed on #144745, insert a nop after unwinding inline assembly, as it may end on a call.

While the change itself is trivial, I ended up having to do two infrastructure changes:

  • The unwind flag needs to be propagated to ExtraInfo of the MachineInstr.
  • The MachineInstr needs to be passed through to emitInlineAsmEnd(), and the method needs to be non-const.

Fixes #157073.


Full diff: https://github.com/llvm/llvm-project/pull/176393.diff

15 Files Affected:

  • (modified) llvm/include/llvm/CodeGen/AsmPrinter.h (+8-7)
  • (modified) llvm/include/llvm/IR/InlineAsm.h (+1)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp (+8-6)
  • (modified) llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp (+2)
  • (modified) llvm/lib/CodeGen/MachineInstr.cpp (+2)
  • (modified) llvm/lib/CodeGen/MachineVerifier.cpp (+2-2)
  • (modified) llvm/lib/CodeGen/SelectionDAG/FastISel.cpp (+2)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (+2)
  • (modified) llvm/lib/Target/ARM/ARMAsmPrinter.cpp (+2-1)
  • (modified) llvm/lib/Target/ARM/ARMAsmPrinter.h (+2-1)
  • (modified) llvm/lib/Target/Mips/MipsAsmPrinter.cpp (+2-1)
  • (modified) llvm/lib/Target/Mips/MipsAsmPrinter.h (+2-1)
  • (modified) llvm/lib/Target/X86/X86AsmPrinter.h (+4)
  • (modified) llvm/lib/Target/X86/X86MCInstLower.cpp (+12)
  • (modified) llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll (+2)
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 7f99e81e7d1a0..707dd5ddcf515 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -908,7 +908,8 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   /// \p EndInfo   - the final subtarget info after parsing the inline asm,
   ///                or NULL if the value is unknown.
   virtual void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                const MCSubtargetInfo *EndInfo) const;
+                                const MCSubtargetInfo *EndInfo,
+                                const MachineInstr *MI);
 
   /// This emits visibility information about symbol, if this is supported by
   /// the target.
@@ -942,15 +943,15 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   void emitFunctionPrefix(ArrayRef<const Constant *> Prefix);
 
   /// Emit a blob of inline asm to the output streamer.
-  void
-  emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
-                const MCTargetOptions &MCOptions,
-                const MDNode *LocMDNode = nullptr,
-                InlineAsm::AsmDialect AsmDialect = InlineAsm::AD_ATT) const;
+  void emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
+                     const MCTargetOptions &MCOptions,
+                     const MDNode *LocMDNode = nullptr,
+                     InlineAsm::AsmDialect AsmDialect = InlineAsm::AD_ATT,
+                     const MachineInstr *MI = nullptr);
 
   /// This method formats and emits the specified machine instruction that is an
   /// inline asm.
-  void emitInlineAsm(const MachineInstr *MI) const;
+  void emitInlineAsm(const MachineInstr *MI);
 
   /// Add inline assembly info to the diagnostics machinery, so we can
   /// emit file and position info. Returns SrcMgr memory buffer position.
diff --git a/llvm/include/llvm/IR/InlineAsm.h b/llvm/include/llvm/IR/InlineAsm.h
index 96887d129a69f..735953ca36bc2 100644
--- a/llvm/include/llvm/IR/InlineAsm.h
+++ b/llvm/include/llvm/IR/InlineAsm.h
@@ -220,6 +220,7 @@ class InlineAsm final : public Value {
     Extra_MayLoad = 8,
     Extra_MayStore = 16,
     Extra_IsConvergent = 32,
+    Extra_MayUnwind = 64,
   };
 
   // Inline asm operands map to multiple SDNode / MachineInstr operands.
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
index 69c2950711c7c..ae9fff80133d0 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
@@ -73,7 +73,8 @@ unsigned AsmPrinter::addInlineAsmDiagBuffer(StringRef AsmStr,
 void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
                                const MCTargetOptions &MCOptions,
                                const MDNode *LocMDNode,
-                               InlineAsm::AsmDialect Dialect) const {
+                               InlineAsm::AsmDialect Dialect,
+                               const MachineInstr *MI) {
   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.
@@ -93,7 +94,7 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
       !OutStreamer->isIntegratedAssemblerRequired()) {
     emitInlineAsmStart();
     OutStreamer->emitRawText(Str);
-    emitInlineAsmEnd(STI, nullptr);
+    emitInlineAsmEnd(STI, nullptr, MI);
     return;
   }
 
@@ -135,7 +136,7 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
   // Don't implicitly switch to the text section before the asm.
   (void)Parser->Run(/*NoInitialTextSection*/ true,
                     /*NoFinalize*/ true);
-  emitInlineAsmEnd(STI, &TAP->getSTI());
+  emitInlineAsmEnd(STI, &TAP->getSTI(), MI);
 }
 
 static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
@@ -336,7 +337,7 @@ static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
 
 /// This method formats and emits the specified machine instruction that is an
 /// inline asm.
-void AsmPrinter::emitInlineAsm(const MachineInstr *MI) const {
+void AsmPrinter::emitInlineAsm(const MachineInstr *MI) {
   assert(MI->isInlineAsm() && "printInlineAsm only works on inline asms");
 
   // Disassemble the AsmStr, printing out the literal pieces, the operands, etc.
@@ -417,7 +418,7 @@ void AsmPrinter::emitInlineAsm(const MachineInstr *MI) const {
   }
 
   emitInlineAsm(StringData, getSubtargetInfo(), TM.Options.MCOptions, LocMD,
-                MI->getInlineAsmDialect());
+                MI->getInlineAsmDialect(), MI);
 
   // Emit the #NOAPP end marker.  This has to happen even if verbose-asm isn't
   // enabled, so we use emitRawComment.
@@ -518,4 +519,5 @@ bool AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
 void AsmPrinter::emitInlineAsmStart() const {}
 
 void AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                  const MCSubtargetInfo *EndInfo) const {}
+                                  const MCSubtargetInfo *EndInfo,
+                                  const MachineInstr *MI) {}
diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
index 2927b075fc360..41b6489064efd 100644
--- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
@@ -51,6 +51,8 @@ class ExtraFlags {
       Flags |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       Flags |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      Flags |= InlineAsm::Extra_MayUnwind;
     if (CB.isConvergent())
       Flags |= InlineAsm::Extra_IsConvergent;
     Flags |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp
index 41b897c3607c1..afdc72244bc86 100644
--- a/llvm/lib/CodeGen/MachineInstr.cpp
+++ b/llvm/lib/CodeGen/MachineInstr.cpp
@@ -1937,6 +1937,8 @@ void MachineInstr::print(raw_ostream &OS, ModuleSlotTracker &MST,
       OS << " [isconvergent]";
     if (ExtraInfo & InlineAsm::Extra_IsAlignStack)
       OS << " [alignstack]";
+    if (ExtraInfo & InlineAsm::Extra_MayUnwind)
+      OS << " [unwind]";
     if (getInlineAsmDialect() == InlineAsm::AD_ATT)
       OS << " [attdialect]";
     if (getInlineAsmDialect() == InlineAsm::AD_Intel)
diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index 5ce7606b051a5..9e64a19f1d017 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -955,8 +955,8 @@ void MachineVerifier::verifyInlineAsm(const MachineInstr *MI) {
     report("Asm flags must be an immediate", MI);
   // Allowed flags are Extra_HasSideEffects = 1, Extra_IsAlignStack = 2,
   // Extra_AsmDialect = 4, Extra_MayLoad = 8, and Extra_MayStore = 16,
-  // and Extra_IsConvergent = 32.
-  if (!isUInt<6>(MI->getOperand(1).getImm()))
+  // and Extra_IsConvergent = 32, Extra_MayUnwind = 64.
+  if (!isUInt<7>(MI->getOperand(1).getImm()))
     report("Unknown asm flags", &MI->getOperand(1), 1);
 
   static_assert(InlineAsm::MIOp_FirstOperand == 2, "Asm format changed");
diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
index 03727b77b3e1a..dd5587625e2aa 100644
--- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
@@ -1162,6 +1162,8 @@ bool FastISel::selectCall(const User *I) {
       ExtraInfo |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       ExtraInfo |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      ExtraInfo |= InlineAsm::Extra_MayUnwind;
     if (Call->isConvergent())
       ExtraInfo |= InlineAsm::Extra_IsConvergent;
     ExtraInfo |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 27131e14141cc..7471a6c856bcf 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -10078,6 +10078,8 @@ class ExtraFlags {
       Flags |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       Flags |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      Flags |= InlineAsm::Extra_MayUnwind;
     if (Call.isConvergent())
       Flags |= InlineAsm::Extra_IsConvergent;
     Flags |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
index 458a3f0ced070..e7d30aec14860 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
@@ -519,7 +519,8 @@ static bool isThumb(const MCSubtargetInfo& STI) {
 }
 
 void ARMAsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                     const MCSubtargetInfo *EndInfo) const {
+                                     const MCSubtargetInfo *EndInfo,
+                                     const MachineInstr *MI) {
   // If either end mode is unknown (EndInfo == NULL) or different than
   // the start mode, then restore the start mode.
   const bool WasThumb = isThumb(StartInfo);
diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.h b/llvm/lib/Target/ARM/ARMAsmPrinter.h
index 12e20d758e415..1d35b3c445958 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.h
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.h
@@ -83,7 +83,8 @@ class LLVM_LIBRARY_VISIBILITY ARMAsmPrinter : public AsmPrinter {
                              const char *ExtraCode, raw_ostream &O) override;
 
   void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                        const MCSubtargetInfo *EndInfo) const override;
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
 
   void emitJumpTableAddrs(const MachineInstr *MI);
   void emitJumpTableInsts(const MachineInstr *MI);
diff --git a/llvm/lib/Target/Mips/MipsAsmPrinter.cpp b/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
index 9d8b9f86daf7a..a0e5872da5e6e 100644
--- a/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
+++ b/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
@@ -810,7 +810,8 @@ void MipsAsmPrinter::emitInlineAsmStart() const {
 }
 
 void MipsAsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                      const MCSubtargetInfo *EndInfo) const {
+                                      const MCSubtargetInfo *EndInfo,
+                                      const MachineInstr *MI) {
   OutStreamer->addBlankLine();
   getTargetStreamer().emitDirectiveSetPop();
 }
diff --git a/llvm/lib/Target/Mips/MipsAsmPrinter.h b/llvm/lib/Target/Mips/MipsAsmPrinter.h
index 8b2fb32dc552d..6d45ad551db72 100644
--- a/llvm/lib/Target/Mips/MipsAsmPrinter.h
+++ b/llvm/lib/Target/Mips/MipsAsmPrinter.h
@@ -83,7 +83,8 @@ class LLVM_LIBRARY_VISIBILITY MipsAsmPrinter : public AsmPrinter {
   void emitInlineAsmStart() const override;
 
   void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                        const MCSubtargetInfo *EndInfo) const override;
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
 
   void emitJumpTableEntry(const MachineJumpTableInfo &MJTI,
                           const MachineBasicBlock *MBB,
diff --git a/llvm/lib/Target/X86/X86AsmPrinter.h b/llvm/lib/Target/X86/X86AsmPrinter.h
index e02b5562d3b5e..705bb9a4aacfb 100644
--- a/llvm/lib/Target/X86/X86AsmPrinter.h
+++ b/llvm/lib/Target/X86/X86AsmPrinter.h
@@ -172,6 +172,10 @@ class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter {
 
   void emitInstruction(const MachineInstr *MI) override;
 
+  void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
+
   void emitBasicBlockEnd(const MachineBasicBlock &MBB) override;
 
   bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
diff --git a/llvm/lib/Target/X86/X86MCInstLower.cpp b/llvm/lib/Target/X86/X86MCInstLower.cpp
index 2287a921a19c0..1dd702069843b 100644
--- a/llvm/lib/Target/X86/X86MCInstLower.cpp
+++ b/llvm/lib/Target/X86/X86MCInstLower.cpp
@@ -2627,6 +2627,18 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) {
   EmitAndCountInstruction(TmpInst);
 }
 
+void X86AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                                     const MCSubtargetInfo *EndInfo,
+                                     const MachineInstr *MI) {
+  if (MI) {
+    // If unwinding inline asm ends on a call, wineh may require insertion of
+    // a nop.
+    unsigned ExtraInfo = MI->getOperand(InlineAsm::MIOp_ExtraInfo).getImm();
+    if (ExtraInfo & InlineAsm::Extra_MayUnwind)
+      maybeEmitNopAfterCallForWindowsEH(MI);
+  }
+}
+
 void X86AsmPrinter::emitCallInstruction(const llvm::MCInst &MCI) {
   // Stackmap shadows cannot include branch targets, so we can count the bytes
   // in a call towards the shadow, but must ensure that the no thread returns
diff --git a/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll b/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
index 5a6aeb6020344..e2959718b6068 100644
--- a/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
+++ b/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
@@ -15,6 +15,7 @@ entry:
 
 ; CHECK-LABEL: .Ltmp0:
 ; CHECK: callq  trap
+; CHECK: nop
 ; CHECK-LABEL: .Ltmp1:
 
   invoke void asm sideeffect unwind "call trap", "~{dirflag},~{fpsr},~{flags}"()
@@ -27,6 +28,7 @@ except:
 
 ; CHECK-LABEL: "?dtor$2@?0?test@4HA":
 ; CHECK: callq	printf
+; CHECK: nop
 
   %0 = cleanuppad within none []
   call void (ptr, ...) @printf(ptr @str) [ "funclet"(token %0) ]

@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Jan 16, 2026

@llvm/pr-subscribers-llvm-ir

Author: Nikita Popov (nikic)

Changes

As discussed on #144745, insert a nop after unwinding inline assembly, as it may end on a call.

While the change itself is trivial, I ended up having to do two infrastructure changes:

  • The unwind flag needs to be propagated to ExtraInfo of the MachineInstr.
  • The MachineInstr needs to be passed through to emitInlineAsmEnd(), and the method needs to be non-const.

Fixes #157073.


Full diff: https://github.com/llvm/llvm-project/pull/176393.diff

15 Files Affected:

  • (modified) llvm/include/llvm/CodeGen/AsmPrinter.h (+8-7)
  • (modified) llvm/include/llvm/IR/InlineAsm.h (+1)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp (+8-6)
  • (modified) llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp (+2)
  • (modified) llvm/lib/CodeGen/MachineInstr.cpp (+2)
  • (modified) llvm/lib/CodeGen/MachineVerifier.cpp (+2-2)
  • (modified) llvm/lib/CodeGen/SelectionDAG/FastISel.cpp (+2)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (+2)
  • (modified) llvm/lib/Target/ARM/ARMAsmPrinter.cpp (+2-1)
  • (modified) llvm/lib/Target/ARM/ARMAsmPrinter.h (+2-1)
  • (modified) llvm/lib/Target/Mips/MipsAsmPrinter.cpp (+2-1)
  • (modified) llvm/lib/Target/Mips/MipsAsmPrinter.h (+2-1)
  • (modified) llvm/lib/Target/X86/X86AsmPrinter.h (+4)
  • (modified) llvm/lib/Target/X86/X86MCInstLower.cpp (+12)
  • (modified) llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll (+2)
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 7f99e81e7d1a0..707dd5ddcf515 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -908,7 +908,8 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   /// \p EndInfo   - the final subtarget info after parsing the inline asm,
   ///                or NULL if the value is unknown.
   virtual void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                const MCSubtargetInfo *EndInfo) const;
+                                const MCSubtargetInfo *EndInfo,
+                                const MachineInstr *MI);
 
   /// This emits visibility information about symbol, if this is supported by
   /// the target.
@@ -942,15 +943,15 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   void emitFunctionPrefix(ArrayRef<const Constant *> Prefix);
 
   /// Emit a blob of inline asm to the output streamer.
-  void
-  emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
-                const MCTargetOptions &MCOptions,
-                const MDNode *LocMDNode = nullptr,
-                InlineAsm::AsmDialect AsmDialect = InlineAsm::AD_ATT) const;
+  void emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
+                     const MCTargetOptions &MCOptions,
+                     const MDNode *LocMDNode = nullptr,
+                     InlineAsm::AsmDialect AsmDialect = InlineAsm::AD_ATT,
+                     const MachineInstr *MI = nullptr);
 
   /// This method formats and emits the specified machine instruction that is an
   /// inline asm.
-  void emitInlineAsm(const MachineInstr *MI) const;
+  void emitInlineAsm(const MachineInstr *MI);
 
   /// Add inline assembly info to the diagnostics machinery, so we can
   /// emit file and position info. Returns SrcMgr memory buffer position.
diff --git a/llvm/include/llvm/IR/InlineAsm.h b/llvm/include/llvm/IR/InlineAsm.h
index 96887d129a69f..735953ca36bc2 100644
--- a/llvm/include/llvm/IR/InlineAsm.h
+++ b/llvm/include/llvm/IR/InlineAsm.h
@@ -220,6 +220,7 @@ class InlineAsm final : public Value {
     Extra_MayLoad = 8,
     Extra_MayStore = 16,
     Extra_IsConvergent = 32,
+    Extra_MayUnwind = 64,
   };
 
   // Inline asm operands map to multiple SDNode / MachineInstr operands.
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
index 69c2950711c7c..ae9fff80133d0 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
@@ -73,7 +73,8 @@ unsigned AsmPrinter::addInlineAsmDiagBuffer(StringRef AsmStr,
 void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
                                const MCTargetOptions &MCOptions,
                                const MDNode *LocMDNode,
-                               InlineAsm::AsmDialect Dialect) const {
+                               InlineAsm::AsmDialect Dialect,
+                               const MachineInstr *MI) {
   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.
@@ -93,7 +94,7 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
       !OutStreamer->isIntegratedAssemblerRequired()) {
     emitInlineAsmStart();
     OutStreamer->emitRawText(Str);
-    emitInlineAsmEnd(STI, nullptr);
+    emitInlineAsmEnd(STI, nullptr, MI);
     return;
   }
 
@@ -135,7 +136,7 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
   // Don't implicitly switch to the text section before the asm.
   (void)Parser->Run(/*NoInitialTextSection*/ true,
                     /*NoFinalize*/ true);
-  emitInlineAsmEnd(STI, &TAP->getSTI());
+  emitInlineAsmEnd(STI, &TAP->getSTI(), MI);
 }
 
 static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
@@ -336,7 +337,7 @@ static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
 
 /// This method formats and emits the specified machine instruction that is an
 /// inline asm.
-void AsmPrinter::emitInlineAsm(const MachineInstr *MI) const {
+void AsmPrinter::emitInlineAsm(const MachineInstr *MI) {
   assert(MI->isInlineAsm() && "printInlineAsm only works on inline asms");
 
   // Disassemble the AsmStr, printing out the literal pieces, the operands, etc.
@@ -417,7 +418,7 @@ void AsmPrinter::emitInlineAsm(const MachineInstr *MI) const {
   }
 
   emitInlineAsm(StringData, getSubtargetInfo(), TM.Options.MCOptions, LocMD,
-                MI->getInlineAsmDialect());
+                MI->getInlineAsmDialect(), MI);
 
   // Emit the #NOAPP end marker.  This has to happen even if verbose-asm isn't
   // enabled, so we use emitRawComment.
@@ -518,4 +519,5 @@ bool AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
 void AsmPrinter::emitInlineAsmStart() const {}
 
 void AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                  const MCSubtargetInfo *EndInfo) const {}
+                                  const MCSubtargetInfo *EndInfo,
+                                  const MachineInstr *MI) {}
diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
index 2927b075fc360..41b6489064efd 100644
--- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
@@ -51,6 +51,8 @@ class ExtraFlags {
       Flags |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       Flags |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      Flags |= InlineAsm::Extra_MayUnwind;
     if (CB.isConvergent())
       Flags |= InlineAsm::Extra_IsConvergent;
     Flags |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp
index 41b897c3607c1..afdc72244bc86 100644
--- a/llvm/lib/CodeGen/MachineInstr.cpp
+++ b/llvm/lib/CodeGen/MachineInstr.cpp
@@ -1937,6 +1937,8 @@ void MachineInstr::print(raw_ostream &OS, ModuleSlotTracker &MST,
       OS << " [isconvergent]";
     if (ExtraInfo & InlineAsm::Extra_IsAlignStack)
       OS << " [alignstack]";
+    if (ExtraInfo & InlineAsm::Extra_MayUnwind)
+      OS << " [unwind]";
     if (getInlineAsmDialect() == InlineAsm::AD_ATT)
       OS << " [attdialect]";
     if (getInlineAsmDialect() == InlineAsm::AD_Intel)
diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index 5ce7606b051a5..9e64a19f1d017 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -955,8 +955,8 @@ void MachineVerifier::verifyInlineAsm(const MachineInstr *MI) {
     report("Asm flags must be an immediate", MI);
   // Allowed flags are Extra_HasSideEffects = 1, Extra_IsAlignStack = 2,
   // Extra_AsmDialect = 4, Extra_MayLoad = 8, and Extra_MayStore = 16,
-  // and Extra_IsConvergent = 32.
-  if (!isUInt<6>(MI->getOperand(1).getImm()))
+  // and Extra_IsConvergent = 32, Extra_MayUnwind = 64.
+  if (!isUInt<7>(MI->getOperand(1).getImm()))
     report("Unknown asm flags", &MI->getOperand(1), 1);
 
   static_assert(InlineAsm::MIOp_FirstOperand == 2, "Asm format changed");
diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
index 03727b77b3e1a..dd5587625e2aa 100644
--- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
@@ -1162,6 +1162,8 @@ bool FastISel::selectCall(const User *I) {
       ExtraInfo |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       ExtraInfo |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      ExtraInfo |= InlineAsm::Extra_MayUnwind;
     if (Call->isConvergent())
       ExtraInfo |= InlineAsm::Extra_IsConvergent;
     ExtraInfo |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 27131e14141cc..7471a6c856bcf 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -10078,6 +10078,8 @@ class ExtraFlags {
       Flags |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       Flags |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      Flags |= InlineAsm::Extra_MayUnwind;
     if (Call.isConvergent())
       Flags |= InlineAsm::Extra_IsConvergent;
     Flags |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
index 458a3f0ced070..e7d30aec14860 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
@@ -519,7 +519,8 @@ static bool isThumb(const MCSubtargetInfo& STI) {
 }
 
 void ARMAsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                     const MCSubtargetInfo *EndInfo) const {
+                                     const MCSubtargetInfo *EndInfo,
+                                     const MachineInstr *MI) {
   // If either end mode is unknown (EndInfo == NULL) or different than
   // the start mode, then restore the start mode.
   const bool WasThumb = isThumb(StartInfo);
diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.h b/llvm/lib/Target/ARM/ARMAsmPrinter.h
index 12e20d758e415..1d35b3c445958 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.h
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.h
@@ -83,7 +83,8 @@ class LLVM_LIBRARY_VISIBILITY ARMAsmPrinter : public AsmPrinter {
                              const char *ExtraCode, raw_ostream &O) override;
 
   void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                        const MCSubtargetInfo *EndInfo) const override;
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
 
   void emitJumpTableAddrs(const MachineInstr *MI);
   void emitJumpTableInsts(const MachineInstr *MI);
diff --git a/llvm/lib/Target/Mips/MipsAsmPrinter.cpp b/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
index 9d8b9f86daf7a..a0e5872da5e6e 100644
--- a/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
+++ b/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
@@ -810,7 +810,8 @@ void MipsAsmPrinter::emitInlineAsmStart() const {
 }
 
 void MipsAsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                      const MCSubtargetInfo *EndInfo) const {
+                                      const MCSubtargetInfo *EndInfo,
+                                      const MachineInstr *MI) {
   OutStreamer->addBlankLine();
   getTargetStreamer().emitDirectiveSetPop();
 }
diff --git a/llvm/lib/Target/Mips/MipsAsmPrinter.h b/llvm/lib/Target/Mips/MipsAsmPrinter.h
index 8b2fb32dc552d..6d45ad551db72 100644
--- a/llvm/lib/Target/Mips/MipsAsmPrinter.h
+++ b/llvm/lib/Target/Mips/MipsAsmPrinter.h
@@ -83,7 +83,8 @@ class LLVM_LIBRARY_VISIBILITY MipsAsmPrinter : public AsmPrinter {
   void emitInlineAsmStart() const override;
 
   void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                        const MCSubtargetInfo *EndInfo) const override;
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
 
   void emitJumpTableEntry(const MachineJumpTableInfo &MJTI,
                           const MachineBasicBlock *MBB,
diff --git a/llvm/lib/Target/X86/X86AsmPrinter.h b/llvm/lib/Target/X86/X86AsmPrinter.h
index e02b5562d3b5e..705bb9a4aacfb 100644
--- a/llvm/lib/Target/X86/X86AsmPrinter.h
+++ b/llvm/lib/Target/X86/X86AsmPrinter.h
@@ -172,6 +172,10 @@ class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter {
 
   void emitInstruction(const MachineInstr *MI) override;
 
+  void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
+
   void emitBasicBlockEnd(const MachineBasicBlock &MBB) override;
 
   bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
diff --git a/llvm/lib/Target/X86/X86MCInstLower.cpp b/llvm/lib/Target/X86/X86MCInstLower.cpp
index 2287a921a19c0..1dd702069843b 100644
--- a/llvm/lib/Target/X86/X86MCInstLower.cpp
+++ b/llvm/lib/Target/X86/X86MCInstLower.cpp
@@ -2627,6 +2627,18 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) {
   EmitAndCountInstruction(TmpInst);
 }
 
+void X86AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                                     const MCSubtargetInfo *EndInfo,
+                                     const MachineInstr *MI) {
+  if (MI) {
+    // If unwinding inline asm ends on a call, wineh may require insertion of
+    // a nop.
+    unsigned ExtraInfo = MI->getOperand(InlineAsm::MIOp_ExtraInfo).getImm();
+    if (ExtraInfo & InlineAsm::Extra_MayUnwind)
+      maybeEmitNopAfterCallForWindowsEH(MI);
+  }
+}
+
 void X86AsmPrinter::emitCallInstruction(const llvm::MCInst &MCI) {
   // Stackmap shadows cannot include branch targets, so we can count the bytes
   // in a call towards the shadow, but must ensure that the no thread returns
diff --git a/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll b/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
index 5a6aeb6020344..e2959718b6068 100644
--- a/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
+++ b/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
@@ -15,6 +15,7 @@ entry:
 
 ; CHECK-LABEL: .Ltmp0:
 ; CHECK: callq  trap
+; CHECK: nop
 ; CHECK-LABEL: .Ltmp1:
 
   invoke void asm sideeffect unwind "call trap", "~{dirflag},~{fpsr},~{flags}"()
@@ -27,6 +28,7 @@ except:
 
 ; CHECK-LABEL: "?dtor$2@?0?test@4HA":
 ; CHECK: callq	printf
+; CHECK: nop
 
   %0 = cleanuppad within none []
   call void (ptr, ...) @printf(ptr @str) [ "funclet"(token %0) ]

@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Jan 16, 2026

@llvm/pr-subscribers-llvm-selectiondag

Author: Nikita Popov (nikic)

Changes

As discussed on #144745, insert a nop after unwinding inline assembly, as it may end on a call.

While the change itself is trivial, I ended up having to do two infrastructure changes:

  • The unwind flag needs to be propagated to ExtraInfo of the MachineInstr.
  • The MachineInstr needs to be passed through to emitInlineAsmEnd(), and the method needs to be non-const.

Fixes #157073.


Full diff: https://github.com/llvm/llvm-project/pull/176393.diff

15 Files Affected:

  • (modified) llvm/include/llvm/CodeGen/AsmPrinter.h (+8-7)
  • (modified) llvm/include/llvm/IR/InlineAsm.h (+1)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp (+8-6)
  • (modified) llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp (+2)
  • (modified) llvm/lib/CodeGen/MachineInstr.cpp (+2)
  • (modified) llvm/lib/CodeGen/MachineVerifier.cpp (+2-2)
  • (modified) llvm/lib/CodeGen/SelectionDAG/FastISel.cpp (+2)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (+2)
  • (modified) llvm/lib/Target/ARM/ARMAsmPrinter.cpp (+2-1)
  • (modified) llvm/lib/Target/ARM/ARMAsmPrinter.h (+2-1)
  • (modified) llvm/lib/Target/Mips/MipsAsmPrinter.cpp (+2-1)
  • (modified) llvm/lib/Target/Mips/MipsAsmPrinter.h (+2-1)
  • (modified) llvm/lib/Target/X86/X86AsmPrinter.h (+4)
  • (modified) llvm/lib/Target/X86/X86MCInstLower.cpp (+12)
  • (modified) llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll (+2)
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 7f99e81e7d1a0..707dd5ddcf515 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -908,7 +908,8 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   /// \p EndInfo   - the final subtarget info after parsing the inline asm,
   ///                or NULL if the value is unknown.
   virtual void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                const MCSubtargetInfo *EndInfo) const;
+                                const MCSubtargetInfo *EndInfo,
+                                const MachineInstr *MI);
 
   /// This emits visibility information about symbol, if this is supported by
   /// the target.
@@ -942,15 +943,15 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   void emitFunctionPrefix(ArrayRef<const Constant *> Prefix);
 
   /// Emit a blob of inline asm to the output streamer.
-  void
-  emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
-                const MCTargetOptions &MCOptions,
-                const MDNode *LocMDNode = nullptr,
-                InlineAsm::AsmDialect AsmDialect = InlineAsm::AD_ATT) const;
+  void emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
+                     const MCTargetOptions &MCOptions,
+                     const MDNode *LocMDNode = nullptr,
+                     InlineAsm::AsmDialect AsmDialect = InlineAsm::AD_ATT,
+                     const MachineInstr *MI = nullptr);
 
   /// This method formats and emits the specified machine instruction that is an
   /// inline asm.
-  void emitInlineAsm(const MachineInstr *MI) const;
+  void emitInlineAsm(const MachineInstr *MI);
 
   /// Add inline assembly info to the diagnostics machinery, so we can
   /// emit file and position info. Returns SrcMgr memory buffer position.
diff --git a/llvm/include/llvm/IR/InlineAsm.h b/llvm/include/llvm/IR/InlineAsm.h
index 96887d129a69f..735953ca36bc2 100644
--- a/llvm/include/llvm/IR/InlineAsm.h
+++ b/llvm/include/llvm/IR/InlineAsm.h
@@ -220,6 +220,7 @@ class InlineAsm final : public Value {
     Extra_MayLoad = 8,
     Extra_MayStore = 16,
     Extra_IsConvergent = 32,
+    Extra_MayUnwind = 64,
   };
 
   // Inline asm operands map to multiple SDNode / MachineInstr operands.
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
index 69c2950711c7c..ae9fff80133d0 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
@@ -73,7 +73,8 @@ unsigned AsmPrinter::addInlineAsmDiagBuffer(StringRef AsmStr,
 void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
                                const MCTargetOptions &MCOptions,
                                const MDNode *LocMDNode,
-                               InlineAsm::AsmDialect Dialect) const {
+                               InlineAsm::AsmDialect Dialect,
+                               const MachineInstr *MI) {
   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.
@@ -93,7 +94,7 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
       !OutStreamer->isIntegratedAssemblerRequired()) {
     emitInlineAsmStart();
     OutStreamer->emitRawText(Str);
-    emitInlineAsmEnd(STI, nullptr);
+    emitInlineAsmEnd(STI, nullptr, MI);
     return;
   }
 
@@ -135,7 +136,7 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
   // Don't implicitly switch to the text section before the asm.
   (void)Parser->Run(/*NoInitialTextSection*/ true,
                     /*NoFinalize*/ true);
-  emitInlineAsmEnd(STI, &TAP->getSTI());
+  emitInlineAsmEnd(STI, &TAP->getSTI(), MI);
 }
 
 static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
@@ -336,7 +337,7 @@ static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
 
 /// This method formats and emits the specified machine instruction that is an
 /// inline asm.
-void AsmPrinter::emitInlineAsm(const MachineInstr *MI) const {
+void AsmPrinter::emitInlineAsm(const MachineInstr *MI) {
   assert(MI->isInlineAsm() && "printInlineAsm only works on inline asms");
 
   // Disassemble the AsmStr, printing out the literal pieces, the operands, etc.
@@ -417,7 +418,7 @@ void AsmPrinter::emitInlineAsm(const MachineInstr *MI) const {
   }
 
   emitInlineAsm(StringData, getSubtargetInfo(), TM.Options.MCOptions, LocMD,
-                MI->getInlineAsmDialect());
+                MI->getInlineAsmDialect(), MI);
 
   // Emit the #NOAPP end marker.  This has to happen even if verbose-asm isn't
   // enabled, so we use emitRawComment.
@@ -518,4 +519,5 @@ bool AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
 void AsmPrinter::emitInlineAsmStart() const {}
 
 void AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                  const MCSubtargetInfo *EndInfo) const {}
+                                  const MCSubtargetInfo *EndInfo,
+                                  const MachineInstr *MI) {}
diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
index 2927b075fc360..41b6489064efd 100644
--- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
@@ -51,6 +51,8 @@ class ExtraFlags {
       Flags |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       Flags |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      Flags |= InlineAsm::Extra_MayUnwind;
     if (CB.isConvergent())
       Flags |= InlineAsm::Extra_IsConvergent;
     Flags |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp
index 41b897c3607c1..afdc72244bc86 100644
--- a/llvm/lib/CodeGen/MachineInstr.cpp
+++ b/llvm/lib/CodeGen/MachineInstr.cpp
@@ -1937,6 +1937,8 @@ void MachineInstr::print(raw_ostream &OS, ModuleSlotTracker &MST,
       OS << " [isconvergent]";
     if (ExtraInfo & InlineAsm::Extra_IsAlignStack)
       OS << " [alignstack]";
+    if (ExtraInfo & InlineAsm::Extra_MayUnwind)
+      OS << " [unwind]";
     if (getInlineAsmDialect() == InlineAsm::AD_ATT)
       OS << " [attdialect]";
     if (getInlineAsmDialect() == InlineAsm::AD_Intel)
diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index 5ce7606b051a5..9e64a19f1d017 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -955,8 +955,8 @@ void MachineVerifier::verifyInlineAsm(const MachineInstr *MI) {
     report("Asm flags must be an immediate", MI);
   // Allowed flags are Extra_HasSideEffects = 1, Extra_IsAlignStack = 2,
   // Extra_AsmDialect = 4, Extra_MayLoad = 8, and Extra_MayStore = 16,
-  // and Extra_IsConvergent = 32.
-  if (!isUInt<6>(MI->getOperand(1).getImm()))
+  // and Extra_IsConvergent = 32, Extra_MayUnwind = 64.
+  if (!isUInt<7>(MI->getOperand(1).getImm()))
     report("Unknown asm flags", &MI->getOperand(1), 1);
 
   static_assert(InlineAsm::MIOp_FirstOperand == 2, "Asm format changed");
diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
index 03727b77b3e1a..dd5587625e2aa 100644
--- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
@@ -1162,6 +1162,8 @@ bool FastISel::selectCall(const User *I) {
       ExtraInfo |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       ExtraInfo |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      ExtraInfo |= InlineAsm::Extra_MayUnwind;
     if (Call->isConvergent())
       ExtraInfo |= InlineAsm::Extra_IsConvergent;
     ExtraInfo |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 27131e14141cc..7471a6c856bcf 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -10078,6 +10078,8 @@ class ExtraFlags {
       Flags |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       Flags |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      Flags |= InlineAsm::Extra_MayUnwind;
     if (Call.isConvergent())
       Flags |= InlineAsm::Extra_IsConvergent;
     Flags |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
index 458a3f0ced070..e7d30aec14860 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
@@ -519,7 +519,8 @@ static bool isThumb(const MCSubtargetInfo& STI) {
 }
 
 void ARMAsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                     const MCSubtargetInfo *EndInfo) const {
+                                     const MCSubtargetInfo *EndInfo,
+                                     const MachineInstr *MI) {
   // If either end mode is unknown (EndInfo == NULL) or different than
   // the start mode, then restore the start mode.
   const bool WasThumb = isThumb(StartInfo);
diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.h b/llvm/lib/Target/ARM/ARMAsmPrinter.h
index 12e20d758e415..1d35b3c445958 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.h
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.h
@@ -83,7 +83,8 @@ class LLVM_LIBRARY_VISIBILITY ARMAsmPrinter : public AsmPrinter {
                              const char *ExtraCode, raw_ostream &O) override;
 
   void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                        const MCSubtargetInfo *EndInfo) const override;
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
 
   void emitJumpTableAddrs(const MachineInstr *MI);
   void emitJumpTableInsts(const MachineInstr *MI);
diff --git a/llvm/lib/Target/Mips/MipsAsmPrinter.cpp b/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
index 9d8b9f86daf7a..a0e5872da5e6e 100644
--- a/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
+++ b/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
@@ -810,7 +810,8 @@ void MipsAsmPrinter::emitInlineAsmStart() const {
 }
 
 void MipsAsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                      const MCSubtargetInfo *EndInfo) const {
+                                      const MCSubtargetInfo *EndInfo,
+                                      const MachineInstr *MI) {
   OutStreamer->addBlankLine();
   getTargetStreamer().emitDirectiveSetPop();
 }
diff --git a/llvm/lib/Target/Mips/MipsAsmPrinter.h b/llvm/lib/Target/Mips/MipsAsmPrinter.h
index 8b2fb32dc552d..6d45ad551db72 100644
--- a/llvm/lib/Target/Mips/MipsAsmPrinter.h
+++ b/llvm/lib/Target/Mips/MipsAsmPrinter.h
@@ -83,7 +83,8 @@ class LLVM_LIBRARY_VISIBILITY MipsAsmPrinter : public AsmPrinter {
   void emitInlineAsmStart() const override;
 
   void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                        const MCSubtargetInfo *EndInfo) const override;
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
 
   void emitJumpTableEntry(const MachineJumpTableInfo &MJTI,
                           const MachineBasicBlock *MBB,
diff --git a/llvm/lib/Target/X86/X86AsmPrinter.h b/llvm/lib/Target/X86/X86AsmPrinter.h
index e02b5562d3b5e..705bb9a4aacfb 100644
--- a/llvm/lib/Target/X86/X86AsmPrinter.h
+++ b/llvm/lib/Target/X86/X86AsmPrinter.h
@@ -172,6 +172,10 @@ class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter {
 
   void emitInstruction(const MachineInstr *MI) override;
 
+  void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
+
   void emitBasicBlockEnd(const MachineBasicBlock &MBB) override;
 
   bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
diff --git a/llvm/lib/Target/X86/X86MCInstLower.cpp b/llvm/lib/Target/X86/X86MCInstLower.cpp
index 2287a921a19c0..1dd702069843b 100644
--- a/llvm/lib/Target/X86/X86MCInstLower.cpp
+++ b/llvm/lib/Target/X86/X86MCInstLower.cpp
@@ -2627,6 +2627,18 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) {
   EmitAndCountInstruction(TmpInst);
 }
 
+void X86AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                                     const MCSubtargetInfo *EndInfo,
+                                     const MachineInstr *MI) {
+  if (MI) {
+    // If unwinding inline asm ends on a call, wineh may require insertion of
+    // a nop.
+    unsigned ExtraInfo = MI->getOperand(InlineAsm::MIOp_ExtraInfo).getImm();
+    if (ExtraInfo & InlineAsm::Extra_MayUnwind)
+      maybeEmitNopAfterCallForWindowsEH(MI);
+  }
+}
+
 void X86AsmPrinter::emitCallInstruction(const llvm::MCInst &MCI) {
   // Stackmap shadows cannot include branch targets, so we can count the bytes
   // in a call towards the shadow, but must ensure that the no thread returns
diff --git a/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll b/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
index 5a6aeb6020344..e2959718b6068 100644
--- a/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
+++ b/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
@@ -15,6 +15,7 @@ entry:
 
 ; CHECK-LABEL: .Ltmp0:
 ; CHECK: callq  trap
+; CHECK: nop
 ; CHECK-LABEL: .Ltmp1:
 
   invoke void asm sideeffect unwind "call trap", "~{dirflag},~{fpsr},~{flags}"()
@@ -27,6 +28,7 @@ except:
 
 ; CHECK-LABEL: "?dtor$2@?0?test@4HA":
 ; CHECK: callq	printf
+; CHECK: nop
 
   %0 = cleanuppad within none []
   call void (ptr, ...) @printf(ptr @str) [ "funclet"(token %0) ]

@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Jan 16, 2026

@llvm/pr-subscribers-backend-x86

Author: Nikita Popov (nikic)

Changes

As discussed on #144745, insert a nop after unwinding inline assembly, as it may end on a call.

While the change itself is trivial, I ended up having to do two infrastructure changes:

  • The unwind flag needs to be propagated to ExtraInfo of the MachineInstr.
  • The MachineInstr needs to be passed through to emitInlineAsmEnd(), and the method needs to be non-const.

Fixes #157073.


Full diff: https://github.com/llvm/llvm-project/pull/176393.diff

15 Files Affected:

  • (modified) llvm/include/llvm/CodeGen/AsmPrinter.h (+8-7)
  • (modified) llvm/include/llvm/IR/InlineAsm.h (+1)
  • (modified) llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp (+8-6)
  • (modified) llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp (+2)
  • (modified) llvm/lib/CodeGen/MachineInstr.cpp (+2)
  • (modified) llvm/lib/CodeGen/MachineVerifier.cpp (+2-2)
  • (modified) llvm/lib/CodeGen/SelectionDAG/FastISel.cpp (+2)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (+2)
  • (modified) llvm/lib/Target/ARM/ARMAsmPrinter.cpp (+2-1)
  • (modified) llvm/lib/Target/ARM/ARMAsmPrinter.h (+2-1)
  • (modified) llvm/lib/Target/Mips/MipsAsmPrinter.cpp (+2-1)
  • (modified) llvm/lib/Target/Mips/MipsAsmPrinter.h (+2-1)
  • (modified) llvm/lib/Target/X86/X86AsmPrinter.h (+4)
  • (modified) llvm/lib/Target/X86/X86MCInstLower.cpp (+12)
  • (modified) llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll (+2)
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 7f99e81e7d1a0..707dd5ddcf515 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -908,7 +908,8 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   /// \p EndInfo   - the final subtarget info after parsing the inline asm,
   ///                or NULL if the value is unknown.
   virtual void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                const MCSubtargetInfo *EndInfo) const;
+                                const MCSubtargetInfo *EndInfo,
+                                const MachineInstr *MI);
 
   /// This emits visibility information about symbol, if this is supported by
   /// the target.
@@ -942,15 +943,15 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   void emitFunctionPrefix(ArrayRef<const Constant *> Prefix);
 
   /// Emit a blob of inline asm to the output streamer.
-  void
-  emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
-                const MCTargetOptions &MCOptions,
-                const MDNode *LocMDNode = nullptr,
-                InlineAsm::AsmDialect AsmDialect = InlineAsm::AD_ATT) const;
+  void emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
+                     const MCTargetOptions &MCOptions,
+                     const MDNode *LocMDNode = nullptr,
+                     InlineAsm::AsmDialect AsmDialect = InlineAsm::AD_ATT,
+                     const MachineInstr *MI = nullptr);
 
   /// This method formats and emits the specified machine instruction that is an
   /// inline asm.
-  void emitInlineAsm(const MachineInstr *MI) const;
+  void emitInlineAsm(const MachineInstr *MI);
 
   /// Add inline assembly info to the diagnostics machinery, so we can
   /// emit file and position info. Returns SrcMgr memory buffer position.
diff --git a/llvm/include/llvm/IR/InlineAsm.h b/llvm/include/llvm/IR/InlineAsm.h
index 96887d129a69f..735953ca36bc2 100644
--- a/llvm/include/llvm/IR/InlineAsm.h
+++ b/llvm/include/llvm/IR/InlineAsm.h
@@ -220,6 +220,7 @@ class InlineAsm final : public Value {
     Extra_MayLoad = 8,
     Extra_MayStore = 16,
     Extra_IsConvergent = 32,
+    Extra_MayUnwind = 64,
   };
 
   // Inline asm operands map to multiple SDNode / MachineInstr operands.
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
index 69c2950711c7c..ae9fff80133d0 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
@@ -73,7 +73,8 @@ unsigned AsmPrinter::addInlineAsmDiagBuffer(StringRef AsmStr,
 void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
                                const MCTargetOptions &MCOptions,
                                const MDNode *LocMDNode,
-                               InlineAsm::AsmDialect Dialect) const {
+                               InlineAsm::AsmDialect Dialect,
+                               const MachineInstr *MI) {
   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.
@@ -93,7 +94,7 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
       !OutStreamer->isIntegratedAssemblerRequired()) {
     emitInlineAsmStart();
     OutStreamer->emitRawText(Str);
-    emitInlineAsmEnd(STI, nullptr);
+    emitInlineAsmEnd(STI, nullptr, MI);
     return;
   }
 
@@ -135,7 +136,7 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
   // Don't implicitly switch to the text section before the asm.
   (void)Parser->Run(/*NoInitialTextSection*/ true,
                     /*NoFinalize*/ true);
-  emitInlineAsmEnd(STI, &TAP->getSTI());
+  emitInlineAsmEnd(STI, &TAP->getSTI(), MI);
 }
 
 static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
@@ -336,7 +337,7 @@ static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
 
 /// This method formats and emits the specified machine instruction that is an
 /// inline asm.
-void AsmPrinter::emitInlineAsm(const MachineInstr *MI) const {
+void AsmPrinter::emitInlineAsm(const MachineInstr *MI) {
   assert(MI->isInlineAsm() && "printInlineAsm only works on inline asms");
 
   // Disassemble the AsmStr, printing out the literal pieces, the operands, etc.
@@ -417,7 +418,7 @@ void AsmPrinter::emitInlineAsm(const MachineInstr *MI) const {
   }
 
   emitInlineAsm(StringData, getSubtargetInfo(), TM.Options.MCOptions, LocMD,
-                MI->getInlineAsmDialect());
+                MI->getInlineAsmDialect(), MI);
 
   // Emit the #NOAPP end marker.  This has to happen even if verbose-asm isn't
   // enabled, so we use emitRawComment.
@@ -518,4 +519,5 @@ bool AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
 void AsmPrinter::emitInlineAsmStart() const {}
 
 void AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                  const MCSubtargetInfo *EndInfo) const {}
+                                  const MCSubtargetInfo *EndInfo,
+                                  const MachineInstr *MI) {}
diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
index 2927b075fc360..41b6489064efd 100644
--- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
@@ -51,6 +51,8 @@ class ExtraFlags {
       Flags |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       Flags |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      Flags |= InlineAsm::Extra_MayUnwind;
     if (CB.isConvergent())
       Flags |= InlineAsm::Extra_IsConvergent;
     Flags |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp
index 41b897c3607c1..afdc72244bc86 100644
--- a/llvm/lib/CodeGen/MachineInstr.cpp
+++ b/llvm/lib/CodeGen/MachineInstr.cpp
@@ -1937,6 +1937,8 @@ void MachineInstr::print(raw_ostream &OS, ModuleSlotTracker &MST,
       OS << " [isconvergent]";
     if (ExtraInfo & InlineAsm::Extra_IsAlignStack)
       OS << " [alignstack]";
+    if (ExtraInfo & InlineAsm::Extra_MayUnwind)
+      OS << " [unwind]";
     if (getInlineAsmDialect() == InlineAsm::AD_ATT)
       OS << " [attdialect]";
     if (getInlineAsmDialect() == InlineAsm::AD_Intel)
diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index 5ce7606b051a5..9e64a19f1d017 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -955,8 +955,8 @@ void MachineVerifier::verifyInlineAsm(const MachineInstr *MI) {
     report("Asm flags must be an immediate", MI);
   // Allowed flags are Extra_HasSideEffects = 1, Extra_IsAlignStack = 2,
   // Extra_AsmDialect = 4, Extra_MayLoad = 8, and Extra_MayStore = 16,
-  // and Extra_IsConvergent = 32.
-  if (!isUInt<6>(MI->getOperand(1).getImm()))
+  // and Extra_IsConvergent = 32, Extra_MayUnwind = 64.
+  if (!isUInt<7>(MI->getOperand(1).getImm()))
     report("Unknown asm flags", &MI->getOperand(1), 1);
 
   static_assert(InlineAsm::MIOp_FirstOperand == 2, "Asm format changed");
diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
index 03727b77b3e1a..dd5587625e2aa 100644
--- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
@@ -1162,6 +1162,8 @@ bool FastISel::selectCall(const User *I) {
       ExtraInfo |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       ExtraInfo |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      ExtraInfo |= InlineAsm::Extra_MayUnwind;
     if (Call->isConvergent())
       ExtraInfo |= InlineAsm::Extra_IsConvergent;
     ExtraInfo |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 27131e14141cc..7471a6c856bcf 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -10078,6 +10078,8 @@ class ExtraFlags {
       Flags |= InlineAsm::Extra_HasSideEffects;
     if (IA->isAlignStack())
       Flags |= InlineAsm::Extra_IsAlignStack;
+    if (IA->canThrow())
+      Flags |= InlineAsm::Extra_MayUnwind;
     if (Call.isConvergent())
       Flags |= InlineAsm::Extra_IsConvergent;
     Flags |= IA->getDialect() * InlineAsm::Extra_AsmDialect;
diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
index 458a3f0ced070..e7d30aec14860 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
@@ -519,7 +519,8 @@ static bool isThumb(const MCSubtargetInfo& STI) {
 }
 
 void ARMAsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                     const MCSubtargetInfo *EndInfo) const {
+                                     const MCSubtargetInfo *EndInfo,
+                                     const MachineInstr *MI) {
   // If either end mode is unknown (EndInfo == NULL) or different than
   // the start mode, then restore the start mode.
   const bool WasThumb = isThumb(StartInfo);
diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.h b/llvm/lib/Target/ARM/ARMAsmPrinter.h
index 12e20d758e415..1d35b3c445958 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.h
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.h
@@ -83,7 +83,8 @@ class LLVM_LIBRARY_VISIBILITY ARMAsmPrinter : public AsmPrinter {
                              const char *ExtraCode, raw_ostream &O) override;
 
   void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                        const MCSubtargetInfo *EndInfo) const override;
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
 
   void emitJumpTableAddrs(const MachineInstr *MI);
   void emitJumpTableInsts(const MachineInstr *MI);
diff --git a/llvm/lib/Target/Mips/MipsAsmPrinter.cpp b/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
index 9d8b9f86daf7a..a0e5872da5e6e 100644
--- a/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
+++ b/llvm/lib/Target/Mips/MipsAsmPrinter.cpp
@@ -810,7 +810,8 @@ void MipsAsmPrinter::emitInlineAsmStart() const {
 }
 
 void MipsAsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                                      const MCSubtargetInfo *EndInfo) const {
+                                      const MCSubtargetInfo *EndInfo,
+                                      const MachineInstr *MI) {
   OutStreamer->addBlankLine();
   getTargetStreamer().emitDirectiveSetPop();
 }
diff --git a/llvm/lib/Target/Mips/MipsAsmPrinter.h b/llvm/lib/Target/Mips/MipsAsmPrinter.h
index 8b2fb32dc552d..6d45ad551db72 100644
--- a/llvm/lib/Target/Mips/MipsAsmPrinter.h
+++ b/llvm/lib/Target/Mips/MipsAsmPrinter.h
@@ -83,7 +83,8 @@ class LLVM_LIBRARY_VISIBILITY MipsAsmPrinter : public AsmPrinter {
   void emitInlineAsmStart() const override;
 
   void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
-                        const MCSubtargetInfo *EndInfo) const override;
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
 
   void emitJumpTableEntry(const MachineJumpTableInfo &MJTI,
                           const MachineBasicBlock *MBB,
diff --git a/llvm/lib/Target/X86/X86AsmPrinter.h b/llvm/lib/Target/X86/X86AsmPrinter.h
index e02b5562d3b5e..705bb9a4aacfb 100644
--- a/llvm/lib/Target/X86/X86AsmPrinter.h
+++ b/llvm/lib/Target/X86/X86AsmPrinter.h
@@ -172,6 +172,10 @@ class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter {
 
   void emitInstruction(const MachineInstr *MI) override;
 
+  void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                        const MCSubtargetInfo *EndInfo,
+                        const MachineInstr *MI) override;
+
   void emitBasicBlockEnd(const MachineBasicBlock &MBB) override;
 
   bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
diff --git a/llvm/lib/Target/X86/X86MCInstLower.cpp b/llvm/lib/Target/X86/X86MCInstLower.cpp
index 2287a921a19c0..1dd702069843b 100644
--- a/llvm/lib/Target/X86/X86MCInstLower.cpp
+++ b/llvm/lib/Target/X86/X86MCInstLower.cpp
@@ -2627,6 +2627,18 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) {
   EmitAndCountInstruction(TmpInst);
 }
 
+void X86AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                                     const MCSubtargetInfo *EndInfo,
+                                     const MachineInstr *MI) {
+  if (MI) {
+    // If unwinding inline asm ends on a call, wineh may require insertion of
+    // a nop.
+    unsigned ExtraInfo = MI->getOperand(InlineAsm::MIOp_ExtraInfo).getImm();
+    if (ExtraInfo & InlineAsm::Extra_MayUnwind)
+      maybeEmitNopAfterCallForWindowsEH(MI);
+  }
+}
+
 void X86AsmPrinter::emitCallInstruction(const llvm::MCInst &MCI) {
   // Stackmap shadows cannot include branch targets, so we can count the bytes
   // in a call towards the shadow, but must ensure that the no thread returns
diff --git a/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll b/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
index 5a6aeb6020344..e2959718b6068 100644
--- a/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
+++ b/llvm/test/CodeGen/X86/seh-unwind-inline-asm-codegen.ll
@@ -15,6 +15,7 @@ entry:
 
 ; CHECK-LABEL: .Ltmp0:
 ; CHECK: callq  trap
+; CHECK: nop
 ; CHECK-LABEL: .Ltmp1:
 
   invoke void asm sideeffect unwind "call trap", "~{dirflag},~{fpsr},~{flags}"()
@@ -27,6 +28,7 @@ except:
 
 ; CHECK-LABEL: "?dtor$2@?0?test@4HA":
 ; CHECK: callq	printf
+; CHECK: nop
 
   %0 = cleanuppad within none []
   call void (ptr, ...) @printf(ptr @str) [ "funclet"(token %0) ]

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jan 16, 2026

🐧 Linux x64 Test Results

  • 188579 tests passed
  • 5005 tests skipped

✅ The build succeeded and all tests passed.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jan 16, 2026

🪟 Windows x64 Test Results

  • 129548 tests passed
  • 2866 tests skipped

✅ The build succeeded and all tests passed.

As discussed on llvm#144745,
insert a nop after unwinding inline assembly, as it may end on a
call.

While the change itself is trivial, I ended up having to do two
infrastructure changes:
 * The unwind flag needs to be propagated to ExtraInfo of the
   MachineInstr.
 * The MachineInstr needs to be passed through to emitInlineAsmEnd(),
   and the method needs to be non-const.
Copy link
Copy Markdown
Contributor

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Copy Markdown
Contributor

@rnk rnk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I made some suggested edits, hopefully they compile.

Comment thread llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
Comment thread llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
Comment thread llvm/include/llvm/CodeGen/AsmPrinter.h
Comment thread llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
@nikic
Copy link
Copy Markdown
Contributor Author

nikic commented Jan 16, 2026

@rnk The reason AsmPrinter does the weird dance with an extra emitInlineAsm() with unpacked arguments is the handling of module-level inline assembly here:

emitInlineAsm(
M.getModuleInlineAsm() + "\n", *TM.getMCSubtargetInfo(),
TM.Options.MCOptions, nullptr,
InlineAsm::AsmDialect(TM.getMCAsmInfo()->getAssemblerDialect()));
That one does not correspond to a MachineInstr (but still needs ability to specify the dialect etc).

@nikic nikic merged commit 792670a into llvm:main Jan 19, 2026
12 checks passed
@nikic nikic deleted the wineh-inlinasm-nop branch January 19, 2026 08:09
nikic added a commit to nikic/llvm-project that referenced this pull request Jan 21, 2026
As discussed on llvm#144745, insert
a nop after unwinding inline assembly, as it may end on a call.

While the change itself is trivial, I ended up having to do two
infrastructure changes:
* The unwind flag needs to be propagated to ExtraInfo of the
MachineInstr.
* The MachineInstr needs to be passed through to emitInlineAsmEnd(), and
the method needs to be non-const.

Fixes llvm#157073.

(cherry picked from commit 792670a)
c-rhodes pushed a commit to llvmbot/llvm-project that referenced this pull request Jan 26, 2026
As discussed on llvm#144745, insert
a nop after unwinding inline assembly, as it may end on a call.

While the change itself is trivial, I ended up having to do two
infrastructure changes:
* The unwind flag needs to be propagated to ExtraInfo of the
MachineInstr.
* The MachineInstr needs to be passed through to emitInlineAsmEnd(), and
the method needs to be non-const.

Fixes llvm#157073.

(cherry picked from commit 792670a)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rust's tests/ui/asm/x86_64/may_unwind.rs fails on Windows after b933f0c376c983614a0701f3bfd4054cf8fe4386

5 participants