From 6545f29f64373d98ab093b059a00f3272efb59bc Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Sat, 1 Aug 2026 20:09:01 +0200 Subject: [PATCH] [IR] Truncate struct field offsets exceeding the pointer index width. (#213436) Update accumulateConstantOffset and collectOffset to implicitly truncate struct field offsets. This fixes crashes in the new tests, where the field offset does not fit in the index width. PR: https://github.com/llvm/llvm-project/pull/213436 --- llvm/lib/IR/Operator.cpp | 10 +++--- .../gep-struct-offset-index-width.ll | 31 +++++++++++++++++++ .../gep-struct-offset-index-width.ll | 28 +++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 llvm/test/Transforms/ConstraintElimination/gep-struct-offset-index-width.ll create mode 100644 llvm/test/Transforms/InstCombine/gep-struct-offset-index-width.ll diff --git a/llvm/lib/IR/Operator.cpp b/llvm/lib/IR/Operator.cpp index 6235ef7000c7..09a719b0f440 100644 --- a/llvm/lib/IR/Operator.cpp +++ b/llvm/lib/IR/Operator.cpp @@ -192,9 +192,10 @@ bool GEPOperator::accumulateConstantOffset( unsigned ElementIdx = ConstOffset->getZExtValue(); const StructLayout *SL = DL.getStructLayout(STy); // Element offset is in bytes. - if (!AccumulateOffset( - APInt(Offset.getBitWidth(), SL->getElementOffset(ElementIdx)), - 1)) + if (!AccumulateOffset(APInt(Offset.getBitWidth(), + SL->getElementOffset(ElementIdx), + /*isSigned=*/false, /*implicitTrunc=*/true), + 1)) return false; continue; } @@ -257,7 +258,8 @@ bool GEPOperator::collectOffset( unsigned ElementIdx = ConstOffset->getZExtValue(); const StructLayout *SL = DL.getStructLayout(STy); // Element offset is in bytes. - CollectConstantOffset(APInt(BitWidth, SL->getElementOffset(ElementIdx)), + CollectConstantOffset(APInt(BitWidth, SL->getElementOffset(ElementIdx), + /*isSigned=*/false, /*implicitTrunc=*/true), 1); continue; } diff --git a/llvm/test/Transforms/ConstraintElimination/gep-struct-offset-index-width.ll b/llvm/test/Transforms/ConstraintElimination/gep-struct-offset-index-width.ll new file mode 100644 index 000000000000..324d03a79f32 --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/gep-struct-offset-index-width.ll @@ -0,0 +1,31 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s + +target datalayout = "e-m:e-p:16:16" + +%s = type { [65536 x i8], i8 } + +; Offset of the struct's second field does not fit into a 16 bit pointer. +define i1 @cmp_gep_struct_offset_exceeds_index_width(ptr %p) { +; CHECK-LABEL: define i1 @cmp_gep_struct_offset_exceeds_index_width( +; CHECK-SAME: ptr [[P:%.*]]) { +; CHECK-NEXT: [[A:%.*]] = getelementptr inbounds [[S:%.*]], ptr [[P]], i16 0, i32 1 +; CHECK-NEXT: ret i1 false +; + %a = getelementptr inbounds %s, ptr %p, i16 0, i32 1 + %c = icmp ult ptr %a, %p + ret i1 %c +} + +define i1 @cmp_geps_struct_offset_exceeds_index_width(ptr %p) { +; CHECK-LABEL: define i1 @cmp_geps_struct_offset_exceeds_index_width( +; CHECK-SAME: ptr [[P:%.*]]) { +; CHECK-NEXT: [[A:%.*]] = getelementptr inbounds [[S:%.*]], ptr [[P]], i16 0, i32 1 +; CHECK-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[P]], i16 1 +; CHECK-NEXT: ret i1 true +; + %a = getelementptr inbounds %s, ptr %p, i16 0, i32 1 + %b = getelementptr inbounds i8, ptr %p, i16 1 + %c = icmp ult ptr %a, %b + ret i1 %c +} diff --git a/llvm/test/Transforms/InstCombine/gep-struct-offset-index-width.ll b/llvm/test/Transforms/InstCombine/gep-struct-offset-index-width.ll new file mode 100644 index 000000000000..65db7beae65b --- /dev/null +++ b/llvm/test/Transforms/InstCombine/gep-struct-offset-index-width.ll @@ -0,0 +1,28 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt -passes=instcombine -S %s | FileCheck %s + +target datalayout = "e-m:e-p:16:16" + +%s = type { [65536 x i8], i8 } + +; Offset of the struct's second field does not fit into a 16 bit pointer. +define ptr @gep_of_gep_struct_offset_exceeds_index_width(ptr %p, i16 %i) { +; CHECK-LABEL: define ptr @gep_of_gep_struct_offset_exceeds_index_width( +; CHECK-SAME: ptr [[P:%.*]], i16 [[I:%.*]]) { +; CHECK-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[P]], i16 [[I]] +; CHECK-NEXT: ret ptr [[B]] +; + %a = getelementptr inbounds %s, ptr %p, i16 0, i32 1 + %b = getelementptr inbounds i8, ptr %a, i16 %i + ret ptr %b +} + +define ptr @gep_struct_offset_exceeds_index_width_multiple(ptr %p) { +; CHECK-LABEL: define ptr @gep_struct_offset_exceeds_index_width_multiple( +; CHECK-SAME: ptr [[P:%.*]]) { +; CHECK-NEXT: ret ptr [[P]] +; + %a = getelementptr inbounds %s, ptr %p, i16 0, i32 1 + %b = getelementptr inbounds %s, ptr %a, i16 0, i32 1 + ret ptr %b +}