Skip to content

Commit

Permalink
[WebAssembly] Bulk memory intrinsics and builtins
Browse files Browse the repository at this point in the history
Summary:
implements llvm intrinsics and clang intrinsics for
memory.init and data.drop.

Reviewers: aheejin

Subscribers: dschuff, sbc100, jgravelle-google, hiraditya, sunfish, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D57736

llvm-svn: 353983
  • Loading branch information
tlively committed Feb 13, 2019
1 parent 221c391 commit de7a0a1
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 36 deletions.
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/BuiltinsWebAssembly.def
Expand Up @@ -25,6 +25,10 @@
BUILTIN(__builtin_wasm_memory_size, "zIi", "n")
BUILTIN(__builtin_wasm_memory_grow, "zIiz", "n")

// Bulk memory builtins
TARGET_BUILTIN(__builtin_wasm_memory_init, "vIUiIUiv*UiUi", "", "bulk-memory")
TARGET_BUILTIN(__builtin_wasm_data_drop, "vIUi", "", "bulk-memory")

// Floating point min/max
BUILTIN(__builtin_wasm_min_f32, "fff", "nc")
BUILTIN(__builtin_wasm_max_f32, "fff", "nc")
Expand Down
24 changes: 24 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Expand Up @@ -13566,6 +13566,30 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID,
Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_memory_grow, ResultType);
return Builder.CreateCall(Callee, Args);
}
case WebAssembly::BI__builtin_wasm_memory_init: {
llvm::APSInt SegConst;
if (!E->getArg(0)->isIntegerConstantExpr(SegConst, getContext()))
llvm_unreachable("Constant arg isn't actually constant?");
llvm::APSInt MemConst;
if (!E->getArg(1)->isIntegerConstantExpr(MemConst, getContext()))
llvm_unreachable("Constant arg isn't actually constant?");
if (!MemConst.isNullValue())
ErrorUnsupported(E, "non-zero memory index");
Value *Args[] = {llvm::ConstantInt::get(getLLVMContext(), SegConst),
llvm::ConstantInt::get(getLLVMContext(), MemConst),
EmitScalarExpr(E->getArg(2)), EmitScalarExpr(E->getArg(3)),
EmitScalarExpr(E->getArg(4))};
Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_memory_init);
return Builder.CreateCall(Callee, Args);
}
case WebAssembly::BI__builtin_wasm_data_drop: {
llvm::APSInt SegConst;
if (!E->getArg(0)->isIntegerConstantExpr(SegConst, getContext()))
llvm_unreachable("Constant arg isn't actually constant?");
Value *Arg = llvm::ConstantInt::get(getLLVMContext(), SegConst);
Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_data_drop);
return Builder.CreateCall(Callee, {Arg});
}
case WebAssembly::BI__builtin_wasm_throw: {
Value *Tag = EmitScalarExpr(E->getArg(0));
Value *Obj = EmitScalarExpr(E->getArg(1));
Expand Down
18 changes: 15 additions & 3 deletions clang/test/CodeGen/builtins-wasm.c
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +unimplemented-simd128 -target-feature +nontrapping-fptoint -target-feature +exception-handling -fno-lax-vector-conversions -O3 -emit-llvm -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY32
// RUN: %clang_cc1 -triple wasm64-unknown-unknown -target-feature +unimplemented-simd128 -target-feature +nontrapping-fptoint -target-feature +exception-handling -fno-lax-vector-conversions -O3 -emit-llvm -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY64
// RUN: not %clang_cc1 -triple wasm64-unknown-unknown -target-feature +nontrapping-fptoint -target-feature +exception-handling -fno-lax-vector-conversions -O3 -emit-llvm -o - %s 2>&1 | FileCheck %s -check-prefixes MISSING-SIMD
// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +unimplemented-simd128 -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -fno-lax-vector-conversions -O3 -emit-llvm -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY32
// RUN: %clang_cc1 -triple wasm64-unknown-unknown -target-feature +unimplemented-simd128 -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -fno-lax-vector-conversions -O3 -emit-llvm -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY64
// RUN: not %clang_cc1 -triple wasm64-unknown-unknown -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -fno-lax-vector-conversions -O3 -emit-llvm -o - %s 2>&1 | FileCheck %s -check-prefixes MISSING-SIMD

// SIMD convenience types
typedef char i8x16 __attribute((vector_size(16)));
Expand All @@ -26,6 +26,18 @@ __SIZE_TYPE__ memory_grow(__SIZE_TYPE__ delta) {
// WEBASSEMBLY64: call i64 @llvm.wasm.memory.grow.i64(i32 0, i64 %{{.*}})
}

void memory_init(void *dest, int offset, int size) {
__builtin_wasm_memory_init(3, 0, dest, offset, size);
// WEBASSEMBLY32: call void @llvm.wasm.memory.init(i32 3, i32 0, i8* %{{.*}}, i32 %{{.*}}, i32 %{{.*}})
// WEBASSEMBLY64: call void @llvm.wasm.memory.init(i32 3, i32 0, i8* %{{.*}}, i32 %{{.*}}, i32 %{{.*}})
}

void data_drop() {
__builtin_wasm_data_drop(3);
// WEBASSEMBLY32: call void @llvm.wasm.data.drop(i32 3)
// WEBASSEMBLY64: call void @llvm.wasm.data.drop(i32 3)
}

void throw(unsigned int tag, void *obj) {
return __builtin_wasm_throw(tag, obj);
// WEBASSEMBLY32: call void @llvm.wasm.throw(i32 %{{.*}}, i8* %{{.*}})
Expand Down
14 changes: 14 additions & 0 deletions llvm/include/llvm/IR/IntrinsicsWebAssembly.td
Expand Up @@ -110,4 +110,18 @@ def int_wasm_alltrue :
[llvm_anyvector_ty],
[IntrNoMem, IntrSpeculatable]>;

//===----------------------------------------------------------------------===//
// Bulk memory intrinsics
//===----------------------------------------------------------------------===//

def int_wasm_memory_init :
Intrinsic<[],
[llvm_i32_ty, llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty],
[IntrWriteMem, IntrInaccessibleMemOrArgMemOnly, WriteOnly<2>,
IntrHasSideEffects]>;
def int_wasm_data_drop :
Intrinsic<[],
[llvm_i32_ty],
[IntrNoDuplicate, IntrHasSideEffects]>;

} // TargetPrefix = "wasm"
43 changes: 36 additions & 7 deletions llvm/lib/Target/WebAssembly/WebAssemblyInstrBulkMemory.td
Expand Up @@ -21,19 +21,48 @@ multiclass BULK_I<dag oops_r, dag iops_r, dag oops_s, dag iops_s,
}

