Index: include/llvm/IR/IntrinsicsWebAssembly.td =================================================================== --- include/llvm/IR/IntrinsicsWebAssembly.td +++ include/llvm/IR/IntrinsicsWebAssembly.td @@ -36,6 +36,17 @@ def int_wasm_current_memory : Intrinsic<[llvm_anyint_ty], [], [IntrReadMem]>; def int_wasm_grow_memory : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>], []>; +//===----------------------------------------------------------------------===// +// Saturating float-to-int conversions +//===----------------------------------------------------------------------===// + +def int_wasm_trunc_saturate_signed : Intrinsic<[llvm_anyint_ty], + [llvm_anyfloat_ty], + [IntrNoMem, IntrSpeculatable]>; +def int_wasm_trunc_saturate_unsigned : Intrinsic<[llvm_anyint_ty], + [llvm_anyfloat_ty], + [IntrNoMem, IntrSpeculatable]>; + //===----------------------------------------------------------------------===// // Exception handling intrinsics //===----------------------------------------------------------------------===// Index: lib/Target/WebAssembly/WebAssemblyISD.def =================================================================== --- lib/Target/WebAssembly/WebAssemblyISD.def +++ lib/Target/WebAssembly/WebAssemblyISD.def @@ -29,5 +29,7 @@ HANDLE_NODETYPE(ADD_SAT_U) HANDLE_NODETYPE(SUB_SAT_S) HANDLE_NODETYPE(SUB_SAT_U) +HANDLE_NODETYPE(TRUNC_SAT_S) +HANDLE_NODETYPE(TRUNC_SAT_U) // add memory opcodes starting at ISD::FIRST_TARGET_MEMORY_OPCODE here... Index: lib/Target/WebAssembly/WebAssemblyISelLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -1004,6 +1004,14 @@ return DAG.getNode(OpCode, DL, Op.getValueType(), Op.getOperand(1)); } + case Intrinsic::wasm_trunc_saturate_signed: + case Intrinsic::wasm_trunc_saturate_unsigned: { + unsigned OpCode = IntNo == Intrinsic::wasm_trunc_saturate_signed + ? WebAssemblyISD::TRUNC_SAT_S + : WebAssemblyISD::TRUNC_SAT_U; + return DAG.getNode(OpCode, DL, Op.getValueType(), Op.getOperand(1)); + } + case Intrinsic::wasm_lsda: // TODO For now, just return 0 not to crash return DAG.getConstant(0, DL, Op.getValueType()); Index: lib/Target/WebAssembly/WebAssemblyInstrConv.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrConv.td +++ lib/Target/WebAssembly/WebAssemblyInstrConv.td @@ -103,6 +103,20 @@ "i64.trunc_u:sat/f64", 0xfc07>, Requires<[HasNontrappingFPToInt]>; +} // defs = [ARGUMENTS] + +// Lower llvm.wasm.trunc.saturate.* to saturating instructions +def : Pat<(WebAssemblytrunc_sat_s F32:$src), (I32_TRUNC_S_SAT_F32 F32:$src)>; +def : Pat<(WebAssemblytrunc_sat_u F32:$src), (I32_TRUNC_U_SAT_F32 F32:$src)>; +def : Pat<(WebAssemblytrunc_sat_s F64:$src), (I32_TRUNC_S_SAT_F64 F64:$src)>; +def : Pat<(WebAssemblytrunc_sat_u F64:$src), (I32_TRUNC_U_SAT_F64 F64:$src)>; +def : Pat<(WebAssemblytrunc_sat_s F32:$src), (I64_TRUNC_S_SAT_F32 F32:$src)>; +def : Pat<(WebAssemblytrunc_sat_u F32:$src), (I64_TRUNC_U_SAT_F32 F32:$src)>; +def : Pat<(WebAssemblytrunc_sat_s F64:$src), (I64_TRUNC_S_SAT_F64 F64:$src)>; +def : Pat<(WebAssemblytrunc_sat_u F64:$src), (I64_TRUNC_U_SAT_F64 F64:$src)>; + +let Defs = [ARGUMENTS] in { + // Conversion from floating point to integer pseudo-instructions which don't // trap on overflow or invalid. let usesCustomInserter = 1, isCodeGenOnly = 1 in { Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -90,6 +90,10 @@ SDT_WebAssemblyReturn, [SDNPHasChain]>; def WebAssemblywrapper : SDNode<"WebAssemblyISD::Wrapper", SDT_WebAssemblyWrapper>; +def WebAssemblytrunc_sat_s : SDNode<"WebAssemblyISD::TRUNC_SAT_S", + SDTFPToIntOp>; +def WebAssemblytrunc_sat_u : SDNode<"WebAssemblyISD::TRUNC_SAT_U", + SDTFPToIntOp>; //===----------------------------------------------------------------------===// // WebAssembly-specific Operands. Index: lib/Target/WebAssembly/WebAssemblyInstrSIMD.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrSIMD.td +++ lib/Target/WebAssembly/WebAssemblyInstrSIMD.td @@ -650,3 +650,13 @@ def : Pat<(v2f64 (build_vector (f64 F64:$x0), (f64 F64:$x1))), (v2f64 (REPLACE_LANE_v2f64 (v2f64 (SPLAT_v2f64 (f64 F64:$x0))), 1, F64:$x1))>; + +// Lower llvm.wasm.trunc.saturate.* to saturating instructions +def : Pat<(v4i32 (WebAssemblytrunc_sat_s (v4f32 V128:$src))), + (fp_to_sint_v4i32_v4f32 (v4f32 V128:$src))>; +def : Pat<(v4i32 (WebAssemblytrunc_sat_u (v4f32 V128:$src))), + (fp_to_uint_v4i32_v4f32 (v4f32 V128:$src))>; +def : Pat<(v2i64 (WebAssemblytrunc_sat_s (v2f64 V128:$src))), + (fp_to_sint_v2i64_v2f64 (v2f64 V128:$src))>; +def : Pat<(v2i64 (WebAssemblytrunc_sat_u (v2f64 V128:$src))), + (fp_to_uint_v2i64_v2f64 (v2f64 V128:$src))>; Index: test/CodeGen/WebAssembly/conv.ll =================================================================== --- test/CodeGen/WebAssembly/conv.ll +++ test/CodeGen/WebAssembly/conv.ll @@ -45,6 +45,17 @@ ret i32 %a } +; CHECK-LABEL: i32_trunc_sat_s_f32: +; CHECK-NEXT: .param f32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.trunc_s:sat/f32 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +declare i32 @llvm.wasm.trunc.saturate.signed.i32.f32(float) +define i32 @i32_trunc_sat_s_f32(float %x) { + %a = call i32 @llvm.wasm.trunc.saturate.signed.i32.f32(float %x) + ret i32 %a +} + ; CHECK-LABEL: i32_trunc_u_f32: ; CHECK-NEXT: .param f32{{$}} ; CHECK-NEXT: .result i32{{$}} @@ -55,6 +66,17 @@ ret i32 %a } +; CHECK-LABEL: i32_trunc_sat_u_f32: +; CHECK-NEXT: .param f32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.trunc_u:sat/f32 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +declare i32 @llvm.wasm.trunc.saturate.unsigned.i32.f32(float) +define i32 @i32_trunc_sat_u_f32(float %x) { + %a = call i32 @llvm.wasm.trunc.saturate.unsigned.i32.f32(float %x) + ret i32 %a +} + ; CHECK-LABEL: i32_trunc_s_f64: ; CHECK-NEXT: .param f64{{$}} ; CHECK-NEXT: .result i32{{$}} @@ -65,6 +87,17 @@ ret i32 %a } +; CHECK-LABEL: i32_trunc_sat_s_f64: +; CHECK-NEXT: .param f64{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.trunc_s:sat/f64 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +declare i32 @llvm.wasm.trunc.saturate.signed.i32.f64(double) +define i32 @i32_trunc_sat_s_f64(double %x) { + %a = call i32 @llvm.wasm.trunc.saturate.signed.i32.f64(double %x) + ret i32 %a +} + ; CHECK-LABEL: i32_trunc_u_f64: ; CHECK-NEXT: .param f64{{$}} ; CHECK-NEXT: .result i32{{$}} @@ -75,6 +108,17 @@ ret i32 %a } +; CHECK-LABEL: i32_trunc_sat_u_f64: +; CHECK-NEXT: .param f64{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: i32.trunc_u:sat/f64 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +declare i32 @llvm.wasm.trunc.saturate.unsigned.i32.f64(double) +define i32 @i32_trunc_sat_u_f64(double %x) { + %a = call i32 @llvm.wasm.trunc.saturate.unsigned.i32.f64(double %x) + ret i32 %a +} + ; CHECK-LABEL: i64_trunc_s_f32: ; CHECK-NEXT: .param f32{{$}} ; CHECK-NEXT: .result i64{{$}} @@ -85,6 +129,17 @@ ret i64 %a } +; CHECK-LABEL: i64_trunc_sat_s_f32: +; CHECK-NEXT: .param f32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.trunc_s:sat/f32 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +declare i64 @llvm.wasm.trunc.saturate.signed.i64.f32(float) +define i64 @i64_trunc_sat_s_f32(float %x) { + %a = call i64 @llvm.wasm.trunc.saturate.signed.i64.f32(float %x) + ret i64 %a +} + ; CHECK-LABEL: i64_trunc_u_f32: ; CHECK-NEXT: .param f32{{$}} ; CHECK-NEXT: .result i64{{$}} @@ -95,6 +150,17 @@ ret i64 %a } +; CHECK-LABEL: i64_trunc_sat_u_f32: +; CHECK-NEXT: .param f32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.trunc_u:sat/f32 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +declare i64 @llvm.wasm.trunc.saturate.unsigned.i64.f32(float) +define i64 @i64_trunc_sat_u_f32(float %x) { + %a = call i64 @llvm.wasm.trunc.saturate.unsigned.i64.f32(float %x) + ret i64 %a +} + ; CHECK-LABEL: i64_trunc_s_f64: ; CHECK-NEXT: .param f64{{$}} ; CHECK-NEXT: .result i64{{$}} @@ -105,6 +171,17 @@ ret i64 %a } +; CHECK-LABEL: i64_trunc_sat_s_f64: +; CHECK-NEXT: .param f64{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.trunc_s:sat/f64 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +declare i64 @llvm.wasm.trunc.saturate.signed.i64.f64(double) +define i64 @i64_trunc_sat_s_f64(double %x) { + %a = call i64 @llvm.wasm.trunc.saturate.signed.i64.f64(double %x) + ret i64 %a +} + ; CHECK-LABEL: i64_trunc_u_f64: ; CHECK-NEXT: .param f64{{$}} ; CHECK-NEXT: .result i64{{$}} @@ -115,6 +192,17 @@ ret i64 %a } +; CHECK-LABEL: i64_trunc_sat_u_f64: +; CHECK-NEXT: .param f64{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: i64.trunc_u:sat/f64 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +declare i64 @llvm.wasm.trunc.saturate.unsigned.i64.f64(double) +define i64 @i64_trunc_sat_u_f64(double %x) { + %a = call i64 @llvm.wasm.trunc.saturate.unsigned.i64.f64(double %x) + ret i64 %a +} + ; CHECK-LABEL: f32_convert_s_i32: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result f32{{$}} Index: test/CodeGen/WebAssembly/simd-intrinsics.ll =================================================================== --- test/CodeGen/WebAssembly/simd-intrinsics.ll +++ test/CodeGen/WebAssembly/simd-intrinsics.ll @@ -226,6 +226,30 @@ ret <4 x i32> %a } +; CHECK-LABEL: trunc_sat_s_v4i32: +; NO-SIMD128-NOT: f32x4 +; SIMD128-NEXT: .param v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: i32x4.trunc_sat_s/f32x4 $push[[R:[0-9]+]]=, $0 +; SIMD128-NEXT: return $pop[[R]] +declare <4 x i32> @llvm.wasm.trunc.saturate.signed.v4i32.v4f32(<4 x float>) +define <4 x i32> @trunc_sat_s_v4i32(<4 x float> %x) { + %a = call <4 x i32> @llvm.wasm.trunc.saturate.signed.v4i32.v4f32(<4 x float> %x) + ret <4 x i32> %a +} + +; CHECK-LABEL: trunc_sat_u_v4i32: +; NO-SIMD128-NOT: f32x4 +; SIMD128-NEXT: .param v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: i32x4.trunc_sat_u/f32x4 $push[[R:[0-9]+]]=, $0 +; SIMD128-NEXT: return $pop[[R]] +declare <4 x i32> @llvm.wasm.trunc.saturate.unsigned.v4i32.v4f32(<4 x float>) +define <4 x i32> @trunc_sat_u_v4i32(<4 x float> %x) { + %a = call <4 x i32> @llvm.wasm.trunc.saturate.unsigned.v4i32.v4f32(<4 x float> %x) + ret <4 x i32> %a +} + ; ============================================================================== ; 2 x i64 ; ============================================================================== @@ -264,6 +288,30 @@ ret <2 x i64> %a } +; CHECK-LABEL: trunc_sat_s_v2i64: +; NO-SIMD128-NOT: f32x4 +; SIMD128-NEXT: .param v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: i64x2.trunc_sat_s/f64x2 $push[[R:[0-9]+]]=, $0 +; SIMD128-NEXT: return $pop[[R]] +declare <2 x i64> @llvm.wasm.trunc.saturate.signed.v2i64.v2f64(<2 x double>) +define <2 x i64> @trunc_sat_s_v2i64(<2 x double> %x) { + %a = call <2 x i64> @llvm.wasm.trunc.saturate.signed.v2i64.v2f64(<2 x double> %x) + ret <2 x i64> %a +} + +; CHECK-LABEL: trunc_sat_u_v2i64: +; NO-SIMD128-NOT: f32x4 +; SIMD128-NEXT: .param v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: i64x2.trunc_sat_u/f64x2 $push[[R:[0-9]+]]=, $0 +; SIMD128-NEXT: return $pop[[R]] +declare <2 x i64> @llvm.wasm.trunc.saturate.unsigned.v2i64.v2f64(<2 x double>) +define <2 x i64> @trunc_sat_u_v2i64(<2 x double> %x) { + %a = call <2 x i64> @llvm.wasm.trunc.saturate.unsigned.v2i64.v2f64(<2 x double> %x) + ret <2 x i64> %a +} + ; ============================================================================== ; 4 x f32 ; ==============================================================================