diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h --- a/clang/lib/Basic/Targets/WebAssembly.h +++ b/clang/lib/Basic/Targets/WebAssembly.h @@ -151,9 +151,11 @@ const TargetOptions &Opts) : WebAssemblyTargetInfo(T, Opts) { if (T.isOSEmscripten()) - resetDataLayout("e-m:e-p:32:32-i64:64-f128:64-n32:64-S128-ni:1:10:20"); + resetDataLayout("e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-f128:64-n32:64-" + "S128-ni:1:10:20"); else - resetDataLayout("e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"); + resetDataLayout( + "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"); } protected: @@ -173,9 +175,11 @@ PtrDiffType = SignedLong; IntPtrType = SignedLong; if (T.isOSEmscripten()) - resetDataLayout("e-m:e-p:64:64-i64:64-f128:64-n32:64-S128-ni:1:10:20"); + resetDataLayout("e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-f128:64-n32:64-" + "S128-ni:1:10:20"); else - resetDataLayout("e-m:e-p:64:64-i64:64-n32:64-S128-ni:1:10:20"); + resetDataLayout( + "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"); } protected: diff --git a/clang/test/CodeGen/target-data.c b/clang/test/CodeGen/target-data.c --- a/clang/test/CodeGen/target-data.c +++ b/clang/test/CodeGen/target-data.c @@ -108,11 +108,11 @@ // RUN: %clang_cc1 -triple wasm32-unknown-unknown -o - -emit-llvm %s | \ // RUN: FileCheck %s -check-prefix=WEBASSEMBLY32 -// WEBASSEMBLY32: target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20" +// WEBASSEMBLY32: target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" // RUN: %clang_cc1 -triple wasm64-unknown-unknown -o - -emit-llvm %s | \ // RUN: FileCheck %s -check-prefix=WEBASSEMBLY64 -// WEBASSEMBLY64: target datalayout = "e-m:e-p:64:64-i64:64-n32:64-S128-ni:1:10:20" +// WEBASSEMBLY64: target datalayout = "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" // RUN: %clang_cc1 -triple lanai-unknown-unknown -o - -emit-llvm %s | \ // RUN: FileCheck %s -check-prefix=LANAI diff --git a/lld/test/wasm/lto/Inputs/archive.ll b/lld/test/wasm/lto/Inputs/archive.ll --- a/lld/test/wasm/lto/Inputs/archive.ll +++ b/lld/test/wasm/lto/Inputs/archive.ll @@ -1,4 +1,4 @@ -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define void @f() { diff --git a/lld/test/wasm/lto/Inputs/cache.ll b/lld/test/wasm/lto/Inputs/cache.ll --- a/lld/test/wasm/lto/Inputs/cache.ll +++ b/lld/test/wasm/lto/Inputs/cache.ll @@ -1,4 +1,4 @@ -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define i32 @_start() { diff --git a/lld/test/wasm/lto/Inputs/libcall-archive.ll b/lld/test/wasm/lto/Inputs/libcall-archive.ll --- a/lld/test/wasm/lto/Inputs/libcall-archive.ll +++ b/lld/test/wasm/lto/Inputs/libcall-archive.ll @@ -1,4 +1,4 @@ -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define void @memcpy() { diff --git a/lld/test/wasm/lto/Inputs/libcall-truncsfhf2.ll b/lld/test/wasm/lto/Inputs/libcall-truncsfhf2.ll --- a/lld/test/wasm/lto/Inputs/libcall-truncsfhf2.ll +++ b/lld/test/wasm/lto/Inputs/libcall-truncsfhf2.ll @@ -1,4 +1,4 @@ -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define half @__truncsfhf2(float) { diff --git a/lld/test/wasm/lto/Inputs/save-temps.ll b/lld/test/wasm/lto/Inputs/save-temps.ll --- a/lld/test/wasm/lto/Inputs/save-temps.ll +++ b/lld/test/wasm/lto/Inputs/save-temps.ll @@ -1,4 +1,4 @@ -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define void @bar() { diff --git a/lld/test/wasm/lto/Inputs/thinlto.ll b/lld/test/wasm/lto/Inputs/thinlto.ll --- a/lld/test/wasm/lto/Inputs/thinlto.ll +++ b/lld/test/wasm/lto/Inputs/thinlto.ll @@ -1,4 +1,4 @@ -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define void @g() { diff --git a/lld/test/wasm/lto/Inputs/used.ll b/lld/test/wasm/lto/Inputs/used.ll --- a/lld/test/wasm/lto/Inputs/used.ll +++ b/lld/test/wasm/lto/Inputs/used.ll @@ -1,4 +1,4 @@ -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" @foo = hidden global i32 1 diff --git a/lld/test/wasm/lto/archive.ll b/lld/test/wasm/lto/archive.ll --- a/lld/test/wasm/lto/archive.ll +++ b/lld/test/wasm/lto/archive.ll @@ -5,7 +5,7 @@ ; RUN: wasm-ld --export-dynamic %t2.o %t.a -o %t3 ; RUN: obj2yaml %t3 | FileCheck %s -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define void @_start() { diff --git a/lld/test/wasm/lto/atomics.ll b/lld/test/wasm/lto/atomics.ll --- a/lld/test/wasm/lto/atomics.ll +++ b/lld/test/wasm/lto/atomics.ll @@ -4,7 +4,7 @@ ; Atomic operations will not fail to compile if atomics are not ; enabled because LLVM atomics will be lowered to regular ops. -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown-wasm" @foo = hidden global i32 1 diff --git a/lld/test/wasm/lto/cache.ll b/lld/test/wasm/lto/cache.ll --- a/lld/test/wasm/lto/cache.ll +++ b/lld/test/wasm/lto/cache.ll @@ -36,7 +36,7 @@ ; RUN: wasm-ld --thinlto-cache-dir=%t.cache --thinlto-cache-policy prune_after=0s:cache_size=0%:cache_size_files=1:prune_interval=0s -o %t.wasm %t2.o %t.o ; RUN: ls %t.cache | count 3 -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown-wasm" define void @globalfunc() #0 { diff --git a/lld/test/wasm/lto/comdat.ll b/lld/test/wasm/lto/comdat.ll --- a/lld/test/wasm/lto/comdat.ll +++ b/lld/test/wasm/lto/comdat.ll @@ -6,7 +6,7 @@ ; RUN: wasm-ld %t.bc %t.o -o %t.wasm ; RUN: wasm-ld %t.o %t.bc -o %t.wasm -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" $foo = comdat any diff --git a/lld/test/wasm/lto/diagnostics.ll b/lld/test/wasm/lto/diagnostics.ll --- a/lld/test/wasm/lto/diagnostics.ll +++ b/lld/test/wasm/lto/diagnostics.ll @@ -4,7 +4,7 @@ ; RUN: llvm-as %s -o %t.o ; RUN: not wasm-ld --lto-O0 %t.o -o %t2 2>&1 | FileCheck %s -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define void @_start() { diff --git a/lld/test/wasm/lto/export.ll b/lld/test/wasm/lto/export.ll --- a/lld/test/wasm/lto/export.ll +++ b/lld/test/wasm/lto/export.ll @@ -3,7 +3,7 @@ ; RUN: wasm-ld --export=hidden_function -o %t.wasm %t.bc ; RUN: obj2yaml %t.wasm | FileCheck %s -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define hidden i32 @hidden_function() local_unnamed_addr { diff --git a/lld/test/wasm/lto/import-attributes.ll b/lld/test/wasm/lto/import-attributes.ll --- a/lld/test/wasm/lto/import-attributes.ll +++ b/lld/test/wasm/lto/import-attributes.ll @@ -3,7 +3,7 @@ ; RUN: obj2yaml %t.wasm | FileCheck %s target triple = "wasm32-unknown-unknown-wasm" -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" define void @_start() { call void @foo(); diff --git a/lld/test/wasm/lto/internalize-basic.ll b/lld/test/wasm/lto/internalize-basic.ll --- a/lld/test/wasm/lto/internalize-basic.ll +++ b/lld/test/wasm/lto/internalize-basic.ll @@ -2,7 +2,7 @@ ; RUN: wasm-ld %t.o -o %t2 -save-temps ; RUN: llvm-dis < %t2.0.2.internalize.bc | FileCheck %s -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown-wasm" define void @_start() { diff --git a/lld/test/wasm/lto/libcall-archive.ll b/lld/test/wasm/lto/libcall-archive.ll --- a/lld/test/wasm/lto/libcall-archive.ll +++ b/lld/test/wasm/lto/libcall-archive.ll @@ -5,7 +5,7 @@ ; RUN: wasm-ld -o %t %t.o %t.a ; RUN: obj2yaml %t | FileCheck %s -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define void @_start(i8* %a, i8* %b) { diff --git a/lld/test/wasm/lto/libcall-truncsfhf2.ll b/lld/test/wasm/lto/libcall-truncsfhf2.ll --- a/lld/test/wasm/lto/libcall-truncsfhf2.ll +++ b/lld/test/wasm/lto/libcall-truncsfhf2.ll @@ -4,7 +4,7 @@ ; RUN: llvm-ar rcs %t.a %t.truncsfhf2.o ; RUN: not wasm-ld --export-all %t.o %t.a -o %t.wasm 2>&1 | FileCheck %s -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" @g_float = global float 0.0 diff --git a/lld/test/wasm/lto/lto-start.ll b/lld/test/wasm/lto/lto-start.ll --- a/lld/test/wasm/lto/lto-start.ll +++ b/lld/test/wasm/lto/lto-start.ll @@ -8,7 +8,7 @@ ; CHECK-NEXT: - Index: 0 ; CHECK-NEXT: Name: _start -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown-wasm" define void @_start() { diff --git a/lld/test/wasm/lto/new-pass-manager.ll b/lld/test/wasm/lto/new-pass-manager.ll --- a/lld/test/wasm/lto/new-pass-manager.ll +++ b/lld/test/wasm/lto/new-pass-manager.ll @@ -5,7 +5,7 @@ ; CHECK: Running pass: GlobalOptPass ; LPM-NOT: Running pass: GlobalOptPass -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define void @_start() local_unnamed_addr { diff --git a/lld/test/wasm/lto/opt-level.ll b/lld/test/wasm/lto/opt-level.ll --- a/lld/test/wasm/lto/opt-level.ll +++ b/lld/test/wasm/lto/opt-level.ll @@ -15,7 +15,7 @@ ; RUN: FileCheck --check-prefix=INVALIDNEGATIVE %s ; INVALIDNEGATIVE: invalid optimization level for LTO: 4294967295 -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown-wasm" ; CHECK-O0: Name: foo diff --git a/lld/test/wasm/lto/parallel.ll b/lld/test/wasm/lto/parallel.ll --- a/lld/test/wasm/lto/parallel.ll +++ b/lld/test/wasm/lto/parallel.ll @@ -4,7 +4,7 @@ ; RUN: llvm-nm %t.lto.o | FileCheck --check-prefix=CHECK0 %s ; RUN: llvm-nm %t1.lto.o | FileCheck --check-prefix=CHECK1 %s -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown-wasm" ; CHECK0-NOT: bar diff --git a/lld/test/wasm/lto/relocatable-undefined.ll b/lld/test/wasm/lto/relocatable-undefined.ll --- a/lld/test/wasm/lto/relocatable-undefined.ll +++ b/lld/test/wasm/lto/relocatable-undefined.ll @@ -2,7 +2,7 @@ ; RUN: wasm-ld -r -o %t.wasm %t.o ; RUN: obj2yaml %t.wasm | FileCheck %s -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" @missing_data = external global i32 diff --git a/lld/test/wasm/lto/relocatable.ll b/lld/test/wasm/lto/relocatable.ll --- a/lld/test/wasm/lto/relocatable.ll +++ b/lld/test/wasm/lto/relocatable.ll @@ -12,7 +12,7 @@ ; CHECK-NEXT: } ; CHECK-NEXT: ] -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define void @foo() { diff --git a/lld/test/wasm/lto/save-temps.ll b/lld/test/wasm/lto/save-temps.ll --- a/lld/test/wasm/lto/save-temps.ll +++ b/lld/test/wasm/lto/save-temps.ll @@ -8,7 +8,7 @@ ; RUN: llvm-nm a.out.lto.o | FileCheck %s ; RUN: llvm-dis a.out.0.0.preopt.bc -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define void @foo() { diff --git a/lld/test/wasm/lto/thinlto.ll b/lld/test/wasm/lto/thinlto.ll --- a/lld/test/wasm/lto/thinlto.ll +++ b/lld/test/wasm/lto/thinlto.ll @@ -52,7 +52,7 @@ ; NM1: T f ; NM2: T g -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" declare void @g(...) diff --git a/lld/test/wasm/lto/tls.ll b/lld/test/wasm/lto/tls.ll --- a/lld/test/wasm/lto/tls.ll +++ b/lld/test/wasm/lto/tls.ll @@ -7,7 +7,7 @@ ; RUN: wasm-ld --export=tls_int --export=get_tls %t1.o -o %t ; RUN: obj2yaml %t | FileCheck %s -target datalayout = "e-m:e-p:32:32-i64:64-f128:64-n32:64-S128-ni:1:10:20" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-f128:64-n32:64-S128-ni:1:10:20" target triple = "wasm32-unknown-emscripten" @tls_int = dso_local thread_local global i32 99 diff --git a/lld/test/wasm/lto/undef.ll b/lld/test/wasm/lto/undef.ll --- a/lld/test/wasm/lto/undef.ll +++ b/lld/test/wasm/lto/undef.ll @@ -2,7 +2,7 @@ ; RUN: wasm-ld %t.o -o %t.wasm --allow-undefined ; RUN: obj2yaml %t.wasm | FileCheck %s -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" declare i32 @bar() diff --git a/lld/test/wasm/lto/used.ll b/lld/test/wasm/lto/used.ll --- a/lld/test/wasm/lto/used.ll +++ b/lld/test/wasm/lto/used.ll @@ -5,7 +5,7 @@ ; Verify that symbols references from regular objects are preserved by LTO -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" declare void @bar() diff --git a/lld/test/wasm/lto/verify-invalid.ll b/lld/test/wasm/lto/verify-invalid.ll --- a/lld/test/wasm/lto/verify-invalid.ll +++ b/lld/test/wasm/lto/verify-invalid.ll @@ -8,7 +8,7 @@ ; RUN: wasm-ld %t.o -o %t2 --no-lto-legacy-pass-manager --lto-debug-pass-manager \ ; RUN: -disable-verify 2>&1 | FileCheck -check-prefix=DISABLE-NPM %s -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" define void @_start() { diff --git a/lld/test/wasm/lto/weak-undefined.ll b/lld/test/wasm/lto/weak-undefined.ll --- a/lld/test/wasm/lto/weak-undefined.ll +++ b/lld/test/wasm/lto/weak-undefined.ll @@ -6,7 +6,7 @@ ; We had a bug where stub function generation was failing because functions ; that are in bitcode (pre-LTO) don't have signatures assigned. -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" declare extern_weak i32 @foo() diff --git a/lld/test/wasm/lto/weak.ll b/lld/test/wasm/lto/weak.ll --- a/lld/test/wasm/lto/weak.ll +++ b/lld/test/wasm/lto/weak.ll @@ -2,7 +2,7 @@ ; RUN: wasm-ld %t.o %t.o -o %t.wasm -r ; RUN: llvm-readobj --symbols %t.wasm | FileCheck %s -target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown-wasm" define weak void @f() { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISD.def b/llvm/lib/Target/WebAssembly/WebAssemblyISD.def --- a/llvm/lib/Target/WebAssembly/WebAssemblyISD.def +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISD.def @@ -48,4 +48,5 @@ HANDLE_MEM_NODETYPE(LOAD_SPLAT) HANDLE_MEM_NODETYPE(GLOBAL_GET) HANDLE_MEM_NODETYPE(GLOBAL_SET) +HANDLE_MEM_NODETYPE(TABLE_GET) HANDLE_MEM_NODETYPE(TABLE_SET) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -136,6 +136,11 @@ SDValue LowerLoad(SDValue Op, SelectionDAG &DAG) const; SDValue LowerStore(SDValue Op, SelectionDAG &DAG) const; + // Helper for LoadLoad and LowerStore + bool MatchTableForLowering(SelectionDAG &DAG, const SDLoc &DL, + const SDValue &Base, GlobalAddressSDNode **GA, + SDValue &Idx) const; + // Custom DAG combine hooks SDValue PerformDAGCombine(SDNode *N, diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -88,7 +88,9 @@ } } if (Subtarget->hasReferenceTypes()) { - for (auto T : {MVT::externref, MVT::funcref}) { + // We need custom load and store lowering for both externref, funcref and + // Other. The MVT::Other here represents tables of reference types. + for (auto T : {MVT::externref, MVT::funcref, MVT::Other}) { setOperationAction(ISD::LOAD, T, Custom); setOperationAction(ISD::STORE, T, Custom); } @@ -1422,6 +1424,81 @@ return WebAssemblyFrameLowering::getLocalForStackObject(MF, FI->getIndex()); } +static bool IsWebAssemblyTable(SDValue Op) { + const GlobalAddressSDNode *GA = dyn_cast(Op); + if (GA && WebAssembly::isWasmVarAddressSpace(GA->getAddressSpace())) { + const GlobalValue *Value = GA->getGlobal(); + const Type *Ty = Value->getValueType(); + + if (Ty->isArrayTy() && WebAssembly::isRefType(Ty->getArrayElementType())) + return true; + } + return false; +} + +// This function will accept as Op any access to a table, so Op can +// be the actual table or an offset into the table. +static bool IsWebAssemblyTableWithOffset(SDValue Op) { + if (Op->getOpcode() == ISD::ADD && Op->getNumOperands() == 2) + return (Op->getOperand(1).getSimpleValueType() == MVT::i32 && + IsWebAssemblyTableWithOffset(Op->getOperand(0))) || + (Op->getOperand(0).getSimpleValueType() == MVT::i32 && + IsWebAssemblyTableWithOffset(Op->getOperand(1))); + + return IsWebAssemblyTable(Op); +} + +// Helper for table pattern matching used in LowerStore and LowerLoad +bool WebAssemblyTargetLowering::MatchTableForLowering(SelectionDAG &DAG, + const SDLoc &DL, + const SDValue &Base, + GlobalAddressSDNode **GA, + SDValue &Idx) const { + // We expect the following graph for a load of the form: + // table[ + ] + // + // Case 1: + // externref = load t1 + // t1: i32 = add t2, i32: + // t2: i32 = add tX, table + // + // This is in some cases simplified to just: + // Case 2: + // externref = load t1 + // t1: i32 = add t2, i32:tX + // + // So, unfortunately we need to check for both cases and if we are in the + // first case extract the table GlobalAddressNode and build a new node tY + // that's tY: i32 = add i32:, i32:tX + // + if (IsWebAssemblyTable(Base)) { + *GA = cast(Base); + Idx = DAG.getConstant(0, DL, MVT::i32); + } else { + *GA = dyn_cast(Base->getOperand(0)); + if (!*GA) { + // This might be Case 1 above (or an error) + SDValue V = Base->getOperand(0); + *GA = dyn_cast(V->getOperand(1)); + + if (V->getOpcode() != ISD::ADD || V->getNumOperands() != 2 || !*GA) + return false; + + SDValue IdxV = DAG.getNode(ISD::ADD, DL, MVT::i32, Base->getOperand(1), + V->getOperand(0)); + Idx = IdxV; + } else { + // We are in Case 2 above. + + Idx = Base->getOperand(1); + if (!Idx || (*GA)->getNumValues() != 1 || Idx->getNumValues() != 1) + return false; + } + } + + return true; +} + SDValue WebAssemblyTargetLowering::LowerStore(SDValue Op, SelectionDAG &DAG) const { SDLoc DL(Op); @@ -1430,6 +1507,26 @@ const SDValue &Base = SN->getBasePtr(); const SDValue &Offset = SN->getOffset(); + if (IsWebAssemblyTableWithOffset(Base)) { + if (!Offset->isUndef()) + report_fatal_error( + "unexpected offset when loading from webassembly table", false); + + SDValue Idx; + GlobalAddressSDNode *GA; + + if (!MatchTableForLowering(DAG, DL, Base, &GA, Idx)) + report_fatal_error("failed pattern matching for lowering table store", + false); + + SDVTList Tys = DAG.getVTList(MVT::Other); + SDValue TableSetOps[] = {SN->getChain(), SDValue(GA, 0), Idx, Value}; + SDValue TableSet = + DAG.getMemIntrinsicNode(WebAssemblyISD::TABLE_SET, DL, Tys, TableSetOps, + SN->getMemoryVT(), SN->getMemOperand()); + return TableSet; + } + if (IsWebAssemblyGlobal(Base)) { if (!Offset->isUndef()) report_fatal_error("unexpected offset when storing to webassembly global", @@ -1462,6 +1559,26 @@ const SDValue &Base = LN->getBasePtr(); const SDValue &Offset = LN->getOffset(); + if (IsWebAssemblyTableWithOffset(Base)) { + if (!Offset->isUndef()) + report_fatal_error( + "unexpected offset when loading from webassembly table", false); + + GlobalAddressSDNode *GA; + SDValue Idx; + + if (!MatchTableForLowering(DAG, DL, Base, &GA, Idx)) + report_fatal_error("failed pattern matching for lowering table load", + false); + + SDVTList Tys = DAG.getVTList(LN->getValueType(0), MVT::Other); + SDValue TableGetOps[] = {LN->getChain(), SDValue(GA, 0), Idx}; + SDValue TableGet = + DAG.getMemIntrinsicNode(WebAssemblyISD::TABLE_GET, DL, Tys, TableGetOps, + LN->getMemoryVT(), LN->getMemOperand()); + return TableGet; + } + if (IsWebAssemblyGlobal(Base)) { if (!Offset->isUndef()) report_fatal_error( diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td @@ -11,9 +11,18 @@ /// Instructions that handle tables //===----------------------------------------------------------------------===// -multiclass TABLE { +def WebAssemblyTableSet_t : SDTypeProfile<0, 3, [SDTCisPtrTy<1>]>; +def WebAssemblyTableSet : SDNode<"WebAssemblyISD::TABLE_SET", WebAssemblyTableSet_t, + [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; + +def WebAssemblyTableGet_t : SDTypeProfile<1, 2, [SDTCisPtrTy<1>]>; +def WebAssemblyTableGet : SDNode<"WebAssemblyISD::TABLE_GET", WebAssemblyTableGet_t, + [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; + + +multiclass TABLE { let mayLoad = 1 in - defm TABLE_GET_#rt : I<(outs rt:$res), (ins table32_op:$table, I32:$i), + defm TABLE_GET_#rc : I<(outs rc:$res), (ins table32_op:$table, I32:$i), (outs), (ins table32_op:$table), [], "table.get\t$res, $table, $i", @@ -21,41 +30,43 @@ 0x25>; let mayStore = 1 in - defm TABLE_SET_#rt : I<(outs), (ins table32_op:$table, I32:$i, rt:$val), + defm TABLE_SET_#rc : I<(outs), (ins table32_op:$table, I32:$i, rc:$val), (outs), (ins table32_op:$table), [], "table.set\t$table, $i, $val", "table.set\t$table", 0x26>; - defm TABLE_GROW_#rt : I<(outs I32:$sz), (ins table32_op:$table, rt:$val, I32:$n), + defm TABLE_GROW_#rc : I<(outs I32:$sz), (ins table32_op:$table, rc:$val, I32:$n), (outs), (ins table32_op:$table), [], "table.grow\t$sz, $table, $val, $n", "table.grow\t$table", 0xfc0f>; - defm TABLE_FILL_#rt : I<(outs), (ins table32_op:$table, I32:$i, rt:$val, I32:$n), + defm TABLE_FILL_#rc : I<(outs), (ins table32_op:$table, I32:$i, rc:$val, I32:$n), (outs), (ins table32_op:$table), [], "table.fill\t$table, $i, $val, $n", "table.fill\t$table", 0xfc11>; + foreach vt = rc.RegTypes in { + def : Pat<(vt (WebAssemblyTableGet (WebAssemblyWrapper tglobaladdr:$table), i32:$idx)), + (!cast("TABLE_GET_" # rc) tglobaladdr:$table, i32:$idx)>; + def : Pat<(WebAssemblyTableSet + (WebAssemblyWrapper tglobaladdr:$table), + i32:$idx, + vt:$src), + (!cast("TABLE_SET_" # rc) tglobaladdr:$table, i32:$idx, vt:$src)>; + } } defm "" : TABLE, Requires<[HasReferenceTypes]>; defm "" : TABLE, Requires<[HasReferenceTypes]>; -def wasm_table_set_t : SDTypeProfile<0, 3, []>; -def wasm_table_set : SDNode<"WebAssemblyISD::TABLE_SET", wasm_table_set_t, - [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; - -def : Pat<(wasm_table_set i32:$table, i32:$idx, funcref:$r), - (TABLE_SET_FUNCREF i32:$table, i32:$idx, funcref:$r)>, - Requires<[HasReferenceTypes]>; -def : Pat<(wasm_table_set i32:$table, i32:$idx, externref:$r), - (TABLE_SET_EXTERNREF i32:$table, i32:$idx, externref:$r)>, +def : Pat<(WebAssemblyTableSet mcsym:$table, i32:$idx, funcref:$r), + (TABLE_SET_FUNCREF mcsym:$table, i32:$idx, funcref:$r)>, Requires<[HasReferenceTypes]>; defm TABLE_SIZE : I<(outs I32:$sz), (ins table32_op:$table), diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -17,6 +17,7 @@ #include "Utils/WebAssemblyTypeUtilities.h" #include "Utils/WebAssemblyUtilities.h" #include "WebAssemblyAsmPrinter.h" +#include "WebAssemblyISelLowering.h" #include "WebAssemblyMachineFunctionInfo.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineFunction.h" @@ -28,6 +29,7 @@ #include "llvm/MC/MCSymbolWasm.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" + using namespace llvm; // This disables the removal of registers when lowering into MC, as required @@ -56,15 +58,30 @@ const MachineFunction &MF = *MO.getParent()->getParent()->getParent(); const TargetMachine &TM = MF.getTarget(); const Function &CurrentFunc = MF.getFunction(); + Type *GlobalVT = Global->getValueType(); SmallVector VTs; - computeLegalValueVTs(CurrentFunc, TM, Global->getValueType(), VTs); - if (VTs.size() != 1) + computeLegalValueVTs(CurrentFunc, TM, GlobalVT, VTs); + + // Tables are represented as Arrays in LLVM IR therefore + // they reach this point as aggregate Array types with an element type + // that is a reference type. + wasm::ValType Type; + if (GlobalVT->isArrayTy() && + WebAssembly::isRefType(GlobalVT->getArrayElementType())) { + MVT VT = + GlobalVT->getArrayElementType()->getPointerAddressSpace() == + WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF + ? MVT::funcref + : MVT::externref; + Type = WebAssembly::toValType(VT); + } else if (VTs.size() == 1) { + Type = WebAssembly::toValType(VTs[0]); + } else report_fatal_error("Aggregate globals not yet implemented"); - bool Mutable = true; - wasm::ValType Type = WebAssembly::toValType(VTs[0]); WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); - WasmSym->setGlobalType(wasm::WasmGlobalType{uint8_t(Type), Mutable}); + WasmSym->setGlobalType( + wasm::WasmGlobalType{uint8_t(Type), /*Mutable=*/true}); } return WasmSym; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp @@ -497,6 +497,10 @@ return WebAssembly::TEE_F64; if (RC == &WebAssembly::V128RegClass) return WebAssembly::TEE_V128; + if (RC == &WebAssembly::EXTERNREFRegClass) + return WebAssembly::TEE_EXTERNREF; + if (RC == &WebAssembly::FUNCREFRegClass) + return WebAssembly::TEE_FUNCREF; llvm_unreachable("Unexpected register class"); } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -133,12 +133,14 @@ : LLVMTargetMachine( T, TT.isArch64Bit() - ? (TT.isOSEmscripten() - ? "e-m:e-p:64:64-i64:64-f128:64-n32:64-S128-ni:1:10:20" - : "e-m:e-p:64:64-i64:64-n32:64-S128-ni:1:10:20") - : (TT.isOSEmscripten() - ? "e-m:e-p:32:32-i64:64-f128:64-n32:64-S128-ni:1:10:20" - : "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"), + ? (TT.isOSEmscripten() ? "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-" + "f128:64-n32:64-S128-ni:1:10:20" + : "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-" + "n32:64-S128-ni:1:10:20") + : (TT.isOSEmscripten() ? "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-" + "f128:64-n32:64-S128-ni:1:10:20" + : "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-" + "n32:64-S128-ni:1:10:20"), TT, CPU, FS, Options, getEffectiveRelocModel(RM, TT), getEffectiveCodeModel(CM, CodeModel::Large), OL), TLOF(new WebAssemblyTargetObjectFile()) { diff --git a/llvm/test/CodeGen/WebAssembly/externref-tableget.ll b/llvm/test/CodeGen/WebAssembly/externref-tableget.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/externref-tableget.ll @@ -0,0 +1,76 @@ +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s + +%extern = type opaque +%externref = type %extern addrspace(10)* ;; addrspace 10 is nonintegral + +@externref_table = local_unnamed_addr addrspace(1) global [0 x %externref] undef + +define %externref @get_externref_from_table(i32 %i) { +; CHECK-LABEL: get_externref_from_table: +; CHECK-NEXT: .functype get_externref_from_table (i32) -> (externref) +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.get externref_table +; CHECK-NEXT: end_function + %p = getelementptr [0 x %externref], [0 x %externref] addrspace (1)* @externref_table, i32 0, i32 %i + %ref = load %externref, %externref addrspace(1)* %p + ret %externref %ref +} + +define %externref @get_externref_from_table_const() { +; CHECK-LABEL: get_externref_from_table_const: +; CHECK-NEXT: .functype get_externref_from_table_const () -> (externref) +; CHECK-NEXT: i32.const 0 +; CHECK-NEXT: table.get externref_table +; CHECK-NEXT: end_function + %p = getelementptr [0 x %externref], [0 x %externref] addrspace (1)* @externref_table, i32 0, i32 0 + %ref = load %externref, %externref addrspace(1)* %p + ret %externref %ref +} + +define %externref @get_externref_from_table_with_offset(i32 %i) { +; CHECK-LABEL: get_externref_from_table_with_offset: +; CHECK-NEXT: .functype get_externref_from_table_with_offset (i32) -> (externref) +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: i32.const 2 +; CHECK-NEXT: i32.add +; CHECK-NEXT: table.get externref_table +; CHECK-NEXT: end_function + %off = add nsw i32 %i, 2 + %p = getelementptr [0 x %externref], [0 x %externref] addrspace (1)* @externref_table, i32 0, i32 %off + %ref = load %externref, %externref addrspace(1)* %p + ret %externref %ref +} + + +define %externref @get_externref_from_table_with_var_offset(i32 %i, i32 %j) { +; CHECK-LABEL: get_externref_from_table_with_var_offset: +; CHECK-NEXT: .functype get_externref_from_table_with_var_offset (i32, i32) -> (externref) +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: i32.add +; CHECK-NEXT: table.get externref_table +; CHECK-NEXT: end_function + %off = add nsw i32 %i, %j + %p = getelementptr [0 x %externref], [0 x %externref] addrspace (1)* @externref_table, i32 0, i32 %off + %ref = load %externref, %externref addrspace(1)* %p + ret %externref %ref +} + +declare i32 @get_offset() + +define %externref @get_externref_from_table_with_var_offset2(i32 %i) { +; CHECK-LABEL: get_externref_from_table_with_var_offset2: +; CHECK-NEXT: .functype get_externref_from_table_with_var_offset2 (i32) -> (externref) +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: call get_offset +; CHECK-NEXT: i32.add +; CHECK-NEXT: table.get externref_table +; CHECK-NEXT: end_function + %j = call i32 @get_offset() + %off = add nsw i32 %i, %j + %p = getelementptr [0 x %externref], [0 x %externref] addrspace (1)* @externref_table, i32 0, i32 %off + %ref = load %externref, %externref addrspace(1)* %p + ret %externref %ref +} + +; CHECK: .globl externref_table diff --git a/llvm/test/CodeGen/WebAssembly/externref-tableset.ll b/llvm/test/CodeGen/WebAssembly/externref-tableset.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/externref-tableset.ll @@ -0,0 +1,82 @@ +; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s | FileCheck %s + +%extern = type opaque +%externref = type %extern addrspace(10)* ;; addrspace 10 is nonintegral + +@externref_table = local_unnamed_addr addrspace(1) global [0 x %externref] undef + +define void @set_externref_table(%externref %g, i32 %i) { +; CHECK-LABEL: set_externref_table: +; CHECK-NEXT: .functype set_externref_table (externref, i32) -> () +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.set externref_table +; CHECK-NEXT: end_function + +;; this generates a table.set of @externref_table + %p = getelementptr [0 x %externref], [0 x %externref] addrspace (1)* @externref_table, i32 0, i32 %i + store %externref %g, %externref addrspace(1)* %p + ret void +} + +define void @set_externref_table_const(%externref %g) { +; CHECK-LABEL: set_externref_table_const: +; CHECK-NEXT: .functype set_externref_table_const (externref) -> () +; CHECK-NEXT: i32.const 0 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.set externref_table +; CHECK-NEXT: end_function + %p = getelementptr [0 x %externref], [0 x %externref] addrspace (1)* @externref_table, i32 0, i32 0 + store %externref %g, %externref addrspace(1)* %p + ret void +} + +define void @set_externref_table_with_offset(%externref %g, i32 %i) { +; CHECK-LABEL: set_externref_table_with_offset: +; CHECK-NEXT: .functype set_externref_table_with_offset (externref, i32) -> () +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: i32.const 2 +; CHECK-NEXT: i32.add +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.set externref_table +; CHECK-NEXT: end_function + %off = add nsw i32 %i, 2 + %p = getelementptr [0 x %externref], [0 x %externref] addrspace (1)* @externref_table, i32 0, i32 %off + store %externref %g, %externref addrspace(1)* %p + ret void +} + +define void @set_externref_table_with_var_offset(%externref %g, i32 %i, i32 %j) { +; CHECK-LABEL: set_externref_table_with_var_offset: +; CHECK-NEXT: .functype set_externref_table_with_var_offset (externref, i32, i32) -> () +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: local.get 2 +; CHECK-NEXT: i32.add +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.set externref_table +; CHECK-NEXT: end_function + %off = add nsw i32 %i, %j + %p = getelementptr [0 x %externref], [0 x %externref] addrspace (1)* @externref_table, i32 0, i32 %off + store %externref %g, %externref addrspace(1)* %p + ret void +} + +declare i32 @set_offset() + +define void @set_externref_table_with_var_offset2(%externref %g, i32 %i) { +; CHECK-LABEL: set_externref_table_with_var_offset2: +; CHECK-NEXT: .functype set_externref_table_with_var_offset2 (externref, i32) -> () +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: call set_offset +; CHECK-NEXT: i32.add +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.set externref_table +; CHECK-NEXT: end_function + %j = call i32 @set_offset() + %off = add nsw i32 %i, %j + %p = getelementptr [0 x %externref], [0 x %externref] addrspace (1)* @externref_table, i32 0, i32 %off + store %externref %g, %externref addrspace(1)* %p + ret void +} + +; CHECK: .globl externref_table diff --git a/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll b/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll @@ -0,0 +1,32 @@ +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s + +%func = type void () +%funcref = type %func addrspace(20)* ;; addrspace 20 is nonintegral + +@funcref_table = local_unnamed_addr addrspace(1) global [0 x %funcref] undef + +define void @call_funcref_from_table(i32 %i) { + %p = getelementptr [0 x %funcref], [0 x %funcref] addrspace (1)* @funcref_table, i32 0, i32 %i + %ref = load %funcref, %funcref addrspace(1)* %p + call addrspace(20) void %ref() + ret void +} + +; CHECK: .tabletype __funcref_call_table, funcref, 1 + +; CHECK-LABEL: call_funcref_from_table: +; CHECK-NEXT: .functype call_funcref_from_table (i32) -> () +; CHECK-NEXT: i32.const 0 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.get funcref_table +; CHECK-NEXT: table.set __funcref_call_table +; CHECK-NEXT: i32.const 0 +; CHECK-NEXT: call_indirect __funcref_call_table, () -> () +; CHECK-NEXT: i32.const 0 +; CHECK-NEXT: ref.null func +; CHECK-NEXT: table.set __funcref_call_table +; CHECK-NEXT: end_function + + +; CHECK: .globl funcref_table +; CHECK-NEXT .globaltype funcref_table, funcref diff --git a/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll b/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll @@ -0,0 +1,76 @@ +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s + +%func = type void () +%funcref = type %func addrspace(20)* ;; addrspace 20 is nonintegral + +@funcref_table = local_unnamed_addr addrspace(1) global [0 x %funcref] undef + +define %funcref @get_funcref_from_table(i32 %i) { +; CHECK-LABEL: get_funcref_from_table: +; CHECK-NEXT: .functype get_funcref_from_table (i32) -> (funcref) +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.get funcref_table +; CHECK-NEXT: end_function + %p = getelementptr [0 x %funcref], [0 x %funcref] addrspace (1)* @funcref_table, i32 0, i32 %i + %ref = load %funcref, %funcref addrspace(1)* %p + ret %funcref %ref +} + +define %funcref @get_funcref_from_table_const() { +; CHECK-LABEL: get_funcref_from_table_const: +; CHECK-NEXT: .functype get_funcref_from_table_const () -> (funcref) +; CHECK-NEXT: i32.const 0 +; CHECK-NEXT: table.get funcref_table +; CHECK-NEXT: end_function + %p = getelementptr [0 x %funcref], [0 x %funcref] addrspace (1)* @funcref_table, i32 0, i32 0 + %ref = load %funcref, %funcref addrspace(1)* %p + ret %funcref %ref +} + +define %funcref @get_funcref_from_table_with_offset(i32 %i) { +; CHECK-LABEL: get_funcref_from_table_with_offset: +; CHECK-NEXT: .functype get_funcref_from_table_with_offset (i32) -> (funcref) +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: i32.const 2 +; CHECK-NEXT: i32.add +; CHECK-NEXT: table.get funcref_table +; CHECK-NEXT: end_function + %off = add nsw i32 %i, 2 + %p = getelementptr [0 x %funcref], [0 x %funcref] addrspace (1)* @funcref_table, i32 0, i32 %off + %ref = load %funcref, %funcref addrspace(1)* %p + ret %funcref %ref +} + + +define %funcref @get_funcref_from_table_with_var_offset(i32 %i, i32 %j) { +; CHECK-LABEL: get_funcref_from_table_with_var_offset: +; CHECK-NEXT: .functype get_funcref_from_table_with_var_offset (i32, i32) -> (funcref) +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: i32.add +; CHECK-NEXT: table.get funcref_table +; CHECK-NEXT: end_function + %off = add nsw i32 %i, %j + %p = getelementptr [0 x %funcref], [0 x %funcref] addrspace (1)* @funcref_table, i32 0, i32 %off + %ref = load %funcref, %funcref addrspace(1)* %p + ret %funcref %ref +} + +declare i32 @get_offset() + +define %funcref @get_funcref_from_table_with_var_offset2(i32 %i) { +; CHECK-LABEL: get_funcref_from_table_with_var_offset2: +; CHECK-NEXT: .functype get_funcref_from_table_with_var_offset2 (i32) -> (funcref) +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: call get_offset +; CHECK-NEXT: i32.add +; CHECK-NEXT: table.get funcref_table +; CHECK-NEXT: end_function + %j = call i32 @get_offset() + %off = add nsw i32 %i, %j + %p = getelementptr [0 x %funcref], [0 x %funcref] addrspace (1)* @funcref_table, i32 0, i32 %off + %ref = load %funcref, %funcref addrspace(1)* %p + ret %funcref %ref +} + +; CHECK: .globl funcref_table diff --git a/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll b/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll @@ -0,0 +1,82 @@ +; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s | FileCheck %s + +%func = type void () +%funcref = type %func addrspace(20)* ;; addrspace 20 is nonintegral + +@funcref_table = local_unnamed_addr addrspace(1) global [0 x %funcref] undef + +define void @set_funcref_table(%funcref %g, i32 %i) { +; CHECK-LABEL: set_funcref_table: +; CHECK-NEXT: .functype set_funcref_table (funcref, i32) -> () +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.set funcref_table +; CHECK-NEXT: end_function + +;; this generates a table.set of @funcref_table + %p = getelementptr [0 x %funcref], [0 x %funcref] addrspace (1)* @funcref_table, i32 0, i32 %i + store %funcref %g, %funcref addrspace(1)* %p + ret void +} + +define void @set_funcref_table_const(%funcref %g) { +; CHECK-LABEL: set_funcref_table_const: +; CHECK-NEXT: .functype set_funcref_table_const (funcref) -> () +; CHECK-NEXT: i32.const 0 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.set funcref_table +; CHECK-NEXT: end_function + %p = getelementptr [0 x %funcref], [0 x %funcref] addrspace (1)* @funcref_table, i32 0, i32 0 + store %funcref %g, %funcref addrspace(1)* %p + ret void +} + +define void @set_funcref_table_with_offset(%funcref %g, i32 %i) { +; CHECK-LABEL: set_funcref_table_with_offset: +; CHECK-NEXT: .functype set_funcref_table_with_offset (funcref, i32) -> () +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: i32.const 2 +; CHECK-NEXT: i32.add +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.set funcref_table +; CHECK-NEXT: end_function + %off = add nsw i32 %i, 2 + %p = getelementptr [0 x %funcref], [0 x %funcref] addrspace (1)* @funcref_table, i32 0, i32 %off + store %funcref %g, %funcref addrspace(1)* %p + ret void +} + +define void @set_funcref_table_with_var_offset(%funcref %g, i32 %i, i32 %j) { +; CHECK-LABEL: set_funcref_table_with_var_offset: +; CHECK-NEXT: .functype set_funcref_table_with_var_offset (funcref, i32, i32) -> () +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: local.get 2 +; CHECK-NEXT: i32.add +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.set funcref_table +; CHECK-NEXT: end_function + %off = add nsw i32 %i, %j + %p = getelementptr [0 x %funcref], [0 x %funcref] addrspace (1)* @funcref_table, i32 0, i32 %off + store %funcref %g, %funcref addrspace(1)* %p + ret void +} + +declare i32 @set_offset() + +define void @set_funcref_table_with_var_offset2(%funcref %g, i32 %i) { +; CHECK-LABEL: set_funcref_table_with_var_offset2: +; CHECK-NEXT: .functype set_funcref_table_with_var_offset2 (funcref, i32) -> () +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: call set_offset +; CHECK-NEXT: i32.add +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.set funcref_table +; CHECK-NEXT: end_function + %j = call i32 @set_offset() + %off = add nsw i32 %i, %j + %p = getelementptr [0 x %funcref], [0 x %funcref] addrspace (1)* @funcref_table, i32 0, i32 %off + store %funcref %g, %funcref addrspace(1)* %p + ret void +} + +; CHECK: .globl funcref_table