Bug 1731853 - Prototype relaxed-SIMD i32x4.trunc_sat_fXXX instructions. r=lth
authorYury Delendik <ydelendik@mozilla.com>
Tue, 28 Sep 2021 18:14:10 +0000
changeset 593488 b957df6ae6cccddacebb62ecfe457b6018fe1c5f
parent 593487 675ff2c4a1cc1a956131d25d398d562d869e4a94
child 593489 00a0fa29976834589c21cccc74e36d3eb79a3fc9
push id38829
push userctuns@mozilla.com
push dateWed, 29 Sep 2021 09:46:07 +0000
treeherdermozilla-central@fc5e583b2dd7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1731853
milestone94.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1731853 - Prototype relaxed-SIMD i32x4.trunc_sat_fXXX instructions. r=lth See https://github.com/WebAssembly/relaxed-simd/issues/21 Differential Revision: https://phabricator.services.mozilla.com/D126513
js/src/jit-test/lib/wasm-binary.js
js/src/jit-test/tests/wasm/simd/experimental.js
js/src/jit/MacroAssembler.h
js/src/jit/arm64/CodeGenerator-arm64.cpp
js/src/jit/arm64/Lowering-arm64.cpp
js/src/jit/arm64/MacroAssembler-arm64-inl.h
js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
js/src/jit/x86-shared/Lowering-x86-shared.cpp
js/src/jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp
js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
js/src/jit/x86-shared/MacroAssembler-x86-shared.h
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmConstants.h
js/src/wasm/WasmIonCompile.cpp
js/src/wasm/WasmOpIter.cpp
js/src/wasm/WasmValidate.cpp
--- a/js/src/jit-test/lib/wasm-binary.js
+++ b/js/src/jit-test/lib/wasm-binary.js
@@ -136,16 +136,20 @@ const V128Load64ZeroCode = 0xfd;
 const F32x4RelaxedFmaCode = 0xaf;
 const F32x4RelaxedFmsCode = 0xb0;
 const F64x2RelaxedFmaCode = 0xcf;
 const F64x2RelaxedFmsCode = 0xd0;
 const F32x4RelaxedMin = 0xb4;
 const F32x4RelaxedMax = 0xe2;
 const F64x2RelaxedMin = 0xd4;
 const F64x2RelaxedMax = 0xee;
+const I32x4RelaxedTruncSSatF32x4 = 0xa5;
+const I32x4RelaxedTruncUSatF32x4 = 0xa6;
+const I32x4RelaxedTruncSatF64x2SZero = 0xc5;
+const I32x4RelaxedTruncSatF64x2UZero = 0xc6;
 
 // SIMD wormhole opcodes.
 const WORMHOLE_SELFTEST = 0;
 const WORMHOLE_PMADDUBSW = 1;
 const WORMHOLE_PMADDWD = 2;
 
 const FirstInvalidOpcode = 0xc5;
 const LastInvalidOpcode = 0xfa;
--- a/js/src/jit-test/tests/wasm/simd/experimental.js
+++ b/js/src/jit-test/tests/wasm/simd/experimental.js
@@ -181,8 +181,64 @@ for (let k of [4, 2]) {
             memorySection(1),
             exportSection([]),
             bodySection([
                 funcBody({locals:[],
                           body: [...V128StoreExpr(0, [...V128Load(0),
                                                       SimdPrefix, varU32(op)])]})])])));
     }
 }
