[llvm] [InstCombine] Skip GEP canonicalization for types containing target extension types (PR #191790)

Maosu Zhao via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 13 03:29:51 PDT 2026


https://github.com/zhaomaosu created https://github.com/llvm/llvm-project/pull/191790

GEP canonicalization in InstCombine rewrites single-index GEPs to use an [N x i8] element type. This transformation is invalid when the GEP's element type contains a target extension type, because target extension types carry backend-defined semantics that must be preserved through IR.

>From d09abeff49f1a314df8d521e3c2225d7d2377b3d Mon Sep 17 00:00:00 2001
From: Maosu Zhao <maosu.zhao at intel.com>
Date: Mon, 13 Apr 2026 18:20:01 +0800
Subject: [PATCH] [InstCombine] Skip GEP canonicalization for types containing
 target extension types

GEP canonicalization in InstCombine rewrites single-index GEPs to use
an [N x i8] element type. This transformation is invalid when the GEP's
element type contains a target extension type, because target extension
types carry backend-defined semantics that must be preserved through IR.
---
 .../InstCombine/InstructionCombining.cpp      | 21 ++++++++++-
 .../InstCombine/gep-target-ext-type.ll        | 37 +++++++++++++++++++
 2 files changed, 57 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/Transforms/InstCombine/gep-target-ext-type.ll

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 6798493de1aa3..ab2ff44b63709 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3479,13 +3479,32 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
         BackIndices, GEP.getNoWrapFlags());
   }
 
+  // Recursively check whether a type contains a target extension type.
+  // Target extension types have backend-defined semantics and must not be
+  // folded or reinterpreted by generic GEP canonicalization.
+  std::function<bool(Type *)> IsTargetExtType = [&](Type *Ty) -> bool {
+    if (isa<TargetExtType>(Ty))
+      return true;
+
+    if (Ty->isVectorTy())
+      return IsTargetExtType(Ty->getScalarType());
+
+    if (Ty->isArrayTy())
+      return IsTargetExtType(Ty->getArrayElementType());
+
+    if (auto *STy = dyn_cast<StructType>(Ty))
+      return any_of(STy->elements(), IsTargetExtType);
+
+    return false;
+  };
   // Canonicalize gep %T to gep [sizeof(%T) x i8]:
   auto IsCanonicalType = [](Type *Ty) {
     if (auto *AT = dyn_cast<ArrayType>(Ty))
       Ty = AT->getElementType();
     return Ty->isIntegerTy(8);
   };
-  if (Indices.size() == 1 && !IsCanonicalType(GEPEltType)) {
+  if (Indices.size() == 1 && !IsCanonicalType(GEPEltType) &&
+      !IsTargetExtType(GEPEltType)) {
     TypeSize Scale = DL.getTypeAllocSize(GEPEltType);
     assert(!Scale.isScalable() && "Should have been handled earlier");
     Type *NewElemTy = Builder.getInt8Ty();
diff --git a/llvm/test/Transforms/InstCombine/gep-target-ext-type.ll b/llvm/test/Transforms/InstCombine/gep-target-ext-type.ll
new file mode 100644
index 0000000000000..49f094bdee159
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/gep-target-ext-type.ll
@@ -0,0 +1,37 @@
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; Tests that GEP canonicalization (to [N x i8] element type) is suppressed
+; when the element type contains a target extension type.
+
+target datalayout = "e-p:64:64"
+
+; GEP over a nested struct: target extension type is in an inner struct.
+; Exercises the recursive descent through the struct branch.
+define ptr @gep_nested_struct_target_ext(ptr %p, i64 %idx) {
+; CHECK-LABEL: @gep_nested_struct_target_ext(
+; CHECK-NEXT:    [[GEP:%.*]] = getelementptr { i32, { target("spirv.DeviceEvent") } }, ptr [[P:%.*]], i64 [[IDX:%.*]]
+; CHECK-NEXT:    ret ptr [[GEP]]
+;
+  %gep = getelementptr { i32, { target("spirv.DeviceEvent") } }, ptr %p, i64 %idx
+  ret ptr %gep
+}
+
+; GEP over an array of target extension types.
+define ptr @gep_array_of_target_ext(ptr %p, i64 %idx) {
+; CHECK-LABEL: @gep_array_of_target_ext(
+; CHECK-NEXT:    [[GEP:%.*]] = getelementptr [4 x target("spirv.DeviceEvent")], ptr [[P:%.*]], i64 [[IDX:%.*]]
+; CHECK-NEXT:    ret ptr [[GEP]]
+;
+  %gep = getelementptr [4 x target("spirv.DeviceEvent")], ptr %p, i64 %idx
+  ret ptr %gep
+}
+
+; GEP over a direct target extension type (base case).
+define ptr @gep_direct_target_ext(ptr %p, i64 %idx) {
+; CHECK-LABEL: @gep_direct_target_ext(
+; CHECK-NEXT:    [[GEP:%.*]] = getelementptr target("spirv.DeviceEvent"), ptr [[P:%.*]], i64 [[IDX:%.*]]
+; CHECK-NEXT:    ret ptr [[GEP]]
+;
+  %gep = getelementptr target("spirv.DeviceEvent"), ptr %p, i64 %idx
+  ret ptr %gep
+}



More information about the llvm-commits mailing list