[llvm] [X86] Restrict offset folding into address mode in 16-bit mode (PR #179399)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Tue Feb 3 08:31:48 PST 2026
https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/179399
>From e16ff3a72fd28fed888dfae955472ad1289c220b Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 2 Feb 2026 21:49:52 -0800
Subject: [PATCH 1/4] [X86] Don't fold offset into address mode in 16-bit mode
```
extern int X;
unsigned f(void) { return (unsigned)&X - 0xf0000; }
```
compiles (-O1 or above, -fno-pic) to
```
.code16
leal X-983040, %eax # R_386_16 relocatin, [-65535,65535]
```
in 16-bit mode, which is rejected by both GNU Assembler and integrated
assembler since #176827 .
Conservatively disable offset folding entirely in 16-bit mode to avoid
generating invalid displacements.
---
llvm/lib/Target/X86/X86ISelDAGToDAG.cpp | 9 ++++---
llvm/test/CodeGen/X86/fold-add-16.ll | 26 +++++++++++++++++++
.../{dag-large-offset.ll => fold-add-32.ll} | 0
3 files changed, 32 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/CodeGen/X86/fold-add-16.ll
rename llvm/test/CodeGen/X86/{dag-large-offset.ll => fold-add-32.ll} (100%)
diff --git a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
index c81d36a9295c0..2aec805aba497 100644
--- a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
@@ -1852,10 +1852,13 @@ bool X86DAGToDAGISel::foldOffsetIntoAddress(uint64_t Offset,
!isDispSafeForFrameIndexOrRegBase((uint32_t)Val) &&
!AM.hasBaseOrIndexReg())
return true;
- } else if (AM.hasBaseOrIndexReg() && !isDispSafeForFrameIndexOrRegBase(Val))
- // For 32-bit X86, make sure the displacement still isn't close to the
- // expressible limit.
+ } else if (Subtarget->is16Bit() || (AM.hasBaseOrIndexReg() &&
+ !isDispSafeForFrameIndexOrRegBase(Val))) {
+ // In 16-bit mode, displacements are limited to [-65535,65535] (
+ // R_386_16 relocation). For simplicity, don't fold offsets. For
+ // 32-bit, ensure the displacement isn't close to the limit.
return true;
+ }
AM.Disp = Val;
return false;
}
diff --git a/llvm/test/CodeGen/X86/fold-add-16.ll b/llvm/test/CodeGen/X86/fold-add-16.ll
new file mode 100644
index 0000000000000..2b0d35ae01680
--- /dev/null
+++ b/llvm/test/CodeGen/X86/fold-add-16.ll
@@ -0,0 +1,26 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=i386-unknown-code16 < %s | FileCheck %s
+
+;; In 16-bit mode, the displacement must fit in 16 bits.
+;; Test that we don't fold a large offset into the address mode.
+;; The implementation simply doesn't fold any offset.
+
+ at X = external global i32, align 4
+
+define i32 @small_offset() {
+; CHECK-LABEL: small_offset:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $X, %eax
+; CHECK-NEXT: addl $-4, %eax
+; CHECK-NEXT: retl
+ ret i32 sub (i32 ptrtoint (ptr @X to i32), i32 4)
+}
+
+define i32 @large_offset() {
+; CHECK-LABEL: large_offset:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $X, %eax
+; CHECK-NEXT: addl $-65536, %eax # imm = 0xFFFF0000
+; CHECK-NEXT: retl
+ ret i32 sub (i32 ptrtoint (ptr @X to i32), i32 65536)
+}
diff --git a/llvm/test/CodeGen/X86/dag-large-offset.ll b/llvm/test/CodeGen/X86/fold-add-32.ll
similarity index 100%
rename from llvm/test/CodeGen/X86/dag-large-offset.ll
rename to llvm/test/CodeGen/X86/fold-add-32.ll
>From 016d1d9f40cbb913e68a02748493989c0dd91176 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 2 Feb 2026 23:06:27 -0800
Subject: [PATCH 2/4] range
---
llvm/lib/Target/X86/X86ISelDAGToDAG.cpp | 14 ++++----
llvm/test/CodeGen/X86/fold-add-16.ll | 43 ++++++++++++++++++-------
2 files changed, 39 insertions(+), 18 deletions(-)
diff --git a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
index 2aec805aba497..76fba0a9b1e61 100644
--- a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
@@ -1852,13 +1852,15 @@ bool X86DAGToDAGISel::foldOffsetIntoAddress(uint64_t Offset,
!isDispSafeForFrameIndexOrRegBase((uint32_t)Val) &&
!AM.hasBaseOrIndexReg())
return true;
- } else if (Subtarget->is16Bit() || (AM.hasBaseOrIndexReg() &&
- !isDispSafeForFrameIndexOrRegBase(Val))) {
- // In 16-bit mode, displacements are limited to [-65535,65535] (
- // R_386_16 relocation). For simplicity, don't fold offsets. For
- // 32-bit, ensure the displacement isn't close to the limit.
+ } else if (Subtarget->is16Bit()) {
+ // In 16-bit mode, displacements are limited to [-65535,65535] (R_386_16
+ // relocation).
+ if (Val < -65535 || Val > 65535)
+ return true;
+ } else if (AM.hasBaseOrIndexReg() && !isDispSafeForFrameIndexOrRegBase(Val))
+ // For 32-bit X86, make sure the displacement still isn't close to the
+ // expressible limit.
return true;
- }
AM.Disp = Val;
return false;
}
diff --git a/llvm/test/CodeGen/X86/fold-add-16.ll b/llvm/test/CodeGen/X86/fold-add-16.ll
index 2b0d35ae01680..5c038381eccd5 100644
--- a/llvm/test/CodeGen/X86/fold-add-16.ll
+++ b/llvm/test/CodeGen/X86/fold-add-16.ll
@@ -1,26 +1,45 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
; RUN: llc -mtriple=i386-unknown-code16 < %s | FileCheck %s
-;; In 16-bit mode, the displacement must fit in 16 bits.
-;; Test that we don't fold a large offset into the address mode.
-;; The implementation simply doesn't fold any offset.
+;; In 16-bit mode, displacements are limited to [-65535,65535] (R_386_16).
+;; Test boundary conditions for offset folding.
@X = external global i32, align 4
-define i32 @small_offset() {
-; CHECK-LABEL: small_offset:
+;; -65535 is within range, should fold
+define i32 @neg_65535() {
+; CHECK-LABEL: neg_65535:
; CHECK: # %bb.0:
-; CHECK-NEXT: movl $X, %eax
-; CHECK-NEXT: addl $-4, %eax
+; CHECK-NEXT: leal X+65535, %eax
; CHECK-NEXT: retl
- ret i32 sub (i32 ptrtoint (ptr @X to i32), i32 4)
+ ret i32 add (i32 ptrtoint (ptr @X to i32), i32 65535)
}
-define i32 @large_offset() {
-; CHECK-LABEL: large_offset:
+;; 65535 is within range, should fold
+define i32 @pos_65535() {
+; CHECK-LABEL: pos_65535:
; CHECK: # %bb.0:
-; CHECK-NEXT: movl $X, %eax
-; CHECK-NEXT: addl $-65536, %eax # imm = 0xFFFF0000
+; CHECK-NEXT: leal X-65535, %eax
+; CHECK-NEXT: retl
+ ret i32 sub (i32 ptrtoint (ptr @X to i32), i32 65535)
+}
+
+;; -65536 is outside range, should NOT fold
+define i32 @neg_65536() {
+; CHECK-LABEL: neg_65536:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $65536, %eax # imm = 0x10000
+; CHECK-NEXT: leal X(%eax), %eax
+; CHECK-NEXT: retl
+ ret i32 add (i32 ptrtoint (ptr @X to i32), i32 65536)
+}
+
+;; 65536 is outside range, should NOT fold
+define i32 @pos_65536() {
+; CHECK-LABEL: pos_65536:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $-65536, %eax # imm = 0xFFFF0000
+; CHECK-NEXT: leal X(%eax), %eax
; CHECK-NEXT: retl
ret i32 sub (i32 ptrtoint (ptr @X to i32), i32 65536)
}
>From e1420db05685afbdf4e72f561b9cb072a7718855 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Tue, 3 Feb 2026 08:26:49 -0800
Subject: [PATCH 3/4] better comment
---
llvm/lib/Target/X86/X86ISelDAGToDAG.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
index 76fba0a9b1e61..291640557e4f5 100644
--- a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
@@ -1853,8 +1853,8 @@ bool X86DAGToDAGISel::foldOffsetIntoAddress(uint64_t Offset,
!AM.hasBaseOrIndexReg())
return true;
} else if (Subtarget->is16Bit()) {
- // In 16-bit mode, displacements are limited to [-65535,65535] (R_386_16
- // relocation).
+ // In 16-bit mode, displacements are limited to [-65535,65535] for FK_Data_2
+ // fixups of unknown signedness. See X86AsmBackend::applyFixup.
if (Val < -65535 || Val > 65535)
return true;
} else if (AM.hasBaseOrIndexReg() && !isDispSafeForFrameIndexOrRegBase(Val))
>From e0f0169062d449d6ca77725835d93129c949df2a Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Tue, 3 Feb 2026 08:31:35 -0800
Subject: [PATCH 4/4] use UINT16_MAX
---
llvm/lib/Target/X86/X86ISelDAGToDAG.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
index 291640557e4f5..eb425a10acf09 100644
--- a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
@@ -1855,7 +1855,7 @@ bool X86DAGToDAGISel::foldOffsetIntoAddress(uint64_t Offset,
} else if (Subtarget->is16Bit()) {
// In 16-bit mode, displacements are limited to [-65535,65535] for FK_Data_2
// fixups of unknown signedness. See X86AsmBackend::applyFixup.
- if (Val < -65535 || Val > 65535)
+ if (Val < -(int64_t)UINT16_MAX || Val > (int64_t)UINT16_MAX)
return true;
} else if (AM.hasBaseOrIndexReg() && !isDispSafeForFrameIndexOrRegBase(Val))
// For 32-bit X86, make sure the displacement still isn't close to the
More information about the llvm-commits
mailing list