+
+// Relaxed I32x4.TruncFXXX, https://github.com/WebAssembly/relaxed-simd/issues/21
+
+var ins = wasmValidateAndEval(moduleWithSections([
+    sigSection([v2vSig]),
+    declSection([0, 0, 0, 0]),
+    memorySection(1),
+    exportSection([{funcIndex: 0, name: "from32s"},
+                   {funcIndex: 1, name: "from32u"},
+                   {funcIndex: 2, name: "from64s"},
+                   {funcIndex: 3, name: "from64u"},
+                   {memIndex: 0, name: "mem"}]),
+    bodySection([
+        funcBody({locals:[],
+                  body: [...V128StoreExpr(0, [...V128Load(16),
+                                              SimdPrefix, varU32(I32x4RelaxedTruncSSatF32x4)])]}),
+        funcBody({locals:[],
+                  body: [...V128StoreExpr(0, [...V128Load(16),
+                                              SimdPrefix, varU32(I32x4RelaxedTruncUSatF32x4)])]}),
+        funcBody({locals:[],
+                  body: [...V128StoreExpr(0, [...V128Load(16),
+                                              SimdPrefix, varU32(I32x4RelaxedTruncSatF64x2SZero)])]}),
+        funcBody({locals:[],
+                  body: [...V128StoreExpr(0, [...V128Load(16),
+                                              SimdPrefix, varU32(I32x4RelaxedTruncSatF64x2UZero)])]})])]));
+
+var mem = ins.exports.mem.buffer;
+set(new Float32Array(mem), 4, [0, 2.3, -3.4, 100000]);
+ins.exports.from32s();
+var result = get(new Int32Array(mem), 0, 4);
+assertSame(result, [0, 2, -3, 100000]);
+set(new Float32Array(mem), 4, [0, 3.3, 0x80000000, 200000]);
+ins.exports.from32u();
+var result = get(new Uint32Array(mem), 0, 4);
+assertSame(result, [0, 3, 0x80000000, 200000]);
+
+set(new Float64Array(mem), 2, [200000.3, -3.4]);
+ins.exports.from64s();
+var result = get(new Int32Array(mem), 0, 2);
+assertSame(result, [200000, -3]);
+set(new Float64Array(mem), 2, [0x90000000 + 0.1, 0]);
+ins.exports.from64u();
+var result = get(new Uint32Array(mem), 0, 2);
+assertSame(result, [0x90000000, 0]);
+
+for (let op of [I32x4RelaxedTruncSSatF32x4, I32x4RelaxedTruncUSatF32x4,
+                I32x4RelaxedTruncSatF64x2SZero, I32x4RelaxedTruncSatF64x2UZero]) {
+    assertEq(false, WebAssembly.validate(moduleWithSections([
+        sigSection([v2vSig]),
+        declSection([0]),
+        memorySection(1),
+        exportSection([]),
+        bodySection([
+            funcBody({locals:[],
+                      body: [...V128StoreExpr(0, [SimdPrefix, varU32(op)])]})])])));
+}
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -3310,16 +3310,32 @@ class MacroAssembler : public MacroAssem
                                          FloatRegister temp)
       DEFINED_ON(x86_shared, arm64);
 
   inline void unsignedTruncSatFloat64x2ToInt32x4(FloatRegister src,
                                                  FloatRegister dest,
                                                  FloatRegister temp)
       DEFINED_ON(x86_shared, arm64);
 
+  inline void truncSatFloat32x4ToInt32x4Relaxed(FloatRegister src,
+                                                FloatRegister dest)
+      DEFINED_ON(x86_shared, arm64);
+
+  inline void unsignedTruncSatFloat32x4ToInt32x4Relaxed(FloatRegister src,
+                                                        FloatRegister dest)
+      DEFINED_ON(x86_shared, arm64);
+
+  inline void truncSatFloat64x2ToInt32x4Relaxed(FloatRegister src,
+                                                FloatRegister dest)
+      DEFINED_ON(x86_shared, arm64);
+
+  inline void unsignedTruncSatFloat64x2ToInt32x4Relaxed(FloatRegister src,
+                                                        FloatRegister dest)
+      DEFINED_ON(x86_shared, arm64);
+
   // Floating point narrowing
 
   inline void convertFloat64x2ToFloat32x4(FloatRegister src, FloatRegister dest)
       DEFINED_ON(x86_shared, arm64);
 
   // Floating point widening
 
   inline void convertFloat32x4ToFloat64x2(FloatRegister src, FloatRegister dest)
