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(); } }