Skip to content

Commit

Permalink
[WebAssembly] Implement table instruction intrinsics
Browse files Browse the repository at this point in the history
This change implements intrinsics for table.grow, table.fill,
table.size, and table.copy.

Differential Revision: https://reviews.llvm.org/D113420
  • Loading branch information
pmatos committed Dec 7, 2021
1 parent ed43aab commit 2fd634a
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 27 deletions.
26 changes: 26 additions & 0 deletions llvm/include/llvm/IR/IntrinsicsWebAssembly.td
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
///
//===----------------------------------------------------------------------===//

// Type definition for a table in an intrinsic
def llvm_table_ty : LLVMQualPointerType<llvm_i8_ty, 1>;

let TargetPrefix = "wasm" in { // All intrinsics start with "llvm.wasm.".

// Query the current memory size, and increase the current memory size.
Expand All @@ -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
//===----------------------------------------------------------------------===//
Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/CodeGen/ValueTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
16 changes: 9 additions & 7 deletions llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def WebAssemblyTableGet : SDNode<"WebAssemblyISD::TABLE_GET", WebAssemblyTableGe
[SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>;


multiclass TABLE<WebAssemblyRegClass rc> {
multiclass TABLE<WebAssemblyRegClass rc, string suffix> {
let mayLoad = 1 in
defm TABLE_GET_#rc : I<(outs rc:$res), (ins table32_op:$table, I32:$i),
(outs), (ins table32_op:$table),
Expand All @@ -39,14 +39,14 @@ multiclass TABLE<WebAssemblyRegClass rc> {

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<Intrinsic>("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<Intrinsic>("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>;
Expand All @@ -62,16 +62,16 @@ multiclass TABLE<WebAssemblyRegClass rc> {
}
}

defm "" : TABLE<FUNCREF>, Requires<[HasReferenceTypes]>;
defm "" : TABLE<EXTERNREF>, Requires<[HasReferenceTypes]>;
defm "" : TABLE<FUNCREF, "funcref">, Requires<[HasReferenceTypes]>;
defm "" : TABLE<EXTERNREF, "externref">, Requires<[HasReferenceTypes]>;

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),
(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>,
Expand All @@ -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>,
Expand Down
34 changes: 26 additions & 8 deletions llvm/test/CodeGen/WebAssembly/funcref-call.ll
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
}



3 changes: 1 addition & 2 deletions llvm/test/CodeGen/WebAssembly/funcref-globalget.ll
Original file line number Diff line number Diff line change
@@ -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

Expand Down
3 changes: 1 addition & 2 deletions llvm/test/CodeGen/WebAssembly/funcref-globalset.ll
Original file line number Diff line number Diff line change
@@ -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

Expand Down
7 changes: 4 additions & 3 deletions llvm/test/CodeGen/WebAssembly/funcref-table_call.ll
Original file line number Diff line number Diff line change
@@ -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
}

Expand Down
3 changes: 1 addition & 2 deletions llvm/test/CodeGen/WebAssembly/funcref-tableget.ll
Original file line number Diff line number Diff line change
@@ -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

Expand Down
3 changes: 1 addition & 2 deletions llvm/test/CodeGen/WebAssembly/funcref-tableset.ll
Original file line number Diff line number Diff line change
@@ -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

Expand Down
44 changes: 44 additions & 0 deletions llvm/test/CodeGen/WebAssembly/table-copy.ll
Original file line number Diff line number Diff line change
@@ -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
}
22 changes: 22 additions & 0 deletions llvm/test/CodeGen/WebAssembly/table-fill.ll
Original file line number Diff line number Diff line change
@@ -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
}
23 changes: 23 additions & 0 deletions llvm/test/CodeGen/WebAssembly/table-grow.ll
Original file line number Diff line number Diff line change
@@ -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
}
19 changes: 19 additions & 0 deletions llvm/test/CodeGen/WebAssembly/table-size.ll
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 2fd634a

Please sign in to comment.