--- a/js/src/jit/arm64/CodeGenerator-arm64.cpp
+++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp
@@ -3929,16 +3929,28 @@ void CodeGenerator::visitWasmUnarySimd12
       masm.extAddPairwiseInt16x8(src, dest);
       break;
     case wasm::SimdOp::I32x4ExtAddPairwiseI16x8U:
       masm.unsignedExtAddPairwiseInt16x8(src, dest);
       break;
     case wasm::SimdOp::I8x16Popcnt:
       masm.popcntInt8x16(src, dest);
       break;
+    case wasm::SimdOp::I32x4RelaxedTruncSSatF32x4:
+      masm.truncSatFloat32x4ToInt32x4Relaxed(src, dest);
+      break;
+    case wasm::SimdOp::I32x4RelaxedTruncUSatF32x4:
+      masm.unsignedTruncSatFloat32x4ToInt32x4Relaxed(src, dest);
+      break;
+    case wasm::SimdOp::I32x4RelaxedTruncSatF64x2SZero:
+      masm.truncSatFloat64x2ToInt32x4Relaxed(src, dest);
+      break;
+    case wasm::SimdOp::I32x4RelaxedTruncSatF64x2UZero:
+      masm.unsignedTruncSatFloat64x2ToInt32x4Relaxed(src, dest);
+      break;
     default:
       MOZ_CRASH("Unary SimdOp not implemented");
   }
 #else
   MOZ_CRASH("No SIMD");
 #endif
 }
 
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -1278,16 +1278,20 @@ void LIRGenerator::visitWasmUnarySimd128
     case wasm::SimdOp::F64x2PromoteLowF32x4:
     case wasm::SimdOp::F64x2ConvertLowI32x4S:
     case wasm::SimdOp::F64x2ConvertLowI32x4U:
     case wasm::SimdOp::I16x8ExtAddPairwiseI8x16S:
     case wasm::SimdOp::I16x8ExtAddPairwiseI8x16U:
     case wasm::SimdOp::I32x4ExtAddPairwiseI16x8S:
     case wasm::SimdOp::I32x4ExtAddPairwiseI16x8U:
     case wasm::SimdOp::I8x16Popcnt:
+    case wasm::SimdOp::I32x4RelaxedTruncSSatF32x4:
+    case wasm::SimdOp::I32x4RelaxedTruncUSatF32x4:
+    case wasm::SimdOp::I32x4RelaxedTruncSatF64x2SZero:
+    case wasm::SimdOp::I32x4RelaxedTruncSatF64x2UZero:
       break;
     case wasm::SimdOp::I32x4TruncSatF64x2SZero:
     case wasm::SimdOp::I32x4TruncSatF64x2UZero:
       tempReg = tempSimd128();
       break;
     default:
       MOZ_CRASH("Unary SimdOp not implemented");
   }
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -3537,16 +3537,38 @@ void MacroAssembler::truncSatFloat64x2To
 
 void MacroAssembler::unsignedTruncSatFloat64x2ToInt32x4(FloatRegister src,
                                                         FloatRegister dest,
                                                         FloatRegister temp) {
   Fcvtzu(Simd2D(dest), Simd2D(src));
   Uqxtn(Simd2S(dest), Simd2D(dest));
 }
 
+void MacroAssembler::truncSatFloat32x4ToInt32x4Relaxed(FloatRegister src,
+                                                       FloatRegister dest) {
+  Fcvtzs(Simd4S(dest), Simd4S(src));
+}
+
+void MacroAssembler::unsignedTruncSatFloat32x4ToInt32x4Relaxed(
+    FloatRegister src, FloatRegister dest) {
+  Fcvtzu(Simd4S(dest), Simd4S(src));
+}
+
+void MacroAssembler::truncSatFloat64x2ToInt32x4Relaxed(FloatRegister src,
+                                                       FloatRegister dest) {
+  Fcvtzs(Simd2D(dest), Simd2D(src));
+  Sqxtn(Simd2S(dest), Simd2D(dest));
+}
+
+void MacroAssembler::unsignedTruncSatFloat64x2ToInt32x4Relaxed(
+    FloatRegister src, FloatRegister dest) {
+  Fcvtzu(Simd2D(dest), Simd2D(src));
+  Uqxtn(Simd2S(dest), Simd2D(dest));
+}
+
 // Floating point narrowing
 
 void MacroAssembler::convertFloat64x2ToFloat32x4(FloatRegister src,
                                                  FloatRegister dest) {
   Fcvtn(Simd2S(dest), Simd2D(src));
 }
 
 // Floating point widening
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -3613,16 +3613,28 @@ void CodeGenerator::visitWasmUnarySimd12
       masm.unsignedExtAddPairwiseInt8x16(src, dest);
       break;
     case wasm::SimdOp::I32x4ExtAddPairwiseI16x8S:
       masm.extAddPairwiseInt16x8(src, dest);
       break;
     case wasm::SimdOp::I32x4ExtAddPairwiseI16x8U:
       masm.unsignedExtAddPairwiseInt16x8(src, dest);
       break;
