From 23af64714d768a73b3abf0031306371c70073632 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 9 Jun 2026 15:41:48 -0700 Subject: [PATCH] [CodeGen] Fix type-legalization crash on vectors of reference-type pointers When the SLP vectorizer cost-models consecutive externref loads/stores, it forms a vector and asks the target to legalize it. Type legalization recovers the vector element type with DataLayout-free EVT helpers which route through EVT::getEVT()/MVT::getVT(). Those cannot lower a pointer to an MVT without a DataLayout and hit llvm_unreachable("Unknown type!") EVT::getTypeForEVT maps MVT::externref / MVT::funcref to opaque pointers in address spaces 10 and 20, so we can fix the crash by making that mapping invertible in MVT::getVT. The mapping is only applied on the HandleUnknown=false path used by that round-trip; HandleUnknown=true callers keep getting MVT::Other, so behavior for every other target is unchanged. Address spaces 10/20 are not reserved for WebAssembly, but a target only reaches this code with such a pointer after producing an externref/funcref MVT, which only WebAssembly does. With the type legalizable, scalarizes, so the vectorizer leaves the reference types scalar rather than crashing. --- llvm/lib/CodeGen/ValueTypes.cpp | 23 +++++++++ .../WebAssembly/externref-no-vectorize.ll | 48 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 llvm/test/Transforms/SLPVectorizer/WebAssembly/externref-no-vectorize.ll diff --git a/llvm/lib/CodeGen/ValueTypes.cpp b/llvm/lib/CodeGen/ValueTypes.cpp index e74068e22f4cd..6bb99f54fe9e0 100644 --- a/llvm/lib/CodeGen/ValueTypes.cpp +++ b/llvm/lib/CodeGen/ValueTypes.cpp @@ -252,6 +252,29 @@ MVT MVT::getVT(Type *Ty, bool HandleUnknown){ default: if (HandleUnknown) return MVT(MVT::Other); llvm_unreachable("Unknown type!"); + case Type::PointerTyID: { + if (HandleUnknown) + return MVT(MVT::Other); + // A pointer normally cannot be lowered to a value type without a + // DataLayout, so it is an "unknown" type here. The exception is the + // WebAssembly reference types: EVT::getTypeForEVT maps MVT::externref / + // MVT::funcref to opaque pointers in address spaces 10 and 20, and that + // mapping has to be invertible so that DataLayout-free EVT helpers can + // recover the element type of an / vector + // during type legalization instead of crashing. + // + // Address spaces 10 and 20 are not reserved for WebAssembly, but a target + // only reaches this code with such a pointer if it first produced an + // externref/funcref MVT, which only WebAssembly does. + switch (cast(Ty)->getAddressSpace()) { + case 10: + return MVT(MVT::externref); + case 20: + return MVT(MVT::funcref); + default: + llvm_unreachable("Unknown type!"); + } + } case Type::VoidTyID: return MVT::isVoid; case Type::ByteTyID: diff --git a/llvm/test/Transforms/SLPVectorizer/WebAssembly/externref-no-vectorize.ll b/llvm/test/Transforms/SLPVectorizer/WebAssembly/externref-no-vectorize.ll new file mode 100644 index 0000000000000..27298fb6ce4e9 --- /dev/null +++ b/llvm/test/Transforms/SLPVectorizer/WebAssembly/externref-no-vectorize.ll @@ -0,0 +1,48 @@ +; RUN: opt < %s -passes=slp-vectorizer -mtriple=wasm32-unknown-unknown -S | FileCheck %s + +; A WebAssembly reference type cannot be vectorized. +; This used to crash type legalization while the SLP vectorizer was computing +; the cost for a value. + +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20" +target triple = "wasm32-unknown-unknown" + +%externref = type ptr addrspace(10) + +declare %externref @foo() +declare void @bar(%externref) + +; Two externref phis that feed two calls are candidates for being gathered into +; a vector but they must be left scalar. Derived the following C code: +; +; __externref_t foo(void); +; void bar(__externref_t); +; void test(int flag, __externref_t ref1, __externref_t ref2) { +; if (flag) { +; ref1 = foo(); +; ref2 = foo(); +; } +; bar(ref1); bar(ref2); +; } +; +; CHECK-LABEL: @test( +; CHECK-NOT: phi <{{.*}} x ptr addrspace(10)> +; CHECK-NOT: call void @bar(<{{.*}} x ptr addrspace(10)> +; CHECK: ret void +define void @test(i32 %flag, %externref %ref1, %externref %ref2) { +entry: + %c = icmp eq i32 %flag, 0 + br i1 %c, label %join, label %then + +then: + %a = tail call %externref @foo() + %b = tail call %externref @foo() + br label %join + +join: + %p1 = phi %externref [ %a, %then ], [ %ref1, %entry ] + %p2 = phi %externref [ %b, %then ], [ %ref2, %entry ] + tail call void @bar(%externref %p1) + tail call void @bar(%externref %p2) + ret void +}