diff --git a/Build/Common.Build.Default.props b/Build/Common.Build.Default.props index 815121e3ab9..07d1dd35f69 100644 --- a/Build/Common.Build.Default.props +++ b/Build/Common.Build.Default.props @@ -17,6 +17,7 @@ v120 v140 v141 + v142 diff --git a/Build/NuGet/.pack-version b/Build/NuGet/.pack-version index 361ffc5b4d8..f33bbfa17f1 100644 --- a/Build/NuGet/.pack-version +++ b/Build/NuGet/.pack-version @@ -1 +1 @@ -1.11.9 +1.11.10 diff --git a/lib/Backend/BackwardPass.cpp b/lib/Backend/BackwardPass.cpp index be4e42d4be4..59210a247b5 100644 --- a/lib/Backend/BackwardPass.cpp +++ b/lib/Backend/BackwardPass.cpp @@ -1645,6 +1645,8 @@ BackwardPass::ProcessLoop(BasicBlock * lastBlock) { Assert(loop->symsAssignedToInLoop == nullptr); loop->symsAssignedToInLoop = JitAnew(this->globOpt->alloc, BVSparse, this->globOpt->alloc); + Assert(loop->preservesNumberValue == nullptr); + loop->preservesNumberValue = JitAnew(this->globOpt->alloc, BVSparse, this->globOpt->alloc); } FOREACH_BLOCK_BACKWARD_IN_RANGE_DEAD_OR_ALIVE(block, lastBlock, nullptr) @@ -4316,7 +4318,10 @@ BackwardPass::ProcessNoImplicitCallDef(IR::Instr *const instr) const bool transferArrayLengthSymUse = !!currentBlock->noImplicitCallArrayLengthSymUses->TestAndClear(dstSym->m_id); IR::Opnd *const src = instr->GetSrc1(); - if(!src || instr->GetSrc2()) + + // Stop attempting to transfer noImplicitCallUses symbol if the instr is not a transfer instr (based on the opcode's + // flags) or does not have the attributes to be a transfer instr (based on the existance of src and src2). + if(!src || (instr->GetSrc2() && !OpCodeAttr::NonIntTransfer(instr->m_opcode))) { return; } @@ -5004,16 +5009,24 @@ BackwardPass::UpdateArrayBailOutKind(IR::Instr *const instr) return; } + instr->GetDst()->AsIndirOpnd()->AllowConversion(true); IR::BailOutKind includeBailOutKinds = IR::BailOutInvalid; if (!baseValueType.IsNotNativeArray() && - (!baseValueType.IsLikelyNativeArray() || instr->GetSrc1()->IsVar()) && !currentBlock->noImplicitCallNativeArrayUses->IsEmpty() && !(instr->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall)) { // There is an upwards-exposed use of a native array. Since the array referenced by this instruction can be aliased, // this instruction needs to bail out if it converts the native array even if this array specifically is not // upwards-exposed. - includeBailOutKinds |= IR::BailOutConvertedNativeArray; + if (!baseValueType.IsLikelyNativeArray() || instr->GetSrc1()->IsVar()) + { + includeBailOutKinds |= IR::BailOutConvertedNativeArray; + } + else + { + // We are assuming that array conversion is impossible here, so make sure we execute code that fails if conversion does happen. + instr->GetDst()->AsIndirOpnd()->AllowConversion(false); + } } if(baseOpnd->IsArrayRegOpnd() && baseOpnd->AsArrayRegOpnd()->EliminatedUpperBoundCheck()) @@ -7410,6 +7423,52 @@ BackwardPass::TrackFloatSymEquivalence(IR::Instr *const instr) } } +bool +BackwardPass::SymIsIntconstOrSelf(Sym *sym, IR::Opnd *opnd) +{ + Assert(sym->IsStackSym()); + if (!opnd->IsRegOpnd()) + { + return false; + } + StackSym *opndSym = opnd->AsRegOpnd()->m_sym; + + if (sym == opndSym) + { + return true; + } + + if (!opndSym->IsSingleDef()) + { + return false; + } + + if (opndSym->GetInstrDef()->m_opcode == Js::OpCode::LdC_A_I4) + { + return true; + } + + return false; +} + +bool +BackwardPass::InstrPreservesNumberValues(IR::Instr *instr, Sym *defSym) +{ + if (instr->m_opcode == Js::OpCode::Ld_A) + { + if (instr->GetSrc1()->IsRegOpnd()) + { + IR::RegOpnd *src1 = instr->GetSrc1()->AsRegOpnd(); + if (src1->m_sym->IsSingleDef()) + { + instr = src1->m_sym->GetInstrDef(); + } + } + } + return (OpCodeAttr::ProducesNumber(instr->m_opcode) || + (instr->m_opcode == Js::OpCode::Add_A && this->SymIsIntconstOrSelf(defSym, instr->GetSrc1()) && this->SymIsIntconstOrSelf(defSym, instr->GetSrc2()))); +} + bool BackwardPass::ProcessDef(IR::Opnd * opnd) { @@ -7424,7 +7483,19 @@ BackwardPass::ProcessDef(IR::Opnd * opnd) this->InvalidateCloneStrCandidate(opnd); if ((tag == Js::BackwardPhase) && IsPrePass()) { - this->currentPrePassLoop->symsAssignedToInLoop->Set(sym->m_id); + bool firstDef = !this->currentPrePassLoop->symsAssignedToInLoop->TestAndSet(sym->m_id); + + if (firstDef) + { + if (this->InstrPreservesNumberValues(this->currentInstr, sym)) + { + this->currentPrePassLoop->preservesNumberValue->Set(sym->m_id); + } + } + else if (!this->InstrPreservesNumberValues(this->currentInstr, sym)) + { + this->currentPrePassLoop->preservesNumberValue->Clear(sym->m_id); + } } } } diff --git a/lib/Backend/BackwardPass.h b/lib/Backend/BackwardPass.h index 4522594d5d3..18a6fe5a94e 100644 --- a/lib/Backend/BackwardPass.h +++ b/lib/Backend/BackwardPass.h @@ -36,6 +36,9 @@ class BackwardPass bool ProcessDef(IR::Opnd * opnd); void ProcessTransfers(IR::Instr * instr); void ProcessFieldKills(IR::Instr * instr); + bool SymIsIntconstOrSelf(Sym *sym, IR::Opnd *opnd); + bool InstrPreservesNumberValues(IR::Instr *instr, Sym *defSym); + template void ClearBucketsOnFieldKill(IR::Instr *instr, HashTable *table); StackSym* ProcessByteCodeUsesDst(IR::ByteCodeUsesInstr * byteCodeUsesInstr); const BVSparse* ProcessByteCodeUsesSrcs(IR::ByteCodeUsesInstr * byteCodeUsesInstr); diff --git a/lib/Backend/FlowGraph.h b/lib/Backend/FlowGraph.h index b08fc1d4c50..0d516222075 100644 --- a/lib/Backend/FlowGraph.h +++ b/lib/Backend/FlowGraph.h @@ -588,6 +588,7 @@ class Loop // cleanup in PreOptPeep in the pre-pass of a loop. For aggressively transferring // values in prepass, we need to know if a source sym was ever assigned to in a loop. BVSparse *symsAssignedToInLoop; + BVSparse *preservesNumberValue; BailOutInfo * bailOutInfo; IR::BailOutInstr * toPrimitiveSideEffectCheck; @@ -733,6 +734,7 @@ class Loop symsAssignedToInLoop(nullptr), needImplicitCallBailoutChecksForJsArrayCheckHoist(false), inductionVariables(nullptr), + preservesNumberValue(nullptr), dominatingLoopCountableBlock(nullptr), loopCount(nullptr), loopCountBasedBoundBaseSyms(nullptr), diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index 261d6b9a5ca..652410f7347 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -1244,7 +1244,7 @@ void GlobOpt::InsertValueCompensation( { IR::Instr *const newInstr = IR::Instr::New( - Js::OpCode::Ld_I4, + Js::OpCode::Ld_A, IR::RegOpnd::New(mergedHeadSegmentLengthSym, mergedHeadSegmentLengthSym->GetType(), func), IR::RegOpnd::New(predecessorHeadSegmentLengthSym, predecessorHeadSegmentLengthSym->GetType(), func), func); @@ -2694,6 +2694,48 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved) return instrNext; } +bool +GlobOpt::IsNonNumericRegOpnd(IR::RegOpnd *opnd, bool inGlobOpt) const +{ + if (opnd == nullptr) + { + return false; + } + + if (opnd->m_sym->m_isNotNumber) + { + return true; + } + + if (!inGlobOpt) + { + return false; + } + + if (opnd->GetValueType().IsNumber() || currentBlock->globOptData.IsTypeSpecialized(opnd->m_sym)) + { + if (!this->IsLoopPrePass()) + { + return false; + } + + Value * opndValue = this->currentBlock->globOptData.FindValue(opnd->m_sym); + ValueInfo * opndValueInfo = opndValue ? opndValue->GetValueInfo() : nullptr; + if (!opndValueInfo) + { + return true; + } + if (this->prePassLoop->preservesNumberValue->Test(opnd->m_sym->m_id)) + { + return false; + } + + return !this->IsSafeToTransferInPrepass(opnd->m_sym, opndValueInfo); + } + + return true; +} + bool GlobOpt::OptTagChecks(IR::Instr *instr) { @@ -12827,6 +12869,26 @@ GlobOpt::ProcessValueKills(IR::Instr *const instr) it.RemoveCurrent(); } } + else if(kills.KillsObjectArraysWithNoMissingValues()) + { + // Some operations may kill objects with arrays-with-no-missing-values in unlikely circumstances. Convert their value types to likely + // versions so that the checks have to be redone. + for(auto it = valuesToKillOnCalls->GetIteratorWithRemovalSupport(); it.IsValid(); it.MoveNext()) + { + Value *const value = it.CurrentValue(); + ValueInfo *const valueInfo = value->GetValueInfo(); + Assert( + valueInfo->IsArrayOrObjectWithArray() || + valueInfo->IsOptimizedVirtualTypedArray() || + valueInfo->IsOptimizedTypedArray() && valueInfo->AsArrayValueInfo()->HeadSegmentLengthSym()); + if(!valueInfo->IsArrayOrObjectWithArray() || valueInfo->IsArray() || !valueInfo->HasNoMissingValues()) + { + continue; + } + ChangeValueType(nullptr, value, valueInfo->Type().ToLikely(), false); + it.RemoveCurrent(); + } + } if(kills.KillsNativeArrays()) { @@ -13358,6 +13420,11 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr) { kills.SetKillsArrayLengths(); } + + if(doArrayMissingValueCheckHoist && !(useValueTypes && arrayValueType.IsArray())) + { + kills.SetKillsObjectArraysWithNoMissingValues(); + } break; } diff --git a/lib/Backend/GlobOpt.h b/lib/Backend/GlobOpt.h index b6afcf657fe..e71c1900730 100644 --- a/lib/Backend/GlobOpt.h +++ b/lib/Backend/GlobOpt.h @@ -317,6 +317,7 @@ class JsArrayKills { bool killsAllArrays : 1; bool killsArraysWithNoMissingValues : 1; + bool killsObjectArraysWithNoMissingValues : 1; bool killsNativeArrays : 1; bool killsArrayHeadSegments : 1; bool killsArrayHeadSegmentLengths : 1; @@ -342,6 +343,9 @@ class JsArrayKills bool KillsArraysWithNoMissingValues() const { return killsArraysWithNoMissingValues; } void SetKillsArraysWithNoMissingValues() { killsArraysWithNoMissingValues = true; } + bool KillsObjectArraysWithNoMissingValues() const { return killsObjectArraysWithNoMissingValues; } + void SetKillsObjectArraysWithNoMissingValues() { killsObjectArraysWithNoMissingValues = true; } + bool KillsNativeArrays() const { return killsNativeArrays; } void SetKillsNativeArrays() { killsNativeArrays = true; } @@ -769,6 +773,8 @@ class GlobOpt const bool lossy = false, const bool forceInvariantHoisting = false, IR::BailOutKind bailoutKind = IR::BailOutInvalid); void HoistInvariantValueInfo(ValueInfo *const invariantValueInfoToHoist, Value *const valueToUpdate, BasicBlock *const targetBlock); void OptHoistUpdateValueType(Loop* loop, IR::Instr* instr, IR::Opnd** srcOpndPtr, Value *const srcVal); + bool IsNonNumericRegOpnd(IR::RegOpnd *opnd, bool inGlobOpt) const; + public: static bool IsTypeSpecPhaseOff(Func const * func); static bool DoAggressiveIntTypeSpec(Func const * func); @@ -891,7 +897,7 @@ class GlobOpt void KillLiveFields(StackSym * stackSym, BVSparse * bv); void KillLiveFields(PropertySym * propertySym, BVSparse * bv); void KillLiveFields(BVSparse *const fieldsToKill, BVSparse *const bv) const; - void KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse * bv, bool inGlobOpt, Func *func); + void KillLiveElems(IR::IndirOpnd * indirOpnd, IR::Opnd * valueOpnd, BVSparse * bv, bool inGlobOpt, Func *func); void KillAllFields(BVSparse * bv); void SetAnyPropertyMayBeWrittenTo(); void AddToPropertiesWrittenTo(Js::PropertyId propertyId); diff --git a/lib/Backend/GlobOptFields.cpp b/lib/Backend/GlobOptFields.cpp index 5a0e3fa8337..92a7e9ec108 100644 --- a/lib/Backend/GlobOptFields.cpp +++ b/lib/Backend/GlobOptFields.cpp @@ -208,7 +208,7 @@ void GlobOpt::KillLiveFields(BVSparse *const fieldsToKill, BV } void -GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse * bv, bool inGlobOpt, Func *func) +GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, IR::Opnd * valueOpnd, BVSparse * bv, bool inGlobOpt, Func *func) { IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd(); @@ -225,14 +225,7 @@ GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse * // - We check the type specialization status for the sym as well. For the purpose of doing kills, we can assume that // if type specialization happened, that fields don't need to be killed. Note that they may be killed in the next // pass based on the value. - if (func->GetThisOrParentInlinerHasArguments() || - ( - indexOpnd && - ( - indexOpnd->m_sym->m_isNotNumber || - (inGlobOpt && !indexOpnd->GetValueType().IsNumber() && !currentBlock->globOptData.IsTypeSpecialized(indexOpnd->m_sym)) - ) - )) + if (func->GetThisOrParentInlinerHasArguments() || this->IsNonNumericRegOpnd(indexOpnd, inGlobOpt)) { this->KillAllFields(bv); // This also kills all property type values, as the same bit-vector tracks those stack syms SetAnyPropertyMayBeWrittenTo(); @@ -248,6 +241,23 @@ GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse * // Write/delete to a non-integer numeric index can't alias a name on the RHS of a dot, but it change object layout this->KillAllObjectTypes(bv); } + else if ((!valueOpnd || valueOpnd->IsVar()) && this->objectTypeSyms != nullptr) + { + // If we wind up converting a native array, block final-type opt at this point, because we could evolve + // to a type with the wrong type ID. Do this by noting that we may have evolved any type and so must + // check it before evolving it further. + IR::RegOpnd *baseOpnd = indirOpnd->GetBaseOpnd(); + Value * baseValue = baseOpnd ? this->currentBlock->globOptData.FindValue(baseOpnd->m_sym) : nullptr; + ValueInfo * baseValueInfo = baseValue ? baseValue->GetValueInfo() : nullptr; + if (!baseValueInfo || !baseValueInfo->IsNotNativeArray()) + { + if (this->currentBlock->globOptData.maybeWrittenTypeSyms == nullptr) + { + this->currentBlock->globOptData.maybeWrittenTypeSyms = JitAnew(this->alloc, BVSparse, this->alloc); + } + this->currentBlock->globOptData.maybeWrittenTypeSyms->Or(this->objectTypeSyms); + } + } } } @@ -340,7 +350,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo case Js::OpCode::StElemI_A_Strict: Assert(dstOpnd != nullptr); KillLiveFields(this->lengthEquivBv, bv); - KillLiveElems(dstOpnd->AsIndirOpnd(), bv, inGlobOpt, instr->m_func); + KillLiveElems(dstOpnd->AsIndirOpnd(), instr->GetSrc1(), bv, inGlobOpt, instr->m_func); if (inGlobOpt) { KillObjectHeaderInlinedTypeSyms(this->currentBlock, false); @@ -350,7 +360,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo case Js::OpCode::InitComputedProperty: case Js::OpCode::InitGetElemI: case Js::OpCode::InitSetElemI: - KillLiveElems(dstOpnd->AsIndirOpnd(), bv, inGlobOpt, instr->m_func); + KillLiveElems(dstOpnd->AsIndirOpnd(), instr->GetSrc1(), bv, inGlobOpt, instr->m_func); if (inGlobOpt) { KillObjectHeaderInlinedTypeSyms(this->currentBlock, false); @@ -360,7 +370,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo case Js::OpCode::DeleteElemI_A: case Js::OpCode::DeleteElemIStrict_A: Assert(dstOpnd != nullptr); - KillLiveElems(instr->GetSrc1()->AsIndirOpnd(), bv, inGlobOpt, instr->m_func); + KillLiveElems(instr->GetSrc1()->AsIndirOpnd(), nullptr, bv, inGlobOpt, instr->m_func); break; case Js::OpCode::DeleteFld: diff --git a/lib/Backend/IR.cpp b/lib/Backend/IR.cpp index 6580465cb73..1e3a4c1074b 100644 --- a/lib/Backend/IR.cpp +++ b/lib/Backend/IR.cpp @@ -3307,7 +3307,14 @@ bool Instr::TransfersSrcValue() // Consider: Add opcode attribute to indicate whether the opcode would use the value or not - return this->GetDst() != nullptr && this->GetSrc2() == nullptr && !OpCodeAttr::DoNotTransfer(this->m_opcode) && !this->CallsAccessor(); + return + this->GetDst() != nullptr && + + // The lack of a Src2 does not always indicate that the instr is not a transfer instr (ex: StSlotChkUndecl). + (this->GetSrc2() == nullptr || OpCodeAttr::NonIntTransfer(this->m_opcode)) && + + !OpCodeAttr::DoNotTransfer(this->m_opcode) && + !this->CallsAccessor(); } diff --git a/lib/Backend/InductionVariable.cpp b/lib/Backend/InductionVariable.cpp index bbb71e7a3ee..275370ef4f7 100644 --- a/lib/Backend/InductionVariable.cpp +++ b/lib/Backend/InductionVariable.cpp @@ -81,6 +81,21 @@ bool InductionVariable::Add(const int n) if(n == 0) return true; + int lowerBound = changeBounds.LowerBound(); + int upperBound = changeBounds.UpperBound(); + + if (n < 0 && (lowerBound < upperBound || (lowerBound == upperBound && lowerBound > 0))) + { + isChangeDeterminate = false; + return false; + } + + if (n > 0 && (lowerBound > upperBound || (lowerBound == upperBound && lowerBound < 0))) + { + isChangeDeterminate = false; + return false; + } + int newLowerBound; if(changeBounds.LowerBound() == IntConstMin) { @@ -148,6 +163,25 @@ void InductionVariable::Merge(const InductionVariable &other) // The value number may be different, the caller will give the merged info the appropriate value number isChangeDeterminate &= other.isChangeDeterminate; + if(!isChangeDeterminate) + return; + + int lowerBound = this->ChangeBounds().LowerBound(); + int upperBound = this->ChangeBounds().UpperBound(); + + int otherLowerBound = other.ChangeBounds().LowerBound(); + int otherUpperBound = other.ChangeBounds().UpperBound(); + + if ((lowerBound < upperBound || (lowerBound == upperBound && lowerBound > 0)) && !(otherLowerBound < otherUpperBound || (otherLowerBound == otherUpperBound && otherLowerBound > 0))) + { + isChangeDeterminate = false; + } + + if ((lowerBound > upperBound || (lowerBound == upperBound && lowerBound < 0)) && !(otherLowerBound > otherUpperBound || (otherLowerBound == otherUpperBound && otherLowerBound < 0))) + { + isChangeDeterminate = false; + } + if(!isChangeDeterminate) return; diff --git a/lib/Backend/Inline.cpp b/lib/Backend/Inline.cpp index 5703333c78c..a09ba6bbb4a 100644 --- a/lib/Backend/Inline.cpp +++ b/lib/Backend/Inline.cpp @@ -69,7 +69,7 @@ Inline::Optimize(Func *func, __in_ecount_opt(callerArgOutCount) IR::Instr *calle if (instr->AsLabelInstr()->m_isForInExit) { - Assert(this->currentForInDepth != 0); + AssertOrFailFast(this->currentForInDepth != 0); this->currentForInDepth--; } } @@ -2158,14 +2158,12 @@ Inline::InlineBuiltInFunction(IR::Instr *callInstr, const FunctionJITTimeInfo * IR::Instr *inlineBuiltInEndInstr = nullptr; if (inlineCallOpCode == Js::OpCode::InlineFunctionApply) { - inlineBuiltInEndInstr = InlineApply(callInstr, inlineeData, inlinerData, symCallerThis, pIsInlined, profileId, recursiveInlineDepth, inlineCallArgCount - (usesThisArgument ? 1 : 0)); - return inlineBuiltInEndInstr->m_next; + return InlineApply(callInstr, inlineeData, inlinerData, symCallerThis, pIsInlined, profileId, recursiveInlineDepth, inlineCallArgCount - (usesThisArgument ? 1 : 0)); } if (inlineCallOpCode == Js::OpCode::InlineFunctionCall) { - inlineBuiltInEndInstr = InlineCall(callInstr, inlineeData, inlinerData, symCallerThis, pIsInlined, profileId, recursiveInlineDepth); - return inlineBuiltInEndInstr->m_next; + return InlineCall(callInstr, inlineeData, inlinerData, symCallerThis, pIsInlined, profileId, recursiveInlineDepth); } @@ -2415,9 +2413,10 @@ IR::Instr* Inline::InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo * // We may still decide not to inline. *pIsInlined = false; + IR::Instr* instrNext = callInstr->m_next; if (argsCount == 0) { - return callInstr; + return instrNext; } Js::BuiltinFunction builtInId = Js::JavascriptLibrary::GetBuiltInForFuncInfo(applyData->GetLocalFunctionId()); @@ -2459,7 +2458,7 @@ IR::Instr* Inline::InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo * if (PHASE_OFF1(Js::InlineApplyWithoutArrayArgPhase)) { *pIsInlined = false; - return callInstr; + return instrNext; } *pIsInlined = true; @@ -2490,7 +2489,7 @@ IR::Instr* Inline::InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo * { INLINE_TESTTRACE(_u("INLINING: Skip Inline: Supporting inlining func.apply(this, array) or func.apply(this, arguments) with formals in the parent function only when func is a built-in inlinable as apply target \tCaller: %s (%s)\n"), inlinerData->GetBody()->GetDisplayName(), inlinerData->GetDebugNumberSet(debugStringBuffer)); - return callInstr; + return instrNext; } } @@ -2506,6 +2505,7 @@ IR::Instr* Inline::InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo * IR::Instr * Inline::InlineApplyWithArgumentsObject(IR::Instr * callInstr, IR::Instr * argsObjectArgInstr, const FunctionJITTimeInfo * funcInfo) { + IR::Instr* instrNext = callInstr->m_next; IR::Instr* ldHeapArguments = argsObjectArgInstr->GetSrc1()->GetStackSym()->GetInstrDef(); argsObjectArgInstr->ReplaceSrc1(ldHeapArguments->GetDst()); @@ -2595,7 +2595,7 @@ IR::Instr * Inline::InlineApplyWithArgumentsObject(IR::Instr * callInstr, IR::In argout = IR::Instr::New(Js::OpCode::ArgOut_A_Dynamic, linkOpnd2, explicitThisArgOut->GetSrc1(), linkOpnd1, callInstr->m_func); // push explicit this as this pointer callInstr->InsertBefore(argout); - return callInstr; + return instrNext; } /* @@ -2603,6 +2603,7 @@ This method will only do CallDirect style inlining of built-in targets. No scrip */ IR::Instr * Inline::InlineApplyBuiltInTargetWithArray(IR::Instr * callInstr, const FunctionJITTimeInfo * applyInfo, const FunctionJITTimeInfo * builtInInfo) { + IR::Instr* instrNext = callInstr->m_next; IR::Instr * implicitThisArgOut = nullptr; IR::Instr * explicitThisArgOut = nullptr; IR::Instr * arrayArgOut = nullptr; @@ -2620,7 +2621,7 @@ IR::Instr * Inline::InlineApplyBuiltInTargetWithArray(IR::Instr * callInstr, con IR::Instr* applyTargetLdInstr = nullptr; if (!TryGetApplyAndTargetLdInstrs(callInstr, &applyLdInstr, &applyTargetLdInstr)) { - return callInstr; + return instrNext; } // Fixed function/function object checks for target built-in callInstr->ReplaceSrc1(applyTargetLdInstr->GetDst()); @@ -2685,11 +2686,12 @@ IR::Instr * Inline::InlineApplyBuiltInTargetWithArray(IR::Instr * callInstr, con callInstr->ReplaceSrc1(helperCallOpnd); callInstr->ReplaceSrc2(argOut->GetDst()); - return callInstr; + return instrNext; } IR::Instr * Inline::InlineApplyWithoutArrayArgument(IR::Instr *callInstr, const FunctionJITTimeInfo * applyInfo, const FunctionJITTimeInfo * applyTargetInfo) { + IR::Instr* instrNext = callInstr->m_next; IR::Instr * implicitThisArgOut = nullptr; IR::Instr * explicitThisArgOut = nullptr; IR::Instr * dummyInstr = nullptr; @@ -2728,12 +2730,12 @@ IR::Instr * Inline::InlineApplyWithoutArrayArgument(IR::Instr *callInstr, const if (!callTargetStackSym->IsSingleDef()) { - return callInstr; + return instrNext; } if (!applyTargetInfo) { - return callInstr; + return instrNext; } bool safeThis = false; @@ -2745,7 +2747,7 @@ IR::Instr * Inline::InlineApplyWithoutArrayArgument(IR::Instr *callInstr, const callInstr->InsertBefore(bytecodeUses); } - return callInstr; + return instrNext; } void Inline::GetArgInstrsForCallAndApply(IR::Instr* callInstr, IR::Instr** implicitThisArgOut, IR::Instr** explicitThisArgOut, IR::Instr** argumentsOrArrayArgOut, uint &argOutCount) @@ -3015,7 +3017,7 @@ Inline::InlineCallApplyTarget_Shared(IR::Instr *callInstr, bool originalCallTarg // instrNext IR::Instr* instrNext = callInstr->m_next; - return InlineFunctionCommon(callInstr, originalCallTargetOpndIsJITOpt, originalCallTargetStackSym, inlineeData, inlinee, instrNext, returnValueOpnd, callInstr, nullptr, recursiveInlineDepth, safeThis, isApplyTarget)->m_prev; + return InlineFunctionCommon(callInstr, originalCallTargetOpndIsJITOpt, originalCallTargetStackSym, inlineeData, inlinee, instrNext, returnValueOpnd, callInstr, nullptr, recursiveInlineDepth, safeThis, isApplyTarget); } IR::Opnd * @@ -3029,6 +3031,7 @@ Inline::ConvertToInlineBuiltInArgOut(IR::Instr * argInstr) IR::Instr* Inline::InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, const FunctionJITTimeInfo * inlinerData, const StackSym *symCallerThis, bool* pIsInlined, uint callSiteId, uint recursiveInlineDepth) { + IR::Instr* instrNext = callInstr->m_next; Func *func = callInstr->m_func; Js::BuiltinFunction builtInId = Js::JavascriptLibrary::GetBuiltInForFuncInfo(funcInfo->GetLocalFunctionId()); @@ -3036,7 +3039,7 @@ Inline::InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, co if (PHASE_OFF(Js::InlineCallPhase, this->topFunc) || PHASE_OFF(Js::InlineCallPhase, func) || !this->topFunc->GetJITFunctionBody()->GetInParamsCount()) { - return callInstr; + return instrNext; } // Convert all the current ARG_OUT to ArgOut_A_InlineBuiltIn @@ -3045,7 +3048,7 @@ Inline::InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, co if (!GetDefInstr(linkOpnd)->GetSrc2()->IsSymOpnd()) { // There is no benefit of inlining.call() with no arguments. - return callInstr; + return instrNext; } *pIsInlined = true; @@ -3125,7 +3128,7 @@ Inline::InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, co } clonedArgout->SetSrc2(startCall->GetDst()); Assert(GetDefInstr(orgLinkOpnd) == argImplicitInstr); - return callInstr; + return instrNext; } bool diff --git a/lib/Backend/JnHelperMethodList.h b/lib/Backend/JnHelperMethodList.h index 1d834ac6273..4a86f9f55ce 100644 --- a/lib/Backend/JnHelperMethodList.h +++ b/lib/Backend/JnHelperMethodList.h @@ -205,6 +205,12 @@ HELPERCALLCHK(Op_SetNativeIntElementI_Int32, Js::JavascriptOperators::OP_SetNati HELPERCALLCHK(Op_SetNativeFloatElementI_Int32, Js::JavascriptOperators::OP_SetNativeFloatElementI_Int32, AttrCanThrow) HELPERCALLCHK(Op_SetNativeIntElementI_UInt32, Js::JavascriptOperators::OP_SetNativeIntElementI_UInt32, AttrCanThrow) HELPERCALLCHK(Op_SetNativeFloatElementI_UInt32, Js::JavascriptOperators::OP_SetNativeFloatElementI_UInt32, AttrCanThrow) +HELPERCALLCHK(Op_SetNativeIntElementI_NoConvert, Js::JavascriptOperators::OP_SetNativeIntElementI_NoConvert, AttrCanThrow) +HELPERCALLCHK(Op_SetNativeFloatElementI_NoConvert, Js::JavascriptOperators::OP_SetNativeFloatElementI_NoConvert, AttrCanThrow) +HELPERCALLCHK(Op_SetNativeIntElementI_Int32_NoConvert, Js::JavascriptOperators::OP_SetNativeIntElementI_Int32_NoConvert, AttrCanThrow) +HELPERCALLCHK(Op_SetNativeFloatElementI_Int32_NoConvert, Js::JavascriptOperators::OP_SetNativeFloatElementI_Int32_NoConvert, AttrCanThrow) +HELPERCALLCHK(Op_SetNativeIntElementI_UInt32_NoConvert, Js::JavascriptOperators::OP_SetNativeIntElementI_UInt32_NoConvert, AttrCanThrow) +HELPERCALLCHK(Op_SetNativeFloatElementI_UInt32_NoConvert, Js::JavascriptOperators::OP_SetNativeFloatElementI_UInt32_NoConvert, AttrCanThrow) HELPERCALLCHK(ScrArr_SetNativeIntElementC, Js::JavascriptArray::OP_SetNativeIntElementC, AttrCanNotBeReentrant) HELPERCALLCHK(ScrArr_SetNativeFloatElementC, Js::JavascriptArray::OP_SetNativeFloatElementC, AttrCanNotBeReentrant) HELPERCALLCHK(Op_DeleteElementI, Js::JavascriptOperators::OP_DeleteElementI, AttrCanThrow) diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index 52b5be80238..dcbfba67c79 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -9001,6 +9001,8 @@ Lowerer::LowerStElemI(IR::Instr * instr, Js::PropertyOperationFlags flags, bool AssertMsg(dst->IsIndirOpnd(), "Expected indirOpnd on StElementI"); + bool allowConvert = dst->AsIndirOpnd()->ConversionAllowed(); + #if !FLOATVAR if (dst->AsIndirOpnd()->GetBaseOpnd()->GetValueType().IsLikelyOptimizedTypedArray() && src1->IsRegOpnd()) { @@ -9085,15 +9087,17 @@ Lowerer::LowerStElemI(IR::Instr * instr, Js::PropertyOperationFlags flags, bool { helperMethod = srcType == TyVar ? IR::HelperOp_SetElementI_Int32 : - srcType == TyInt32 ? IR::HelperOp_SetNativeIntElementI_Int32 : - IR::HelperOp_SetNativeFloatElementI_Int32; + srcType == TyInt32 ? + (allowConvert ? IR::HelperOp_SetNativeIntElementI_Int32 : IR::HelperOp_SetNativeIntElementI_Int32_NoConvert) : + (allowConvert ? IR::HelperOp_SetNativeFloatElementI_Int32 : IR::HelperOp_SetNativeFloatElementI_Int32_NoConvert) ; } else if (indexOpnd->GetType() == TyUint32) { helperMethod = srcType == TyVar ? IR::HelperOp_SetElementI_UInt32 : - srcType == TyInt32 ? IR::HelperOp_SetNativeIntElementI_UInt32 : - IR::HelperOp_SetNativeFloatElementI_UInt32; + srcType == TyInt32 ? + (allowConvert ? IR::HelperOp_SetNativeIntElementI_UInt32 : IR::HelperOp_SetNativeIntElementI_UInt32_NoConvert) : + (allowConvert ? IR::HelperOp_SetNativeFloatElementI_UInt32 : IR::HelperOp_SetNativeFloatElementI_UInt32_NoConvert) ; } else { @@ -9111,8 +9115,9 @@ Lowerer::LowerStElemI(IR::Instr * instr, Js::PropertyOperationFlags flags, bool if (srcType != TyVar) { - helperMethod = - srcType == TyInt32 ? IR::HelperOp_SetNativeIntElementI : IR::HelperOp_SetNativeFloatElementI; + helperMethod = srcType == TyInt32 ? + (allowConvert ? IR::HelperOp_SetNativeIntElementI : IR::HelperOp_SetNativeIntElementI_NoConvert) : + (allowConvert ? IR::HelperOp_SetNativeFloatElementI : IR::HelperOp_SetNativeFloatElementI_NoConvert); } } diff --git a/lib/Backend/Opnd.cpp b/lib/Backend/Opnd.cpp index fa8a2814123..d9243e86299 100644 --- a/lib/Backend/Opnd.cpp +++ b/lib/Backend/Opnd.cpp @@ -2538,6 +2538,7 @@ IndirOpnd::New(RegOpnd *baseOpnd, int32 offset, IRType type, Func *func, bool do indirOpnd->m_type = type; indirOpnd->SetIsJITOptimizedReg(false); + indirOpnd->m_conversionAllowed = false; indirOpnd->m_kind = OpndKindIndir; @@ -2596,6 +2597,7 @@ IndirOpnd::CopyInternal(Func *func) newOpnd->canStoreTemp = this->canStoreTemp; newOpnd->SetOffset(m_offset, m_dontEncode); newOpnd->SetIsJITOptimizedReg(this->GetIsJITOptimizedReg()); + newOpnd->m_conversionAllowed = this->m_conversionAllowed; #if DBG_DUMP newOpnd->m_addrKind = m_addrKind; diff --git a/lib/Backend/Opnd.h b/lib/Backend/Opnd.h index 2f63cd1d28c..500f5403364 100644 --- a/lib/Backend/Opnd.h +++ b/lib/Backend/Opnd.h @@ -1662,6 +1662,8 @@ class IndirOpnd: public Opnd byte GetScale() const; void SetScale(byte scale); bool TryGetIntConstIndexValue(bool trySym, IntConstType *pValue, bool *pIsNotInt); + void AllowConversion(bool value) { m_conversionAllowed = value; } + bool ConversionAllowed() const { return m_conversionAllowed; } #if DBG_DUMP || defined(ENABLE_IR_VIEWER) const char16 * GetDescription(); IR::AddrOpndKind GetAddrKind() const; @@ -1678,6 +1680,7 @@ class IndirOpnd: public Opnd RegOpnd * m_indexOpnd; int32 m_offset; byte m_scale; + bool m_conversionAllowed; Func * m_func; // We need the allocator to copy the base and index... #if DBG_DUMP || defined(ENABLE_IR_VIEWER) diff --git a/lib/Common/ChakraCoreVersion.h b/lib/Common/ChakraCoreVersion.h index b7144c0f96d..a13c4fac2fa 100644 --- a/lib/Common/ChakraCoreVersion.h +++ b/lib/Common/ChakraCoreVersion.h @@ -17,7 +17,7 @@ // ChakraCore version number definitions (used in ChakraCore binary metadata) #define CHAKRA_CORE_MAJOR_VERSION 1 #define CHAKRA_CORE_MINOR_VERSION 11 -#define CHAKRA_CORE_PATCH_VERSION 9 +#define CHAKRA_CORE_PATCH_VERSION 10 #define CHAKRA_CORE_VERSION_RELEASE_QFE 0 // Redundant with PATCH_VERSION. Keep this value set to 0. // ------------- diff --git a/lib/Parser/Parse.cpp b/lib/Parser/Parse.cpp index 69159bcba42..b49d0feec31 100644 --- a/lib/Parser/Parse.cpp +++ b/lib/Parser/Parse.cpp @@ -5444,20 +5444,6 @@ void Parser::ParseFncDeclHelper(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, us } return false; }); - - if (pnodeFnc->IsBodyAndParamScopeMerged() && !fDeclaration && pnodeFnc->pnodeName != nullptr) - { - Assert(pnodeFnc->pnodeName->nop == knopVarDecl); - Symbol* funcSym = pnodeFnc->pnodeName->sym; - if (funcSym->GetPid()->GetTopRef()->GetFuncScopeId() > pnodeFnc->functionId) - { - // This is a function expression with name captured in the param scope. In non-eval, non-split cases the function - // name symbol is added to the body scope to make it accessible in the body. But if there is a function or var - // declaration with the same name in the body then adding to the body will fail. So in this case we have to add - // the name symbol to the param scope by splitting it. - pnodeFnc->ResetBodyAndParamScopeMerged(); - } - } } } diff --git a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp index 1d6c3f1d17e..8230046ea17 100644 --- a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp +++ b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp @@ -3091,6 +3091,17 @@ void ByteCodeGenerator::ProcessCapturedSym(Symbol *sym) Assert(sym->NeedsSlotAlloc(this, funcHome) || sym->GetIsGlobal() || sym->GetIsModuleImport() || sym->GetIsModuleExportStorage()); + if (sym->GetScope()->GetScopeType() == ScopeType_FuncExpr) + { + if ((funcHome->GetParamScope() && Scope::HasSymbolName(funcHome->GetParamScope(), sym->GetName())) || + (funcHome->IsBodyAndParamScopeMerged() && funcHome->GetBodyScope() && Scope::HasSymbolName(funcHome->GetBodyScope(), sym->GetName()))) + { + // Make sure the function expression scope gets instantiated, since we can't merge the name symbol into another scope. + // Make it an object, since that's the only case the code gen can currently handle. + sym->GetScope()->SetIsObject(); + } + } + // If this is not a local property, or not all its references can be tracked, or // it's not scoped to the function, or we're in debug mode, disable the delayed capture optimization. if (funcHome->IsGlobalFunction() || diff --git a/lib/Runtime/ByteCode/OpCodes.h b/lib/Runtime/ByteCode/OpCodes.h index d75019d0d04..9cfa26b03bb 100755 --- a/lib/Runtime/ByteCode/OpCodes.h +++ b/lib/Runtime/ByteCode/OpCodes.h @@ -464,21 +464,21 @@ MACRO_WMS( StEnvSlot, ElementSlotI2, None) MACRO_WMS( StInnerSlot, ElementSlotI2, None) MACRO_WMS( StLocalSlot, ElementSlotI1, None) MACRO_EXTEND_WMS( StParamSlot, ElementSlotI1, None) -MACRO_BACKEND_ONLY( StSlotChkUndecl, ElementSlot, OpSideEffect) -MACRO_EXTEND_WMS( StEnvSlotChkUndecl, ElementSlotI2, OpSideEffect) -MACRO_EXTEND_WMS( StInnerSlotChkUndecl, ElementSlotI2, OpSideEffect) -MACRO_EXTEND_WMS( StLocalSlotChkUndecl, ElementSlotI1, OpSideEffect) -MACRO_EXTEND_WMS( StParamSlotChkUndecl, ElementSlotI1, OpSideEffect) +MACRO_BACKEND_ONLY( StSlotChkUndecl, ElementSlot, OpSideEffect|OpNonIntTransfer) // Src1 is transferred to Dst, Src2 holds the same value as Dst to communicate Dst's liveness. +MACRO_EXTEND_WMS( StEnvSlotChkUndecl, ElementSlotI2, OpSideEffect|OpNonIntTransfer) +MACRO_EXTEND_WMS( StInnerSlotChkUndecl, ElementSlotI2, OpSideEffect|OpNonIntTransfer) +MACRO_EXTEND_WMS( StLocalSlotChkUndecl, ElementSlotI1, OpSideEffect|OpNonIntTransfer) +MACRO_EXTEND_WMS( StParamSlotChkUndecl, ElementSlotI1, OpSideEffect|OpNonIntTransfer) MACRO_EXTEND_WMS( StObjSlot, ElementSlot, OpSideEffect) MACRO_EXTEND_WMS( StInnerObjSlot, ElementSlotI2, OpSideEffect) MACRO_EXTEND_WMS( StLocalObjSlot, ElementSlotI1, OpSideEffect) MACRO_EXTEND_WMS( StParamObjSlot, ElementSlotI1, OpSideEffect) -MACRO_EXTEND_WMS( StLocalObjSlotChkUndecl, ElementSlotI1, OpSideEffect) -MACRO_EXTEND_WMS( StParamObjSlotChkUndecl, ElementSlotI1, OpSideEffect) +MACRO_EXTEND_WMS( StLocalObjSlotChkUndecl, ElementSlotI1, OpSideEffect|OpNonIntTransfer) +MACRO_EXTEND_WMS( StParamObjSlotChkUndecl, ElementSlotI1, OpSideEffect|OpNonIntTransfer) MACRO_EXTEND_WMS( StEnvObjSlot, ElementSlotI2, OpSideEffect) -MACRO_EXTEND_WMS( StObjSlotChkUndecl, ElementSlot, OpSideEffect) -MACRO_EXTEND_WMS( StInnerObjSlotChkUndecl, ElementSlotI2, OpSideEffect) -MACRO_EXTEND_WMS( StEnvObjSlotChkUndecl, ElementSlotI2, OpSideEffect) +MACRO_EXTEND_WMS( StObjSlotChkUndecl, ElementSlot, OpSideEffect|OpNonIntTransfer) +MACRO_EXTEND_WMS( StInnerObjSlotChkUndecl, ElementSlotI2, OpSideEffect|OpNonIntTransfer) +MACRO_EXTEND_WMS( StEnvObjSlotChkUndecl, ElementSlotI2, OpSideEffect|OpNonIntTransfer) MACRO_EXTEND_WMS( StModuleSlot, ElementSlotI2, OpSideEffect) MACRO_BACKEND_ONLY( LdAsmJsFunc, ElementSlot, OpTempNumberSources|OpCanCSE) MACRO_BACKEND_ONLY( LdWasmFunc, ElementSlot, OpSideEffect) diff --git a/lib/Runtime/ByteCode/Scope.h b/lib/Runtime/ByteCode/Scope.h index 2618f27b16b..5f9dc1ccb57 100644 --- a/lib/Runtime/ByteCode/Scope.h +++ b/lib/Runtime/ByteCode/Scope.h @@ -110,6 +110,18 @@ class Scope } } + static bool HasSymbolName(Scope * scope, const JsUtil::CharacterBuffer& name) + { + for (Symbol *sym = scope->m_symList; sym; sym = sym->GetNext()) + { + if (sym->GetName() == name) + { + return true; + } + } + return false; + } + void AddSymbol(Symbol *sym) { if (enclosingScope == nullptr) @@ -117,12 +129,9 @@ class Scope sym->SetIsGlobal(true); } sym->SetScope(this); - for (Symbol *symInList = m_symList; symInList; symInList = symInList->GetNext()) + if (HasSymbolName(this, sym->GetName())) { - if (symInList->GetName() == sym->GetName()) - { - return; - } + return; } sym->SetNext(m_symList); m_symList = sym; @@ -136,6 +145,7 @@ class Scope sym->SetIsGlobal(true); } sym->SetScope(this); + Assert(!HasSymbolName(this, sym->GetName())); sym->SetNext(m_symList); m_symList = sym; m_count++; diff --git a/lib/Runtime/Language/JavascriptOperators.cpp b/lib/Runtime/Language/JavascriptOperators.cpp index 90f94b6e184..4d67ce2a0b7 100644 --- a/lib/Runtime/Language/JavascriptOperators.cpp +++ b/lib/Runtime/Language/JavascriptOperators.cpp @@ -4644,6 +4644,120 @@ using namespace Js; return JavascriptOperators::SetProperty(receiver, object, propertyRecord->GetPropertyId(), value, scriptContext, flags); } + BOOL JavascriptOperators::OP_SetNativeIntElementI_NoConvert( + Var instance, + Var aElementIndex, + int32 iValue, + ScriptContext* scriptContext, + PropertyOperationFlags flags) + { + JIT_HELPER_REENTRANT_HEADER(Op_SetNativeIntElementI_NoConvert); + JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeIntElementI_NoConvert, Op_SetNativeIntElementI); + BOOL converted = OP_SetNativeIntElementI(instance, aElementIndex, iValue, scriptContext, flags); + if (converted) + { + AssertMsg(false, "Unexpected native array conversion"); + Js::Throw::FatalInternalError(); + } + return FALSE; + JIT_HELPER_END(Op_SetNativeIntElementI_NoConvert); + } + + BOOL JavascriptOperators::OP_SetNativeIntElementI_UInt32_NoConvert( + Var instance, + uint32 aElementIndex, + int32 iValue, + ScriptContext* scriptContext, + PropertyOperationFlags flags) + { + JIT_HELPER_REENTRANT_HEADER(Op_SetNativeIntElementI_UInt32_NoConvert); + JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeIntElementI_UInt32_NoConvert, Op_SetNativeIntElementI_UInt32); + BOOL converted = OP_SetNativeIntElementI_UInt32(instance, aElementIndex, iValue, scriptContext, flags); + if (converted) + { + AssertMsg(false, "Unexpected native array conversion"); + Js::Throw::FatalInternalError(); + } + return FALSE; + JIT_HELPER_END(Op_SetNativeIntElementI_UInt32_NoConvert); + } + + BOOL JavascriptOperators::OP_SetNativeIntElementI_Int32_NoConvert( + Var instance, + int32 aElementIndex, + int32 iValue, + ScriptContext* scriptContext, + PropertyOperationFlags flags) + { + JIT_HELPER_REENTRANT_HEADER(Op_SetNativeIntElementI_Int32_NoConvert); + JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeIntElementI_Int32_NoConvert, Op_SetNativeIntElementI_Int32); + BOOL converted = OP_SetNativeIntElementI_Int32(instance, aElementIndex, iValue, scriptContext, flags); + if (converted) + { + AssertMsg(false, "Unexpected native array conversion"); + Js::Throw::FatalInternalError(); + } + return FALSE; + JIT_HELPER_END(Op_SetNativeIntElementI_Int32_NoConvert); + } + + BOOL JavascriptOperators::OP_SetNativeFloatElementI_NoConvert( + Var instance, + Var aElementIndex, + ScriptContext* scriptContext, + PropertyOperationFlags flags, + double dValue) + { + JIT_HELPER_REENTRANT_HEADER(Op_SetNativeFloatElementI_NoConvert); + JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeFloatElementI_NoConvert, Op_SetNativeFloatElementI); + BOOL converted = OP_SetNativeFloatElementI(instance, aElementIndex, scriptContext, flags, dValue); + if (converted) + { + AssertMsg(false, "Unexpected native array conversion"); + Js::Throw::FatalInternalError(); + } + return FALSE; + JIT_HELPER_END(Op_SetNativeFloatElementI_NoConvert); + } + + BOOL JavascriptOperators::OP_SetNativeFloatElementI_UInt32_NoConvert( + Var instance, + uint32 aElementIndex, + ScriptContext* scriptContext, + PropertyOperationFlags flags, + double dValue) + { + JIT_HELPER_REENTRANT_HEADER(Op_SetNativeFloatElementI_UInt32_NoConvert); + JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeFloatElementI_NoConvert, Op_SetNativeFloatElementI_UInt32); + BOOL converted = OP_SetNativeFloatElementI_UInt32(instance, aElementIndex, scriptContext, flags, dValue); + if (converted) + { + AssertMsg(false, "Unexpected native array conversion"); + Js::Throw::FatalInternalError(); + } + return FALSE; + JIT_HELPER_END(Op_SetNativeFloatElementI_UInt32_NoConvert); + } + + BOOL JavascriptOperators::OP_SetNativeFloatElementI_Int32_NoConvert( + Var instance, + int32 aElementIndex, + ScriptContext* scriptContext, + PropertyOperationFlags flags, + double dValue) + { + JIT_HELPER_REENTRANT_HEADER(Op_SetNativeFloatElementI_Int32_NoConvert); + JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeFloatElementI_NoConvert, Op_SetNativeFloatElementI_Int32); + BOOL converted = OP_SetNativeFloatElementI_Int32(instance, aElementIndex, scriptContext, flags, dValue); + if (converted) + { + AssertMsg(false, "Unexpected native array conversion"); + Js::Throw::FatalInternalError(); + } + return FALSE; + JIT_HELPER_END(Op_SetNativeFloatElementI_Int32_NoConvert); + } + BOOL JavascriptOperators::OP_SetNativeIntElementI( Var instance, Var aElementIndex, @@ -9541,6 +9655,10 @@ using namespace Js; Var result = CALL_ENTRYPOINT(threadContext, marshalledFunction->GetEntryPoint(), function, CallInfo(flags, 1), thisVar); result = CrossSite::MarshalVar(requestContext, result); + // Set implicit call flags so we bail out if we're trying to propagate the value forward, e.g., from a compare. Subsequent calls + // to the getter may produce different results. + threadContext->AddImplicitCallFlags(ImplicitCall_Accessor); + return result; }); } diff --git a/lib/Runtime/Language/JavascriptOperators.h b/lib/Runtime/Language/JavascriptOperators.h index fe88a90575b..583488bd610 100644 --- a/lib/Runtime/Language/JavascriptOperators.h +++ b/lib/Runtime/Language/JavascriptOperators.h @@ -392,6 +392,12 @@ namespace Js static BOOL OP_SetElementI_UInt32(Var instance, uint32 aElementIndex, Var aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None); static BOOL OP_SetElementI_Int32(Var instance, int32 aElementIndex, Var aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None); static BOOL SetElementIHelper(Var receiver, RecyclableObject* object, Var index, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags); + static BOOL OP_SetNativeIntElementI_NoConvert(Var instance, Var aElementIndex, int32 aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None); + static BOOL OP_SetNativeIntElementI_UInt32_NoConvert(Var instance, uint32 aElementIndex, int32 aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None); + static BOOL OP_SetNativeIntElementI_Int32_NoConvert(Var instance, int aElementIndex, int32 aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None); + static BOOL OP_SetNativeFloatElementI_NoConvert(Var instance, Var aElementIndex, ScriptContext* scriptContext, PropertyOperationFlags flags, double value); + static BOOL OP_SetNativeFloatElementI_UInt32_NoConvert(Var instance, uint32 aElementIndex, ScriptContext* scriptContext, PropertyOperationFlags flags, double value); + static BOOL OP_SetNativeFloatElementI_Int32_NoConvert(Var instance, int aElementIndex, ScriptContext* scriptContext, PropertyOperationFlags flags, double value); static BOOL OP_SetNativeIntElementI(Var instance, Var aElementIndex, int32 aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None); static BOOL OP_SetNativeIntElementI_UInt32(Var instance, uint32 aElementIndex, int32 aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None); static BOOL OP_SetNativeIntElementI_Int32(Var instance, int aElementIndex, int32 aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None); diff --git a/lib/Runtime/Library/JavascriptProxy.cpp b/lib/Runtime/Library/JavascriptProxy.cpp index 6e026109735..ed817269a82 100644 --- a/lib/Runtime/Library/JavascriptProxy.cpp +++ b/lib/Runtime/Library/JavascriptProxy.cpp @@ -392,6 +392,8 @@ namespace Js } propertyDescriptor->SetValue(getGetResult); + threadContext->AddImplicitCallFlags(Js::ImplicitCall_External); + return TRUE; } @@ -1907,6 +1909,9 @@ namespace Js } } } + + threadContext->AddImplicitCallFlags(Js::ImplicitCall_External); + return TRUE; }