+    case wasm::SimdOp::I32x4RelaxedTruncSSatF32x4:
+      masm.truncSatFloat32x4ToInt32x4Relaxed(src, dest);
+      break;
+    case wasm::SimdOp::I32x4RelaxedTruncUSatF32x4:
+      masm.unsignedTruncSatFloat32x4ToInt32x4Relaxed(src, dest);
+      break;
+    case wasm::SimdOp::I32x4RelaxedTruncSatF64x2SZero:
+      masm.truncSatFloat64x2ToInt32x4Relaxed(src, dest);
+      break;
+    case wasm::SimdOp::I32x4RelaxedTruncSatF64x2UZero:
+      masm.unsignedTruncSatFloat64x2ToInt32x4Relaxed(src, dest);
+      break;
     default:
       MOZ_CRASH("Unary SimdOp not implemented");
   }
 #else
   MOZ_CRASH("No SIMD");
 #endif
 }
 
--- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp
@@ -1391,16 +1391,20 @@ void LIRGenerator::visitWasmUnarySimd128
     case wasm::SimdOp::I32x4Abs:
     case wasm::SimdOp::I64x2Abs:
     case wasm::SimdOp::I32x4TruncSSatF32x4:
     case wasm::SimdOp::F32x4ConvertUI32x4:
     case wasm::SimdOp::I16x8ExtAddPairwiseI8x16S:
     case wasm::SimdOp::I16x8ExtAddPairwiseI8x16U:
     case wasm::SimdOp::I32x4ExtAddPairwiseI16x8S:
     case wasm::SimdOp::I32x4ExtAddPairwiseI16x8U:
+    case wasm::SimdOp::I32x4RelaxedTruncSSatF32x4:
+    case wasm::SimdOp::I32x4RelaxedTruncUSatF32x4:
+    case wasm::SimdOp::I32x4RelaxedTruncSatF64x2SZero:
+    case wasm::SimdOp::I32x4RelaxedTruncSatF64x2UZero:
       // Prefer src == dest to avoid an unconditional src->dest move.
       useAtStart = true;
       reuseInput = true;
       break;
     case wasm::SimdOp::I32x4TruncUSatF32x4:
     case wasm::SimdOp::I32x4TruncSatF64x2SZero:
     case wasm::SimdOp::I32x4TruncSatF64x2UZero:
     case wasm::SimdOp::I8x16Popcnt:
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp
@@ -1183,16 +1183,38 @@ void MacroAssemblerX86Shared::unsignedTr
   vcvttps2dq(dest, dest);
 
   // Add temp to the result.  Overflow lanes with 80000000h becomes FFFFFFFFh,
   // biased high-value unsigned lanes become unbiased, everything else is left
   // unchanged.
   vpaddd(Operand(temp), dest, dest);
 }
 
