[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
This commit is contained in:
parent
ed43aab98d
commit
2fd634a5e3
@ -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.
|
||||
@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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),
|
||||
@ -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>;
|
||||
@ -62,8 +62,8 @@ 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)>,
|
||||
@ -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>,
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
44
llvm/test/CodeGen/WebAssembly/table-copy.ll
Normal file
44
llvm/test/CodeGen/WebAssembly/table-copy.ll
Normal file
@ -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
llvm/test/CodeGen/WebAssembly/table-fill.ll
Normal file
22
llvm/test/CodeGen/WebAssembly/table-fill.ll
Normal file
@ -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
llvm/test/CodeGen/WebAssembly/table-grow.ll
Normal file
23
llvm/test/CodeGen/WebAssembly/table-grow.ll
Normal file
@ -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
llvm/test/CodeGen/WebAssembly/table-size.ll
Normal file
19
llvm/test/CodeGen/WebAssembly/table-size.ll
Normal file
@ -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
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user