[llvm] 2c30f74 - [llvm] Avoid PREL32 overflow to .data.rel.ro on AArch64 and re-enable (#222809)

via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 11 16:47:08 PDT 2026


Author: PiJoules
Date: 2026-09-11T23:47:02Z
New Revision: 2c30f74239871203b0a998cd4797e01c3338a9fa

URL: https://github.com/llvm/llvm-project/commit/2c30f74239871203b0a998cd4797e01c3338a9fa
DIFF: https://github.com/llvm/llvm-project/commit/2c30f74239871203b0a998cd4797e01c3338a9fa.diff

LOG: [llvm] Avoid PREL32 overflow to .data.rel.ro on AArch64 and re-enable (#222809)

Under the [AArch64 Small Code
Model](https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst#code-models),
the text segment (containing .text and .rodata) is limited to 2GB, but
the combined span of text and data segments can be up to 4GB.

When a relative lookup table is emitted in .rodata but references target
operands that require dynamic relocations (placed in .data.rel.ro in the
data segment), the distance between .rodata and .data.rel.ro can legally
exceed the 2GB range of signed 32-bit R_AARCH64_PREL32 relocations.

In this PR:
1. Ensure that on AArch64, lookup table operands do not need dynamic
relocations. If the lookup table were in `.rodata` (text segment), then
any of the target components would need to be in the data segment, so
the dynamic reloc check would suffice.
2. Re-enable RelLookupTableConverter for AArch64 (while keeping Darwin
disabled as before).
3. Limit the existing `AArch64/no_relative_lookup_table.ll` test to just
darwin aarch64.
4. Move the `X86/relative_lookup_table.ll` into the parent dir since
those checks are (mostly) generic, and allow those same checks to be run
for aarch64. That also includes a minor refactor for not checking the
`unnamed_addr` bit since `unnamed_addr` is
[dropped](https://github.com/llvm/llvm-project/blob/6959b7d081df5fb2349e67e0d8e8e87841fa10d9/llvm/lib/Transforms/Utils/RelLookupTableConverter.cpp#L82)
for aarch64.

AI: Gemini was used to help make this patch and debug the issue. I
reviewed this PR to the best of my ability before submitting.

Added: 
    llvm/test/Transforms/RelLookupTableConverter/AArch64/dynamic_relocs.ll
    llvm/test/Transforms/RelLookupTableConverter/relative_lookup_table.ll

Modified: 
    llvm/include/llvm/CodeGen/BasicTTIImpl.h
    llvm/lib/Transforms/Utils/RelLookupTableConverter.cpp
    llvm/test/Transforms/RelLookupTableConverter/AArch64/no_relative_lookup_table.ll

Removed: 
    llvm/test/Transforms/RelLookupTableConverter/X86/relative_lookup_table.ll


################################################################################
diff  --git a/llvm/include/llvm/CodeGen/BasicTTIImpl.h b/llvm/include/llvm/CodeGen/BasicTTIImpl.h
index 6718825daacac..ae720e221dd04 100644
--- a/llvm/include/llvm/CodeGen/BasicTTIImpl.h
+++ b/llvm/include/llvm/CodeGen/BasicTTIImpl.h
@@ -668,10 +668,9 @@ class BasicTTIImplBase : public TargetTransformInfoImplCRTPBase<T> {
     if (!TargetTriple.isArch64Bit())
       return false;
 
-    // Disable relative lookup tables for all AArch64 targets. Even AArch64's
-    // small code model allows a 4GB span of text + data, which might not fit
-    // in the 32-bit offsets relative lookup tables generate.
-    if (TargetTriple.isAArch64())
+    // TODO: Triggers issues on aarch64 on darwin, so temporarily disable it
+    // there.
+    if (TargetTriple.getArch() == Triple::aarch64 && TargetTriple.isOSDarwin())
       return false;
 
     return true;

diff  --git a/llvm/lib/Transforms/Utils/RelLookupTableConverter.cpp b/llvm/lib/Transforms/Utils/RelLookupTableConverter.cpp
index 1891dacd19a7a..c14116ee44f1a 100644
--- a/llvm/lib/Transforms/Utils/RelLookupTableConverter.cpp
+++ b/llvm/lib/Transforms/Utils/RelLookupTableConverter.cpp
@@ -116,6 +116,15 @@ static bool shouldConvertToRelLookupTable(LookupTableInfo &Info, Module &M,
         !GlovalVarOp->isImplicitDSOLocal())
       return false;
 
+    // On AArch64 small code model, the text-to-data span can be up to 4GB,
+    // which exceeds 32-bit signed relative offsets. Avoid converting if the
+    // target operand requires dynamic relocations (placing it in .data.rel.ro
+    // in the data segment rather than .rodata in the text segment).
+    if (TT.isAArch64() &&
+        (!GlovalVarOp->hasInitializer() ||
+         GlovalVarOp->getInitializer()->needsDynamicRelocation()))
+      return false;
+
     if (ShouldDropUnnamedAddr)
       GVOps.push_back(GlovalVarOp);
 

diff  --git a/llvm/test/Transforms/RelLookupTableConverter/AArch64/dynamic_relocs.ll b/llvm/test/Transforms/RelLookupTableConverter/AArch64/dynamic_relocs.ll
new file mode 100644
index 0000000000000..258ca1f4014e8
--- /dev/null
+++ b/llvm/test/Transforms/RelLookupTableConverter/AArch64/dynamic_relocs.ll
@@ -0,0 +1,101 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; REQUIRES: aarch64-registered-target
+; RUN: opt < %s -passes=rel-lookup-table-converter -mtriple=aarch64-linux -relocation-model=pic -S | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "aarch64-unknown-linux-gnu"
+
+; Negative test: operands contain function pointers which require dynamic
+; relocations under PIC (placing them in .data.rel.ro). On AArch64, avoid
+; converting to a relative lookup table because .data.rel.ro and .rodata
+; may be separated by more than 2GB.
+ at table0 = internal constant [2 x ptr] [ptr @ext_func0, ptr @ext_func1], align 8
+ at table1 = internal constant [2 x ptr] [ptr @ext_func1, ptr @ext_func0], align 8
+ at table2 = internal constant [2 x ptr] [ptr @ext_func0, ptr @ext_func0], align 8
+ at table3 = internal constant [2 x ptr] [ptr @ext_func1, ptr @ext_func1], align 8
+ at switch.table.get_table = private unnamed_addr constant [4 x ptr] [ptr @table0, ptr @table1, ptr @table2, ptr @table3], align 8
+
+;.
+; CHECK: @table0 = internal constant [2 x ptr] [ptr @ext_func0, ptr @ext_func1], align 8
+; CHECK: @table1 = internal constant [2 x ptr] [ptr @ext_func1, ptr @ext_func0], align 8
+; CHECK: @table2 = internal constant [2 x ptr] [ptr @ext_func0, ptr @ext_func0], align 8
+; CHECK: @table3 = internal constant [2 x ptr] [ptr @ext_func1, ptr @ext_func1], align 8
+; CHECK: @switch.table.get_table = private unnamed_addr constant [4 x ptr] [ptr @table0, ptr @table1, ptr @table2, ptr @table3], align 8
+; CHECK: @rodata_table0 = internal constant [2 x i32] [i32 0, i32 1], align 4
+; CHECK: @rodata_table1 = internal constant [2 x i32] [i32 1, i32 0], align 4
+; CHECK: @rodata_table2 = internal constant [2 x i32] zeroinitializer, align 4
+; CHECK: @rodata_table3 = internal constant [2 x i32] [i32 1, i32 1], align 4
+; CHECK:      @switch.table.get_rodata_table.rel = private unnamed_addr constant [4 x i32] [
+; CHECK-SAME:   i32 trunc (i64 sub (i64 ptrtoint (ptr @rodata_table0 to i64), i64 ptrtoint (ptr @switch.table.get_rodata_table.rel to i64)) to i32),
+; CHECK-SAME:   i32 trunc (i64 sub (i64 ptrtoint (ptr @rodata_table1 to i64), i64 ptrtoint (ptr @switch.table.get_rodata_table.rel to i64)) to i32),
+; CHECK-SAME:   i32 trunc (i64 sub (i64 ptrtoint (ptr @rodata_table2 to i64), i64 ptrtoint (ptr @switch.table.get_rodata_table.rel to i64)) to i32),
+; CHECK-SAME:   i32 trunc (i64 sub (i64 ptrtoint (ptr @rodata_table3 to i64), i64 ptrtoint (ptr @switch.table.get_rodata_table.rel to i64)) to i32)
+; CHECK-SAME: ], align 4
+;.
+define ptr @get_table(i32 %cond) {
+; CHECK-LABEL: @get_table(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[COND:%.*]], 4
+; CHECK-NEXT:    br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]]
+; CHECK:       switch.lookup:
+; CHECK-NEXT:    [[SWITCH_GEP:%.*]] = getelementptr inbounds [4 x ptr], ptr @switch.table.get_table, i32 0, i32 [[COND]]
+; CHECK-NEXT:    [[SWITCH_LOAD:%.*]] = load ptr, ptr [[SWITCH_GEP]], align 8
+; CHECK-NEXT:    ret ptr [[SWITCH_LOAD]]
+; CHECK:       return:
+; CHECK-NEXT:    ret ptr @table0
+;
+entry:
+  %0 = icmp ult i32 %cond, 4
+  br i1 %0, label %switch.lookup, label %return
+
+switch.lookup:
+  %switch.gep = getelementptr inbounds [4 x ptr], ptr @switch.table.get_table, i32 0, i32 %cond
+  %switch.load = load ptr, ptr %switch.gep, align 8
+  ret ptr %switch.load
+
+return:
+  ret ptr @table0
+}
+
+; Positive test: operands do not require dynamic relocations (placed in .rodata).
+; Converting to a relative lookup table on AArch64 is safe because both the
+; lookup table and its target operands are in .rodata within the text segment (< 2GB).
+ at rodata_table0 = internal constant [2 x i32] [i32 0, i32 1], align 4
+ at rodata_table1 = internal constant [2 x i32] [i32 1, i32 0], align 4
+ at rodata_table2 = internal constant [2 x i32] [i32 0, i32 0], align 4
+ at rodata_table3 = internal constant [2 x i32] [i32 1, i32 1], align 4
+ at switch.table.get_rodata_table = private unnamed_addr constant [4 x ptr] [
+  ptr @rodata_table0,
+  ptr @rodata_table1,
+  ptr @rodata_table2,
+  ptr @rodata_table3
+], align 8
+
+define ptr @get_rodata_table(i32 %cond) {
+; CHECK-LABEL: @get_rodata_table(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp ult i32 [[COND:%.*]], 4
+; CHECK-NEXT:    br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]]
+; CHECK:       switch.lookup:
+; CHECK-NEXT:    [[RELTABLE_SHIFT:%.*]] = shl i32 [[COND]], 2
+; CHECK-NEXT:    [[RELTABLE_INTRINSIC:%.*]] = call ptr @llvm.load.relative.i32(ptr @switch.table.get_rodata_table.rel, i32 [[RELTABLE_SHIFT]])
+; CHECK-NEXT:    ret ptr [[RELTABLE_INTRINSIC]]
+; CHECK:       return:
+; CHECK-NEXT:    ret ptr @rodata_table0
+;
+entry:
+  %0 = icmp ult i32 %cond, 4
+  br i1 %0, label %switch.lookup, label %return
+
+switch.lookup:
+  %switch.gep = getelementptr inbounds [4 x ptr], ptr @switch.table.get_rodata_table, i32 0, i32 %cond
+  %switch.load = load ptr, ptr %switch.gep, align 8
+  ret ptr %switch.load
+
+return:
+  ret ptr @rodata_table0
+}
+
+declare void @ext_func0()
+declare void @ext_func1()
+

diff  --git a/llvm/test/Transforms/RelLookupTableConverter/AArch64/no_relative_lookup_table.ll b/llvm/test/Transforms/RelLookupTableConverter/AArch64/no_relative_lookup_table.ll
index f52fa9ef5552d..584f14aeea1f7 100644
--- a/llvm/test/Transforms/RelLookupTableConverter/AArch64/no_relative_lookup_table.ll
+++ b/llvm/test/Transforms/RelLookupTableConverter/AArch64/no_relative_lookup_table.ll
@@ -1,6 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals
 ; REQUIRES: aarch64-registered-target
-; RUN: opt < %s -passes=rel-lookup-table-converter -mtriple=aarch64-linux -relocation-model=pic -S | FileCheck %s
+; RUN: opt < %s -passes=rel-lookup-table-converter -mtriple=aarch64-apple-darwin -relocation-model=pic -S | FileCheck %s
 
 @.str = private unnamed_addr constant [5 x i8] c"zero\00", align 1
 @.str.1 = private unnamed_addr constant [4 x i8] c"one\00", align 1

diff  --git a/llvm/test/Transforms/RelLookupTableConverter/X86/relative_lookup_table.ll b/llvm/test/Transforms/RelLookupTableConverter/relative_lookup_table.ll
similarity index 95%
rename from llvm/test/Transforms/RelLookupTableConverter/X86/relative_lookup_table.ll
rename to llvm/test/Transforms/RelLookupTableConverter/relative_lookup_table.ll
index efd644543c016..a900a0728c239 100644
--- a/llvm/test/Transforms/RelLookupTableConverter/X86/relative_lookup_table.ll
+++ b/llvm/test/Transforms/RelLookupTableConverter/relative_lookup_table.ll
@@ -1,8 +1,9 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 5
 ; REQUIRES: x86-registered-target
-; RUN: opt < %s -passes=rel-lookup-table-converter -relocation-model=pic -S | FileCheck %s
+; REQUIRES: aarch64-registered-target
+; RUN: opt < %s -passes=rel-lookup-table-converter -relocation-model=pic -mtriple=x86_64-unknown-linux-gnu -S | FileCheck %s
+; RUN: opt < %s -passes=rel-lookup-table-converter -relocation-model=pic -mtriple=aarch64-unknown-linux-gnu -S | FileCheck %s
 target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
 
 @.str = private unnamed_addr constant [5 x i8] c"zero\00", align 1
 @.str.1 = private unnamed_addr constant [4 x i8] c"one\00", align 1
@@ -99,17 +100,17 @@ target triple = "x86_64-unknown-linux-gnu"
 ], align 16
 
 ;.
-; CHECK: @.str = private unnamed_addr constant [5 x i8] c"zero\00", align 1
-; CHECK: @.str.1 = private unnamed_addr constant [4 x i8] c"one\00", align 1
-; CHECK: @.str.2 = private unnamed_addr constant [4 x i8] c"two\00", align 1
-; CHECK: @.str.3 = private unnamed_addr constant [8 x i8] c"default\00", align 1
-; CHECK: @.str.4 = private unnamed_addr constant [6 x i8] c"three\00", align 1
-; CHECK: @.str.5 = private unnamed_addr constant [5 x i8] c"str1\00", align 1
-; CHECK: @.str.6 = private unnamed_addr constant [5 x i8] c"str2\00", align 1
-; CHECK: @.str.7 = private unnamed_addr constant [12 x i8] c"singlevalue\00", align 1
-; CHECK: @.str.8 = private unnamed_addr constant [2 x i8] c"a\00", align 1
-; CHECK: @.str.9 = private unnamed_addr constant [2 x i8] c"b\00", align 1
-; CHECK: @.str.10 = private unnamed_addr constant [2 x i8] c"c\00", align 1
+; CHECK: @.str = private {{.*}}constant [5 x i8] c"zero\00", align 1
+; CHECK: @.str.1 = private {{.*}}constant [4 x i8] c"one\00", align 1
+; CHECK: @.str.2 = private {{.*}}constant [4 x i8] c"two\00", align 1
+; CHECK: @.str.3 = private {{.*}}constant [8 x i8] c"default\00", align 1
+; CHECK: @.str.4 = private {{.*}}constant [6 x i8] c"three\00", align 1
+; CHECK: @.str.5 = private {{.*}}constant [5 x i8] c"str1\00", align 1
+; CHECK: @.str.6 = private {{.*}}constant [5 x i8] c"str2\00", align 1
+; CHECK: @.str.7 = private {{.*}}constant [12 x i8] c"singlevalue\00", align 1
+; CHECK: @.str.8 = private {{.*}}constant [2 x i8] c"a\00", align 1
+; CHECK: @.str.9 = private {{.*}}constant [2 x i8] c"b\00", align 1
+; CHECK: @.str.10 = private {{.*}}constant [2 x i8] c"c\00", align 1
 ; CHECK: @a1 = external global i32, align 4
 ; CHECK: @b1 = external global i32, align 4
 ; CHECK: @c1 = external global i32, align 4


        


More information about the llvm-commits mailing list