// Bespoke types and nodes for bulk memory ops
def wasm_memcpy_t : SDTypeProfile<0, 3,
[SDTCisPtrTy<0>, SDTCisPtrTy<1>, SDTCisInt<2>]
def wasm_memcpy_t : SDTypeProfile<0, 5,
[SDTCisInt<0>, SDTCisInt<1>, SDTCisPtrTy<2>, SDTCisPtrTy<3>, SDTCisInt<4>]
>;
def wasm_memcpy : SDNode<"WebAssemblyISD::MEMORY_COPY", wasm_memcpy_t,
[SDNPHasChain, SDNPMayLoad, SDNPMayStore]>;

//===----------------------------------------------------------------------===//
// memory.init
//===----------------------------------------------------------------------===//

let mayStore = 1 in
defm MEMORY_INIT :
BULK_I<(outs),
(ins i32imm_op:$seg, i32imm_op:$idx, I32:$dest,
I32:$offset, I32:$size),
(outs), (ins i32imm_op:$seg, i32imm_op:$idx),
[(int_wasm_memory_init (i32 imm:$seg), (i32 imm:$idx), I32:$dest,
I32:$offset, I32:$size
)],
"memory.init\t$seg, $idx, $dest, $offset, $size",
"memory.init\t$seg, $idx", 0x08>;

//===----------------------------------------------------------------------===//
// data.drop
//===----------------------------------------------------------------------===//

defm DATA_DROP :
BULK_I<(outs), (ins i32imm_op:$seg), (outs), (ins i32imm_op:$seg),
[(int_wasm_data_drop (i32 imm:$seg))],
"data.drop\t$seg", "data.drop\t$seg", 0x09>;

//===----------------------------------------------------------------------===//
// memory.copy
//===----------------------------------------------------------------------===//

