From 2fd634a5e307fc21ea7d05ab7c7fe9689f14fe25 Mon Sep 17 00:00:00 2001 From: Paulo Matos Date: Mon, 6 Dec 2021 09:51:39 +0100 Subject: [PATCH] [WebAssembly] Implement table instruction intrinsics This change implements intrinsics for table.grow, table.fill, table.size, and table.copy. Differential Revision: https://reviews.llvm.org/D113420 --- llvm/include/llvm/IR/IntrinsicsWebAssembly.td | 26 +++++++++++ llvm/lib/CodeGen/ValueTypes.cpp | 4 +- .../WebAssembly/WebAssemblyInstrTable.td | 16 ++++--- llvm/test/CodeGen/WebAssembly/funcref-call.ll | 34 ++++++++++---- .../CodeGen/WebAssembly/funcref-globalget.ll | 3 +- .../CodeGen/WebAssembly/funcref-globalset.ll | 3 +- .../CodeGen/WebAssembly/funcref-table_call.ll | 7 +-- .../CodeGen/WebAssembly/funcref-tableget.ll | 3 +- .../CodeGen/WebAssembly/funcref-tableset.ll | 3 +- llvm/test/CodeGen/WebAssembly/table-copy.ll | 44 +++++++++++++++++++ llvm/test/CodeGen/WebAssembly/table-fill.ll | 22 ++++++++++ llvm/test/CodeGen/WebAssembly/table-grow.ll | 23 ++++++++++ llvm/test/CodeGen/WebAssembly/table-size.ll | 19 ++++++++ 13 files changed, 180 insertions(+), 27 deletions(-) create mode 100644 llvm/test/CodeGen/WebAssembly/table-copy.ll create mode 100644 llvm/test/CodeGen/WebAssembly/table-fill.ll create mode 100644 llvm/test/CodeGen/WebAssembly/table-grow.ll create mode 100644 llvm/test/CodeGen/WebAssembly/table-size.ll diff --git a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td index 850b6500028b..aecc3d91fae7 100644 --- a/llvm/include/llvm/IR/IntrinsicsWebAssembly.td +++ b/llvm/include/llvm/IR/IntrinsicsWebAssembly.td @@ -11,6 +11,9 @@ /// //===----------------------------------------------------------------------===// +// Type definition for a table in an intrinsic +def llvm_table_ty : LLVMQualPointerType; + let TargetPrefix = "wasm" in { // All intrinsics start with "llvm.wasm.". // Query the current memory size, and increase the current memory size. @@ -29,6 +32,29 @@ def int_wasm_memory_grow : Intrinsic<[llvm_anyint_ty], def int_wasm_ref_null_extern : Intrinsic<[llvm_externref_ty], [], [IntrNoMem]>; def int_wasm_ref_null_func : Intrinsic<[llvm_funcref_ty], [], [IntrNoMem]>; +//===----------------------------------------------------------------------===// +// Table intrinsics +//===----------------------------------------------------------------------===// +// Query the current table size, and increase the current table size. +def int_wasm_table_size : Intrinsic<[llvm_i32_ty], + [llvm_table_ty], + [IntrReadMem]>; +def int_wasm_table_copy : Intrinsic<[], + [llvm_table_ty, llvm_table_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty], + []>; +def int_wasm_table_grow_externref : Intrinsic<[llvm_i32_ty], + [llvm_table_ty, llvm_externref_ty, llvm_i32_ty], + []>; +def int_wasm_table_grow_funcref : Intrinsic<[llvm_i32_ty], + [llvm_table_ty, llvm_funcref_ty, llvm_i32_ty], + []>; +def int_wasm_table_fill_externref : Intrinsic<[], + [llvm_table_ty, llvm_i32_ty, llvm_externref_ty, llvm_i32_ty], + []>; +def int_wasm_table_fill_funcref : Intrinsic<[], + [llvm_table_ty, llvm_i32_ty, llvm_funcref_ty, llvm_i32_ty], + []>; + //===----------------------------------------------------------------------===// // Trapping float-to-int conversions //===----------------------------------------------------------------------===// diff --git a/llvm/lib/CodeGen/ValueTypes.cpp b/llvm/lib/CodeGen/ValueTypes.cpp index 4876b9e23717..0c42bef82005 100644 --- a/llvm/lib/CodeGen/ValueTypes.cpp +++ b/llvm/lib/CodeGen/ValueTypes.cpp @@ -201,9 +201,11 @@ Type *EVT::getTypeForEVT(LLVMContext &Context) const { case MVT::x86amx: return Type::getX86_AMXTy(Context); case MVT::i64x8: return IntegerType::get(Context, 512); case MVT::externref: + // pointer to opaque struct in addrspace(10) return PointerType::get(StructType::create(Context), 10); case MVT::funcref: - return PointerType::get(StructType::create(Context), 20); + // pointer to i8 addrspace(20) + return PointerType::get(Type::getInt8Ty(Context), 20); case MVT::v1i1: return FixedVectorType::get(Type::getInt1Ty(Context), 1); case MVT::v2i1: diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td index e44c2073eaeb..1fd00bf1cbc8 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td @@ -20,7 +20,7 @@ def WebAssemblyTableGet : SDNode<"WebAssemblyISD::TABLE_GET", WebAssemblyTableGe [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; -multiclass TABLE { +multiclass TABLE { let mayLoad = 1 in defm TABLE_GET_#rc : I<(outs rc:$res), (ins table32_op:$table, I32:$i), (outs), (ins table32_op:$table), @@ -39,14 +39,14 @@ multiclass TABLE { defm TABLE_GROW_#rc : I<(outs I32:$sz), (ins table32_op:$table, rc:$val, I32:$n), (outs), (ins table32_op:$table), - [], + [(set I32:$sz, (!cast("int_wasm_table_grow_" # suffix) (WebAssemblyWrapper tglobaladdr:$table), rc:$val, I32:$n))], "table.grow\t$sz, $table, $val, $n", "table.grow\t$table", 0xfc0f>; defm TABLE_FILL_#rc : I<(outs), (ins table32_op:$table, I32:$i, rc:$val, I32:$n), (outs), (ins table32_op:$table), - [], + [(!cast("int_wasm_table_fill_" # suffix) (WebAssemblyWrapper tglobaladdr:$table), I32:$i, rc:$val, I32:$n)], "table.fill\t$table, $i, $val, $n", "table.fill\t$table", 0xfc11>; @@ -62,8 +62,8 @@ multiclass TABLE { } } -defm "" : TABLE, Requires<[HasReferenceTypes]>; -defm "" : TABLE, Requires<[HasReferenceTypes]>; +defm "" : TABLE, Requires<[HasReferenceTypes]>; +defm "" : TABLE, Requires<[HasReferenceTypes]>; def : Pat<(WebAssemblyTableSet mcsym:$table, i32:$idx, funcref:$r), (TABLE_SET_FUNCREF mcsym:$table, i32:$idx, funcref:$r)>, @@ -71,7 +71,7 @@ def : Pat<(WebAssemblyTableSet mcsym:$table, i32:$idx, funcref:$r), defm TABLE_SIZE : I<(outs I32:$sz), (ins table32_op:$table), (outs), (ins table32_op:$table), - [], + [(set I32:$sz, (int_wasm_table_size (WebAssemblyWrapper tglobaladdr:$table)))], "table.size\t$sz, $table", "table.size\t$table", 0xfc10>, @@ -80,7 +80,9 @@ defm TABLE_SIZE : I<(outs I32:$sz), (ins table32_op:$table), defm TABLE_COPY : I<(outs), (ins table32_op:$table1, table32_op:$table2, I32:$d, I32:$s, I32:$n), (outs), (ins table32_op:$table1, table32_op:$table2), - [], + [(int_wasm_table_copy (WebAssemblyWrapper tglobaladdr:$table1), + (WebAssemblyWrapper tglobaladdr:$table2), + I32:$d, I32:$s, I32:$n)], "table.copy\t$table1, $table2, $d, $s, $n", "table.copy\t$table1, $table2", 0xfc0e>, diff --git a/llvm/test/CodeGen/WebAssembly/funcref-call.ll b/llvm/test/CodeGen/WebAssembly/funcref-call.ll index f65ea111e1ac..d568c817a126 100644 --- a/llvm/test/CodeGen/WebAssembly/funcref-call.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-call.ll @@ -1,17 +1,13 @@ ; 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 - -define void @call_funcref(%funcref %ref) { - call addrspace(20) void %ref() - ret void -} +%funcptr = type void () addrspace(20)* +%funcref = type i8 addrspace(20)* ;; addrspace 20 is nonintegral ; CHECK: .tabletype __funcref_call_table, funcref, 1 +define void @call_funcref(%funcref %ref) { ; CHECK-LABEL: call_funcref: -; CHECK-NEXT: functype call_funcref (funcref) -> () +; CHECK-NEXT: .functype call_funcref (funcref) -> () ; CHECK-NEXT: i32.const 0 ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: table.set __funcref_call_table @@ -21,4 +17,26 @@ define void @call_funcref(%funcref %ref) { ; CHECK-NEXT: ref.null_func ; CHECK-NEXT: table.set __funcref_call_table ; CHECK-NEXT: end_function + %f = bitcast %funcref %ref to %funcptr + call addrspace(20) void %f() + ret void +} + +define void @call_funcptr(%funcptr %ref) { +; CHECK-LABEL: call_funcptr: +; CHECK-NEXT: .functype call_funcptr (funcref) -> () +; CHECK-NEXT: i32.const 0 +; CHECK-NEXT: local.get 0 +; 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 + call addrspace(20) void %ref() + ret void +} + + diff --git a/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll b/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll index 901404aafd71..37a72aba50b2 100644 --- a/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll @@ -1,7 +1,6 @@ ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s -%func = type opaque -%funcref = type %func addrspace(20)* ;; addrspace 20 is nonintegral +%funcref = type i8 addrspace(20)* ;; addrspace 20 is nonintegral @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 18b0e02cf739..3c23abe49cbc 100644 --- a/llvm/test/CodeGen/WebAssembly/funcref-globalset.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-globalset.ll @@ -1,7 +1,6 @@ ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s -%func = type opaque -%funcref = type %func addrspace(20)* ;; addrspace 20 is nonintegral +%funcref = type i8 addrspace(20)* ;; addrspace 20 is nonintegral @funcref_global = local_unnamed_addr addrspace(1) global %funcref undef diff --git a/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll b/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll index f87f38eae2ab..a9a317175e26 100644 --- a/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-table_call.ll @@ -1,14 +1,15 @@ ; 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 +%funcptr = type void () addrspace(20)* +%funcref = type i8 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() + %fn = bitcast %funcref %ref to %funcptr + call addrspace(20) void %fn() ret void } diff --git a/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll b/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll index f40c9e0c8972..c374da7d1eb3 100644 --- a/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-tableget.ll @@ -1,7 +1,6 @@ ; 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 = type i8 addrspace(20)* ;; addrspace 20 is nonintegral @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 7d016f536c06..1bb50d16e3fe 100644 --- a/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll +++ b/llvm/test/CodeGen/WebAssembly/funcref-tableset.ll @@ -1,7 +1,6 @@ ; 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 = type i8 addrspace(20)* ;; addrspace 20 is nonintegral @funcref_table = local_unnamed_addr addrspace(1) global [0 x %funcref] undef diff --git a/llvm/test/CodeGen/WebAssembly/table-copy.ll b/llvm/test/CodeGen/WebAssembly/table-copy.ll new file mode 100644 index 000000000000..7239a3f5febf --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/table-copy.ll @@ -0,0 +1,44 @@ +; 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_table1 = local_unnamed_addr addrspace(1) global [0 x %externref] undef +@externref_table2 = local_unnamed_addr addrspace(1) global [0 x %externref] undef + +declare void @llvm.wasm.table.copy(i8 addrspace(1)*, i8 addrspace(1)*, i32, i32, i32) nounwind readonly + +define void @table_copy(i32 %dst, i32 %src, i32 %len) { +; CHECK-LABEL: table_copy: +; CHECK-NEXT: .functype table_copy (i32, i32, i32) -> () +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: local.get 2 +; CHECK-NEXT: table.copy externref_table1, externref_table2 +; CHECK-NEXT: end_function + %tableptr1 = getelementptr [0 x %externref], [0 x %externref] addrspace(1)* @externref_table1, i32 0, i32 0 + %tb1 = bitcast %externref addrspace(1)* %tableptr1 to i8 addrspace(1)* + %tableptr2 = getelementptr [0 x %externref], [0 x %externref] addrspace(1)* @externref_table2, i32 0, i32 0 + %tb2 = bitcast %externref addrspace(1)* %tableptr2 to i8 addrspace(1)* + call void @llvm.wasm.table.copy(i8 addrspace(1)* %tb1, i8 addrspace(1)* %tb2, i32 %dst, i32 %src, i32 %len) + ret void +} + +; Testing copying from a table to itself at different offsets +; Copies len items from table1 at src to table1 at src+off +define void @self_table_copy(i32 %src, i32 %off, i32 %len) { +; CHECK-LABEL: self_table_copy: +; CHECK-NEXT: .functype self_table_copy (i32, i32, i32) -> () +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: i32.add +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 2 +; CHECK-NEXT: table.copy externref_table1, externref_table1 +; CHECK-NEXT: end_function + %dst = add nsw i32 %src, %off + %tableptr1 = getelementptr [0 x %externref], [0 x %externref] addrspace(1)* @externref_table1, i32 0, i32 0 + %tb1 = bitcast %externref addrspace(1)* %tableptr1 to i8 addrspace(1)* + call void @llvm.wasm.table.copy(i8 addrspace(1)* %tb1, i8 addrspace(1)* %tb1, i32 %dst, i32 %src, i32 %len) + ret void +} diff --git a/llvm/test/CodeGen/WebAssembly/table-fill.ll b/llvm/test/CodeGen/WebAssembly/table-fill.ll new file mode 100644 index 000000000000..d547f2e8ac36 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/table-fill.ll @@ -0,0 +1,22 @@ +; 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 + +declare void @llvm.wasm.table.fill.externref(i8 addrspace(1)*, i32, %externref, i32) nounwind readonly + +define void @table_fill(i32 %start, i32 %len, %externref %val) { +; CHECK-LABEL: table_fill: +; CHECK-NEXT: .functype table_fill (i32, i32, externref) -> () +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 2 +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: table.fill externref_table +; CHECK-NEXT: end_function + %tableptr = getelementptr [0 x %externref], [0 x %externref] addrspace(1)* @externref_table, i32 0, i32 0 + %tb = bitcast %externref addrspace(1)* %tableptr to i8 addrspace(1)* + call void @llvm.wasm.table.fill.externref(i8 addrspace(1)* %tb, i32 %start, %externref %val, i32 %len) + ret void +} diff --git a/llvm/test/CodeGen/WebAssembly/table-grow.ll b/llvm/test/CodeGen/WebAssembly/table-grow.ll new file mode 100644 index 000000000000..de6ab83771ac --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/table-grow.ll @@ -0,0 +1,23 @@ +; 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 + +declare i32 @llvm.wasm.table.grow.externref(i8 addrspace(1)*, %externref, i32) nounwind readonly +declare %externref @llvm.wasm.ref.null.extern() nounwind readonly + +define i32 @table_grow(i32 %sz) { +; CHECK-LABEL: table_grow: +; CHECK-NEXT: .functype table_grow (i32) -> (i32) +; CHECK-NEXT: ref.null_extern +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: table.grow externref_table +; CHECK-NEXT: end_function + %null = call %externref @llvm.wasm.ref.null.extern() + %tableptr = getelementptr [0 x %externref], [0 x %externref] addrspace(1)* @externref_table, i32 0, i32 0 + %tb = bitcast %externref addrspace(1)* %tableptr to i8 addrspace(1)* + %newsz = call i32 @llvm.wasm.table.grow.externref(i8 addrspace(1)* %tb, %externref %null, i32 %sz) + ret i32 %newsz +} diff --git a/llvm/test/CodeGen/WebAssembly/table-size.ll b/llvm/test/CodeGen/WebAssembly/table-size.ll new file mode 100644 index 000000000000..267d22a96b09 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/table-size.ll @@ -0,0 +1,19 @@ +; 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 + +declare i32 @llvm.wasm.table.size(i8 addrspace(1)*) nounwind readonly + +define i32 @table_size() { +; CHECK-LABEL: table_size: +; CHECK-NEXT: .functype table_size () -> (i32) +; CHECK-NEXT: table.size externref_table +; CHECK-NEXT: end_function + %tableptr = getelementptr [0 x %externref], [0 x %externref] addrspace(1)* @externref_table, i32 0, i32 0 + %tb = bitcast %externref addrspace(1)* %tableptr to i8 addrspace(1)* + %sz = call i32 @llvm.wasm.table.size(i8 addrspace(1)* %tb) + ret i32 %sz +}