bool AsmJsEncoder::ReadOp() { #if DBG_DUMP int bytecodeoffset = mReader.GetCurrentOffset(); #endif LayoutSize layoutSize; OpCodeAsmJs op = (OpCodeAsmJs)mReader.ReadOp(layoutSize); ip = mReader.GetIP(); #if DBG_DUMP if (PHASE_TRACE(Js::AsmjsEncoderPhase, mFunctionBody)) { Output::Print(_u("%d.%d:Encoding "), this->mFunctionBody->GetSourceContextId(), this->mFunctionBody->GetLocalFunctionId()); AsmJsByteCodeDumper::DumpOp( op, layoutSize, mReader, mFunctionBody ); if( ip != mReader.GetIP() ) { mReader.SetIP( ip ); } Output::Print(_u(" at offset 0x%X (buffer size = 0x%X)\n"), bytecodeoffset, (int)(mPc-mEncodeBuffer)); Output::Flush(); } #endif if( op == OpCodeAsmJs::EndOfBlock ) { Assert(mReader.GetCurrentOffset() == mFunctionBody->GetByteCode()->GetLength()); // last bytecode return false; } switch( layoutSize ) { case Js::SmallLayout: ReadOpTemplate<Js::SmallLayout>( op ); break; case Js::MediumLayout: ReadOpTemplate<Js::MediumLayout>( op ); break; case Js::LargeLayout: ReadOpTemplate<Js::LargeLayout>( op ); break; default: break; } return true; }
void* AsmJsEncoder::Encode( FunctionBody* functionBody ) { Assert( functionBody ); mFunctionBody = functionBody; #if DBG_DUMP AsmJsJitTemplate::Globals::CurrentEncodingFunction = mFunctionBody; #endif AsmJsFunctionInfo* asmInfo = functionBody->GetAsmJsFunctionInfo(); FunctionEntryPointInfo* entryPointInfo = ((FunctionEntryPointInfo*)(functionBody->GetDefaultEntryPointInfo())); // number of var on the stack + ebp + eip mIntOffset = asmInfo->GetIntByteOffset() + GetOffset<Var>(); mDoubleOffset = asmInfo->GetDoubleByteOffset() + GetOffset<Var>(); mFloatOffset = asmInfo->GetFloatByteOffset() + GetOffset<Var>(); mSimdOffset = asmInfo->GetSimdByteOffset() + GetOffset<Var>(); NoRecoverMemoryArenaAllocator localAlloc(_u("BE-AsmJsEncoder"), GetPageAllocator(), Js::Throw::OutOfMemory); mLocalAlloc = &localAlloc; mRelocLabelMap = Anew( mLocalAlloc, RelocLabelMap, mLocalAlloc ); mTemplateData = AsmJsJitTemplate::InitTemplateData(); mEncodeBufferSize = GetEncodeBufferSize(functionBody); mEncodeBuffer = AnewArray((&localAlloc), BYTE, mEncodeBufferSize); mPc = mEncodeBuffer; mReader.Create( functionBody ); ip = mReader.GetIP(); #ifdef ENABLE_DEBUG_CONFIG_OPTIONS if( PHASE_TRACE( Js::AsmjsEncoderPhase, mFunctionBody ) ) { Output::Print( _u("\n\n") ); functionBody->DumpFullFunctionName(); Output::Print( _u("\n StackSize = %d , Offsets: Var = %d, Int = %d, Double = %d\n"), mFunctionBody->GetAsmJsFunctionInfo()->GetTotalSizeinBytes(), GetOffset<Var>(), GetOffset<int>(), GetOffset<double>() ); } #endif AsmJsJitTemplate::FunctionEntry::ApplyTemplate( this, mPc ); while( ReadOp() ){} AsmJsJitTemplate::FunctionExit::ApplyTemplate( this, mPc ); AsmJsJitTemplate::FreeTemplateData( mTemplateData ); #if DBG_DUMP AsmJsJitTemplate::Globals::CurrentEncodingFunction = nullptr; #endif ApplyRelocs(); ptrdiff_t codeSize = mPc - mEncodeBuffer; if( codeSize > 0 ) { Assert( ::Math::FitsInDWord( codeSize ) ); BYTE *buffer; EmitBufferAllocation *allocation = GetCodeGenAllocator()->emitBufferManager.AllocateBuffer( codeSize, &buffer, 0, 0 ); functionBody->GetAsmJsFunctionInfo()->mTJBeginAddress = buffer; if (buffer == nullptr) { Js::Throw::OutOfMemory(); } if (!GetCodeGenAllocator()->emitBufferManager.CommitBuffer(allocation, buffer, codeSize, mEncodeBuffer)) { Js::Throw::OutOfMemory(); } functionBody->GetScriptContext()->GetThreadContext()->SetValidCallTargetForCFG(buffer); // TODO: improve this once EntryPoint cleanup work is complete! #if 0 const char16 *const functionName = functionBody->GetDisplayName(); const char16 *const suffix = _u("TJ"); char16 functionNameArray[256]; const size_t functionNameCharLength = functionBody->GetDisplayNameLength(); wcscpy_s(functionNameArray, 256, functionName); wcscpy_s(&functionNameArray[functionNameCharLength], 256 - functionNameCharLength, suffix); #endif JS_ETW(EventWriteMethodLoad(functionBody->GetScriptContext(), (void *)buffer, codeSize, EtwTrace::GetFunctionId(functionBody), 0 /* methodFlags - for future use*/, MethodType_Jit, EtwTrace::GetSourceId(functionBody), functionBody->GetLineNumber(), functionBody->GetColumnNumber(), functionBody->GetDisplayName())); entryPointInfo->SetTJCodeGenDone(); // set the codegen to done state for TJ entryPointInfo->SetCodeSize(codeSize); return buffer; } return nullptr; }
///---------------------------------------------------------------------------- /// /// Encoder::Encode /// /// Main entrypoint of encoder. Encode each IR instruction into the /// appropriate machine encoding. /// ///---------------------------------------------------------------------------- void Encoder::Encode() { NoRecoverMemoryArenaAllocator localAlloc(_u("BE-Encoder"), m_func->m_alloc->GetPageAllocator(), Js::Throw::OutOfMemory); m_tempAlloc = &localAlloc; uint32 instrCount = m_func->GetInstrCount(); size_t totalJmpTableSizeInBytes = 0; JmpTableList * jumpTableListForSwitchStatement = nullptr; m_encoderMD.Init(this); m_encodeBufferSize = UInt32Math::Mul(instrCount, MachMaxInstrSize); m_encodeBufferSize += m_func->m_totalJumpTableSizeInBytesForSwitchStatements; m_encodeBuffer = AnewArray(m_tempAlloc, BYTE, m_encodeBufferSize); #if DBG_DUMP m_instrNumber = 0; m_offsetBuffer = AnewArray(m_tempAlloc, uint, instrCount); #endif m_pragmaInstrToRecordMap = Anew(m_tempAlloc, PragmaInstrList, m_tempAlloc); if (DoTrackAllStatementBoundary()) { // Create a new list, if we are tracking all statement boundaries. m_pragmaInstrToRecordOffset = Anew(m_tempAlloc, PragmaInstrList, m_tempAlloc); } else { // Set the list to the same as the throw map list, so that processing of the list // of pragma are done on those only. m_pragmaInstrToRecordOffset = m_pragmaInstrToRecordMap; } #if defined(_M_IX86) || defined(_M_X64) // for BR shortening m_inlineeFrameRecords = Anew(m_tempAlloc, InlineeFrameRecords, m_tempAlloc); #endif m_pc = m_encodeBuffer; m_inlineeFrameMap = Anew(m_tempAlloc, InlineeFrameMap, m_tempAlloc); m_bailoutRecordMap = Anew(m_tempAlloc, BailoutRecordMap, m_tempAlloc); IR::PragmaInstr* pragmaInstr = nullptr; uint32 pragmaOffsetInBuffer = 0; #ifdef _M_X64 bool inProlog = false; #endif bool isCallInstr = false; FOREACH_INSTR_IN_FUNC(instr, m_func) { Assert(Lowerer::ValidOpcodeAfterLower(instr, m_func)); if (GetCurrentOffset() + MachMaxInstrSize < m_encodeBufferSize) { ptrdiff_t count; #if DBG_DUMP AssertMsg(m_instrNumber < instrCount, "Bad instr count?"); __analysis_assume(m_instrNumber < instrCount); m_offsetBuffer[m_instrNumber++] = GetCurrentOffset(); #endif if (instr->IsPragmaInstr()) { switch(instr->m_opcode) { #ifdef _M_X64 case Js::OpCode::PrologStart: m_func->m_prologEncoder.Begin(m_pc - m_encodeBuffer); inProlog = true; continue; case Js::OpCode::PrologEnd: m_func->m_prologEncoder.End(); inProlog = false; continue; #endif case Js::OpCode::StatementBoundary: pragmaOffsetInBuffer = GetCurrentOffset(); pragmaInstr = instr->AsPragmaInstr(); pragmaInstr->m_offsetInBuffer = pragmaOffsetInBuffer; // will record after BR shortening with adjusted offsets if (DoTrackAllStatementBoundary()) { m_pragmaInstrToRecordOffset->Add(pragmaInstr); } break; default: continue; } } else if (instr->IsBranchInstr() && instr->AsBranchInstr()->IsMultiBranch()) { Assert(instr->GetSrc1() && instr->GetSrc1()->IsRegOpnd()); IR::MultiBranchInstr * multiBranchInstr = instr->AsBranchInstr()->AsMultiBrInstr(); if (multiBranchInstr->m_isSwitchBr && (multiBranchInstr->m_kind == IR::MultiBranchInstr::IntJumpTable || multiBranchInstr->m_kind == IR::MultiBranchInstr::SingleCharStrJumpTable)) { BranchJumpTableWrapper * branchJumpTableWrapper = multiBranchInstr->GetBranchJumpTable(); if (jumpTableListForSwitchStatement == nullptr) { jumpTableListForSwitchStatement = Anew(m_tempAlloc, JmpTableList, m_tempAlloc); } jumpTableListForSwitchStatement->Add(branchJumpTableWrapper); totalJmpTableSizeInBytes += (branchJumpTableWrapper->tableSize * sizeof(void*)); } else { //Reloc Records EncoderMD * encoderMD = &(this->m_encoderMD); multiBranchInstr->MapMultiBrTargetByAddress([=](void ** offset) -> void { #if defined(_M_ARM32_OR_ARM64) encoderMD->AddLabelReloc((byte*) offset); #else encoderMD->AppendRelocEntry(RelocTypeLabelUse, (void*) (offset)); #endif }); } } else { isCallInstr = LowererMD::IsCall(instr); if (pragmaInstr && (instr->isInlineeEntryInstr || isCallInstr)) { // will record throw map after BR shortening with adjusted offsets m_pragmaInstrToRecordMap->Add(pragmaInstr); pragmaInstr = nullptr; // Only once per pragma instr -- do we need to make this record? } if (instr->HasBailOutInfo()) { Assert(this->m_func->hasBailout); Assert(LowererMD::IsCall(instr)); instr->GetBailOutInfo()->FinalizeBailOutRecord(this->m_func); } if (instr->isInlineeEntryInstr) { m_encoderMD.EncodeInlineeCallInfo(instr, GetCurrentOffset()); } if (instr->m_opcode == Js::OpCode::InlineeStart) { Assert(!instr->isInlineeEntryInstr); if (pragmaInstr) { m_pragmaInstrToRecordMap->Add(pragmaInstr); pragmaInstr = nullptr; } Func* inlinee = instr->m_func; if (inlinee->frameInfo && inlinee->frameInfo->record) { inlinee->frameInfo->record->Finalize(inlinee, GetCurrentOffset()); #if defined(_M_IX86) || defined(_M_X64) // Store all records to be adjusted for BR shortening m_inlineeFrameRecords->Add(inlinee->frameInfo->record); #endif } continue; } } count = m_encoderMD.Encode(instr, m_pc, m_encodeBuffer); #if DBG_DUMP if (PHASE_TRACE(Js::EncoderPhase, this->m_func)) { instr->Dump((IRDumpFlags)(IRDumpFlags_SimpleForm | IRDumpFlags_SkipEndLine | IRDumpFlags_SkipByteCodeOffset)); Output::SkipToColumn(80); for (BYTE * current = m_pc; current < m_pc + count; current++) { Output::Print(_u("%02X "), *current); } Output::Print(_u("\n")); Output::Flush(); } #endif #ifdef _M_X64 if (inProlog) m_func->m_prologEncoder.EncodeInstr(instr, count & 0xFF); #endif m_pc += count; #if defined(_M_IX86) || defined(_M_X64) // for BR shortening. if (instr->isInlineeEntryInstr) m_encoderMD.AppendRelocEntry(RelocType::RelocTypeInlineeEntryOffset, (void*) (m_pc - MachPtr)); #endif if (isCallInstr) { isCallInstr = false; this->RecordInlineeFrame(instr->m_func, GetCurrentOffset()); } if (instr->HasBailOutInfo() && Lowerer::DoLazyBailout(this->m_func)) { this->RecordBailout(instr, (uint32)(m_pc - m_encodeBuffer)); } } else { Fatal(); } }
void TypedRegisterAllocator::CommitToFunctionInfo(Js::AsmJsFunctionInfo* funcInfo, Js::FunctionBody* body) const { uint32 offset = Js::AsmJsFunctionMemory::RequiredVarConstants * sizeof(Js::Var); WAsmJs::TypedConstSourcesInfo constSourcesInfo = GetConstSourceInfos(); #if DBG_DUMP if (PHASE_TRACE(Js::AsmjsInterpreterStackPhase, body)) { Output::Print(_u("ASMFunctionInfo Stack Data for %s\n"), body->GetDisplayName()); Output::Print(_u("==========================\n")); Output::Print(_u("RequiredVarConstants:%d\n"), Js::AsmJsFunctionMemory::RequiredVarConstants); } #endif for (int i = 0; i < WAsmJs::LIMIT; ++i) { Types type = (Types)i; TypedSlotInfo& slotInfo = *funcInfo->GetTypedSlotInfo(type); // Check if we don't want to commit this type if (!IsTypeExcluded(type)) { RegisterSpace* registerSpace = GetRegisterSpace(type); slotInfo.constCount = registerSpace->GetConstCount(); slotInfo.varCount = registerSpace->GetVarCount(); slotInfo.tmpCount = registerSpace->GetTmpCount(); slotInfo.constSrcByteOffset = constSourcesInfo.srcByteOffsets[i]; offset = Math::AlignOverflowCheck(offset, GetTypeByteSize(type)); slotInfo.byteOffset = offset; // Update offset for next type uint32 totalTypeCount = 0; totalTypeCount = UInt32Math::Add(totalTypeCount, slotInfo.constCount); totalTypeCount = UInt32Math::Add(totalTypeCount, slotInfo.varCount); totalTypeCount = UInt32Math::Add(totalTypeCount, slotInfo.tmpCount); offset = UInt32Math::Add(offset, UInt32Math::Mul(totalTypeCount, GetTypeByteSize(type))); #if DBG_DUMP if (PHASE_TRACE(Js::AsmjsInterpreterStackPhase, body)) { char16 buf[16]; RegisterSpace::GetTypeDebugName(type, buf, 16); Output::Print(_u("%s Offset:%d ConstCount:%d VarCount:%d TmpCount:%d = %d * %d = 0x%x bytes\n"), buf, slotInfo.byteOffset, slotInfo.constCount, slotInfo.varCount, slotInfo.tmpCount, totalTypeCount, GetTypeByteSize(type), totalTypeCount * GetTypeByteSize(type)); } #endif } } // These bytes offset already calculated the alignment, used them to determine how many Js::Var we need to do the allocation uint32 stackByteSize = offset; uint32 bytesUsedForConst = constSourcesInfo.bytesUsed; uint32 jsVarUsedForConstsTable = ConvertToJsVarOffset<byte>(bytesUsedForConst); uint32 totalVarsNeeded = ConvertToJsVarOffset<byte>(stackByteSize); uint32 jsVarNeededForVars = totalVarsNeeded - jsVarUsedForConstsTable; if (totalVarsNeeded < jsVarUsedForConstsTable) { // If for some reason we allocated more space in the const table than what we need, just don't allocate anymore vars jsVarNeededForVars = 0; } Assert((jsVarUsedForConstsTable + jsVarNeededForVars) >= totalVarsNeeded); body->CheckAndSetVarCount(jsVarNeededForVars); #if DBG_DUMP if (PHASE_TRACE(Js::AsmjsInterpreterStackPhase, body)) { Output::Print(_u("Total memory required: (%u consts + %u vars) * sizeof(Js::Var) >= %u * sizeof(Js::Var) = 0x%x bytes\n"), jsVarUsedForConstsTable, jsVarNeededForVars, totalVarsNeeded, stackByteSize); } #endif }