[llvm] [X86] Insert WAIT before fnstenv/fnsave and skip meta-instructions (PR #204108)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 30 04:05:09 PDT 2026


https://github.com/adalal-2441 updated https://github.com/llvm/llvm-project/pull/204108

>From 4b95e774b02b05b05c0cb4521a9b6d20c0c67086 Mon Sep 17 00:00:00 2001
From: Akshat <adalal at amd.com>
Date: Sat, 13 Jun 2026 22:04:53 +0530
Subject: [PATCH] [X86] Insert WAIT before fnstenv/fnsave

fnstenv/fnsave (FSTENVm/FSAVEm) are non-waiting, so they don't
synchronize a pending FP exception; the WAIT pass shouldn't skip the
WAIT before them.

Also skip meta-instructions when finding the next op so WAIT placement
doesn't depend on -g.

Added a new X87ControlKind enum class to classify x87 control
instructions in the pass, replacing the existing ad-hoc switches.

Co-authored-by: Cursor <cursoragent at cursor.com>
---
 llvm/lib/Target/X86/X86InsertX87Wait.cpp  | 56 +++++++++---------
 llvm/test/CodeGen/X86/x87-insert-wait.mir | 72 +++++++++++++++++++++++
 2 files changed, 99 insertions(+), 29 deletions(-)
 create mode 100644 llvm/test/CodeGen/X86/x87-insert-wait.mir

diff --git a/llvm/lib/Target/X86/X86InsertX87Wait.cpp b/llvm/lib/Target/X86/X86InsertX87Wait.cpp
index 6d192d61301ab..83896c46ffbb4 100644
--- a/llvm/lib/Target/X86/X86InsertX87Wait.cpp
+++ b/llvm/lib/Target/X86/X86InsertX87Wait.cpp
@@ -56,41 +56,32 @@ FunctionPass *llvm::createX86InsertX87WaitLegacyPass() {
   return new X86InsertX87WaitLegacy();
 }
 
-static bool isX87ControlInstruction(MachineInstr &MI) {
-  switch (MI.getOpcode()) {
+// Classifies an x87 control instruction by whether it performs the implicit
+// wait (FP exception sync); non-waiting FN-prefixed forms do not.
+enum class X87ControlKind { NotControl, Waiting, NonWaiting };
+
+static X87ControlKind classifyX87ControlInstruction(unsigned Opcode) {
+  switch (Opcode) {
+  default:
+    return X87ControlKind::NotControl;
   case X86::FNINIT:
-  case X86::FLDCW16m:
-  case X86::FNSTCW16m:
+  case X86::FNCLEX:
   case X86::FNSTSW16r:
   case X86::FNSTSWm:
-  case X86::FNCLEX:
-  case X86::FLDENVm:
+  case X86::FNSTCW16m:
   case X86::FSTENVm:
-  case X86::FRSTORm:
   case X86::FSAVEm:
+    return X87ControlKind::NonWaiting;
+  case X86::FLDCW16m:
+  case X86::FLDENVm:
+  case X86::FRSTORm:
   case X86::FINCSTP:
   case X86::FDECSTP:
   case X86::FFREE:
   case X86::FFREEP:
   case X86::FNOP:
   case X86::WAIT:
-    return true;
-  default:
-    return false;
-  }
-}
-
-static bool isX87NonWaitingControlInstruction(MachineInstr &MI) {
-  // a few special control instructions don't perform a wait operation
-  switch (MI.getOpcode()) {
-  case X86::FNINIT:
-  case X86::FNSTSW16r:
-  case X86::FNSTSWm:
-  case X86::FNSTCW16m:
-  case X86::FNCLEX:
-    return true;
-  default:
-    return false;
+    return X87ControlKind::Waiting;
   }
 }
 
@@ -111,13 +102,20 @@ static bool insertWaitInstruction(MachineFunction &MF) {
       // a load/store instruction, or the instruction is x87 control
       // instruction, do not insert wait.
       if (!(MI->mayRaiseFPException() || MI->mayLoadOrStore()) ||
-          isX87ControlInstruction(*MI))
+          classifyX87ControlInstruction(MI->getOpcode()) !=
+              X87ControlKind::NotControl)
         continue;
-      // If the following instruction is an X87 instruction and isn't an X87
-      // non-waiting control instruction, we can omit insert wait instruction.
+      // If the following instruction is an X87 instruction that performs the
+      // wait operation itself, we can omit inserting wait. Skip
+      // meta-instructions so the decision is independent of debug info, and
+      // keep the wait for non-waiting (FN-prefixed) successors.
       MachineBasicBlock::iterator AfterMI = std::next(MI);
-      if (AfterMI != MBB.end() && X86::isX87Instruction(*AfterMI) &&
-          !isX87NonWaitingControlInstruction(*AfterMI))
+      MachineBasicBlock::iterator NextMI = AfterMI;
+      while (NextMI != MBB.end() && NextMI->isMetaInstruction())
+        ++NextMI;
+      if (NextMI != MBB.end() && X86::isX87Instruction(*NextMI) &&
+          classifyX87ControlInstruction(NextMI->getOpcode()) !=
+              X87ControlKind::NonWaiting)
         continue;
 
       BuildMI(MBB, AfterMI, MI->getDebugLoc(), TII->get(X86::WAIT));
diff --git a/llvm/test/CodeGen/X86/x87-insert-wait.mir b/llvm/test/CodeGen/X86/x87-insert-wait.mir
new file mode 100644
index 0000000000000..acae9ce89a898
--- /dev/null
+++ b/llvm/test/CodeGen/X86/x87-insert-wait.mir
@@ -0,0 +1,72 @@
+# RUN: llc -mtriple=i686-- -passes=x86-insert-x87-wait -o - %s | FileCheck %s
+--- |
+  define void @fnstenv(ptr %p) strictfp { ret void }
+  define void @fnsave(ptr %p) strictfp { ret void }
+  define void @dbg(ptr %p) strictfp !dbg !4 { ret void }
+  attributes #0 = { strictfp }
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!3}
+  !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, emissionKind: FullDebug)
+  !1 = !DIFile(filename: "x.c", directory: "/")
+  !3 = !{i32 2, !"Debug Info Version", i32 3}
+  !4 = distinct !DISubprogram(name: "dbg", scope: !1, file: !1, line: 1, type: !6, unit: !0)
+  !5 = !DILocalVariable(name: "x", scope: !4, file: !1, line: 1)
+  !6 = !DISubroutineType(types: !7)
+  !7 = !{null}
+  !8 = !DILocation(line: 1, scope: !4)
+...
+---
+# fnstenv is non-waiting: WAIT must be inserted between the add and the store.
+# CHECK-LABEL: name: fnstenv
+# CHECK:      ADD_Fp80
+# CHECK-NEXT: WAIT
+# CHECK-NEXT: FSTENVm
+name:            fnstenv
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $eax
+    %0:gr32 = COPY $eax
+    %1:rfp80 = nofpexcept LD_Fp80m %0, 1, $noreg, 0, $noreg, implicit-def dead $fpsw, implicit $fpcw :: (load (s80))
+    %2:rfp80 = ADD_Fp80 %1, %1, implicit-def dead $fpsw, implicit $fpcw
+    FSTENVm %0, 1, $noreg, 0, $noreg, implicit-def dead $fpsw, implicit-def dead $fpcw, implicit $fpsw, implicit $fpcw :: (store (s256))
+    RET 0
+...
+---
+# fnsave is non-waiting: WAIT must be inserted between the add and the store.
+# CHECK-LABEL: name: fnsave
+# CHECK:      ADD_Fp80
+# CHECK-NEXT: WAIT
+# CHECK-NEXT: FSAVEm
+name:            fnsave
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $eax
+    %0:gr32 = COPY $eax
+    %1:rfp80 = nofpexcept LD_Fp80m %0, 1, $noreg, 0, $noreg, implicit-def dead $fpsw, implicit $fpcw :: (load (s80))
+    %2:rfp80 = ADD_Fp80 %1, %1, implicit-def dead $fpsw, implicit $fpcw
+    FSAVEm %0, 1, $noreg, 0, $noreg, implicit-def dead $fpsw, implicit-def dead $fpcw, implicit $fpsw, implicit $fpcw :: (store (s256))
+    RET 0
+...
+---
+# A waiting x87 successor synchronizes, so no WAIT after the first add; a
+# DBG_VALUE in between must not change that.
+# CHECK-LABEL: name: dbg
+# CHECK:      ADD_Fp80
+# CHECK-NOT:  WAIT
+# CHECK:      DBG_VALUE
+# CHECK:      ADD_Fp80
+# CHECK-NEXT: WAIT
+name:            dbg
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $eax
+    %0:gr32 = COPY $eax
+    %1:rfp80 = nofpexcept LD_Fp80m %0, 1, $noreg, 0, $noreg, implicit-def dead $fpsw, implicit $fpcw :: (load (s80))
+    %2:rfp80 = ADD_Fp80 %1, %1, implicit-def dead $fpsw, implicit $fpcw
+    DBG_VALUE %2, $noreg, !5, !DIExpression(), debug-location !8
+    %3:rfp80 = ADD_Fp80 %2, %1, implicit-def dead $fpsw, implicit $fpcw
+    RET 0
+...



More information about the llvm-commits mailing list