https://github.com/hoodmane updated 
https://github.com/llvm/llvm-project/pull/203165

>From 475bcb6fe19776b6a724648d16f29ebe3250f255 Mon Sep 17 00:00:00 2001
From: Hood Chatham <[email protected]>
Date: Wed, 10 Jun 2026 20:36:28 -0700
Subject: [PATCH] [WebAssembly] Represent reference types as TargetExtType

Originally #71540 by Paolo Matos, I picked it up and finished it.

Model WebAssembly externref and funcref as target("wasm.externref") /
target("wasm.funcref") TargetExtTypes instead of pointers in non-integral
address spaces 10 and 20.

The entire WebAssemblyLowerRefTypesIntPtrConv can be removed.

This breaks the GlobalISel handling for reference types, I just disabled
GlobalISel handling for functions that use them.

I added intrinsics for `wasm.ptr.to_funcref` and `wasm.funcref.to_ptr`.
ptr.to_funcref does a table.get from the indirect function pointer table. As a
special case, 0 is converted to the null funcref rather than doing table.get on
0. `wasm.funcref.to_ptr` is only handled when we call it immediately, otherwise
it will fail to lower. We could dynamically put the funcref into the table to
make it work but that would require a stack of spilled funcrefs and isn't worth
the effort.

In the process of looking into this, I noticed that clang used to allow casting
from funcref to function pointer but if the cast result escaped it would hit
`Cannot select: FUNCREF_TO_PTR` in the backend. I added a diagnostic that says
"a funcref can only be converted to a pointer to be directly called" to make
this a little cleaner.

Co-authored-by: Hood Chatham <[email protected]>
Co-authored-by: Paulo Matos <[email protected]>
---
 clang/lib/CodeGen/CGCall.cpp                  |  13 ++
 clang/lib/CodeGen/CGExprScalar.cpp            |  43 +++-
 clang/lib/CodeGen/CodeGenTypes.cpp            |   4 +
 .../WebAssembly/builtins-table-externref.c    |  28 +--
 .../WebAssembly/builtins-table-funcref.c      |  16 +-
 .../WebAssembly/builtins-test-fp-sig.c        |   4 +-
 .../test/CodeGen/WebAssembly/wasm-externref.c |   8 +-
 .../WebAssembly/wasm-funcref-to-ptr-error.c   |  21 ++
 clang/test/CodeGen/WebAssembly/wasm-funcref.c |  69 ++++--
 clang/test/CodeGen/builtins-wasm.c            |   4 +-
 llvm/include/llvm/IR/Intrinsics.h             |   2 +
 llvm/include/llvm/IR/IntrinsicsWebAssembly.td |  11 +
 .../CodeGen/SelectionDAG/SelectionDAGISel.cpp |  23 +-
 llvm/lib/CodeGen/ValueTypes.cpp               |   4 +
 llvm/lib/IR/Intrinsics.cpp                    |  17 +-
 llvm/lib/IR/Type.cpp                          |  12 +-
 llvm/lib/Target/WebAssembly/CMakeLists.txt    |   1 -
 .../GISel/WebAssemblyCallLowering.cpp         |  53 +++--
 .../WebAssembly/Utils/WasmAddressSpaces.h     |   6 +-
 .../Utils/WebAssemblyTypeUtilities.h          |  10 +-
 llvm/lib/Target/WebAssembly/WebAssembly.h     |   2 -
 .../WebAssembly/WebAssemblyISelDAGToDAG.cpp   |  21 ++
 .../WebAssembly/WebAssemblyISelLowering.cpp   |  49 +++--
 .../WebAssembly/WebAssemblyISelLowering.h     |   3 -
 .../WebAssemblyLowerRefTypesIntPtrConv.cpp    |  85 --------
 .../WebAssemblyRefTypeMem2Local.cpp           |   2 +
 .../WebAssembly/WebAssemblyTargetMachine.cpp  |   2 -
 .../GlobalISel/irtranslator/args.ll           |  24 +--
 .../GlobalISel/irtranslator/call-basics.ll    |  56 +----
 .../GlobalISel/irtranslator/ret-basics.ll     |  30 +--
 .../GlobalISel/reference-types-fallback.ll    |  40 ++++
 .../WebAssembly/externref-globalget.ll        |   2 +-
 .../WebAssembly/externref-globalset.ll        |   2 +-
 .../CodeGen/WebAssembly/externref-inttoptr.ll |  12 +-
 .../CodeGen/WebAssembly/externref-ptrtoint.ll |  11 +-
 .../CodeGen/WebAssembly/externref-tableget.ll |   2 +-
 .../CodeGen/WebAssembly/externref-tableset.ll |   2 +-
 .../WebAssembly/externref-unsized-load.ll     |   2 +-
 .../WebAssembly/externref-unsized-store.ll    |   2 +-
 llvm/test/CodeGen/WebAssembly/funcref-call.ll |  10 +-
 .../CodeGen/WebAssembly/funcref-globalget.ll  |   2 +-
 .../CodeGen/WebAssembly/funcref-globalset.ll  |   2 +-
 .../WebAssembly/funcref-ptr-conversion.ll     |  28 +++
 .../CodeGen/WebAssembly/funcref-table_call.ll |   6 +-
 .../CodeGen/WebAssembly/funcref-tableget.ll   |   2 +-
 .../CodeGen/WebAssembly/funcref-tableset.ll   |   2 +-
 .../WebAssembly/funcref-to-ptr-error.ll       |  30 +++
 llvm/test/CodeGen/WebAssembly/ref-null.ll     |   4 +-
 .../test/CodeGen/WebAssembly/ref-test-func.ll | 201 ++++++++++++------
 .../CodeGen/WebAssembly/ref-type-mem2local.ll |  44 ++--
 .../CodeGen/WebAssembly/select-reftype.ll     |  24 +--
 llvm/test/CodeGen/WebAssembly/table-copy.ll   |   2 +-
 llvm/test/CodeGen/WebAssembly/table-fill.ll   |   2 +-
 llvm/test/CodeGen/WebAssembly/table-grow.ll   |   2 +-
 llvm/test/CodeGen/WebAssembly/table-size.ll   |   2 +-
 llvm/test/CodeGen/WebAssembly/table-types.ll  |   4 +-
 .../llvm/lib/Target/WebAssembly/BUILD.gn      |   1 -
 57 files changed, 616 insertions(+), 450 deletions(-)
 create mode 100644 clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
 delete mode 100644 
llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp
 create mode 100644 
llvm/test/CodeGen/WebAssembly/GlobalISel/reference-types-fallback.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/funcref-ptr-conversion.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/funcref-to-ptr-error.ll

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 40cc275d40273..3b91a210248ff 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -49,6 +49,7 @@
 #include "llvm/IR/InlineAsm.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsWebAssembly.h"
 #include "llvm/IR/Type.h"
 #include "llvm/Transforms/Utils/Local.h"
 #include <optional>
@@ -5972,6 +5973,18 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo 
&CallInfo,
   const CGCallee &ConcreteCallee = Callee.prepareConcreteCallee(*this);
   llvm::Value *CalleePtr = ConcreteCallee.getFunctionPointer();
 
+  // A WebAssembly funcref is an opaque reference type and llvm only accepts
+  // function pointers as the call target. To make an indirect call through a
+  // reference type, first use the llvm.wasm.funcref.to_ptr intrinsic to make a
+  // fake function pointer to it. The backend lowers the resulting indirect 
call
+  // to a table.set into a single element dummy table + call_indirect 0.
+  if (auto *TET = dyn_cast<llvm::TargetExtType>(CalleePtr->getType());
+      TET && TET->getName() == "wasm.funcref") {
+    llvm::Function *ToPtr =
+        CGM.getIntrinsic(llvm::Intrinsic::wasm_funcref_to_ptr);
+    CalleePtr = Builder.CreateCall(ToPtr, {CalleePtr});
+  }
+
   // If we're using inalloca, set up that argument.
   if (ArgMemory.isValid()) {
     llvm::Value *Arg = ArgMemory.getPointer();
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp 
b/clang/lib/CodeGen/CGExprScalar.cpp
index 3a3dff7bec347..18ed6570730f4 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -47,6 +47,7 @@
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/IntrinsicsPowerPC.h"
+#include "llvm/IR/IntrinsicsWebAssembly.h"
 #include "llvm/IR/MatrixBuilder.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/TypeSize.h"
@@ -2839,6 +2840,43 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
     return CGF.authPointerToPointerCast(Result, E->getType(), DestTy);
   }
   case CK_AddressSpaceConversion: {
+    llvm::Type *DestLTy = ConvertType(DestTy);
+    // WebAssembly reference types are opaque target extension types so an
+    // "address space conversion" involving them is not a real pointer cast.
+    auto IsWasmFuncref = [](llvm::Type *T) {
+      auto *TET = dyn_cast<llvm::TargetExtType>(T);
+      return TET && TET->getName() == "wasm.funcref";
+    };
+    bool SrcIsFuncref = IsWasmFuncref(ConvertType(E->getType()));
+    bool DestIsFuncref = IsWasmFuncref(DestLTy);
+    if (SrcIsFuncref && DestIsFuncref) {
+      // funcref -> funcref (e.g. between differently-typed funcrefs) is the
+      // identity on the opaque reference value.
+      return Visit(E);
+    }
+    if (SrcIsFuncref && !DestIsFuncref) {
+      // funcref -> pointer: use wasm_funcref_to_ptr. This will probably crash
+      // later in codegen since we haven't implemented a way to actually get a
+      // function pointer from a funcref.
+      llvm::Function *ToPtr =
+          CGF.CGM.getIntrinsic(llvm::Intrinsic::wasm_funcref_to_ptr);
+      return CGF.Builder.CreateCall(ToPtr, {Visit(E)});
+    }
+    if (!SrcIsFuncref && DestIsFuncref) {
+      // A null function pointer converts to a null funcref (ref.null func),
+      // rather than a table lookup at index 0.
+      Expr::EvalResult NullResult;
+      if (E->EvaluateAsRValue(NullResult, CGF.getContext()) &&
+          NullResult.Val.isNullPointer()) {
+        if (NullResult.HasSideEffects)
+          Visit(E);
+        return llvm::Constant::getNullValue(DestLTy);
+      }
+      // pointer -> funcref: do a table.get from the indirect function table.
+      llvm::Function *ToFuncref =
+          CGF.CGM.getIntrinsic(llvm::Intrinsic::wasm_ptr_to_funcref);
+      return CGF.Builder.CreateCall(ToFuncref, {Visit(E)});
+    }
     Expr::EvalResult Result;
     if (E->EvaluateAsRValue(Result, CGF.getContext()) &&
         Result.Val.isNullPointer()) {
@@ -2847,12 +2885,11 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
       // eliminate the useless instructions emitted during translating E.
       if (Result.HasSideEffects)
         Visit(E);
-      return CGF.CGM.getNullPointer(cast<llvm::PointerType>(
-          ConvertType(DestTy)), DestTy);
+      return CGF.CGM.getNullPointer(cast<llvm::PointerType>(DestLTy), DestTy);
     }
     // Since target may map different address spaces in AST to the same address
     // space, an address space conversion may end up as a bitcast.
-    return CGF.performAddrSpaceCast(Visit(E), ConvertType(DestTy));
+    return CGF.performAddrSpaceCast(Visit(E), DestLTy);
   }
   case CK_AtomicToNonAtomic:
   case CK_NonAtomicToAtomic:
diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp 
b/clang/lib/CodeGen/CodeGenTypes.cpp
index b28a0eb82f302..3de3bad6affb5 100644
--- a/clang/lib/CodeGen/CodeGenTypes.cpp
+++ b/clang/lib/CodeGen/CodeGenTypes.cpp
@@ -645,6 +645,10 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
   case Type::Pointer: {
     const PointerType *PTy = cast<PointerType>(Ty);
     QualType ETy = PTy->getPointeeType();
+    if (ETy.getAddressSpace() == LangAS::wasm_funcref) {
+      ResultType = CGM.getTargetCodeGenInfo().getWasmFuncrefReferenceType();
+      break;
+    }
     unsigned AS = getTargetAddressSpace(ETy);
     ResultType = llvm::PointerType::get(getLLVMContext(), AS);
     break;
diff --git a/clang/test/CodeGen/WebAssembly/builtins-table-externref.c 
b/clang/test/CodeGen/WebAssembly/builtins-table-externref.c
index 7600a53ba3aa2..454fc31a5f53f 100644
--- a/clang/test/CodeGen/WebAssembly/builtins-table-externref.c
+++ b/clang/test/CodeGen/WebAssembly/builtins-table-externref.c
@@ -8,8 +8,8 @@ static const __externref_t const_table[0];
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get
 // CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0:[0-9]+]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(10) 
@llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
-// CHECK-NEXT:    ret ptr addrspace(10) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.externref") 
@llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
+// CHECK-NEXT:    ret target("wasm.externref") [[TMP0]]
 //
 __externref_t test_builtin_wasm_table_get(int index) {
   return __builtin_wasm_table_get(table, index);
@@ -18,18 +18,18 @@ __externref_t test_builtin_wasm_table_get(int index) {
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get_const
 // CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(10) 
@llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
-// CHECK-NEXT:    ret ptr addrspace(10) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.externref") 
@llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]])
+// CHECK-NEXT:    ret target("wasm.externref") [[TMP0]]
 //
 __externref_t test_builtin_wasm_table_get_const(const int index) {
   return __builtin_wasm_table_get(table, index);
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]]) 
#[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.externref") 
[[REF:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) 
@const_table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) 
@table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) 
@const_table, i32 [[INDEX]], target("wasm.externref") [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) 
@table, i32 [[INDEX]], target("wasm.externref") [[REF]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_set(const int index, __externref_t ref) {
@@ -38,10 +38,10 @@ void test_builtin_wasm_table_set(const int index, 
__externref_t ref) {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set_const
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]]) 
#[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.externref") 
[[REF:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) 
@table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
-// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) 
@const_table, i32 [[INDEX]], ptr addrspace(10) [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) 
@table, i32 [[INDEX]], target("wasm.externref") [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.externref(ptr addrspace(1) 
@const_table, i32 [[INDEX]], target("wasm.externref") [[REF]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_set_const(const int index, const __externref_t 
ref) {
@@ -60,9 +60,9 @@ int test_builtin_wasm_table_size() {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_grow
-// CHECK-SAME: (ptr addrspace(10) [[REF:%.*]], i32 noundef [[NELEM:%.*]]) 
#[[ATTR0]] {
+// CHECK-SAME: (target("wasm.externref") [[REF:%.*]], i32 noundef 
[[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.externref(ptr 
addrspace(1) @table, ptr addrspace(10) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.externref(ptr 
addrspace(1) @table, target("wasm.externref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret i32 [[TMP0]]
 //
 int test_builtin_wasm_table_grow(__externref_t ref, int nelem) {
@@ -70,9 +70,9 @@ int test_builtin_wasm_table_grow(__externref_t ref, int 
nelem) {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_fill
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]], i32 
noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.externref") 
[[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.fill.externref(ptr addrspace(1) 
@table, i32 [[INDEX]], ptr addrspace(10) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    call void @llvm.wasm.table.fill.externref(ptr addrspace(1) 
@table, i32 [[INDEX]], target("wasm.externref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_fill(int index, __externref_t ref, int nelem) {
diff --git a/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c 
b/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
index b4f729669a795..f80e9b10c4941 100644
--- a/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
+++ b/clang/test/CodeGen/WebAssembly/builtins-table-funcref.c
@@ -8,17 +8,17 @@ static funcref_t table[0];
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get
 // CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0:[0-9]+]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(20) 
@llvm.wasm.table.get.funcref(ptr addrspace(1) @table, i32 [[INDEX]])
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.funcref") 
@llvm.wasm.table.get.funcref(ptr addrspace(1) @table, i32 [[INDEX]])
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 funcref_t test_builtin_wasm_table_get(int index) {
   return __builtin_wasm_table_get(table, index);
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(20) noundef 
[[REF:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.funcref") noundef 
[[REF:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.set.funcref(ptr addrspace(1) 
@table, i32 [[INDEX]], ptr addrspace(20) [[REF]])
+// CHECK-NEXT:    call void @llvm.wasm.table.set.funcref(ptr addrspace(1) 
@table, i32 [[INDEX]], target("wasm.funcref") [[REF]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_set(int index, funcref_t ref) {
@@ -37,9 +37,9 @@ int test_builtin_wasm_table_size() {
 
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_grow
-// CHECK-SAME: (ptr addrspace(20) noundef [[REF:%.*]], i32 noundef 
[[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (target("wasm.funcref") noundef [[REF:%.*]], i32 noundef 
[[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.funcref(ptr 
addrspace(1) @table, ptr addrspace(20) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.funcref(ptr 
addrspace(1) @table, target("wasm.funcref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret i32 [[TMP0]]
 //
 int test_builtin_wasm_table_grow(funcref_t ref, int nelem) {
@@ -47,9 +47,9 @@ int test_builtin_wasm_table_grow(funcref_t ref, int nelem) {
 }
 
 // CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_fill
-// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(20) noundef 
[[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
+// CHECK-SAME: (i32 noundef [[INDEX:%.*]], target("wasm.funcref") noundef 
[[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    call void @llvm.wasm.table.fill.funcref(ptr addrspace(1) 
@table, i32 [[INDEX]], ptr addrspace(20) [[REF]], i32 [[NELEM]])
+// CHECK-NEXT:    call void @llvm.wasm.table.fill.funcref(ptr addrspace(1) 
@table, i32 [[INDEX]], target("wasm.funcref") [[REF]], i32 [[NELEM]])
 // CHECK-NEXT:    ret void
 //
 void test_builtin_wasm_table_fill(int index, funcref_t ref, int nelem) {
diff --git a/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c 
b/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c
index 88447f7fa232d..c8825028e1789 100644
--- a/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c
+++ b/clang/test/CodeGen/WebAssembly/builtins-test-fp-sig.c
@@ -32,13 +32,13 @@ void test_function_pointer_signature_varargs(FVarArgs func) 
{
 
 typedef __externref_t (*FExternRef)(__externref_t, __externref_t);
 void test_function_pointer_externref(FExternRef func) {
-  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr 
%func, ptr addrspace(10) poison, token poison, ptr addrspace(10) poison, ptr 
addrspace(10) poison)
+  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr 
%func, target("wasm.externref") poison, token poison, target("wasm.externref") 
poison, target("wasm.externref") poison)
   use(__builtin_wasm_test_function_pointer_signature(func));
 }
 
 typedef __funcref Fpointers (*FFuncRef)(__funcref Fvoid, __funcref Ffloats);
 void test_function_pointer_funcref(FFuncRef func) {
-  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr 
%func, ptr addrspace(20) poison, token poison, ptr addrspace(20) poison, ptr 
addrspace(20) poison)
+  // WEBASSEMBLY:  %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr 
%func, target("wasm.funcref") poison, token poison, target("wasm.funcref") 
poison, target("wasm.funcref") poison)
   use(__builtin_wasm_test_function_pointer_signature(func));
 }
 
diff --git a/clang/test/CodeGen/WebAssembly/wasm-externref.c 
b/clang/test/CodeGen/WebAssembly/wasm-externref.c
index 788438bb4a86a..d226c51b7fd4e 100644
--- a/clang/test/CodeGen/WebAssembly/wasm-externref.c
+++ b/clang/test/CodeGen/WebAssembly/wasm-externref.c
@@ -7,10 +7,10 @@ void helper(externref_t);
 
 // CHECK-LABEL: @handle(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[OBJ_ADDR:%.*]] = alloca ptr addrspace(10), align 1
-// CHECK-NEXT:    store ptr addrspace(10) [[OBJ:%.*]], ptr [[OBJ_ADDR]], align 
1
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(10), ptr [[OBJ_ADDR]], 
align 1
-// CHECK-NEXT:    call void @helper(ptr addrspace(10) [[TMP0]])
+// CHECK-NEXT:    [[OBJ_ADDR:%.*]] = alloca target("wasm.externref"), align 1
+// CHECK-NEXT:    store target("wasm.externref") [[OBJ:%.*]], ptr 
[[OBJ_ADDR]], align 1
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.externref"), ptr 
[[OBJ_ADDR]], align 1
+// CHECK-NEXT:    call void @helper(target("wasm.externref") [[TMP0]])
 // CHECK-NEXT:    ret void
 //
 void handle(externref_t obj) {
diff --git a/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c 
b/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
new file mode 100644
index 0000000000000..ce663a70dd3b1
--- /dev/null
+++ b/clang/test/CodeGen/WebAssembly/wasm-funcref-to-ptr-error.c
@@ -0,0 +1,21 @@
+// RUN: not %clang_cc1 -triple wasm32 -target-feature +reference-types -S -o 
/dev/null %s 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -triple wasm64 -target-feature +reference-types -S -o 
/dev/null %s 2>&1 | FileCheck %s
+
+// We haven't implemented a way of converting a funcref to a function pointer.
+// We can generate code for it if the result is immediately called, which 
avoids
+// the need for creating a function pointer. If the resulting pointer escapes,
+// we haven't implemented codegen for that. Diagnose it in the front end rather
+// than crashing in the backend.
+
+typedef void (*__funcref funcref_t)(void);
+typedef void (*fn_t)(void);
+
+// CHECK: error: a funcref can only be converted to a pointer to be directly 
called; the resulting pointer cannot otherwise be used
+void store_funcref_as_ptr(funcref_t f, fn_t *out) {
+  *out = (fn_t)f;
+}
+
+// CHECK: error: a funcref can only be converted to a pointer to be directly 
called; the resulting pointer cannot otherwise be used
+fn_t return_funcref_as_ptr(funcref_t f) {
+  return (fn_t)f;
+}
diff --git a/clang/test/CodeGen/WebAssembly/wasm-funcref.c 
b/clang/test/CodeGen/WebAssembly/wasm-funcref.c
index f01af0db321dd..63e9099b08a15 100644
--- a/clang/test/CodeGen/WebAssembly/wasm-funcref.c
+++ b/clang/test/CodeGen/WebAssembly/wasm-funcref.c
@@ -8,8 +8,8 @@ typedef int (*fn_t)(int);
 // Null funcref builtin call
 // CHECK-LABEL: @get_null(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(20) 
@llvm.wasm.ref.null.func()
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.funcref") 
@llvm.wasm.ref.null.func()
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 funcref_t get_null() {
   return __builtin_wasm_ref_null_func();
@@ -19,8 +19,8 @@ funcref_t get_null() {
 // default return value for builtin is a funcref with function type () -> ().
 // CHECK-LABEL: @get_null_ii(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[TMP0:%.*]] = call ptr addrspace(20) 
@llvm.wasm.ref.null.func()
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[TMP0:%.*]] = call target("wasm.funcref") 
@llvm.wasm.ref.null.func()
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 fn_funcref_t get_null_ii() {
   return (fn_funcref_t) __builtin_wasm_ref_null_func();
@@ -29,10 +29,10 @@ fn_funcref_t get_null_ii() {
 // Identity function for funcref.
 // CHECK-LABEL: @identity(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca ptr addrspace(20), align 4
-// CHECK-NEXT:    store ptr addrspace(20) [[FN:%.*]], ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(20), ptr [[FN_ADDR]], 
align 4
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP0]]
+// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca target("wasm.funcref"), align 4
+// CHECK-NEXT:    store target("wasm.funcref") [[FN:%.*]], ptr [[FN_ADDR]], 
align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.funcref"), ptr [[FN_ADDR]], 
align 4
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP0]]
 //
 funcref_t identity(funcref_t fn) {
   return fn;
@@ -43,10 +43,10 @@ void helper(funcref_t);
 // Pass funcref ref as an argument to a helper function.
 // CHECK-LABEL: @handle(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca ptr addrspace(20), align 4
-// CHECK-NEXT:    store ptr addrspace(20) [[FN:%.*]], ptr [[FN_ADDR]], align 4
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(20), ptr [[FN_ADDR]], 
align 4
-// CHECK-NEXT:    call void @helper(ptr addrspace(20) noundef [[TMP0]])
+// CHECK-NEXT:    [[FN_ADDR:%.*]] = alloca target("wasm.funcref"), align 4
+// CHECK-NEXT:    store target("wasm.funcref") [[FN:%.*]], ptr [[FN_ADDR]], 
align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.funcref"), ptr [[FN_ADDR]], 
align 4
+// CHECK-NEXT:    call void @helper(target("wasm.funcref") noundef [[TMP0]])
 // CHECK-NEXT:    ret i32 0
 //
 int handle(funcref_t fn) {
@@ -60,29 +60,58 @@ int handle(funcref_t fn) {
 // CHECK-NEXT:    [[FNPTR_ADDR:%.*]] = alloca ptr, align 4
 // CHECK-NEXT:    store ptr [[FNPTR:%.*]], ptr [[FNPTR_ADDR]], align 4
 // CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[FNPTR_ADDR]], align 4
-// CHECK-NEXT:    [[TMP1:%.*]] = addrspacecast ptr [[TMP0]] to ptr 
addrspace(20)
-// CHECK-NEXT:    ret ptr addrspace(20) [[TMP1]]
+// CHECK-NEXT:    [[TMP1:%.*]] = call target("wasm.funcref") 
@llvm.wasm.ptr.to_funcref(ptr [[TMP0]])
+// CHECK-NEXT:    ret target("wasm.funcref") [[TMP1]]
 //
 fn_funcref_t get_ref(fn_t fnptr) {
   return (fn_funcref_t) fnptr;
 }
 
+// Casting a null function pointer to a funcref yields a null funcref
+// (ref.null func), not a lookup of index 0 in the indirect function table.
+// CHECK-LABEL: @get_null_from_fnptr(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    ret target("wasm.funcref") zeroinitializer
+//
+fn_funcref_t get_null_from_fnptr() {
+  return (fn_funcref_t)(fn_t)0;
+}
+
 // Call funcref
 // CHECK-LABEL: @call_fn(
 // CHECK-NEXT:  entry:
-// CHECK-NEXT:    [[REF_ADDR:%.*]] = alloca ptr addrspace(20), align 4
+// CHECK-NEXT:    [[REF_ADDR:%.*]] = alloca target("wasm.funcref"), align 4
 // CHECK-NEXT:    [[X_ADDR:%.*]] = alloca i32, align 4
-// CHECK-NEXT:    store ptr addrspace(20) [[REF:%.*]], ptr [[REF_ADDR]], align 
4
+// CHECK-NEXT:    store target("wasm.funcref") [[REF:%.*]], ptr [[REF_ADDR]], 
align 4
 // CHECK-NEXT:    store i32 [[X:%.*]], ptr [[X_ADDR]], align 4
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr addrspace(20), ptr [[REF_ADDR]], 
align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.funcref"), ptr 
[[REF_ADDR]], align 4
 // CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4
-// CHECK-NEXT:    [[CALL:%.*]] = call addrspace(20) i32 [[TMP0]](i32 noundef 
[[TMP1]])
+// CHECK-NEXT:    [[TMP2:%.*]] = call ptr 
@llvm.wasm.funcref.to_ptr(target("wasm.funcref") [[TMP0]])
+// CHECK-NEXT:    [[CALL:%.*]] = call i32 [[TMP2]](i32 noundef [[TMP1]])
 // CHECK-NEXT:    ret i32 [[CALL]]
 //
 int call_fn(fn_funcref_t ref, int x) {
   return ref(x);
 }
 
+// Explicitly casting a funcref to a plain function pointer and calling it
+// immediately is allowed: the conversion feeds directly into the indirect 
call.
+// CHECK-LABEL: @call_fn_via_cast(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[REF_ADDR:%.*]] = alloca target("wasm.funcref"), align 4
+// CHECK-NEXT:    [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store target("wasm.funcref") [[REF:%.*]], ptr [[REF_ADDR]], 
align 4
+// CHECK-NEXT:    store i32 [[X:%.*]], ptr [[X_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load target("wasm.funcref"), ptr 
[[REF_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = call ptr 
@llvm.wasm.funcref.to_ptr(target("wasm.funcref") [[TMP0]])
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NEXT:    [[CALL:%.*]] = call i32 [[TMP1]](i32 noundef [[TMP2]])
+// CHECK-NEXT:    ret i32 [[CALL]]
+//
+int call_fn_via_cast(fn_funcref_t ref, int x) {
+  return ((fn_t)ref)(x);
+}
+
 typedef fn_funcref_t (*builtin_refnull_t)();
 
 // Calling ref.null through a function pointer.
@@ -91,8 +120,8 @@ typedef fn_funcref_t (*builtin_refnull_t)();
 // CHECK-NEXT:    [[REFNULL_ADDR:%.*]] = alloca ptr, align 4
 // CHECK-NEXT:    store ptr [[REFNULL:%.*]], ptr [[REFNULL_ADDR]], align 4
 // CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[REFNULL_ADDR]], align 4
-// CHECK-NEXT:    [[CALL:%.*]] = call ptr addrspace(20) [[TMP0]]()
-// CHECK-NEXT:    ret ptr addrspace(20) [[CALL]]
+// CHECK-NEXT:    [[CALL:%.*]] = call target("wasm.funcref") [[TMP0]]()
+// CHECK-NEXT:    ret target("wasm.funcref") [[CALL]]
 //
 fn_funcref_t get_null_fptr(builtin_refnull_t refnull) {
   return refnull();
diff --git a/clang/test/CodeGen/builtins-wasm.c 
b/clang/test/CodeGen/builtins-wasm.c
index 375664b852636..40788b0afeb45 100644
--- a/clang/test/CodeGen/builtins-wasm.c
+++ b/clang/test/CodeGen/builtins-wasm.c
@@ -737,13 +737,13 @@ f16x8 pmax_f16x8(f16x8 a, f16x8 b) {
 }
 __externref_t externref_null() {
   return __builtin_wasm_ref_null_extern();
-  // WEBASSEMBLY: tail call ptr addrspace(10) @llvm.wasm.ref.null.extern()
+  // WEBASSEMBLY: tail call target("wasm.externref") 
@llvm.wasm.ref.null.extern()
   // WEBASSEMBLY-NEXT: ret
 }
 
 int externref_is_null(__externref_t arg) {
   return __builtin_wasm_ref_is_null_extern(arg);
-  // WEBASSEMBLY: tail call i32 @llvm.wasm.ref.is_null.extern(ptr 
addrspace(10) %arg)
+  // WEBASSEMBLY: tail call i32 
@llvm.wasm.ref.is_null.extern(target("wasm.externref") %arg)
   // WEBASSEMBLY-NEXT: ret
 }
 
diff --git a/llvm/include/llvm/IR/Intrinsics.h 
b/llvm/include/llvm/IR/Intrinsics.h
index 5ccb18d7281bc..d78a66e45a3ad 100644
--- a/llvm/include/llvm/IR/Intrinsics.h
+++ b/llvm/include/llvm/IR/Intrinsics.h
@@ -177,6 +177,8 @@ struct IITDescriptor {
     AMX,
     PPCQuad,
     AArch64Svcount,
+    WasmExternref,
+    WasmFuncref,
 
     // Overloaded type.
     Overloaded, // AnyKind and overload index in OverloadInfo.
diff --git a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td 
b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
index c1e4b97e96bc8..a0e83cee9f055 100644
--- a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
+++ b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td
@@ -47,6 +47,17 @@ def int_wasm_ref_test_func
     : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_ptr_ty, llvm_vararg_ty],
                             [IntrNoMem]>;
 
+//===----------------------------------------------------------------------===//
+// funcref <--> pointer conversion intrinsics
+//===----------------------------------------------------------------------===//
+def int_wasm_funcref_to_ptr :
+  DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_funcref_ty],
+                        [IntrNoMem], "llvm.wasm.funcref.to_ptr">;
+
+def int_wasm_ptr_to_funcref :
+  DefaultAttrsIntrinsic<[llvm_funcref_ty], [llvm_ptr_ty],
+                        [IntrNoMem], "llvm.wasm.ptr.to_funcref">;
+
 
//===----------------------------------------------------------------------===//
 // Table intrinsics
 
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp 
b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
index 5ae52cae771fb..1b479c2b36058 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -230,9 +230,26 @@ static bool dontUseFastISelFor(const Function &Fn) {
   // Debug info on those is reliant on good Argument lowering, and FastISel is
   // not capable of lowering the entire function. Mixing the two selectors tend
   // to result in poor lowering of Arguments.
-  return any_of(Fn.args(), [](const Argument &Arg) {
-    return Arg.hasAttribute(Attribute::AttrKind::SwiftAsync);
-  });
+  if (any_of(Fn.args(), [](const Argument &Arg) {
+        return Arg.hasAttribute(Attribute::AttrKind::SwiftAsync);
+      }))
+    return true;
+
+  // A WebAssembly funcref call is expressed in IR as a call through the 
pointer
+  // produced by the llvm.wasm.funcref.to_ptr intrinsic. SelectionDAG fuses the
+  // intrinsic and the call into a table.set + call_indirect through the
+  // dedicated __funcref_call_table. FastISel selects each call as its own
+  // single-instruction block and therefore cannot perform this fusion, so fall
+  // back to SelectionDAG for the whole function when the intrinsic is present.
+  if (Fn.getParent()->getTargetTriple().isWasm()) {
+    for (const BasicBlock &BB : Fn)
+      for (const Instruction &I : BB)
+        if (const auto *II = dyn_cast<IntrinsicInst>(&I))
+          if (II->getIntrinsicID() == Intrinsic::wasm_funcref_to_ptr)
+            return true;
+  }
+
+  return false;
 }
 
 static bool maintainPGOProfile(const TargetMachine &TM,
diff --git a/llvm/lib/CodeGen/ValueTypes.cpp b/llvm/lib/CodeGen/ValueTypes.cpp
index e74068e22f4cd..a8eb5f801a280 100644
--- a/llvm/lib/CodeGen/ValueTypes.cpp
+++ b/llvm/lib/CodeGen/ValueTypes.cpp
@@ -268,6 +268,10 @@ MVT MVT::getVT(Type *Ty, bool HandleUnknown){
     TargetExtType *TargetExtTy = cast<TargetExtType>(Ty);
     if (TargetExtTy->getName() == "aarch64.svcount")
       return MVT(MVT::aarch64svcount);
+    else if (TargetExtTy->getName() == "wasm.externref")
+      return MVT(MVT::externref);
+    else if (TargetExtTy->getName() == "wasm.funcref")
+      return MVT(MVT::funcref);
     else if (TargetExtTy->getName().starts_with("spirv."))
       return MVT(MVT::spirvbuiltin);
     if (TargetExtTy->getName() == "riscv.vector.tuple") {
diff --git a/llvm/lib/IR/Intrinsics.cpp b/llvm/lib/IR/Intrinsics.cpp
index b0bad878f0c6f..ed4d37df33e7d 100644
--- a/llvm/lib/IR/Intrinsics.cpp
+++ b/llvm/lib/IR/Intrinsics.cpp
@@ -358,10 +358,10 @@ DecodeIITType(unsigned &NextElt, ArrayRef<unsigned char> 
Infos,
     DecodeIITType(NextElt, Infos, OutputTable);
     return;
   case IIT_EXTERNREF:
-    OutputTable.push_back(IITDescriptor::get(IITDescriptor::Pointer, 10));
+    OutputTable.push_back(IITDescriptor::get(IITDescriptor::WasmExternref, 0));
     return;
   case IIT_FUNCREF:
-    OutputTable.push_back(IITDescriptor::get(IITDescriptor::Pointer, 20));
+    OutputTable.push_back(IITDescriptor::get(IITDescriptor::WasmFuncref, 0));
     return;
   case IIT_PTR:
     OutputTable.push_back(IITDescriptor::get(IITDescriptor::Pointer, 0));
@@ -556,7 +556,10 @@ static Type 
*DecodeFixedType(ArrayRef<Intrinsic::IITDescriptor> &Infos,
     return Type::getPPC_FP128Ty(Context);
   case IITDescriptor::AArch64Svcount:
     return TargetExtType::get(Context, "aarch64.svcount");
-
+  case IITDescriptor::WasmExternref:
+    return TargetExtType::get(Context, "wasm.externref");
+  case IITDescriptor::WasmFuncref:
+    return TargetExtType::get(Context, "wasm.funcref");
   case IITDescriptor::Integer:
     return IntegerType::get(Context, D.IntegerWidth);
   case IITDescriptor::Vector:
@@ -1029,6 +1032,14 @@ matchIntrinsicType(Type *Ty, 
ArrayRef<Intrinsic::IITDescriptor> &Infos,
     return PrintMsg(isa<TargetExtType>(Ty) &&
                         cast<TargetExtType>(Ty)->getName() == 
"aarch64.svcount",
                     "aarch64.svcount");
+  case IITDescriptor::WasmExternref:
+    return PrintMsg(isa<TargetExtType>(Ty) &&
+                        cast<TargetExtType>(Ty)->getName() == "wasm.externref",
+                    "wasm.externref");
+  case IITDescriptor::WasmFuncref:
+    return PrintMsg(isa<TargetExtType>(Ty) &&
+                        cast<TargetExtType>(Ty)->getName() == "wasm.funcref",
+                    "wasm.funcref");
   case IITDescriptor::Vector: {
     VectorType *VT = dyn_cast<VectorType>(Ty);
     StringRef Scalable = D.VectorWidth.isScalable() ? "vscale " : "";
diff --git a/llvm/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp
index 47b230d44285b..d5f4671487acb 100644
--- a/llvm/lib/IR/Type.cpp
+++ b/llvm/lib/IR/Type.cpp
@@ -334,13 +334,11 @@ Type *Type::getByteFromIntType(Type *Ty) {
 }
 
 Type *Type::getWasm_ExternrefTy(LLVMContext &C) {
-  // opaque pointer in addrspace(10)
-  return PointerType::get(C, 10);
+  return TargetExtType::get(C, "wasm.externref", {}, {});
 }
 
 Type *Type::getWasm_FuncrefTy(LLVMContext &C) {
-  // opaque pointer in addrspace(20)
-  return PointerType::get(C, 20);
+  return TargetExtType::get(C, "wasm.funcref", {}, {});
 }
 
 
//===----------------------------------------------------------------------===//
@@ -1134,6 +1132,12 @@ static TargetTypeInfo getTargetTypeInfo(const 
TargetExtType *Ty) {
                           TargetExtType::CanBeVectorElement);
   }
 
+  // Opaque types in the WebAssembly name space.
+  if (Name == "wasm.funcref" || Name == "wasm.externref")
+    return TargetTypeInfo(PointerType::getUnqual(C), 
TargetExtType::HasZeroInit,
+                          TargetExtType::CanBeGlobal,
+                          TargetExtType::CanBeLocal);
+
   return TargetTypeInfo(Type::getVoidTy(C));
 }
 
diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt 
b/llvm/lib/Target/WebAssembly/CMakeLists.txt
index ef5f7d11e7e49..a024a354890d2 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -51,7 +51,6 @@ add_llvm_target(WebAssemblyCodeGen
   WebAssemblyInstrInfo.cpp
   WebAssemblyLowerBrUnless.cpp
   WebAssemblyLowerEmscriptenEHSjLj.cpp
-  WebAssemblyLowerRefTypesIntPtrConv.cpp
   WebAssemblyMachineFunctionInfo.cpp
   WebAssemblyMCInstLower.cpp
   WebAssemblyMCLowerPrePass.cpp
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp 
b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
index 9f3a1d1ba7fa2..3241e30789b97 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyCallLowering.cpp
@@ -14,7 +14,7 @@
 
 #include "WebAssemblyCallLowering.h"
 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
-#include "Utils/WasmAddressSpaces.h"
+#include "Utils/WebAssemblyTypeUtilities.h"
 #include "WebAssemblyISelLowering.h"
 #include "WebAssemblyMachineFunctionInfo.h"
 #include "WebAssemblyRegisterInfo.h"
@@ -58,22 +58,18 @@ static unsigned extendOpFromFlags(ISD::ArgFlagsTy Flags) {
   return TargetOpcode::G_ANYEXT;
 }
 
-static LLT getLLTForWasmMVT(MVT Ty, const DataLayout &DL) {
-  if (Ty == MVT::externref) {
-    return LLT::pointer(
-        WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF,
-        DL.getPointerSizeInBits(
-            WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF));
-  }
-
-  if (Ty == MVT::funcref) {
-    return LLT::pointer(
-        WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF,
-        DL.getPointerSizeInBits(
-            WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF));
-  }
-
-  return llvm::getLLTForMVT(Ty);
+// GlobalISel doesn't handle reference types. We bail out of GlobalISel for
+// functions passing/returning references and fall back to SDAG.
+static bool typeContainsReference(const Type *Ty) {
+  if (WebAssembly::isWebAssemblyReferenceType(Ty))
+    return true;
+  if (const auto *ArrTy = dyn_cast<ArrayType>(Ty))
+    return typeContainsReference(ArrTy->getElementType());
+  if (const auto *StructTy = dyn_cast<StructType>(Ty))
+    return llvm::any_of(StructTy->elements(), [](const Type *ElemTy) {
+      return typeContainsReference(ElemTy);
+    });
+  return false;
 }
 
 WebAssemblyCallLowering::WebAssemblyCallLowering(
@@ -99,6 +95,9 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder 
&MIRBuilder,
   const WebAssemblyTargetLowering &TLI = *getTLI<WebAssemblyTargetLowering>();
   const DataLayout &DL = F.getDataLayout();
 
+  if (Val && typeContainsReference(Val->getType()))
+    return false;
+
   MachineInstrBuilder MIB = MIRBuilder.buildInstrNoInsert(WebAssembly::RETURN);
 
   assert(((Val && !VRegs.empty()) || (!Val && VRegs.empty())) &&
@@ -134,7 +133,7 @@ bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder 
&MIRBuilder,
             TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
         const LLT OrigLLT =
             getLLTForType(*OrigVT.getTypeForEVT(F.getContext()), DL);
-        const LLT NewLLT = getLLTForWasmMVT(NewVT, DL);
+        const LLT NewLLT = llvm::getLLTForMVT(NewVT);
 
         const TargetRegisterClass &NewRegClass = *TLI.getRegClassFor(NewVT);
 
@@ -276,6 +275,12 @@ bool WebAssemblyCallLowering::lowerFormalArguments(
   if (!callingConvSupported(CallConv))
     return false;
 
+  if (typeContainsReference(F.getReturnType()))
+    return false;
+  for (const Argument &Arg : F.args())
+    if (typeContainsReference(Arg.getType()))
+      return false;
+
   MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS);
   MF.front().addLiveIn(WebAssembly::ARGUMENTS);
 
@@ -308,7 +313,7 @@ bool WebAssemblyCallLowering::lowerFormalArguments(
     const MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
     const LLT OrigLLT =
         getLLTForType(*OrigVT.getTypeForEVT(F.getContext()), DL);
-    const LLT NewLLT = getLLTForWasmMVT(NewVT, DL);
+    const LLT NewLLT = llvm::getLLTForMVT(NewVT);
 
     // If we need to split the type over multiple regs, check it's a scenario
     // we currently support.
@@ -410,6 +415,12 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder 
&MIRBuilder,
   if (!callingConvSupported(CallConv))
     return false;
 
+  if (typeContainsReference(Info.OrigRet.Ty))
+    return false;
+  for (const ArgInfo &Arg : Info.OrigArgs)
+    if (typeContainsReference(Arg.Ty))
+      return false;
+
   // TODO: tail calls
   if (Info.IsMustTailCall)
     return false;
@@ -456,7 +467,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder 
&MIRBuilder,
     const MVT NewVT = TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
     const LLT OrigLLT =
         getLLTForType(*OrigVT.getTypeForEVT(F.getContext()), DL);
-    const LLT NewLLT = getLLTForWasmMVT(NewVT, DL);
+    const LLT NewLLT = llvm::getLLTForMVT(NewVT);
 
     const TargetRegisterClass &NewRegClass = *TLI.getRegClassFor(NewVT);
 
@@ -552,7 +563,7 @@ bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder 
&MIRBuilder,
           TLI.getRegisterTypeForCallingConv(Ctx, CallConv, OrigVT);
       const LLT OrigLLT =
           getLLTForType(*OrigVT.getTypeForEVT(F.getContext()), DL);
-      const LLT NewLLT = getLLTForWasmMVT(NewVT, DL);
+      const LLT NewLLT = llvm::getLLTForMVT(NewVT);
 
       const TargetRegisterClass &NewRegClass = *TLI.getRegClassFor(NewVT);
 
diff --git a/llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h 
b/llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h
index 2239badca69c3..d2ab2c0f9e777 100644
--- a/llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h
+++ b/llvm/lib/Target/WebAssembly/Utils/WasmAddressSpaces.h
@@ -24,11 +24,7 @@ enum WasmAddressSpace : unsigned {
   // linear memory: WebAssembly globals or WebAssembly locals.  Loads and 
stores
   // to these pointers are lowered to global.get / global.set or local.get /
   // local.set, as appropriate.
-  WASM_ADDRESS_SPACE_VAR = 1,
-  // A non-integral address space for externref values
-  WASM_ADDRESS_SPACE_EXTERNREF = 10,
-  // A non-integral address space for funcref values
-  WASM_ADDRESS_SPACE_FUNCREF = 20,
+  WASM_ADDRESS_SPACE_VAR = 1
 };
 
 inline bool isDefaultAddressSpace(unsigned AS) {
diff --git a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h 
b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h
index c5e0dcfffef9c..47ba91df81161 100644
--- a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h
+++ b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h
@@ -28,16 +28,14 @@ namespace WebAssembly {
 
 /// Return true if this is a WebAssembly Externref Type.
 inline bool isWebAssemblyExternrefType(const Type *Ty) {
-  return Ty->isPointerTy() &&
-         Ty->getPointerAddressSpace() ==
-             WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF;
+  const TargetExtType *TargetTy = dyn_cast<TargetExtType>(Ty);
+  return TargetTy && TargetTy->getName() == "wasm.externref";
 }
 
 /// Return true if this is a WebAssembly Funcref Type.
 inline bool isWebAssemblyFuncrefType(const Type *Ty) {
-  return Ty->isPointerTy() &&
-         Ty->getPointerAddressSpace() ==
-             WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF;
+  const TargetExtType *TargetTy = dyn_cast<TargetExtType>(Ty);
+  return TargetTy && TargetTy->getName() == "wasm.funcref";
 }
 
 /// Return true if this is a WebAssembly Reference Type.
diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h 
b/llvm/lib/Target/WebAssembly/WebAssembly.h
index 101348501bece..6556cb5e25c0e 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.h
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.h
@@ -32,7 +32,6 @@ ModulePass *createWebAssemblyLowerEmscriptenEHSjLj();
 ModulePass *createWebAssemblyAddMissingPrototypes();
 ModulePass *createWebAssemblyFixFunctionBitcasts();
 FunctionPass *createWebAssemblyOptimizeReturned();
-FunctionPass *createWebAssemblyLowerRefTypesIntPtrConv();
 FunctionPass *createWebAssemblyRefTypeMem2Local();
 FunctionPass *createWebAssemblyReduceToAnyAllTrue(WebAssemblyTargetMachine 
&TM);
 
@@ -94,7 +93,6 @@ void 
initializeWebAssemblyFixIrreducibleControlFlowPass(PassRegistry &);
 void initializeWebAssemblyLateEHPreparePass(PassRegistry &);
 void initializeWebAssemblyLowerBrUnlessPass(PassRegistry &);
 void initializeWebAssemblyLowerEmscriptenEHSjLjPass(PassRegistry &);
-void initializeWebAssemblyLowerRefTypesIntPtrConvPass(PassRegistry &);
 void initializeWebAssemblyMCLowerPrePassPass(PassRegistry &);
 void initializeWebAssemblyMemIntrinsicResultsPass(PassRegistry &);
 void initializeWebAssemblyNullifyDebugValueListsPass(PassRegistry &);
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp 
b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
index c7b57588877b7..69e9e6b5a9717 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
@@ -275,6 +275,27 @@ void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {
       ReplaceNode(Node, TLSAlign);
       return;
     }
+    case Intrinsic::wasm_ptr_to_funcref: {
+      // Convert a function pointer to a funcref by reading the corresponding
+      // entry from the __indirect_function_table.
+      MachineFunction &MF = CurDAG->getMachineFunction();
+      auto PtrVT = 
MVT::getIntegerVT(MF.getDataLayout().getPointerSizeInBits());
+      MCSymbol *Table = WebAssembly::getOrCreateFunctionTableSymbol(
+          MF.getContext(), Subtarget);
+      SDValue TableSym = CurDAG->getMCSymbol(Table, PtrVT);
+      SDValue FuncPtr = Node->getOperand(1);
+      if (Subtarget->hasAddr64() && FuncPtr.getValueType() == MVT::i64) {
+        // table.get expects an i32 but on 64 bit platforms the function 
pointer
+        // is an i64. In that case, i32.wrap_i64 to convert.
+        FuncPtr = SDValue(CurDAG->getMachineNode(WebAssembly::I32_WRAP_I64, DL,
+                                                 MVT::i32, FuncPtr),
+                          0);
+      }
+      MachineSDNode *FuncRef = CurDAG->getMachineNode(
+          WebAssembly::TABLE_GET_FUNCREF, DL, MVT::funcref, TableSym, FuncPtr);
+      ReplaceNode(Node, FuncRef);
+      return;
+    }
     case Intrinsic::wasm_ref_test_func: {
       // First emit the TABLE_GET instruction to convert function pointer ==>
       // funcref
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp 
b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index bba3a34b08df8..7c0fc3fffea01 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -436,24 +436,6 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
   setMinimumJumpTableEntries(2);
 }
 
-MVT WebAssemblyTargetLowering::getPointerTy(const DataLayout &DL,
-                                            uint32_t AS) const {
-  if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF)
-    return MVT::externref;
-  if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF)
-    return MVT::funcref;
-  return TargetLowering::getPointerTy(DL, AS);
-}
-
-MVT WebAssemblyTargetLowering::getPointerMemTy(const DataLayout &DL,
-                                               uint32_t AS) const {
-  if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF)
-    return MVT::externref;
-  if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF)
-    return MVT::funcref;
-  return TargetLowering::getPointerMemTy(DL, AS);
-}
-
 TargetLowering::AtomicExpansionKind
 WebAssemblyTargetLowering::shouldExpandAtomicRMWInIR(
     const AtomicRMWInst *AI) const {
@@ -1294,6 +1276,17 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo 
&CLI,
   MachineFunction &MF = DAG.getMachineFunction();
   auto Layout = MF.getDataLayout();
 
+  // A call through a funcref is expressed in IR as a call through the pointer
+  // produced by the llvm.wasm.funcref.to_ptr intrinsic. Detect this here and
+  // recover the underlying funcref value so the call can be lowered to a
+  // table.set + call_indirect through the dedicated __funcref_call_table.
+  bool IsFuncrefCall = false;
+  if (Callee.getOpcode() == ISD::INTRINSIC_WO_CHAIN &&
+      Callee.getConstantOperandVal(0) == Intrinsic::wasm_funcref_to_ptr) {
+    Callee = Callee.getOperand(1);
+    IsFuncrefCall = true;
+  }
+
   CallingConv::ID CallConv = CLI.CallConv;
   if (!callingConvSupported(CallConv))
     fail(DL, DAG,
@@ -1537,8 +1530,7 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo 
&CLI,
 
   // Lastly, if this is a call to a funcref we need to add an instruction
   // table.set to the chain and transform the call.
-  if (CLI.CB && WebAssembly::isWebAssemblyFuncrefType(
-                    CLI.CB->getCalledOperand()->getType())) {
+  if (IsFuncrefCall) {
     // In the absence of function references proposal where a funcref call is
     // lowered to call_ref, using reference types we generate a table.set to 
set
     // the funcref to a special table used solely for this purpose, followed by
@@ -1554,11 +1546,7 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo 
&CLI,
     SDValue TableSetOps[] = {Chain, Sym, TableSlot, Callee};
     SDValue TableSet = DAG.getMemIntrinsicNode(
         WebAssemblyISD::TABLE_SET, DL, DAG.getVTList(MVT::Other), TableSetOps,
-        MVT::funcref,
-        // Machine Mem Operand args
-        MachinePointerInfo(
-            WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF),
-        CLI.CB->getCalledOperand()->getPointerAlignment(DAG.getDataLayout()),
+        MVT::funcref, MachinePointerInfo(), Align(1),
         MachineMemOperand::MOStore);
 
     Ops[0] = TableSet; // The new chain is the TableSet itself
@@ -2277,6 +2265,17 @@ SDValue 
WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op,
     return DAG.getNode(WebAssemblyISD::SHUFFLE, DL, Op.getValueType(), Ops);
   }
 
+  case Intrinsic::wasm_funcref_to_ptr: {
+    // llvm.wasm.funcref.to_ptr only has a defined lowering when its result
+    // feeds directly into an indirect call. Reaching here means the pointer
+    // escapes a direct call. We haven't implemented conversion of a funcref
+    // into a real function pointer so we crash if we get here.
+    fail(DL, DAG,
+         "a funcref can only be converted to a pointer to be directly called; "
+         "the resulting pointer cannot otherwise be used");
+    return DAG.getUNDEF(Op.getValueType());
+  }
+
   case Intrinsic::thread_pointer: {
     return SDValue(WebAssembly::getTLSBase(DAG, DL, Subtarget), 0);
   }
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h 
b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
index 42f047840e504..04e6d6f2d9367 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
@@ -26,9 +26,6 @@ class WebAssemblyTargetLowering final : public TargetLowering 
{
   WebAssemblyTargetLowering(const TargetMachine &TM,
                             const WebAssemblySubtarget &STI);
 
-  MVT getPointerTy(const DataLayout &DL, uint32_t AS = 0) const override;
-  MVT getPointerMemTy(const DataLayout &DL, uint32_t AS = 0) const override;
-
 private:
   /// Keep a pointer to the WebAssemblySubtarget around so that we can make the
   /// right decision when generating code for different targets.
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp 
b/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp
deleted file mode 100644
index be500de67e320..0000000000000
--- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-//=== WebAssemblyLowerRefTypesIntPtrConv.cpp -
-//                     Lower IntToPtr and PtrToInt on Reference Types   
---===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Lowers IntToPtr and PtrToInt instructions on reference types to
-/// Trap instructions since they have been allowed to operate
-/// on non-integral pointers.
-///
-//===----------------------------------------------------------------------===//
-
-#include "Utils/WebAssemblyTypeUtilities.h"
-#include "WebAssembly.h"
-#include "WebAssemblySubtarget.h"
-#include "llvm/IR/InstIterator.h"
-#include "llvm/Pass.h"
-#include <set>
-
-using namespace llvm;
-
-#define DEBUG_TYPE "wasm-lower-reftypes-intptr-conv"
-
-namespace {
-class WebAssemblyLowerRefTypesIntPtrConv final : public FunctionPass {
-  StringRef getPassName() const override {
-    return "WebAssembly Lower RefTypes Int-Ptr Conversions";
-  }
-
-  bool runOnFunction(Function &MF) override;
-
-public:
-  static char ID; // Pass identification
-  WebAssemblyLowerRefTypesIntPtrConv() : FunctionPass(ID) {}
-};
-} // end anonymous namespace
-
-char WebAssemblyLowerRefTypesIntPtrConv::ID = 0;
-INITIALIZE_PASS(WebAssemblyLowerRefTypesIntPtrConv, DEBUG_TYPE,
-                "WebAssembly Lower RefTypes Int-Ptr Conversions", false, false)
-
-FunctionPass *llvm::createWebAssemblyLowerRefTypesIntPtrConv() {
-  return new WebAssemblyLowerRefTypesIntPtrConv();
-}
-
-bool WebAssemblyLowerRefTypesIntPtrConv::runOnFunction(Function &F) {
-  LLVM_DEBUG(dbgs() << "********** Lower RefTypes IntPtr Convs **********\n"
-                       "********** Function: "
-                    << F.getName() << '\n');
-
-  // This function will check for uses of ptrtoint and inttoptr on reference
-  // types and replace them with a trap instruction.
-  //
-  // We replace the instruction by a trap instruction
-  // and its uses by null in the case of inttoptr and 0 in the
-  // case of ptrtoint.
-  std::set<Instruction *> worklist;
-
-  for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) {
-    PtrToIntInst *PTI = dyn_cast<PtrToIntInst>(&*I);
-    IntToPtrInst *ITP = dyn_cast<IntToPtrInst>(&*I);
-    if (!(PTI && WebAssembly::isWebAssemblyReferenceType(
-                     PTI->getPointerOperand()->getType())) &&
-        !(ITP && WebAssembly::isWebAssemblyReferenceType(ITP->getDestTy())))
-      continue;
-
-    I->replaceAllUsesWith(PoisonValue::get(I->getType()));
-
-    Function *TrapIntrin =
-        Intrinsic::getOrInsertDeclaration(F.getParent(), Intrinsic::debugtrap);
-    CallInst::Create(TrapIntrin, {}, "", I->getIterator());
-
-    worklist.insert(&*I);
-  }
-
-  // erase each instruction replaced by trap
-  for (Instruction *I : worklist)
-    I->eraseFromParent();
-
-  return !worklist.empty();
-}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRefTypeMem2Local.cpp 
b/llvm/lib/Target/WebAssembly/WebAssemblyRefTypeMem2Local.cpp
index 04b4c7d78aabb..d2ff9b264d576 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyRefTypeMem2Local.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRefTypeMem2Local.cpp
@@ -64,6 +64,8 @@ void WebAssemblyRefTypeMem2Local::visitAllocaInst(AllocaInst 
&AI) {
     auto *NewAI = IRB.CreateAlloca(AI.getAllocatedType(),
                                    WebAssembly::WASM_ADDRESS_SPACE_VAR, 
nullptr,
                                    AI.getName() + ".var");
+    // Preserve the original alloca's alignment.
+    NewAI->setAlignment(AI.getAlign());
 
     // The below is basically equivalent to AI.replaceAllUsesWith(NewAI), but 
we
     // cannot use it because it requires the old and new types be the same,
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp 
b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 886ea0a8ab574..63bda89309dfc 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -119,7 +119,6 @@ LLVMInitializeWebAssemblyTarget() {
   initializeWebAssemblyDebugFixupPass(PR);
   initializeWebAssemblyPeepholePass(PR);
   initializeWebAssemblyMCLowerPrePassPass(PR);
-  initializeWebAssemblyLowerRefTypesIntPtrConvPass(PR);
   initializeWebAssemblyFixBrTableDefaultsPass(PR);
   initializeWebAssemblyDAGToDAGISelLegacyPass(PR);
 }
@@ -670,7 +669,6 @@ void WebAssemblyPassConfig::addPreEmitPass() {
 
 bool WebAssemblyPassConfig::addPreISel() {
   TargetPassConfig::addPreISel();
-  addPass(createWebAssemblyLowerRefTypesIntPtrConv());
   return false;
 }
 
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll 
b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll
index 152a607b14d5a..97413111536be 100644
--- a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/args.ll
@@ -144,27 +144,9 @@ define void @test_f128_arg(fp128 %arg) {
   ret void
 }
 
-%externref = type ptr addrspace(10)
-define void @test_externref_arg(%externref %arg) {
-  ; CHECK-LABEL: name: test_externref_arg
-  ; CHECK: bb.1 (%ir-block.0):
-  ; CHECK-NEXT:   liveins: $arguments
-  ; CHECK-NEXT: {{  $}}
-  ; CHECK-NEXT:   [[ARGUMENT_externref:%[0-9]+]]:externref(p10) = 
ARGUMENT_externref 0, implicit $arguments
-  ; CHECK-NEXT:   RETURN implicit-def $arguments
-  ret void
-}
-
-%funcref = type ptr addrspace(20)
-define void @test_funcref_arg(%funcref %arg) {
-  ; CHECK-LABEL: name: test_funcref_arg
-  ; CHECK: bb.1 (%ir-block.0):
-  ; CHECK-NEXT:   liveins: $arguments
-  ; CHECK-NEXT: {{  $}}
-  ; CHECK-NEXT:   [[ARGUMENT_funcref:%[0-9]+]]:funcref(p20) = ARGUMENT_funcref 
0, implicit $arguments
-  ; CHECK-NEXT:   RETURN implicit-def $arguments
-  ret void
-}
+; NOTE: Reference types (externref/funcref) are intentionally not tested here.
+; GlobalISel currently falls back to SelectionDAG for them; see
+; reference-types-fallback.ll.
 
 define void @test_multiple_args(ptr %arg1, float %arg2, i1 %arg3) {
   ; WASM32-LABEL: name: test_multiple_args
diff --git 
a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/call-basics.ll 
b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/call-basics.ll
index d767ed5104dae..5a1a4fc8f3c68 100644
--- a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/call-basics.ll
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/call-basics.ll
@@ -5,8 +5,6 @@
 ; RUN: llc -mtriple=wasm64 -mattr=+simd128,+multivalue 
-target-abi=experimental-mv -global-isel -stop-after=irtranslator < %s | 
FileCheck %s -check-prefixes=CHECK,WASM64,MULTIVAL-SIMD,WASM64-MULTIVAL-SIMD
 
 
-%externref = type ptr addrspace(10)
-%funcref = type ptr addrspace(20)
 
 declare void @ret_void_args_none()
 define void @call_ret_void_args_none() {
@@ -87,29 +85,9 @@ define ptr @call_ret_ptr_args_none() {
   ret ptr %ret
 }
 
-declare %externref @ret_externref_args_none()
-define %externref @call_ret_externref_args_none() {
-  ; CHECK-LABEL: name: call_ret_externref_args_none
-  ; CHECK: bb.1 (%ir-block.0):
-  ; CHECK-NEXT:   liveins: $arguments
-  ; CHECK-NEXT: {{  $}}
-  ; CHECK-NEXT:   [[CALL:%[0-9]+]]:externref(p10) = CALL 
@ret_externref_args_none, implicit-def $arguments, implicit $sp32, implicit 
$sp64
-  ; CHECK-NEXT:   RETURN [[CALL]](p10), implicit-def $arguments
-  %ret = call %externref @ret_externref_args_none()
-  ret %externref %ret
-}
-
-declare %funcref @ret_funcref_args_none()
-define %funcref @call_ret_funcref_args_none() {
-  ; CHECK-LABEL: name: call_ret_funcref_args_none
-  ; CHECK: bb.1 (%ir-block.0):
-  ; CHECK-NEXT:   liveins: $arguments
-  ; CHECK-NEXT: {{  $}}
-  ; CHECK-NEXT:   [[CALL:%[0-9]+]]:funcref(p20) = CALL @ret_funcref_args_none, 
implicit-def $arguments, implicit $sp32, implicit $sp64
-  ; CHECK-NEXT:   RETURN [[CALL]](p20), implicit-def $arguments
-  %ret = call %funcref @ret_funcref_args_none()
-  ret %funcref %ret
-}
+; NOTE: Reference-type (externref/funcref) returns are intentionally not tested
+; here. GlobalISel currently falls back to SelectionDAG for them; see
+; reference-types-fallback.ll.
 
 declare i128 @ret_i128_args_none()
 define i128 @call_ret_i128_args_none() {
@@ -234,31 +212,9 @@ define void @call_ret_void_args_ptr(ptr %a) {
   ret void
 }
 
-declare void @ret_void_args_externref(%externref)
-define void @call_ret_void_args_externref(%externref %a) {
-  ; CHECK-LABEL: name: call_ret_void_args_externref
-  ; CHECK: bb.1 (%ir-block.0):
-  ; CHECK-NEXT:   liveins: $arguments
-  ; CHECK-NEXT: {{  $}}
-  ; CHECK-NEXT:   [[ARGUMENT_externref:%[0-9]+]]:externref(p10) = 
ARGUMENT_externref 0, implicit $arguments
-  ; CHECK-NEXT:   CALL @ret_void_args_externref, [[ARGUMENT_externref]](p10), 
implicit-def $arguments, implicit $sp32, implicit $sp64
-  ; CHECK-NEXT:   RETURN implicit-def $arguments
-  call void @ret_void_args_externref(%externref %a)
-  ret void
-}
-
-declare void @ret_void_args_funcref(%funcref)
-define void @call_ret_void_args_funcref(%funcref %a) {
-  ; CHECK-LABEL: name: call_ret_void_args_funcref
-  ; CHECK: bb.1 (%ir-block.0):
-  ; CHECK-NEXT:   liveins: $arguments
-  ; CHECK-NEXT: {{  $}}
-  ; CHECK-NEXT:   [[ARGUMENT_funcref:%[0-9]+]]:funcref(p20) = ARGUMENT_funcref 
0, implicit $arguments
-  ; CHECK-NEXT:   CALL @ret_void_args_funcref, [[ARGUMENT_funcref]](p20), 
implicit-def $arguments, implicit $sp32, implicit $sp64
-  ; CHECK-NEXT:   RETURN implicit-def $arguments
-  call void @ret_void_args_funcref(%funcref %a)
-  ret void
-}
+; NOTE: Reference-type (externref/funcref) arguments are intentionally not
+; tested here. GlobalISel currently falls back to SelectionDAG for them; see
+; reference-types-fallback.ll.
 
 declare void @ret_void_args_i128(i128)
 define void @call_ret_void_args_i128(i128 %a) {
diff --git 
a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/ret-basics.ll 
b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/ret-basics.ll
index 21e066e78c515..3bc6aaaa3d48c 100644
--- a/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/ret-basics.ll
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/irtranslator/ret-basics.ll
@@ -124,30 +124,6 @@ define double @test_ret_f64() {
   ret double 0.0
 }
 
-%externref = type ptr addrspace(10)
-define %externref @test_ret_externref() {
-  ; CHECK-LABEL: name: test_ret_externref
-  ; CHECK: bb.1 (%ir-block.0):
-  ; CHECK-NEXT:   liveins: $arguments
-  ; CHECK-NEXT: {{  $}}
-  ; CHECK-NEXT:   [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX 
%stack.0.ref_ptr
-  ; CHECK-NEXT:   [[LOAD:%[0-9]+]]:_(p10) = G_LOAD [[FRAME_INDEX]](p0) :: 
(load (p10) from %ir.ref_ptr)
-  ; CHECK-NEXT:   RETURN [[LOAD]](p10), implicit-def $arguments
-  %ref_ptr = alloca %externref
-  %ref = load %externref, ptr %ref_ptr
-  ret %externref %ref
-}
-
-%funcref = type ptr addrspace(20)
-define %funcref @test_ret_funcref() {
-  ; CHECK-LABEL: name: test_ret_funcref
-  ; CHECK: bb.1 (%ir-block.0):
-  ; CHECK-NEXT:   liveins: $arguments
-  ; CHECK-NEXT: {{  $}}
-  ; CHECK-NEXT:   [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX 
%stack.0.ref_ptr
-  ; CHECK-NEXT:   [[LOAD:%[0-9]+]]:_(p20) = G_LOAD [[FRAME_INDEX]](p0) :: 
(load (p20) from %ir.ref_ptr)
-  ; CHECK-NEXT:   RETURN [[LOAD]](p20), implicit-def $arguments
-  %ref_ptr = alloca %funcref
-  %ref = load %funcref, ptr %ref_ptr
-  ret %funcref %ref
-}
+; NOTE: Reference types (externref/funcref) are intentionally not tested here.
+; GlobalISel currently falls back to SelectionDAG for them; see
+; reference-types-fallback.ll.
diff --git 
a/llvm/test/CodeGen/WebAssembly/GlobalISel/reference-types-fallback.ll 
b/llvm/test/CodeGen/WebAssembly/GlobalISel/reference-types-fallback.ll
new file mode 100644
index 0000000000000..bce9127a8969e
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/reference-types-fallback.ll
@@ -0,0 +1,40 @@
+; RUN: llc -mtriple=wasm32 -mattr=+reference-types -global-isel 
-global-isel-abort=2 -pass-remarks-missed='gisel*' %s -o - 2>&1 | FileCheck %s
+; RUN: llc -mtriple=wasm64 -mattr=+reference-types -global-isel 
-global-isel-abort=2 -pass-remarks-missed='gisel*' %s -o - 2>&1 | FileCheck %s
+
+; GlobalISel does not yet correctly model WebAssembly reference types
+; (externref/funcref): the generic getLLTForType derives an integer scalar LLT
+; from their pointer layout type, which would round-trip references through
+; invalid integer loads/bitcasts. Until that is fixed, the WebAssembly
+; CallLowering bails out for any function passing or returning a reference 
type,
+; so instruction selection falls back to SelectionDAG. This test verifies that
+; fallback (rather than emission of incorrect code).
+
+define target("wasm.externref") @ret_externref(target("wasm.externref") %a) {
+; CHECK: remark: {{.*}} unable to lower arguments{{.*}}wasm.externref
+; CHECK-LABEL: warning: Instruction selection used fallback path for 
ret_externref
+  ret target("wasm.externref") %a
+}
+
+define target("wasm.funcref") @ret_funcref(target("wasm.funcref") %a) {
+; CHECK: remark: {{.*}} unable to lower arguments{{.*}}wasm.funcref
+; CHECK-LABEL: warning: Instruction selection used fallback path for 
ret_funcref
+  ret target("wasm.funcref") %a
+}
+
+declare void @take_externref(target("wasm.externref"))
+
+define void @call_take_externref(target("wasm.externref") %a) {
+; CHECK: remark: {{.*}} unable to lower arguments{{.*}}wasm.externref
+; CHECK-LABEL: warning: Instruction selection used fallback path for 
call_take_externref
+  call void @take_externref(target("wasm.externref") %a)
+  ret void
+}
+
+declare target("wasm.externref") @produce_externref()
+
+define void @call_produce_externref() {
+; CHECK: remark: {{.*}} unable to {{.*}}
+; CHECK-LABEL: warning: Instruction selection used fallback path for 
call_produce_externref
+  %ref = call target("wasm.externref") @produce_externref()
+  ret void
+}
diff --git a/llvm/test/CodeGen/WebAssembly/externref-globalget.ll 
b/llvm/test/CodeGen/WebAssembly/externref-globalget.ll
index 79d7932486e22..8d377cbfdad30 100644
--- a/llvm/test/CodeGen/WebAssembly/externref-globalget.ll
+++ b/llvm/test/CodeGen/WebAssembly/externref-globalget.ll
@@ -3,7 +3,7 @@
 ; not error out.
 ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types -print-after=finalize-isel | FileCheck %s
 
-%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral
+%externref = type target("wasm.externref")
 
 @externref_global = local_unnamed_addr addrspace(1) global %externref undef
 
diff --git a/llvm/test/CodeGen/WebAssembly/externref-globalset.ll 
b/llvm/test/CodeGen/WebAssembly/externref-globalset.ll
index 5bfd673e89fa1..57cfffe82dee0 100644
--- a/llvm/test/CodeGen/WebAssembly/externref-globalset.ll
+++ b/llvm/test/CodeGen/WebAssembly/externref-globalset.ll
@@ -1,6 +1,6 @@
 ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types < %s | FileCheck %s
 
-%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral
+%externref = type target("wasm.externref")
 
 @externref_global = local_unnamed_addr addrspace(1) global %externref undef
 
diff --git a/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll 
b/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll
index 64f955b6ed0f0..bdf04f73c54a7 100644
--- a/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll
+++ b/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll
@@ -1,16 +1,10 @@
-; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types 2>&1 | FileCheck %s
+; RUN: not llc --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR
 
-%externref = type ptr addrspace(10)
+%externref = type target("wasm.externref")
 
 define %externref @int_to_externref(i32 %i) {
   %ref = inttoptr i32 %i to %externref
   ret %externref %ref
 }
 
-
-; CHECK-LABEL: int_to_externref:
-; CHECK-NEXT: .functype       int_to_externref (i32) -> (externref)
-; CHECK-NEXT: .local externref
-; CHECK-NEXT: unreachable
-; CHECK-NEXT: local.get 1
-; CHECK-NEXT: end_function
+# CHECK-ERROR: error: invalid cast opcode for cast from 'i32' to 
'target("wasm.externref")'
diff --git a/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll 
b/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll
index 22558796f0624..f3cfe31445b60 100644
--- a/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll
+++ b/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll
@@ -1,15 +1,10 @@
-; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types 2>&1 | FileCheck %s
+; RUN: not llc --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR
 
-%externref = type ptr addrspace(10)
+%externref = type target("wasm.externref")
 
 define i32 @externref_to_int(%externref %ref) {
   %i = ptrtoint %externref %ref to i32
   ret i32 %i
 }
 
-; CHECK-LABEL: externref_to_int:
-; CHECK-NEXT: .functype       externref_to_int (externref) -> (i32)
-; CHECK-NEXT: .local i32
-; CHECK-NEXT: unreachable
-; CHECK-NEXT: local.get 1
-; CHECK-NEXT: end_function
+# CHECK-ERROR: error: invalid cast opcode for cast from 
'target("wasm.externref")' to 'i32'
diff --git a/llvm/test/CodeGen/WebAssembly/externref-tableget.ll 
b/llvm/test/CodeGen/WebAssembly/externref-tableget.ll
index d9ae7c8f6c9b1..1189fc3571902 100644
--- a/llvm/test/CodeGen/WebAssembly/externref-tableget.ll
+++ b/llvm/test/CodeGen/WebAssembly/externref-tableget.ll
@@ -1,6 +1,6 @@
 ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types | FileCheck %s
 
-%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral
+%externref = type target("wasm.externref")
 
 @externref_table = local_unnamed_addr addrspace(1) global [0 x %externref] 
undef
 
diff --git a/llvm/test/CodeGen/WebAssembly/externref-tableset.ll 
b/llvm/test/CodeGen/WebAssembly/externref-tableset.ll
index 37c663869428e..f84ae4dba68eb 100644
--- a/llvm/test/CodeGen/WebAssembly/externref-tableset.ll
+++ b/llvm/test/CodeGen/WebAssembly/externref-tableset.ll
@@ -1,6 +1,6 @@
 ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types < %s | FileCheck %s
 
-%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral
+%externref = type target("wasm.externref")
 
 @externref_table = local_unnamed_addr addrspace(1) global [0 x %externref] 
undef
 
diff --git a/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll 
b/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll
index 1f8f4d5140c51..98a0e801af387 100644
--- a/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll
+++ b/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll
@@ -1,6 +1,6 @@
 ; RUN: not llc --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR
 
-%externref = type ptr addrspace(10)
+%externref = type target("wasm.externref")
 
 define void @load_extern(%externref %ref) {
   %e = load %extern, %externref %ref
diff --git a/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll 
b/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll
index c7e062d1b0526..f3d3ee3685230 100644
--- a/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll
+++ b/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll
@@ -1,6 +1,6 @@
 ; RUN: not llc --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR
 
-%externref = type ptr addrspace(10)
+%externref = type target("wasm.externref")
 
 define void @store_extern(%externref %ref) {
   store %extern undef, %externref %ref
diff --git a/llvm/test/CodeGen/WebAssembly/funcref-call.ll 
b/llvm/test/CodeGen/WebAssembly/funcref-call.ll
index 9904df2280e81..66692ac0eb0f0 100644
--- a/llvm/test/CodeGen/WebAssembly/funcref-call.ll
+++ b/llvm/test/CodeGen/WebAssembly/funcref-call.ll
@@ -2,7 +2,9 @@
 ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -fast-isel=0 
-mattr=+reference-types | FileCheck %s
 ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -fast-isel=1 
-mattr=+reference-types | FileCheck %s
 
-%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral
+%funcref = type target("wasm.funcref")
+
+declare ptr @llvm.wasm.funcref.to_ptr(%funcref) nounwind
 
 ; CHECK: .tabletype __funcref_call_table, funcref, 1
 
@@ -19,7 +21,8 @@ define void @call_funcref(%funcref %ref) {
 ; CHECK-NEXT:    ref.null_func
 ; CHECK-NEXT:    table.set __funcref_call_table
 ; CHECK-NEXT:    # fallthrough-return
-  call addrspace(20) void %ref()
+  %refptr = call ptr @llvm.wasm.funcref.to_ptr(%funcref %ref)
+  call void %refptr()
   ret void
 }
 
@@ -41,6 +44,7 @@ define float @call_funcref_with_args(%funcref %ref) {
 ; CHECK-NEXT:    table.set __funcref_call_table
 ; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    # fallthrough-return
-  %ret = call addrspace(20) float %ref(double 1.0, i32 2)
+  %refptr = call ptr @llvm.wasm.funcref.to_ptr(%funcref %ref)
+  %ret = call float %refptr(double 1.0, i32 2)
   ret float %ret
 }
diff --git a/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll 
b/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll
index 9aa7fdabfdea9..2634e5a6fc57e 100644
--- a/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll
+++ b/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll
@@ -1,6 +1,6 @@
 ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types | FileCheck %s
 
-%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral
+%funcref = type target("wasm.funcref")
 
 @funcref_global = local_unnamed_addr addrspace(1) global %funcref undef
 
diff --git a/llvm/test/CodeGen/WebAssembly/funcref-globalset.ll 
b/llvm/test/CodeGen/WebAssembly/funcref-globalset.ll
index ca2feb6617996..791e8648537c0 100644
--- a/llvm/test/CodeGen/WebAssembly/funcref-globalset.ll
+++ b/llvm/test/CodeGen/WebAssembly/funcref-globalset.ll
@@ -1,6 +1,6 @@
 ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types | FileCheck %s
 
-%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral
+%funcref = type target("wasm.funcref")
 
 @funcref_global = local_unnamed_addr addrspace(1) global %funcref undef
 
diff --git a/llvm/test/CodeGen/WebAssembly/funcref-ptr-conversion.ll 
b/llvm/test/CodeGen/WebAssembly/funcref-ptr-conversion.ll
new file mode 100644
index 0000000000000..b9792b661a742
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/funcref-ptr-conversion.ll
@@ -0,0 +1,28 @@
+; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types | FileCheck %s
+; RUN: llc < %s --mtriple=wasm64-unknown-unknown -asm-verbose=false 
-mattr=+reference-types | FileCheck %s --check-prefix=CHECK64
+
+%funcref = type target("wasm.funcref")
+
+declare %funcref @llvm.wasm.ptr.to_funcref(ptr) nounwind
+
+; CHECK: .tabletype __indirect_function_table, funcref
+
+; Converting a function pointer to a funcref is a table.get from the
+; __indirect_function_table.
+define %funcref @ptr_to_funcref(ptr %p) {
+; CHECK-LABEL: ptr_to_funcref:
+; CHECK:         .functype ptr_to_funcref (i32) -> (funcref)
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    table.get __indirect_function_table
+; CHECK-NEXT:    end_function
+;
+; On wasm64 the function pointer is an i64 and must be wrapped to i32 first.
+; CHECK64-LABEL: ptr_to_funcref:
+; CHECK64:         .functype ptr_to_funcref (i64) -> (funcref)
+; CHECK64-NEXT:    local.get 0
+; CHECK64-NEXT:    i32.wrap_i64
+; CHECK64-NEXT:    table.get __indirect_function_table
+; CHECK64-NEXT:    end_function
+  %ref = call %funcref @llvm.wasm.ptr.to_funcref(ptr %p)
+  ret %funcref %ref
+}
diff --git a/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll 
b/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll
index 74bbc802ac077..e5343fa80d846 100644
--- a/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll
+++ b/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll
@@ -1,12 +1,13 @@
 ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types | FileCheck %s
 
-%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral
+%funcref = type target("wasm.funcref")
 
 @funcref_table = local_unnamed_addr addrspace(1) global [0 x %funcref] undef
 
 ;  CHECK: .tabletype  __funcref_call_table, funcref, 1
 
 declare %funcref @llvm.wasm.table.get.funcref(ptr addrspace(1), i32) nounwind
+declare ptr @llvm.wasm.funcref.to_ptr(%funcref) nounwind
 
 define void @call_funcref_from_table(i32 %i) {
 ; CHECK-LABEL: call_funcref_from_table:
@@ -22,7 +23,8 @@ define void @call_funcref_from_table(i32 %i) {
 ; CHECK-NEXT: table.set       __funcref_call_table
 ; CHECK-NEXT: end_function
   %ref = call %funcref @llvm.wasm.table.get.funcref(ptr addrspace(1) 
@funcref_table, i32 %i)
-  call addrspace(20) void %ref()
+  %refptr = call ptr @llvm.wasm.funcref.to_ptr(%funcref %ref)
+  call void %refptr()
   ret void
 }
 
diff --git a/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll 
b/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll
index 3df308c5ddf80..2621dcde6a79b 100644
--- a/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll
+++ b/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll
@@ -1,6 +1,6 @@
 ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types | FileCheck %s
 
-%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral
+%funcref = type target("wasm.funcref")
 
 @funcref_table = local_unnamed_addr addrspace(1) global [0 x %funcref] undef
 
diff --git a/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll 
b/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll
index 98e1b55613d7d..e62eb8a6f2d20 100644
--- a/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll
+++ b/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll
@@ -1,6 +1,6 @@
 ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types < %s | FileCheck %s
 
-%funcref = type ptr addrspace(20) ;; addrspace 20 is nonintegral
+%funcref = type target("wasm.funcref")
 
 @funcref_table = local_unnamed_addr addrspace(1) global [0 x %funcref] undef
 
diff --git a/llvm/test/CodeGen/WebAssembly/funcref-to-ptr-error.ll 
b/llvm/test/CodeGen/WebAssembly/funcref-to-ptr-error.ll
new file mode 100644
index 0000000000000..b756513ecbafa
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/funcref-to-ptr-error.ll
@@ -0,0 +1,30 @@
+; RUN: not llc < %s --mtriple=wasm32-unknown-unknown -mattr=+reference-types 
2>&1 | FileCheck %s
+
+; We have only implemented a lowering for llvm.wasm.funcref.to_ptr its result
+; feeds directly into an indirect call. Check that we diagnose the case where 
we
+; spill the result rather than crashing in the backend.
+
+%funcref = type target("wasm.funcref")
+
+declare ptr @llvm.wasm.funcref.to_ptr(%funcref)
+declare void @sink(ptr)
+
+; CHECK: error: {{.*}}in function escape_via_store {{.*}}: a funcref can only 
be converted to a pointer to be directly called; the resulting pointer cannot 
otherwise be used
+define void @escape_via_store(%funcref %ref, ptr %dst) {
+  %p = call ptr @llvm.wasm.funcref.to_ptr(%funcref %ref)
+  store ptr %p, ptr %dst
+  ret void
+}
+
+; CHECK: error: {{.*}}in function escape_via_return {{.*}}: a funcref can only 
be converted to a pointer to be directly called; the resulting pointer cannot 
otherwise be used
+define ptr @escape_via_return(%funcref %ref) {
+  %p = call ptr @llvm.wasm.funcref.to_ptr(%funcref %ref)
+  ret ptr %p
+}
+
+; CHECK: error: {{.*}}in function escape_via_arg {{.*}}: a funcref can only be 
converted to a pointer to be directly called; the resulting pointer cannot 
otherwise be used
+define void @escape_via_arg(%funcref %ref) {
+  %p = call ptr @llvm.wasm.funcref.to_ptr(%funcref %ref)
+  call void @sink(ptr %p)
+  ret void
+}
diff --git a/llvm/test/CodeGen/WebAssembly/ref-null.ll 
b/llvm/test/CodeGen/WebAssembly/ref-null.ll
index af6ddfd8e0814..f2f2a0dad378b 100644
--- a/llvm/test/CodeGen/WebAssembly/ref-null.ll
+++ b/llvm/test/CodeGen/WebAssembly/ref-null.ll
@@ -1,8 +1,8 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
 ; RUN: llc --mtriple=wasm32-unknown-unknown -mattr=+reference-types < %s | 
FileCheck %s
 
-%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral
-%funcref = type ptr addrspace(20)   ;; addrspace 20 is nonintegral
+%externref = type target("wasm.externref")
+%funcref = type target("wasm.funcref")
 
 declare %externref @llvm.wasm.ref.null.extern() nounwind
 declare %funcref @llvm.wasm.ref.null.func() nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll 
b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll
index 4fda253d39fe3..745f55a45f8d7 100644
--- a/llvm/test/CodeGen/WebAssembly/ref-test-func.ll
+++ b/llvm/test/CodeGen/WebAssembly/ref-test-func.ll
@@ -3,16 +3,24 @@
 ; RUN: llc < %s --mtriple=wasm64-unknown-unknown -mcpu=mvp 
-mattr=+reference-types -mattr=+gc -verify-machineinstrs | FileCheck 
--check-prefixes CHECK,CHK64 %s
 
 define void @test_fpsig_void_void(ptr noundef %func) local_unnamed_addr #0 {
-; CHECK-LABEL: test_fpsig_void_void:
+; CHK32-LABEL: test_fpsig_void_void:
 ; CHK32:         .functype test_fpsig_void_void (i32) -> ()
+; CHK32-NEXT:  # %bb.0: # %entry
+; CHK32-NEXT:    local.get 0
+; CHK32-NEXT:    table.get __indirect_function_table
+; CHK32-NEXT:    ref.test () -> ()
+; CHK32-NEXT:    call use
+; CHK32-NEXT:    # fallthrough-return
+;
+; CHK64-LABEL: test_fpsig_void_void:
 ; CHK64:         .functype test_fpsig_void_void (i64) -> ()
-; CHECK-NEXT:  # %bb.0: # %entry
-; CHECK-NEXT:    local.get 0
+; CHK64-NEXT:  # %bb.0: # %entry
+; CHK64-NEXT:    local.get 0
 ; CHK64-NEXT:    i32.wrap_i64
-; CHECK-NEXT:    table.get __indirect_function_table
-; CHECK-NEXT:    ref.test () -> ()
-; CHECK-NEXT:    call use
-; CHECK-NEXT:    # fallthrough-return
+; CHK64-NEXT:    table.get __indirect_function_table
+; CHK64-NEXT:    ref.test () -> ()
+; CHK64-NEXT:    call use
+; CHK64-NEXT:    # fallthrough-return
 entry:
   %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func)
   tail call void @use(i32 noundef %res) #3
@@ -20,16 +28,24 @@ entry:
 }
 
 define void @test_fpsig_return_i32(ptr noundef %func) local_unnamed_addr #0 {
-; CHECK-LABEL: test_fpsig_return_i32:
+; CHK32-LABEL: test_fpsig_return_i32:
 ; CHK32:         .functype test_fpsig_return_i32 (i32) -> ()
+; CHK32-NEXT:  # %bb.0: # %entry
+; CHK32-NEXT:    local.get 0
+; CHK32-NEXT:    table.get __indirect_function_table
+; CHK32-NEXT:    ref.test () -> (i32)
+; CHK32-NEXT:    call use
+; CHK32-NEXT:    # fallthrough-return
+;
+; CHK64-LABEL: test_fpsig_return_i32:
 ; CHK64:         .functype test_fpsig_return_i32 (i64) -> ()
-; CHECK-NEXT:  # %bb.0: # %entry
-; CHECK-NEXT:    local.get 0
+; CHK64-NEXT:  # %bb.0: # %entry
+; CHK64-NEXT:    local.get 0
 ; CHK64-NEXT:    i32.wrap_i64
-; CHECK-NEXT:    table.get __indirect_function_table
-; CHECK-NEXT:    ref.test () -> (i32)
-; CHECK-NEXT:    call use
-; CHECK-NEXT:    # fallthrough-return
+; CHK64-NEXT:    table.get __indirect_function_table
+; CHK64-NEXT:    ref.test () -> (i32)
+; CHK64-NEXT:    call use
+; CHK64-NEXT:    # fallthrough-return
 entry:
   %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 
poison)
   tail call void @use(i32 noundef %res) #3
@@ -37,16 +53,24 @@ entry:
 }
 
 define void @test_fpsig_return_i64(ptr noundef %func) local_unnamed_addr #0 {
-; CHECK-LABEL: test_fpsig_return_i64:
+; CHK32-LABEL: test_fpsig_return_i64:
 ; CHK32:         .functype test_fpsig_return_i64 (i32) -> ()
+; CHK32-NEXT:  # %bb.0: # %entry
+; CHK32-NEXT:    local.get 0
+; CHK32-NEXT:    table.get __indirect_function_table
+; CHK32-NEXT:    ref.test () -> (i64)
+; CHK32-NEXT:    call use
+; CHK32-NEXT:    # fallthrough-return
+;
+; CHK64-LABEL: test_fpsig_return_i64:
 ; CHK64:         .functype test_fpsig_return_i64 (i64) -> ()
-; CHECK-NEXT:  # %bb.0: # %entry
-; CHECK-NEXT:    local.get 0
+; CHK64-NEXT:  # %bb.0: # %entry
+; CHK64-NEXT:    local.get 0
 ; CHK64-NEXT:    i32.wrap_i64
-; CHECK-NEXT:    table.get __indirect_function_table
-; CHECK-NEXT:    ref.test () -> (i64)
-; CHECK-NEXT:    call use
-; CHECK-NEXT:    # fallthrough-return
+; CHK64-NEXT:    table.get __indirect_function_table
+; CHK64-NEXT:    ref.test () -> (i64)
+; CHK64-NEXT:    call use
+; CHK64-NEXT:    # fallthrough-return
 entry:
   %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i64 
poison)
   tail call void @use(i32 noundef %res) #3
@@ -54,16 +78,24 @@ entry:
 }
 
 define void @test_fpsig_return_f32(ptr noundef %func) local_unnamed_addr #0 {
-; CHECK-LABEL: test_fpsig_return_f32:
+; CHK32-LABEL: test_fpsig_return_f32:
 ; CHK32:         .functype test_fpsig_return_f32 (i32) -> ()
+; CHK32-NEXT:  # %bb.0: # %entry
+; CHK32-NEXT:    local.get 0
+; CHK32-NEXT:    table.get __indirect_function_table
+; CHK32-NEXT:    ref.test () -> (f32)
+; CHK32-NEXT:    call use
+; CHK32-NEXT:    # fallthrough-return
+;
+; CHK64-LABEL: test_fpsig_return_f32:
 ; CHK64:         .functype test_fpsig_return_f32 (i64) -> ()
-; CHECK-NEXT:  # %bb.0: # %entry
-; CHECK-NEXT:    local.get 0
+; CHK64-NEXT:  # %bb.0: # %entry
+; CHK64-NEXT:    local.get 0
 ; CHK64-NEXT:    i32.wrap_i64
-; CHECK-NEXT:    table.get __indirect_function_table
-; CHECK-NEXT:    ref.test () -> (f32)
-; CHECK-NEXT:    call use
-; CHECK-NEXT:    # fallthrough-return
+; CHK64-NEXT:    table.get __indirect_function_table
+; CHK64-NEXT:    ref.test () -> (f32)
+; CHK64-NEXT:    call use
+; CHK64-NEXT:    # fallthrough-return
 entry:
   %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float 
poison)
   tail call void @use(i32 noundef %res) #3
@@ -71,16 +103,24 @@ entry:
 }
 
 define void @test_fpsig_return_f64(ptr noundef %func) local_unnamed_addr #0 {
-; CHECK-LABEL: test_fpsig_return_f64:
+; CHK32-LABEL: test_fpsig_return_f64:
 ; CHK32:         .functype test_fpsig_return_f64 (i32) -> ()
+; CHK32-NEXT:  # %bb.0: # %entry
+; CHK32-NEXT:    local.get 0
+; CHK32-NEXT:    table.get __indirect_function_table
+; CHK32-NEXT:    ref.test () -> (f64)
+; CHK32-NEXT:    call use
+; CHK32-NEXT:    # fallthrough-return
+;
+; CHK64-LABEL: test_fpsig_return_f64:
 ; CHK64:         .functype test_fpsig_return_f64 (i64) -> ()
-; CHECK-NEXT:  # %bb.0: # %entry
-; CHECK-NEXT:    local.get 0
+; CHK64-NEXT:  # %bb.0: # %entry
+; CHK64-NEXT:    local.get 0
 ; CHK64-NEXT:    i32.wrap_i64
-; CHECK-NEXT:    table.get __indirect_function_table
-; CHECK-NEXT:    ref.test () -> (f64)
-; CHECK-NEXT:    call use
-; CHECK-NEXT:    # fallthrough-return
+; CHK64-NEXT:    table.get __indirect_function_table
+; CHK64-NEXT:    ref.test () -> (f64)
+; CHK64-NEXT:    call use
+; CHK64-NEXT:    # fallthrough-return
 entry:
   %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, double 
poison)
   tail call void @use(i32 noundef %res) #3
@@ -89,16 +129,24 @@ entry:
 
 
 define void @test_fpsig_param_i32(ptr noundef %func) local_unnamed_addr #0 {
-; CHECK-LABEL: test_fpsig_param_i32:
+; CHK32-LABEL: test_fpsig_param_i32:
 ; CHK32:         .functype test_fpsig_param_i32 (i32) -> ()
+; CHK32-NEXT:  # %bb.0: # %entry
+; CHK32-NEXT:    local.get 0
+; CHK32-NEXT:    table.get __indirect_function_table
+; CHK32-NEXT:    ref.test (f64) -> ()
+; CHK32-NEXT:    call use
+; CHK32-NEXT:    # fallthrough-return
+;
+; CHK64-LABEL: test_fpsig_param_i32:
 ; CHK64:         .functype test_fpsig_param_i32 (i64) -> ()
-; CHECK-NEXT:  # %bb.0: # %entry
-; CHECK-NEXT:    local.get 0
+; CHK64-NEXT:  # %bb.0: # %entry
+; CHK64-NEXT:    local.get 0
 ; CHK64-NEXT:    i32.wrap_i64
-; CHECK-NEXT:    table.get __indirect_function_table
-; CHECK-NEXT:    ref.test (f64) -> ()
-; CHECK-NEXT:    call use
-; CHECK-NEXT:    # fallthrough-return
+; CHK64-NEXT:    table.get __indirect_function_table
+; CHK64-NEXT:    ref.test (f64) -> ()
+; CHK64-NEXT:    call use
+; CHK64-NEXT:    # fallthrough-return
 entry:
   %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token 
poison, double poison)
   tail call void @use(i32 noundef %res) #3
@@ -107,16 +155,24 @@ entry:
 
 
 define void @test_fpsig_multiple_params_and_returns(ptr noundef %func) 
local_unnamed_addr #0 {
-; CHECK-LABEL: test_fpsig_multiple_params_and_returns:
+; CHK32-LABEL: test_fpsig_multiple_params_and_returns:
 ; CHK32:         .functype test_fpsig_multiple_params_and_returns (i32) -> ()
+; CHK32-NEXT:  # %bb.0: # %entry
+; CHK32-NEXT:    local.get 0
+; CHK32-NEXT:    table.get __indirect_function_table
+; CHK32-NEXT:    ref.test (i64, f32, i64) -> (i32, i64, f32, f64)
+; CHK32-NEXT:    call use
+; CHK32-NEXT:    # fallthrough-return
+;
+; CHK64-LABEL: test_fpsig_multiple_params_and_returns:
 ; CHK64:         .functype test_fpsig_multiple_params_and_returns (i64) -> ()
-; CHECK-NEXT:  # %bb.0: # %entry
-; CHECK-NEXT:    local.get 0
+; CHK64-NEXT:  # %bb.0: # %entry
+; CHK64-NEXT:    local.get 0
 ; CHK64-NEXT:    i32.wrap_i64
-; CHECK-NEXT:    table.get __indirect_function_table
-; CHECK-NEXT:    ref.test (i64, f32, i64) -> (i32, i64, f32, f64)
-; CHECK-NEXT:    call use
-; CHECK-NEXT:    # fallthrough-return
+; CHK64-NEXT:    table.get __indirect_function_table
+; CHK64-NEXT:    ref.test (i64, f32, i64) -> (i32, i64, f32, f64)
+; CHK64-NEXT:    call use
+; CHK64-NEXT:    # fallthrough-return
 entry:
   %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, i32 
poison, i64 poison, float poison, double poison, token poison, i64 poison, 
float poison, i64 poison)
   tail call void @use(i32 noundef %res) #3
@@ -125,17 +181,24 @@ entry:
 
 
 define void @test_fpsig_ptrs(ptr noundef %func) local_unnamed_addr #0 {
-; CHECK-LABEL: test_fpsig_ptrs:
+; CHK32-LABEL: test_fpsig_ptrs:
 ; CHK32:         .functype test_fpsig_ptrs (i32) -> ()
+; CHK32-NEXT:  # %bb.0: # %entry
+; CHK32-NEXT:    local.get 0
+; CHK32-NEXT:    table.get __indirect_function_table
+; CHK32-NEXT:    ref.test (i32, i32) -> (i32)
+; CHK32-NEXT:    call use
+; CHK32-NEXT:    # fallthrough-return
+;
+; CHK64-LABEL: test_fpsig_ptrs:
 ; CHK64:         .functype test_fpsig_ptrs (i64) -> ()
-; CHECK-NEXT:  # %bb.0: # %entry
-; CHECK-NEXT:    local.get 0
+; CHK64-NEXT:  # %bb.0: # %entry
+; CHK64-NEXT:    local.get 0
 ; CHK64-NEXT:    i32.wrap_i64
-; CHECK-NEXT:    table.get __indirect_function_table
-; CHK32-NEXT:    ref.test (i32, i32) -> (i32)
+; CHK64-NEXT:    table.get __indirect_function_table
 ; CHK64-NEXT:    ref.test (i64, i64) -> (i64)
-; CHECK-NEXT:    call use
-; CHECK-NEXT:    # fallthrough-return
+; CHK64-NEXT:    call use
+; CHK64-NEXT:    # fallthrough-return
 entry:
   %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, ptr 
poison, token poison, ptr poison, ptr poison)
   tail call void @use(i32 noundef %res) #3
@@ -143,20 +206,30 @@ entry:
 }
 
 define void @test_reference_types(ptr noundef %func) local_unnamed_addr #0 {
-; CHECK-LABEL: test_reference_types:
+; CHK32-LABEL: test_reference_types:
 ; CHK32:         .functype test_reference_types (i32) -> ()
+; CHK32-NEXT:  # %bb.0: # %entry
+; CHK32-NEXT:    local.get 0
+; CHK32-NEXT:    table.get __indirect_function_table
+; CHK32-NEXT:    ref.test (funcref, externref) -> (externref)
+; CHK32-NEXT:    call use
+; CHK32-NEXT:    # fallthrough-return
+;
+; CHK64-LABEL: test_reference_types:
 ; CHK64:         .functype test_reference_types (i64) -> ()
-; CHECK-NEXT:  # %bb.0: # %entry
-; CHECK-NEXT:    local.get 0
+; CHK64-NEXT:  # %bb.0: # %entry
+; CHK64-NEXT:    local.get 0
 ; CHK64-NEXT:    i32.wrap_i64
-; CHECK-NEXT:    table.get __indirect_function_table
-; CHECK-NEXT:    ref.test (funcref, externref) -> (externref)
-; CHECK-NEXT:    call use
-; CHECK-NEXT:    # fallthrough-return
+; CHK64-NEXT:    table.get __indirect_function_table
+; CHK64-NEXT:    ref.test (funcref, externref) -> (externref)
+; CHK64-NEXT:    call use
+; CHK64-NEXT:    # fallthrough-return
 entry:
-  %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, ptr 
addrspace(10) poison, token poison, ptr addrspace(20) poison, ptr addrspace(10) 
poison)
+  %res = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, 
target("wasm.externref") poison, token poison, target("wasm.funcref") poison, 
target("wasm.externref") poison)
   tail call void @use(i32 noundef %res) #3
   ret void
 }
 
 declare void @use(i32 noundef) local_unnamed_addr #1
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add 
tests below this line:
+; CHECK: {{.*}}
diff --git a/llvm/test/CodeGen/WebAssembly/ref-type-mem2local.ll 
b/llvm/test/CodeGen/WebAssembly/ref-type-mem2local.ll
index 911e5bb516a2f..75b3002b3be76 100644
--- a/llvm/test/CodeGen/WebAssembly/ref-type-mem2local.ll
+++ b/llvm/test/CodeGen/WebAssembly/ref-type-mem2local.ll
@@ -3,8 +3,8 @@
 
 target triple = "wasm32-unknown-unknown"
 
-%externref = type ptr addrspace(10)
-%funcref = type ptr addrspace(20)
+%externref = type target("wasm.externref")
+%funcref = type target("wasm.funcref")
 
 declare %externref @get_externref()
 declare %funcref @get_funcref()
@@ -22,22 +22,22 @@ entry:
   store %externref %eref, ptr %alloc.externref, align 1
   %eref.loaded = load %externref, ptr %alloc.externref, align 1
   call void @take_externref(%externref %eref.loaded)
-  ; CHECK:      %alloc.externref.var = alloca ptr addrspace(10), align 1, 
addrspace(1)
-  ; CHECK-NEXT: %eref = call ptr addrspace(10) @get_externref()
-  ; CHECK-NEXT: store ptr addrspace(10) %eref, ptr addrspace(1) 
%alloc.externref.var, align 1
-  ; CHECK-NEXT: %eref.loaded = load ptr addrspace(10), ptr addrspace(1) 
%alloc.externref.var, align 1
-  ; CHECK-NEXT: call void @take_externref(ptr addrspace(10) %eref.loaded)
+  ; CHECK:      %alloc.externref.var = alloca target("wasm.externref"), align 
1, addrspace(1)
+  ; CHECK-NEXT: %eref = call target("wasm.externref") @get_externref()
+  ; CHECK-NEXT: store target("wasm.externref") %eref, ptr addrspace(1) 
%alloc.externref.var, align 1
+  ; CHECK-NEXT: %eref.loaded = load target("wasm.externref"), ptr addrspace(1) 
%alloc.externref.var, align 1
+  ; CHECK-NEXT: call void @take_externref(target("wasm.externref") 
%eref.loaded)
 
   %alloc.funcref = alloca %funcref, align 1
   %fref = call %funcref @get_funcref()
   store %funcref %fref, ptr %alloc.funcref, align 1
   %fref.loaded = load %funcref, ptr %alloc.funcref, align 1
   call void @take_funcref(%funcref %fref.loaded)
-  ; CHECK-NEXT: %alloc.funcref.var = alloca ptr addrspace(20), align 1, 
addrspace(1)
-  ; CHECK-NEXT: %fref = call ptr addrspace(20) @get_funcref()
-  ; CHECK-NEXT: store ptr addrspace(20) %fref, ptr addrspace(1) 
%alloc.funcref.var, align 1
-  ; CHECK-NEXT: %fref.loaded = load ptr addrspace(20), ptr addrspace(1) 
%alloc.funcref.var, align 1
-  ; CHECK-NEXT: call void @take_funcref(ptr addrspace(20) %fref.loaded)
+  ; CHECK-NEXT: %alloc.funcref.var = alloca target("wasm.funcref"), align 1, 
addrspace(1)
+  ; CHECK-NEXT: %fref = call target("wasm.funcref") @get_funcref()
+  ; CHECK-NEXT: store target("wasm.funcref") %fref, ptr addrspace(1) 
%alloc.funcref.var, align 1
+  ; CHECK-NEXT: %fref.loaded = load target("wasm.funcref"), ptr addrspace(1) 
%alloc.funcref.var, align 1
+  ; CHECK-NEXT: call void @take_funcref(target("wasm.funcref") %fref.loaded)
 
   ret void
 }
@@ -68,22 +68,22 @@ entry:
   store %externref %eref, ptr %alloc.externref, align 1
   %eref.loaded = load %externref, ptr %alloc.externref, align 1
   call void @take_externref(%externref %eref.loaded)
-  ; ATTR:      %alloc.externref.var = alloca ptr addrspace(10), align 1, 
addrspace(1)
-  ; ATTR-NEXT: %eref = call ptr addrspace(10) @get_externref()
-  ; ATTR-NEXT: store ptr addrspace(10) %eref, ptr addrspace(1) 
%alloc.externref.var, align 1
-  ; ATTR-NEXT: %eref.loaded = load ptr addrspace(10), ptr addrspace(1) 
%alloc.externref.var, align 1
-  ; ATTR-NEXT: call void @take_externref(ptr addrspace(10) %eref.loaded)
+  ; ATTR:      %alloc.externref.var = alloca target("wasm.externref"), align 
1, addrspace(1)
+  ; ATTR-NEXT: %eref = call target("wasm.externref") @get_externref()
+  ; ATTR-NEXT: store target("wasm.externref") %eref, ptr addrspace(1) 
%alloc.externref.var, align 1
+  ; ATTR-NEXT: %eref.loaded = load target("wasm.externref"), ptr addrspace(1) 
%alloc.externref.var, align 1
+  ; ATTR-NEXT: call void @take_externref(target("wasm.externref") %eref.loaded)
 
   %alloc.funcref = alloca %funcref, align 1
   %fref = call %funcref @get_funcref()
   store %funcref %fref, ptr %alloc.funcref, align 1
   %fref.loaded = load %funcref, ptr %alloc.funcref, align 1
   call void @take_funcref(%funcref %fref.loaded)
-  ; ATTR-NEXT: %alloc.funcref.var = alloca ptr addrspace(20), align 1, 
addrspace(1)
-  ; ATTR-NEXT: %fref = call ptr addrspace(20) @get_funcref()
-  ; ATTR-NEXT: store ptr addrspace(20) %fref, ptr addrspace(1) 
%alloc.funcref.var, align 1
-  ; ATTR-NEXT: %fref.loaded = load ptr addrspace(20), ptr addrspace(1) 
%alloc.funcref.var, align 1
-  ; ATTR-NEXT: call void @take_funcref(ptr addrspace(20) %fref.loaded)
+  ; ATTR-NEXT: %alloc.funcref.var = alloca target("wasm.funcref"), align 1, 
addrspace(1)
+  ; ATTR-NEXT: %fref = call target("wasm.funcref") @get_funcref()
+  ; ATTR-NEXT: store target("wasm.funcref") %fref, ptr addrspace(1) 
%alloc.funcref.var, align 1
+  ; ATTR-NEXT: %fref.loaded = load target("wasm.funcref"), ptr addrspace(1) 
%alloc.funcref.var, align 1
+  ; ATTR-NEXT: call void @take_funcref(target("wasm.funcref") %fref.loaded)
 
   ret void
 }
diff --git a/llvm/test/CodeGen/WebAssembly/select-reftype.ll 
b/llvm/test/CodeGen/WebAssembly/select-reftype.ll
index baca3ca6258a3..985796048fb47 100644
--- a/llvm/test/CodeGen/WebAssembly/select-reftype.ll
+++ b/llvm/test/CodeGen/WebAssembly/select-reftype.ll
@@ -5,46 +5,46 @@
 
 target triple = "wasm32-unknown-unknown"
 
-define ptr addrspace(10) @select_externref_eq(i32 %a, ptr addrspace(10) %b, 
ptr addrspace(10) %c) {
+define target("wasm.externref") @select_externref_eq(i32 %a, 
target("wasm.externref") %b, target("wasm.externref") %c) {
 ; CHECK-LABEL: select_externref_eq:
 ; CHECK:         .functype select_externref_eq (i32, externref, externref) -> 
(externref)
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    externref.select $push0=, $2, $1, $0
 ; CHECK-NEXT:    return $pop0
   %cmp = icmp eq i32 %a, 0
-  %cond = select i1 %cmp, ptr addrspace(10) %b, ptr addrspace(10) %c
-  ret ptr addrspace(10) %cond
+  %cond = select i1 %cmp, target("wasm.externref") %b, 
target("wasm.externref") %c
+  ret target("wasm.externref") %cond
 }
 
-define ptr addrspace(10) @select_externref_ne(i32 %a, ptr addrspace(10) %b, 
ptr addrspace(10) %c) {
+define target("wasm.externref") @select_externref_ne(i32 %a, 
target("wasm.externref") %b, target("wasm.externref") %c) {
 ; CHECK-LABEL: select_externref_ne:
 ; CHECK:         .functype select_externref_ne (i32, externref, externref) -> 
(externref)
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    externref.select $push0=, $1, $2, $0
 ; CHECK-NEXT:    return $pop0
   %cmp = icmp ne i32 %a, 0
-  %cond = select i1 %cmp, ptr addrspace(10) %b, ptr addrspace(10) %c
-  ret ptr addrspace(10) %cond
+  %cond = select i1 %cmp, target("wasm.externref") %b, 
target("wasm.externref") %c
+  ret target("wasm.externref") %cond
 }
 
-define ptr addrspace(20) @select_funcref_eq(i32 %a, ptr addrspace(20) %b, ptr 
addrspace(20) %c) {
+define target("wasm.funcref") @select_funcref_eq(i32 %a, 
target("wasm.funcref") %b, target("wasm.funcref") %c) {
 ; CHECK-LABEL: select_funcref_eq:
 ; CHECK:         .functype select_funcref_eq (i32, funcref, funcref) -> 
(funcref)
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    funcref.select $push0=, $2, $1, $0
 ; CHECK-NEXT:    return $pop0
   %cmp = icmp eq i32 %a, 0
-  %cond = select i1 %cmp, ptr addrspace(20) %b, ptr addrspace(20) %c
-  ret ptr addrspace(20) %cond
+  %cond = select i1 %cmp, target("wasm.funcref") %b, target("wasm.funcref") %c
+  ret target("wasm.funcref") %cond
 }
 
-define ptr addrspace(20) @select_funcref_ne(i32 %a, ptr addrspace(20) %b, ptr 
addrspace(20) %c) {
+define target("wasm.funcref") @select_funcref_ne(i32 %a, 
target("wasm.funcref") %b, target("wasm.funcref") %c) {
 ; CHECK-LABEL: select_funcref_ne:
 ; CHECK:         .functype select_funcref_ne (i32, funcref, funcref) -> 
(funcref)
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    funcref.select $push0=, $1, $2, $0
 ; CHECK-NEXT:    return $pop0
   %cmp = icmp ne i32 %a, 0
-  %cond = select i1 %cmp, ptr addrspace(20) %b, ptr addrspace(20) %c
-  ret ptr addrspace(20) %cond
+  %cond = select i1 %cmp, target("wasm.funcref") %b, target("wasm.funcref") %c
+  ret target("wasm.funcref") %cond
 }
diff --git a/llvm/test/CodeGen/WebAssembly/table-copy.ll 
b/llvm/test/CodeGen/WebAssembly/table-copy.ll
index 5c0647ada4ab0..d1387428c4368 100644
--- a/llvm/test/CodeGen/WebAssembly/table-copy.ll
+++ b/llvm/test/CodeGen/WebAssembly/table-copy.ll
@@ -1,6 +1,6 @@
 ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types < %s | FileCheck %s
 
-%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral
+%externref = type target("wasm.externref")
 
 @externref_table1 = local_unnamed_addr addrspace(1) global [0 x %externref] 
undef
 @externref_table2 = local_unnamed_addr addrspace(1) global [0 x %externref] 
undef
diff --git a/llvm/test/CodeGen/WebAssembly/table-fill.ll 
b/llvm/test/CodeGen/WebAssembly/table-fill.ll
index 0b78124f038b1..7ed39361a1afd 100644
--- a/llvm/test/CodeGen/WebAssembly/table-fill.ll
+++ b/llvm/test/CodeGen/WebAssembly/table-fill.ll
@@ -1,6 +1,6 @@
 ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types < %s | FileCheck %s
 
-%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral
+%externref = type target("wasm.externref")
 
 @externref_table = local_unnamed_addr addrspace(1) global [0 x %externref] 
undef
 
diff --git a/llvm/test/CodeGen/WebAssembly/table-grow.ll 
b/llvm/test/CodeGen/WebAssembly/table-grow.ll
index 614c3400a782b..2b5801ea2fa17 100644
--- a/llvm/test/CodeGen/WebAssembly/table-grow.ll
+++ b/llvm/test/CodeGen/WebAssembly/table-grow.ll
@@ -1,6 +1,6 @@
 ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types < %s | FileCheck %s
 
-%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral
+%externref = type target("wasm.externref")
 
 @externref_table = local_unnamed_addr addrspace(1) global [0 x %externref] 
undef
 
diff --git a/llvm/test/CodeGen/WebAssembly/table-size.ll 
b/llvm/test/CodeGen/WebAssembly/table-size.ll
index 42cd2e8a909d7..2b8d0185bb049 100644
--- a/llvm/test/CodeGen/WebAssembly/table-size.ll
+++ b/llvm/test/CodeGen/WebAssembly/table-size.ll
@@ -1,6 +1,6 @@
 ; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types < %s | FileCheck %s
 
-%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral
+%externref = type target("wasm.externref")
 
 @externref_table = local_unnamed_addr addrspace(1) global [0 x %externref] 
undef
 
diff --git a/llvm/test/CodeGen/WebAssembly/table-types.ll 
b/llvm/test/CodeGen/WebAssembly/table-types.ll
index cb5e54e2af230..da04ba35dd58e 100644
--- a/llvm/test/CodeGen/WebAssembly/table-types.ll
+++ b/llvm/test/CodeGen/WebAssembly/table-types.ll
@@ -1,7 +1,7 @@
 ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false 
-mattr=+reference-types | FileCheck %s
 
-%externref = type ptr addrspace(10) ;; addrspace 10 is nonintegral
-%funcref = type ptr addrspace(20)   ;; addrspace 20 is nonintegral
+%externref = type target("wasm.externref")
+%funcref = type target("wasm.funcref")
 
 ; CHECK: .tabletype eref_table, externref
 ; CHECK-NEXT: .globl eref_table
diff --git a/llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn 
b/llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn
index f8bd3fa48a92d..67f9a904bb1e7 100644
--- a/llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn
+++ b/llvm/utils/gn/secondary/llvm/lib/Target/WebAssembly/BUILD.gn
@@ -106,7 +106,6 @@ static_library("LLVMWebAssemblyCodeGen") {
     "WebAssemblyLateEHPrepare.cpp",
     "WebAssemblyLowerBrUnless.cpp",
     "WebAssemblyLowerEmscriptenEHSjLj.cpp",
-    "WebAssemblyLowerRefTypesIntPtrConv.cpp",
     "WebAssemblyMCInstLower.cpp",
     "WebAssemblyMCLowerPrePass.cpp",
     "WebAssemblyMachineFunctionInfo.cpp",

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to