From 70285a03596946657b49fed5ded6558050bb6e5b Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 5 Oct 2018 00:45:20 +0000 Subject: [PATCH] [WebAssembly] Saturating arithmetic intrinsics Summary: Depends on D52805. Reviewers: aheejin, dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D52813 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@343833 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/IR/IntrinsicsWebAssembly.td | 16 ++++ lib/Target/WebAssembly/WebAssemblyISD.def | 4 + lib/Target/WebAssembly/WebAssemblyISelLowering.cpp | 26 ++++++ lib/Target/WebAssembly/WebAssemblyInstrSIMD.td | 15 +++ test/CodeGen/WebAssembly/simd-arith.ll | 8 +- test/CodeGen/WebAssembly/simd-intrinsics.ll | 104 +++++++++++++++++++++ test/MC/WebAssembly/simd-encodings.s | 24 +++++ 7 files changed, 193 insertions(+), 4 deletions(-) diff --git a/include/llvm/IR/IntrinsicsWebAssembly.td b/include/llvm/IR/IntrinsicsWebAssembly.td index 2189d9ef27f..54408d317d2 100644 --- a/include/llvm/IR/IntrinsicsWebAssembly.td +++ b/include/llvm/IR/IntrinsicsWebAssembly.td @@ -91,6 +91,22 @@ def int_wasm_atomic_notify: // SIMD intrinsics //===----------------------------------------------------------------------===// +def int_wasm_add_saturate_signed : + Intrinsic<[llvm_anyvector_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem, IntrSpeculatable]>; +def int_wasm_add_saturate_unsigned : + Intrinsic<[llvm_anyvector_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem, IntrSpeculatable]>; +def int_wasm_sub_saturate_signed : + Intrinsic<[llvm_anyvector_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem, IntrSpeculatable]>; +def int_wasm_sub_saturate_unsigned : + Intrinsic<[llvm_anyvector_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem, IntrSpeculatable]>; def int_wasm_bitselect : Intrinsic<[llvm_anyvector_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], diff --git a/lib/Target/WebAssembly/WebAssemblyISD.def b/lib/Target/WebAssembly/WebAssemblyISD.def index 9e1d198b079..f326d37944f 100644 --- a/lib/Target/WebAssembly/WebAssemblyISD.def +++ b/lib/Target/WebAssembly/WebAssemblyISD.def @@ -25,5 +25,9 @@ HANDLE_NODETYPE(SHUFFLE) HANDLE_NODETYPE(ANYTRUE) HANDLE_NODETYPE(ALLTRUE) HANDLE_NODETYPE(BITSELECT) +HANDLE_NODETYPE(ADD_SAT_S) +HANDLE_NODETYPE(ADD_SAT_U) +HANDLE_NODETYPE(SUB_SAT_S) +HANDLE_NODETYPE(SUB_SAT_U) // add memory opcodes starting at ISD::FIRST_TARGET_MEMORY_OPCODE here... diff --git a/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index d5dcbf1d699..4ecbf6d7487 100644 --- a/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -966,6 +966,32 @@ WebAssemblyTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, default: return {}; // Don't custom lower most intrinsics. + case Intrinsic::wasm_add_saturate_signed: + case Intrinsic::wasm_add_saturate_unsigned: + case Intrinsic::wasm_sub_saturate_signed: + case Intrinsic::wasm_sub_saturate_unsigned: { + unsigned OpCode; + switch (IntNo) { + case Intrinsic::wasm_add_saturate_signed: + OpCode = WebAssemblyISD::ADD_SAT_S; + break; + case Intrinsic::wasm_add_saturate_unsigned: + OpCode = WebAssemblyISD::ADD_SAT_U; + break; + case Intrinsic::wasm_sub_saturate_signed: + OpCode = WebAssemblyISD::SUB_SAT_S; + break; + case Intrinsic::wasm_sub_saturate_unsigned: + OpCode = WebAssemblyISD::SUB_SAT_U; + break; + default: + llvm_unreachable("unexpected intrinsic id"); + break; + } + return DAG.getNode(OpCode, DL, Op.getValueType(), Op.getOperand(1), + Op.getOperand(2)); + } + case Intrinsic::wasm_bitselect: return DAG.getNode(WebAssemblyISD::BITSELECT, DL, Op.getValueType(), Op.getOperand(1), Op.getOperand(2), Op.getOperand(3)); diff --git a/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td b/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td index bf5582a11f0..55bac7971d7 100644 --- a/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td +++ b/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td @@ -20,11 +20,18 @@ def LaneIdx#SIZE : ImmLeaf; // Custom nodes for custom operations def wasm_shuffle_t : SDTypeProfile<1, 18, []>; +def wasm_saturate_t : SDTypeProfile<1, 2, + [SDTCisVec<0>, SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>] +>; def wasm_bitselect_t : SDTypeProfile<1, 3, [SDTCisVec<0>, SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, SDTCisSameAs<0, 3>] >; def wasm_reduce_t : SDTypeProfile<1, 1, [SDTCisVT<0, i32>, SDTCisVec<1>]>; def wasm_shuffle : SDNode<"WebAssemblyISD::SHUFFLE", wasm_shuffle_t>; +def wasm_add_sat_s : SDNode<"WebAssemblyISD::ADD_SAT_S", wasm_saturate_t>; +def wasm_add_sat_u : SDNode<"WebAssemblyISD::ADD_SAT_U", wasm_saturate_t>; +def wasm_sub_sat_s : SDNode<"WebAssemblyISD::SUB_SAT_S", wasm_saturate_t>; +def wasm_sub_sat_u : SDNode<"WebAssemblyISD::SUB_SAT_U", wasm_saturate_t>; def wasm_bitselect : SDNode<"WebAssemblyISD::BITSELECT", wasm_bitselect_t>; def wasm_anytrue : SDNode<"WebAssemblyISD::ANYTRUE", wasm_reduce_t>; def wasm_alltrue : SDNode<"WebAssemblyISD::ALLTRUE", wasm_reduce_t>; @@ -139,6 +146,10 @@ multiclass SIMDBinaryFP baseInst> { defm "" : SIMDBinary; defm "" : SIMDBinary; } +multiclass SIMDBinarySat baseInst> { + defm "" : SIMDBinary; + defm "" : SIMDBinary; +} multiclass SIMDShift simdop> { defm _#vec_t : SIMD_I<(outs V128:$dst), (ins V128:$vec, I32:$x), @@ -366,10 +377,14 @@ defm ADD : SIMDBinaryInt; defm ADD : SIMDBinaryFP; defm MUL : SIMDBinaryIntNoI64x2; defm MUL : SIMDBinaryFP; +defm ADD_SAT_S : SIMDBinarySat; +defm ADD_SAT_U : SIMDBinarySat; } // isCommutable = 1 defm SUB : SIMDBinaryInt; defm SUB : SIMDBinaryFP; +defm SUB_SAT_S : SIMDBinarySat; +defm SUB_SAT_U : SIMDBinarySat; defm DIV : SIMDBinaryFP; defm "" : SIMDNegInt; diff --git a/test/CodeGen/WebAssembly/simd-arith.ll b/test/CodeGen/WebAssembly/simd-arith.ll index 317b87fd692..9b496226598 100644 --- a/test/CodeGen/WebAssembly/simd-arith.ll +++ b/test/CodeGen/WebAssembly/simd-arith.ll @@ -1,9 +1,9 @@ ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -wasm-enable-unimplemented-simd -mattr=+simd128 | FileCheck %s --check-prefixes CHECK,SIMD128,SIMD128-SLOW ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -wasm-enable-unimplemented-simd -mattr=+simd128 -fast-isel | FileCheck %s --check-prefixes CHECK,SIMD128,SIMD128-FAST -; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+simd128 | FileCheck %s --check-prefixes CHECK,SIMD128-VM,SIMD128-VM-SLOW -; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+simd128 -fast-isel | FileCheck %s --check-prefixes CHECK,SIMD128-VM,SIMD128-VM-FAST -; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=-simd128 | FileCheck %s --check-prefixes CHECK,NO-SIMD128,NO-SIMD128-SLOW -; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=-simd128 -fast-isel | FileCheck %s --check-prefixes CHECK,NO-SIMD128,NO-SIMD128-FAST +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+simd128 | FileCheck %s --check-prefixes CHECK,SIMD128-VM +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+simd128 -fast-isel | FileCheck %s --check-prefixes CHECK,SIMD128-VM +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=-simd128 | FileCheck %s --check-prefixes CHECK,NO-SIMD128 +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=-simd128 -fast-isel | FileCheck %s --check-prefixes CHECK,NO-SIMD128 ; Test that basic SIMD128 arithmetic operations assemble as expected. diff --git a/test/CodeGen/WebAssembly/simd-intrinsics.ll b/test/CodeGen/WebAssembly/simd-intrinsics.ll index 1c693164305..f9f4eb0cf9e 100644 --- a/test/CodeGen/WebAssembly/simd-intrinsics.ll +++ b/test/CodeGen/WebAssembly/simd-intrinsics.ll @@ -11,6 +11,58 @@ target triple = "wasm32-unknown-unknown" ; ============================================================================== ; 16 x i8 ; ============================================================================== +; CHECK-LABEL: add_sat_s_v16i8: +; SIMD128-NEXT: .param v128, v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: i8x16.add_saturate_s $push[[R:[0-9]+]]=, $0, $1{{$}} +; SIMD128-NEXT: return $pop[[R]]{{$}} +declare <16 x i8> @llvm.wasm.add.saturate.signed.v16i8(<16 x i8>, <16 x i8>) +define <16 x i8> @add_sat_s_v16i8(<16 x i8> %x, <16 x i8> %y) { + %a = call <16 x i8> @llvm.wasm.add.saturate.signed.v16i8( + <16 x i8> %x, <16 x i8> %y + ) + ret <16 x i8> %a +} + +; CHECK-LABEL: add_sat_u_v16i8: +; SIMD128-NEXT: .param v128, v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: i8x16.add_saturate_u $push[[R:[0-9]+]]=, $0, $1{{$}} +; SIMD128-NEXT: return $pop[[R]]{{$}} +declare <16 x i8> @llvm.wasm.add.saturate.unsigned.v16i8(<16 x i8>, <16 x i8>) +define <16 x i8> @add_sat_u_v16i8(<16 x i8> %x, <16 x i8> %y) { + %a = call <16 x i8> @llvm.wasm.add.saturate.unsigned.v16i8( + <16 x i8> %x, <16 x i8> %y + ) + ret <16 x i8> %a +} + +; CHECK-LABEL: sub_sat_s_v16i8: +; SIMD128-NEXT: .param v128, v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: i8x16.sub_saturate_s $push[[R:[0-9]+]]=, $0, $1{{$}} +; SIMD128-NEXT: return $pop[[R]]{{$}} +declare <16 x i8> @llvm.wasm.sub.saturate.signed.v16i8(<16 x i8>, <16 x i8>) +define <16 x i8> @sub_sat_s_v16i8(<16 x i8> %x, <16 x i8> %y) { + %a = call <16 x i8> @llvm.wasm.sub.saturate.signed.v16i8( + <16 x i8> %x, <16 x i8> %y + ) + ret <16 x i8> %a +} + +; CHECK-LABEL: sub_sat_u_v16i8: +; SIMD128-NEXT: .param v128, v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: i8x16.sub_saturate_u $push[[R:[0-9]+]]=, $0, $1{{$}} +; SIMD128-NEXT: return $pop[[R]]{{$}} +declare <16 x i8> @llvm.wasm.sub.saturate.unsigned.v16i8(<16 x i8>, <16 x i8>) +define <16 x i8> @sub_sat_u_v16i8(<16 x i8> %x, <16 x i8> %y) { + %a = call <16 x i8> @llvm.wasm.sub.saturate.unsigned.v16i8( + <16 x i8> %x, <16 x i8> %y + ) + ret <16 x i8> %a +} + ; CHECK-LABEL: any_v16i8: ; SIMD128-NEXT: .param v128{{$}} ; SIMD128-NEXT: .result i32{{$}} @@ -49,6 +101,58 @@ define <16 x i8> @bitselect_v16i8(<16 x i8> %c, <16 x i8> %v1, <16 x i8> %v2) { ; ============================================================================== ; 8 x i16 ; ============================================================================== +; CHECK-LABEL: add_sat_s_v8i16: +; SIMD128-NEXT: .param v128, v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: i16x8.add_saturate_s $push[[R:[0-9]+]]=, $0, $1{{$}} +; SIMD128-NEXT: return $pop[[R]]{{$}} +declare <8 x i16> @llvm.wasm.add.saturate.signed.v8i16(<8 x i16>, <8 x i16>) +define <8 x i16> @add_sat_s_v8i16(<8 x i16> %x, <8 x i16> %y) { + %a = call <8 x i16> @llvm.wasm.add.saturate.signed.v8i16( + <8 x i16> %x, <8 x i16> %y + ) + ret <8 x i16> %a +} + +; CHECK-LABEL: add_sat_u_v8i16: +; SIMD128-NEXT: .param v128, v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: i16x8.add_saturate_u $push[[R:[0-9]+]]=, $0, $1{{$}} +; SIMD128-NEXT: return $pop[[R]]{{$}} +declare <8 x i16> @llvm.wasm.add.saturate.unsigned.v8i16(<8 x i16>, <8 x i16>) +define <8 x i16> @add_sat_u_v8i16(<8 x i16> %x, <8 x i16> %y) { + %a = call <8 x i16> @llvm.wasm.add.saturate.unsigned.v8i16( + <8 x i16> %x, <8 x i16> %y + ) + ret <8 x i16> %a +} + +; CHECK-LABEL: sub_sat_s_v8i16: +; SIMD128-NEXT: .param v128, v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: i16x8.sub_saturate_s $push[[R:[0-9]+]]=, $0, $1{{$}} +; SIMD128-NEXT: return $pop[[R]]{{$}} +declare <8 x i16> @llvm.wasm.sub.saturate.signed.v8i16(<8 x i16>, <8 x i16>) +define <8 x i16> @sub_sat_s_v8i16(<8 x i16> %x, <8 x i16> %y) { + %a = call <8 x i16> @llvm.wasm.sub.saturate.signed.v8i16( + <8 x i16> %x, <8 x i16> %y + ) + ret <8 x i16> %a +} + +; CHECK-LABEL: sub_sat_u_v8i16: +; SIMD128-NEXT: .param v128, v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: i16x8.sub_saturate_u $push[[R:[0-9]+]]=, $0, $1{{$}} +; SIMD128-NEXT: return $pop[[R]]{{$}} +declare <8 x i16> @llvm.wasm.sub.saturate.unsigned.v8i16(<8 x i16>, <8 x i16>) +define <8 x i16> @sub_sat_u_v8i16(<8 x i16> %x, <8 x i16> %y) { + %a = call <8 x i16> @llvm.wasm.sub.saturate.unsigned.v8i16( + <8 x i16> %x, <8 x i16> %y + ) + ret <8 x i16> %a +} + ; CHECK-LABEL: any_v8i16: ; SIMD128-NEXT: .param v128{{$}} ; SIMD128-NEXT: .result i32{{$}} diff --git a/test/MC/WebAssembly/simd-encodings.s b/test/MC/WebAssembly/simd-encodings.s index a8c9b6df445..02d07674c16 100644 --- a/test/MC/WebAssembly/simd-encodings.s +++ b/test/MC/WebAssembly/simd-encodings.s @@ -145,6 +145,30 @@ # CHECK: i64x2.neg # encoding: [0xfd,0x27] i64x2.neg + # CHECK: i8x16.add_saturate_s # encoding: [0xfd,0x28] + i8x16.add_saturate_s + + # CHECK: i8x16.add_saturate_u # encoding: [0xfd,0x29] + i8x16.add_saturate_u + + # CHECK: i16x8.add_saturate_s # encoding: [0xfd,0x2a] + i16x8.add_saturate_s + + # CHECK: i16x8.add_saturate_u # encoding: [0xfd,0x2b] + i16x8.add_saturate_u + + # CHECK: i8x16.sub_saturate_s # encoding: [0xfd,0x2c] + i8x16.sub_saturate_s + + # CHECK: i8x16.sub_saturate_u # encoding: [0xfd,0x2d] + i8x16.sub_saturate_u + + # CHECK: i16x8.sub_saturate_s # encoding: [0xfd,0x2e] + i16x8.sub_saturate_s + + # CHECK: i16x8.sub_saturate_u # encoding: [0xfd,0x2f] + i16x8.sub_saturate_u + # CHECK: i8x16.shl # encoding: [0xfd,0x30] i8x16.shl -- 2.11.0