Bug 1389461 - WebAssembly sign extension opcodes. r=bbouvier
authorLars T Hansen <lhansen@mozilla.com>
Fri, 11 Aug 2017 16:24:51 +0200
changeset 375969 b755618d7c138728721a13e7481c63035e7aec4d
parent 375968 3777a6f18c8ab930a8b4d81c4f5e74dc0bc5a89c
child 375970 9bf5648598b0c49cb6083edd2ca3eb259cc31de7
push id32370
push userkwierso@gmail.com
push dateMon, 21 Aug 2017 23:40:30 +0000
treeherdermozilla-central@128a79130ecd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1389461
milestone57.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 1389461 - WebAssembly sign extension opcodes. r=bbouvier
js/src/jit-test/lib/wasm-binary.js
js/src/jit-test/tests/wasm/conversion.js
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/Recover.cpp
js/src/jit/Recover.h
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/arm/CodeGenerator-arm.h
js/src/jit/arm/Lowering-arm.cpp
js/src/jit/arm/Lowering-arm.h
js/src/jit/arm64/Lowering-arm64.cpp
js/src/jit/arm64/Lowering-arm64.h
js/src/jit/mips-shared/Lowering-mips-shared.cpp
js/src/jit/mips-shared/Lowering-mips-shared.h
js/src/jit/none/Lowering-none.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
js/src/jit/x64/Assembler-x64.h
js/src/jit/x64/BaseAssembler-x64.h
js/src/jit/x64/CodeGenerator-x64.cpp
js/src/jit/x64/CodeGenerator-x64.h
js/src/jit/x64/Lowering-x64.cpp
js/src/jit/x64/Lowering-x64.h
js/src/jit/x86/CodeGenerator-x86.cpp
js/src/jit/x86/CodeGenerator-x86.h
js/src/jit/x86/Lowering-x86.cpp
js/src/jit/x86/Lowering-x86.h
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmBinaryConstants.h
js/src/wasm/WasmBinaryIterator.cpp
js/src/wasm/WasmBinaryToAST.cpp
js/src/wasm/WasmBinaryToText.cpp
js/src/wasm/WasmIonCompile.cpp
js/src/wasm/WasmTextToBinary.cpp
js/src/wasm/WasmValidate.cpp
--- a/js/src/jit-test/lib/wasm-binary.js
+++ b/js/src/jit-test/lib/wasm-binary.js
@@ -85,17 +85,17 @@ const I32TruncSF32Code = 0xa8;
 const I32TruncUF32Code = 0xa9;
 const I32TruncSF64Code = 0xaa;
 const I32TruncUF64Code = 0xab;
 const I64TruncSF32Code = 0xae;
 const I64TruncUF32Code = 0xaf;
 const I64TruncSF64Code = 0xb0;
 const I64TruncUF64Code = 0xb1;
 
-const FirstInvalidOpcode = 0xc0;
+const FirstInvalidOpcode = wasmThreadsSupported() ? 0xc5 : 0xc0;
 const LastInvalidOpcode = 0xfd;
 const AtomicPrefix = 0xfe;
 const MozPrefix = 0xff;
 
 // DefinitionKind
 const FunctionCode     = 0x00;
 const TableCode        = 0x01;
 const MemoryCode       = 0x02;