let mayLoad = 1, mayStore = 1 in
defm MEMORY_COPY : BULK_I<(outs), (ins I32:$dst, I32:$src, I32:$len),
(outs), (ins),
[(wasm_memcpy I32:$dst, I32:$src, I32:$len)],
"memory.copy\t$dst, $src, $len",
"memory.copy", 0x0a>;
defm MEMORY_COPY :
BULK_I<(outs), (ins i32imm_op:$src_idx, i32imm_op:$dst_idx,
I32:$dst, I32:$src, I32:$len),
(outs), (ins i32imm_op:$src_idx, i32imm_op:$dst_idx),
[(wasm_memcpy (i32 imm:$src_idx), (i32 imm:$dst_idx),
I32:$dst, I32:$src, I32:$len
)],
"memory.copy\t$src_idx, $dst_idx, $dst, $src, $len",
"memory.copy\t$src_idx, $dst_idx", 0x0a>;
10 changes: 6 additions & 4 deletions llvm/lib/Target/WebAssembly/WebAssemblySelectionDAGInfo.cpp
Expand Up @@ -19,16 +19,18 @@ using namespace llvm;
WebAssemblySelectionDAGInfo::~WebAssemblySelectionDAGInfo() = default; // anchor

SDValue WebAssemblySelectionDAGInfo::EmitTargetCodeForMemcpy(
SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, SDValue Op1, SDValue Op2,
SDValue Op3, unsigned Align, bool IsVolatile, bool AlwaysInline,
SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, SDValue Dst, SDValue Src,
SDValue Size, unsigned Align, bool IsVolatile, bool AlwaysInline,
MachinePointerInfo DstPtrInfo, MachinePointerInfo SrcPtrInfo) const {
if (!DAG.getMachineFunction()
.getSubtarget<WebAssemblySubtarget>()
.hasBulkMemory())
return SDValue();

return DAG.getNode(WebAssemblyISD::MEMORY_COPY, DL, MVT::Other, Chain, Op1,
Op2, Op3);
SDValue MemIdx = DAG.getConstant(0, DL, MVT::i32);
return DAG.getNode(WebAssemblyISD::MEMORY_COPY, DL, MVT::Other,
{Chain, MemIdx, MemIdx, Dst, Src,
DAG.getZExtOrTrunc(Size, DL, MVT::i32)});
}

SDValue WebAssemblySelectionDAGInfo::EmitTargetCodeForMemmove(
Expand Down
28 changes: 28 additions & 0 deletions llvm/test/CodeGen/WebAssembly/bulk-memory-intrinsics.ll
@@ -0,0 +1,28 @@
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+bulk-memory | FileCheck %s

; Test that bulk memory intrinsics lower correctly

target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"

; CHECK-LABEL: memory_init:
; CHECK-NEXT: .functype memory_init (i32, i32, i32) -> ()
; CHECK-NEXT: memory.init 3, 0, $0, $1, $2
; CHECK-NEXT: return
declare void @llvm.wasm.memory.init(i32, i32, i8*, i32, i32)
define void @memory_init(i8* %dest, i32 %offset, i32 %size) {
call void @llvm.wasm.memory.init(
i32 3, i32 0, i8* %dest, i32 %offset, i32 %size
)
ret void
}

; CHECK-LABEL: data_drop:
; CHECK-NEXT: .functype data_drop () -> ()
; CHECK-NEXT: data.drop 3
; CHECK-NEXT: return
declare void @llvm.wasm.data.drop(i32)
define void @data_drop() {
call void @llvm.wasm.data.drop(i32 3)
ret void
}
40 changes: 18 additions & 22 deletions llvm/test/CodeGen/WebAssembly/bulk-memory.ll
Expand Up @@ -6,40 +6,39 @@
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"

declare void @llvm.memcpy.p0i8.p0i8.i8(i8*, i8*, i8, i1)
declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i1)
declare void @llvm.memcpy.p0i32.p0i32.i32(i32*, i32*, i32, i1)

declare void @llvm.memmove.p0i8.p0i8.i8(i8*, i8*, i8, i1)
declare void @llvm.memmove.p0i8.p0i8.i32(i8*, i8*, i32, i1)
declare void @llvm.memmove.p0i32.p0i32.i32(i32*, i32*, i32, i1)

; CHECK-LABEL: memcpy_i8:
; NO-BULK-MEM-NOT: memory.copy
; BULK-MEM-NEXT: .functype memcpy_i8 (i32, i32, i32) -> ()
; BULK-MEM-NEXT: memory.copy $0, $1, $2
; BULK-MEM-NEXT: memory.copy 0, 0, $0, $1, $2
; BULK-MEM-NEXT: return
declare void @llvm.memcpy.p0i8.p0i8.i32(
i8* %dest, i8* %src, i32 %len, i1 %volatile
)
define void @memcpy_i8(i8* %dest, i8* %src, i32 %len) {
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i1 0)
define void @memcpy_i8(i8* %dest, i8* %src, i8 zeroext %len) {
call void @llvm.memcpy.p0i8.p0i8.i8(i8* %dest, i8* %src, i8 %len, i1 0)
ret void
}