+void MacroAssemblerX86Shared::unsignedTruncSatFloat32x4ToInt32x4Relaxed(
+    FloatRegister src, FloatRegister dest) {
+  ScratchSimd128Scope scratch(asMasm());
+  asMasm().moveSimd128Float(src, dest);
+
+  // Place lanes below 80000000h into dest, otherwise into scratch.
+  // Keep dest or scratch 0 as default.
+  asMasm().loadConstantSimd128Float(SimdConstant::SplatX4(0x4f000000), scratch);
+  vcmpltps(Operand(src), scratch);
+  vpand(Operand(src), scratch, scratch);
+  vpxor(Operand(scratch), dest, dest);
+
+  // Convert lanes below 80000000h into unsigned int without issues.
+  vcvttps2dq(dest, dest);
+  // Knowing IEEE-754 number representation, to convert lanes above
+  // 7FFFFFFFh, just shift left by 7 bits.
+  vpslld(Imm32(7), scratch, scratch);
+
+  // Combine the results.
+  vpaddd(Operand(scratch), dest, dest);
+}
+
 void MacroAssemblerX86Shared::unsignedConvertInt32x4ToFloat64x2(
     FloatRegister src, FloatRegister dest) {
   ScratchSimd128Scope scratch(asMasm());
   vmovaps(src, dest);
 
   asMasm().loadConstantSimd128Float(SimdConstant::SplatX4(0x43300000), scratch);
   vunpcklps(scratch, dest, dest);
 
@@ -1227,16 +1249,30 @@ void MacroAssemblerX86Shared::unsignedTr
   vminpd(Operand(temp), dest, dest);
   vroundpd(SSERoundingMode::Trunc, Operand(dest), dest);
   asMasm().loadConstantSimd128Float(SimdConstant::SplatX2(4503599627370496.0),
                                     temp);
   vaddpd(Operand(temp), dest, dest);
   vshufps(0x88, scratch, dest, dest);
 }
 
+void MacroAssemblerX86Shared::unsignedTruncSatFloat64x2ToInt32x4Relaxed(
+    FloatRegister src, FloatRegister dest) {
+  ScratchSimd128Scope scratch(asMasm());
+  asMasm().moveSimd128Float(src, dest);
+
+  // The same as unsignedConvertInt32x4ToFloat64x2, but without NaN
+  // and out-of-bounds checks.
+  vroundpd(SSERoundingMode::Trunc, Operand(dest), dest);
+  asMasm().loadConstantSimd128Float(SimdConstant::SplatX2(4503599627370496.0),
+                                    scratch);
+  vaddpd(Operand(scratch), dest, dest);
+  vshufps(0x88, scratch, dest, dest);
+}
+
 void MacroAssemblerX86Shared::popcntInt8x16(FloatRegister src,
                                             FloatRegister temp,
                                             FloatRegister output) {
   ScratchSimd128Scope scratch(asMasm());
   asMasm().loadConstantSimd128Float(SimdConstant::SplatX16(0x0f), scratch);
   asMasm().moveSimd128Int(src, temp);
   vpand(scratch, temp, temp);
   vpandn(src, scratch, scratch);
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
@@ -2714,16 +2714,36 @@ void MacroAssembler::truncSatFloat64x2To
 }
 
 void MacroAssembler::unsignedTruncSatFloat64x2ToInt32x4(FloatRegister src,
                                                         FloatRegister dest,
                                                         FloatRegister temp) {
   MacroAssemblerX86Shared::unsignedTruncSatFloat64x2ToInt32x4(src, temp, dest);
 }
 