--- a/js/src/jit-test/tests/wasm/conversion.js
+++ b/js/src/jit-test/tests/wasm/conversion.js
@@ -1,50 +1,64 @@
-function testConversion(resultType, opcode, paramType, op, expect) {
-  if (paramType === 'i64') {
+function testConversion0(resultType, opcode, paramType, op, expect) {
+  function rectify(v, t) {
+    if (t == 'i64')
+      return createI64(v);
+    return v;
+  }
+  if (paramType === 'i64' || resultType == 'i64') {
+    let fullPass = resultType == 'i64' ? wasmFullPassI64 : wasmFullPass;
     // i64 cannot be imported, so we use a wrapper function.
-    wasmFullPass(`(module
-                    (func (param i64) (result ${resultType}) (${resultType}.${opcode}/i64 (get_local 0)))
-                    (export "run" 0))`, expect, {}, createI64(op));
+    fullPass(`(module
+               (func (param ${paramType}) (result ${resultType})
+		(${opcode} (get_local 0)))
+               (export "run" 0))`,
+	     rectify(expect, resultType), {}, rectify(op, paramType));
     // The same, but now the input is a constant.
-    wasmFullPass(`(module
-                    (func (result ${resultType}) (${resultType}.${opcode}/i64 (i64.const ${op})))
-                    (export "run" 0))`, expect);
-  } else if (resultType === 'i64') {
-    wasmFullPassI64(`(module
-                        (func (param ${paramType}) (result i64) (i64.${opcode}/${paramType} (get_local 0)))
-                        (export "run" 0))`, createI64(expect), {}, op);
-    // The same, but now the input is a constant.
-    wasmFullPassI64(`(module
-                        (func (result i64) (i64.${opcode}/${paramType} (${paramType}.const ${op})))
-                        (export "run" 0))`, createI64(expect));
+    fullPass(`(module
+               (func (result ${resultType})
+		(${opcode} (${paramType}.const ${op})))
+               (export "run" 0))`,
+	     rectify(expect, resultType));
   } else {
-    wasmFullPass('(module (func (param ' + paramType + ') (result ' + resultType + ') (' + resultType + '.' + opcode + '/' + paramType + ' (get_local 0))) (export "run" 0))', expect, {}, op);
+      wasmFullPass(`(module
+		     (func (param ${paramType}) (result ${resultType})
+		      (${opcode} (get_local 0)))
+		     (export "run" 0))`,
+		   expect, {}, op);
   }
 
   let formerTestMode = getJitCompilerOptions()['wasm.test-mode'];
   setJitCompilerOption('wasm.test-mode', 1);
   for (var bad of ['i32', 'f32', 'f64', 'i64']) {
       if (bad != resultType) {
           wasmFailValidateText(
-              `(module (func (param ${paramType}) (result ${bad}) (${resultType}.${opcode}/${paramType} (get_local 0))))`,
+              `(module (func (param ${paramType}) (result ${bad}) (${opcode} (get_local 0))))`,
               mismatchError(resultType, bad)
           );
       }
 
       if (bad != paramType) {
           wasmFailValidateText(
-              `(module (func (param ${bad}) (result ${resultType}) (${resultType}.${opcode}/${paramType} (get_local 0))))`,
+              `(module (func (param ${bad}) (result ${resultType}) (${opcode} (get_local 0))))`,
               mismatchError(bad, paramType)
           );
       }
   }
   setJitCompilerOption('wasm.test-mode', formerTestMode);
 }
 
+function testConversion(resultType, opcode, paramType, op, expect) {
+  testConversion0(resultType, `${resultType}.${opcode}/${paramType}`, paramType, op, expect);
+}
+
+function testSignExtension(resultType, opcode, paramType, op, expect) {
+  testConversion0(resultType, `${resultType}.${opcode}`, paramType, op, expect);
+}
+
 function testTrap(resultType, opcode, paramType, op, expect) {
     let func = wasmEvalText(`(module
         (func
             (param ${paramType})
             (result ${resultType})
             (${resultType}.${opcode}/${paramType} (get_local 0))
         )
         (export "" 0)
@@ -215,16 +229,29 @@ function testTrap(resultType, opcode, pa
     testTrap('i64', 'trunc_u', 'f32', -1);
     testTrap('i64', 'trunc_u', 'f32', "nan");
     testTrap('i64', 'trunc_u', 'f32', "infinity");
     testTrap('i64', 'trunc_u', 'f32', "-infinity");
 
     testConversion('i64', 'reinterpret', 'f64', 40.09999999999968, "0x40440ccccccccca0");
     testConversion('f64', 'reinterpret', 'i64', "0x40440ccccccccca0", 40.09999999999968);
 
+    if (wasmThreadsSupported()) {
+	testSignExtension('i32', 'extend8_s', 'i32', 0x7F, 0x7F);
+	testSignExtension('i32', 'extend8_s', 'i32', 0x80, -0x80);
+	testSignExtension('i32', 'extend16_s', 'i32', 0x7FFF, 0x7FFF);
+	testSignExtension('i32', 'extend16_s', 'i32', 0x8000, -0x8000);
+    	testSignExtension('i64', 'extend8_s', 'i64', 0x7F, 0x7F);
+	testSignExtension('i64', 'extend8_s', 'i64', 0x80, -0x80);
+	testSignExtension('i64', 'extend16_s', 'i64', 0x7FFF, 0x7FFF);
+	testSignExtension('i64', 'extend16_s', 'i64', 0x8000, -0x8000);
+	testSignExtension('i64', 'extend32_s', 'i64', 0x7FFFFFFF, 0x7FFFFFFF);
+	testSignExtension('i64', 'extend32_s', 'i64', "0x80000000", "0xFFFFFFFF80000000");
+    }
+
     setJitCompilerOption('wasm.test-mode', 0);
 }
 
 // i32.trunc_s* : all values in ] -2**31 - 1; 2**31 [ are acceptable.
 // f32:
 var p = Math.pow;
 testConversion('i32', 'trunc_s', 'f32', 40.1, 40);
 testConversion('i32', 'trunc_s', 'f32', p(2, 31) - 128, p(2, 31) - 128); // last f32 value exactly representable < 2**31.
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -12784,26 +12784,26 @@ CodeGenerator::visitRandom(LRandom* ins)
     else
         masm.convertUInt64ToDouble(s1Reg, output, Register::Invalid());
 
     // output *= ScaleInv
     masm.mulDoublePtr(ImmPtr(&ScaleInv), tempReg, output);
 }
 
 void
-CodeGenerator::visitSignExtend(LSignExtend* ins)
+CodeGenerator::visitSignExtendInt32(LSignExtendInt32* ins)
 {
     Register input = ToRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     switch (ins->mode()) {
-      case MSignExtend::Byte:
+      case MSignExtendInt32::Byte:
         masm.move8SignExtend(input, output);
         break;
-      case MSignExtend::Half:
+      case MSignExtendInt32::Half:
         masm.move16SignExtend(input, output);
         break;
     }
 }
 
 void
 CodeGenerator::visitRotate(LRotate* ins)
 {
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -454,17 +454,17 @@ class CodeGenerator final : public CodeG
     void visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit* ins);
     void visitWasmTrap(LWasmTrap* lir);
     void visitWasmLoadTls(LWasmLoadTls* ins);
     void visitWasmBoundsCheck(LWasmBoundsCheck* ins);
     void visitRecompileCheck(LRecompileCheck* ins);
     void visitRotate(LRotate* ins);
 
     void visitRandom(LRandom* ins);
-    void visitSignExtend(LSignExtend* ins);
+    void visitSignExtendInt32(LSignExtendInt32* ins);
 
 #ifdef DEBUG
     void emitDebugForceBailing(LInstruction* lir);
 #endif
 
     IonScriptCounts* extractScriptCounts() {
         IonScriptCounts* counts = scriptCounts_;
         scriptCounts_ = nullptr;  // prevent delete in dtor
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1356,24 +1356,24 @@ LIRGenerator::visitRsh(MRsh* ins)
 
 void
 LIRGenerator::visitUrsh(MUrsh* ins)
 {
     lowerShiftOp(JSOP_URSH, ins);
 }
 
 void
-LIRGenerator::visitSignExtend(MSignExtend* ins)
+LIRGenerator::visitSignExtendInt32(MSignExtendInt32* ins)
 {
     LInstructionHelper<1, 1, 0>* lir;
 
-    if (ins->mode() == MSignExtend::Byte)
-        lir = new(alloc()) LSignExtend(useByteOpRegisterAtStart(ins->input()), ins->mode());
+    if (ins->mode() == MSignExtendInt32::Byte)
+        lir = new(alloc()) LSignExtendInt32(useByteOpRegisterAtStart(ins->input()), ins->mode());
     else
-        lir = new(alloc()) LSignExtend(useRegisterAtStart(ins->input()), ins->mode());
+        lir = new(alloc()) LSignExtendInt32(useRegisterAtStart(ins->input()), ins->mode());
 
     define(lir, ins);
 }
 
 void
 LIRGenerator::visitRotate(MRotate* ins)
 {
     MDefinition* input = ins->input();
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -126,17 +126,17 @@ class LIRGenerator : public LIRGenerator
     void visitToId(MToId* ins);
     void visitBitNot(MBitNot* ins);
     void visitBitAnd(MBitAnd* ins);
     void visitBitOr(MBitOr* ins);
     void visitBitXor(MBitXor* ins);
     void visitLsh(MLsh* ins);
     void visitRsh(MRsh* ins);
     void visitUrsh(MUrsh* ins);
-    void visitSignExtend(MSignExtend* ins);
+    void visitSignExtendInt32(MSignExtendInt32* ins);
     void visitRotate(MRotate* ins);
     void visitFloor(MFloor* ins);
     void visitCeil(MCeil* ins);
     void visitRound(MRound* ins);
     void visitNearbyInt(MNearbyInt* ins);
     void visitMinMax(MMinMax* ins);
     void visitAbs(MAbs* ins);
     void visitClz(MClz* ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -3160,19 +3160,19 @@ MRsh::foldsTo(TempAllocator& alloc)
 
     uint32_t shift = rhs->toConstant()->toInt32();
     uint32_t shift_lhs = lhs->getOperand(1)->toConstant()->toInt32();
     if (shift != shift_lhs)
         return this;
 
     switch (shift) {
       case 16:
-        return MSignExtend::New(alloc, lhs->getOperand(0), MSignExtend::Half);
+        return MSignExtendInt32::New(alloc, lhs->getOperand(0), MSignExtendInt32::Half);
       case 24:
-        return MSignExtend::New(alloc, lhs->getOperand(0), MSignExtend::Byte);
+        return MSignExtendInt32::New(alloc, lhs->getOperand(0), MSignExtendInt32::Byte);
     }
 
     return this;
 }
 
 MDefinition*
 MBinaryArithInstruction::foldsTo(TempAllocator& alloc)
 {
@@ -4375,16 +4375,51 @@ MExtendInt32ToInt64::foldsTo(TempAllocat
         int64_t res = isUnsigned() ? int64_t(uint32_t(c)) : int64_t(c);
         return MConstant::NewInt64(alloc, res);
     }
 
     return this;
 }
 
 MDefinition*
+MSignExtendInt32::foldsTo(TempAllocator& alloc)
+{
+    MDefinition* input = this->input();
+    if (input->isConstant()) {
+        int32_t c = input->toConstant()->toInt32();
+        int32_t res;
+        switch (mode_) {
+          case Byte: res = int32_t(int8_t(c & 0xFF)); break;
+          case Half: res = int32_t(int16_t(c & 0xFFFF)); break;
+        }
+        return MConstant::New(alloc, Int32Value(res));
+    }
+
+    return this;
+}
+
+MDefinition*
+MSignExtendInt64::foldsTo(TempAllocator& alloc)
+{
+    MDefinition* input = this->input();
+    if (input->isConstant()) {
+        int64_t c = input->toConstant()->toInt64();
+        int64_t res;
+        switch (mode_) {
+          case Byte: res = int64_t(int8_t(c & 0xFF)); break;
+          case Half: res = int64_t(int16_t(c & 0xFFFF)); break;
+          case Word: res = int64_t(int32_t(c & 0xFFFFFFFFU)); break;
+        }
+        return MConstant::NewInt64(alloc, res);
+    }
+
+    return this;
+}
+
+MDefinition*
 MToDouble::foldsTo(TempAllocator& alloc)
 {
     MDefinition* input = getOperand(0);
     if (input->isBox())
         input = input->getOperand(0);
 
     if (input->type() == MIRType::Double)
         return input;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -6248,48 +6248,98 @@ class MUrsh : public MShiftInstruction
     MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return specialization_ < MIRType::Object;
     }
 
     ALLOW_CLONE(MUrsh)
 };
 
-class MSignExtend
+class MSignExtendInt32
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
   public:
     enum Mode {
         Byte,
         Half
     };
 
   private:
     Mode mode_;
 
-    MSignExtend(MDefinition* op, Mode mode)
+    MSignExtendInt32(MDefinition* op, Mode mode)
       : MUnaryInstruction(op), mode_(mode)
     {
         setResultType(MIRType::Int32);
         setMovable();
     }
 
   public:
-    INSTRUCTION_HEADER(SignExtend)
-    TRIVIAL_NEW_WRAPPERS
-
-    Mode mode() { return mode_; }
+    INSTRUCTION_HEADER(SignExtendInt32)
+    TRIVIAL_NEW_WRAPPERS
+
+    Mode mode() const { return mode_; }
+
+    MDefinition* foldsTo(TempAllocator& alloc) override;
+    bool congruentTo(const MDefinition* ins) const override {
+        if (!congruentIfOperandsEqual(ins))
+            return false;
+        return ins->isSignExtendInt32() && ins->toSignExtendInt32()->mode_ == mode_;
+    }
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
 
     MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return true;
     }
 
-    ALLOW_CLONE(MSignExtend)
+    ALLOW_CLONE(MSignExtendInt32)
+};
+
+class MSignExtendInt64
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
+{
+  public:
+    enum Mode {
+        Byte,
+        Half,
+        Word
+    };
+
+  private:
+    Mode mode_;
+
+    MSignExtendInt64(MDefinition* op, Mode mode)
+      : MUnaryInstruction(op), mode_(mode)
+    {
+        setResultType(MIRType::Int64);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(SignExtendInt64)
+    TRIVIAL_NEW_WRAPPERS
+
+    Mode mode() const { return mode_; }
+
+    MDefinition* foldsTo(TempAllocator& alloc) override;
+    bool congruentTo(const MDefinition* ins) const override {
+        if (!congruentIfOperandsEqual(ins))
+            return false;
+        return ins->isSignExtendInt64() && ins->toSignExtendInt64()->mode_ == mode_;
+    }
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+
+    ALLOW_CLONE(MSignExtendInt64)
 };
 
 class MBinaryArithInstruction
   : public MBinaryInstruction,
     public ArithPolicy::Data
 {
     // Implicit truncate flag is set by the truncate backward range analysis
     // optimization phase, and by wasm pre-processing. It is used in
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -83,17 +83,18 @@ namespace jit {
     _(ToAsyncIter)                                                          \
     _(ToId)                                                                 \
     _(BitAnd)                                                               \
     _(BitOr)                                                                \
     _(BitXor)                                                               \
     _(Lsh)                                                                  \
     _(Rsh)                                                                  \
     _(Ursh)                                                                 \
-    _(SignExtend)                                                           \
+    _(SignExtendInt32)                                                      \
+    _(SignExtendInt64)                                                      \
     _(MinMax)                                                               \
     _(Abs)                                                                  \
     _(Clz)                                                                  \
     _(Ctz)                                                                  \
     _(Popcnt)                                                               \
     _(Sqrt)                                                                 \
     _(Atan2)                                                                \
     _(Hypot)                                                                \
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -332,42 +332,42 @@ RUrsh::recover(JSContext* cx, SnapshotIt
     if (!js::UrshOperation(cx, lhs, rhs, &result))
         return false;
 
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
-MSignExtend::writeRecoverData(CompactBufferWriter& writer) const
+MSignExtendInt32::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
-    writer.writeUnsigned(uint32_t(RInstruction::Recover_SignExtend));
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_SignExtendInt32));
     MOZ_ASSERT(Mode(uint8_t(mode_)) == mode_);
     writer.writeByte(uint8_t(mode_));
     return true;
 }
 
-RSignExtend::RSignExtend(CompactBufferReader& reader)
+RSignExtendInt32::RSignExtendInt32(CompactBufferReader& reader)
 {
     mode_ = reader.readByte();
 }
 
 bool
-RSignExtend::recover(JSContext* cx, SnapshotIterator& iter) const
+RSignExtendInt32::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedValue operand(cx, iter.read());
 
     int32_t result;
-    switch (MSignExtend::Mode(mode_)) {
-      case MSignExtend::Byte:
+    switch (MSignExtendInt32::Mode(mode_)) {
+      case MSignExtendInt32::Byte:
         if (!js::SignExtendOperation<int8_t>(cx, operand, &result))
             return false;
         break;
-      case MSignExtend::Half:
+      case MSignExtendInt32::Half:
         if (!js::SignExtendOperation<int16_t>(cx, operand, &result))
             return false;
         break;
     }
 
     RootedValue rootedResult(cx, js::Int32Value(result));
     iter.storeInstructionResult(rootedResult);
     return true;
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -60,17 +60,17 @@ namespace jit {
     _(ResumePoint)                              \
     _(BitNot)                                   \
     _(BitAnd)                                   \
     _(BitOr)                                    \
     _(BitXor)                                   \
     _(Lsh)                                      \
     _(Rsh)                                      \
     _(Ursh)                                     \
-    _(SignExtend)                               \
+    _(SignExtendInt32)                          \
     _(Add)                                      \
     _(Sub)                                      \
     _(Mul)                                      \
     _(Div)                                      \
     _(Mod)                                      \
     _(Not)                                      \
     _(Concat)                                   \
     _(StringLength)                             \
@@ -253,23 +253,23 @@ class RRsh final : public RInstruction
 class RUrsh final : public RInstruction
 {
   public:
     RINSTRUCTION_HEADER_NUM_OP_(Ursh, 2)
 
     MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
 };
 
-class RSignExtend final : public RInstruction
+class RSignExtendInt32 final : public RInstruction
 {
   private:
     uint8_t mode_;
 
   public:
-    RINSTRUCTION_HEADER_NUM_OP_(SignExtend, 1)
+    RINSTRUCTION_HEADER_NUM_OP_(SignExtendInt32, 1)
 
     MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
 };
 
 class RAdd final : public RInstruction
 {
   private:
     bool isFloatOperation_;
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -3086,16 +3086,34 @@ CodeGeneratorARM::visitExtendInt32ToInt6
     MOZ_ASSERT(ToRegister(lir->input()) == output.low);
 
     if (lir->mir()->isUnsigned())
         masm.ma_mov(Imm32(0), output.high);
     else
         masm.ma_asr(Imm32(31), output.low, output.high);
 }
 
+void
+CodeGeneratorARM::visitSignExtendInt64(LSignExtendInt64* lir)
+{
+    Register64 input = ToRegister64(lir->getInt64Operand(0));
+    Register64 output = ToOutRegister64(lir);
+    switch (lir->mode()) {
+      case MSignExtendInt64::Byte:
+        masm.move8SignExtend(input.low, output.low);
+        break;
+      case MSignExtendInt64::Half:
+        masm.move16SignExtend(input.low, output.low);
+        break;
+      case MSignExtendInt64::Word:
+        break;
+    }
+    masm.ma_asr(Imm32(31), output.low, output.high);
+}
+
 static Register
 WasmGetTemporaryForDivOrMod(Register64 lhs, Register64 rhs)
 {
     MOZ_ASSERT(IsCompilingWasm());
 
     // All inputs are useAtStart for a call instruction. As a result we cannot
     // ask the register allocator for a non-aliasing temp.
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -164,16 +164,17 @@ class CodeGeneratorARM : public CodeGene
     virtual void visitCeilF(LCeilF* lir);
     virtual void visitRound(LRound* lir);
     virtual void visitRoundF(LRoundF* lir);
     virtual void visitTruncateDToInt32(LTruncateDToInt32* ins);
     virtual void visitTruncateFToInt32(LTruncateFToInt32* ins);
 
     virtual void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
     virtual void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
+    virtual void visitSignExtendInt64(LSignExtendInt64* ins);
     virtual void visitAddI64(LAddI64* lir);
     virtual void visitSubI64(LSubI64* lir);
     virtual void visitMulI64(LMulI64* lir);
     virtual void visitDivOrModI64(LDivOrModI64* lir);
     virtual void visitUDivOrModI64(LUDivOrModI64* lir);
     virtual void visitCompareI64(LCompareI64* lir);
     virtual void visitCompareI64AndBranch(LCompareI64AndBranch* lir);
     virtual void visitBitOpI64(LBitOpI64* lir);
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -1052,8 +1052,14 @@ LIRGeneratorARM::visitExtendInt32ToInt64
     defineInt64(lir, ins);
 
     LDefinition def(LDefinition::GENERAL, LDefinition::MUST_REUSE_INPUT);
     def.setReusedInput(0);
     def.setVirtualRegister(ins->virtualRegister());
 
     lir->setDef(0, def);
 }
+
+void
+LIRGeneratorARM::visitSignExtendInt64(MSignExtendInt64* ins)
+{
+    defineInt64(new(alloc()) LSignExtendInt64(useInt64RegisterAtStart(ins->input())), ins);
+}
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -117,16 +117,17 @@ class LIRGeneratorARM : public LIRGenera
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
     void visitRandom(MRandom* ins);
     void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
     void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
     void visitCopySign(MCopySign* ins);
     void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
+    void visitSignExtendInt64(MSignExtendInt64* ins);
 };
 
 typedef LIRGeneratorARM LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_Lowering_arm_h */
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -362,8 +362,14 @@ LIRGeneratorARM64::visitCopySign(MCopySi
     MOZ_CRASH("NY");
 }
 
 void
 LIRGeneratorARM64::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
 {
     MOZ_CRASH("NYI");
 }
+
+void
+LIRGeneratorARM64::visitSignExtendInt64(MSignExtendInt64* ins)
+{
+    MOZ_CRASH("NYI");
+}
--- a/js/src/jit/arm64/Lowering-arm64.h
+++ b/js/src/jit/arm64/Lowering-arm64.h
@@ -117,16 +117,17 @@ class LIRGeneratorARM64 : public LIRGene
     void visitSubstr(MSubstr* ins);
     void visitRandom(MRandom* ins);
     void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
     void visitWasmLoad(MWasmLoad* ins);
     void visitWasmStore(MWasmStore* ins);
     void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
     void visitCopySign(MCopySign* ins);
     void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
+    void visitSignExtendInt64(MSignExtendInt64* ins);
 };
 
 typedef LIRGeneratorARM64 LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm64_Lowering_arm64_h */
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -746,8 +746,14 @@ LIRGeneratorMIPSShared::visitCopySign(MC
     defineReuseInput(lir, ins, 0);
 }
 
 void
 LIRGeneratorMIPSShared::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
 {
     defineInt64(new(alloc()) LExtendInt32ToInt64(useRegisterAtStart(ins->input())), ins);
 }
+
+void
+LIRGeneratorMIPSShared::visitSignExtendInt64(MSignExtendInt64* ins)
+{
+    MOZ_CRASH("NYI : SignExtendInt64");
+}
--- a/js/src/jit/mips-shared/Lowering-mips-shared.h
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.h
@@ -94,15 +94,16 @@ class LIRGeneratorMIPSShared : public LI
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
     void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
     void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
     void visitCopySign(MCopySign* ins);
     void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
+    void visitSignExtendInt64(MSignExtendInt64* ins);
 
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_shared_Lowering_mips_shared_h */
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -103,16 +103,17 @@ class LIRGeneratorNone : public LIRGener
     void visitSubstr(MSubstr*) { MOZ_CRASH(); }
     void visitSimdBinaryArith(js::jit::MSimdBinaryArith*) { MOZ_CRASH(); }
     void visitSimdBinarySaturating(MSimdBinarySaturating* ins) { MOZ_CRASH(); }
     void visitRandom(js::jit::MRandom*) { MOZ_CRASH(); }
     void visitCopySign(js::jit::MCopySign*) { MOZ_CRASH(); }
     void visitWasmTruncateToInt64(MWasmTruncateToInt64*) { MOZ_CRASH(); }
     void visitInt64ToFloatingPoint(MInt64ToFloatingPoint*) { MOZ_CRASH(); }
     void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins) { MOZ_CRASH(); }
+    void visitSignExtendInt64(MSignExtendInt64* ins) { MOZ_CRASH(); }
 };
 
 typedef LIRGeneratorNone LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_none_Lowering_none_h */
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -3359,29 +3359,44 @@ class LShiftI64 : public LInstructionHel
     }
 
     const char* extraName() const {
         return CodeName[op_];
     }
 };
 
 // Sign extension
-class LSignExtend : public LInstructionHelper<1, 1, 0>
-{
-    MSignExtend::Mode mode_;
-
-  public:
-    LIR_HEADER(SignExtend);
-    explicit LSignExtend(const LAllocation& num, MSignExtend::Mode mode)
+class LSignExtendInt32 : public LInstructionHelper<1, 1, 0>
+{
+    MSignExtendInt32::Mode mode_;
+
+  public:
+    LIR_HEADER(SignExtendInt32);
+    explicit LSignExtendInt32(const LAllocation& num, MSignExtendInt32::Mode mode)
       : mode_(mode)
     {
         setOperand(0, num);
     }
 
-    MSignExtend::Mode mode() { return mode_; }
+    MSignExtendInt32::Mode mode() { return mode_; }
+};
+
+class LSignExtendInt64 : public LInstructionHelper<INT64_PIECES, INT64_PIECES, 0>
+{
+  public:
+    LIR_HEADER(SignExtendInt64)
+    explicit LSignExtendInt64(const LInt64Allocation& input) {
+        setInt64Operand(0, input);
+    }
+
+    const MSignExtendInt64* mir() const {
+        return mir_->toSignExtendInt64();
+    }
+
+    MSignExtendInt64::Mode mode() const { return mir()->mode(); }
 };
 
 class LUrshD : public LBinaryMath<1>
 {
   public:
     LIR_HEADER(UrshD)
 
     LUrshD(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) {
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -107,17 +107,18 @@
     _(ComputeThis)                  \
     _(BitNotI)                      \
     _(BitNotV)                      \
     _(BitOpI)                       \
     _(BitOpI64)                     \
     _(BitOpV)                       \
     _(ShiftI)                       \
     _(ShiftI64)                     \
-    _(SignExtend)                   \
+    _(SignExtendInt32)              \
+    _(SignExtendInt64)              \
     _(UrshD)                        \
     _(Return)                       \
     _(Throw)                        \
     _(Phi)                          \
     _(TestIAndBranch)               \
     _(TestI64AndBranch)             \
     _(TestDAndBranch)               \
     _(TestFAndBranch)               \
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -473,16 +473,19 @@ class Assembler : public AssemblerX86Sha
     }
 
     void xchgq(Register src, Register dest) {
         masm.xchgq_rr(src.encoding(), dest.encoding());
     }
 
     void movsbq(const Operand& src, Register dest) {
         switch (src.kind()) {
+          case Operand::REG:
+            masm.movsbq_rr(src.reg(), dest.encoding());
+            break;
           case Operand::MEM_REG_DISP:
             masm.movsbq_mr(src.disp(), src.base(), dest.encoding());
             break;
           case Operand::MEM_SCALE:
             masm.movsbq_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
@@ -492,16 +495,19 @@ class Assembler : public AssemblerX86Sha
     void movzbq(const Operand& src, Register dest) {
         // movzbl zero-extends to 64 bits and is one byte smaller, so use that
         // instead.
         movzbl(src, dest);
     }
 
     void movswq(const Operand& src, Register dest) {
         switch (src.kind()) {
+          case Operand::REG:
+            masm.movswq_rr(src.reg(), dest.encoding());
+            break;
           case Operand::MEM_REG_DISP:
             masm.movswq_mr(src.disp(), src.base(), dest.encoding());
             break;
           case Operand::MEM_SCALE:
             masm.movswq_mr(src.disp(), src.base(), src.index(), src.scale(), dest.encoding());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
--- a/js/src/jit/x64/BaseAssembler-x64.h
+++ b/js/src/jit/x64/BaseAssembler-x64.h
@@ -659,27 +659,37 @@ class BaseAssemblerX64 : public BaseAsse
 
     void movq_i64r(int64_t imm, RegisterID dst)
     {
         spew("movabsq    $0x%" PRIx64 ", %s", imm, GPReg64Name(dst));
         m_formatter.oneByteOp64(OP_MOV_EAXIv, dst);
         m_formatter.immediate64(imm);
     }
 
+    void movsbq_rr(RegisterID src, RegisterID dst)
+    {
+        spew("movsbq     %s, %s", GPReg32Name(src), GPReg64Name(dst));
+        m_formatter.twoByteOp64(OP2_MOVSX_GvEb, src, dst);
+    }
     void movsbq_mr(int32_t offset, RegisterID base, RegisterID dst)
     {
         spew("movsbq     " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
         m_formatter.twoByteOp64(OP2_MOVSX_GvEb, offset, base, dst);
     }
     void movsbq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
         spew("movsbq     " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst));
         m_formatter.twoByteOp64(OP2_MOVSX_GvEb, offset, base, index, scale, dst);
     }
 
+    void movswq_rr(RegisterID src, RegisterID dst)
+    {
+        spew("movswq     %s, %s", GPReg32Name(src), GPReg64Name(dst));
+        m_formatter.twoByteOp64(OP2_MOVSX_GvEw, src, dst);
+    }
     void movswq_mr(int32_t offset, RegisterID base, RegisterID dst)
     {
         spew("movswq     " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
         m_formatter.twoByteOp64(OP2_MOVSX_GvEw, offset, base, dst);
     }
     void movswq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
         spew("movswq     " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst));
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -676,16 +676,34 @@ CodeGeneratorX64::visitExtendInt32ToInt6
 
     if (lir->mir()->isUnsigned())
         masm.movl(ToOperand(input), output);
     else
         masm.movslq(ToOperand(input), output);
 }
 
 void
+CodeGeneratorX64::visitSignExtendInt64(LSignExtendInt64* ins)
+{
+    Register64 input = ToRegister64(ins->getInt64Operand(0));
+    Register64 output = ToOutRegister64(ins);
+    switch (ins->mode()) {
+      case MSignExtendInt64::Byte:
+        masm.movsbq(Operand(input.reg), output.reg);
+        break;
+      case MSignExtendInt64::Half:
+        masm.movswq(Operand(input.reg), output.reg);
+        break;
+      case MSignExtendInt64::Word:
+        masm.movslq(Operand(input.reg), output.reg);
+        break;
+    }
+}
+
+void
 CodeGeneratorX64::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
     Register64 output = ToOutRegister64(lir);
 
     MWasmTruncateToInt64* mir = lir->mir();
     MIRType inputType = mir->input()->type();
 
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -48,16 +48,17 @@ class CodeGeneratorX64 : public CodeGene
     void visitUDivOrModI64(LUDivOrModI64* lir);
     void visitNotI64(LNotI64* lir);
     void visitClzI64(LClzI64* lir);
     void visitCtzI64(LCtzI64* lir);
     void visitTruncateDToInt32(LTruncateDToInt32* ins);
     void visitTruncateFToInt32(LTruncateFToInt32* ins);
     void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
     void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
+    void visitSignExtendInt64(LSignExtendInt64* ins);
     void visitWasmTruncateToInt64(LWasmTruncateToInt64* lir);
     void visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir);
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitWasmLoad(LWasmLoad* ins);
     void visitWasmLoadI64(LWasmLoadI64* ins);
     void visitWasmStore(LWasmStore* ins);
     void visitWasmStoreI64(LWasmStoreI64* ins);
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -490,8 +490,14 @@ LIRGeneratorX64::visitInt64ToFloatingPoi
     define(new(alloc()) LInt64ToFloatingPoint(useInt64Register(opd), maybeTemp), ins);
 }
 
 void
 LIRGeneratorX64::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
 {
     defineInt64(new(alloc()) LExtendInt32ToInt64(useAtStart(ins->input())), ins);
 }
+
+void
+LIRGeneratorX64::visitSignExtendInt64(MSignExtendInt64* ins)
+{
+    defineInt64(new(alloc()) LSignExtendInt64(useInt64RegisterAtStart(ins->input())), ins);
+}
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -65,16 +65,17 @@ class LIRGeneratorX64 : public LIRGenera
     void visitWasmLoad(MWasmLoad* ins);
     void visitWasmStore(MWasmStore* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitSubstr(MSubstr* ins);
     void visitRandom(MRandom* ins);
     void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
     void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
     void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
+    void visitSignExtendInt64(MSignExtendInt64* ins);
 };
 
 typedef LIRGeneratorX64 LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_Lowering_x64_h */
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -1050,16 +1050,38 @@ CodeGeneratorX86::visitExtendInt32ToInt6
         MOZ_ASSERT(output.low == input);
         MOZ_ASSERT(output.low == eax);
         MOZ_ASSERT(output.high == edx);
         masm.cdq();
     }
 }
 
 void
+CodeGeneratorX86::visitSignExtendInt64(LSignExtendInt64* lir)
+{
+    Register64 input = ToRegister64(lir->getInt64Operand(0));
+    Register64 output = ToOutRegister64(lir);
+    MOZ_ASSERT(input.low == eax);
+    MOZ_ASSERT(output.low == eax);
+    MOZ_ASSERT(input.high == edx);
+    MOZ_ASSERT(output.high == edx);
+    switch (lir->mode()) {
+      case MSignExtendInt64::Byte:
+        masm.move8SignExtend(eax, eax);
+        break;
+      case MSignExtendInt64::Half:
+        masm.move16SignExtend(eax, eax);
+        break;
+      case MSignExtendInt64::Word:
+        break;
+    }
+    masm.cdq();
+}
+
+void
 CodeGeneratorX86::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir)
 {
     const LInt64Allocation& input = lir->getInt64Operand(0);
     Register output = ToRegister(lir->output());
 
     if (lir->mir()->bottomHalf())
         masm.movl(ToRegister(input.low()), output);
     else
--- a/js/src/jit/x86/CodeGenerator-x86.h
+++ b/js/src/jit/x86/CodeGenerator-x86.h
@@ -66,16 +66,17 @@ class CodeGeneratorX86 : public CodeGene
     void visitCompareI64(LCompareI64* lir);
     void visitCompareI64AndBranch(LCompareI64AndBranch* lir);
     void visitDivOrModI64(LDivOrModI64* lir);
     void visitUDivOrModI64(LUDivOrModI64* lir);
     void visitWasmSelectI64(LWasmSelectI64* lir);
     void visitWasmReinterpretFromI64(LWasmReinterpretFromI64* lir);
     void visitWasmReinterpretToI64(LWasmReinterpretToI64* lir);
     void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
+    void visitSignExtendInt64(LSignExtendInt64* ins);
     void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
     void visitClzI64(LClzI64* lir);
     void visitCtzI64(LCtzI64* lir);
     void visitNotI64(LNotI64* lir);
     void visitWasmTruncateToInt64(LWasmTruncateToInt64* lir);
     void visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir);
     void visitTestI64AndBranch(LTestI64AndBranch* lir);
 
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -716,8 +716,18 @@ LIRGeneratorX86::visitExtendInt32ToInt64
         defineInt64(new(alloc()) LExtendInt32ToInt64(useRegisterAtStart(ins->input())), ins);
     } else {
         LExtendInt32ToInt64* lir =
             new(alloc()) LExtendInt32ToInt64(useFixedAtStart(ins->input(), eax));
         defineInt64Fixed(lir, ins, LInt64Allocation(LAllocation(AnyRegister(edx)),
                                                     LAllocation(AnyRegister(eax))));
     }
 }
+
+void
+LIRGeneratorX86::visitSignExtendInt64(MSignExtendInt64* ins)
+{
+    // Here we'll end up using cdq which requires input and output in (edx,eax).
+    LSignExtendInt64* lir =
+        new(alloc()) LSignExtendInt64(useInt64FixedAtStart(ins->input(), Register64(edx, eax)));
+    defineInt64Fixed(lir, ins, LInt64Allocation(LAllocation(AnyRegister(edx)),
+                                                LAllocation(AnyRegister(eax))));
+}
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -72,16 +72,17 @@ class LIRGeneratorX86 : public LIRGenera
     void visitWasmLoad(MWasmLoad* ins);
     void visitWasmStore(MWasmStore* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitSubstr(MSubstr* ins);
     void visitRandom(MRandom* ins);
     void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
     void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
     void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
+    void visitSignExtendInt64(MSignExtendInt64* ins);
     void lowerPhi(MPhi* phi);
 
     static bool allowTypedElementHoleCheck() {
         return true;
     }
 
     static bool allowStaticTypedArrayAccesses() {
         return true;
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -2999,16 +2999,48 @@ class BaseCompiler
         (void)r0;               // x0 is the widening of r0
 #else
         RegI32 r0 = popI32();
         RegI64 x0 = widenI32(r0);
 #endif
         return x0;
     }
 
+    RegI64 popI64ForSignExtendI64() {
+#if defined(JS_CODEGEN_X86)
+        need2xI32(specific_edx, specific_eax);
+        // Low on top, high underneath
+        return popI64ToSpecific(RegI64(Register64(specific_edx, specific_eax)));
+#else
+        return popI64();
+#endif
+    }
+
+    void signExtendI64_8(RegI64 r) {
+#if defined(JS_CODEGEN_X64)
+        masm.movsbq(Operand(r.reg), r.reg);
+#elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
+        masm.move8SignExtend(r.low, r.low);
+        signExtendI32ToI64(RegI32(r.low), r);
+#else
+        MOZ_CRASH("Basecompiler platform hook: signExtendI64_8");
+#endif
+    }
+
+    void signExtendI64_16(RegI64 r) {
+#if defined(JS_CODEGEN_X64)
+        masm.movswq(Operand(r.reg), r.reg);
+#elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
+        masm.move16SignExtend(r.low, r.low);
+        signExtendI32ToI64(RegI32(r.low), r);
+#else
+        MOZ_CRASH("Basecompiler platform hook: signExtendI64_16");
+#endif
+    }
+
     void signExtendI32ToI64(RegI32 src, RegI64 dest) {
 #if defined(JS_CODEGEN_X64)
         masm.movslq(src, dest.reg);
 #elif defined(JS_CODEGEN_X86)
         MOZ_ASSERT(dest.low == src);
         MOZ_ASSERT(dest.low == eax);
         MOZ_ASSERT(dest.high == edx);
         masm.cdq();
@@ -3905,16 +3937,21 @@ class BaseCompiler
 #ifdef FLOAT_TO_I64_CALLOUT
     MOZ_MUST_USE bool emitConvertFloatingToInt64Callout(SymbolicAddress callee, ValType operandType,
                                                         ValType resultType);
 #else
     template<bool isUnsigned> MOZ_MUST_USE bool emitTruncateF32ToI64();
     template<bool isUnsigned> MOZ_MUST_USE bool emitTruncateF64ToI64();
 #endif
     void emitWrapI64ToI32();
+    void emitExtendI32_8();
+    void emitExtendI32_16();
+    void emitExtendI64_8();
+    void emitExtendI64_16();
+    void emitExtendI64_32();
     void emitExtendI32ToI64();
     void emitExtendU32ToI64();
     void emitReinterpretF32AsI32();
     void emitReinterpretF64AsI64();
     void emitConvertF64ToF32();
     void emitConvertI32ToF32();
     void emitConvertU32ToF32();
     void emitConvertF32ToF64();
@@ -4963,16 +5000,58 @@ BaseCompiler::emitWrapI64ToI32()
     RegI64 r0 = popI64();
     RegI32 i0 = fromI64(r0);
     wrapI64ToI32(r0, i0);
     freeI64Except(r0, i0);
     pushI32(i0);
 }
 
 void
+BaseCompiler::emitExtendI32_8()
+{
+    RegI32 r = popI32();
+    masm.move8SignExtend(r, r);
+    pushI32(r);
+}
+
+void
+BaseCompiler::emitExtendI32_16()
+{
+    RegI32 r = popI32();
+    masm.move16SignExtend(r, r);
+    pushI32(r);
+}
+
+void
+BaseCompiler::emitExtendI64_8()
+{
+    RegI64 r = popI64ForSignExtendI64();
+    signExtendI64_8(r);
+    pushI64(r);
+}
+
+void
+BaseCompiler::emitExtendI64_16()
+{
+    RegI64 r = popI64ForSignExtendI64();
+    signExtendI64_16(r);
+    pushI64(r);
+}
+
+void
+BaseCompiler::emitExtendI64_32()
+{
+    RegI64 x0 = popI64ForSignExtendI64();
+    RegI32 r0 = RegI32(lowPart(x0));
+    signExtendI32ToI64(r0, x0);
+    pushI64(x0);
+    // Note: no need to free r0, since it is part of x0
+}
+
+void
 BaseCompiler::emitExtendI32ToI64()
 {
     RegI64 x0 = popI32ForSignExtendI64();
     RegI32 r0 = RegI32(lowPart(x0));
     signExtendI32ToI64(r0, x0);
     pushI64(x0);
     // Note: no need to free r0, since it is part of x0
 }
@@ -7359,16 +7438,30 @@ BaseCompiler::emitBody()
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleLessThan));
           case uint16_t(Op::F64Le):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleLessThanOrEqual));
           case uint16_t(Op::F64Gt):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleGreaterThan));
           case uint16_t(Op::F64Ge):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, Assembler::DoubleGreaterThanOrEqual));
 
+          // Sign extensions
+#ifdef ENABLE_WASM_THREAD_OPS
+          case uint16_t(Op::I32Extend8S):
+            CHECK_NEXT(emitConversion(emitExtendI32_8, ValType::I32, ValType::I32));
+          case uint16_t(Op::I32Extend16S):
+            CHECK_NEXT(emitConversion(emitExtendI32_16, ValType::I32, ValType::I32));
+          case uint16_t(Op::I64Extend8S):
+            CHECK_NEXT(emitConversion(emitExtendI64_8, ValType::I64, ValType::I64));
+          case uint16_t(Op::I64Extend16S):
+            CHECK_NEXT(emitConversion(emitExtendI64_16, ValType::I64, ValType::I64));
+          case uint16_t(Op::I64Extend32S):
+            CHECK_NEXT(emitConversion(emitExtendI64_32, ValType::I64, ValType::I64));
+#endif
+
           // Memory Related
           case uint16_t(Op::GrowMemory):
             CHECK_NEXT(emitGrowMemory());
           case uint16_t(Op::CurrentMemory):
             CHECK_NEXT(emitCurrentMemory());
 
           default:
             return iter_.unrecognizedOpcode(&op);
--- a/js/src/wasm/WasmBinaryConstants.h
+++ b/js/src/wasm/WasmBinaryConstants.h
@@ -301,16 +301,25 @@ enum class Op
     F64PromoteF32                        = 0xbb,
 
     // Reinterpretations
     I32ReinterpretF32                    = 0xbc,
     I64ReinterpretF64                    = 0xbd,
     F32ReinterpretI32                    = 0xbe,
     F64ReinterpretI64                    = 0xbf,
 
+#ifdef ENABLE_WASM_THREAD_OPS
+    // Sign extension
+    I32Extend8S                          = 0xc0,
+    I32Extend16S                         = 0xc1,
+    I64Extend8S                          = 0xc2,
+    I64Extend16S                         = 0xc3,
+    I64Extend32S                         = 0xc4,
+#endif
+
     AtomicPrefix                         = 0xfe,
     MozPrefix                            = 0xff,
 
     Limit                                = 0x100
 };
 
 inline bool
 IsPrefixByte(uint8_t b) {
--- a/js/src/wasm/WasmBinaryIterator.cpp
+++ b/js/src/wasm/WasmBinaryIterator.cpp
@@ -172,16 +172,23 @@ wasm::Classify(OpBytes op)
       case Op::F32ConvertUI64:
       case Op::F32DemoteF64:
       case Op::F64ConvertSI32:
       case Op::F64ConvertUI32:
       case Op::F64ConvertSI64:
       case Op::F64ConvertUI64:
       case Op::F64ReinterpretI64:
       case Op::F64PromoteF32:
+#ifdef ENABLE_WASM_THREAD_OPS
+      case Op::I32Extend8S:
+      case Op::I32Extend16S:
+      case Op::I64Extend8S:
+      case Op::I64Extend16S:
+      case Op::I64Extend32S:
+#endif
         return OpKind::Conversion;
       case Op::I32Load8S:
       case Op::I32Load8U:
       case Op::I32Load16S:
       case Op::I32Load16U:
       case Op::I64Load8S:
       case Op::I64Load8U:
       case Op::I64Load16S:
--- a/js/src/wasm/WasmBinaryToAST.cpp
+++ b/js/src/wasm/WasmBinaryToAST.cpp
@@ -1248,16 +1248,29 @@ AstDecodeExpr(AstDecodeContext& c)
       case uint16_t(Op::F64ReinterpretI64):
         if (!AstDecodeConversion(c, ValType::I64, ValType::F64, Op(op.b0)))
             return false;
         break;
       case uint16_t(Op::F64PromoteF32):
         if (!AstDecodeConversion(c, ValType::F32, ValType::F64, Op(op.b0)))
             return false;
         break;
+#ifdef ENABLE_WASM_THREAD_OPS
+      case uint16_t(Op::I32Extend8S):
+      case uint16_t(Op::I32Extend16S):
+        if (!AstDecodeConversion(c, ValType::I32, ValType::I32, Op(op.b0)))
+            return false;
+        break;
+      case uint16_t(Op::I64Extend8S):
+      case uint16_t(Op::I64Extend16S):
+      case uint16_t(Op::I64Extend32S):
+        if (!AstDecodeConversion(c, ValType::I64, ValType::I64, Op(op.b0)))
+            return false;
+        break;
+#endif
       case uint16_t(Op::I32Load8S):
       case uint16_t(Op::I32Load8U):
         if (!AstDecodeLoad(c, ValType::I32, 1, Op(op.b0)))
             return false;
         break;
       case uint16_t(Op::I32Load16S):
       case uint16_t(Op::I32Load16U):
         if (!AstDecodeLoad(c, ValType::I32, 2, Op(op.b0)))
--- a/js/src/wasm/WasmBinaryToText.cpp
+++ b/js/src/wasm/WasmBinaryToText.cpp
@@ -731,16 +731,23 @@ RenderConversionOperator(WasmRenderConte
       case Op::F32ConvertUI64:    opStr = "f32.convert_u/i64"; break;
       case Op::F32DemoteF64:      opStr = "f32.demote/f64"; break;
       case Op::F64ConvertSI32:    opStr = "f64.convert_s/i32"; break;
       case Op::F64ConvertUI32:    opStr = "f64.convert_u/i32"; break;
       case Op::F64ConvertSI64:    opStr = "f64.convert_s/i64"; break;
       case Op::F64ConvertUI64:    opStr = "f64.convert_u/i64"; break;
       case Op::F64ReinterpretI64: opStr = "f64.reinterpret/i64"; break;
       case Op::F64PromoteF32:     opStr = "f64.promote/f32"; break;
+#ifdef ENABLE_WASM_THREAD_OPS
+      case Op::I32Extend8S:       opStr = "i32.extend8_s"; break;
+      case Op::I32Extend16S:      opStr = "i32.extend16_s"; break;
+      case Op::I64Extend8S:       opStr = "i64.extend8_s"; break;
+      case Op::I64Extend16S:      opStr = "i64.extend16_s"; break;
+      case Op::I64Extend32S:      opStr = "i64.extend32_s"; break;
+#endif
       case Op::I32Eqz:            opStr = "i32.eqz"; break;
       case Op::I64Eqz:            opStr = "i64.eqz"; break;
       default:                      return Fail(c, "unexpected conversion operator");
     }
     return c.buffer.append(opStr, strlen(opStr));
 }
 
 static bool
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -666,16 +666,51 @@ class FunctionCompiler
     {
         if (inDeadCode())
             return nullptr;
         auto* ins = MExtendInt32ToInt64::New(alloc(), op, isUnsigned);
         curBlock_->add(ins);
         return ins;
     }
 
+    MDefinition* signExtend(MDefinition* op, uint32_t srcSize, uint32_t targetSize)
+    {
+        if (inDeadCode())
+            return nullptr;
+        MInstruction* ins;
+        switch (targetSize) {
+          case 4: {
+            MSignExtendInt32::Mode mode;
+            switch (srcSize) {
+              case 1:  mode = MSignExtendInt32::Byte; break;
+              case 2:  mode = MSignExtendInt32::Half; break;
+              default: MOZ_CRASH("Bad sign extension");
+            }
+            ins = MSignExtendInt32::New(alloc(), op, mode);
+            break;
+          }
+          case 8: {
+            MSignExtendInt64::Mode mode;
+            switch (srcSize) {
+              case 1:  mode = MSignExtendInt64::Byte; break;
+              case 2:  mode = MSignExtendInt64::Half; break;
+              case 4:  mode = MSignExtendInt64::Word; break;
+              default: MOZ_CRASH("Bad sign extension");
+            }
+            ins = MSignExtendInt64::New(alloc(), op, mode);
+            break;
+          }
+          default: {
+            MOZ_CRASH("Bad sign extension");
+          }
+        }
+        curBlock_->add(ins);
+        return ins;
+    }
+
     MDefinition* convertI64ToFloatingPoint(MDefinition* op, MIRType type, bool isUnsigned)
     {
         if (inDeadCode())
             return nullptr;
         auto* ins = MInt64ToFloatingPoint::New(alloc(), op, type, bytecodeOffset(), isUnsigned);
         curBlock_->add(ins);
         return ins;
     }
@@ -2254,16 +2289,30 @@ EmitTruncate(FunctionCompiler& f, ValTyp
     } else {
         MOZ_ASSERT(resultType == ValType::I64);
         MOZ_ASSERT(!f.env().isAsmJS());
         f.iter().setResult(f.truncate<MWasmTruncateToInt64>(input, isUnsigned));
     }
     return true;
 }
 
+#ifdef ENABLE_WASM_THREAD_OPS
+static bool
+EmitSignExtend(FunctionCompiler& f, uint32_t srcSize, uint32_t targetSize)
+{
+    MDefinition* input;
+    ValType type = targetSize == 4 ? ValType::I32 : ValType::I64;
+    if (!f.iter().readConversion(type, type, &input))
+        return false;
+
+    f.iter().setResult(f.signExtend(input, srcSize, targetSize));
+    return true;
+}
+#endif
+
 static bool
 EmitExtendI32(FunctionCompiler& f, bool isUnsigned)
 {
     MDefinition* input;
     if (!f.iter().readConversion(ValType::I32, ValType::I64, &input))
         return false;
 
     f.iter().setResult(f.extendI32(input, isUnsigned));
@@ -3596,16 +3645,30 @@ EmitBodyExprs(FunctionCompiler& f)
             CHECK(EmitReinterpret(f, ValType::I32, ValType::F32, MIRType::Int32));
           case uint16_t(Op::I64ReinterpretF64):
             CHECK(EmitReinterpret(f, ValType::I64, ValType::F64, MIRType::Int64));
           case uint16_t(Op::F32ReinterpretI32):
             CHECK(EmitReinterpret(f, ValType::F32, ValType::I32, MIRType::Float32));
           case uint16_t(Op::F64ReinterpretI64):
             CHECK(EmitReinterpret(f, ValType::F64, ValType::I64, MIRType::Double));
 
+          // Sign extensions
+#ifdef ENABLE_WASM_THREAD_OPS
+          case uint16_t(Op::I32Extend8S):
+            CHECK(EmitSignExtend(f, 1, 4));
+          case uint16_t(Op::I32Extend16S):
+            CHECK(EmitSignExtend(f, 2, 4));
+          case uint16_t(Op::I64Extend8S):
+            CHECK(EmitSignExtend(f, 1, 8));
+          case uint16_t(Op::I64Extend16S):
+            CHECK(EmitSignExtend(f, 2, 8));
+          case uint16_t(Op::I64Extend32S):
+            CHECK(EmitSignExtend(f, 4, 8));
+#endif
+
           // asm.js-specific operators
 
           case uint16_t(Op::MozPrefix): {
             switch (op.b1) {
               case uint16_t(MozOp::TeeGlobal):
                 CHECK_ASMJS(EmitTeeGlobal(f));
               case uint16_t(MozOp::I32Min):
               case uint16_t(MozOp::I32Max):
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -1118,16 +1118,22 @@ WasmTokenStream::next()
                 if (consume(u"div_u"))
                     return WasmToken(WasmToken::BinaryOpcode, Op::I32DivU, begin, cur_);
                 break;
               case 'e':
                 if (consume(u"eqz"))
                     return WasmToken(WasmToken::UnaryOpcode, Op::I32Eqz, begin, cur_);
                 if (consume(u"eq"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I32Eq, begin, cur_);
+#ifdef ENABLE_WASM_THREAD_OPS
+                if (consume(u"extend8_s"))
+                    return WasmToken(WasmToken::ConversionOpcode, Op::I32Extend8S, begin, cur_);
+                if (consume(u"extend16_s"))
+                    return WasmToken(WasmToken::ConversionOpcode, Op::I32Extend16S, begin, cur_);
+#endif
                 break;
               case 'g':
                 if (consume(u"ge_s"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I32GeS, begin, cur_);
                 if (consume(u"ge_u"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I32GeU, begin, cur_);
                 if (consume(u"gt_s"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I32GtS, begin, cur_);
@@ -1262,16 +1268,24 @@ WasmTokenStream::next()
                 if (consume(u"eq"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I64Eq, begin, cur_);
                 if (consume(u"extend_s/i32"))
                     return WasmToken(WasmToken::ConversionOpcode, Op::I64ExtendSI32,
                                      begin, cur_);
                 if (consume(u"extend_u/i32"))
                     return WasmToken(WasmToken::ConversionOpcode, Op::I64ExtendUI32,
                                      begin, cur_);
+#ifdef ENABLE_WASM_THREAD_OPS
+                if (consume(u"extend8_s"))
+                    return WasmToken(WasmToken::ConversionOpcode, Op::I64Extend8S, begin, cur_);
+                if (consume(u"extend16_s"))
+                    return WasmToken(WasmToken::ConversionOpcode, Op::I64Extend16S, begin, cur_);
+                if (consume(u"extend32_s"))
+                    return WasmToken(WasmToken::ConversionOpcode, Op::I64Extend32S, begin, cur_);
+#endif
                 break;
               case 'g':
                 if (consume(u"ge_s"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I64GeS, begin, cur_);
                 if (consume(u"ge_u"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I64GeU, begin, cur_);
                 if (consume(u"gt_s"))
                     return WasmToken(WasmToken::ComparisonOpcode, Op::I64GtS, begin, cur_);
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -574,16 +574,25 @@ DecodeFunctionBodyExprs(const ModuleEnvi
           case uint16_t(Op::F64ConvertUI32):
             CHECK(iter.readConversion(ValType::I32, ValType::F64, &nothing));
           case uint16_t(Op::F64ConvertSI64):
           case uint16_t(Op::F64ConvertUI64):
           case uint16_t(Op::F64ReinterpretI64):
             CHECK(iter.readConversion(ValType::I64, ValType::F64, &nothing));
           case uint16_t(Op::F64PromoteF32):
             CHECK(iter.readConversion(ValType::F32, ValType::F64, &nothing));
+#ifdef ENABLE_WASM_THREAD_OPS
+          case uint16_t(Op::I32Extend8S):
+          case uint16_t(Op::I32Extend16S):
+            CHECK(iter.readConversion(ValType::I32, ValType::I32, &nothing));
+          case uint16_t(Op::I64Extend8S):
+          case uint16_t(Op::I64Extend16S):
+          case uint16_t(Op::I64Extend32S):
+            CHECK(iter.readConversion(ValType::I64, ValType::I64, &nothing));
+#endif
           case uint16_t(Op::I32Load8S):
           case uint16_t(Op::I32Load8U): {
             LinearMemoryAddress<Nothing> addr;
             CHECK(iter.readLoad(ValType::I32, 1, &addr));
           }
           case uint16_t(Op::I32Load16S):
           case uint16_t(Op::I32Load16U): {
             LinearMemoryAddress<Nothing> addr;