[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