+void MacroAssembler::truncSatFloat32x4ToInt32x4Relaxed(FloatRegister src,
+                                                       FloatRegister dest) {
+  vcvttps2dq(src, dest);
+}
+
+void MacroAssembler::unsignedTruncSatFloat32x4ToInt32x4Relaxed(
+    FloatRegister src, FloatRegister dest) {
+  MacroAssemblerX86Shared::unsignedTruncSatFloat32x4ToInt32x4Relaxed(src, dest);
+}
+
+void MacroAssembler::truncSatFloat64x2ToInt32x4Relaxed(FloatRegister src,
+                                                       FloatRegister dest) {
+  vcvttpd2dq(src, dest);
+}
+
+void MacroAssembler::unsignedTruncSatFloat64x2ToInt32x4Relaxed(
+    FloatRegister src, FloatRegister dest) {
+  MacroAssemblerX86Shared::unsignedTruncSatFloat64x2ToInt32x4Relaxed(src, dest);
+}
+
 // Floating point widening
 
 void MacroAssembler::convertFloat64x2ToFloat32x4(FloatRegister src,
                                                  FloatRegister dest) {
   vcvtpd2ps(src, dest);
 }
 
 void MacroAssembler::convertFloat32x4ToFloat64x2(FloatRegister src,
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h
@@ -405,20 +405,24 @@ class MacroAssemblerX86Shared : public A
 
   void unsignedConvertInt32x4ToFloat32x4(FloatRegister src, FloatRegister dest);
   void unsignedConvertInt32x4ToFloat64x2(FloatRegister src, FloatRegister dest);
   void bitwiseTestSimd128(const SimdConstant& rhs, FloatRegister lhs);
 
   void truncSatFloat32x4ToInt32x4(FloatRegister src, FloatRegister dest);
   void unsignedTruncSatFloat32x4ToInt32x4(FloatRegister src, FloatRegister temp,
                                           FloatRegister dest);
+  void unsignedTruncSatFloat32x4ToInt32x4Relaxed(FloatRegister src,
+                                                 FloatRegister dest);
   void truncSatFloat64x2ToInt32x4(FloatRegister src, FloatRegister temp,
                                   FloatRegister dest);
   void unsignedTruncSatFloat64x2ToInt32x4(FloatRegister src, FloatRegister temp,
                                           FloatRegister dest);
+  void unsignedTruncSatFloat64x2ToInt32x4Relaxed(FloatRegister src,
+                                                 FloatRegister dest);
 
   void splatX16(Register input, FloatRegister output);
   void splatX8(Register input, FloatRegister output);
   void splatX4(Register input, FloatRegister output);
   void splatX4(FloatRegister input, FloatRegister output);
   void splatX2(FloatRegister input, FloatRegister output);
 
   void extractLaneInt32x4(FloatRegister input, Register output, unsigned lane);
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -7377,16 +7377,36 @@ static void ConvertF64x2ToI32x4(MacroAss
   masm.truncSatFloat64x2ToInt32x4(rs, rd, temp);
 }
 
 static void ConvertF64x2ToUI32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd,
                                  RegV128 temp) {
   masm.unsignedTruncSatFloat64x2ToInt32x4(rs, rd, temp);
 }
 
+static void RelaxedConvertF32x4ToI32x4(MacroAssembler& masm, RegV128 rs,
+                                       RegV128 rd) {
+  masm.truncSatFloat32x4ToInt32x4Relaxed(rs, rd);
+}
+
+static void RelaxedConvertF32x4ToUI32x4(MacroAssembler& masm, RegV128 rs,
+                                        RegV128 rd) {
+  masm.unsignedTruncSatFloat32x4ToInt32x4Relaxed(rs, rd);
+}
+
+static void RelaxedConvertF64x2ToI32x4(MacroAssembler& masm, RegV128 rs,
+                                       RegV128 rd) {
+  masm.truncSatFloat64x2ToInt32x4Relaxed(rs, rd);
+}
+
+static void RelaxedConvertF64x2ToUI32x4(MacroAssembler& masm, RegV128 rs,
+                                        RegV128 rd) {
+  masm.unsignedTruncSatFloat64x2ToInt32x4Relaxed(rs, rd);
+}
+
 static void DemoteF64x2ToF32x4(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
   masm.convertFloat64x2ToFloat32x4(rs, rd);
 }
 
 static void PromoteF32x4ToF64x2(MacroAssembler& masm, RegV128 rs, RegV128 rd) {
   masm.convertFloat32x4ToFloat64x2(rs, rd);
 }
 
@@ -9079,16 +9099,36 @@ bool BaseCompiler::emitBody() {
               return iter_.unrecognizedOpcode(&op);
             }
             CHECK_NEXT(dispatchVectorBinary(RelaxedMinF64x2));
           case uint32_t(SimdOp::F64x2RelaxedMax):
             if (!moduleEnv_.v128RelaxedEnabled()) {
               return iter_.unrecognizedOpcode(&op);
             }
             CHECK_NEXT(dispatchVectorBinary(RelaxedMaxF64x2));
