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/WebAssemblyInstrConv.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrConv.td +++ lib/Target/WebAssembly/WebAssemblyInstrConv.td @@ -97,6 +97,24 @@ "i64.trunc_u:sat/f64", 0xfc07>, Requires<[HasNontrappingFPToInt]>; +// Lower llvm.wasm.trunc.saturate.* to saturating instructions +def : Pat<(int_wasm_trunc_saturate_signed F32:$src), + (I32_TRUNC_S_SAT_F32 F32:$src)>; +def : Pat<(int_wasm_trunc_saturate_unsigned F32:$src), + (I32_TRUNC_U_SAT_F32 F32:$src)>; +def : Pat<(int_wasm_trunc_saturate_signed F64:$src), + (I32_TRUNC_S_SAT_F64 F64:$src)>; +def : Pat<(int_wasm_trunc_saturate_unsigned F64:$src), + (I32_TRUNC_U_SAT_F64 F64:$src)>; +def : Pat<(int_wasm_trunc_saturate_signed F32:$src), + (I64_TRUNC_S_SAT_F32 F32:$src)>; +def : Pat<(int_wasm_trunc_saturate_unsigned F32:$src), + (I64_TRUNC_U_SAT_F32 F32:$src)>; +def : Pat<(int_wasm_trunc_saturate_signed F64:$src), + (I64_TRUNC_S_SAT_F64 F64:$src)>; +def : Pat<(int_wasm_trunc_saturate_unsigned F64:$src), + (I64_TRUNC_U_SAT_F64 F64:$src)>; + // 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/WebAssemblyInstrSIMD.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrSIMD.td +++ lib/Target/WebAssembly/WebAssemblyInstrSIMD.td @@ -782,6 +782,16 @@ defm "" : SIMDConvert; defm "" : SIMDConvert; +// Lower llvm.wasm.trunc.saturate.* to saturating instructions +def : Pat<(v4i32 (int_wasm_trunc_saturate_signed (v4f32 V128:$src))), + (fp_to_sint_v4i32_v4f32 (v4f32 V128:$src))>; +def : Pat<(v4i32 (int_wasm_trunc_saturate_unsigned (v4f32 V128:$src))), + (fp_to_uint_v4i32_v4f32 (v4f32 V128:$src))>; +def : Pat<(v2i64 (int_wasm_trunc_saturate_signed (v2f64 V128:$src))), + (fp_to_sint_v2i64_v2f64 (v2f64 V128:$src))>; +def : Pat<(v2i64 (int_wasm_trunc_saturate_unsigned (v2f64 V128:$src))), + (fp_to_uint_v2i64_v2f64 (v2f64 V128:$src))>; + // Bitcasts are nops // Matching bitcast t1 to t1 causes strange errors, so avoid repeating types foreach t1 = [v16i8, v8i16, v4i32, v2i64, v4f32, v2f64] in 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 ; ==============================================================================