From 0ec1c7c2bdf68dfa61f67b46226c6ae451375b2f Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Tue, 5 Sep 2017 19:03:24 -0700 Subject: [PATCH] add inlining support for asm.js/wasm --- lib/Backend/BackendOpCodeAttrAsmJs.cpp | 20 +- lib/Backend/BackendOpCodeAttrAsmJs.h | 2 + lib/Backend/Func.cpp | 39 +- lib/Backend/Func.h | 13 +- lib/Backend/GlobOpt.cpp | 23 +- lib/Backend/GlobOpt.h | 2 +- lib/Backend/GlobOptBailOut.cpp | 27 +- lib/Backend/GlobOptBlockData.cpp | 12 +- lib/Backend/GlobOptBlockData.h | 2 +- lib/Backend/GlobOptFields.cpp | 22 +- lib/Backend/IR.cpp | 51 +- lib/Backend/IR.h | 2 + lib/Backend/IRBuilderAsmJs.cpp | 205 ++++--- lib/Backend/IRBuilderAsmJs.h | 16 +- lib/Backend/Inline.cpp | 199 +++++-- lib/Backend/InliningDecider.cpp | 9 +- lib/Backend/InliningHeuristics.cpp | 20 +- lib/Backend/InliningHeuristics.h | 5 +- lib/Backend/LinearScan.cpp | 4 +- lib/Backend/Lower.cpp | 25 +- lib/Backend/Lower.h | 2 +- lib/Backend/LowerMDShared.cpp | 29 +- lib/Backend/LowerMDShared.h | 1 - lib/Backend/NativeCodeGenerator.cpp | 6 +- lib/Backend/Sym.h | 1 + lib/Backend/amd64/LowererMDArch.cpp | 21 +- lib/Backend/amd64/LowererMDArch.h | 1 - lib/Backend/amd64/PeepsMD.cpp | 4 +- lib/Backend/arm/EncoderMD.cpp | 9 +- lib/Backend/arm/LowerMD.cpp | 7 +- lib/Backend/arm/LowerMD.h | 1 - lib/Backend/arm64/LowerMD.h | 1 - lib/Backend/i386/LowererMDArch.cpp | 152 +---- lib/Backend/i386/LowererMDArch.h | 1 - lib/Backend/i386/PeepsMD.cpp | 2 +- lib/Common/ConfigFlagsList.h | 2 + lib/Runtime/Base/FunctionBody.cpp | 38 +- lib/Runtime/ByteCode/AsmJsByteCodeDumper.cpp | 16 +- lib/Runtime/ByteCode/AsmJsByteCodeWriter.cpp | 16 +- lib/Runtime/ByteCode/AsmJsByteCodeWriter.h | 3 +- lib/Runtime/ByteCode/ByteCodeWriter.cpp | 6 +- lib/Runtime/ByteCode/IWasmByteCodeWriter.h | 4 +- lib/Runtime/ByteCode/LayoutTypesAsmJs.h | 16 +- lib/Runtime/ByteCode/OpCodeUtilAsmJs.cpp | 7 + lib/Runtime/ByteCode/OpCodeUtilAsmJs.h | 2 +- lib/Runtime/ByteCode/OpCodes.h | 4 +- lib/Runtime/ByteCode/OpCodesAsmJs.h | 36 +- lib/Runtime/ByteCode/OpCodesSimd.h | 11 - lib/Runtime/ByteCode/OpLayoutsAsmJs.h | 11 + lib/Runtime/ByteCode/WasmByteCodeWriter.cpp | 4 + .../Language/AsmJsByteCodeGenerator.cpp | 535 +++++++----------- lib/Runtime/Language/AsmJsByteCodeGenerator.h | 2 + lib/Runtime/Language/AsmJsEncoder.h | 8 - lib/Runtime/Language/AsmJsEncoder.inl | 34 -- lib/Runtime/Language/AsmJsEncoderHandler.inl | 8 - lib/Runtime/Language/AsmJsJitTemplate.h | 7 - lib/Runtime/Language/AsmJsModule.h | 3 - lib/Runtime/Language/AsmJsTypes.cpp | 26 +- lib/Runtime/Language/AsmJsTypes.h | 8 +- lib/Runtime/Language/DynamicProfileInfo.cpp | 88 +++ lib/Runtime/Language/DynamicProfileInfo.h | 3 + .../Language/InterpreterHandlerAsmJs.inl | 24 +- .../Language/InterpreterProcessOpCodeAsmJs.h | 21 +- .../Language/InterpreterStackFrame.cpp | 114 ++-- lib/Runtime/Language/InterpreterStackFrame.h | 11 +- .../Language/i386/AsmJsJitTemplate.cpp | 72 +-- lib/WasmReader/EmptyWasmByteCodeWriter.h | 4 +- lib/WasmReader/WasmBinaryReader.cpp | 1 + lib/WasmReader/WasmByteCodeGenerator.cpp | 140 +++-- lib/WasmReader/WasmByteCodeGenerator.h | 4 +- lib/WasmReader/WasmParseTree.h | 1 + test/AsmJs/cseBug.baseline | 2 +- test/AsmJs/rlexe.xml | 2 +- test/wasm/inlining.js | 72 +++ 74 files changed, 1203 insertions(+), 1099 deletions(-) create mode 100644 test/wasm/inlining.js diff --git a/lib/Backend/BackendOpCodeAttrAsmJs.cpp b/lib/Backend/BackendOpCodeAttrAsmJs.cpp index 5231ff3b87a..296a1d9d2c4 100644 --- a/lib/Backend/BackendOpCodeAttrAsmJs.cpp +++ b/lib/Backend/BackendOpCodeAttrAsmJs.cpp @@ -7,20 +7,14 @@ #ifdef ASMJS_PLAT namespace OpCodeAttrAsmJs { - // OpSideEffect: - // Opcode has side effect not just to the dst/src on the instruction. - // The opcode cannot be deadstored. (e.g. StFld, LdFld from DOM, call valueOf/toString/getter/setter) - // Doesn't include all "exit" script (e.g. LdThis doesn't have side effect for HostDispatch for exiting script to getting the name space parent) - // OpHasImplicitCall: - // Include all possible exit scripts, call valueOf/toString/getter/setter - // OpSerialized: - // Op is a serialized (indirected) variant of another op code enum OpCodeAttrEnum { None = 0, OpNoFallThrough = 1 << 0, // Opcode doesn't fallthrough in flow and its always jump to the return from this opcode. OpHasMultiSizeLayout = 1 << 1, - + OpHasProfiled = 1 << 2, + OpProfiled = 1 << 3, + OpByteCodeOnly = 1 << 4 }; static const int OpcodeAttributesAsmJs[] = @@ -65,6 +59,14 @@ namespace OpCodeAttrAsmJs return CheckHasFlag( OpHasMultiSizeLayout ); } + bool HasProfiledOp(Js::OpCodeAsmJs opcode) + { + return ((GetOpCodeAttributes(opcode) & OpHasProfiled) != 0); + } + bool IsProfiledOp(Js::OpCodeAsmJs opcode) + { + return ((GetOpCodeAttributes(opcode) & OpProfiled) != 0); + } }; // OpCodeAttrAsmJs #endif \ No newline at end of file diff --git a/lib/Backend/BackendOpCodeAttrAsmJs.h b/lib/Backend/BackendOpCodeAttrAsmJs.h index ec1ef80f264..d267d7516f6 100644 --- a/lib/Backend/BackendOpCodeAttrAsmJs.h +++ b/lib/Backend/BackendOpCodeAttrAsmJs.h @@ -9,5 +9,7 @@ namespace OpCodeAttrAsmJs bool HasFallThrough(Js::OpCodeAsmJs opcode); // True if the opcode has a small/large layout bool HasMultiSizeLayout(Js::OpCodeAsmJs opcode); + bool HasProfiledOp(Js::OpCodeAsmJs opcode); + bool IsProfiledOp(Js::OpCodeAsmJs opcode); }; #endif \ No newline at end of file diff --git a/lib/Backend/Func.cpp b/lib/Backend/Func.cpp index d96eaaea2c3..820defc27d9 100644 --- a/lib/Backend/Func.cpp +++ b/lib/Backend/Func.cpp @@ -68,6 +68,7 @@ Func::Func(JitArenaAllocator *alloc, JITTimeWorkItem * workItem, m_canDoInlineArgsOpt(true), m_doFastPaths(false), hasBailout(false), + firstIRTemp(0), hasBailoutInEHRegion(false), hasInstrNumber(false), maintainByteCodeOffset(true), @@ -78,7 +79,7 @@ Func::Func(JitArenaAllocator *alloc, JITTimeWorkItem * workItem, hasAnyStackNestedFunc(false), hasMarkTempObjects(false), postCallByteCodeOffset(postCallByteCodeOffset), - maxInlineeArgOutCount(0), + maxInlineeArgOutSize(0), returnValueRegSlot(returnValueRegSlot), firstActualStackOffset(-1), m_localVarSlotsOffset(Js::Constants::InvalidOffset), @@ -744,19 +745,27 @@ void Func::SetFirstArgOffset(IR::Instr* inlineeStart) int32 lastOffset; IR::Instr* arg = inlineeStart->GetNextArg(); - const auto lastArgOutStackSym = arg->GetDst()->AsSymOpnd()->m_sym->AsStackSym(); - lastOffset = lastArgOutStackSym->m_offset; - Assert(lastArgOutStackSym->m_isSingleDef); - const auto secondLastArgOutOpnd = lastArgOutStackSym->m_instrDef->GetSrc2(); - if (secondLastArgOutOpnd->IsSymOpnd()) - { - const auto secondLastOffset = secondLastArgOutOpnd->AsSymOpnd()->m_sym->AsStackSym()->m_offset; - if (secondLastOffset > lastOffset) + if (arg) + { + const auto lastArgOutStackSym = arg->GetDst()->AsSymOpnd()->m_sym->AsStackSym(); + lastOffset = lastArgOutStackSym->m_offset; + Assert(lastArgOutStackSym->m_isSingleDef); + const auto secondLastArgOutOpnd = lastArgOutStackSym->m_instrDef->GetSrc2(); + if (secondLastArgOutOpnd->IsSymOpnd()) { - lastOffset = secondLastOffset; + const auto secondLastOffset = secondLastArgOutOpnd->AsSymOpnd()->m_sym->AsStackSym()->m_offset; + if (secondLastOffset > lastOffset) + { + lastOffset = secondLastOffset; + } } + lastOffset += MachPtr; + } + else + { + Assert(this->GetTopFunc()->GetJITFunctionBody()->IsAsmJsMode()); + lastOffset = MachPtr; } - lastOffset += MachPtr; int32 firstActualStackOffset = lastOffset - ((this->actualCount + Js::Constants::InlineeMetaArgCount) * MachPtr); Assert((this->firstActualStackOffset == -1) || (this->firstActualStackOffset == firstActualStackOffset)); this->firstActualStackOffset = firstActualStackOffset; @@ -918,7 +927,7 @@ Int64RegPair Func::FindOrCreateInt64Pair(IR::Opnd* opnd) { Js::ArgSlot slotNumber = stackSym->GetArgSlotNum(); symPair.low = StackSym::NewArgSlotSym(slotNumber, this, pairType); - symPair.high = StackSym::NewArgSlotSym(slotNumber + 1, this, pairType); + symPair.high = StackSym::NewArgSlotSym(slotNumber, this, pairType); } else { @@ -1363,11 +1372,11 @@ Func::EnsureLoopParamSym() } void -Func::UpdateMaxInlineeArgOutCount(uint inlineeArgOutCount) +Func::UpdateMaxInlineeArgOutSize(uint inlineeArgOutSize) { - if (maxInlineeArgOutCount < inlineeArgOutCount) + if (this->maxInlineeArgOutSize < inlineeArgOutSize) { - maxInlineeArgOutCount = inlineeArgOutCount; + this->maxInlineeArgOutSize = inlineeArgOutSize; } } diff --git a/lib/Backend/Func.h b/lib/Backend/Func.h index 424a8f512ac..8bca8782464 100644 --- a/lib/Backend/Func.h +++ b/lib/Backend/Func.h @@ -687,6 +687,7 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece; uint32 inlineDepth; uint32 postCallByteCodeOffset; Js::RegSlot returnValueRegSlot; + Js::RegSlot firstIRTemp; Js::ArgSlot actualCount; int32 firstActualStackOffset; uint32 tryCatchNestingLevel; @@ -760,8 +761,8 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece; bool DoMaintainByteCodeOffset() const { return this->HasByteCodeOffset() && this->GetTopFunc()->maintainByteCodeOffset; } void StopMaintainByteCodeOffset() { this->GetTopFunc()->maintainByteCodeOffset = false; } Func * GetParentFunc() const { return parentFunc; } - uint GetMaxInlineeArgOutCount() const { return maxInlineeArgOutCount; } - void UpdateMaxInlineeArgOutCount(uint inlineeArgOutCount); + uint GetMaxInlineeArgOutSize() const { return this->maxInlineeArgOutSize; } + void UpdateMaxInlineeArgOutSize(uint inlineeArgOutSize); #if DBG_DUMP ptrdiff_t m_codeSize; #endif @@ -983,10 +984,10 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece; #if defined(_M_ARM32_OR_ARM64) int32 GetInlineeArgumentStackSize() { - int32 count = this->GetMaxInlineeArgOutCount(); - if (count) + int32 size = this->GetMaxInlineeArgOutSize(); + if (size) { - return ((count + 1) * MachPtr); // +1 for the dedicated zero out argc slot + return size + MachPtr; // +1 for the dedicated zero out argc slot } return 0; } @@ -1014,7 +1015,7 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece; #endif Func * const parentFunc; StackSym * m_inlineeFrameStartSym; - uint maxInlineeArgOutCount; + uint maxInlineeArgOutSize; const bool m_isBackgroundJIT; bool hasInstrNumber; bool maintainByteCodeOffset; diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index eaaf260e695..c2b9ede872f 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -1577,6 +1577,7 @@ GlobOpt::OptArguments(IR::Instr *instr) if (instr->HasAnyLoadHeapArgsOpCode()) { +#ifdef ENABLE_DEBUG_CONFIG_OPTIONS if (instr->m_func->IsStackArgsEnabled()) { if (instr->GetSrc1()->IsRegOpnd() && instr->m_func->GetJITFunctionBody()->GetInParamsCount() > 1) @@ -1593,6 +1594,7 @@ GlobOpt::OptArguments(IR::Instr *instr) } } } +#endif if (instr->m_func->GetJITFunctionBody()->GetInParamsCount() != 1 && !instr->m_func->IsStackArgsEnabled()) { @@ -2632,11 +2634,8 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved) } // Track calls after any pre-op bailouts have been inserted before the call, because they will need to restore out params. - // We don't inline in asmjs and hence we don't need to track calls in asmjs too, skipping this step for asmjs. - if (!GetIsAsmJSFunc()) - { - this->TrackCalls(instr); - } + + this->TrackCalls(instr); if (instr->GetSrc1()) { @@ -6764,17 +6763,13 @@ GlobOpt::OptConstFoldBranch(IR::Instr *instr, Value *src1Val, Value*src2Val, Val // this path would probably work outside of asm.js, but we should verify that if we ever hit this scenario Assert(GetIsAsmJSFunc()); constVal = 0; - if (src1Val->GetValueInfo()->TryGetIntConstantValue(&constVal) && constVal != 0) + if (!src1Val->GetValueInfo()->TryGetIntConstantValue(&constVal)) { - instr->FreeSrc1(); - if (instr->GetSrc2()) - { - instr->FreeSrc2(); - } - instr->m_opcode = Js::OpCode::Nop; - return true; + return false; } - return false; + + result = constVal == 0; + break; default: return false; diff --git a/lib/Backend/GlobOpt.h b/lib/Backend/GlobOpt.h index 958007ea42f..fa0df76ab58 100644 --- a/lib/Backend/GlobOpt.h +++ b/lib/Backend/GlobOpt.h @@ -585,7 +585,7 @@ class GlobOpt IR::Instr * GetExtendedArg(IR::Instr *instr); int GetBoundCheckOffsetForSimd(ValueType arrValueType, const IR::Instr *instr, const int oldOffset = -1); - IR::Instr * OptNewScObject(IR::Instr** instrPtr, Value* srcVal); + void OptNewScObject(IR::Instr** instrPtr, Value* srcVal); template bool OptConstFoldBinaryWasm(IR::Instr * *pInstr, const Value* src1, const Value* src2, Value **pDstVal); template diff --git a/lib/Backend/GlobOptBailOut.cpp b/lib/Backend/GlobOptBailOut.cpp index 41199ff8ae6..8d0504a7306 100644 --- a/lib/Backend/GlobOptBailOut.cpp +++ b/lib/Backend/GlobOptBailOut.cpp @@ -281,6 +281,10 @@ GlobOpt::CaptureArguments(BasicBlock *block, BailOutInfo * bailOutInfo, JitArena void GlobOpt::TrackByteCodeSymUsed(IR::Instr * instr, BVSparse * instrByteCodeStackSymUsed, PropertySym **pPropertySym) { + if(instr->m_func->GetJITFunctionBody()->IsAsmJsMode()) + { + return; + } IR::Opnd * src = instr->GetSrc1(); if (src) { @@ -427,7 +431,7 @@ GlobOpt::MarkNonByteCodeUsed(IR::Opnd * opnd) void GlobOpt::CaptureByteCodeSymUses(IR::Instr * instr) { - if (this->byteCodeUses) + if (this->byteCodeUses || this->func->GetJITFunctionBody()->IsAsmJsMode()) { // We already captured it before. return; @@ -493,7 +497,8 @@ GlobOpt::TrackCalls(IR::Instr * instr) Assert(stackSym->IsArgSlotSym()); if (stackSym->m_isInlinedArgSlot) { - this->currentBlock->globOptData.inlinedArgOutCount++; + uint size = TySize[instr->GetDst()->GetType()]; + this->currentBlock->globOptData.inlinedArgOutSize += size < MachPtr ? MachPtr : size; // We want to update the offsets only once: don't do in prepass. if (!this->IsLoopPrePass() && stackSym->m_offset >= 0) { @@ -523,7 +528,7 @@ GlobOpt::TrackCalls(IR::Instr * instr) this->currentBlock->globOptData.curFunc = instr->m_func; this->currentBlock->globOptData.curFunc = instr->m_func; - this->func->UpdateMaxInlineeArgOutCount(this->currentBlock->globOptData.inlinedArgOutCount); + this->func->UpdateMaxInlineeArgOutSize(this->currentBlock->globOptData.inlinedArgOutSize); this->EndTrackCall(instr); if (DoInlineArgsOpt(instr->m_func)) @@ -560,8 +565,8 @@ GlobOpt::TrackCalls(IR::Instr * instr) } EndTrackingOfArgObjSymsForInlinee(); - Assert(this->currentBlock->globOptData.inlinedArgOutCount >= instr->GetArgOutCount(/*getInterpreterArgOutCount*/ false)); - this->currentBlock->globOptData.inlinedArgOutCount -= instr->GetArgOutCount(/*getInterpreterArgOutCount*/ false); + Assert(this->currentBlock->globOptData.inlinedArgOutSize >= instr->GetArgOutSize(/*getInterpreterArgOutCount*/ false)); + this->currentBlock->globOptData.inlinedArgOutSize -= instr->GetArgOutSize(/*getInterpreterArgOutCount*/ false); break; case Js::OpCode::InlineeMetaArg: @@ -578,7 +583,7 @@ GlobOpt::TrackCalls(IR::Instr * instr) Func * currentFunc = instr->m_func->GetParentFunc(); stackSym->FixupStackOffset(currentFunc); } - this->currentBlock->globOptData.inlinedArgOutCount++; + this->currentBlock->globOptData.inlinedArgOutSize += MachPtr; break; } @@ -651,8 +656,8 @@ GlobOpt::TrackCalls(IR::Instr * instr) this->EndTrackCall(instr); } - Assert(this->currentBlock->globOptData.inlinedArgOutCount >= instr->GetArgOutCount(/*getInterpreterArgOutCount*/ false)); - this->currentBlock->globOptData.inlinedArgOutCount -= instr->GetArgOutCount(/*getInterpreterArgOutCount*/ false); + Assert(this->currentBlock->globOptData.inlinedArgOutSize >= instr->GetArgOutSize(/*getInterpreterArgOutCount*/ false)); + this->currentBlock->globOptData.inlinedArgOutSize -= instr->GetArgOutSize(/*getInterpreterArgOutCount*/ false); this->inInlinedBuiltIn = false; break; @@ -1043,7 +1048,11 @@ IR::ByteCodeUsesInstr * GlobOpt::InsertByteCodeUses(IR::Instr * instr, bool includeDef) { IR::ByteCodeUsesInstr * byteCodeUsesInstr = nullptr; - Assert(this->byteCodeUses); + if (!this->byteCodeUses) + { + Assert(this->isAsmJSFunc); + return nullptr; + } IR::RegOpnd * dstOpnd = nullptr; if (includeDef) { diff --git a/lib/Backend/GlobOptBlockData.cpp b/lib/Backend/GlobOptBlockData.cpp index 41da586c382..56413c303da 100644 --- a/lib/Backend/GlobOptBlockData.cpp +++ b/lib/Backend/GlobOptBlockData.cpp @@ -34,7 +34,7 @@ GlobOptBlockData::NullOutBlockData(GlobOpt* globOpt, Func* func) this->startCallCount = 0; this->argOutCount = 0; this->totalOutParamCount = 0; - this->inlinedArgOutCount = 0; + this->inlinedArgOutSize = 0; this->hasCSECandidates = false; this->curFunc = func; @@ -84,7 +84,7 @@ GlobOptBlockData::InitBlockData(GlobOpt* globOpt, Func* func) this->startCallCount = 0; this->argOutCount = 0; this->totalOutParamCount = 0; - this->inlinedArgOutCount = 0; + this->inlinedArgOutSize = 0; this->hasCSECandidates = false; this->curFunc = func; @@ -137,7 +137,7 @@ GlobOptBlockData::ReuseBlockData(GlobOptBlockData *fromData) this->startCallCount = fromData->startCallCount; this->argOutCount = fromData->argOutCount; this->totalOutParamCount = fromData->totalOutParamCount; - this->inlinedArgOutCount = fromData->inlinedArgOutCount; + this->inlinedArgOutSize = fromData->inlinedArgOutSize; this->hasCSECandidates = fromData->hasCSECandidates; this->stackLiteralInitFldDataMap = fromData->stackLiteralInitFldDataMap; @@ -180,7 +180,7 @@ GlobOptBlockData::CopyBlockData(GlobOptBlockData *fromData) this->startCallCount = fromData->startCallCount; this->argOutCount = fromData->argOutCount; this->totalOutParamCount = fromData->totalOutParamCount; - this->inlinedArgOutCount = fromData->inlinedArgOutCount; + this->inlinedArgOutSize = fromData->inlinedArgOutSize; this->hasCSECandidates = fromData->hasCSECandidates; this->changedSyms = fromData->changedSyms; @@ -348,7 +348,7 @@ void GlobOptBlockData::CloneBlockData(BasicBlock *const toBlockContext, BasicBlo this->startCallCount = fromData->startCallCount; this->argOutCount = fromData->argOutCount; this->totalOutParamCount = fromData->totalOutParamCount; - this->inlinedArgOutCount = fromData->inlinedArgOutCount; + this->inlinedArgOutSize = fromData->inlinedArgOutSize; this->hasCSECandidates = fromData->hasCSECandidates; // Although we don't need the data on loop pre pass, we need to do it for the loop header @@ -835,7 +835,7 @@ GlobOptBlockData::MergeBlockData( Assert(this->startCallCount == fromData->startCallCount); Assert(this->argOutCount == fromData->argOutCount); Assert(this->totalOutParamCount == fromData->totalOutParamCount); - Assert(this->inlinedArgOutCount == fromData->inlinedArgOutCount); + Assert(this->inlinedArgOutSize == fromData->inlinedArgOutSize); // stackLiteralInitFldDataMap is a union of the stack literal from two path. // Although we don't need the data on loop prepass, we need to do it for the loop header diff --git a/lib/Backend/GlobOptBlockData.h b/lib/Backend/GlobOptBlockData.h index b0070740e7b..2f25f52b77a 100644 --- a/lib/Backend/GlobOptBlockData.h +++ b/lib/Backend/GlobOptBlockData.h @@ -194,7 +194,7 @@ class GlobOptBlockData CapturedValues * capturedValues; BVSparse * changedSyms; - uint inlinedArgOutCount; + uint inlinedArgOutSize; bool hasCSECandidates; diff --git a/lib/Backend/GlobOptFields.cpp b/lib/Backend/GlobOptFields.cpp index 5ebc0007a9e..a86f1f64d19 100644 --- a/lib/Backend/GlobOptFields.cpp +++ b/lib/Backend/GlobOptFields.cpp @@ -2040,8 +2040,8 @@ GlobOpt::AssertCanCopyPropOrCSEFieldLoad(IR::Instr * instr) || instr->m_opcode == Js::OpCode::CheckFixedFld || instr->m_opcode == Js::OpCode::CheckPropertyGuardAndLoadType); - Assert(instr->m_opcode == Js::OpCode::CheckFixedFld || instr->GetDst()->GetType() == TyVar); - Assert(instr->GetSrc1()->GetType() == TyVar); + Assert(instr->m_opcode == Js::OpCode::CheckFixedFld || instr->GetDst()->GetType() == TyVar || instr->m_func->GetJITFunctionBody()->IsAsmJsMode()); + Assert(instr->GetSrc1()->GetType() == TyVar || instr->m_func->GetJITFunctionBody()->IsAsmJsMode()); Assert(instr->GetSrc1()->AsSymOpnd()->m_sym->IsPropertySym()); Assert(instr->GetSrc2() == nullptr); } @@ -2755,24 +2755,14 @@ GlobOpt::ProcessPropOpInTypeCheckSeq(IR::Instr* instr, IR::PropertySymOpnd *opnd return isSpecialized; } -IR::Instr* +void GlobOpt::OptNewScObject(IR::Instr** instrPtr, Value* srcVal) { IR::Instr *&instr = *instrPtr; - if (IsLoopPrePass()) + if (!instr->IsNewScObjectInstr() || IsLoopPrePass() || !this->DoFieldRefOpts() || PHASE_OFF(Js::ObjTypeSpecNewObjPhase, this->func)) { - return instr; - } - - if (PHASE_OFF(Js::ObjTypeSpecNewObjPhase, this->func) || !this->DoFieldRefOpts()) - { - return instr; - } - - if (!instr->IsNewScObjectInstr()) - { - return nullptr; + return; } bool isCtorInlined = instr->m_opcode == Js::OpCode::NewScObjectNoCtor; @@ -2787,8 +2777,6 @@ GlobOpt::OptNewScObject(IR::Instr** instrPtr, Value* srcVal) { GenerateBailAtOperation(instrPtr, IR::BailOutFailedCtorGuardCheck); } - - return instr; } void diff --git a/lib/Backend/IR.cpp b/lib/Backend/IR.cpp index e8aae5cfca2..644605764e2 100644 --- a/lib/Backend/IR.cpp +++ b/lib/Backend/IR.cpp @@ -3479,15 +3479,54 @@ uint Instr::GetArgOutCount(bool getInterpreterArgOutCount) Assert(opcode == Js::OpCode::StartCall || opcode == Js::OpCode::InlineeEnd || opcode == Js::OpCode::InlineBuiltInEnd|| opcode == Js::OpCode::InlineNonTrackingBuiltInEnd || opcode == Js::OpCode::EndCallForPolymorphicInlinee || opcode == Js::OpCode::LoweredStartCall); - if (!getInterpreterArgOutCount) + + Assert(!getInterpreterArgOutCount || opcode == Js::OpCode::StartCall); + uint argOutCount = !this->GetSrc2() || !getInterpreterArgOutCount || m_func->GetJITFunctionBody()->IsAsmJsMode() + ? this->GetSrc1()->AsIntConstOpnd()->AsUint32() + : this->GetSrc2()->AsIntConstOpnd()->AsUint32(); + + return (uint)argOutCount; +} + +uint Instr::GetAsmJsArgOutSize() +{ + switch (m_opcode) + { + case Js::OpCode::StartCall: + case Js::OpCode::LoweredStartCall: + return GetSrc2()->AsIntConstOpnd()->AsUint32(); + + case Js::OpCode::InlineeEnd: { - return this->GetSrc1()->AsIntConstOpnd()->AsUint32(); + // StartCall instr has the size, so walk back to it + IR::Instr *argInstr = this; + while(argInstr->m_opcode != Js::OpCode::StartCall && argInstr->m_opcode != Js::OpCode::LoweredStartCall) + { + argInstr = argInstr->GetSrc2()->GetStackSym()->GetInstrDef(); + } + // add StartCall arg size with inlinee meta args for full size + uint size = UInt32Math::Add(argInstr->GetSrc2()->AsIntConstOpnd()->AsUint32(), Js::Constants::InlineeMetaArgCount * MachPtr); + return size; } + default: + Assert(UNREACHED); + return 0; + } +} - Assert(opcode == Js::OpCode::StartCall); - IntConstType argOutCount = !this->GetSrc2() ? this->GetSrc1()->AsIntConstOpnd()->GetValue() : this->GetSrc2()->AsIntConstOpnd()->GetValue(); - Assert(argOutCount >= 0 && argOutCount < UINT32_MAX); - return (uint)argOutCount; +uint Instr::GetArgOutSize(bool getInterpreterArgOutCount) +{ + Js::OpCode opcode = this->m_opcode; + Assert(opcode == Js::OpCode::StartCall || + opcode == Js::OpCode::InlineeEnd || opcode == Js::OpCode::InlineBuiltInEnd || opcode == Js::OpCode::InlineNonTrackingBuiltInEnd || + opcode == Js::OpCode::EndCallForPolymorphicInlinee || opcode == Js::OpCode::LoweredStartCall); + + Assert(!getInterpreterArgOutCount || opcode == Js::OpCode::StartCall); + if (m_func->GetJITFunctionBody()->IsAsmJsMode()) + { + return GetAsmJsArgOutSize(); + } + return UInt32Math::Mul(GetArgOutCount(getInterpreterArgOutCount)); } PropertySymOpnd *Instr::GetPropertySymOpnd() const diff --git a/lib/Backend/IR.h b/lib/Backend/IR.h index e1145d1aaee..9ba06b9086e 100644 --- a/lib/Backend/IR.h +++ b/lib/Backend/IR.h @@ -434,6 +434,8 @@ class Instr IR::Instr* GetArgOutSnapshot(); FixedFieldInfo* GetFixedFunction() const; uint GetArgOutCount(bool getInterpreterArgOutCount); + uint GetArgOutSize(bool getInterpreterArgOutCount); + uint GetAsmJsArgOutSize(); IR::PropertySymOpnd *GetPropertySymOpnd() const; bool CallsAccessor(IR::PropertySymOpnd * methodOpnd = nullptr); bool CallsGetter(); diff --git a/lib/Backend/IRBuilderAsmJs.cpp b/lib/Backend/IRBuilderAsmJs.cpp index 53b4edb55e3..8d7f339da02 100644 --- a/lib/Backend/IRBuilderAsmJs.cpp +++ b/lib/Backend/IRBuilderAsmJs.cpp @@ -18,7 +18,7 @@ IRBuilderAsmJs::Build() uint32 offset; uint32 statementIndex = m_statementReader.GetStatementIndex(); - m_argStack = JitAnew(m_tempAlloc, SList, m_tempAlloc); + m_argStack = JitAnew(m_tempAlloc, SListCounted, m_tempAlloc); m_tempList = JitAnew(m_tempAlloc, SList, m_tempAlloc); m_argOffsetStack = JitAnew(m_tempAlloc, SList, m_tempAlloc); @@ -44,7 +44,7 @@ IRBuilderAsmJs::Build() m_firstsType[i] += m_firstsType[i - 1]; } - m_firstIRTemp = m_firstsType[m_firstsTypeCount - 1]; + m_func->firstIRTemp = m_firstsType[m_firstsTypeCount - 1]; m_simdOpcodesMap = JitAnewArrayZ(m_tempAlloc, Js::OpCode, Js::Simd128AsmJsOpcodeCount()); { @@ -64,7 +64,7 @@ IRBuilderAsmJs::Build() } // we will be using lower space for type specialized syms, so bump up where new temp syms can be created - m_func->m_symTable->IncreaseStartingID(m_firstIRTemp - m_func->m_symTable->GetMaxSymID()); + m_func->m_symTable->IncreaseStartingID(m_func->firstIRTemp); if (m_tempCount > 0) { @@ -207,8 +207,7 @@ IRBuilderAsmJs::Build() void IRBuilderAsmJs::LoadNativeCodeData() { - Assert(m_func->IsTopFunc()); - if (m_func->IsOOPJIT()) + if (m_func->IsOOPJIT() && m_func->IsTopFunc()) { IR::RegOpnd * nativeDataOpnd = IR::RegOpnd::New(TyVar, m_func); IR::Instr * instr = IR::Instr::New(Js::OpCode::LdNativeCodeData, nativeDataOpnd, m_func); @@ -732,12 +731,10 @@ void IRBuilderAsmJs::BuildConstantLoads() { Js::Var * constTable = (Js::Var *)m_func->GetJITFunctionBody()->GetConstTable(); - // Load FrameDisplay IR::RegOpnd * asmJsEnvDstOpnd = BuildDstOpnd(AsmJsRegSlots::ModuleMemReg, TyVar); IR::Instr * ldAsmJsEnvInstr = IR::Instr::New(Js::OpCode::LdAsmJsEnv, asmJsEnvDstOpnd, m_func); AddInstr(ldAsmJsEnvInstr, Js::Constants::NoByteCodeOffset); - // Load heap buffer if (m_asmFuncInfo->UsesHeapBuffer()) { @@ -781,6 +778,20 @@ IRBuilderAsmJs::BuildConstantLoads() } ); break; +#if TARGET_64 + case WAsmJs::INT64: + CreateLoadConstInstrForType( + table, + regAllocated, + info.constCount, + info.constSrcByteOffset, + TyInt64, + ValueType::GetInt(false), + Js::OpCode::Ld_I4, + [&](IR::Instr* instr, int64 val) {} + ); + break; +#endif case WAsmJs::FLOAT32: CreateLoadConstInstrForType( table, @@ -1062,8 +1073,6 @@ IRBuilderAsmJs::BuildEmpty(Js::OpCodeAsmJs newOpcode, uint32 offset) break; case Js::AsmJsRetType::Which::Void: - retSlot = GetRegSlotFromVarReg(0); - regOpnd = BuildDstOpnd(retSlot, TyVar); break; default: @@ -1077,7 +1086,10 @@ IRBuilderAsmJs::BuildEmpty(Js::OpCodeAsmJs newOpcode, uint32 offset) } } - instr->SetSrc1(regOpnd); + if (regOpnd) + { + instr->SetSrc1(regOpnd); + } AddInstr(instr, offset); break; @@ -1127,40 +1139,40 @@ IRBuilderAsmJs::BuildElementSlot(Js::OpCodeAsmJs newOpcode, uint32 offset, int32 switch (newOpcode) { case Js::OpCodeAsmJs::LdSlot: - valueRegSlot = GetRegSlotFromVarReg(value); + valueRegSlot = GetRegSlotFromPtrReg(value); slotOpnd = BuildFieldOpnd(AsmJsRegSlots::ModuleMemReg, slotIndex, PropertyKindSlotArray, TyVar); - regOpnd = BuildDstOpnd(valueRegSlot, TyVar); + regOpnd = BuildDstOpnd(valueRegSlot, TyMachReg); instr = IR::Instr::New(Js::OpCode::LdSlot, regOpnd, slotOpnd, m_func); break; case Js::OpCodeAsmJs::LdSlotArr: - valueRegSlot = GetRegSlotFromVarReg(value); + valueRegSlot = GetRegSlotFromPtrReg(value); slotOpnd = BuildFieldOpnd(AsmJsRegSlots::ModuleMemReg, slotIndex, PropertyKindSlots, TyVar); - regOpnd = BuildDstOpnd(valueRegSlot, TyVar); + regOpnd = BuildDstOpnd(valueRegSlot, TyMachReg); instr = IR::Instr::New(Js::OpCode::LdSlot, regOpnd, slotOpnd, m_func); break; case Js::OpCodeAsmJs::LdArr_Func: { - IR::RegOpnd * baseOpnd = BuildSrcOpnd(GetRegSlotFromVarReg(instance), TyVar); + IR::RegOpnd * baseOpnd = BuildSrcOpnd(GetRegSlotFromPtrReg(instance), TyMachReg); IR::RegOpnd * indexOpnd = BuildSrcOpnd(GetRegSlotFromIntReg(slotIndex), TyUint32); - IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(baseOpnd, indexOpnd, TyVar, m_func); + IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(baseOpnd, indexOpnd, TyMachReg, m_func); - regOpnd = BuildDstOpnd(GetRegSlotFromVarReg(value), TyVar); + regOpnd = BuildDstOpnd(GetRegSlotFromPtrReg(value), TyMachReg); instr = IR::Instr::New(Js::OpCode::LdAsmJsFunc, regOpnd, indirOpnd, m_func); break; } case Js::OpCodeAsmJs::LdArr_WasmFunc: { - IR::RegOpnd * baseOpnd = BuildSrcOpnd(GetRegSlotFromVarReg(instance), TyVar); + IR::RegOpnd * baseOpnd = BuildSrcOpnd(GetRegSlotFromPtrReg(instance), TyMachReg); IR::RegOpnd * indexOpnd = BuildSrcOpnd(GetRegSlotFromIntReg(slotIndex), TyUint32); - regOpnd = BuildDstOpnd(GetRegSlotFromVarReg(value), TyVar); + regOpnd = BuildDstOpnd(GetRegSlotFromPtrReg(value), TyMachReg); instr = IR::Instr::New(Js::OpCode::LdWasmFunc, regOpnd, baseOpnd, indexOpnd, m_func); break; } @@ -1287,17 +1299,20 @@ IRBuilderAsmJs::BuildStartCall(Js::OpCodeAsmJs newOpcode, uint32 offset) Assert(!OpCodeAttrAsmJs::HasMultiSizeLayout(newOpcode)); const unaligned Js::OpLayoutStartCall * layout = m_jnReader.StartCall(); - IR::RegOpnd * dstOpnd = IR::RegOpnd::New(TyVar, m_func); - IR::IntConstOpnd * srcOpnd = IR::IntConstOpnd::New(layout->ArgCount, TyInt32, m_func); + IR::RegOpnd * dstOpnd = IR::RegOpnd::New(TyMachReg, m_func); IR::Instr * instr = nullptr; StackSym * symDst = nullptr; IR::SymOpnd * argDst = nullptr; IR::AddrOpnd * addrOpnd = nullptr; + IR::IntConstOpnd * srcOpnd = nullptr; switch (newOpcode) { case Js::OpCodeAsmJs::I_StartCall: - instr = IR::Instr::New(Js::OpCode::StartCall, dstOpnd, srcOpnd, m_func); + srcOpnd = IR::IntConstOpnd::New(layout->ArgCount - MachPtr, TyUint16, m_func); + instr = IR::Instr::New(Js::OpCode::StartCall, dstOpnd, m_func); + // this is the arg size, we will set src1 to be the actual arg count + instr->SetSrc2(srcOpnd); AddInstr(instr, offset); @@ -1307,8 +1322,10 @@ IRBuilderAsmJs::BuildStartCall(Js::OpCodeAsmJs newOpcode, uint32 offset) break; case Js::OpCodeAsmJs::StartCall: - instr = IR::Instr::New(Js::OpCode::StartCall, dstOpnd, srcOpnd, m_func); - + srcOpnd = IR::IntConstOpnd::New(layout->ArgCount, TyUint16, m_func); + instr = IR::Instr::New(Js::OpCode::StartCall, dstOpnd, m_func); + // we will set src1 to be the actual arg count + instr->SetSrc2(srcOpnd); AddInstr(instr, offset); m_argStack->Push(instr); @@ -1319,7 +1336,7 @@ IRBuilderAsmJs::BuildStartCall(Js::OpCodeAsmJs newOpcode, uint32 offset) symDst = m_func->m_symTable->GetArgSlotSym(1); - argDst = IR::SymOpnd::New(symDst, TyVar, m_func); + argDst = IR::SymOpnd::New(symDst, TyMachReg, m_func); instr = IR::Instr::New(Js::OpCode::ArgOut_A, argDst, addrOpnd, m_func); @@ -1577,21 +1594,28 @@ IRBuilderAsmJs::BuildAsmTypedArr(Js::OpCodeAsmJs newOpcode, uint32 offset, uint3 AddInstr(instr, offset); } +template +void +IRBuilderAsmJs::BuildProfiledAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset) +{ + Assert(OpCodeAttrAsmJs::HasMultiSizeLayout(newOpcode)); + auto layout = m_jnReader.GetLayout>>(); + BuildAsmCall(newOpcode, offset, layout->ArgCount, layout->Return, layout->Function, layout->ReturnType, layout->profileId); +} + template void IRBuilderAsmJs::BuildAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset) { Assert(OpCodeAttrAsmJs::HasMultiSizeLayout(newOpcode)); auto layout = m_jnReader.GetLayout>(); - BuildAsmCall(newOpcode, offset, layout->ArgCount, layout->Return, layout->Function, layout->ReturnType); + BuildAsmCall(newOpcode, offset, layout->ArgCount, layout->Return, layout->Function, layout->ReturnType, Js::Constants::NoProfileId); } void -IRBuilderAsmJs::BuildAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::ArgSlot argCount, Js::RegSlot ret, Js::RegSlot function, int8 returnType) +IRBuilderAsmJs::BuildAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::ArgSlot argCount, Js::RegSlot ret, Js::RegSlot function, int8 returnType, Js::ProfileId profileId) { -#if DBG - int count = 0; -#endif + Js::ArgSlot count = 0; IR::Instr * instr = nullptr; IR::RegOpnd * dstOpnd = nullptr; @@ -1602,8 +1626,9 @@ IRBuilderAsmJs::BuildAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::ArgSl switch (newOpcode) { case Js::OpCodeAsmJs::I_Call: - srcRegSlot = GetRegSlotFromVarReg(function); - srcOpnd = BuildSrcOpnd(srcRegSlot, TyVar); + case Js::OpCodeAsmJs::ProfiledI_Call: + srcRegSlot = GetRegSlotFromPtrReg(function); + srcOpnd = BuildSrcOpnd(srcRegSlot, TyMachReg); switch ((Js::AsmJsRetType::Which)returnType) { @@ -1678,15 +1703,22 @@ IRBuilderAsmJs::BuildAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::ArgSl dstRegSlot = GetRegSlotFromSimd128Reg(ret); dstOpnd = BuildDstOpnd(dstRegSlot, TySimd128U16); break; - default: + default: Assume(UNREACHED); } - instr = IR::Instr::New(Js::OpCode::AsmJsCallI, m_func); - instr->SetSrc1(srcOpnd); - if (dstOpnd) { - instr->SetDst(dstOpnd); + if (profileId == Js::Constants::NoProfileId) + { + instr = IR::Instr::New(Js::OpCode::AsmJsCallI, dstOpnd, srcOpnd, m_func); + } + else + { + Assert(newOpcode == Js::OpCodeAsmJs::ProfiledI_Call); + instr = IR::ProfiledInstr::New(Js::OpCode::AsmJsCallI, dstOpnd, srcOpnd, m_func); + AssertOrFailFast(profileId < m_func->GetJITFunctionBody()->GetProfiledCallSiteCount()); + instr->AsProfiledInstr()->u.profileId = profileId; + } } argOffset = m_argOffsetStack->Pop(); @@ -1694,11 +1726,11 @@ IRBuilderAsmJs::BuildAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::ArgSl break; case Js::OpCodeAsmJs::Call: - srcRegSlot = GetRegSlotFromVarReg(function); - srcOpnd = BuildSrcOpnd(srcRegSlot, TyVar); + srcRegSlot = GetRegSlotFromPtrReg(function); + srcOpnd = BuildSrcOpnd(srcRegSlot, TyMachReg); - dstRegSlot = GetRegSlotFromVarReg(ret); - dstOpnd = BuildDstOpnd(dstRegSlot, TyVar); + dstRegSlot = GetRegSlotFromPtrReg(ret); + dstOpnd = BuildDstOpnd(dstRegSlot, TyMachReg); instr = IR::Instr::New(Js::OpCode::AsmJsCallE, dstOpnd, srcOpnd, m_func); break; @@ -1710,9 +1742,9 @@ IRBuilderAsmJs::BuildAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::ArgSl IR::Instr * argInstr = nullptr; IR::Instr * prevInstr = instr; - for (argInstr = m_argStack->Pop(); argInstr && argInstr->m_opcode != Js::OpCode::StartCall; argInstr = m_argStack->Pop()) + for (argInstr = m_argStack->Pop(); argInstr->m_opcode != Js::OpCode::StartCall; argInstr = m_argStack->Pop()) { - if (newOpcode == Js::OpCodeAsmJs::I_Call) + if (newOpcode == Js::OpCodeAsmJs::I_Call || newOpcode == Js::OpCodeAsmJs::ProfiledI_Call) { #if _M_IX86 argOffset -= argInstr->GetDst()->GetSize(); @@ -1737,12 +1769,13 @@ IRBuilderAsmJs::BuildAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::ArgSl #endif #endif -#if DBG count++; -#endif } + + Assert(argInstr->m_opcode == Js::OpCode::StartCall); + argInstr->SetSrc1(IR::IntConstOpnd::New(count, TyUint16, m_func)); + Assert(argOffset == 0); - AnalysisAssert(argInstr); prevInstr->SetSrc2(argInstr->GetDst()); #ifdef ENABLE_SIMDJS @@ -1756,7 +1789,7 @@ IRBuilderAsmJs::BuildAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::ArgSl IR::Instr * instrArg = m_tempList->Pop(); // record argument position and make room for implicit args instrArg->GetDst()->GetStackSym()->m_argPosition = i; - if (newOpcode == Js::OpCodeAsmJs::I_Call) + if (newOpcode == Js::OpCodeAsmJs::I_Call || newOpcode == Js::OpCodeAsmJs::ProfiledI_Call) { // implicit func obj arg instrArg->GetDst()->GetStackSym()->m_argPosition += 1; @@ -1824,39 +1857,17 @@ IRBuilderAsmJs::BuildAsmReg1(Js::OpCodeAsmJs newOpcode, uint32 offset) void IRBuilderAsmJs::BuildAsmReg1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot dstReg) { + Assert(newOpcode == Js::OpCodeAsmJs::CurrentMemory_Int); + Js::RegSlot dstRegSlot = GetRegSlotFromIntReg(dstReg); + IR::RegOpnd * dstOpnd = BuildDstOpnd(dstRegSlot, TyInt32); + IR::IntConstOpnd* constZero = IR::IntConstOpnd::New(0, TyInt32, m_func); + IR::IntConstOpnd* constSixteen = IR::IntConstOpnd::New(16, TyUint8, m_func); - if (newOpcode == Js::OpCodeAsmJs::LdUndef) - { - - Js::RegSlot dstRegSlot = GetRegSlotFromVarReg(dstReg); - IR::RegOpnd * dstOpnd = BuildDstOpnd(dstRegSlot, TyVar); - - if (dstOpnd->m_sym->m_isSingleDef) - { - dstOpnd->m_sym->m_isConst = true; - dstOpnd->m_sym->m_isNotInt = true; - } - - IR::AddrOpnd * addrOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndefinedAddr(), IR::AddrOpndKindDynamicVar, m_func, true); - addrOpnd->SetValueType(ValueType::Undefined); - - IR::Instr * instr = IR::Instr::New(Js::OpCode::Ld_A, dstOpnd, addrOpnd, m_func); - AddInstr(instr, offset); - } - else - { - Assert(newOpcode == Js::OpCodeAsmJs::CurrentMemory_Int); - Js::RegSlot dstRegSlot = GetRegSlotFromIntReg(dstReg); - IR::RegOpnd * dstOpnd = BuildDstOpnd(dstRegSlot, TyInt32); - IR::IntConstOpnd* constZero = IR::IntConstOpnd::New(0, TyInt32, m_func); - IR::IntConstOpnd* constSixteen = IR::IntConstOpnd::New(16, TyUint8, m_func); - - IR::Instr * instr = m_asmFuncInfo->UsesHeapBuffer() ? - IR::Instr::New(Js::OpCode::ShrU_I4, dstOpnd, BuildSrcOpnd(AsmJsRegSlots::LengthReg, TyUint32), constSixteen, m_func) : - IR::Instr::New(Js::OpCode::Ld_I4, dstOpnd, constZero, m_func); + IR::Instr * instr = m_asmFuncInfo->UsesHeapBuffer() ? + IR::Instr::New(Js::OpCode::ShrU_I4, dstOpnd, BuildSrcOpnd(AsmJsRegSlots::LengthReg, TyUint32), constSixteen, m_func) : + IR::Instr::New(Js::OpCode::Ld_I4, dstOpnd, constZero, m_func); - AddInstr(instr, offset); - } + AddInstr(instr, offset); } @@ -1873,6 +1884,7 @@ IRBuilderAsmJs::BuildAsmReg1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSl #define LongProc(v) GetRegSlotFromInt64Reg(v) #define FloatProc(v) GetRegSlotFromFloatReg(v) #define DoubleProc(v) GetRegSlotFromDoubleReg(v) +#define PtrProc(v) GetRegSlotFromPtrReg(v) #define IntConstProc(v) v #define LongConstProc(v) v #define FloatConstProc(v) v @@ -1910,6 +1922,7 @@ IRBuilderAsmJs::BuildAsmReg1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSl #undef _PREFIX_HELPER #undef _M #undef RegProc +#undef PtrProc #undef IntProc #undef LongProc #undef FloatProc @@ -1950,7 +1963,12 @@ void IRBuilderAsmJs::BuildArgOut(IR::Opnd* srcOpnd, uint32 dstRegSlot, uint32 of } else { - symDst = StackSym::NewArgSlotSym(dstArgSlot, m_func, type); + // Some arg types may use multiple slots, so can't rely on count from bytecode + Assert(dstArgSlot >= m_argStack->Count()); + uint slotIndex = m_argStack->Count(); + AssertOrFailFast(slotIndex < UINT16_MAX); + + symDst = StackSym::NewArgSlotSym((Js::ArgSlot)slotIndex, m_func, type); symDst->m_allocated = true; } @@ -1968,7 +1986,7 @@ void IRBuilderAsmJs::BuildArgOut(IR::Opnd* srcOpnd, uint32 dstRegSlot, uint32 of void IRBuilderAsmJs::BuildFromVar(uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot srcRegSlot, IRType irType, ValueType valueType) { - IR::RegOpnd * srcOpnd = BuildSrcOpnd(GetRegSlotFromVarReg(srcRegSlot), TyVar); + IR::RegOpnd * srcOpnd = BuildSrcOpnd(GetRegSlotFromPtrReg(srcRegSlot), TyVar); IR::RegOpnd * dstOpnd = BuildDstOpnd(dstRegSlot, irType); dstOpnd->SetValueType(valueType); @@ -2194,7 +2212,7 @@ IRBuilderAsmJs::BuildReg1IntConst1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js: { Assert(newOpcode == Js::OpCodeAsmJs::CheckSignature); - IR::RegOpnd * funcReg = BuildSrcOpnd(reg1, TyMachPtr); + IR::RegOpnd * funcReg = BuildSrcOpnd(GetRegSlotFromPtrReg(reg1), TyMachReg); IR::IntConstOpnd * sigIndex = IR::IntConstOpnd::New(constInt, TyInt32, m_func); @@ -2369,10 +2387,6 @@ IRBuilderAsmJs::BuildInt2(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot instr = IR::Instr::New(Js::OpCode::Ctz, dstOpnd, srcOpnd, m_func); break; - case Js::OpCodeAsmJs::I_Conv_VTI: - instr = IR::Instr::New(Js::OpCode::Ld_I4, dstOpnd, srcOpnd, m_func); - break; - case Js::OpCodeAsmJs::PopCnt_Int: instr = IR::Instr::New(Js::OpCode::PopCnt, dstOpnd, srcOpnd, m_func); break; @@ -2635,9 +2649,6 @@ IRBuilderAsmJs::BuildDouble2(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSl AddInstr(slotInstr, offset); } break; - case Js::OpCodeAsmJs::I_Conv_VTD: - instr = IR::Instr::New(Js::OpCode::Ld_A, dstOpnd, srcOpnd, m_func); - break; case Js::OpCodeAsmJs::Trunc_Db: instr = IR::Instr::New(Js::OpCode::Trunc_A, dstOpnd, srcOpnd, m_func); break; @@ -2691,9 +2702,6 @@ IRBuilderAsmJs::BuildFloat2(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlo AddInstr(slotInstr, offset); } break; - case Js::OpCodeAsmJs::I_Conv_VTF: - instr = IR::Instr::New(Js::OpCode::Ld_A, dstOpnd, srcOpnd, m_func); - break; case Js::OpCodeAsmJs::Trunc_Flt: instr = IR::Instr::New(Js::OpCode::Trunc_A, dstOpnd, srcOpnd, m_func); break; @@ -3473,10 +3481,10 @@ IRBuilderAsmJs::GenerateLoopBodySlotAccesses(uint offset) symSrc->m_offset = (argument + LowererMD::GetFormalParamOffset()) * MachPtr; symSrc->m_allocated = true; m_func->SetHasImplicitParamLoad(); - IR::SymOpnd *srcOpnd = IR::SymOpnd::New(symSrc, TyMachPtr, m_func); + IR::SymOpnd *srcOpnd = IR::SymOpnd::New(symSrc, TyMachReg, m_func); StackSym *loopParamSym = m_func->EnsureLoopParamSym(); - IR::RegOpnd *loopParamOpnd = IR::RegOpnd::New(loopParamSym, TyMachPtr, m_func); + IR::RegOpnd *loopParamOpnd = IR::RegOpnd::New(loopParamSym, TyMachReg, m_func); IR::Instr *instrArgIn = IR::Instr::New(Js::OpCode::ArgIn_A, loopParamOpnd, srcOpnd, m_func); m_func->m_headInstr->InsertAfter(instrArgIn); @@ -4542,7 +4550,6 @@ IRBuilderAsmJs::BuildFloat64x2_2(Js::OpCodeAsmJs newOpcode, uint32 offset, BUILD } opcode = Js::OpCode::Ld_A; break; - case Js::OpCodeAsmJs::Simd128_I_Conv_VTD2: case Js::OpCodeAsmJs::Simd128_Ld_D2: opcode = Js::OpCode::Ld_A; break; @@ -6259,16 +6266,6 @@ void IRBuilderAsmJs::BuildSimd_2(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::R } opcode = Js::OpCode::Ld_A; break; - case Js::OpCodeAsmJs::Simd128_I_Conv_VTF4: - case Js::OpCodeAsmJs::Simd128_I_Conv_VTI4: - case Js::OpCodeAsmJs::Simd128_I_Conv_VTI8: - case Js::OpCodeAsmJs::Simd128_I_Conv_VTI16: - case Js::OpCodeAsmJs::Simd128_I_Conv_VTU4: - case Js::OpCodeAsmJs::Simd128_I_Conv_VTU8: - case Js::OpCodeAsmJs::Simd128_I_Conv_VTU16: - case Js::OpCodeAsmJs::Simd128_I_Conv_VTB4: - case Js::OpCodeAsmJs::Simd128_I_Conv_VTB8: - case Js::OpCodeAsmJs::Simd128_I_Conv_VTB16: case Js::OpCodeAsmJs::Simd128_Ld_F4: case Js::OpCodeAsmJs::Simd128_Ld_I4: case Js::OpCodeAsmJs::Simd128_Ld_I8: diff --git a/lib/Backend/IRBuilderAsmJs.h b/lib/Backend/IRBuilderAsmJs.h index 1776f14bbc3..ac822db644a 100644 --- a/lib/Backend/IRBuilderAsmJs.h +++ b/lib/Backend/IRBuilderAsmJs.h @@ -80,7 +80,7 @@ class IRBuilderAsmJs BranchReloc * AddBranchInstr(IR::BranchInstr *instr, uint32 offset, uint32 targetOffset); BranchReloc * CreateRelocRecord(IR::BranchInstr * branchInstr, uint32 offset, uint32 targetOffset); void BuildHeapBufferReload(uint32 offset); - template + template void CreateLoadConstInstrForType(byte* table, Js::RegSlot& regAllocated, uint32 constCount, uint32 offset, IRType irType, ValueType valueType, Js::OpCode opcode, F extraProcess); void BuildConstantLoads(); void BuildImplicitArgIns(); @@ -91,6 +91,15 @@ class IRBuilderAsmJs #endif Js::RegSlot GetTypedRegFromRegSlot(Js::RegSlot reg, WAsmJs::Types type); Js::RegSlot GetRegSlotFromTypedReg(Js::RegSlot srcReg, WAsmJs::Types type); + Js::RegSlot GetRegSlotFromPtrReg(Js::RegSlot srcReg) + { +#if TARGET_32 + return GetRegSlotFromIntReg(srcReg); +#elif TARGET_64 + return GetRegSlotFromInt64Reg(srcReg); +#endif + } + Js::RegSlot GetRegSlotFromIntReg(Js::RegSlot srcIntReg) {return GetRegSlotFromTypedReg(srcIntReg, WAsmJs::INT32);} Js::RegSlot GetRegSlotFromInt64Reg(Js::RegSlot srcIntReg) {return GetRegSlotFromTypedReg(srcIntReg, WAsmJs::INT64);} Js::RegSlot GetRegSlotFromFloatReg(Js::RegSlot srcFloatReg) {return GetRegSlotFromTypedReg(srcFloatReg, WAsmJs::FLOAT32);} @@ -139,7 +148,7 @@ class IRBuilderAsmJs void BuildWasmMemAccess(Js::OpCodeAsmJs newOpcode, uint32 offset, uint32 slotIndex, Js::RegSlot value, uint32 constOffset, Js::ArrayBufferView::ViewType viewType); void BuildAsmTypedArr(Js::OpCodeAsmJs newOpcode, uint32 offset, uint32 slotIndex, Js::RegSlot value, Js::ArrayBufferView::ViewType viewType); void BuildAsmSimdTypedArr(Js::OpCodeAsmJs newOpcode, uint32 offset, uint32 slotIndex, Js::RegSlot value, Js::ArrayBufferView::ViewType viewType, uint8 DataWidth); - void BuildAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::ArgSlot argCount, Js::RegSlot ret, Js::RegSlot function, int8 returnType); + void BuildAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::ArgSlot argCount, Js::RegSlot ret, Js::RegSlot function, int8 returnType, Js::ProfileId profileId); void BuildAsmReg1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot dstReg); void BuildBrInt1(Js::OpCodeAsmJs newOpcode, uint32 offset, int32 relativeOffset, Js::RegSlot src); void BuildBrInt2(Js::OpCodeAsmJs newOpcode, uint32 offset, int32 relativeOffset, Js::RegSlot src1, Js::RegSlot src2); @@ -163,7 +172,7 @@ class IRBuilderAsmJs IR::Instr ** m_offsetToInstruction; Js::ByteCodeReader m_jnReader; Js::StatementReader m_statementReader; - SList * m_argStack; + SListCounted *m_argStack; SList * m_tempList; SList * m_argOffsetStack; SList * m_branchRelocList; @@ -171,7 +180,6 @@ class IRBuilderAsmJs static const uint32 m_firstsTypeCount = WAsmJs::LIMIT * 3 + 1; Js::RegSlot m_firstsType[m_firstsTypeCount]; Js::RegSlot m_firstVarConst; - Js::RegSlot m_firstIRTemp; Js::RegSlot m_tempCount; Js::OpCode * m_simdOpcodesMap; diff --git a/lib/Backend/Inline.cpp b/lib/Backend/Inline.cpp index 7aa1e58452c..31fea8d4dc8 100644 --- a/lib/Backend/Inline.cpp +++ b/lib/Backend/Inline.cpp @@ -13,7 +13,7 @@ Inline::Optimize() void Inline::Optimize(Func *func, __in_ecount_opt(callerArgOutCount) IR::Instr *callerArgOuts[], Js::ArgSlot callerArgOutCount, uint recursiveInlineDepth) { - if (!func->DoInline() || !topFunc->DoInline() || func->GetJITFunctionBody()->IsAsmJsMode()) // disable inlining for asm + if (!func->DoInline() || !topFunc->DoInline()) { return; } @@ -379,7 +379,81 @@ Inline::Optimize(Func *func, __in_ecount_opt(callerArgOutCount) IR::Instr *calle } break; } + case Js::OpCode::AsmJsCallI: + { + if (this->inlineesProcessed == inlinerData->GetInlineeCount() || !instr->IsProfiledInstr()) + { + break; + } + + const auto profileId = static_cast(instr->AsProfiledInstr()->u.profileId); + if (profileId >= func->GetJITFunctionBody()->GetProfiledCallSiteCount()) + { + break; + } + + const auto inlineeData = inlinerData->GetInlinee(profileId); + if (!inlineeData) + { + break; + } + if (PHASE_OFF(Js::InlineCandidatePhase, inlineeData)) + { + break; + } + + // Don't try to inline a function if it doesn't have profile data + if (!inlineeData->GetBody()->HasProfileInfo()) + { + break; + } + uint16 constantArguments = 0; + if (!PHASE_OFF(Js::InlineRecursivePhase, func)) + { + instr->IterateArgInstrs([&](IR::Instr* argInstr) { + IR::Opnd *src1 = argInstr->GetSrc1(); + if (!src1->IsRegOpnd()) + { + return false; + } + StackSym *sym = src1->AsRegOpnd()->m_sym; + if (sym->IsIntConst()) + { + if (argInstr->GetSrc2() && argInstr->GetSrc2()->IsSymOpnd()) + { + StackSym *dstSym = argInstr->GetDst()->AsSymOpnd()->m_sym->AsStackSym(); + Assert(dstSym->IsSingleDef()); + Assert(dstSym->IsArgSlotSym()); + Js::ArgSlot argCount = dstSym->GetArgSlotNum() - 1; + + if (argCount == Js::Constants::MaximumArgumentCountForConstantArgumentInlining) + { + return true; + } + constantArguments |= (1 << argCount); + } + } + return false; + }); + } + + if (!inliningHeuristics.BackendInlineIntoInliner( + inlineeData, func, this->topFunc, profileId, isCtor, true /*isFixedMethodCall*/, + this->IsInliningOutSideLoops(), this->isInLoop != 0, recursiveInlineDepth, constantArguments)) + { + break; + } + + instrNext = this->InlineScriptFunction(instr, inlineeData, symThis, profileId, &isInlined, recursiveInlineDepth); + + if (++this->inlineesProcessed == inlinerData->GetInlineeCount()) + { + return; + } + + break; + } case Js::OpCode::CallIExtended: { if (this->inlineesProcessed == inlinerData->GetInlineeCount()) @@ -1291,8 +1365,18 @@ Inline::BuildIRForInlinee(Func *inlinee, JITTimeFunctionBody *funcBody, IR::Inst BEGIN_CODEGEN_PHASE(this->topFunc, Js::IRBuilderPhase); - IRBuilder irBuilder(inlinee); - irBuilder.Build(); +#ifdef ASMJS_PLAT + if (this->topFunc->GetJITFunctionBody()->IsAsmJsMode()) + { + IRBuilderAsmJs irBuilder(inlinee); + irBuilder.Build(); + } + else +#endif + { + IRBuilder irBuilder(inlinee); + irBuilder.Build(); + } END_CODEGEN_PHASE_NO_DUMP(this->topFunc, Js::IRBuilderPhase); @@ -3621,16 +3705,21 @@ Inline::InlineFunctionCommon(IR::Instr *callInstr, bool originalCallTargetOpndIs { BuildIRForInlinee(inlinee, funcInfo->GetBody(), callInstr, isApplyTarget, recursiveInlineDepth); - Js::ArgSlot formalCount = funcInfo->GetBody()->GetInParamsCount(); + Js::ArgSlot formalCount = +#ifdef ASMJS_PLAT + funcInfo->GetBody()->IsAsmJsMode() ? funcInfo->GetBody()->GetAsmJsInfo()->GetArgCount() : +#endif + funcInfo->GetBody()->GetInParamsCount(); + IR::Instr *argOuts[Js::InlineeCallInfo::MaxInlineeArgoutCount]; #if DBG - memset(argOuts, 0xFE, sizeof(argOuts)); + memset(argOuts, 0, sizeof(argOuts)); #endif if (callInstr->m_opcode == Js::OpCode::CallIFixed) { Assert(callInstr->GetFixedFunction()->GetFuncInfoAddr() == funcInfo->GetFunctionInfoAddr()); } - else + else if(!this->topFunc->GetJITFunctionBody()->IsAsmJsMode()) { PrepareInsertionPoint(callInstr, funcInfo, inlineBailoutChecksBeforeInstr); } @@ -3646,7 +3735,7 @@ Inline::InlineFunctionCommon(IR::Instr *callInstr, bool originalCallTargetOpndIs bool stackArgsArgOutExpanded = false; Js::ArgSlot actualCount = MapActuals(callInstr, argOuts, formalCount, inlinee, (Js::ProfileId)callInstr->AsProfiledInstr()->u.profileId, &stackArgsArgOutExpanded, argOutsExtra); inlinee->actualCount = actualCount; - Assert(actualCount > 0); + Assert(actualCount > 0 || funcInfo->GetBody()->IsAsmJsMode()); #if DBG if(safeThis) @@ -3674,7 +3763,7 @@ Inline::InlineFunctionCommon(IR::Instr *callInstr, bool originalCallTargetOpndIs // Set it to belong to the inlinee, so that we can use the actual count when lowering InlineeStart callInstr->m_func = inlinee; - callInstr->SetDst(IR::RegOpnd::New(TyVar, inlinee)); + callInstr->SetDst(IR::RegOpnd::New(returnValueOpnd ? returnValueOpnd->GetType() : TyVar, inlinee)); // Put the meta arguments that the stack walker expects to find on the stack. SetupInlineeFrame(inlinee, callInstr, actualCount, callInstr->GetSrc1()); @@ -4538,8 +4627,11 @@ Inline::MapActuals(IR::Instr *callInstr, __out_ecount(maxParamCount) IR::Instr * // This allows us to markTemp the argOut source. argInstr->m_opcode = Js::OpCode::ArgOut_A_Inline; } + if (!inlinee->GetJITFunctionBody()->IsAsmJsMode()) + { + argInstr->GenerateBytecodeArgOutCapture(); + } } - argInstr->GenerateBytecodeArgOutCapture(); } // Expand @@ -4776,48 +4868,57 @@ Inline::MapFormals(Func *inlinee, if (argOuts[argIndex]) { IR::Instr *argOut = argOuts[argIndex]; - IR::Instr* instrDef; - if (argOut->HasByteCodeArgOutCapture()) + + if (instr->m_func->GetJITFunctionBody()->IsAsmJsMode()) { - instrDef = argOut->GetBytecodeArgOutCapture(); + instr->SetSrc1(argOut->GetSrc1()); + instr->m_opcode = Js::OpCode::Ld_A; } else { - Assert(argOut->m_opcode == Js::OpCode::ArgOut_A_FixupForStackArgs); - instrDef = argOut->GetArgOutSnapshot(); - } + IR::Instr* instrDef; + if (argOut->HasByteCodeArgOutCapture()) + { + instrDef = argOut->GetBytecodeArgOutCapture(); + } + else + { + Assert(argOut->m_opcode == Js::OpCode::ArgOut_A_FixupForStackArgs); + instrDef = argOut->GetArgOutSnapshot(); + } - instr->SetSrc1(instrDef->GetDst()); - instr->m_opcode = Js::OpCode::Ld_A; - IR::Opnd* dst = instr->GetDst(); - IR::Opnd* src = instrDef->GetSrc1(); + instr->SetSrc1(instrDef->GetDst()); + instr->m_opcode = Js::OpCode::Ld_A; + IR::Opnd* dst = instr->GetDst(); + IR::Opnd* src = instrDef->GetSrc1(); - if (argIndex == 0) - { - // Look at the "this" argument source. - // If it's known to be a normal object (the caller has already guaranteed that, or - // it was defined by an instruction that produces normal objects), we'll omit CheckThis. - // If it's a constant value, we'll do the mapping at jit time and copy the final value. - if (src->IsRegOpnd()) + if (argIndex == 0) { - symThis = dst->AsRegOpnd()->m_sym; - - StackSym *symSrc = src->AsRegOpnd()->m_sym; - if (symSrc == symCallerThis || - symSrc->m_isSafeThis || - inlinee->IsInlinedConstructor()) + // Look at the "this" argument source. + // If it's known to be a normal object (the caller has already guaranteed that, or + // it was defined by an instruction that produces normal objects), we'll omit CheckThis. + // If it's a constant value, we'll do the mapping at jit time and copy the final value. + if (src->IsRegOpnd()) { - fUsesSafeThis = true; - } - else if (symSrc->m_isSingleDef && symSrc->IsConst() && !symSrc->IsIntConst() && !symSrc->IsFloatConst()) - { - thisConstSym = symSrc; - fUsesConstThis = true; - } - else if(fixedFunctionSafeThis) - { - // Note this need to come after we determined that this pointer is not const (undefined/null) - fUsesSafeThis = true; + symThis = dst->AsRegOpnd()->m_sym; + + StackSym *symSrc = src->AsRegOpnd()->m_sym; + if (symSrc == symCallerThis || + symSrc->m_isSafeThis || + inlinee->IsInlinedConstructor()) + { + fUsesSafeThis = true; + } + else if (symSrc->m_isSingleDef && symSrc->IsConst() && !symSrc->IsIntConst() && !symSrc->IsFloatConst()) + { + thisConstSym = symSrc; + fUsesConstThis = true; + } + else if (fixedFunctionSafeThis) + { + // Note this need to come after we determined that this pointer is not const (undefined/null) + fUsesSafeThis = true; + } } } } @@ -4927,7 +5028,16 @@ Inline::MapFormals(Func *inlinee, } break; } - + case Js::OpCode::LdAsmJsEnv: + if (instr->m_func == inlinee) + { + instr->SetSrc1(funcObjOpnd); + } + else + { + Assert(instr->GetSrc1() != nullptr); + } + break; case Js::OpCode::LdEnv: if (instr->m_func == inlinee) { @@ -5087,6 +5197,7 @@ Inline::MapFormals(Func *inlinee, } else { + Assert(instr->GetSrc1() != nullptr); instr->m_opcode = Js::OpCode::Ld_A; instr->SetDst(retOpnd); } diff --git a/lib/Backend/InliningDecider.cpp b/lib/Backend/InliningDecider.cpp index e33fcfbbf1f..5a3760bc98b 100644 --- a/lib/Backend/InliningDecider.cpp +++ b/lib/Backend/InliningDecider.cpp @@ -5,7 +5,7 @@ #include "Backend.h" InliningDecider::InliningDecider(Js::FunctionBody *const topFunc, bool isLoopBody, bool isInDebugMode, const ExecutionMode jitMode) - : topFunc(topFunc), isLoopBody(isLoopBody), isInDebugMode(isInDebugMode), jitMode(jitMode), bytecodeInlinedCount(0), numberOfInlineesWithLoop (0), threshold(topFunc->GetByteCodeWithoutLDACount(), isLoopBody) + : topFunc(topFunc), isLoopBody(isLoopBody), isInDebugMode(isInDebugMode), jitMode(jitMode), bytecodeInlinedCount(0), numberOfInlineesWithLoop (0), threshold(topFunc->GetByteCodeWithoutLDACount(), isLoopBody, topFunc->GetIsAsmjsMode()) { Assert(topFunc); } @@ -68,7 +68,7 @@ bool InliningDecider::InlineIntoInliner(Js::FunctionBody *const inliner) const return false; } - if (!inliner->GetAnyDynamicProfileInfo()->HasCallSiteInfo(inliner)) + if (!Js::DynamicProfileInfo::HasCallSiteInfo(inliner)) { INLINE_TESTTRACE(_u("INLINING: Skip Inline: No call site info\tCaller: %s (#%d)\n"), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer)); @@ -235,7 +235,8 @@ Js::FunctionInfo *InliningDecider::Inline(Js::FunctionBody *const inliner, Js::F return nullptr; } - if (inlinee->GetInParamsCount() == 0) + // Wasm functions can have no params + if (inlinee->GetInParamsCount() == 0 && !inlinee->GetIsAsmjsMode()) { // Inline candidate has no params, not even a this pointer. This can only be the global function, // which we shouldn't inline. @@ -670,7 +671,7 @@ bool InliningDecider::DeciderInlineIntoInliner(Js::FunctionBody * inlinee, Js::F return false; } - if (inlinee->GetIsAsmjsMode() || inliner->GetIsAsmjsMode()) + if (inliner->GetIsAsmjsMode() != inlinee->GetIsAsmjsMode()) { return false; } diff --git a/lib/Backend/InliningHeuristics.cpp b/lib/Backend/InliningHeuristics.cpp index bc31868be2a..d1ec5e1fbb7 100644 --- a/lib/Backend/InliningHeuristics.cpp +++ b/lib/Backend/InliningHeuristics.cpp @@ -4,18 +4,14 @@ //------------------------------------------------------------------------------------------------------- #include "Backend.h" -InliningThreshold::InliningThreshold(uint nonLoadByteCodeCount, bool forLoopBody, bool aggressive) : nonLoadByteCodeCount(nonLoadByteCodeCount) +InliningThreshold::InliningThreshold(uint nonLoadByteCodeCount, bool forLoopBody, bool asmjs) : + nonLoadByteCodeCount(nonLoadByteCodeCount), + forLoopBody(forLoopBody), + asmjs(asmjs) { - this->forLoopBody = forLoopBody; - if (aggressive) - { - SetAggressiveHeuristics(); - } - else - { - SetHeuristics(); - } + SetHeuristics(); } + void InliningThreshold::SetAggressiveHeuristics() { Assert(!this->forLoopBody); @@ -54,6 +50,10 @@ void InliningThreshold::SetHeuristics() { inlineThreshold += CONFIG_FLAG(InlineThresholdAdjustCountInSmallFunction); } + if (this->asmjs) + { + inlineThreshold += CONFIG_FLAG(AsmJsInlineAdjust); + } constructorInlineThreshold = CONFIG_FLAG(ConstructorInlineThreshold); outsideLoopInlineThreshold = CONFIG_FLAG(OutsideLoopInlineThreshold); diff --git a/lib/Backend/InliningHeuristics.h b/lib/Backend/InliningHeuristics.h index 6e133ec0a17..633a0a4391a 100644 --- a/lib/Backend/InliningHeuristics.h +++ b/lib/Backend/InliningHeuristics.h @@ -10,6 +10,7 @@ struct InliningThreshold { uint nonLoadByteCodeCount; bool forLoopBody; + bool asmjs; int inlineThreshold; int constructorInlineThreshold; int outsideLoopInlineThreshold; @@ -20,7 +21,7 @@ struct InliningThreshold int maxNumberOfInlineesWithLoop; int constantArgumentInlineThreshold; - InliningThreshold(uint nonLoadByteCodeCount, bool forLoopBody, bool aggressive = false); + InliningThreshold(uint nonLoadByteCodeCount, bool forLoopBody, bool asmjs); void SetHeuristics(); void SetAggressiveHeuristics(); void Reset(); @@ -36,7 +37,7 @@ class InliningHeuristics public: public: - InliningHeuristics(const FunctionJITTimeInfo * topFunc, bool forLoopBody) :topFunc(topFunc), threshold(topFunc->GetBody()->GetNonLoadByteCodeCount(), forLoopBody) {}; + InliningHeuristics(const FunctionJITTimeInfo * topFunc, bool forLoopBody) :topFunc(topFunc), threshold(topFunc->GetBody()->GetNonLoadByteCodeCount(), forLoopBody, topFunc->GetBody()->IsAsmJsMode()) {}; bool BackendInlineIntoInliner(const FunctionJITTimeInfo * inlinee, Func * inliner, Func *topFunc, diff --git a/lib/Backend/LinearScan.cpp b/lib/Backend/LinearScan.cpp index cd0a716aea8..9897e8dac73 100644 --- a/lib/Backend/LinearScan.cpp +++ b/lib/Backend/LinearScan.cpp @@ -2105,7 +2105,7 @@ LinearScan::FillBailOutRecord(IR::Instr * instr) { this->SpillInlineeArgs(instr); } - else + else if (!instr->m_func->GetJITFunctionBody()->IsAsmJsMode()) { // There is a chance that the instruction was hoisting from an inlinee func // but if there are no inlinee frames - make sure the instr belongs to the outer func @@ -3467,7 +3467,7 @@ LinearScan::KillImplicitRegs(IR::Instr *instr) { this->SpillInlineeArgs(instr); } - else + else if(!instr->m_func->GetJITFunctionBody()->IsAsmJsMode()) { instr->m_func = this->func; } diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index 20fbb9c9479..978fed6091a 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -850,11 +850,11 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa break; } case Js::OpCode::AsmJsCallI: - m_lowererMD.LowerAsmJsCallI(instr); + instrPrev = m_lowererMD.LowerAsmJsCallI(instr); break; case Js::OpCode::AsmJsCallE: - m_lowererMD.LowerAsmJsCallE(instr); + instrPrev = m_lowererMD.LowerAsmJsCallE(instr); break; case Js::OpCode::CallIEval: @@ -5439,7 +5439,7 @@ Lowerer::LowerPrologEpilogAsmJs() instr = m_func->m_headInstr; AssertMsg(instr->IsEntryInstr(), "First instr isn't an EntryInstr..."); - m_lowererMD.LowerEntryInstrAsmJs(instr->AsEntryInstr()); + m_lowererMD.LowerEntryInstr(instr->AsEntryInstr()); instr = m_func->m_exitInstr; AssertMsg(instr->IsExitInstr(), "Last instr isn't an ExitInstr..."); @@ -13508,14 +13508,14 @@ Lowerer::GenerateFastCondBranch(IR::BranchInstr * instrBranch, bool *pIsHelper) return true; } -void +IR::Instr * Lowerer::LowerInlineeStart(IR::Instr * inlineeStartInstr) { IR::Opnd *linkOpnd = inlineeStartInstr->GetSrc2(); if (!linkOpnd) { Assert(inlineeStartInstr->m_func->m_hasInlineArgsOpt); - return; + return inlineeStartInstr->m_prev; } AssertMsg(inlineeStartInstr->m_func->firstActualStackOffset != -1, "This should have been already done in backward pass"); @@ -13529,7 +13529,16 @@ Lowerer::LowerInlineeStart(IR::Instr * inlineeStartInstr) #pragma prefast(suppress:6235, "Non-Zero Constant in Condition") if (!PHASE_ON(Js::EliminateArgoutForInlineePhase, this->m_func) || inlineeStartInstr->m_func->GetJITFunctionBody()->HasOrParentHasArguments()) { - m_lowererMD.ChangeToAssign(argInstr); +#ifdef _M_IX86 + if (argInstr->GetDst()->IsInt64()) + { + m_lowererMD.LowerInt64Assign(argInstr); + } + else +#endif + { + m_lowererMD.ChangeToAssign(argInstr); + } } else { @@ -13573,6 +13582,9 @@ Lowerer::LowerInlineeStart(IR::Instr * inlineeStartInstr) i++; return false; }); + + IR::Instr* prev = inlineeStartInstr->m_prev; + if (inlineeStartInstr->m_func->m_hasInlineArgsOpt) { inlineeStartInstr->FreeSrc1(); @@ -13583,6 +13595,7 @@ Lowerer::LowerInlineeStart(IR::Instr * inlineeStartInstr) { inlineeStartInstr->Remove(); } + return prev; } void diff --git a/lib/Backend/Lower.h b/lib/Backend/Lower.h index 09dfdf33224..9aaaf509786 100644 --- a/lib/Backend/Lower.h +++ b/lib/Backend/Lower.h @@ -496,7 +496,7 @@ class Lowerer void LowerRemR8(IR::Instr * const instr); void LowerRemR4(IR::Instr * const instr); - void LowerInlineeStart(IR::Instr * instr); + IR::Instr* LowerInlineeStart(IR::Instr * instr); void LowerInlineeEnd(IR::Instr * instr); static diff --git a/lib/Backend/LowerMDShared.cpp b/lib/Backend/LowerMDShared.cpp index 4e87df21d18..4aa8682cb93 100644 --- a/lib/Backend/LowerMDShared.cpp +++ b/lib/Backend/LowerMDShared.cpp @@ -322,12 +322,6 @@ LowererMD::LowerExitInstr(IR::ExitInstr * exitInstr) return this->lowererMDArch.LowerExitInstr(exitInstr); } -IR::Instr * -LowererMD::LowerEntryInstrAsmJs(IR::EntryInstr * entryInstr) -{ - return this->lowererMDArch.LowerEntryInstrAsmJs(entryInstr); -} - IR::Instr * LowererMD::LowerExitInstrAsmJs(IR::ExitInstr * exitInstr) { @@ -801,8 +795,8 @@ LowererMD::CreateAssign(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsertPt, IR::Instr * LowererMD::LowerRet(IR::Instr * retInstr) { - IR::RegOpnd * retReg; - + IR::RegOpnd * retReg = nullptr; + bool needsRetReg = true; #ifdef ASMJS_PLAT if (m_func->GetJITFunctionBody()->IsAsmJsMode() && !m_func->IsLoopBody()) // for loop body ret is the bytecodeoffset { @@ -852,8 +846,10 @@ LowererMD::LowerRet(IR::Instr * retInstr) #endif break; } - case Js::AsmJsRetType::Signed: case Js::AsmJsRetType::Void: + needsRetReg = false; + break; + case Js::AsmJsRetType::Signed: regType = TyInt32; break; case Js::AsmJsRetType::Float32x4: @@ -893,8 +889,11 @@ LowererMD::LowerRet(IR::Instr * retInstr) Assert(UNREACHED); } - retReg = IR::RegOpnd::New(regType, m_func); - retReg->SetReg(lowererMDArch.GetRegReturnAsmJs(regType)); + if (needsRetReg) + { + retReg = IR::RegOpnd::New(regType, m_func); + retReg->SetReg(lowererMDArch.GetRegReturnAsmJs(regType)); + } } else #endif @@ -902,9 +901,11 @@ LowererMD::LowerRet(IR::Instr * retInstr) retReg = IR::RegOpnd::New(TyMachReg, m_func); retReg->SetReg(lowererMDArch.GetRegReturn(TyMachReg)); } - - Lowerer::InsertMove(retReg, retInstr->UnlinkSrc1(), retInstr); - retInstr->SetSrc1(retReg); + if (needsRetReg) + { + Lowerer::InsertMove(retReg, retInstr->UnlinkSrc1(), retInstr); + retInstr->SetSrc1(retReg); + } return retInstr; } diff --git a/lib/Backend/LowerMDShared.h b/lib/Backend/LowerMDShared.h index ca7c61ba8cd..647714f25b7 100644 --- a/lib/Backend/LowerMDShared.h +++ b/lib/Backend/LowerMDShared.h @@ -268,7 +268,6 @@ class LowererMD IR::Instr * LoadFloatHelperArgument(IR::Instr * instr, IR::Opnd * opndArg); IR::Instr * LowerEntryInstr(IR::EntryInstr * entryInstr); IR::Instr * LowerExitInstr(IR::ExitInstr * exitInstr); - IR::Instr * LowerEntryInstrAsmJs(IR::EntryInstr * entryInstr); IR::Instr * LowerExitInstrAsmJs(IR::ExitInstr * exitInstr); IR::Instr * LoadNewScObjFirstArg(IR::Instr * instr, IR::Opnd * dst, ushort extraArgs = 0); IR::Instr * LowerToFloat(IR::Instr *instr); diff --git a/lib/Backend/NativeCodeGenerator.cpp b/lib/Backend/NativeCodeGenerator.cpp index 46a84232131..28c4ae7c5ee 100644 --- a/lib/Backend/NativeCodeGenerator.cpp +++ b/lib/Backend/NativeCodeGenerator.cpp @@ -1670,7 +1670,11 @@ NativeCodeGenerator::CheckCodeGenDone( entryPointInfo->GetNativeEntrypoint()); jsMethod = entryPointInfo->jsMethod; - Assert(!functionBody->NeedEnsureDynamicProfileInfo() || jsMethod == Js::DynamicProfileInfo::EnsureDynamicProfileInfoThunk); + Assert(!functionBody->NeedEnsureDynamicProfileInfo() || jsMethod == Js::DynamicProfileInfo::EnsureDynamicProfileInfoThunk || functionBody->GetIsAsmjsMode()); + if (functionBody->GetIsAsmjsMode() && functionBody->NeedEnsureDynamicProfileInfo()) + { + functionBody->EnsureDynamicProfileInfo(); + } } Assert(!IsThunk(jsMethod)); diff --git a/lib/Backend/Sym.h b/lib/Backend/Sym.h index 315b8d4c781..e6cb3ea97d9 100644 --- a/lib/Backend/Sym.h +++ b/lib/Backend/Sym.h @@ -176,6 +176,7 @@ class StackSym: public Sym bool IsFromByteCodeConstantTable() const { return m_isFromByteCodeConstantTable; } void SetIsFromByteCodeConstantTable() { this->m_isFromByteCodeConstantTable = true; } Js::ArgSlot GetArgSlotNum() const { Assert(HasArgSlotNum()); return m_slotNum; } + void SetArgSlotNum(Js::ArgSlot newNum) { m_slotNum = newNum; } bool HasArgSlotNum() const { return !!(m_isArgSlotSym | m_isArgSlotRegSym); } void IncrementArgSlotNum(); void DecrementArgSlotNum(); diff --git a/lib/Backend/amd64/LowererMDArch.cpp b/lib/Backend/amd64/LowererMDArch.cpp index f8b1dd3d132..17708af7926 100644 --- a/lib/Backend/amd64/LowererMDArch.cpp +++ b/lib/Backend/amd64/LowererMDArch.cpp @@ -1543,7 +1543,7 @@ LowererMDArch::LowerEntryInstr(IR::EntryInstr * entryInstr) // Allocate the inlined arg out stack in the locals. Allocate an additional slot so that // we can unconditionally clear the first slot past the current frame. - this->m_func->m_localStackHeight += ((this->m_func->GetMaxInlineeArgOutCount() + 1) * MachPtr); + this->m_func->m_localStackHeight += m_func->GetMaxInlineeArgOutSize() + MachPtr; uint32 stackLocalsSize = this->m_func->m_localStackHeight; if(xmmOffset != 0) @@ -1574,7 +1574,7 @@ LowererMDArch::LowerEntryInstr(IR::EntryInstr * entryInstr) throw Js::OperationAbortedException(); } - if (this->m_func->GetMaxInlineeArgOutCount()) + if (m_func->HasInlinee()) { this->m_func->GetJITOutput()->SetFrameHeight(this->m_func->m_localStackHeight); } @@ -1639,7 +1639,7 @@ LowererMDArch::LowerEntryInstr(IR::EntryInstr * entryInstr) } // Zero initialize the first inlinee frames argc. - if (this->m_func->GetMaxInlineeArgOutCount()) + if (m_func->HasInlinee()) { if(!movRax0) { @@ -2053,9 +2053,10 @@ LowererMDArch::LowerExitInstr(IR::ExitInstr * exitInstr) break; case Js::AsmJsRetType::Int64: case Js::AsmJsRetType::Signed: - case Js::AsmJsRetType::Void: retReg = IR::RegOpnd::New(nullptr, this->GetRegReturn(TyMachReg), TyMachReg, this->m_func); break; + case Js::AsmJsRetType::Void: + break; default: Assume(UNREACHED); } @@ -2069,7 +2070,10 @@ LowererMDArch::LowerExitInstr(IR::ExitInstr * exitInstr) // Generate RET IR::Instr * retInstr = IR::Instr::New(Js::OpCode::RET, this->m_func); retInstr->SetSrc1(intSrc); - retInstr->SetSrc2(retReg); + if (retReg) + { + retInstr->SetSrc2(retReg); + } exitInstr->InsertBefore(retInstr); retInstr->m_opcode = Js::OpCode::RET; @@ -2078,13 +2082,6 @@ LowererMDArch::LowerExitInstr(IR::ExitInstr * exitInstr) return exitInstr; } -IR::Instr * -LowererMDArch::LowerEntryInstrAsmJs(IR::EntryInstr * entryInstr) -{ - // prologue is almost identical on x64, except for loading args - return LowerEntryInstr(entryInstr); -} - IR::Instr * LowererMDArch::LowerExitInstrAsmJs(IR::ExitInstr * exitInstr) { diff --git a/lib/Backend/amd64/LowererMDArch.h b/lib/Backend/amd64/LowererMDArch.h index 700b69f5851..a198a2c61bd 100644 --- a/lib/Backend/amd64/LowererMDArch.h +++ b/lib/Backend/amd64/LowererMDArch.h @@ -107,7 +107,6 @@ class LowererMDArch IR::Instr * LowerEntryInstr(IR::EntryInstr * entryInstr); void GeneratePrologueStackProbe(IR::Instr *entryInstr, IntConstType frameSize); IR::Instr * LowerExitInstr(IR::ExitInstr * exitInstr); - IR::Instr * LowerEntryInstrAsmJs(IR::EntryInstr * entryInstr); IR::Instr * LowerExitInstrAsmJs(IR::ExitInstr * exitInstr); IR::Instr * LowerInt64Assign(IR::Instr * instr); static void EmitInt4Instr(IR::Instr *instr, bool signExtend = false); diff --git a/lib/Backend/amd64/PeepsMD.cpp b/lib/Backend/amd64/PeepsMD.cpp index e150be30250..9b5ac4818cf 100644 --- a/lib/Backend/amd64/PeepsMD.cpp +++ b/lib/Backend/amd64/PeepsMD.cpp @@ -48,7 +48,7 @@ PeepsMD::PeepAssign(IR::Instr *instr) { IR::Opnd* dst = instr->GetDst(); IR::Opnd* src = instr->GetSrc1(); - if(dst->IsRegOpnd() && instr->m_opcode == Js::OpCode::MOV) + if(dst->IsRegOpnd() && instr->m_opcode == Js::OpCode::MOV && !instr->isInlineeEntryInstr) { if (src->IsImmediateOpnd() && src->GetImmediateValue(instr->m_func) == 0) { @@ -64,7 +64,7 @@ PeepsMD::PeepAssign(IR::Instr *instr) instr->ReplaceSrc1(dst); instr->SetSrc2(dst); } - else if (!instr->isInlineeEntryInstr) + else { if(src->IsIntConstOpnd() && src->GetSize() <= TySize[TyUint32]) { diff --git a/lib/Backend/arm/EncoderMD.cpp b/lib/Backend/arm/EncoderMD.cpp index 74674d7e3c6..d410bcc8768 100644 --- a/lib/Backend/arm/EncoderMD.cpp +++ b/lib/Backend/arm/EncoderMD.cpp @@ -2209,9 +2209,8 @@ EncoderMD::BaseAndOffsetFromSym(IR::SymOpnd *symOpnd, RegNum *pBaseReg, int32 *p { // SP points to the base of the argument area. Non-reg SP points directly to the locals. offset += (func->m_argSlotsForFunctionsCalled * MachRegInt); - if (func->GetMaxInlineeArgOutCount()) + if (func->HasInlinee()) { - Assert(func->HasInlinee()); if ((!stackSym->IsArgSlotSym() || stackSym->m_isOrphanedArg) && !stackSym->IsParamSlotSym()) { offset += func->GetInlineeArgumentStackSize(); @@ -2237,18 +2236,18 @@ EncoderMD::BaseAndOffsetFromSym(IR::SymOpnd *symOpnd, RegNum *pBaseReg, int32 *p Assert(offset >= 0); Assert(baseReg != RegSP || (uint)offset >= (func->m_argSlotsForFunctionsCalled * MachRegInt)); - if (func->GetMaxInlineeArgOutCount()) + if (func->HasInlinee()) { Assert(baseReg == RegSP); if (stackSym->IsArgSlotSym() && !stackSym->m_isOrphanedArg) { Assert(stackSym->m_isInlinedArgSlot); - Assert((uint)offset <= ((func->m_argSlotsForFunctionsCalled + func->GetMaxInlineeArgOutCount()) * MachRegInt)); + Assert((uint)offset <= func->m_argSlotsForFunctionsCalled * MachRegInt + func->GetMaxInlineeArgOutSize()); } else { AssertMsg(stackSym->IsAllocated(), "StackSym offset should be set"); - Assert((uint)offset > ((func->m_argSlotsForFunctionsCalled + func->GetMaxInlineeArgOutCount()) * MachRegInt)); + Assert((uint)offset > func->m_argSlotsForFunctionsCalled * MachRegInt + func->GetMaxInlineeArgOutSize()); } } // TODO: restore the following assert (very useful) once we have a way to tell whether prolog/epilog diff --git a/lib/Backend/arm/LowerMD.cpp b/lib/Backend/arm/LowerMD.cpp index aa30ba9251d..faaded508fb 100644 --- a/lib/Backend/arm/LowerMD.cpp +++ b/lib/Backend/arm/LowerMD.cpp @@ -1055,9 +1055,8 @@ LowererMD::LowerEntryInstr(IR::EntryInstr * entryInstr) this->m_func->m_localStackHeight = Math::Align(this->m_func->m_localStackHeight, MachStackAlignment); } - if (this->m_func->GetMaxInlineeArgOutCount()) + if (this->m_func->HasInlinee()) { - Assert(this->m_func->HasInlinee()); // Allocate the inlined arg out stack in the locals. Allocate an additional slot so that // we can unconditionally clear the first slot past the current frame. this->m_func->m_localStackHeight += this->m_func->GetInlineeArgumentStackSize(); @@ -1314,7 +1313,7 @@ LowererMD::LowerEntryInstr(IR::EntryInstr * entryInstr) } Assert(fpOffsetSize >= 0); - if (this->m_func->GetMaxInlineeArgOutCount()) + if (m_func->HasInlinee()) { // subtracting 2 for frame pointer & return address this->m_func->GetJITOutput()->SetFrameHeight(this->m_func->m_localStackHeight + this->m_func->m_ArgumentsOffset - 2 * MachRegInt); @@ -1453,7 +1452,7 @@ LowererMD::LowerEntryInstr(IR::EntryInstr * entryInstr) //As we have already allocated the stack here, we can safely zero out the inlinee argout slot. // Zero initialize the first inlinee frames argc. - if (this->m_func->GetMaxInlineeArgOutCount()) + if (m_func->HasInlinee()) { // This is done post prolog. so we don't have to emit unwind data. if (r12Opnd == nullptr || isScratchRegisterThrashed) diff --git a/lib/Backend/arm/LowerMD.h b/lib/Backend/arm/LowerMD.h index 8bb1af94dd0..cddb20c5590 100644 --- a/lib/Backend/arm/LowerMD.h +++ b/lib/Backend/arm/LowerMD.h @@ -215,7 +215,6 @@ class LowererMD IR::Instr * LowerEntryInstr(IR::EntryInstr * entryInstr); IR::Instr * LowerExitInstr(IR::ExitInstr * exitInstr); - IR::Instr * LowerEntryInstrAsmJs(IR::EntryInstr * entryInstr) { Assert(UNREACHED); return nullptr; } IR::Instr * LowerExitInstrAsmJs(IR::ExitInstr * exitInstr) { Assert(UNREACHED); return nullptr; } IR::Instr * LoadNewScObjFirstArg(IR::Instr * instr, IR::Opnd * dst, ushort extraArgs = 0); IR::Instr * LowerTry(IR::Instr *instr, IR::JnHelperMethod helperMethod); diff --git a/lib/Backend/arm64/LowerMD.h b/lib/Backend/arm64/LowerMD.h index 0b3bf92ad6d..e66cb411ae2 100644 --- a/lib/Backend/arm64/LowerMD.h +++ b/lib/Backend/arm64/LowerMD.h @@ -212,7 +212,6 @@ class LowererMD IR::Instr * LowerEntryInstr(IR::EntryInstr * entryInstr) { __debugbreak(); return 0; } IR::Instr * LowerExitInstr(IR::ExitInstr * exitInstr) { __debugbreak(); return 0; } - IR::Instr * LowerEntryInstrAsmJs(IR::EntryInstr * entryInstr) { Assert(UNREACHED); return NULL; } IR::Instr * LowerExitInstrAsmJs(IR::ExitInstr * exitInstr) { Assert(UNREACHED); return NULL; } IR::Instr * LoadNewScObjFirstArg(IR::Instr * instr, IR::Opnd * dst, ushort extraArgs = 0) { __debugbreak(); return 0; } IR::Instr * LowerTry(IR::Instr *instr, IR::JnHelperMethod helperMethod) { __debugbreak(); return 0; } diff --git a/lib/Backend/i386/LowererMDArch.cpp b/lib/Backend/i386/LowererMDArch.cpp index 69a3c65a57c..e2901695102 100644 --- a/lib/Backend/i386/LowererMDArch.cpp +++ b/lib/Backend/i386/LowererMDArch.cpp @@ -1093,7 +1093,7 @@ LowererMDArch::LowerCallArgs(IR::Instr *callInstr, ushort callFlags, Js::ArgSlot } AssertMsg(startCallInstr->m_opcode == Js::OpCode::StartCall || startCallInstr->m_opcode == Js::OpCode::LoweredStartCall, "Problem with arg chain."); - AssertMsg(m_func->GetJITFunctionBody()->IsAsmJsMode() || startCallInstr->GetArgOutCount(/*getInterpreterArgOutCount*/ false) == argCount, "ArgCount doesn't match StartCall count"); + AssertMsg(startCallInstr->GetArgOutCount(/*getInterpreterArgOutCount*/ false) == argCount, "ArgCount doesn't match StartCall count"); // // Machine dependent lowering @@ -1285,15 +1285,11 @@ int32 LowererMDArch::LowerStartCallAsmJs(IR::Instr * startCallInstr, IR::Instr * insertInstr, IR::Instr * callInstr) { AssertMsg(startCallInstr->GetSrc1()->IsIntConstOpnd(), "Bad src on StartCall"); + AssertMsg(startCallInstr->GetSrc2()->IsIntConstOpnd(), "Bad src on StartCall"); - IR::IntConstOpnd * sizeOpnd = startCallInstr->GetSrc1()->AsIntConstOpnd(); + IR::IntConstOpnd * sizeOpnd = startCallInstr->GetSrc2()->AsIntConstOpnd(); IntConstType sizeValue = sizeOpnd->GetValue(); - if (callInstr->m_opcode == Js::OpCode::AsmJsCallI) - { - // we will push FunctionObject, so don't need to worry about that - sizeValue -= MachPtr; - } // Maintain 8 byte alignment of the stack. // We do this by adjusting the SUB for stackCall to make sure it maintains 8 byte alignment. @@ -1494,7 +1490,7 @@ LowererMDArch::LowerEntryInstr(IR::EntryInstr * entryInstr) // Allocate the inlined arg out stack in the locals. Allocate an additional slot so that // we can unconditionally clear the argc slot of the next frame. - this->m_func->m_localStackHeight += ((this->m_func->GetMaxInlineeArgOutCount() + 1) * MachPtr); + this->m_func->m_localStackHeight += m_func->GetMaxInlineeArgOutSize() + MachPtr; bytesOnStack += this->m_func->m_localStackHeight; @@ -1508,14 +1504,10 @@ LowererMDArch::LowerEntryInstr(IR::EntryInstr * entryInstr) Assert(this->m_func->hasBailout || this->bailOutStackRestoreLabel == nullptr); this->m_func->frameSize = bytesOnStack; - if (this->m_func->GetMaxInlineeArgOutCount()) + if (this->m_func->HasInlinee()) { this->m_func->GetJITOutput()->SetFrameHeight(this->m_func->m_localStackHeight); - } - // Zero initialize the first inlinee frames argc. - if (this->m_func->GetMaxInlineeArgOutCount()) - { StackSym *sym = this->m_func->m_symTable->GetArgSlotSym((Js::ArgSlot)-1); sym->m_isInlinedArgSlot = true; sym->m_offset = 0; @@ -1584,115 +1576,6 @@ LowererMDArch::LowerEntryInstr(IR::EntryInstr * entryInstr) return entryInstr; } -IR::Instr * -LowererMDArch::LowerEntryInstrAsmJs(IR::EntryInstr * entryInstr) -{ - // PUSH EBP - // MOV EBP, ESP - // StackProbe - // MOV EAX, LocalStackHeight / LEA ESP, [ESP - stackSize] - // CALL chkstk / - // PUSH used nonvolatiles - - // Calculate stack size - int32 bytesOnStack = MachRegInt + MachRegInt; // Account for return address+push EBP... - - for (RegNum reg = (RegNum)(RegNOREG + 1); reg < RegNumCount; reg = (RegNum)(reg + 1)) - { - if (LinearScan::IsCalleeSaved(reg) && (m_func->m_regsUsed.Test(reg))) - { - bytesOnStack += MachRegInt; - } - } - m_func->m_localStackHeight += MachPtr; - bytesOnStack += m_func->m_localStackHeight; - - int32 alignment = Math::Align(bytesOnStack, MachStackAlignment) - bytesOnStack; - - // Make sure this frame allocation maintains 8-byte alignment. Our point of reference is the return address - m_func->m_localStackHeight += alignment; - bytesOnStack += alignment; - Assert(Math::Align(bytesOnStack, MachStackAlignment) == bytesOnStack); - - m_func->frameSize = bytesOnStack; - - - IR::Instr * insertInstr = entryInstr->m_next; - - IR::RegOpnd * ebpOpnd = IR::RegOpnd::New(nullptr, GetRegBlockPointer(), TyMachReg, m_func); - IR::RegOpnd * espOpnd = IR::RegOpnd::New(nullptr, GetRegStackPointer(), TyMachReg, m_func); - - // Generate PUSH EBP - IR::Instr * pushEbpInstr = IR::Instr::New(Js::OpCode::PUSH, m_func); - pushEbpInstr->SetSrc1(ebpOpnd); - insertInstr->InsertBefore(pushEbpInstr); - - // Generate MOV EBP, ESP - IR::Instr * movInstr = IR::Instr::New(Js::OpCode::MOV, ebpOpnd, espOpnd, m_func); - insertInstr->InsertBefore(movInstr); - - // Generate StackProbe - size_t frameSize = bytesOnStack + m_func->m_argSlotsForFunctionsCalled * MachPtr + Js::Constants::MinStackJIT; - GeneratePrologueStackProbe(insertInstr->m_prev, frameSize); - - if (m_func->m_localStackHeight != 0) - { - int32 stackSize = m_func->m_localStackHeight - MachPtr; - if (m_func->m_localStackHeight <= PAGESIZE) - { - // Generate LEA ESP, [ESP - LocalStackHeight] // Atom prefers LEA for address computations - - IR::IndirOpnd *indirOpnd = IR::IndirOpnd::New(espOpnd, -stackSize, TyMachReg, m_func); - IR::Instr * subInstr = IR::Instr::New(Js::OpCode::LEA, espOpnd, indirOpnd, m_func); - - insertInstr->InsertBefore(subInstr); - } - else - { - IR::RegOpnd *eaxOpnd = IR::RegOpnd::New(nullptr, GetRegChkStkParam(), TyMachReg, m_func); - - // Generate MOV EAX, LocalStackHeight - IR::IntConstOpnd * stackSizeOpnd = IR::IntConstOpnd::New(stackSize, TyMachReg, m_func); - lowererMD->CreateAssign(eaxOpnd, stackSizeOpnd, insertInstr); - - // Generate CALL chkstk - IR::Instr * callInstr = IR::Instr::New(Js::OpCode::Call, eaxOpnd, - IR::HelperCallOpnd::New(IR::HelperCRT_chkstk, m_func), m_func); - insertInstr->InsertBefore(callInstr); - - LowerCall(callInstr, 0, RegECX); - - } - } - - // PUSH used callee-saved registers - - for (RegNum reg = (RegNum)(RegNumCount - 1); reg > RegNOREG; reg = (RegNum)(reg - 1)) - { - if (LinearScan::IsCalleeSaved(reg) && (m_func->m_regsUsed.Test(reg))) - { - IR::RegOpnd * regOpnd = IR::RegOpnd::New(nullptr, reg, TyMachReg, m_func); - IR::Instr * pushInstr = IR::Instr::New(Js::OpCode::PUSH, m_func); - pushInstr->SetSrc1(regOpnd); - insertInstr->InsertBefore(pushInstr); - } - } - -#ifdef ENABLE_DEBUG_CONFIG_OPTIONS - if (Js::Configuration::Global.flags.IsEnabled(Js::CheckAlignmentFlag)) - { - // CALL CheckAlignment - IR::Instr * callInstr = IR::Instr::New(Js::OpCode::Call, m_func); - callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::HelperScrFunc_CheckAlignment, m_func)); - insertInstr->InsertBefore(callInstr); - - LowerCall(callInstr, 0, RegEAX); - } -#endif - - return entryInstr; -} - void LowererMDArch::GeneratePrologueStackProbe(IR::Instr *entryInstr, size_t frameSize) { @@ -1879,18 +1762,20 @@ LowererMDArch::LowerExitInstrCommon(IR::ExitInstr * exitInstr) IR::Instr * LowererMDArch::LowerInt64Assign(IR::Instr * instr) { - IR::Opnd* dst = instr->GetDst(); - IR::Opnd* src1 = instr->GetSrc1(); + IR::Opnd* dst = instr->UnlinkDst(); + IR::Opnd* src1 = instr->UnlinkSrc1(); if (dst && (dst->IsRegOpnd() || dst->IsSymOpnd() || dst->IsIndirOpnd()) && src1) { int dstSize = dst->GetSize(); int srcSize = src1->GetSize(); Int64RegPair dstPair = m_func->FindOrCreateInt64Pair(dst); Int64RegPair src1Pair = m_func->FindOrCreateInt64Pair(src1); - IR::Instr* lowLoadInstr = IR::Instr::New(Js::OpCode::Ld_I4, dstPair.low, src1Pair.low, m_func); - instr->InsertBefore(lowLoadInstr); - lowererMD->ChangeToAssign(lowLoadInstr); + instr->SetSrc1(src1Pair.low); + instr->SetDst(dstPair.low); + instr->m_opcode = Js::OpCode::Ld_I4; + lowererMD->ChangeToAssign(instr); + IR::Instr * insertBeforeInstr = instr->m_next; // Do not store to memory if we wanted less than 8 bytes const bool canAssignHigh = !dst->IsIndirOpnd() || dstSize == 8; @@ -1901,7 +1786,7 @@ LowererMDArch::LowerInt64Assign(IR::Instr * instr) { // Normal case, assign source's high bits to dst's high bits IR::Instr* highLoadInstr = IR::Instr::New(Js::OpCode::Ld_I4, dstPair.high, src1Pair.high, m_func); - instr->InsertBefore(highLoadInstr); + insertBeforeInstr->InsertBefore(highLoadInstr); lowererMD->ChangeToAssign(highLoadInstr); } else @@ -1913,23 +1798,22 @@ LowererMDArch::LowerInt64Assign(IR::Instr * instr) // If this is an unsigned assign from memory, we can simply set the high bits to 0 IR::Instr* highLoadInstr = IR::Instr::New(Js::OpCode::Ld_I4, dstPair.high, IR::IntConstOpnd::New(0, TyInt32, m_func), m_func); lowererMD->ChangeToAssign(highLoadInstr); - instr->InsertBefore(highLoadInstr); + insertBeforeInstr->InsertBefore(highLoadInstr); } else { // If this is a signed assign from memory, we need to extend the sign IR::Instr* highExtendInstr = IR::Instr::New(Js::OpCode::Ld_I4, dstPair.high, dstPair.low, m_func); - instr->InsertBefore(highExtendInstr); + insertBeforeInstr->InsertBefore(highExtendInstr); lowererMD->ChangeToAssign(highExtendInstr); highExtendInstr = IR::Instr::New(Js::OpCode::SAR, dstPair.high, dstPair.high, IR::IntConstOpnd::New(31, TyInt32, m_func), m_func); - instr->InsertBefore(highExtendInstr); + insertBeforeInstr->InsertBefore(highExtendInstr); } } } - - instr->Remove(); - return lowLoadInstr->m_prev; + + return instr->m_prev; } return instr; } diff --git a/lib/Backend/i386/LowererMDArch.h b/lib/Backend/i386/LowererMDArch.h index f9952b3afb1..f4a7cd035e5 100644 --- a/lib/Backend/i386/LowererMDArch.h +++ b/lib/Backend/i386/LowererMDArch.h @@ -69,7 +69,6 @@ class LowererMDArch IR::Instr * LoadHeapArgsCached(IR::Instr * instr); IR::Instr * LowerEntryInstr(IR::EntryInstr * entryInstr); IR::Instr * LowerExitInstr(IR::ExitInstr * exitInstr); - IR::Instr * LowerEntryInstrAsmJs(IR::EntryInstr * entryInstr); IR::Instr * LowerExitInstrAsmJs(IR::ExitInstr * exitInstr); IR::ExitInstr * LowerExitInstrCommon(IR::ExitInstr * exitInstr); IR::Instr * LowerInt64Assign(IR::Instr * instr); diff --git a/lib/Backend/i386/PeepsMD.cpp b/lib/Backend/i386/PeepsMD.cpp index 09124ac6cc5..72d25115015 100644 --- a/lib/Backend/i386/PeepsMD.cpp +++ b/lib/Backend/i386/PeepsMD.cpp @@ -56,7 +56,7 @@ PeepsMD::PeepAssign(IR::Instr *instr) IR::Opnd *dst = instr->GetDst(); if (instr->m_opcode == Js::OpCode::MOV && src->IsIntConstOpnd() - && src->AsIntConstOpnd()->GetValue() == 0 && dst->IsRegOpnd()) + && src->AsIntConstOpnd()->GetValue() == 0 && dst->IsRegOpnd() && !instr->isInlineeEntryInstr) { Assert(instr->GetSrc2() == NULL); diff --git a/lib/Common/ConfigFlagsList.h b/lib/Common/ConfigFlagsList.h index d7a74eea6c4..b9416e9361d 100644 --- a/lib/Common/ConfigFlagsList.h +++ b/lib/Common/ConfigFlagsList.h @@ -446,6 +446,7 @@ PHASE(All) #define DEFAULT_CONFIG_InlineThresholdAdjustCountInMediumSizedFunction (6) #define DEFAULT_CONFIG_InlineThresholdAdjustCountInSmallFunction (10) #define DEFAULT_CONFIG_ConstructorInlineThreshold (21) //Monomorphic constructor threshold +#define DEFAULT_CONFIG_AsmJsInlineAdjust (35) // wasm functions are cheaper to inline, so worth being more aggressive #define DEFAULT_CONFIG_ConstructorCallsRequiredToFinalizeCachedType (2) #define DEFAULT_CONFIG_OutsideLoopInlineThreshold (16) //Threshold to inline outside loops #define DEFAULT_CONFIG_LeafInlineThreshold (60) //Inlinee threshold for function which is leaf (irrespective of it has loops or not) @@ -1168,6 +1169,7 @@ FLAGNR(Number, AggressiveInlineThreshold, "Maximum size in bytecodes of an inli FLAGNR(Number, InlineThresholdAdjustCountInLargeFunction , "Adjustment in the maximum size in bytecodes of an inline candidate in a large function", DEFAULT_CONFIG_InlineThresholdAdjustCountInLargeFunction) FLAGNR(Number, InlineThresholdAdjustCountInMediumSizedFunction , "Adjustment in the maximum size in bytecodes of an inline candidate in a medium sized function", DEFAULT_CONFIG_InlineThresholdAdjustCountInMediumSizedFunction) FLAGNR(Number, InlineThresholdAdjustCountInSmallFunction , "Adjustment in the maximum size in bytecodes of an inline candidate in a small function", DEFAULT_CONFIG_InlineThresholdAdjustCountInSmallFunction) +FLAGNR(Number, AsmJsInlineAdjust , "Adjustment in the maximum size in bytecodes of an inline candidate for wasm function", DEFAULT_CONFIG_AsmJsInlineAdjust) FLAGNR(String, Interpret , "List of functions to interpret", nullptr) FLAGNR(Phases, Instrument , "Instrument the generated code from the given phase", ) FLAGNR(Number, JitQueueThreshold , "Max number of work items/script context in the jit queue", DEFAULT_CONFIG_JitQueueThreshold) diff --git a/lib/Runtime/Base/FunctionBody.cpp b/lib/Runtime/Base/FunctionBody.cpp index aa373c5d8e9..36f9d2e965a 100644 --- a/lib/Runtime/Base/FunctionBody.cpp +++ b/lib/Runtime/Base/FunctionBody.cpp @@ -3405,10 +3405,6 @@ namespace Js // and dynamic profile collection is enabled return !this->m_isFromNativeCodeModule && - !this->m_isAsmJsFunction && -#ifdef ASMJS_PLAT - !this->GetAsmJsModuleInfo() && -#endif !this->HasExecutionDynamicProfileInfo() && DynamicProfileInfo::IsEnabled(this); } @@ -7387,6 +7383,9 @@ namespace Js { return !PHASE_OFF(Js::SimpleJitPhase, this) && +#ifdef ASMJS_PLAT + !GetIsAsmjsMode() && +#endif !GetScriptContext()->GetConfig()->IsNoNative() && !GetScriptContext()->IsScriptContextInDebugMode() && DoInterpreterProfile() && @@ -7399,6 +7398,9 @@ namespace Js { return !PHASE_OFF(Js::SimpleJitPhase, this) && +#ifdef ASMJS_PLAT + !GetIsAsmjsMode() && +#endif !GetScriptContext()->GetConfig()->IsNoNative() && !this->IsInDebugMode() && DoInterpreterProfileWithLock() && @@ -7417,17 +7419,7 @@ namespace Js bool FunctionBody::DoInterpreterProfile() const { #if ENABLE_PROFILE_INFO -#ifdef ASMJS_PLAT - // Switch off profiling is asmJsFunction - if (this->GetIsAsmJsFunction() || this->GetAsmJsModuleInfo()) - { - return false; - } - else -#endif - { - return !PHASE_OFF(InterpreterProfilePhase, this) && DynamicProfileInfo::IsEnabled(this); - } + return !PHASE_OFF(InterpreterProfilePhase, this) && DynamicProfileInfo::IsEnabled(this); #else return false; #endif @@ -7436,17 +7428,7 @@ namespace Js bool FunctionBody::DoInterpreterProfileWithLock() const { #if ENABLE_PROFILE_INFO -#ifdef ASMJS_PLAT - // Switch off profiling is asmJsFunction - if (this->GetIsAsmJsFunction() || this->GetAsmJsModuleInfoWithLock()) - { - return false; - } - else -#endif - { - return !PHASE_OFF(InterpreterProfilePhase, this) && DynamicProfileInfo::IsEnabled(this); - } + return !PHASE_OFF(InterpreterProfilePhase, this) && DynamicProfileInfo::IsEnabled(this); #else return false; #endif @@ -7456,6 +7438,10 @@ namespace Js { Assert(DoInterpreterProfile()); +#ifdef ASMJS_PLAT + if (this->GetIsAsmjsMode()) return false; +#endif + return !PHASE_OFF(InterpreterAutoProfilePhase, this) && !this->IsInDebugMode(); } diff --git a/lib/Runtime/ByteCode/AsmJsByteCodeDumper.cpp b/lib/Runtime/ByteCode/AsmJsByteCodeDumper.cpp index 1978892b880..77461230096 100644 --- a/lib/Runtime/ByteCode/AsmJsByteCodeDumper.cpp +++ b/lib/Runtime/ByteCode/AsmJsByteCodeDumper.cpp @@ -129,11 +129,6 @@ namespace Js Output::Print(_u("\n")); } - if (funcInfo->GetReturnType() == AsmJsRetType::Void) - { - Output::Print(_u(" 0000 %-20s R0\n"), OpCodeUtilAsmJs::GetOpCodeName(OpCodeAsmJs::LdUndef)); - } - uint32 statementIndex = 0; while (true) { @@ -601,6 +596,17 @@ namespace Js Output::Print(_u(" R%d(ArgCount: %d)"), data->Function, data->ArgCount); } + template + void AsmJsByteCodeDumper::DumpProfiledAsmCall(OpCodeAsmJs op, const unaligned T * data, FunctionBody * dumpFunction, ByteCodeReader& reader) + { + if (data->Return != Constants::NoRegister) + { + DumpReg((RegSlot)data->Return); + Output::Print(_u("=")); + } + Output::Print(_u(" R%d(ArgCount: %d, profileId: %d)"), data->Function, data->ArgCount, data->profileId); + } + template void AsmJsByteCodeDumper::DumpAsmUnsigned1(OpCodeAsmJs op, const unaligned T * data, FunctionBody * dumpFunction, ByteCodeReader& reader) { diff --git a/lib/Runtime/ByteCode/AsmJsByteCodeWriter.cpp b/lib/Runtime/ByteCode/AsmJsByteCodeWriter.cpp index 12aacac8e55..e55d57dc260 100644 --- a/lib/Runtime/ByteCode/AsmJsByteCodeWriter.cpp +++ b/lib/Runtime/ByteCode/AsmJsByteCodeWriter.cpp @@ -19,6 +19,12 @@ namespace Js uint offset = GetCurrentOffset(); EncodeOpCode((ushort)op, writer); + if (op > Js::OpCodeAsmJs::Ld_Db || op < Js::OpCodeAsmJs::Ld_IntConst) + { + writer->m_byteCodeWithoutLDACount++; + } + CompileAssert((int)Js::OpCodeAsmJs::Ld_Db - (int)Js::OpCodeAsmJs::Ld_IntConst == 7); + if (!isPatching) { writer->IncreaseByteCodeCount(); @@ -546,9 +552,17 @@ namespace Js m_byteCodeData.Encode(op, &data, sizeof(data), this, isPatching); } - void AsmJsByteCodeWriter::AsmCall(OpCodeAsmJs op, RegSlot returnValueRegister, RegSlot functionRegister, ArgSlot givenArgCount, AsmJsRetType retType) + void AsmJsByteCodeWriter::AsmCall(OpCodeAsmJs op, RegSlot returnValueRegister, RegSlot functionRegister, ArgSlot givenArgCount, AsmJsRetType retType, Js::ProfileId profileId) { + if (DoDynamicProfileOpcode(InlinePhase) && profileId != Js::Constants::NoProfileId && OpCodeAttrAsmJs::HasProfiledOp(op)) + { + OpCodeUtilAsmJs::ConvertOpToProfiled(&op); + } MULTISIZE_LAYOUT_WRITE(AsmCall, op, returnValueRegister, functionRegister, givenArgCount, retType); + if (OpCodeAttrAsmJs::IsProfiledOp(op)) + { + m_byteCodeData.Encode(&profileId, sizeof(Js::ProfileId)); + } } void AsmJsByteCodeWriter::AsmTypedArr(OpCodeAsmJs op, RegSlot value, uint32 slotIndex, ArrayBufferView::ViewType viewType) diff --git a/lib/Runtime/ByteCode/AsmJsByteCodeWriter.h b/lib/Runtime/ByteCode/AsmJsByteCodeWriter.h index 9fa304743b6..7bbdad773d8 100644 --- a/lib/Runtime/ByteCode/AsmJsByteCodeWriter.h +++ b/lib/Runtime/ByteCode/AsmJsByteCodeWriter.h @@ -39,7 +39,7 @@ namespace Js IMP_IWASM void AsmBrReg1(OpCodeAsmJs op, ByteCodeLabel labelID, RegSlot R1); IMP_IWASM void AsmBrReg1Const1(OpCodeAsmJs op, ByteCodeLabel labelID, RegSlot R1, int C1); IMP_IWASM void AsmStartCall(OpCodeAsmJs op, ArgSlot ArgCount, bool isPatching = false); - IMP_IWASM void AsmCall(OpCodeAsmJs op, RegSlot returnValueRegister, RegSlot functionRegister, ArgSlot givenArgCount, AsmJsRetType retType); + IMP_IWASM void AsmCall(OpCodeAsmJs op, RegSlot returnValueRegister, RegSlot functionRegister, ArgSlot givenArgCount, AsmJsRetType retType, ProfileId profileId); IMP_IWASM void AsmSlot(OpCodeAsmJs op, RegSlot value, RegSlot instance, uint32 slotId); IMP_IWASM void WasmMemAccess(OpCodeAsmJs op, RegSlot value, uint32 slotIndex, uint32 offset, ArrayBufferView::ViewType viewType); @@ -113,6 +113,7 @@ namespace Js virtual void End() override; virtual void Reset() override; virtual ByteCodeLabel DefineLabel() override; + virtual void SetCallSiteCount(Js::ProfileId callSiteCount) override; #endif }; } diff --git a/lib/Runtime/ByteCode/ByteCodeWriter.cpp b/lib/Runtime/ByteCode/ByteCodeWriter.cpp index e93fa52eddb..17bf17991a7 100644 --- a/lib/Runtime/ByteCode/ByteCodeWriter.cpp +++ b/lib/Runtime/ByteCode/ByteCodeWriter.cpp @@ -3243,7 +3243,7 @@ namespace Js } template - inline uint ByteCodeWriter::Data::EncodeT(OpCode op, ByteCodeWriter* writer) + uint ByteCodeWriter::Data::EncodeT(OpCode op, ByteCodeWriter* writer) { #ifdef BYTECODE_BRANCH_ISLAND if (writer->useBranchIsland) @@ -3268,7 +3268,7 @@ namespace Js } template - inline uint ByteCodeWriter::Data::EncodeT(OpCode op, const void* rawData, int byteSize, ByteCodeWriter* writer) + uint ByteCodeWriter::Data::EncodeT(OpCode op, const void* rawData, int byteSize, ByteCodeWriter* writer) { AssertMsg((rawData != nullptr) && (byteSize < 100), "Ensure valid data for opcode"); @@ -3277,7 +3277,7 @@ namespace Js return offset; } - inline void ByteCodeWriter::Data::Encode(const void* rawData, int byteSize) + void ByteCodeWriter::Data::Encode(const void* rawData, int byteSize) { AssertMsg(rawData != nullptr, "Ensure valid data for opcode"); Write(rawData, byteSize); diff --git a/lib/Runtime/ByteCode/IWasmByteCodeWriter.h b/lib/Runtime/ByteCode/IWasmByteCodeWriter.h index 482ce6e7650..07baefef447 100644 --- a/lib/Runtime/ByteCode/IWasmByteCodeWriter.h +++ b/lib/Runtime/ByteCode/IWasmByteCodeWriter.h @@ -39,7 +39,9 @@ namespace Js virtual void ExitLoop(uint loopId) = 0; virtual void AsmStartCall(OpCodeAsmJs op, ArgSlot ArgCount, bool isPatching = false) = 0; - virtual void AsmCall(OpCodeAsmJs op, RegSlot returnValueRegister, RegSlot functionRegister, ArgSlot givenArgCount, AsmJsRetType retType) = 0; + virtual void AsmCall(OpCodeAsmJs op, RegSlot returnValueRegister, RegSlot functionRegister, ArgSlot givenArgCount, AsmJsRetType retType, Js::ProfileId profileId) = 0; + + virtual void SetCallSiteCount(Js::ProfileId callSiteCount) = 0; }; } #endif diff --git a/lib/Runtime/ByteCode/LayoutTypesAsmJs.h b/lib/Runtime/ByteCode/LayoutTypesAsmJs.h index df5e5a885d4..297e5b20824 100644 --- a/lib/Runtime/ByteCode/LayoutTypesAsmJs.h +++ b/lib/Runtime/ByteCode/LayoutTypesAsmJs.h @@ -19,6 +19,18 @@ LAYOUT_TYPE(layout##_Large) #endif +#ifndef LAYOUT_TYPE_PROFILED +#define LAYOUT_TYPE_PROFILED(layout) \ + LAYOUT_TYPE(layout) \ + LAYOUT_TYPE(Profiled##layout) +#endif + +#ifndef LAYOUT_TYPE_PROFILED_WMS +#define LAYOUT_TYPE_PROFILED_WMS(layout) \ + LAYOUT_TYPE_WMS(layout) \ + LAYOUT_TYPE_WMS(Profiled##layout) +#endif + #ifndef LAYOUT_TYPE_WMS_REG2 #define LAYOUT_TYPE_WMS_REG2(layout, t0, t1) LAYOUT_TYPE_WMS(layout) #endif @@ -119,7 +131,7 @@ LAYOUT_TYPE_DUP ( Empty ) LAYOUT_TYPE_WMS ( AsmTypedArr ) LAYOUT_TYPE_WMS ( WasmMemAccess ) -LAYOUT_TYPE_WMS ( AsmCall ) +LAYOUT_TYPE_PROFILED_WMS( AsmCall ) LAYOUT_TYPE ( AsmBr ) LAYOUT_TYPE_WMS ( AsmReg1 ) // Generic layout with 1 RegSlot LAYOUT_TYPE_WMS_FE ( AsmReg2 ) // Generic layout with 2 RegSlot @@ -386,6 +398,8 @@ LAYOUT_TYPE_WMS ( AsmSimdTypedArr ) #undef EXCLUDE_DUP_LAYOUT #undef LAYOUT_TYPE_WMS_FE #undef EXCLUDE_FRONTEND_LAYOUT +#undef LAYOUT_TYPE_PROFILED_WMS +#undef LAYOUT_TYPE_PROFILED #undef LAYOUT_PREFIX_Int #undef LAYOUT_PREFIX_IntConst diff --git a/lib/Runtime/ByteCode/OpCodeUtilAsmJs.cpp b/lib/Runtime/ByteCode/OpCodeUtilAsmJs.cpp index 8e905ace5fb..3d5a3e454ae 100644 --- a/lib/Runtime/ByteCode/OpCodeUtilAsmJs.cpp +++ b/lib/Runtime/ByteCode/OpCodeUtilAsmJs.cpp @@ -97,5 +97,12 @@ namespace Js return IsValidByteCodeOpcode(op) || (op > Js::OpCodeAsmJs::ByteCodeLast && op < Js::OpCodeAsmJs::Count); } + + void OpCodeUtilAsmJs::ConvertOpToProfiled(OpCodeAsmJs * op) + { + Assert(OpCodeAttrAsmJs::HasProfiledOp(*op)); + *op += 1; + Assert(OpCodeAttrAsmJs::IsProfiledOp(*op)); + } }; #endif diff --git a/lib/Runtime/ByteCode/OpCodeUtilAsmJs.h b/lib/Runtime/ByteCode/OpCodeUtilAsmJs.h index bb785142783..4ccaa976a01 100644 --- a/lib/Runtime/ByteCode/OpCodeUtilAsmJs.h +++ b/lib/Runtime/ByteCode/OpCodeUtilAsmJs.h @@ -15,7 +15,7 @@ namespace Js static OpLayoutTypeAsmJs GetOpCodeLayout(OpCodeAsmJs op); static bool IsValidByteCodeOpcode(OpCodeAsmJs op); static bool IsValidOpcode(OpCodeAsmJs op); - + static void ConvertOpToProfiled(OpCodeAsmJs * op); private: #if DBG_DUMP || ENABLE_DEBUG_CONFIG_OPTIONS static char16 const * const OpCodeAsmJsNames[(int)Js::OpCodeAsmJs::MaxByteSizedOpcodes + 1]; diff --git a/lib/Runtime/ByteCode/OpCodes.h b/lib/Runtime/ByteCode/OpCodes.h index 756a4a87028..d0891cc60af 100755 --- a/lib/Runtime/ByteCode/OpCodes.h +++ b/lib/Runtime/ByteCode/OpCodes.h @@ -330,8 +330,6 @@ MACRO_BACKEND_ONLY( ArgOut_A_Dynamic, Empty, OpSideEffect MACRO_BACKEND_ONLY( ArgOut_A_FromStackArgs, Empty, OpSideEffect) // Copy from "local slot" to "out slot" MACRO_BACKEND_ONLY( ArgOut_A_FixupForStackArgs, Empty, OpSideEffect) MACRO_BACKEND_ONLY( ArgOut_A_SpreadArg, Empty, OpSideEffect) -MACRO_BACKEND_ONLY( ArgOutAsmJsI_A, Empty, OpSideEffect) -MACRO_BACKEND_ONLY( ArgOutAsmJsE_A, Empty, OpSideEffect) MACRO_WMS( Delete_A, Reg2, OpSideEffect|OpPostOpDbgBailOut) // Delete Var // Object operations @@ -767,7 +765,7 @@ MACRO_BACKEND_ONLY( TrapIfZero, Reg3, OpSideEffect) #undef MACRO_WMS_PROFILED #undef MACRO_WMS_PROFILED2 -#undef MACRO_WMS_PROFILED_OPCODE +#undef MACRO_WMS_PROFILED_OP #undef MACRO_PROFILED #undef MACRO_DEBUG_WMS #undef MACRO_DEBUG diff --git a/lib/Runtime/ByteCode/OpCodesAsmJs.h b/lib/Runtime/ByteCode/OpCodesAsmJs.h index 248dd9bd33d..6b5fe468c19 100755 --- a/lib/Runtime/ByteCode/OpCodesAsmJs.h +++ b/lib/Runtime/ByteCode/OpCodesAsmJs.h @@ -19,6 +19,11 @@ #define MACRO_EXTEND_WMS(opcode, layout, attr) #endif +#define MACRO_WMS_PROFILED( opcode, layout, attr) \ + MACRO_WMS(opcode, layout, OpHasProfiled|attr) \ + MACRO_WMS(Profiled##opcode, Profiled##layout, OpByteCodeOnly|OpProfiled|attr) \ + + // ( OpCodeAsmJs , LayoutAsmJs , OpCodeAttrAsmJs ) // ( | , | , | ) // ( | , | , | ) @@ -37,6 +42,19 @@ MACRO_EXTEND ( InvalidOpCode , Empty , None MACRO ( Label , Empty , None ) // No operation (Default value = 0) MACRO ( Ret , Empty , None ) + +// loads +// Note: bytecode generator assumes this is a contiguous group with this ordering, so don't insert other opcodes in the middle +// (See ByteCodeWriter::Data::EncodeT in AsmJsBytecodeWriter.cpp) +MACRO_WMS ( Ld_IntConst , Int1Const1 , None ) // Sets an int register from a const int +MACRO_WMS ( Ld_Int , Int2 , None ) // Sets an int from another int register +MACRO_WMS ( Ld_LongConst , Long1Const1 , None ) // Sets an int64 register from a const int64 +MACRO_WMS ( Ld_Long , Long2 , None ) // Sets an int64 from another int64 register +MACRO_WMS ( Ld_FltConst , Float1Const1 , None ) // Sets a float register from a const float +MACRO_WMS ( Ld_Flt , Float2 , None ) // Sets a float from another float register +MACRO_WMS ( Ld_DbConst , Double1Const1 , None ) // Sets a double register from a const double +MACRO_WMS ( Ld_Db , Double2 , None ) // Sets a double from another double register + // External Function calls MACRO ( StartCall , StartCall , None ) // Initialize memory for a call MACRO_WMS ( Call , AsmCall , None ) // Execute call and place return value in register @@ -45,19 +63,16 @@ MACRO_WMS ( ArgOut_Long , Reg1Long1 , None MACRO_WMS ( ArgOut_Flt , Reg1Float1 , None ) // convert float to var and place it for function call MACRO_WMS ( ArgOut_Db , Reg1Double1 , None ) // convert double to var and place it for function call MACRO_WMS ( Conv_VTI , Int1Reg1 , None ) // convert var to int -MACRO_WMS ( Conv_VTL , Long1Reg1 , None ) // convert var to int64 MACRO_WMS ( Conv_VTF , Float1Reg1 , None ) // convert var to float MACRO_WMS ( Conv_VTD , Double1Reg1 , None ) // convert var to double +MACRO_WMS ( Conv_VTL , Long1Reg1 , None ) // convert var to int64 // Internal calls MACRO ( I_StartCall , StartCall , None ) // Initialize memory for a call -MACRO_WMS ( I_Call , AsmCall , None ) // Execute call and place return value in register +MACRO_WMS_PROFILED( I_Call , AsmCall , None ) // Execute call and place return value in register MACRO_WMS ( I_ArgOut_Db , Reg1Double1 , None ) // place double arg for function call MACRO_WMS ( I_ArgOut_Int , Reg1Int1 , None ) // place int arg for function call MACRO_WMS ( I_ArgOut_Long , Reg1Long1 , None ) // place int64 arg for function call MACRO_WMS ( I_ArgOut_Flt , Reg1Float1 , None ) // place float arg for function call -MACRO_WMS ( I_Conv_VTI , Int2 , None ) // convert var to int -MACRO_WMS ( I_Conv_VTF , Float2 , None ) // convert var to float -MACRO_WMS ( I_Conv_VTD , Double2 , None ) // convert var to double // loop MACRO_WMS ( AsmJsLoopBodyStart , AsmUnsigned1 , None ) // Marks the start of a loop body @@ -103,7 +118,6 @@ MACRO_WMS ( Return_Flt , Float2 , None MACRO_WMS ( Return_Int , Int2 , None ) // convert int to var // Module memory manipulation -MACRO_WMS ( LdUndef , AsmReg1 , None ) // Load 'undefined' usually in return register MACRO_WMS ( LdSlotArr , ElementSlot , None ) // Loads an array of Var from an array of Var MACRO_WMS ( LdSlot , ElementSlot , None ) // Loads a Var from an array of Var MACRO_WMS ( LdSlot_Db , ElementSlot , None ) // Loads a double from the Module @@ -126,9 +140,8 @@ MACRO_WMS ( LdArrConst , AsmTypedArr , None MACRO_WMS ( StArr , AsmTypedArr , None ) MACRO_WMS ( StArrConst , AsmTypedArr , None ) + // Int math -MACRO_WMS ( Ld_IntConst , Int1Const1 , None ) // Sets an int register from a const int -MACRO_WMS ( Ld_Int , Int2 , None ) // Sets an int from another int register MACRO_WMS ( Neg_Int , Int2 , None ) // int unary '-' MACRO_WMS ( Not_Int , Int2 , None ) // int unary '~' MACRO_WMS ( LogNot_Int , Int2 , None ) // int unary '!' @@ -157,8 +170,6 @@ MACRO_WMS ( Rem_UInt , Int3 , None MACRO_WMS ( Rem_Trap_UInt , Int3 , None ) // (checked) uint32 Arithmetic '%' // Int64 Math -MACRO_WMS ( Ld_LongConst , Long1Const1 , None ) // Sets an int64 register from a const int64 -MACRO_WMS ( Ld_Long , Long2 , None ) // Sets an int64 from another int64 register MACRO_WMS ( Add_Long , Long3 , None ) // int64 Arithmetic '+' MACRO_WMS ( Sub_Long , Long3 , None ) // int64 Arithmetic '-' (subtract) MACRO_WMS ( Mul_Long , Long3 , None ) // int64 Arithmetic '*' @@ -179,8 +190,6 @@ MACRO_WMS ( Ctz_Long , Long2 , None MACRO_WMS ( PopCnt_Long , Long2 , None ) // Double math -MACRO_WMS ( Ld_DbConst , Double1Const1 , None ) // Sets a double register from a const double -MACRO_WMS ( Ld_Db , Double2 , None ) // Sets a double from another double register MACRO_WMS ( Neg_Db , Double2 , None ) // Double Unary '-' MACRO_WMS ( Add_Db , Double3 , None ) // Double Arithmetic '+' MACRO_WMS ( Sub_Db , Double3 , None ) // Double Arithmetic '-' (subtract) @@ -189,8 +198,6 @@ MACRO_WMS ( Div_Db , Double3 , None MACRO_WMS ( Rem_Db , Double3 , None ) // Double Arithmetic '%' // float math -MACRO_WMS ( Ld_FltConst , Float1Const1 , None ) // Sets a float register from a const float -MACRO_WMS ( Ld_Flt , Float2 , None ) // Sets a float from another float register MACRO_WMS ( Neg_Flt , Float2 , None ) // Float Unary '-' MACRO_WMS ( Add_Flt , Float3 , None ) // Float Arithmetic '+' MACRO_WMS ( Sub_Flt , Float3 , None ) // Float Arithmetic '-' (subtract) @@ -323,3 +330,4 @@ MACRO_EXTEND_WMS( PrintF64, Double2, None) #undef MACRO_WMS #undef MACRO_EXTEND #undef MACRO_EXTEND_WMS +#undef MACRO_WMS_PROFILED diff --git a/lib/Runtime/ByteCode/OpCodesSimd.h b/lib/Runtime/ByteCode/OpCodesSimd.h index 78deb6ba688..28fc1aa56c2 100644 --- a/lib/Runtime/ByteCode/OpCodesSimd.h +++ b/lib/Runtime/ByteCode/OpCodesSimd.h @@ -90,7 +90,6 @@ MACRO_SIMD_ASMJS_ONLY_WMS ( Simd128_LdSlot_I4 , ElementSlot MACRO_SIMD_ASMJS_ONLY_WMS ( Simd128_StSlot_I4 , ElementSlot , None , None ) MACRO_SIMD_ASMJS_ONLY_WMS ( Simd128_Return_I4 , Int32x4_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_WMS ( Simd128_I_ArgOut_I4 , Reg1Int32x4_1 , None , None ) -MACRO_SIMD_ASMJS_ONLY_WMS ( Simd128_I_Conv_VTI4 , Int32x4_2 , None , None ) // Extended opcodes. Running out of 1-byte opcode space. Add new opcodes here. MACRO_SIMD(Simd128_End, Empty, None, None) // Just a marker to indicate SIMD opcodes region @@ -129,7 +128,6 @@ MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS( Simd128_LdSlot_F4 , ElementSlo MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS( Simd128_StSlot_F4 , ElementSlot , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS( Simd128_Return_F4 , Float32x4_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS( Simd128_I_ArgOut_F4 , Reg1Float32x4_1 , None , None ) -MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS( Simd128_I_Conv_VTF4 , Float32x4_2 , None , None ) // Float64x2 #if 0 //Disabling this type until the specification decides to include or not. @@ -161,7 +159,6 @@ MACRO_SIMD_EXTEND_WMS(Simd128_Gt_D2, Float64x2_3, None, None, 0) MACRO_SIMD_EXTEND_WMS(Simd128_GtEq_D2, Float64x2_3, None, None, 0) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS(Simd128_Return_D2, Float64x2_2, None, None) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS(Simd128_I_ArgOut_D2, Reg1Float64x2_1, None, None) -MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS(Simd128_I_Conv_VTD2, Float64x2_2, None, None) #endif // 0 //Disabling this type until the specification decides to include or not. //Int8x16 @@ -237,31 +234,24 @@ MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_LdSlot_U16 , ElementSlot MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_StSlot_U16 , ElementSlot , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_ArgOut_I8 , Reg1Int16x8_1 , None , None ) -MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_Conv_VTI8 , Int16x8_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_Return_I8 , Int16x8_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_ArgOut_U4 , Reg1Uint32x4_1 , None , None ) -MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_Conv_VTU4 , Uint32x4_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_Return_U4 , Uint32x4_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_ArgOut_U8 , Reg1Uint16x8_1 , None , None ) -MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_Conv_VTU8 , Uint16x8_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_Return_U8 , Uint16x8_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_ArgOut_U16 , Reg1Uint8x16_1 , None , None ) -MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_Conv_VTU16 , Uint8x16_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_Return_U16 , Uint8x16_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_ArgOut_B4 , Reg1Bool32x4_1 , None , None ) -MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_Conv_VTB4 , Bool32x4_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_Return_B4 , Bool32x4_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_ArgOut_B8 , Reg1Bool16x8_1 , None , None ) -MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_Conv_VTB8 , Bool16x8_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_Return_B8 , Bool16x8_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_ArgOut_B16 , Reg1Bool8x16_1 , None , None ) -MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_I_Conv_VTB16 , Bool8x16_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS ( Simd128_Return_B16 , Bool8x16_2 , None , None ) MACRO_SIMD_EXTEND_WMS ( Simd128_LdArr_I4 , AsmSimdTypedArr , None , OpCanCSE , 4, &Js::SIMDInt32x4Lib::EntryInfo::Load, T_I4, ValueType::GetObject(ObjectType::Int8Array) /*dummy place-holder for any typed array*/, T_INT) @@ -345,7 +335,6 @@ MACRO_SIMD_ASMJS_ONLY_WMS (Simd128_LdSlot_I16 , ElementSlot MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS (Simd128_StSlot_I16 , ElementSlot , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS (Simd128_Return_I16 , Int8x16_2 , None , None ) MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS (Simd128_I_ArgOut_I16 , Reg1Int8x16_1 , None , None ) -MACRO_SIMD_ASMJS_ONLY_EXTEND_WMS (Simd128_I_Conv_VTI16 , Int8x16_2 , None , None ) // Uint32x4 MACRO_SIMD_EXTEND_WMS ( Simd128_IntsToU4 , Uint32x4_1Int4 , None , None , 0) diff --git a/lib/Runtime/ByteCode/OpLayoutsAsmJs.h b/lib/Runtime/ByteCode/OpLayoutsAsmJs.h index 006c590ed67..e70f9c09c83 100644 --- a/lib/Runtime/ByteCode/OpLayoutsAsmJs.h +++ b/lib/Runtime/ByteCode/OpLayoutsAsmJs.h @@ -527,6 +527,17 @@ namespace Js typedef OpLayoutT_##layout OpLayout##layout##_Large; \ typedef OpLayoutT_##layout OpLayout##layout##_Medium; \ typedef OpLayoutT_##layout OpLayout##layout##_Small; + + // Generate the profiled type defs +#define LAYOUT_TYPE_PROFILED(layout) \ + typedef OpLayoutDynamicProfile OpLayoutProfiled##layout; + +#define LAYOUT_TYPE_PROFILED_WMS(layout) \ + LAYOUT_TYPE_WMS(layout) \ + LAYOUT_TYPE_PROFILED(layout##_Large) \ + LAYOUT_TYPE_PROFILED(layout##_Medium) \ + LAYOUT_TYPE_PROFILED(layout##_Small) + #include "LayoutTypesAsmJs.h" #pragma pack(pop) diff --git a/lib/Runtime/ByteCode/WasmByteCodeWriter.cpp b/lib/Runtime/ByteCode/WasmByteCodeWriter.cpp index 3bce18a9d2a..3ab6f764ee3 100644 --- a/lib/Runtime/ByteCode/WasmByteCodeWriter.cpp +++ b/lib/Runtime/ByteCode/WasmByteCodeWriter.cpp @@ -35,6 +35,10 @@ ByteCodeLabel WasmByteCodeWriter::DefineLabel() { return ByteCodeWriter::DefineLabel(); } +void WasmByteCodeWriter::SetCallSiteCount(Js::ProfileId callSiteCount) +{ + ByteCodeWriter::SetCallSiteCount(callSiteCount); +} } #endif diff --git a/lib/Runtime/Language/AsmJsByteCodeGenerator.cpp b/lib/Runtime/Language/AsmJsByteCodeGenerator.cpp index 81d5300661a..c5aa38535b9 100644 --- a/lib/Runtime/Language/AsmJsByteCodeGenerator.cpp +++ b/lib/Runtime/Language/AsmJsByteCodeGenerator.cpp @@ -91,7 +91,6 @@ namespace Js , mInfo( mFunction->GetFuncInfo() ) , mCompiler( compiler ) , mByteCodeGenerator(mCompiler->GetByteCodeGenerator()) - , mNestedCallCount(0) { mWriter.Create(); @@ -149,6 +148,9 @@ namespace Js case WAsmJs::INT32: byteTable = SetConstsToTable(byteTable, 0); break; case WAsmJs::FLOAT32: byteTable = SetConstsToTable(byteTable, 0); break; case WAsmJs::FLOAT64: byteTable = SetConstsToTable(byteTable, 0); break; +#if TARGET_64 + case WAsmJs::INT64: SetConstsToTable(byteTable, 0); break; +#endif case WAsmJs::SIMD: { AsmJsSIMDValue zeroValue; @@ -244,6 +246,7 @@ namespace Js byteCodeGen->MapCacheIdsToPropertyIds( mInfo ); byteCodeGen->MapReferencedPropertyIds( mInfo ); + mWriter.SetCallSiteCount(mFunction->GetProfileIdCount()); mWriter.End(); autoCleanup.FinishCompilation(); @@ -541,10 +544,6 @@ namespace Js return EmitLdArrayBuffer( pnode ); case knopEndCode: StartStatement(pnode); - if( mFunction->GetReturnType() == AsmJsRetType::Void ) - { - mWriter.AsmReg1( Js::OpCodeAsmJs::LdUndef, AsmJsFunctionMemory::ReturnRegister ); - } mWriter.MarkAsmJsLabel( mFunction->GetFuncInfo()->singleExit ); mWriter.EmptyAsm( OpCodeAsmJs::Ret ); EndStatement(pnode); @@ -773,8 +772,6 @@ namespace Js throw AsmJsCompilationException( _u("Different return type for the function") ); } retType = AsmJsRetType::Void; - // Make sure we return something - mWriter.AsmReg1(Js::OpCodeAsmJs::LdUndef, AsmJsFunctionMemory::ReturnRegister); } else { @@ -926,28 +923,91 @@ namespace Js return false; } - // First set of opcode are for External calls, second set is for internal calls - static const OpCodeAsmJs callOpCode[2][7] = + RegSlot AsmJsFunc::AcquireTmpRegisterGeneric(AsmJsRetType retType) { + switch (retType.which()) { - OpCodeAsmJs::StartCall - , OpCodeAsmJs::Call - , OpCodeAsmJs::ArgOut_Db - , OpCodeAsmJs::ArgOut_Int - , OpCodeAsmJs::Conv_VTD - , OpCodeAsmJs::Conv_VTI - , OpCodeAsmJs::Conv_VTF - }, - { - OpCodeAsmJs::I_StartCall - , OpCodeAsmJs::I_Call - , OpCodeAsmJs::I_ArgOut_Db - , OpCodeAsmJs::I_ArgOut_Int - , OpCodeAsmJs::I_Conv_VTD - , OpCodeAsmJs::I_Conv_VTI - , OpCodeAsmJs::I_Conv_VTF + case AsmJsRetType::Signed: + return AcquireTmpRegister(); + case AsmJsRetType::Double: + return AcquireTmpRegister(); + case AsmJsRetType::Float: + return AcquireTmpRegister(); + case AsmJsRetType::Float32x4: + case AsmJsRetType::Int32x4: + case AsmJsRetType::Int16x8: + case AsmJsRetType::Int8x16: + case AsmJsRetType::Uint32x4: + case AsmJsRetType::Uint16x8: + case AsmJsRetType::Uint8x16: + case AsmJsRetType::Bool32x4: + case AsmJsRetType::Bool16x8: + case AsmJsRetType::Bool8x16: + return AcquireTmpRegister(); + case AsmJsRetType::Void: + return Js::Constants::NoRegister; + default: + Assert(UNREACHED); + return Js::Constants::NoRegister; } - }; + } + + + RegSlot AsmJSByteCodeGenerator::EmitIndirectCallIndex(ParseNode* identifierNode, ParseNode* indexNode) + { + // check for table size annotation + if (indexNode->nop != knopAnd) + { + throw AsmJsCompilationException(_u("Function table call must be of format identifier[expr & NumericLiteral](...)")); + } + + ParseNode* tableSizeNode = ParserWrapper::GetBinaryRight(indexNode); + if (tableSizeNode->nop != knopInt) + { + throw AsmJsCompilationException(_u("Function table call must be of format identifier[expr & NumericLiteral](...)")); + } + if (tableSizeNode->sxInt.lw < 0) + { + throw AsmJsCompilationException(_u("Function table size must be positive")); + } + const uint tableSize = tableSizeNode->sxInt.lw + 1; + if (!::Math::IsPow2(tableSize)) + { + throw AsmJsCompilationException(_u("Function table size must be a power of 2")); + } + + // Check for function table identifier + if (!ParserWrapper::IsNameDeclaration(identifierNode)) + { + throw AsmJsCompilationException(_u("Function call must be of format identifier(...) or identifier[expr & size](...)")); + } + PropertyName funcName = identifierNode->name(); + AsmJsFunctionDeclaration* sym = mCompiler->LookupFunction(funcName); + if (!sym) + { + throw AsmJsCompilationException(_u("Unable to find function table %s"), funcName->Psz()); + } + else + { + if (sym->GetSymbolType() != AsmJsSymbol::FuncPtrTable) + { + throw AsmJsCompilationException(_u("Identifier %s is not a function table"), funcName->Psz()); + } + AsmJsFunctionTable* funcTable = sym->Cast(); + if (funcTable->GetSize() != tableSize) + { + throw AsmJsCompilationException(_u("Trying to load from Function table %s of size [%d] with size [%d]"), funcName->Psz(), funcTable->GetSize(), tableSize); + } + } + + const EmitExpressionInfo& indexInfo = Emit(indexNode); + if (!indexInfo.type.isInt()) + { + throw AsmJsCompilationException(_u("Array Buffer View index must be type int")); + } + CheckNodeLocation(indexInfo, int); + return indexInfo.location; + } Js::EmitExpressionInfo AsmJSByteCodeGenerator::EmitCall(ParseNode * pnode, AsmJsRetType expectedType /*= AsmJsType::Void*/) { @@ -962,58 +1022,7 @@ namespace Js identifierNode = ParserWrapper::GetBinaryLeft( pnode->sxCall.pnodeTarget ); ParseNode* indexNode = ParserWrapper::GetBinaryRight( pnode->sxCall.pnodeTarget ); - // check for table size annotation - if( indexNode->nop != knopAnd ) - { - throw AsmJsCompilationException( _u("Function table call must be of format identifier[expr & NumericLiteral](...)") ); - } - - ParseNode* tableSizeNode = ParserWrapper::GetBinaryRight( indexNode ); - if( tableSizeNode->nop != knopInt ) - { - throw AsmJsCompilationException( _u("Function table call must be of format identifier[expr & NumericLiteral](...)") ); - } - if (tableSizeNode->sxInt.lw < 0) - { - throw AsmJsCompilationException(_u("Function table size must be positive")); - } - const uint tableSize = tableSizeNode->sxInt.lw+1; - if( !::Math::IsPow2(tableSize) ) - { - throw AsmJsCompilationException( _u("Function table size must be a power of 2") ); - } - - // Check for function table identifier - if( !ParserWrapper::IsNameDeclaration( identifierNode ) ) - { - throw AsmJsCompilationException( _u("Function call must be of format identifier(...) or identifier[expr & size](...)") ); - } - PropertyName funcName = identifierNode->name(); - AsmJsFunctionDeclaration* sym = mCompiler->LookupFunction( funcName ); - if( !sym ) - { - throw AsmJsCompilationException( _u("Unable to find function table %s"), funcName->Psz() ); - } - else - { - if( sym->GetSymbolType() != AsmJsSymbol::FuncPtrTable ) - { - throw AsmJsCompilationException( _u("Identifier %s is not a function table"), funcName->Psz() ); - } - AsmJsFunctionTable* funcTable = sym->Cast(); - if( funcTable->GetSize() != tableSize ) - { - throw AsmJsCompilationException( _u("Trying to load from Function table %s of size [%d] with size [%d]"), funcName->Psz(), funcTable->GetSize(), tableSize ); - } - } - - const EmitExpressionInfo& indexInfo = Emit( indexNode ); - if( !indexInfo.type.isInt() ) - { - throw AsmJsCompilationException( _u("Array Buffer View index must be type int") ); - } - CheckNodeLocation( indexInfo, int ); - funcTableIndexRegister = indexInfo.location; + funcTableIndexRegister = EmitIndirectCallIndex(identifierNode, indexNode); } if( !ParserWrapper::IsNameDeclaration( identifierNode ) ) @@ -1027,7 +1036,6 @@ namespace Js throw AsmJsCompilationException( _u("Undefined function %s"), funcName ); } - if (sym->GetSymbolType() == AsmJsSymbol::SIMDBuiltinFunction) { AsmJsSIMDFunction *simdFun = sym->Cast(); @@ -1053,46 +1061,70 @@ namespace Js { throw AsmJsCompilationException(_u("Different return type found for function %s"), funcName->Psz()); } - const int StartCallIndex = 0; - const int CallIndex = 1; - const int ArgOut_DbIndex = 2; - const int ArgOut_IntIndex = 3; - const int Conv_VTDIndex = 4; - const int Conv_VTIIndex =5; - const int Conv_VTFIndex = 6; - const int funcOpCode = isFFI ? 0 : 1; - // StartCall const ArgSlot argCount = pnode->sxCall.argCount; - StartStatement(pnode); - ++mNestedCallCount; + EmitExpressionInfo * argArray = nullptr; + AsmJsType* types = nullptr; + + // first, evaluate function arguments + if (argCount > 0) + { + ParseNode* argNode = pnode->sxCall.pnodeArgs; + argArray = AnewArray(&mAllocator, EmitExpressionInfo, argCount); + types = AnewArray(&mAllocator, AsmJsType, argCount); + for (ArgSlot i = 0; i < argCount; i++) + { + ParseNode* arg = argNode; + if (argNode->nop == knopList) + { + arg = ParserWrapper::GetBinaryLeft(argNode); + argNode = ParserWrapper::GetBinaryRight(argNode); + } - uint startCallOffset = mWriter.GetCurrentOffset(); - auto startCallChunk = mWriter.GetCurrentChunk(); - uint startCallChunkOffset = startCallChunk->GetCurrentOffset(); + // Emit argument + argArray[i] = Emit(arg); + types[i] = argArray[i].type; + } + } - bool patchStartCall = sym->GetArgCount() == Constants::InvalidArgSlot; - if (patchStartCall) + // Check if this function supports the type of these arguments + AsmJsRetType retType; + const bool supported = sym->SupportsArgCall(argCount, types, retType); + if (!supported) { - // we will not know the types of the arguments for the first call to a deferred function, - // so we put a placeholder instr in the bytecode and then patch it with correct arg size - // once we evaluate the arguments - mWriter.AsmStartCall(callOpCode[funcOpCode][StartCallIndex], Constants::InvalidArgSlot); + throw AsmJsCompilationException(_u("Function %s doesn't support arguments"), funcName->Psz()); } - else + if (types) { - // args size + 1 pointer - const ArgSlot argByteSize = ArgSlotMath::Add(sym->GetArgByteSize(argCount), sizeof(Var)); - mWriter.AsmStartCall(callOpCode[funcOpCode][StartCallIndex], argByteSize); + AdeleteArray(&mAllocator, argCount, types); + types = nullptr; } - AutoArrayPtr types(nullptr, 0); - int maxDepthForLevel = mFunction->GetArgOutDepth(); + + // need to validate return type again because function might support arguments, + // but return a different type, i.e.: abs(int) -> int, but expecting double + // don't validate the return type for foreign import functions + if (!isFFI && retType != expectedType) + { + throw AsmJsCompilationException(_u("Function %s returns different type"), funcName->Psz()); + } + + const ArgSlot argByteSize = ArgSlotMath::Add(sym->GetArgByteSize(argCount), sizeof(Var)); + // +1 is for function object + ArgSlot runtimeArg = ArgSlotMath::Add(argCount, 1); + if (!isFFI) // for non import functions runtimeArg is calculated from argByteSize + { + runtimeArg = (ArgSlot)(::ceil((double)(argByteSize / sizeof(Var)))) + 1; + } + + StartStatement(pnode); + + mWriter.AsmStartCall(isFFI ? OpCodeAsmJs::StartCall : OpCodeAsmJs::I_StartCall, argByteSize); + if( argCount > 0 ) { ParseNode* argNode = pnode->sxCall.pnodeArgs; uint16 regSlotLocation = 1; - types.Set(HeapNewArray( AsmJsType, argCount ), argCount); for(ArgSlot i = 0; i < argCount; i++) { @@ -1103,25 +1135,21 @@ namespace Js arg = ParserWrapper::GetBinaryLeft( argNode ); argNode = ParserWrapper::GetBinaryRight( argNode ); } - - // Emit argument - const EmitExpressionInfo& argInfo = Emit( arg ); - types[i] = argInfo.type; + EmitExpressionInfo argInfo = argArray[i]; // OutParams i if( argInfo.type.isDouble() ) { CheckNodeLocation( argInfo, double ); - if (callOpCode[funcOpCode][ArgOut_DbIndex] == OpCodeAsmJs::ArgOut_Db) + if (isFFI) { - mWriter.AsmReg2(callOpCode[funcOpCode][ArgOut_DbIndex], regSlotLocation, argInfo.location); + mWriter.AsmReg2(OpCodeAsmJs::ArgOut_Db, regSlotLocation, argInfo.location); regSlotLocation++; // in case of external calls this is boxed and converted to a Var } else { - mWriter.AsmReg2(callOpCode[funcOpCode][ArgOut_DbIndex], regSlotLocation, argInfo.location); + mWriter.AsmReg2(OpCodeAsmJs::I_ArgOut_Db, regSlotLocation, argInfo.location); regSlotLocation += sizeof(double) / sizeof(Var);// in case of internal calls we will pass this arg as double } - mFunction->ReleaseLocation( &argInfo ); } else if (argInfo.type.isFloat()) { @@ -1132,14 +1160,12 @@ namespace Js } mWriter.AsmReg2(OpCodeAsmJs::I_ArgOut_Flt, regSlotLocation, argInfo.location); regSlotLocation++; - mFunction->ReleaseLocation(&argInfo); } else if (argInfo.type.isInt()) { CheckNodeLocation( argInfo, int ); - mWriter.AsmReg2(callOpCode[funcOpCode][ArgOut_IntIndex], regSlotLocation, argInfo.location); + mWriter.AsmReg2(isFFI ? OpCodeAsmJs::ArgOut_Int : OpCodeAsmJs::I_ArgOut_Int, regSlotLocation, argInfo.location); regSlotLocation++; - mFunction->ReleaseLocation( &argInfo ); } else if (argInfo.type.isSIMDType()) { @@ -1187,220 +1213,80 @@ namespace Js } mWriter.AsmReg2(opcode, regSlotLocation, argInfo.location); regSlotLocation += sizeof(AsmJsSIMDValue) / sizeof(Var); - mFunction->ReleaseLocation(&argInfo); } else { throw AsmJsCompilationException(_u("Function %s doesn't support argument of type %s"), funcName->Psz(), argInfo.type.toChars()); } - // if there are nested calls, track whichever is the deepest - if (maxDepthForLevel < mFunction->GetArgOutDepth()) - { - maxDepthForLevel = mFunction->GetArgOutDepth(); - } } - } - // Check if this function supports the type of these arguments - AsmJsRetType retType; - const bool supported = sym->SupportsArgCall( argCount, types, retType ); - if( !supported ) - { - throw AsmJsCompilationException( _u("Function %s doesn't support arguments"), funcName->Psz() ); - } - - // need to validate return type again because function might support arguments, - // but return a different type, i.e.: abs(int) -> int, but expecting double - // don't validate the return type for foreign import functions - if( !isFFI && retType != expectedType ) - { - throw AsmJsCompilationException( _u("Function %s returns different type"), funcName->Psz() ); - } - const ArgSlot argByteSize = ArgSlotMath::Add(sym->GetArgByteSize(argCount), sizeof(Var)); - // +1 is for function object - ArgSlot runtimeArg = ArgSlotMath::Add(argCount, 1); - if (funcOpCode == 1) // for non import functions runtimeArg is calculated from argByteSize - { - runtimeArg = (ArgSlot)(::ceil((double)(argByteSize / sizeof(Var)))) + 1; + for (ArgSlot i = argCount; i > 0; --i) + { + mFunction->ReleaseLocationGeneric(&argArray[i - 1]); + } + AdeleteArray(&mAllocator, argCount, argArray); + argArray = nullptr; } - // +1 is for return address - maxDepthForLevel += ArgSlotMath::Add(runtimeArg, 1); // Make sure we have enough memory allocated for OutParameters - if (mNestedCallCount > 1) - { - mFunction->SetArgOutDepth(maxDepthForLevel); - } - else - { - mFunction->SetArgOutDepth(0); - } - mFunction->UpdateMaxArgOutDepth(maxDepthForLevel); - - if (patchStartCall) - { - uint latestOffset = mWriter.GetCurrentOffset(); - auto latestChunk = mWriter.GetCurrentChunk(); - uint latestChunkOffset = latestChunk->GetCurrentOffset(); - - // now that we know the types, we can patch the StartCall instr - startCallChunk->SetCurrentOffset(startCallChunkOffset); - mWriter.SetCurrent(startCallOffset, startCallChunk); - - // args size + 1 pointer - mWriter.AsmStartCall(callOpCode[funcOpCode][StartCallIndex], argByteSize, true /* isPatching */); - - // ... and return to where we left off in the buffer like nothing ever happened - latestChunk->SetCurrentOffset(latestChunkOffset); - mWriter.SetCurrent(latestOffset, latestChunk); - } + // +1 is for return address + mFunction->UpdateMaxArgOutDepth(ArgSlotMath::Add(runtimeArg, 1)); // Load function from env + ProfileId profileId = Js::Constants::NoProfileId; + RegSlot funcReg = Js::Constants::NoRegister; switch( sym->GetSymbolType() ) { case AsmJsSymbol::ModuleFunction: - LoadModuleFunction( AsmJsFunctionMemory::FunctionRegister, sym->GetFunctionIndex() ); + funcReg = mFunction->AcquireTmpRegister(); + LoadModuleFunction(funcReg, sym->GetFunctionIndex()); + profileId = mFunction->GetNextProfileId(); break; case AsmJsSymbol::ImportFunction: - LoadModuleFFI( AsmJsFunctionMemory::FunctionRegister, sym->GetFunctionIndex() ); + funcReg = mFunction->AcquireTmpRegister(); + LoadModuleFFI(funcReg, sym->GetFunctionIndex()); break; case AsmJsSymbol::FuncPtrTable: - LoadModuleFunctionTable( AsmJsFunctionMemory::FunctionRegister, sym->GetFunctionIndex(), funcTableIndexRegister ); - mFunction->ReleaseTmpRegister( funcTableIndexRegister ); + mFunction->ReleaseTmpRegister(funcTableIndexRegister); + funcReg = mFunction->AcquireTmpRegister(); + LoadModuleFunctionTable(funcReg, sym->GetFunctionIndex(), funcTableIndexRegister); break; default: Assert( false ); } - // Call - mWriter.AsmCall( callOpCode[funcOpCode][CallIndex], AsmJsFunctionMemory::CallReturnRegister, AsmJsFunctionMemory::FunctionRegister, runtimeArg, expectedType ); // use expected type because return type could be invalid if the function is a FFI - EmitExpressionInfo info( expectedType.toType() ); - switch( expectedType.which() ) - { - case AsmJsRetType::Void: - // do nothing - break; - case AsmJsRetType::Signed: - { - RegSlot intReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2( callOpCode[funcOpCode][Conv_VTIIndex], intReg, AsmJsFunctionMemory::CallReturnRegister ); - info.location = intReg; - break; - } - case AsmJsRetType::Double: - { - RegSlot dbReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2( callOpCode[funcOpCode][Conv_VTDIndex], dbReg, AsmJsFunctionMemory::CallReturnRegister ); - info.location = dbReg; - break; - } - case AsmJsRetType::Float: - { - Assert(!isFFI); //check spec - RegSlot fltReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2(callOpCode[funcOpCode][Conv_VTFIndex], fltReg, AsmJsFunctionMemory::CallReturnRegister); - info.location = fltReg; - break; - } - case AsmJsRetType::Float32x4: - { - Assert(!isFFI); - RegSlot simdReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2(OpCodeAsmJs::Simd128_I_Conv_VTF4, simdReg, AsmJsFunctionMemory::CallReturnRegister); - info.location = simdReg; - break; - } - case AsmJsRetType::Int32x4: + EmitExpressionInfo info(expectedType.toType()); + mFunction->ReleaseTmpRegister(funcReg); + if (isFFI) { - Assert(!isFFI); - RegSlot simdReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2(OpCodeAsmJs::Simd128_I_Conv_VTI4, simdReg, AsmJsFunctionMemory::CallReturnRegister); - info.location = simdReg; - break; - } -#if 0 - case AsmJsRetType::Float64x2: - { - Assert(!isFFI); - RegSlot simdReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2(OpCodeAsmJs::Simd128_I_Conv_VTD2, simdReg, AsmJsFunctionMemory::CallReturnRegister); - info.location = simdReg; - break; - } -#endif // 0 + RegSlot retReg = mFunction->AcquireTmpRegister(); + mWriter.AsmCall(OpCodeAsmJs::Call, retReg, funcReg, runtimeArg, expectedType, profileId); - case AsmJsRetType::Int16x8: - { - Assert(!isFFI); - RegSlot simdReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2(OpCodeAsmJs::Simd128_I_Conv_VTI8, simdReg, AsmJsFunctionMemory::CallReturnRegister); - info.location = simdReg; - break; - } - case AsmJsRetType::Int8x16: - { - Assert(!isFFI); - RegSlot simdReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2(OpCodeAsmJs::Simd128_I_Conv_VTI16, simdReg, AsmJsFunctionMemory::CallReturnRegister); - info.location = simdReg; - break; - } - case AsmJsRetType::Uint32x4: - { - Assert(!isFFI); - RegSlot simdReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2(OpCodeAsmJs::Simd128_I_Conv_VTU4, simdReg, AsmJsFunctionMemory::CallReturnRegister); - info.location = simdReg; - break; - } - case AsmJsRetType::Uint16x8: - { - Assert(!isFFI); - RegSlot simdReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2(OpCodeAsmJs::Simd128_I_Conv_VTU8, simdReg, AsmJsFunctionMemory::CallReturnRegister); - info.location = simdReg; - break; - } - case AsmJsRetType::Uint8x16: - { - Assert(!isFFI); - RegSlot simdReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2(OpCodeAsmJs::Simd128_I_Conv_VTU16, simdReg, AsmJsFunctionMemory::CallReturnRegister); - info.location = simdReg; - break; - } - case AsmJsRetType::Bool32x4: - { - Assert(!isFFI); - RegSlot simdReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2(OpCodeAsmJs::Simd128_I_Conv_VTB4, simdReg, AsmJsFunctionMemory::CallReturnRegister); - info.location = simdReg; - break; - } - case AsmJsRetType::Bool16x8: - { - Assert(!isFFI); - RegSlot simdReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2(OpCodeAsmJs::Simd128_I_Conv_VTB8, simdReg, AsmJsFunctionMemory::CallReturnRegister); - info.location = simdReg; - break; + mFunction->ReleaseTmpRegister(retReg); + info.location = mFunction->AcquireTmpRegisterGeneric(expectedType); + + switch (expectedType.which()) + { + case AsmJsRetType::Void: + break; + case AsmJsRetType::Signed: + mWriter.AsmReg2(OpCodeAsmJs::Conv_VTI, info.location, retReg); + break; + case AsmJsRetType::Double: + mWriter.AsmReg2(OpCodeAsmJs::Conv_VTD, info.location, retReg); + break; + default: + Assert(UNREACHED); + } } - case AsmJsRetType::Bool8x16: + else { - Assert(!isFFI); - RegSlot simdReg = mFunction->AcquireTmpRegister(); - mWriter.AsmReg2(OpCodeAsmJs::Simd128_I_Conv_VTB16, simdReg, AsmJsFunctionMemory::CallReturnRegister); - info.location = simdReg; - break; - } - default: - break; + info.location = mFunction->AcquireTmpRegisterGeneric(expectedType); + mWriter.AsmCall(OpCodeAsmJs::I_Call, info.location, funcReg, runtimeArg, expectedType, profileId); } EndStatement(pnode); - --mNestedCallCount; - Assert(mNestedCallCount >= 0); return info; } @@ -1932,8 +1818,6 @@ namespace Js return EmitMinMax(pnode, mathFunction); } - ++mNestedCallCount; - const ArgSlot argCount = pnode->sxCall.argCount; ParseNode* argNode = pnode->sxCall.pnodeArgs; const bool isFRound = AsmJsMathFunction::IsFround(mathFunction); @@ -1964,7 +1848,6 @@ namespace Js AutoArrayPtr types(nullptr, 0); AutoArrayPtr argsInfo(nullptr, 0); - int maxDepthForLevel = mFunction->GetArgOutDepth(); if( argCount > 0 ) { types.Set(HeapNewArray(AsmJsType, argCount), argCount); @@ -1996,11 +1879,6 @@ namespace Js argsInfo[i].type = argInfo.type; argsInfo[i].location = argInfo.location; } - // if there are nested calls, track whichever is the deepest - if (maxDepthForLevel < mFunction->GetArgOutDepth()) - { - maxDepthForLevel = mFunction->GetArgOutDepth(); - } } } StartStatement(pnode); @@ -2022,19 +1900,10 @@ namespace Js const int argByteSize = mathFunction->GetArgByteSize(argCount) + sizeof(Var); // + 1 is for function object int runtimeArg = (int)(::ceil((double)(argByteSize / sizeof(Var)))) + 1; - // + 1 for return address - maxDepthForLevel += runtimeArg + 1; // Make sure we have enough memory allocated for OutParameters - if (mNestedCallCount > 1) - { - mFunction->SetArgOutDepth(maxDepthForLevel); - } - else - { - mFunction->SetArgOutDepth(0); - } - mFunction->UpdateMaxArgOutDepth(maxDepthForLevel); + // + 1 for return address + mFunction->UpdateMaxArgOutDepth(runtimeArg + 1); const bool isInt = retType.toType().isInt(); const bool isFloatish = retType.toType().isFloatish(); @@ -2085,15 +1954,12 @@ namespace Js } #endif EndStatement(pnode); - --mNestedCallCount; return emitInfo; } EmitExpressionInfo AsmJSByteCodeGenerator::EmitMinMax(ParseNode* pnode, AsmJsMathFunction* mathFunction) { Assert(mathFunction->GetArgCount() == 2); - ++mNestedCallCount; - uint16 argCount = pnode->sxCall.argCount; ParseNode* argNode = pnode->sxCall.pnodeArgs; @@ -2111,7 +1977,6 @@ namespace Js argNode = ParserWrapper::GetBinaryRight(argNode); // Emit first arg as arg0 argsInfo[0] = Emit(arg); - int maxDepthForLevel = mFunction->GetArgOutDepth(); types[0] = argsInfo[0].type; EmitExpressionInfo dstInfo; @@ -2130,12 +1995,6 @@ namespace Js argsInfo[1] = Emit(arg); types[1] = argsInfo[1].type; - // if there are nested calls, track whichever is the deepest - if (maxDepthForLevel < mFunction->GetArgOutDepth()) - { - maxDepthForLevel = mFunction->GetArgOutDepth(); - } - // Check if this function supports the type of these arguments AsmJsRetType retType; OpCodeAsmJs op; @@ -2149,19 +2008,9 @@ namespace Js // +1 is for function object int runtimeArg = (int)(::ceil((double)(argByteSize / sizeof(Var)))) + 1; // +1 is for return address - maxDepthForLevel += runtimeArg + 1; // Make sure we have enough memory allocated for OutParameters - if (mNestedCallCount > 1) - { - mFunction->SetArgOutDepth(maxDepthForLevel); - } - else - { - mFunction->SetArgOutDepth(0); - } - mFunction->UpdateMaxArgOutDepth(maxDepthForLevel); - maxDepthForLevel = 0; + mFunction->UpdateMaxArgOutDepth(runtimeArg + 1); mFunction->ReleaseLocationGeneric(&argsInfo[1]); mFunction->ReleaseLocationGeneric(&argsInfo[0]); @@ -2197,7 +2046,6 @@ namespace Js } #endif } - --mNestedCallCount; return dstInfo; } @@ -3340,8 +3188,11 @@ namespace Js void AsmJSByteCodeGenerator::LoadModuleFunctionTable( RegSlot dst, RegSlot FuncTableIndex, RegSlot FuncIndexLocation ) { - mWriter.AsmSlot( OpCodeAsmJs::LdSlotArr, AsmJsFunctionMemory::ModuleSlotRegister, AsmJsFunctionMemory::ModuleEnvRegister, FuncTableIndex+mCompiler->GetFuncPtrOffset() ); - mWriter.AsmSlot( OpCodeAsmJs::LdArr_Func, dst, AsmJsFunctionMemory::ModuleSlotRegister, FuncIndexLocation ); + RegSlot slotReg = mFunction->AcquireTmpRegister(); + mWriter.AsmSlot( OpCodeAsmJs::LdSlotArr, slotReg, AsmJsFunctionMemory::ModuleEnvRegister, FuncTableIndex+mCompiler->GetFuncPtrOffset() ); + mWriter.AsmSlot( OpCodeAsmJs::LdArr_Func, dst, slotReg, FuncIndexLocation ); + + mFunction->ReleaseTmpRegister(slotReg); } void AsmJSByteCodeGenerator::SetModuleInt( Js::RegSlot dst, RegSlot src ) diff --git a/lib/Runtime/Language/AsmJsByteCodeGenerator.h b/lib/Runtime/Language/AsmJsByteCodeGenerator.h index d9d9bb13f21..02cd244d9ee 100644 --- a/lib/Runtime/Language/AsmJsByteCodeGenerator.h +++ b/lib/Runtime/Language/AsmJsByteCodeGenerator.h @@ -79,6 +79,8 @@ namespace Js LoadTypedArray, StoreTypedArray, }; + RegSlot EmitIndirectCallIndex(ParseNode* identifierNode, ParseNode* indexNode); + EmitExpressionInfo EmitTypedArrayIndex(ParseNode* indexNode, OpCodeAsmJs &op, uint32 &indexSlot, ArrayBufferView::ViewType viewType, TypedArrayEmitType emitType); EmitExpressionInfo EmitAssignment( ParseNode * pnode ); EmitExpressionInfo EmitReturn( ParseNode * pnode ); diff --git a/lib/Runtime/Language/AsmJsEncoder.h b/lib/Runtime/Language/AsmJsEncoder.h index 320d5a01488..c18b3cfcf74 100644 --- a/lib/Runtime/Language/AsmJsEncoder.h +++ b/lib/Runtime/Language/AsmJsEncoder.h @@ -67,7 +67,6 @@ namespace Js void OP_Label( const unaligned OpLayoutEmpty* playout ); - template void OP_LdUndef( const unaligned T* playout ); template void OP_Br( const unaligned T* playout ); template void OP_BrEq( const unaligned T* playout ); template void OP_BrEqConst( const unaligned T* playout ); @@ -113,9 +112,6 @@ namespace Js template void OP_I_ArgOut_Db( const unaligned T* playout ); template void OP_I_ArgOut_Int(const unaligned T* playout); template void OP_I_ArgOut_Flt(const unaligned T* playout); - template void OP_I_Conv_VTD( const unaligned T* playout ); - template void OP_I_Conv_VTI( const unaligned T* playout ); - template void OP_I_Conv_VTF( const unaligned T* playout ); template void OP_AsmJsLoopBody(const unaligned T* playout); @@ -222,10 +218,6 @@ namespace Js template void OP_Simd128_I_ArgOutF4(const unaligned T* playout); template void OP_Simd128_I_ArgOutI4(const unaligned T* playout); template void OP_Simd128_I_ArgOutD2(const unaligned T* playout); - - template void OP_Simd128_I_Conv_VTF4(const unaligned T* playout); - template void OP_Simd128_I_Conv_VTI4(const unaligned T* playout); - template void OP_Simd128_I_Conv_VTD2(const unaligned T* playout); }; } diff --git a/lib/Runtime/Language/AsmJsEncoder.inl b/lib/Runtime/Language/AsmJsEncoder.inl index be4924824b4..be78c000f8c 100644 --- a/lib/Runtime/Language/AsmJsEncoder.inl +++ b/lib/Runtime/Language/AsmJsEncoder.inl @@ -173,12 +173,6 @@ namespace Js this->GetAsmJsFunctionInfo()->mbyteCodeTJMap->AddNew(mReader.GetCurrentOffset(), offset); } - template - void AsmJsEncoder::OP_LdUndef( const unaligned T* playout ) - { - AsmJsJitTemplate::LdUndef::ApplyTemplate( this, mPc, CalculateOffset(playout->R0) ); - } - template void AsmJsEncoder::OP_Br( const unaligned T* playout ) { @@ -434,21 +428,6 @@ namespace Js AsmJsJitTemplate::I_ArgOut_Int::ApplyTemplate( this, mPc, playout->R0, CalculateOffset(playout->I1)); } - template void Js::AsmJsEncoder::OP_I_Conv_VTD( const unaligned T* playout ) - { - AsmJsJitTemplate::I_Conv_VTD::ApplyTemplate(this, mPc, CalculateOffset(playout->D0), CalculateOffset(playout->D1)); - } - - template void Js::AsmJsEncoder::OP_I_Conv_VTF(const unaligned T* playout) - { - AsmJsJitTemplate::I_Conv_VTF::ApplyTemplate(this, mPc, CalculateOffset(playout->F0), CalculateOffset(playout->F1)); - } - - template void Js::AsmJsEncoder::OP_I_Conv_VTI( const unaligned T* playout ) - { - AsmJsJitTemplate::I_Conv_VTI::ApplyTemplate( this, mPc, CalculateOffset(playout->I0), CalculateOffset(playout->I1)); - } - template void Js::AsmJsEncoder::Op_LdArr( const unaligned T* playout ) { @@ -993,17 +972,4 @@ namespace Js { AsmJsJitTemplate::Simd128_I_ArgOut_D2::ApplyTemplate(this, mPc, playout->R0, CalculateOffset(playout->D2_1)); } - - template void Js::AsmJsEncoder::OP_Simd128_I_Conv_VTF4(const unaligned T* playout) - { - AsmJsJitTemplate::Simd128_I_Conv_VTF4::ApplyTemplate(this, mPc, CalculateOffset(playout->F4_0), CalculateOffset(playout->F4_1)); - } - template void Js::AsmJsEncoder::OP_Simd128_I_Conv_VTI4(const unaligned T* playout) - { - AsmJsJitTemplate::Simd128_I_Conv_VTI4::ApplyTemplate(this, mPc, CalculateOffset(playout->I4_0), CalculateOffset(playout->I4_1)); - } - template void Js::AsmJsEncoder::OP_Simd128_I_Conv_VTD2(const unaligned T* playout) - { - AsmJsJitTemplate::Simd128_I_Conv_VTD2::ApplyTemplate(this, mPc, CalculateOffset(playout->D2_0), CalculateOffset(playout->D2_1)); - } } diff --git a/lib/Runtime/Language/AsmJsEncoderHandler.inl b/lib/Runtime/Language/AsmJsEncoderHandler.inl index 2c34ebe5e5d..1dd346e088b 100644 --- a/lib/Runtime/Language/AsmJsEncoderHandler.inl +++ b/lib/Runtime/Language/AsmJsEncoderHandler.inl @@ -61,9 +61,6 @@ EXDEF3 ( CUSTOM , InvalidOpCode , OP_Empty , Empty DEF3_WMS( CUSTOM , I_ArgOut_Db , OP_I_ArgOut_Db , Reg1Double1 ) DEF3_WMS( CUSTOM , I_ArgOut_Flt , OP_I_ArgOut_Flt , Reg1Float1 ) DEF3_WMS( CUSTOM , I_ArgOut_Int , OP_I_ArgOut_Int , Reg1Int1 ) - DEF3_WMS( CUSTOM , I_Conv_VTD , OP_I_Conv_VTD , Double2 ) - DEF3_WMS( CUSTOM , I_Conv_VTI , OP_I_Conv_VTI , Int2 ) - DEF3_WMS( CUSTOM , I_Conv_VTF , OP_I_Conv_VTF , Float2 ) DEF3 ( CUSTOM , AsmBr , OP_Br , AsmBr ) DEF3_WMS( CUSTOM , BrTrue_Int , OP_BrTrue , BrInt1 ) @@ -86,7 +83,6 @@ EXDEF3 ( CUSTOM , InvalidOpCode , OP_Empty , Empty DEF3_WMS( CUSTOM , Return_Int , OP_SetReturnInt , Int2 ) - DEF3_WMS( CUSTOM , LdUndef , OP_LdUndef , AsmReg1 ) DEF3_WMS( CUSTOM , LdSlotArr , OP_LdSlot , ElementSlot ) DEF3_WMS( CUSTOM , LdSlot , OP_LdSlot , ElementSlot ) DEF3_WMS( CUSTOM , LdSlot_Db , Op_LdSlot_Db , ElementSlot ) @@ -318,10 +314,6 @@ EXDEF3 ( CUSTOM , InvalidOpCode , OP_Empty , Empty DEF3_WMS( CUSTOM , Simd128_ReplaceLane_I4 , OP_Simd128_ReplaceLaneI4 , Int32x4_2Int2) DEF3_WMS( CUSTOM , Simd128_ReplaceLane_F4 , OP_Simd128_ReplaceLaneF4 , Float32x4_2Int1Float1) - DEF3_WMS( CUSTOM , Simd128_I_Conv_VTF4 , OP_Simd128_I_Conv_VTF4 , Float32x4_2 ) - DEF3_WMS( CUSTOM , Simd128_I_Conv_VTI4 , OP_Simd128_I_Conv_VTI4 , Int32x4_2 ) - //DEF3_WMS( CUSTOM , Simd128_I_Conv_VTD2 , OP_Simd128_I_Conv_VTD2 , Float64x2_2 ) - // help the caller to undefine all the macros #undef DEF2 #undef DEF3 diff --git a/lib/Runtime/Language/AsmJsJitTemplate.h b/lib/Runtime/Language/AsmJsJitTemplate.h index a637808abfb..35b7b65b8ce 100644 --- a/lib/Runtime/Language/AsmJsJitTemplate.h +++ b/lib/Runtime/Language/AsmJsJitTemplate.h @@ -167,9 +167,6 @@ namespace Js CreateTemplate( I_ArgOut_Db, int argIndex, int offset); CreateTemplate( I_ArgOut_Flt, int argIndex, int offset); CreateTemplate( I_Call, int targetOffset, int funcOffset, int nbArgs, AsmJsRetType retType); - CreateTemplate( I_Conv_VTI, int targetOffset, int srcOffset); - CreateTemplate( I_Conv_VTD, int targetOffset, int srcOffset); - CreateTemplate( I_Conv_VTF, int targetOffset, int srcOffset); CreateTemplate( LdArr, int targetOffset, int slotVarIndex, ArrayBufferView::ViewType viewType); CreateTemplate( LdArrDb, int targetOffset, int slotVarIndex, ArrayBufferView::ViewType viewType); @@ -307,10 +304,6 @@ namespace Js CreateTemplate(Simd128_I_ArgOut_F4, int argIndex, int offset); CreateTemplate(Simd128_I_ArgOut_I4, int argIndex, int offset); CreateTemplate(Simd128_I_ArgOut_D2, int argIndex, int offset); - - CreateTemplate(Simd128_I_Conv_VTF4, int targetOffset, int srcOffset); - CreateTemplate(Simd128_I_Conv_VTI4, int targetOffset, int srcOffset); - CreateTemplate(Simd128_I_Conv_VTD2, int targetOffset, int srcOffset); }; }; diff --git a/lib/Runtime/Language/AsmJsModule.h b/lib/Runtime/Language/AsmJsModule.h index 7062a681af7..0d8c04f7abd 100644 --- a/lib/Runtime/Language/AsmJsModule.h +++ b/lib/Runtime/Language/AsmJsModule.h @@ -101,10 +101,7 @@ namespace Js { { // Register where module slots are loaded ModuleSlotRegister = 0, - ReturnRegister = 0, - FunctionRegister = 0, - CallReturnRegister = 0, // These are created from the const table which starts after the FirstRegSlot ModuleEnvRegister = FunctionBody::FirstRegSlot, ArrayBufferRegister, diff --git a/lib/Runtime/Language/AsmJsTypes.cpp b/lib/Runtime/Language/AsmJsTypes.cpp index d3532bb404e..ace53488885 100644 --- a/lib/Runtime/Language/AsmJsTypes.cpp +++ b/lib/Runtime/Language/AsmJsTypes.cpp @@ -817,6 +817,9 @@ namespace Js case WAsmJs::FLOAT32: return Anew(alloc, AsmJsRegisterSpace, alloc); case WAsmJs::FLOAT64: return Anew(alloc, AsmJsRegisterSpace, alloc); case WAsmJs::SIMD: return Anew(alloc, AsmJsRegisterSpace, alloc); +#if TARGET_64 + case WAsmJs::INT64: return Anew(alloc, AsmJsRegisterSpace, alloc); +#endif default: AssertMsg(false, "Invalid native asm.js type"); Js::Throw::InternalError(); @@ -829,20 +832,24 @@ namespace Js , mVarMap(allocator) , mBodyNode(nullptr) , mFncNode(pnodeFnc) + , mCurrentProfileId(0) , mTypedRegisterAllocator( allocator, AllocateRegisterSpace, - // Exclude int64 and simd if not enabled - 1 << WAsmJs::INT64 | ( +#if TARGET_32 + 1 << WAsmJs::INT64 | +#endif + // Exclude simd if not enabled + ( #ifdef ENABLE_SIMDJS scriptContext->GetConfig()->IsSimdjsEnabled() ? 0 : #endif - 1 << WAsmJs::SIMD) + 1 << WAsmJs::SIMD + ) ) , mFuncInfo(pnodeFnc->sxFnc.funcInfo) , mFuncBody(nullptr) , mSimdVarsList(allocator) - , mArgOutDepth(0) , mMaxArgOutDepth(0) , mDefined( false ) { @@ -880,6 +887,12 @@ namespace Js return var; } + ProfileId AsmJsFunc::GetNextProfileId() + { + ProfileId nextProfileId = mCurrentProfileId; + UInt16Math::Inc(mCurrentProfileId); + return nextProfileId; + } AsmJsVarBase* AsmJsFunc::FindVar(const PropertyName name) const { @@ -919,11 +932,6 @@ namespace Js return var; } - void AsmJsFunc::SetArgOutDepth( int outParamsCount ) - { - mArgOutDepth = outParamsCount; - } - void AsmJsFunc::UpdateMaxArgOutDepth(int outParamsCount) { if (mMaxArgOutDepth < outParamsCount) diff --git a/lib/Runtime/Language/AsmJsTypes.h b/lib/Runtime/Language/AsmJsTypes.h index 2f8ea499e77..21b359787a1 100644 --- a/lib/Runtime/Language/AsmJsTypes.h +++ b/lib/Runtime/Language/AsmJsTypes.h @@ -743,9 +743,9 @@ namespace Js FuncInfo* mFuncInfo; FunctionBody* mFuncBody; - int mArgOutDepth; int mMaxArgOutDepth; ULONG mOrigParseFlags; + ProfileId mCurrentProfileId; bool mDeferred; bool mDefined : 1; // true when compiled completely without any errors public: @@ -753,7 +753,8 @@ namespace Js unsigned GetCompileTime() const { return mCompileTime; } void AccumulateCompileTime(unsigned ms) { mCompileTime += ms; } - + ProfileId GetNextProfileId(); + ProfileId GetProfileIdCount() const { return mCurrentProfileId; } inline ParseNode* GetFncNode() const{ return mFncNode; } inline void SetFncNode(ParseNode* fncNode) { mFncNode = fncNode; } inline FuncInfo* GetFuncInfo() const{ return mFuncInfo; } @@ -787,15 +788,14 @@ namespace Js template inline bool IsVarLocation ( const EmitExpressionInfo* pnode ){return GetRegisterSpace().IsVarLocation( pnode );} template inline bool IsValidLocation ( const EmitExpressionInfo* pnode ){return GetRegisterSpace().IsValidLocation( pnode );} void ReleaseLocationGeneric( const EmitExpressionInfo* pnode ); + RegSlot AcquireTmpRegisterGeneric(AsmJsRetType retType); // Search for a var in the varMap of the function, return nullptr if not found AsmJsVarBase* FindVar( const PropertyName name ) const; // Defines a new variable int the function, return nullptr if already exists or theres an error AsmJsVarBase* DefineVar(PropertyName name, bool isArg = false, bool isMutable = true); AsmJsSymbol* LookupIdentifier( const PropertyName name, AsmJsLookupSource::Source* lookupSource = nullptr ) const; - void SetArgOutDepth(int outParamsCount); void UpdateMaxArgOutDepth(int outParamsCount); - inline int GetArgOutDepth() const{ return mArgOutDepth; } inline int GetMaxArgOutDepth() const{ return mMaxArgOutDepth; } void CommitToFunctionInfo(Js::AsmJsFunctionInfo* funcInfo, FunctionBody* body) {mTypedRegisterAllocator.CommitToFunctionInfo(funcInfo, body);} void CommitToFunctionBody(FunctionBody* body) { mTypedRegisterAllocator.CommitToFunctionBody(body); } diff --git a/lib/Runtime/Language/DynamicProfileInfo.cpp b/lib/Runtime/Language/DynamicProfileInfo.cpp index 56c7dee5225..dd01e18ff90 100644 --- a/lib/Runtime/Language/DynamicProfileInfo.cpp +++ b/lib/Runtime/Language/DynamicProfileInfo.cpp @@ -409,6 +409,94 @@ namespace Js return callSiteInfo[callSiteId].isArgConstant; } +#ifdef ASMJS_PLAT + void DynamicProfileInfo::RecordAsmJsCallSiteInfo(FunctionBody* callerBody, ProfileId callSiteId, FunctionBody* calleeBody) + { + if (!callerBody || !callerBody->GetIsAsmjsMode() || !calleeBody || !calleeBody->GetIsAsmjsMode()) + { + AssertMsg(UNREACHED, "Call to RecordAsmJsCallSiteInfo without 2 wasm function body"); + return; + } + +#if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION) + // If we persistsAcrossScriptContext, the dynamic profile info may be referred to by multiple function body from + // different script context + Assert(!DynamicProfileInfo::NeedProfileInfoList() || this->persistsAcrossScriptContexts || this->functionBody == callerBody); +#endif + + bool doInline = true; + // This is a hard limit as we only use 4 bits to encode the actual count in the InlineeCallInfo + if (calleeBody->GetAsmJsFunctionInfo()->GetArgCount() > Js::InlineeCallInfo::MaxInlineeArgoutCount) + { + doInline = false; + } + + // Mark the callsite bit where caller and callee is same function + if (calleeBody == callerBody && callSiteId < 32) + { + this->m_recursiveInlineInfo = this->m_recursiveInlineInfo | (1 << callSiteId); + } + + // TODO: support polymorphic inlining in wasm + Assert(!callSiteInfo[callSiteId].isPolymorphic); + Js::SourceId oldSourceId = callSiteInfo[callSiteId].u.functionData.sourceId; + if (oldSourceId == InvalidSourceId) + { + return; + } + + Js::LocalFunctionId oldFunctionId = callSiteInfo[callSiteId].u.functionData.functionId; + + Js::SourceId sourceId = InvalidSourceId; + Js::LocalFunctionId functionId; + // We can only inline function that are from the same script context + if (callerBody->GetScriptContext() == calleeBody->GetScriptContext()) + { + if (callerBody->GetSecondaryHostSourceContext() == calleeBody->GetSecondaryHostSourceContext()) + { + if (callerBody->GetHostSourceContext() == calleeBody->GetHostSourceContext()) + { + sourceId = CurrentSourceId; // Caller and callee in same file + } + else + { + sourceId = (Js::SourceId)calleeBody->GetHostSourceContext(); // Caller and callee in different files + } + functionId = calleeBody->GetLocalFunctionId(); + } + else + { + // Pretend that we are cross context when call is crossing script file. + functionId = CallSiteCrossContext; + } + } + else + { + functionId = CallSiteCrossContext; + } + + if (oldSourceId == NoSourceId) + { + callSiteInfo[callSiteId].u.functionData.sourceId = sourceId; + callSiteInfo[callSiteId].u.functionData.functionId = functionId; + this->currentInlinerVersion++; // we don't mind if this overflows + } + else if (oldSourceId != sourceId || oldFunctionId != functionId) + { + if (oldFunctionId != CallSiteMixed) + { + this->currentInlinerVersion++; // we don't mind if this overflows + } + + callSiteInfo[callSiteId].u.functionData.functionId = CallSiteMixed; + doInline = false; + } + callSiteInfo[callSiteId].isConstructorCall = false; + callSiteInfo[callSiteId].dontInline = !doInline; + callSiteInfo[callSiteId].ldFldInlineCacheId = Js::Constants::NoInlineCacheIndex; + } +#endif + void DynamicProfileInfo::RecordCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId, FunctionInfo* calleeFunctionInfo, JavascriptFunction* calleeFunction, ArgSlot actualArgCount, bool isConstructorCall, InlineCacheIndex ldFldInlineCacheId) { #if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION) diff --git a/lib/Runtime/Language/DynamicProfileInfo.h b/lib/Runtime/Language/DynamicProfileInfo.h index 1f9705507a5..cbc1a5fecb6 100644 --- a/lib/Runtime/Language/DynamicProfileInfo.h +++ b/lib/Runtime/Language/DynamicProfileInfo.h @@ -386,6 +386,9 @@ namespace Js ValueType GetSwitchType(FunctionBody* body, ProfileId switchId) const; ValueType * GetSwitchTypeInfo() const { return switchTypeInfo; } +#ifdef ASMJS_PLAT + void RecordAsmJsCallSiteInfo(FunctionBody* callerBody, ProfileId callSiteId, FunctionBody* calleeBody); +#endif void RecordCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId, FunctionInfo * calleeFunctionInfo, JavascriptFunction* calleeFunction, ArgSlot actualArgCount, bool isConstructorCall, InlineCacheIndex ldFldInlineCacheId = Js::Constants::NoInlineCacheIndex); void RecordConstParameterAtCallSite(ProfileId callSiteId, int argNum); static bool HasCallSiteInfo(FunctionBody* functionBody); diff --git a/lib/Runtime/Language/InterpreterHandlerAsmJs.inl b/lib/Runtime/Language/InterpreterHandlerAsmJs.inl index 44f7a62a0f6..45badce1865 100755 --- a/lib/Runtime/Language/InterpreterHandlerAsmJs.inl +++ b/lib/Runtime/Language/InterpreterHandlerAsmJs.inl @@ -15,15 +15,15 @@ EXDEF2 (NOPASMJS , InvalidOpCode, Empty DEF2 ( NOPASMJS , Label , Empty ) DEF2 ( BR_ASM , AsmBr , OP_Br ) - DEF2_WMS( FALLTHROUGH_ASM , LdSlotArr , /* Common case with LdSlot */ ) - DEF3_WMS( GET_ELEM_SLOT_ASM, LdSlot , OP_LdAsmJsSlot , ElementSlot ) - DEF2_WMS( FUNCtoA1Mem , LdUndef , JavascriptOperators::OP_LdUndef ) + DEF2_WMS( FALLTHROUGH_ASM , LdSlotArr , /* Common case with LdSlot */ ) + DEF3_WMS( CUSTOM_ASMJS , LdSlot , OP_LdAsmJsSlot , ElementSlot ) // Function Calls DEF2 ( FALLTHROUGH_ASM , I_StartCall, /* Common case with StartCall */ ) DEF3 ( CUSTOM_ASMJS , StartCall , OP_AsmStartCall , StartCall ) DEF3_WMS( CUSTOM_ASMJS , I_Call , OP_I_AsmCall , AsmCall ) + DEF3_WMS( CUSTOM_ASMJS , ProfiledI_Call, OP_ProfiledI_AsmCall , ProfiledAsmCall ) DEF3_WMS( CUSTOM_ASMJS , Call , OP_AsmCall , AsmCall ) DEF2_WMS( D1toR1Out , I_ArgOut_Db , OP_I_SetOutAsmDb ) // set double as internal outparam DEF2_WMS( D1toR1Out , ArgOut_Db , OP_SetOutAsmDb ) // convert double to var and set it as outparam @@ -32,14 +32,11 @@ EXDEF2 (NOPASMJS , InvalidOpCode, Empty DEF2_WMS( F1toR1Out , I_ArgOut_Flt , OP_I_SetOutAsmFlt ) // set float as internal outparam DEF2_WMS( F1toR1Out , ArgOut_Flt , OP_SetOutAsmFlt ) // convert float to var and set as outparam DEF2_WMS( L1toR1Out , I_ArgOut_Long, OP_I_SetOutAsmLong ) // set int64 as internal outparam - DEF2_WMS( D1toD1Mem , I_Conv_VTD , (double) ) DEF2_WMS( R1toD1Mem , Conv_VTD , JavascriptConversion::ToNumber ) // convert var to double - DEF2_WMS( F1toF1Mem , I_Conv_VTF , (float) ) DEF2_WMS( R1toF1Mem , Conv_VTF , JavascriptConversion::ToNumber ) // convert var to float - DEF2_WMS( I1toI1Mem , I_Conv_VTI , (int) ) DEF2_WMS( R1toI1Mem , Conv_VTI , JavascriptMath::ToInt32 ) // convert var to int - DEF3_WMS( CUSTOM_ASMJS , ArgOut_Long , OP_InvalidWasmTypeConversion , Reg1Long1 ) // convert int64 to Var DEF3_WMS( CUSTOM_ASMJS , Conv_VTL , OP_InvalidWasmTypeConversion , Long1Reg1 ) // convert var to int64 + DEF3_WMS( CUSTOM_ASMJS , ArgOut_Long , OP_InvalidWasmTypeConversion , Reg1Long1 ) // convert int64 to Var DEF3_WMS( CUSTOM_ASMJS , LdArr_Func , OP_LdArrFunc , ElementSlot ) DEF3_WMS( CUSTOM_ASMJS , LdArr_WasmFunc,OP_LdArrWasmFunc , ElementSlot ) @@ -392,9 +389,6 @@ EXDEF2_WMS( SIMD_B16_1U16_2toU16_1 , Simd128_Select_U16 , Js::SIMDInt32x4Oper EXDEF2_WMS ( SIMD_F4_1toR1Mem , Simd128_I_ArgOut_F4 , OP_I_SetOutAsmSimd ) DEF2_WMS ( SIMD_I4_1toR1Mem , Simd128_I_ArgOut_I4 , OP_I_SetOutAsmSimd ) -EXDEF2_WMS ( SIMD_F4_1toF4_1 , Simd128_I_Conv_VTF4 , (AsmJsSIMDValue) ) - DEF2_WMS ( SIMD_I4_1toI4_1 , Simd128_I_Conv_VTI4 , (AsmJsSIMDValue) ) - EXDEF2_WMS ( SIMD_F4_1I4toF4_1 , Simd128_Swizzle_F4 , SIMDUtils::SIMD128InnerShuffle ) EXDEF2_WMS ( SIMD_F4_2I4toF4_1 , Simd128_Shuffle_F4 , SIMDUtils::SIMD128InnerShuffle ) @@ -451,7 +445,6 @@ EXDEF2_WMS ( SIMD_I16_2toI16_1 , Simd128_Xor_I16 , Js::SIMDInt32x4Ope EXDEF2_WMS ( SIMD_I16_1toI16_1 , Simd128_Return_I16 , (AsmJsSIMDValue) ) EXDEF2_WMS ( SIMD_I16_1toR1Mem , Simd128_I_ArgOut_I16 , OP_I_SetOutAsmSimd ) -EXDEF2_WMS ( SIMD_I16_1toI16_1 , Simd128_I_Conv_VTI16 , (AsmJsSIMDValue) ) //Lane Access EXDEF2_WMS ( SIMD_I16_1I1toI1 , Simd128_ExtractLane_I16 , SIMDUtils::SIMD128InnerExtractLaneI16 ) @@ -513,14 +506,6 @@ EXDEF2_WMS ( SIMD_B4_1toR1Mem , Simd128_I_ArgOut_B4 , OP_I_SetOutAsmSimd EXDEF2_WMS ( SIMD_B8_1toR1Mem , Simd128_I_ArgOut_B8 , OP_I_SetOutAsmSimd ) EXDEF2_WMS ( SIMD_B16_1toR1Mem , Simd128_I_ArgOut_B16 , OP_I_SetOutAsmSimd ) -EXDEF2_WMS ( SIMD_I8_1toI8_1 , Simd128_I_Conv_VTI8 , (AsmJsSIMDValue) ) -EXDEF2_WMS ( SIMD_U4_1toU4_1 , Simd128_I_Conv_VTU4 , (AsmJsSIMDValue) ) -EXDEF2_WMS ( SIMD_U8_1toU8_1 , Simd128_I_Conv_VTU8 , (AsmJsSIMDValue) ) -EXDEF2_WMS ( SIMD_U16_1toU16_1 , Simd128_I_Conv_VTU16 , (AsmJsSIMDValue) ) -EXDEF2_WMS ( SIMD_B4_1toB4_1 , Simd128_I_Conv_VTB4 , (AsmJsSIMDValue) ) -EXDEF2_WMS ( SIMD_B8_1toB8_1 , Simd128_I_Conv_VTB8 , (AsmJsSIMDValue) ) -EXDEF2_WMS ( SIMD_B16_1toB16_1 , Simd128_I_Conv_VTB16 , (AsmJsSIMDValue) ) - EXDEF2_WMS ( SIMD_I8_1toI8_1 , Simd128_Return_I8 , (AsmJsSIMDValue) ) EXDEF2_WMS ( SIMD_U4_1toU4_1 , Simd128_Return_U4 , (AsmJsSIMDValue) ) EXDEF2_WMS ( SIMD_U8_1toU8_1 , Simd128_Return_U8 , (AsmJsSIMDValue) ) @@ -727,7 +712,6 @@ EXDEF2_WMS(SIMD_I4_1D2_2toD2_1, Simd128_Select_D2, Js::SIMDFloat64x2Operation::O EXDEF2_WMS(SIMD_D2_3toD2_1, Simd128_Clamp_D2, Js::SIMDFloat64x2Operation::OpClamp) EXDEF2_WMS(SIMD_D2_1toI1, Simd128_LdSignMask_D2, Js::SIMDFloat64x2Operation::OpGetSignMask) EXDEF2_WMS(SIMD_D2_1toR1Mem, Simd128_I_ArgOut_D2, OP_I_SetOutAsmSimd) -EXDEF2_WMS(SIMD_D2_1toD2_1, Simd128_I_Conv_VTD2, (AsmJsSIMDValue)) EXDEF2_WMS(SIMD_D2_1toD2_1, Simd128_Ld_D2, (AsmJsSIMDValue) EXDEF2_WMS(SIMD_D2_1I2toD2_1, Simd128_Swizzle_D2, SIMD128InnerShuffle) EXDE2_WMS(SIMD_D2_2I2toD2_1, Simd128_Shuffle_D2, SIMD128InnerShuffle) diff --git a/lib/Runtime/Language/InterpreterProcessOpCodeAsmJs.h b/lib/Runtime/Language/InterpreterProcessOpCodeAsmJs.h index dd4a7942b34..188c7302131 100755 --- a/lib/Runtime/Language/InterpreterProcessOpCodeAsmJs.h +++ b/lib/Runtime/Language/InterpreterProcessOpCodeAsmJs.h @@ -31,19 +31,6 @@ break; \ } - -#define PROCESS_GET_ELEM_SLOT_ASM_COMMON(name, func, layout, suffix) \ - case OpCodeAsmJs::name: \ - { \ - PROCESS_READ_LAYOUT_ASMJS(name, layout, suffix); \ - SetNonVarReg(playout->Value, \ - func(GetNonVarReg(playout->Instance), playout)); \ - break; \ - } - -#define PROCESS_GET_ELEM_SLOT_ASM(name, func, layout) PROCESS_GET_ELEM_SLOT_ASM_COMMON(name, func, layout,) - - #define PROCESS_FUNCtoA1Mem_COMMON(name, func, suffix) \ case OpCodeAsmJs::name: \ { \ @@ -387,7 +374,7 @@ if (switchProfileMode) \ { \ PROCESS_READ_LAYOUT_ASMJS(name, Double1Reg1, suffix); \ SetRegRawDouble(playout->D0, \ - func(GetReg(playout->R1),scriptContext)); \ + func(GetRegRawPtr(playout->R1),scriptContext)); \ break; \ } @@ -398,7 +385,7 @@ if (switchProfileMode) \ { \ PROCESS_READ_LAYOUT_ASMJS(name, Float1Reg1, suffix); \ SetRegRawFloat(playout->F0, \ - (float)func(GetReg(playout->R1), scriptContext)); \ + (float)func(GetRegRawPtr(playout->R1), scriptContext)); \ break; \ } @@ -410,7 +397,7 @@ if (switchProfileMode) \ { \ PROCESS_READ_LAYOUT_ASMJS(name, Int1Reg1, suffix); \ SetRegRawInt(playout->I0, \ - func(GetReg(playout->R1),scriptContext)); \ + func(GetRegRawPtr(playout->R1),scriptContext)); \ break; \ } @@ -421,7 +408,7 @@ if (switchProfileMode) \ { \ PROCESS_READ_LAYOUT_ASMJS(name, Long1Reg1, suffix); \ SetRegRawInt64(playout->L0, \ - func(GetReg(playout->R1),scriptContext)); \ + func(GetRegRawPtr(playout->R1),scriptContext)); \ break; \ } diff --git a/lib/Runtime/Language/InterpreterStackFrame.cpp b/lib/Runtime/Language/InterpreterStackFrame.cpp index 29107d78df8..ca4822c2c0d 100644 --- a/lib/Runtime/Language/InterpreterStackFrame.cpp +++ b/lib/Runtime/Language/InterpreterStackFrame.cpp @@ -3241,10 +3241,6 @@ namespace Js Output::Flush(); } #endif - if( info->GetReturnType() == AsmJsRetType::Void ) - { - m_localSlots[0] = JavascriptOperators::OP_LdUndef( scriptContext ); - } } #endif @@ -3766,30 +3762,46 @@ namespace Js } #ifdef ASMJS_PLAT -#if _M_X64 || _M_IX86 - void InterpreterStackFrame::OP_CallAsmInternal(RecyclableObject * function) + void InterpreterStackFrame::OP_ProfiledCallAsmInternal(RegSlot funcReg, RegSlot returnReg, ProfileId profileId) + { + Var target = GetRegRawPtr(funcReg); + ScriptFunction* function = (ScriptFunction*)OP_CallGetFunc(target); + + Js::FunctionBody * body = function->GetFunctionBody(); + DynamicProfileInfo * dynamicProfileInfo = GetFunctionBody()->GetDynamicProfileInfo(); + dynamicProfileInfo->RecordAsmJsCallSiteInfo(GetFunctionBody(), profileId, body); + return OP_CallAsmInternalCommon(function, returnReg); + } + void InterpreterStackFrame::OP_CallAsmInternal(RegSlot funcReg, RegSlot returnReg) { - AsmJsFunctionInfo* asmInfo = ((ScriptFunction*)function)->GetFunctionBody()->GetAsmJsFunctionInfo(); + Var target = GetRegRawPtr(funcReg); + ScriptFunction* function = (ScriptFunction*)OP_CallGetFunc(target); + return OP_CallAsmInternalCommon(function, returnReg); + } + void InterpreterStackFrame::OP_CallAsmInternalCommon(ScriptFunction* function, RegSlot returnReg) + { + AsmJsFunctionInfo* asmInfo = function->GetFunctionBody()->GetAsmJsFunctionInfo(); uint argsSize = asmInfo->GetArgByteSize(); - ScriptFunction* scriptFunc = (ScriptFunction*)function; ScriptContext * scriptContext = function->GetScriptContext(); PROBE_STACK_CALL(scriptContext, function, argsSize); - Js::FunctionEntryPointInfo* entrypointInfo = (Js::FunctionEntryPointInfo*)scriptFunc->GetEntryPointInfo(); + Js::FunctionEntryPointInfo* entrypointInfo = (Js::FunctionEntryPointInfo*)function->GetEntryPointInfo(); switch (asmInfo->GetReturnType().which()) { case AsmJsRetType::Void: + JavascriptFunction::CallAsmJsFunction(function, entrypointInfo->jsMethod, asmInfo->GetArgCount(), m_outParams); + break; case AsmJsRetType::Signed: - m_localIntSlots[0] = JavascriptFunction::CallAsmJsFunction(function, entrypointInfo->jsMethod, asmInfo->GetArgCount(), m_outParams); + m_localIntSlots[returnReg] = JavascriptFunction::CallAsmJsFunction(function, entrypointInfo->jsMethod, asmInfo->GetArgCount(), m_outParams); break; case AsmJsRetType::Int64: - m_localInt64Slots[0] = JavascriptFunction::CallAsmJsFunction(function, entrypointInfo->jsMethod, asmInfo->GetArgCount(), m_outParams); + m_localInt64Slots[returnReg] = JavascriptFunction::CallAsmJsFunction(function, entrypointInfo->jsMethod, asmInfo->GetArgCount(), m_outParams); break; case AsmJsRetType::Double: - m_localDoubleSlots[0] = JavascriptFunction::CallAsmJsFunction(function, entrypointInfo->jsMethod, asmInfo->GetArgCount(), m_outParams); + m_localDoubleSlots[returnReg] = JavascriptFunction::CallAsmJsFunction(function, entrypointInfo->jsMethod, asmInfo->GetArgCount(), m_outParams); break; case AsmJsRetType::Float: - m_localFloatSlots[0] = JavascriptFunction::CallAsmJsFunction(function, entrypointInfo->jsMethod, asmInfo->GetArgCount(), m_outParams); + m_localFloatSlots[returnReg] = JavascriptFunction::CallAsmJsFunction(function, entrypointInfo->jsMethod, asmInfo->GetArgCount(), m_outParams); break; #ifdef ENABLE_SIMDJS case AsmJsRetType::Float32x4: @@ -3806,9 +3818,9 @@ namespace Js #if _M_X64 X86SIMDValue simdVal; simdVal.m128_value = JavascriptFunction::CallAsmJsFunction<__m128>(function, entrypointInfo->jsMethod, asmInfo->GetArgCount(), m_outParams); - m_localSimdSlots[0] = X86SIMDValue::ToSIMDValue(simdVal); + m_localSimdSlots[returnReg] = X86SIMDValue::ToSIMDValue(simdVal); #else - m_localSimdSlots[0] = JavascriptFunction::CallAsmJsFunction(function, entrypointInfo->jsMethod, asmInfo->GetArgCount(), m_outParams); + m_localSimdSlots[returnReg] = JavascriptFunction::CallAsmJsFunction(function, entrypointInfo->jsMethod, asmInfo->GetArgCount(), m_outParams); #endif break; #endif @@ -3839,18 +3851,35 @@ namespace Js #endif Assert(function); } -#else - void InterpreterStackFrame::OP_CallAsmInternal(RecyclableObject * function) - { - __debugbreak(); - } -#endif #endif template void InterpreterStackFrame::OP_AsmCall(const unaligned T* playout) { - OP_CallCommon(playout, OP_CallGetFunc(GetRegAllowStackVar(playout->Function)), CallFlags_None); + Var target = GetRegRawPtr(playout->Function); + + RecyclableObject * function = OP_CallGetFunc(target); + +#if DBG + if (this->IsInDebugMode()) + { + JavascriptFunction::CheckValidDebugThunk(scriptContext, function); + } +#endif + + if (playout->Return == Js::Constants::NoRegister) + { + Arguments args(CallInfo(CallFlags_NotUsed, playout->ArgCount), m_outParams); + JavascriptFunction::CallFunction(function, function->GetEntryPoint(), args); + } + else + { + Arguments args(CallInfo(CallFlags_Value, playout->ArgCount), m_outParams); + Var retVal = JavascriptFunction::CallFunction(function, function->GetEntryPoint(), args); + SetRegRawPtr(playout->Return, retVal); + } + + PopOut(playout->ArgCount); AsmJsModuleInfo::EnsureHeapAttached(this->function); } @@ -6057,11 +6086,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) } else { - // we do not support this in asmjs, need to add support in IrBuilderAsmjs if we need this support for asmjs - if (!entryPointInfo->GetIsAsmJSFunction()) - { - this->CheckIfLoopIsHot(loopHeader->profiledLoopCounter); - } + this->CheckIfLoopIsHot(loopHeader->profiledLoopCounter); if (newOffset >= loopHeader->endOffset) { @@ -7832,6 +7857,26 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) return m_localInt64Slots[localRegisterID]; } + template + void* InterpreterStackFrame::GetRegRawPtr(RegSlotType localRegisterID) const + { +#if TARGET_32 + return (void*)m_localIntSlots[localRegisterID]; +#elif TARGET_64 + return (void*)m_localInt64Slots[localRegisterID]; +#endif + } + + template + void InterpreterStackFrame::SetRegRawPtr(RegSlotType localRegisterID, void* val) + { +#if TARGET_32 + m_localIntSlots[localRegisterID] = (int32)val; +#elif TARGET_64 + m_localInt64Slots[localRegisterID] = (int64)val; +#endif + } + template void InterpreterStackFrame::SetRegRawInt64(RegSlotType localRegisterID, int64 bValue) { @@ -8538,16 +8583,16 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) template void InterpreterStackFrame::OP_LdArrFunc(const unaligned T* playout) { - Var* arr = (Var*)GetNonVarReg(playout->Instance); + Var* arr = (Var*)GetRegRawPtr(playout->Instance); const uint32 index = (uint32)GetRegRawInt(playout->SlotIndex); - m_localSlots[playout->Value] = arr[index]; + SetRegRawPtr(playout->Value, arr[index]); } template void InterpreterStackFrame::OP_LdArrWasmFunc(const unaligned T* playout) { #ifdef ENABLE_WASM - WebAssemblyTable * table = WebAssemblyTable::FromVar(GetNonVarReg(playout->Instance)); + WebAssemblyTable * table = WebAssemblyTable::FromVar(GetRegRawPtr(playout->Instance)); const uint32 index = (uint32)GetRegRawInt(playout->SlotIndex); if (index >= table->GetCurrentLength()) { @@ -8558,7 +8603,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) { JavascriptError::ThrowWebAssemblyRuntimeError(GetScriptContext(), WASMERR_NeedWebAssemblyFunc); } - m_localSlots[playout->Value] = func; + SetRegRawPtr(playout->Value, func); #endif } @@ -8566,7 +8611,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) void InterpreterStackFrame::OP_CheckSignature(const unaligned T* playout) { #ifdef ENABLE_WASM - ScriptFunction * func = ScriptFunction::FromVar(GetNonVarReg(playout->R0)); + ScriptFunction * func = ScriptFunction::FromVar(GetRegRawPtr(playout->R0)); int sigIndex = playout->C1; Wasm::WasmSignature * expected = &m_signatures[sigIndex]; if (func->GetFunctionInfo()->IsDeferredParseFunction()) @@ -8608,9 +8653,10 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) } template - Var InterpreterStackFrame::OP_LdAsmJsSlot(Var instance, const unaligned T* playout) + void InterpreterStackFrame::OP_LdAsmJsSlot(const unaligned T* playout) { - return ((Var*)instance)[playout->SlotIndex]; + Var * slotArray = (Var*)GetNonVarReg(playout->Instance); + SetRegRawPtr(playout->Value, slotArray[playout->SlotIndex]); } template diff --git a/lib/Runtime/Language/InterpreterStackFrame.h b/lib/Runtime/Language/InterpreterStackFrame.h index 0af3935e388..7e673f5bca5 100644 --- a/lib/Runtime/Language/InterpreterStackFrame.h +++ b/lib/Runtime/Language/InterpreterStackFrame.h @@ -231,6 +231,8 @@ namespace Js template void SetRegRawInt( RegSlotType localRegisterID, int bValue ); template int64 GetRegRawInt64( RegSlotType localRegisterID ) const; template void SetRegRawInt64( RegSlotType localRegisterID, int64 bValue ); + template void* GetRegRawPtr(RegSlotType localRegisterID) const; + template void SetRegRawPtr(RegSlotType localRegisterID, void* val); template double VECTORCALL GetRegRawDouble(RegSlotType localRegisterID) const; template float VECTORCALL GetRegRawFloat(RegSlotType localRegisterID) const; template void SetRegRawDouble(RegSlotType localRegisterID, double bValue); @@ -455,8 +457,11 @@ namespace Js void OP_StartCall( const unaligned OpLayoutStartCall * playout ); void OP_StartCall(uint outParamCount); template void OP_CallCommon(const unaligned T *playout, RecyclableObject * aFunc, unsigned flags, const Js::AuxArray *spreadIndices = nullptr); - void OP_CallAsmInternal( RecyclableObject * function); - template void OP_I_AsmCall(const unaligned T* playout) { OP_CallAsmInternal((ScriptFunction*)OP_CallGetFunc(GetRegAllowStackVar(playout->Function))); } + void OP_CallAsmInternalCommon(ScriptFunction* function, RegSlot returnReg); + void OP_CallAsmInternal(RegSlot funcReg, RegSlot returnReg); + template void OP_I_AsmCall(const unaligned T* playout) { OP_CallAsmInternal(playout->Function, playout->Return); } + void OP_ProfiledCallAsmInternal(RegSlot funcReg, RegSlot returnReg, ProfileId profileId); + template void OP_ProfiledI_AsmCall(const unaligned T* playout) { OP_ProfiledCallAsmInternal(playout->Function, playout->Return, playout->profileId); } template void OP_CallCommonI(const unaligned T *playout, RecyclableObject * aFunc, unsigned flags); template void OP_ProfileCallCommon(const unaligned T *playout, RecyclableObject * aFunc, unsigned flags, ProfileId profileId, InlineCacheIndex inlineCacheIndex = Js::Constants::NoInlineCacheIndex, const Js::AuxArray *spreadIndices = nullptr); @@ -649,7 +654,7 @@ namespace Js template inline void OP_CheckSignature(const unaligned T* playout); template T GetArrayViewOverflowVal(); template inline void OP_StArr( uint32 index, RegSlot value ); - template inline Var OP_LdAsmJsSlot(Var instance, const unaligned T* playout ); + template inline void OP_LdAsmJsSlot(const unaligned T* playout ); template inline void OP_StSlotPrimitive(const unaligned T* playout); template inline void OP_LdSlotPrimitive( const unaligned T* playout ); template inline void OP_LdArrGeneric ( const unaligned T* playout ); diff --git a/lib/Runtime/Language/i386/AsmJsJitTemplate.cpp b/lib/Runtime/Language/i386/AsmJsJitTemplate.cpp index 7de298a82ee..4ced850da4c 100644 --- a/lib/Runtime/Language/i386/AsmJsJitTemplate.cpp +++ b/lib/Runtime/Language/i386/AsmJsJitTemplate.cpp @@ -3287,6 +3287,24 @@ namespace Js templateData->InternalCallDone(); size += EncodingHelpers::ReloadArrayBuffer(context, buffer); + + switch (retType.which()) + { + case AsmJsRetType::Signed: + size += EncodingHelpers::SetStackReg(buffer, templateData, targetOffset, RegEAX); + case AsmJsRetType::Double: + size += MOVSD::EncodeInstruction(buffer, InstrParamsAddrReg(RegEBP, targetOffset, RegXMM0)); + templateData->OverwriteStack(targetOffset); + break; + case AsmJsRetType::Float: + size += MOVSS::EncodeInstruction(buffer, InstrParamsAddrReg(RegEBP, targetOffset, RegXMM0)); + templateData->OverwriteStack(targetOffset); + break; + case AsmJsRetType::Void: + break; + default: + Assert(UNREACHED); + } return size; } int AsmJsLoopBody::ApplyTemplate(TemplateContext context, BYTE*& buffer, int loopNumber) @@ -3368,43 +3386,6 @@ namespace Js return size; } - int I_Conv_VTI::ApplyTemplate( TemplateContext context, BYTE*& buffer, int targetOffset, int srcOffset ) - { - X86TemplateData* templateData = GetTemplateData( context ); - int size = 0; - targetOffset -= templateData->GetBaseOffSet(); - srcOffset -= templateData->GetBaseOffSet(); - - size += EncodingHelpers::SetStackReg( buffer, templateData, targetOffset , RegEAX); - - return size; - } - int I_Conv_VTD::ApplyTemplate( TemplateContext context, BYTE*& buffer, int targetOffset, int srcOffset ) - { - X86TemplateData* templateData = GetTemplateData( context ); - int size = 0; - targetOffset -= templateData->GetBaseOffSet(); - srcOffset -= templateData->GetBaseOffSet(); - - size += MOVSD::EncodeInstruction(buffer, InstrParamsAddrReg(RegEBP, targetOffset,RegXMM0)); - templateData->OverwriteStack( targetOffset ); - - return size; - } - - int I_Conv_VTF::ApplyTemplate(TemplateContext context, BYTE*& buffer, int targetOffset, int srcOffset) - { - X86TemplateData* templateData = GetTemplateData(context); - int size = 0; - targetOffset -= templateData->GetBaseOffSet(); - srcOffset -= templateData->GetBaseOffSet(); - - size += MOVSS::EncodeInstruction(buffer, InstrParamsAddrReg(RegEBP, targetOffset, RegXMM0)); - templateData->OverwriteStack(targetOffset); - - return size; - } - int LdUndef::ApplyTemplate( TemplateContext context, BYTE*& buffer, int targetOffset ) { X86TemplateData* templateData = GetTemplateData( context ); @@ -4695,23 +4676,6 @@ namespace Js { return Simd128_I_ArgOut_F4::ApplyTemplate(context, buffer, argIndex, offset); } - - int Simd128_I_Conv_VTF4::ApplyTemplate(TemplateContext context, BYTE*& buffer, int targetOffset, int srcOffset) - { - X86TemplateData* templateData = GetTemplateData(context); - targetOffset -= templateData->GetBaseOffSet(); - srcOffset -= templateData->GetBaseOffSet(); - - return EncodingHelpers::SIMDSetStackReg(buffer, templateData, targetOffset, RegXMM0); - } - int Simd128_I_Conv_VTI4::ApplyTemplate(TemplateContext context, BYTE*& buffer, int targetOffset, int srcOffset) - { - return Simd128_I_Conv_VTF4::ApplyTemplate(context, buffer, targetOffset, srcOffset); - } - int Simd128_I_Conv_VTD2::ApplyTemplate(TemplateContext context, BYTE*& buffer, int targetOffset, int srcOffset) - { - return Simd128_I_Conv_VTF4::ApplyTemplate(context, buffer, targetOffset, srcOffset); - } }; } #endif diff --git a/lib/WasmReader/EmptyWasmByteCodeWriter.h b/lib/WasmReader/EmptyWasmByteCodeWriter.h index e53fe7833eb..a2cf099dfb3 100644 --- a/lib/WasmReader/EmptyWasmByteCodeWriter.h +++ b/lib/WasmReader/EmptyWasmByteCodeWriter.h @@ -35,7 +35,9 @@ namespace Js virtual uint32 EnterLoop(ByteCodeLabel loopEntrance) override {return 0;} virtual void ExitLoop(uint32 loopId) override {} virtual void AsmStartCall(OpCodeAsmJs op, ArgSlot ArgCount, bool isPatching = false) override {} - virtual void AsmCall(OpCodeAsmJs op, RegSlot returnValueRegister, RegSlot functionRegister, ArgSlot givenArgCount, AsmJsRetType retType) override {} + virtual void AsmCall(OpCodeAsmJs op, RegSlot returnValueRegister, RegSlot functionRegister, ArgSlot givenArgCount, AsmJsRetType retType, Js::ProfileId profileId) override {} + + virtual void SetCallSiteCount(Js::ProfileId callSiteCount) override {} }; } #endif \ No newline at end of file diff --git a/lib/WasmReader/WasmBinaryReader.cpp b/lib/WasmReader/WasmBinaryReader.cpp index a7e1bfa42a3..caa6b089a53 100644 --- a/lib/WasmReader/WasmBinaryReader.cpp +++ b/lib/WasmReader/WasmBinaryReader.cpp @@ -31,6 +31,7 @@ uint32 GetTypeByteSize(WasmType type) case I64: return sizeof(int64); case F32: return sizeof(float); case F64: return sizeof(double); + case Ptr: return sizeof(void*); default: Js::Throw::InternalError(); } diff --git a/lib/WasmReader/WasmByteCodeGenerator.cpp b/lib/WasmReader/WasmByteCodeGenerator.cpp index be3f822ba23..89121da920a 100644 --- a/lib/WasmReader/WasmByteCodeGenerator.cpp +++ b/lib/WasmReader/WasmByteCodeGenerator.cpp @@ -255,6 +255,11 @@ Js::WebAssemblyModule* WasmModuleGenerator::GenerateModule() } uint32 funcCount = m_module->GetWasmFunctionCount(); + SourceContextInfo * sourceContextInfo = m_sourceInfo->GetSrcInfo()->sourceContextInfo; + m_sourceInfo->EnsureInitialized(funcCount); + sourceContextInfo->nextLocalFunctionId += funcCount; + sourceContextInfo->EnsureInitialized(); + for (uint32 i = 0; i < funcCount; ++i) { GenerateFunctionHeader(i); @@ -451,6 +456,7 @@ WasmBytecodeGenerator::WasmBytecodeGenerator(Js::ScriptContext* scriptContext, W m_evalStack(&m_alloc), mTypedRegisterAllocator(&m_alloc, AllocateRegisterSpace, 1 << WAsmJs::SIMD), m_blockInfos(&m_alloc), + currentProfileId(0), isUnreachable(false) { m_emptyWriter = Anew(&m_alloc, Js::EmptyWasmByteCodeWriter); @@ -505,6 +511,7 @@ void WasmBytecodeGenerator::GenerateFunction() SetUnreachableState(false); m_writer->MarkAsmJsLabel(exitLabel); m_writer->EmptyAsm(Js::OpCodeAsmJs::Ret); + m_writer->SetCallSiteCount(this->currentProfileId); m_writer->End(); GetReader()->FunctionEnd(); } @@ -530,7 +537,6 @@ void WasmBytecodeGenerator::GenerateFunction() mTypedRegisterAllocator.CommitToFunctionInfo(info, GetFunctionBody()); GetFunctionBody()->CheckAndSetOutParamMaxDepth(m_maxArgOutDepth); - autoDisableInterrupt.Completed(); } @@ -946,20 +952,27 @@ EmitInfo WasmBytecodeGenerator::EmitCall() uint32 funcNum = Js::Constants::UninitializedValue; uint32 signatureId = Js::Constants::UninitializedValue; WasmSignature* calleeSignature = nullptr; + Js::ProfileId profileId = Js::Constants::NoProfileId; EmitInfo indirectIndexInfo; const bool isImportCall = GetReader()->m_currentNode.call.funcType == FunctionIndexTypes::Import; Assert(isImportCall || GetReader()->m_currentNode.call.funcType == FunctionIndexTypes::Function || GetReader()->m_currentNode.call.funcType == FunctionIndexTypes::ImportThunk); switch (wasmOp) { case wbCall: + { funcNum = GetReader()->m_currentNode.call.num; - calleeSignature = m_module->GetWasmFunctionInfo(funcNum)->GetSignature(); + WasmFunctionInfo* calleeInfo = m_module->GetWasmFunctionInfo(funcNum); + calleeSignature = calleeInfo->GetSignature(); + if (!isImportCall) + { + profileId = GetNextProfileId(); + } break; + } case wbCallIndirect: indirectIndexInfo = PopEvalStack(WasmTypes::I32, _u("Indirect call index must be int type")); signatureId = GetReader()->m_currentNode.call.num; calleeSignature = m_module->GetSignature(signatureId); - ReleaseLocation(&indirectIndexInfo); break; default: Assume(UNREACHED); @@ -1001,7 +1014,6 @@ EmitInfo WasmBytecodeGenerator::EmitCall() { EmitInfo info = PopEvalStack(calleeSignature->GetParam((Js::ArgSlot)i), _u("Call argument does not match formal type")); // We can release the location of the arguments now, because we won't create new temps between start/call - ReleaseLocation(&info); argsList[i] = info; } @@ -1054,7 +1066,7 @@ EmitInfo WasmBytecodeGenerator::EmitCall() } } - AdeleteArray(&m_alloc, nArgs, argsList); + Js::RegSlot funcReg = GetRegisterSpace(WasmTypes::Ptr)->AcquireTmpRegister(); // emit call switch (wasmOp) @@ -1063,62 +1075,91 @@ EmitInfo WasmBytecodeGenerator::EmitCall() { uint32 offset = isImportCall ? m_module->GetImportFuncOffset() : m_module->GetFuncOffset(); uint32 index = UInt32Math::Add(offset, funcNum); - m_writer->AsmSlot(Js::OpCodeAsmJs::LdSlot, 0, 1, index); + m_writer->AsmSlot(Js::OpCodeAsmJs::LdSlot, funcReg, Js::AsmJsFunctionMemory::ModuleEnvRegister, index); break; } case wbCallIndirect: - m_writer->AsmSlot(Js::OpCodeAsmJs::LdSlotArr, 0, 1, m_module->GetTableEnvironmentOffset()); - m_writer->AsmSlot(Js::OpCodeAsmJs::LdArr_WasmFunc, 0, 0, indirectIndexInfo.location); - m_writer->AsmReg1IntConst1(Js::OpCodeAsmJs::CheckSignature, 0, calleeSignature->GetSignatureId()); + { + Js::RegSlot slotReg = GetRegisterSpace(WasmTypes::Ptr)->AcquireTmpRegister(); + m_writer->AsmSlot(Js::OpCodeAsmJs::LdSlotArr, slotReg, Js::AsmJsFunctionMemory::ModuleEnvRegister, m_module->GetTableEnvironmentOffset()); + m_writer->AsmSlot(Js::OpCodeAsmJs::LdArr_WasmFunc, funcReg, slotReg, indirectIndexInfo.location); + GetRegisterSpace(WasmTypes::Ptr)->ReleaseTmpRegister(slotReg); + m_writer->AsmReg1IntConst1(Js::OpCodeAsmJs::CheckSignature, funcReg, calleeSignature->GetSignatureId()); break; + } default: Assume(UNREACHED); } + EmitInfo retInfo; + retInfo.type = calleeSignature->GetResultType(); // calculate number of RegSlots(Js::Var) the call consumes Js::ArgSlot args; - Js::OpCodeAsmJs callOp = Js::OpCodeAsmJs::Nop; if (isImportCall) { args = calleeSignature->GetParamCount(); ArgSlotMath::Inc(args, argOverflow); - callOp = Js::OpCodeAsmJs::Call; + + Js::RegSlot varRetReg = GetRegisterSpace(WasmTypes::Ptr)->AcquireTmpRegister(); + m_writer->AsmCall(Js::OpCodeAsmJs::Call, varRetReg, funcReg, args, WasmToAsmJs::GetAsmJsReturnType(calleeSignature->GetResultType()), profileId); + + GetRegisterSpace(WasmTypes::Ptr)->ReleaseTmpRegister(varRetReg); + GetRegisterSpace(WasmTypes::Ptr)->ReleaseTmpRegister(funcReg); + + ReleaseLocation(&indirectIndexInfo); + //registers need to be released from higher ordinals to lower + for (uint32 i = nArgs; i > 0; --i) + { + ReleaseLocation(&(argsList[i - 1])); + } + // emit result coercion + if (retInfo.type != WasmTypes::Void) + { + Js::OpCodeAsmJs convertOp = Js::OpCodeAsmJs::Nop; + switch (retInfo.type) + { + case WasmTypes::F32: + convertOp = Js::OpCodeAsmJs::Conv_VTF; + break; + case WasmTypes::F64: + convertOp = Js::OpCodeAsmJs::Conv_VTD; + break; + case WasmTypes::I32: + convertOp = Js::OpCodeAsmJs::Conv_VTI; + break; + case WasmTypes::I64: + convertOp = Js::OpCodeAsmJs::Conv_VTL; + break; + default: + throw WasmCompilationException(_u("Unknown call return type %u"), retInfo.type); + } + if (retInfo.type != WasmTypes::Void) + { + retInfo.location = GetRegisterSpace(retInfo.type)->AcquireTmpRegister(); + } + m_writer->AsmReg2(convertOp, retInfo.location, varRetReg); + } } else { - Assert(Math::IsAligned(argSize, sizeof(Js::Var))); - args = argSize / sizeof(Js::Var); - callOp = Js::OpCodeAsmJs::I_Call; - } - m_writer->AsmCall(callOp, 0, 0, args, WasmToAsmJs::GetAsmJsReturnType(calleeSignature->GetResultType())); + GetRegisterSpace(WasmTypes::Ptr)->ReleaseTmpRegister(funcReg); - // emit result coercion - EmitInfo retInfo; - retInfo.type = calleeSignature->GetResultType(); - if (retInfo.type != WasmTypes::Void) - { - Js::OpCodeAsmJs convertOp = Js::OpCodeAsmJs::Nop; - retInfo.location = GetRegisterSpace(retInfo.type)->AcquireTmpRegister(); - switch (retInfo.type) + ReleaseLocation(&indirectIndexInfo); + //registers need to be released from higher ordinals to lower + for (uint32 i = nArgs; i > 0; --i) { - case WasmTypes::F32: - convertOp = isImportCall ? Js::OpCodeAsmJs::Conv_VTF : Js::OpCodeAsmJs::Ld_Flt; - break; - case WasmTypes::F64: - convertOp = isImportCall ? Js::OpCodeAsmJs::Conv_VTD : Js::OpCodeAsmJs::Ld_Db; - break; - case WasmTypes::I32: - convertOp = isImportCall ? Js::OpCodeAsmJs::Conv_VTI : Js::OpCodeAsmJs::Ld_Int; - break; - case WasmTypes::I64: - convertOp = isImportCall ? Js::OpCodeAsmJs::Conv_VTL : Js::OpCodeAsmJs::Ld_Long; - break; - default: - throw WasmCompilationException(_u("Unknown call return type %u"), retInfo.type); + ReleaseLocation(&(argsList[i - 1])); + } + if (retInfo.type != WasmTypes::Void) + { + retInfo.location = GetRegisterSpace(retInfo.type)->AcquireTmpRegister(); } - m_writer->AsmReg2(convertOp, retInfo.location, 0); + + args = (Js::ArgSlot)(::ceil((double)(argSize / sizeof(Js::Var)))); + m_writer->AsmCall(Js::OpCodeAsmJs::I_Call, retInfo.location, funcReg, args, WasmToAsmJs::GetAsmJsReturnType(calleeSignature->GetResultType()), profileId); } + AdeleteArray(&m_alloc, nArgs, argsList); // track stack requirements for out params @@ -1321,12 +1362,7 @@ EmitInfo WasmBytecodeGenerator::EmitMemAccess(WasmOp wasmOp, const WasmTypes::Wa void WasmBytecodeGenerator::EmitReturnExpr(EmitInfo* explicitRetInfo) { - if (m_funcInfo->GetResultType() == WasmTypes::Void) - { - // TODO (michhol): consider moving off explicit 0 for return reg - m_writer->AsmReg1(Js::OpCodeAsmJs::LdUndef, 0); - } - else + if (m_funcInfo->GetResultType() != WasmTypes::Void) { EmitInfo retExprInfo = explicitRetInfo ? *explicitRetInfo : PopEvalStack(); if (retExprInfo.type != WasmTypes::Any && m_funcInfo->GetResultType() != retExprInfo.type) @@ -1561,11 +1597,25 @@ Wasm::BlockInfo WasmBytecodeGenerator::GetBlockInfo(uint32 relativeDepth) const return m_blockInfos.Peek(relativeDepth); } +Js::ProfileId +WasmBytecodeGenerator::GetNextProfileId() +{ + Js::ProfileId nextProfileId = this->currentProfileId; + UInt16Math::Inc(this->currentProfileId); + return nextProfileId; +} + WasmRegisterSpace* WasmBytecodeGenerator::GetRegisterSpace(WasmTypes::WasmType type) { switch (type) { +#if TARGET_32 + case WasmTypes::Ptr: +#endif case WasmTypes::I32: return mTypedRegisterAllocator.GetRegisterSpace(WAsmJs::INT32); +#if TARGET_64 + case WasmTypes::Ptr: +#endif case WasmTypes::I64: return mTypedRegisterAllocator.GetRegisterSpace(WAsmJs::INT64); case WasmTypes::F32: return mTypedRegisterAllocator.GetRegisterSpace(WAsmJs::FLOAT32); case WasmTypes::F64: return mTypedRegisterAllocator.GetRegisterSpace(WAsmJs::FLOAT64); diff --git a/lib/WasmReader/WasmByteCodeGenerator.h b/lib/WasmReader/WasmByteCodeGenerator.h index ee11a97474a..ee1da5021a1 100644 --- a/lib/WasmReader/WasmByteCodeGenerator.h +++ b/lib/WasmReader/WasmByteCodeGenerator.h @@ -173,8 +173,6 @@ namespace Wasm static const Js::RegSlot ModuleSlotRegister = 0; static const Js::RegSlot ReturnRegister = 0; - static const Js::RegSlot FunctionRegister = 0; - static const Js::RegSlot CallReturnRegister = 0; static const Js::RegSlot ModuleEnvRegister = 1; static const Js::RegSlot ArrayBufferRegister = 2; static const Js::RegSlot ArraySizeRegister = 3; @@ -252,6 +250,7 @@ namespace Wasm Js::FunctionBody* GetFunctionBody() const { return m_funcInfo->GetBody(); } WasmReaderBase* GetReader() const; + Js::ProfileId GetNextProfileId(); bool IsValidating() const { return m_originalWriter == m_emptyWriter; } ArenaAllocator m_alloc; @@ -271,6 +270,7 @@ namespace Wasm WAsmJs::TypedRegisterAllocator mTypedRegisterAllocator; + Js::ProfileId currentProfileId; JsUtil::Stack m_blockInfos; JsUtil::Stack m_evalStack; }; diff --git a/lib/WasmReader/WasmParseTree.h b/lib/WasmReader/WasmParseTree.h index ab353973580..1bd1969dd90 100644 --- a/lib/WasmReader/WasmParseTree.h +++ b/lib/WasmReader/WasmParseTree.h @@ -18,6 +18,7 @@ namespace Wasm F32 = 3, F64 = 4, Limit, + Ptr, Any }; bool IsLocalType(WasmTypes::WasmType type); diff --git a/test/AsmJs/cseBug.baseline b/test/AsmJs/cseBug.baseline index 292c30ea8e8..8cff939938f 100644 --- a/test/AsmJs/cseBug.baseline +++ b/test/AsmJs/cseBug.baseline @@ -1,6 +1,6 @@ Successfully compiled asm.js code +Successfully compiled asm.js code 258 258 -Successfully compiled asm.js code -1 -1 diff --git a/test/AsmJs/rlexe.xml b/test/AsmJs/rlexe.xml index b96f72c5ca6..fef20cc43a7 100644 --- a/test/AsmJs/rlexe.xml +++ b/test/AsmJs/rlexe.xml @@ -609,7 +609,7 @@ cseBug.js cseBug.baseline - -testtrace:asmjs -forcedeferparse + -testtrace:asmjs -off:deferparse diff --git a/test/wasm/inlining.js b/test/wasm/inlining.js new file mode 100644 index 00000000000..4c3a379da1e --- /dev/null +++ b/test/wasm/inlining.js @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------------------------------- +// 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. +//------------------------------------------------------------------------------------------------------- + +WebAssembly.instantiate(WebAssembly.wabt.convertWast2Wasm(` +(module + (type $t1 (func (result i32))) + (type $t2 (func (param i32) (result i32))) + (type $t3 (func)) + (import "test" "foo" (func $foo (type $t2))) + + (memory (export "memory") 5000 5000) + (global $x (mut i32) (i32.const -12)) + (func $f1 (type $t1) (local i32) + (set_local 0 (i32.const 321)) + (return + (i32.add + (i32.const 2) + (call $f3 (get_local 0)) + ) + ) + ) + (func $f2 (export "a") (type $t2) (local f32) + (if (i32.ge_s (i32.const 26) (i32.const 25)) + (set_local 0 (i32.add (get_local 0) (i32.const 4))) + ) + (set_local 0 (i32.add (get_local 0) (call_indirect $t1 (i32.const 0)))) + (if (i32.ge_s (i32.const 22) (i32.const 25)) + (set_local 0 (i32.add (get_local 0) (i32.const 4))) + (set_local 0 (i32.sub (get_local 0) (i32.const 5))) + ) + (block + (set_local 0 (i32.add (get_local 0) (i32.const 4))) + (set_local 0 (i32.add (get_local 0) (i32.clz (get_local 0)))) + (set_local 0 (i32.add (get_local 0) (call $f1))) + (br_if 0 (select (f32.ne (get_local 1) (get_local 1)) (i32.const 0) (i32.const 1))) + (set_local 0 (i32.add (get_local 0) (i32.const 4))) + ) + (call $foo (get_local 0)) + (i32.store (get_local 0) (i32.add (get_local 0) (i32.const 7))) + (set_local 0 (i32.load (get_local 0))) + (set_local 1 (f32.convert_s/i32 (get_local 0))) + (set_local 1 (f32.add (get_local 1) (get_local 1))) + (set_local 0 (i32.reinterpret/f32 (get_local 1))) + (set_local 0 (i32.add (get_local 0) (call $foo (get_local 0)))) + (return (i32.add (get_local 0) (i32.const 42))) + ) + (func $f3 (type $t2) + (set_global $x (get_local 0)) + (i32.store (get_global $x) + (i32.add + (get_local 0) + (i32.const 456) + ) + ) + (call $f4) + (return (i32.load (get_local 0)) + )) + (func $f4 (type $t3) + (return) + ) + (table anyfunc (elem $f1 $f2)) +)`), {test: {foo(v) {return v + 5;}}}) + .then(({instance: {exports: {a}}}) => { + console.log(a()); + console.log(a()); + }, console.log) + .catch(console.log); + + +