+          case uint32_t(SimdOp::I32x4RelaxedTruncSSatF32x4):
+            if (!moduleEnv_.v128RelaxedEnabled()) {
+              return iter_.unrecognizedOpcode(&op);
+            }
+            CHECK_NEXT(dispatchVectorUnary(RelaxedConvertF32x4ToI32x4));
+          case uint32_t(SimdOp::I32x4RelaxedTruncUSatF32x4):
+            if (!moduleEnv_.v128RelaxedEnabled()) {
+              return iter_.unrecognizedOpcode(&op);
+            }
+            CHECK_NEXT(dispatchVectorUnary(RelaxedConvertF32x4ToUI32x4));
+          case uint32_t(SimdOp::I32x4RelaxedTruncSatF64x2SZero):
+            if (!moduleEnv_.v128RelaxedEnabled()) {
+              return iter_.unrecognizedOpcode(&op);
+            }
+            CHECK_NEXT(dispatchVectorUnary(RelaxedConvertF64x2ToI32x4));
+          case uint32_t(SimdOp::I32x4RelaxedTruncSatF64x2UZero):
+            if (!moduleEnv_.v128RelaxedEnabled()) {
+              return iter_.unrecognizedOpcode(&op);
+            }
+            CHECK_NEXT(dispatchVectorUnary(RelaxedConvertF64x2ToUI32x4));
 #  endif
           default:
             break;
         }  // switch (op.b1)
         return iter_.unrecognizedOpcode(&op);
       }
 #endif  // ENABLE_WASM_SIMD
 