; CHECK-LABEL: memmove_i8:
; NO-BULK-MEM-NOT: memory.copy
; BULK-MEM-NEXT: .functype memmove_i8 (i32, i32, i32) -> ()
; BULK-MEM-NEXT: memory.copy $0, $1, $2
; BULK-MEM-NEXT: memory.copy 0, 0, $0, $1, $2
; BULK-MEM-NEXT: return
declare void @llvm.memmove.p0i8.p0i8.i32(
i8* %dest, i8* %src, i32 %len, i1 %volatile
)
define void @memmove_i8(i8* %dest, i8* %src, i32 %len) {
call void @llvm.memmove.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i1 0)
define void @memmove_i8(i8* %dest, i8* %src, i8 zeroext %len) {
call void @llvm.memmove.p0i8.p0i8.i8(i8* %dest, i8* %src, i8 %len, i1 0)
ret void
}

; CHECK-LABEL: memcpy_i32:
; NO-BULK-MEM-NOT: memory.copy
; BULK-MEM-NEXT: .functype memcpy_i32 (i32, i32, i32) -> ()
; BULK-MEM-NEXT: memory.copy $0, $1, $2
; BULK-MEM-NEXT: memory.copy 0, 0, $0, $1, $2
; BULK-MEM-NEXT: return
declare void @llvm.memcpy.p0i32.p0i32.i32(
i32* %dest, i32* %src, i32 %len, i1 %volatile
)
define void @memcpy_i32(i32* %dest, i32* %src, i32 %len) {
call void @llvm.memcpy.p0i32.p0i32.i32(i32* %dest, i32* %src, i32 %len, i1 0)
ret void
Expand All @@ -48,11 +47,8 @@ define void @memcpy_i32(i32* %dest, i32* %src, i32 %len) {
; CHECK-LABEL: memmove_i32:
; NO-BULK-MEM-NOT: memory.copy
; BULK-MEM-NEXT: .functype memmove_i32 (i32, i32, i32) -> ()
; BULK-MEM-NEXT: memory.copy $0, $1, $2
; BULK-MEM-NEXT: memory.copy 0, 0, $0, $1, $2
; BULK-MEM-NEXT: return
declare void @llvm.memmove.p0i32.p0i32.i32(
i32* %dest, i32* %src, i32 %len, i1 %volatile
)
define void @memmove_i32(i32* %dest, i32* %src, i32 %len) {
call void @llvm.memmove.p0i32.p0i32.i32(i32* %dest, i32* %src, i32 %len, i1 0)
ret void
Expand Down Expand Up @@ -82,7 +78,7 @@ define void @memmove_1(i8* %dest, i8* %src) {
; NO-BULK-MEM-NOT: memory.copy
; BULK-MEM-NEXT: .functype memcpy_1024 (i32, i32) -> ()
; BULK-MEM-NEXT: i32.const $push[[L0:[0-9]+]]=, 1024
; BULK-MEM-NEXT: memory.copy $0, $1, $pop[[L0]]
; BULK-MEM-NEXT: memory.copy 0, 0, $0, $1, $pop[[L0]]
; BULK-MEM-NEXT: return
define void @memcpy_1024(i8* %dest, i8* %src) {
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 1024, i1 0)
Expand All @@ -93,7 +89,7 @@ define void @memcpy_1024(i8* %dest, i8* %src) {
; NO-BULK-MEM-NOT: memory.copy
; BULK-MEM-NEXT: .functype memmove_1024 (i32, i32) -> ()
; BULK-MEM-NEXT: i32.const $push[[L0:[0-9]+]]=, 1024
; BULK-MEM-NEXT: memory.copy $0, $1, $pop[[L0]]
; BULK-MEM-NEXT: memory.copy 0, 0, $0, $1, $pop[[L0]]
; BULK-MEM-NEXT: return
define void @memmove_1024(i8* %dest, i8* %src) {
call void @llvm.memmove.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 1024, i1 0)
Expand Down

0 comments on commit de7a0a1

Please sign in to comment.