--- a/js/src/wasm/WasmConstants.h
+++ b/js/src/wasm/WasmConstants.h
@@ -663,18 +663,18 @@ enum class SimdOp {
   I16x8ExtMulHighSI8x16 = 0x9d,
   I16x8ExtMulLowUI8x16 = 0x9e,
   I16x8ExtMulHighUI8x16 = 0x9f,
   I32x4Abs = 0xa0,
   I32x4Neg = 0xa1,
   // Narrow = 0xa2
   I32x4AllTrue = 0xa3,
   I32x4Bitmask = 0xa4,
-  // Narrow = 0xa5
-  // Narrow = 0xa6
+  I32x4RelaxedTruncSSatF32x4 = 0xa5,
+  I32x4RelaxedTruncUSatF32x4 = 0xa6,
   I32x4WidenLowSI16x8 = 0xa7,
   I32x4WidenHighSI16x8 = 0xa8,
   I32x4WidenLowUI16x8 = 0xa9,
   I32x4WidenHighUI16x8 = 0xaa,
   I32x4Shl = 0xab,
   I32x4ShrS = 0xac,
   I32x4ShrU = 0xad,
   I32x4Add = 0xae,
@@ -695,18 +695,18 @@ enum class SimdOp {
   I32x4ExtMulHighSI16x8 = 0xbd,
   I32x4ExtMulLowUI16x8 = 0xbe,
   I32x4ExtMulHighUI16x8 = 0xbf,
   I64x2Abs = 0xc0,
   I64x2Neg = 0xc1,
   // AnyTrue = 0xc2
   I64x2AllTrue = 0xc3,
   I64x2Bitmask = 0xc4,
-  // Narrow = 0xc5
-  // Narrow = 0xc6
+  I32x4RelaxedTruncSatF64x2SZero = 0xc5,
+  I32x4RelaxedTruncSatF64x2UZero = 0xc6,
   I64x2WidenLowSI32x4 = 0xc7,
   I64x2WidenHighSI32x4 = 0xc8,
   I64x2WidenLowUI32x4 = 0xc9,
   I64x2WidenHighUI32x4 = 0xca,
   I64x2Shl = 0xcb,
   I64x2ShrS = 0xcc,
   I64x2ShrU = 0xcd,
   I64x2Add = 0xce,
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -5467,16 +5467,25 @@ static bool EmitBodyExprs(FunctionCompil
           case uint32_t(SimdOp::F32x4RelaxedMax):
           case uint32_t(SimdOp::F64x2RelaxedMin):
           case uint32_t(SimdOp::F64x2RelaxedMax): {
             if (!f.moduleEnv().v128RelaxedEnabled()) {
               return f.iter().unrecognizedOpcode(&op);
             }
             CHECK(EmitBinarySimd128(f, /* commutative= */ true, SimdOp(op.b1)));
           }
+          case uint32_t(SimdOp::I32x4RelaxedTruncSSatF32x4):
+          case uint32_t(SimdOp::I32x4RelaxedTruncUSatF32x4):
+          case uint32_t(SimdOp::I32x4RelaxedTruncSatF64x2SZero):
+          case uint32_t(SimdOp::I32x4RelaxedTruncSatF64x2UZero): {
+            if (!f.moduleEnv().v128RelaxedEnabled()) {
+              return f.iter().unrecognizedOpcode(&op);
+            }
+            CHECK(EmitUnarySimd128(f, SimdOp(op.b1)));
+          }
 #  endif
 
           default:
             return f.iter().unrecognizedOpcode(&op);
         }  // switch (op.b1)
         break;
       }
 #endif
--- a/js/src/wasm/WasmOpIter.cpp
+++ b/js/src/wasm/WasmOpIter.cpp
@@ -546,16 +546,20 @@ OpKind wasm::Classify(OpBytes op) {
         case SimdOp::F64x2ConvertLowI32x4S:
         case SimdOp::F64x2ConvertLowI32x4U:
         case SimdOp::I32x4TruncSatF64x2SZero:
         case SimdOp::I32x4TruncSatF64x2UZero:
         case SimdOp::I16x8ExtAddPairwiseI8x16S:
         case SimdOp::I16x8ExtAddPairwiseI8x16U:
         case SimdOp::I32x4ExtAddPairwiseI16x8S:
         case SimdOp::I32x4ExtAddPairwiseI16x8U:
+        case SimdOp::I32x4RelaxedTruncSSatF32x4:
+        case SimdOp::I32x4RelaxedTruncUSatF32x4:
+        case SimdOp::I32x4RelaxedTruncSatF64x2SZero:
+        case SimdOp::I32x4RelaxedTruncSatF64x2UZero:
           WASM_SIMD_OP(OpKind::Unary);
         case SimdOp::I8x16Shl:
         case SimdOp::I8x16ShrS:
         case SimdOp::I8x16ShrU:
         case SimdOp::I16x8Shl:
         case SimdOp::I16x8ShrS:
         case SimdOp::I16x8ShrU:
         case SimdOp::I32x4Shl:
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -1021,16 +1021,25 @@ static bool DecodeFunctionBodyExprs(cons
           case uint32_t(SimdOp::F32x4RelaxedMax):
           case uint32_t(SimdOp::F64x2RelaxedMin):
           case uint32_t(SimdOp::F64x2RelaxedMax): {
             if (!env.v128RelaxedEnabled()) {
               return iter.unrecognizedOpcode(&op);
             }
             CHECK(iter.readBinary(ValType::V128, &nothing, &nothing));
           }
+          case uint32_t(SimdOp::I32x4RelaxedTruncSSatF32x4):
+          case uint32_t(SimdOp::I32x4RelaxedTruncUSatF32x4):
+          case uint32_t(SimdOp::I32x4RelaxedTruncSatF64x2SZero):
+          case uint32_t(SimdOp::I32x4RelaxedTruncSatF64x2UZero): {
+            if (!env.v128RelaxedEnabled()) {
+              return iter.unrecognizedOpcode(&op);
+            }
+            CHECK(iter.readUnary(ValType::V128, &nothing));
+          }
 #  endif
 
           default:
             return iter.unrecognizedOpcode(&op);
         }
         break;
       }
 #endif  // ENABLE_WASM_SIMD