Encoder.cpp 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "Backend.h"
  6. ///----------------------------------------------------------------------------
  7. ///
  8. /// Encoder::Encode
  9. ///
  10. /// Main entrypoint of encoder. Encode each IR instruction into the
  11. /// appropriate machine encoding.
  12. ///
  13. ///----------------------------------------------------------------------------
  14. void
  15. Encoder::Encode()
  16. {
  17. NoRecoverMemoryArenaAllocator localAlloc(_u("BE-Encoder"), m_func->m_alloc->GetPageAllocator(), Js::Throw::OutOfMemory);
  18. m_tempAlloc = &localAlloc;
  19. uint32 instrCount = m_func->GetInstrCount();
  20. size_t totalJmpTableSizeInBytes = 0;
  21. JmpTableList * jumpTableListForSwitchStatement = nullptr;
  22. m_encoderMD.Init(this);
  23. m_encodeBufferSize = UInt32Math::Mul(instrCount, MachMaxInstrSize);
  24. m_encodeBufferSize += m_func->m_totalJumpTableSizeInBytesForSwitchStatements;
  25. m_encodeBuffer = AnewArray(m_tempAlloc, BYTE, m_encodeBufferSize);
  26. #if DBG_DUMP
  27. m_instrNumber = 0;
  28. m_offsetBuffer = AnewArray(m_tempAlloc, uint, instrCount);
  29. #endif
  30. m_pragmaInstrToRecordMap = Anew(m_tempAlloc, PragmaInstrList, m_tempAlloc);
  31. if (DoTrackAllStatementBoundary())
  32. {
  33. // Create a new list, if we are tracking all statement boundaries.
  34. m_pragmaInstrToRecordOffset = Anew(m_tempAlloc, PragmaInstrList, m_tempAlloc);
  35. }
  36. else
  37. {
  38. // Set the list to the same as the throw map list, so that processing of the list
  39. // of pragma are done on those only.
  40. m_pragmaInstrToRecordOffset = m_pragmaInstrToRecordMap;
  41. }
  42. #if defined(_M_IX86) || defined(_M_X64)
  43. // for BR shortening
  44. m_inlineeFrameRecords = Anew(m_tempAlloc, InlineeFrameRecords, m_tempAlloc);
  45. #endif
  46. m_pc = m_encodeBuffer;
  47. m_inlineeFrameMap = Anew(m_tempAlloc, InlineeFrameMap, m_tempAlloc);
  48. m_bailoutRecordMap = Anew(m_tempAlloc, BailoutRecordMap, m_tempAlloc);
  49. IR::PragmaInstr* pragmaInstr = nullptr;
  50. uint32 pragmaOffsetInBuffer = 0;
  51. #ifdef _M_X64
  52. bool inProlog = false;
  53. #endif
  54. bool isCallInstr = false;
  55. FOREACH_INSTR_IN_FUNC(instr, m_func)
  56. {
  57. Assert(Lowerer::ValidOpcodeAfterLower(instr, m_func));
  58. if (GetCurrentOffset() + MachMaxInstrSize < m_encodeBufferSize)
  59. {
  60. ptrdiff_t count;
  61. #if DBG_DUMP
  62. AssertMsg(m_instrNumber < instrCount, "Bad instr count?");
  63. __analysis_assume(m_instrNumber < instrCount);
  64. m_offsetBuffer[m_instrNumber++] = GetCurrentOffset();
  65. #endif
  66. if (instr->IsPragmaInstr())
  67. {
  68. switch(instr->m_opcode)
  69. {
  70. #ifdef _M_X64
  71. case Js::OpCode::PrologStart:
  72. inProlog = true;
  73. continue;
  74. case Js::OpCode::PrologEnd:
  75. inProlog = false;
  76. continue;
  77. #endif
  78. case Js::OpCode::StatementBoundary:
  79. pragmaOffsetInBuffer = GetCurrentOffset();
  80. pragmaInstr = instr->AsPragmaInstr();
  81. pragmaInstr->m_offsetInBuffer = pragmaOffsetInBuffer;
  82. // will record after BR shortening with adjusted offsets
  83. if (DoTrackAllStatementBoundary())
  84. {
  85. m_pragmaInstrToRecordOffset->Add(pragmaInstr);
  86. }
  87. break;
  88. default:
  89. continue;
  90. }
  91. }
  92. else if (instr->IsBranchInstr() && instr->AsBranchInstr()->IsMultiBranch())
  93. {
  94. Assert(instr->GetSrc1() && instr->GetSrc1()->IsRegOpnd());
  95. IR::MultiBranchInstr * multiBranchInstr = instr->AsBranchInstr()->AsMultiBrInstr();
  96. if (multiBranchInstr->m_isSwitchBr &&
  97. (multiBranchInstr->m_kind == IR::MultiBranchInstr::IntJumpTable || multiBranchInstr->m_kind == IR::MultiBranchInstr::SingleCharStrJumpTable))
  98. {
  99. BranchJumpTableWrapper * branchJumpTableWrapper = multiBranchInstr->GetBranchJumpTable();
  100. if (jumpTableListForSwitchStatement == nullptr)
  101. {
  102. jumpTableListForSwitchStatement = Anew(m_tempAlloc, JmpTableList, m_tempAlloc);
  103. }
  104. jumpTableListForSwitchStatement->Add(branchJumpTableWrapper);
  105. totalJmpTableSizeInBytes += (branchJumpTableWrapper->tableSize * sizeof(void*));
  106. }
  107. else
  108. {
  109. //Reloc Records
  110. EncoderMD * encoderMD = &(this->m_encoderMD);
  111. multiBranchInstr->MapMultiBrTargetByAddress([=](void ** offset) -> void
  112. {
  113. #if defined(_M_ARM32_OR_ARM64)
  114. encoderMD->AddLabelReloc((byte*) offset);
  115. #else
  116. encoderMD->AppendRelocEntry(RelocTypeLabelUse, (void*) (offset));
  117. #endif
  118. });
  119. }
  120. }
  121. else
  122. {
  123. isCallInstr = LowererMD::IsCall(instr);
  124. if (pragmaInstr && (instr->isInlineeEntryInstr || isCallInstr))
  125. {
  126. // will record throw map after BR shortening with adjusted offsets
  127. m_pragmaInstrToRecordMap->Add(pragmaInstr);
  128. pragmaInstr = nullptr; // Only once per pragma instr -- do we need to make this record?
  129. }
  130. if (instr->HasBailOutInfo())
  131. {
  132. Assert(this->m_func->hasBailout);
  133. Assert(LowererMD::IsCall(instr));
  134. instr->GetBailOutInfo()->FinalizeBailOutRecord(this->m_func);
  135. }
  136. if (instr->isInlineeEntryInstr)
  137. {
  138. m_encoderMD.EncodeInlineeCallInfo(instr, GetCurrentOffset());
  139. }
  140. if (instr->m_opcode == Js::OpCode::InlineeStart)
  141. {
  142. Assert(!instr->isInlineeEntryInstr);
  143. if (pragmaInstr)
  144. {
  145. m_pragmaInstrToRecordMap->Add(pragmaInstr);
  146. pragmaInstr = nullptr;
  147. }
  148. Func* inlinee = instr->m_func;
  149. if (inlinee->frameInfo && inlinee->frameInfo->record)
  150. {
  151. inlinee->frameInfo->record->Finalize(inlinee, GetCurrentOffset());
  152. #if defined(_M_IX86) || defined(_M_X64)
  153. // Store all records to be adjusted for BR shortening
  154. m_inlineeFrameRecords->Add(inlinee->frameInfo->record);
  155. #endif
  156. }
  157. continue;
  158. }
  159. }
  160. count = m_encoderMD.Encode(instr, m_pc, m_encodeBuffer);
  161. #if DBG_DUMP
  162. if (PHASE_TRACE(Js::EncoderPhase, this->m_func))
  163. {
  164. instr->Dump((IRDumpFlags)(IRDumpFlags_SimpleForm | IRDumpFlags_SkipEndLine | IRDumpFlags_SkipByteCodeOffset));
  165. Output::SkipToColumn(80);
  166. for (BYTE * current = m_pc; current < m_pc + count; current++)
  167. {
  168. Output::Print(_u("%02X "), *current);
  169. }
  170. Output::Print(_u("\n"));
  171. Output::Flush();
  172. }
  173. #endif
  174. #ifdef _M_X64
  175. if (inProlog)
  176. m_func->m_prologEncoder.EncodeInstr(instr, count & 0xFF);
  177. #endif
  178. m_pc += count;
  179. #if defined(_M_IX86) || defined(_M_X64)
  180. // for BR shortening.
  181. if (instr->isInlineeEntryInstr)
  182. m_encoderMD.AppendRelocEntry(RelocType::RelocTypeInlineeEntryOffset, (void*) (m_pc - MachPtr));
  183. #endif
  184. if (isCallInstr)
  185. {
  186. isCallInstr = false;
  187. this->RecordInlineeFrame(instr->m_func, GetCurrentOffset());
  188. }
  189. if (instr->HasBailOutInfo() && Lowerer::DoLazyBailout(this->m_func))
  190. {
  191. this->RecordBailout(instr, (uint32)(m_pc - m_encodeBuffer));
  192. }
  193. }
  194. else
  195. {
  196. Fatal();
  197. }
  198. } NEXT_INSTR_IN_FUNC;
  199. ptrdiff_t codeSize = m_pc - m_encodeBuffer + totalJmpTableSizeInBytes;
  200. #if defined(_M_IX86) || defined(_M_X64)
  201. BOOL isSuccessBrShortAndLoopAlign = false;
  202. // Shorten branches. ON by default
  203. if (!PHASE_OFF(Js::BrShortenPhase, m_func))
  204. {
  205. isSuccessBrShortAndLoopAlign = ShortenBranchesAndLabelAlign(&m_encodeBuffer, &codeSize);
  206. }
  207. #endif
  208. #if DBG_DUMP | defined(VTUNE_PROFILING)
  209. if (this->m_func->DoRecordNativeMap())
  210. {
  211. // Record PragmaInstr offsets and throw maps
  212. for (int32 i = 0; i < m_pragmaInstrToRecordOffset->Count(); i++)
  213. {
  214. IR::PragmaInstr *inst = m_pragmaInstrToRecordOffset->Item(i);
  215. inst->Record(inst->m_offsetInBuffer);
  216. }
  217. }
  218. #endif
  219. if (m_pragmaInstrToRecordMap->Count() > 0)
  220. {
  221. if (m_func->IsOOPJIT())
  222. {
  223. Js::ThrowMapEntry * throwMap = NativeCodeDataNewArrayNoFixup(m_func->GetNativeCodeDataAllocator(), Js::ThrowMapEntry, m_pragmaInstrToRecordMap->Count());
  224. for (int32 i = 0; i < m_pragmaInstrToRecordMap->Count(); i++)
  225. {
  226. IR::PragmaInstr *inst = m_pragmaInstrToRecordMap->Item(i);
  227. throwMap[i].nativeBufferOffset = inst->m_offsetInBuffer;
  228. throwMap[i].statementIndex = inst->m_statementIndex;
  229. }
  230. m_func->GetJITOutput()->RecordThrowMap(throwMap, m_pragmaInstrToRecordMap->Count());
  231. }
  232. else
  233. {
  234. auto entryPointInfo = m_func->GetInProcJITEntryPointInfo();
  235. auto functionBody = entryPointInfo->GetFunctionBody();
  236. Js::SmallSpanSequenceIter iter;
  237. for (int32 i = 0; i < m_pragmaInstrToRecordMap->Count(); i++)
  238. {
  239. IR::PragmaInstr *inst = m_pragmaInstrToRecordMap->Item(i);
  240. functionBody->RecordNativeThrowMap(iter, inst->m_offsetInBuffer, inst->m_statementIndex, entryPointInfo, Js::LoopHeader::NoLoop);
  241. }
  242. }
  243. }
  244. JITTimeWorkItem * workItem = m_func->GetWorkItem();
  245. BEGIN_CODEGEN_PHASE(m_func, Js::EmitterPhase);
  246. // Copy to permanent buffer.
  247. Assert(Math::FitsInDWord(codeSize));
  248. ushort xdataSize;
  249. ushort pdataCount;
  250. #ifdef _M_X64
  251. pdataCount = 1;
  252. xdataSize = (ushort)m_func->m_prologEncoder.SizeOfUnwindInfo();
  253. #elif _M_ARM
  254. pdataCount = (ushort)m_func->m_unwindInfo.GetPDataCount(codeSize);
  255. xdataSize = (UnwindInfoManager::MaxXdataBytes + 3) * pdataCount;
  256. #else
  257. xdataSize = 0;
  258. pdataCount = 0;
  259. #endif
  260. OUTPUT_VERBOSE_TRACE(Js::EmitterPhase, _u("PDATA count:%u\n"), pdataCount);
  261. OUTPUT_VERBOSE_TRACE(Js::EmitterPhase, _u("Size of XDATA:%u\n"), xdataSize);
  262. OUTPUT_VERBOSE_TRACE(Js::EmitterPhase, _u("Size of code:%u\n"), codeSize);
  263. TryCopyAndAddRelocRecordsForSwitchJumpTableEntries(m_encodeBuffer, codeSize, jumpTableListForSwitchStatement, totalJmpTableSizeInBytes);
  264. EmitBufferAllocation * alloc = m_func->GetJITOutput()->RecordNativeCodeSize(m_func, (DWORD)codeSize, pdataCount, xdataSize);
  265. if (!alloc->inPrereservedRegion)
  266. {
  267. m_func->GetThreadContextInfo()->ResetIsAllJITCodeInPreReservedRegion();
  268. }
  269. this->m_bailoutRecordMap->MapAddress([=](int index, LazyBailOutRecord* record)
  270. {
  271. this->m_encoderMD.AddLabelReloc((BYTE*)&record->instructionPointer);
  272. });
  273. // Relocs
  274. m_encoderMD.ApplyRelocs((size_t)alloc->allocation->address);
  275. m_func->GetJITOutput()->RecordNativeCode(m_func, m_encodeBuffer, alloc);
  276. #ifdef _M_X64
  277. m_func->m_prologEncoder.FinalizeUnwindInfo();
  278. m_func->GetJITOutput()->RecordUnwindInfo(
  279. 0,
  280. m_func->m_prologEncoder.GetUnwindInfo(),
  281. m_func->m_prologEncoder.SizeOfUnwindInfo(),
  282. alloc->allocation->xdata.address,
  283. m_func->GetThreadContextInfo()->GetProcessHandle());
  284. #elif _M_ARM
  285. m_func->m_unwindInfo.EmitUnwindInfo(m_func->GetJITOutput(), alloc);
  286. m_func->GetJITOutput()->SetCodeAddress(m_func->GetJITOutput()->GetCodeAddress() | 0x1); // Set thumb mode
  287. #endif
  288. const bool isSimpleJit = m_func->IsSimpleJit();
  289. if (this->m_inlineeFrameMap->Count() > 0 &&
  290. !(this->m_inlineeFrameMap->Count() == 1 && this->m_inlineeFrameMap->Item(0).record == nullptr))
  291. {
  292. if (!m_func->IsOOPJIT()) // in-proc JIT
  293. {
  294. m_func->GetInProcJITEntryPointInfo()->RecordInlineeFrameMap(m_inlineeFrameMap);
  295. }
  296. else // OOP JIT
  297. {
  298. NativeOffsetInlineeFrameRecordOffset* pairs = NativeCodeDataNewArrayZNoFixup(m_func->GetNativeCodeDataAllocator(), NativeOffsetInlineeFrameRecordOffset, this->m_inlineeFrameMap->Count());
  299. this->m_inlineeFrameMap->Map([&pairs](int i, NativeOffsetInlineeFramePair& p)
  300. {
  301. pairs[i].offset = p.offset;
  302. if (p.record)
  303. {
  304. pairs[i].recordOffset = NativeCodeData::GetDataChunk(p.record)->offset;
  305. }
  306. else
  307. {
  308. pairs[i].recordOffset = NativeOffsetInlineeFrameRecordOffset::InvalidRecordOffset;
  309. }
  310. });
  311. m_func->GetJITOutput()->RecordInlineeFrameOffsetsInfo(NativeCodeData::GetDataChunk(pairs)->offset, this->m_inlineeFrameMap->Count());
  312. }
  313. }
  314. if (this->m_bailoutRecordMap->Count() > 0)
  315. {
  316. m_func->GetInProcJITEntryPointInfo()->RecordBailOutMap(m_bailoutRecordMap);
  317. }
  318. if (this->m_func->pinnedTypeRefs != nullptr)
  319. {
  320. Assert(!isSimpleJit);
  321. int pinnedTypeRefCount = this->m_func->pinnedTypeRefs->Count();
  322. PinnedTypeRefsIDL* pinnedTypeRefs = nullptr;
  323. if (this->m_func->IsOOPJIT())
  324. {
  325. pinnedTypeRefs = (PinnedTypeRefsIDL*)midl_user_allocate(offsetof(PinnedTypeRefsIDL, typeRefs) + sizeof(void*)*pinnedTypeRefCount);
  326. pinnedTypeRefs->count = pinnedTypeRefCount;
  327. pinnedTypeRefs->isOOPJIT = true;
  328. this->m_func->GetJITOutput()->GetOutputData()->pinnedTypeRefs = pinnedTypeRefs;
  329. }
  330. else
  331. {
  332. pinnedTypeRefs = HeapNewStructPlus(offsetof(PinnedTypeRefsIDL, typeRefs) + sizeof(void*)*pinnedTypeRefCount - sizeof(PinnedTypeRefsIDL), PinnedTypeRefsIDL);
  333. pinnedTypeRefs->count = pinnedTypeRefCount;
  334. pinnedTypeRefs->isOOPJIT = false;
  335. }
  336. int index = 0;
  337. this->m_func->pinnedTypeRefs->Map([&pinnedTypeRefs, &index](void* typeRef) -> void
  338. {
  339. pinnedTypeRefs->typeRefs[index++] = ((JITType*)typeRef)->GetAddr();
  340. });
  341. if (PHASE_TRACE(Js::TracePinnedTypesPhase, this->m_func))
  342. {
  343. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  344. Output::Print(_u("PinnedTypes: function %s(%s) pinned %d types.\n"),
  345. this->m_func->GetJITFunctionBody()->GetDisplayName(), this->m_func->GetDebugNumberSet(debugStringBuffer), pinnedTypeRefCount);
  346. Output::Flush();
  347. }
  348. if (!this->m_func->IsOOPJIT())
  349. {
  350. m_func->GetInProcJITEntryPointInfo()->GetJitTransferData()->SetRuntimeTypeRefs(pinnedTypeRefs);
  351. }
  352. }
  353. // Save all equivalent type guards in a fixed size array on the JIT transfer data
  354. if (this->m_func->equivalentTypeGuards != nullptr)
  355. {
  356. AssertMsg(!PHASE_OFF(Js::EquivObjTypeSpecPhase, this->m_func), "Why do we have equivalent type guards if we don't do equivalent object type spec?");
  357. int equivalentTypeGuardsCount = this->m_func->equivalentTypeGuards->Count();
  358. if (this->m_func->IsOOPJIT())
  359. {
  360. auto& equivalentTypeGuardOffsets = this->m_func->GetJITOutput()->GetOutputData()->equivalentTypeGuardOffsets;
  361. equivalentTypeGuardOffsets = (EquivalentTypeGuardOffsets*)midl_user_allocate(offsetof(EquivalentTypeGuardOffsets, guards) + equivalentTypeGuardsCount * sizeof(EquivalentTypeGuardIDL));
  362. equivalentTypeGuardOffsets->count = equivalentTypeGuardsCount;
  363. int i = 0;
  364. this->m_func->equivalentTypeGuards->Map([&equivalentTypeGuardOffsets, &i](Js::JitEquivalentTypeGuard* srcGuard) -> void
  365. {
  366. equivalentTypeGuardOffsets->guards[i].offset = NativeCodeData::GetDataTotalOffset(srcGuard);
  367. auto cache = srcGuard->GetCache();
  368. equivalentTypeGuardOffsets->guards[i].cache.guardOffset = NativeCodeData::GetDataTotalOffset(cache->guard);
  369. equivalentTypeGuardOffsets->guards[i].cache.hasFixedValue = cache->hasFixedValue;
  370. equivalentTypeGuardOffsets->guards[i].cache.isLoadedFromProto = cache->isLoadedFromProto;
  371. equivalentTypeGuardOffsets->guards[i].cache.nextEvictionVictim = cache->nextEvictionVictim;
  372. equivalentTypeGuardOffsets->guards[i].cache.record.propertyCount = cache->record.propertyCount;
  373. equivalentTypeGuardOffsets->guards[i].cache.record.propertyOffset = NativeCodeData::GetDataTotalOffset(cache->record.properties);
  374. for (int j = 0; j < EQUIVALENT_TYPE_CACHE_SIZE_IDL; j++)
  375. {
  376. equivalentTypeGuardOffsets->guards[i].cache.types[j] = (intptr_t)cache->types[j];
  377. }
  378. i++;
  379. });
  380. Assert(equivalentTypeGuardsCount == i);
  381. }
  382. else
  383. {
  384. Js::JitEquivalentTypeGuard** guards = HeapNewArrayZ(Js::JitEquivalentTypeGuard*, equivalentTypeGuardsCount);
  385. Js::JitEquivalentTypeGuard** dstGuard = guards;
  386. this->m_func->equivalentTypeGuards->Map([&dstGuard](Js::JitEquivalentTypeGuard* srcGuard) -> void
  387. {
  388. *dstGuard++ = srcGuard;
  389. });
  390. m_func->GetInProcJITEntryPointInfo()->GetJitTransferData()->SetEquivalentTypeGuards(guards, equivalentTypeGuardsCount);
  391. }
  392. }
  393. if (this->m_func->lazyBailoutProperties.Count() > 0)
  394. {
  395. int count = this->m_func->lazyBailoutProperties.Count();
  396. Js::PropertyId* lazyBailoutProperties = HeapNewArrayZ(Js::PropertyId, count);
  397. Js::PropertyId* dstProperties = lazyBailoutProperties;
  398. this->m_func->lazyBailoutProperties.Map([&](Js::PropertyId propertyId)
  399. {
  400. *dstProperties++ = propertyId;
  401. });
  402. m_func->GetInProcJITEntryPointInfo()->GetJitTransferData()->SetLazyBailoutProperties(lazyBailoutProperties, count);
  403. }
  404. // Save all property guards on the JIT transfer data in a map keyed by property ID. We will use this map when installing the entry
  405. // point to register each guard for invalidation.
  406. if (this->m_func->propertyGuardsByPropertyId != nullptr)
  407. {
  408. Assert(!isSimpleJit);
  409. # if 0 // TODO: OOP JIT, is this assert valid?
  410. AssertMsg(!(PHASE_OFF(Js::ObjTypeSpecPhase, this->m_func) && PHASE_OFF(Js::FixedMethodsPhase, this->m_func)),
  411. "Why do we have type guards if we don't do object type spec or fixed methods?");
  412. #endif
  413. #if DBG
  414. int totalGuardCount = (this->m_func->singleTypeGuards != nullptr ? this->m_func->singleTypeGuards->Count() : 0)
  415. + (this->m_func->equivalentTypeGuards != nullptr ? this->m_func->equivalentTypeGuards->Count() : 0);
  416. Assert(totalGuardCount > 0);
  417. Assert(totalGuardCount == this->m_func->indexedPropertyGuardCount);
  418. #endif
  419. if (!this->m_func->IsOOPJIT())
  420. {
  421. int propertyCount = this->m_func->propertyGuardsByPropertyId->Count();
  422. Assert(propertyCount > 0);
  423. int guardSlotCount = 0;
  424. this->m_func->propertyGuardsByPropertyId->Map([&guardSlotCount](Js::PropertyId propertyId, Func::IndexedPropertyGuardSet* set) -> void
  425. {
  426. guardSlotCount += set->Count();
  427. });
  428. size_t typeGuardTransferSize = // Reserve enough room for:
  429. propertyCount * sizeof(Js::TypeGuardTransferEntry) + // each propertyId,
  430. propertyCount * sizeof(Js::JitIndexedPropertyGuard*) + // terminating nullptr guard for each propertyId,
  431. guardSlotCount * sizeof(Js::JitIndexedPropertyGuard*); // a pointer for each guard we counted above.
  432. // The extra room for sizeof(Js::TypePropertyGuardEntry) allocated by HeapNewPlus will be used for the terminating invalid propertyId.
  433. // Review (jedmiad): Skip zeroing? This is heap allocated so there shouldn't be any false recycler references.
  434. Js::TypeGuardTransferEntry* typeGuardTransferRecord = HeapNewPlusZ(typeGuardTransferSize, Js::TypeGuardTransferEntry);
  435. Func* func = this->m_func;
  436. Js::TypeGuardTransferEntry* dstEntry = typeGuardTransferRecord;
  437. this->m_func->propertyGuardsByPropertyId->Map([func, &dstEntry](Js::PropertyId propertyId, Func::IndexedPropertyGuardSet* srcSet) -> void
  438. {
  439. dstEntry->propertyId = propertyId;
  440. int guardIndex = 0;
  441. srcSet->Map([dstEntry, &guardIndex](Js::JitIndexedPropertyGuard* guard) -> void
  442. {
  443. dstEntry->guards[guardIndex++] = guard;
  444. });
  445. dstEntry->guards[guardIndex++] = nullptr;
  446. dstEntry = reinterpret_cast<Js::TypeGuardTransferEntry*>(&dstEntry->guards[guardIndex]);
  447. });
  448. dstEntry->propertyId = Js::Constants::NoProperty;
  449. dstEntry++;
  450. Assert(reinterpret_cast<char*>(dstEntry) <= reinterpret_cast<char*>(typeGuardTransferRecord) + typeGuardTransferSize + sizeof(Js::TypeGuardTransferEntry));
  451. m_func->GetInProcJITEntryPointInfo()->RecordTypeGuards(this->m_func->indexedPropertyGuardCount, typeGuardTransferRecord, typeGuardTransferSize);
  452. }
  453. else
  454. {
  455. Func* func = this->m_func;
  456. this->m_func->GetJITOutput()->GetOutputData()->propertyGuardCount = this->m_func->indexedPropertyGuardCount;
  457. auto entry = &this->m_func->GetJITOutput()->GetOutputData()->typeGuardEntries;
  458. this->m_func->propertyGuardsByPropertyId->Map([func, &entry](Js::PropertyId propertyId, Func::IndexedPropertyGuardSet* srcSet) -> void
  459. {
  460. auto count = srcSet->Count();
  461. (*entry) = (TypeGuardTransferEntryIDL*)midl_user_allocate(offsetof(TypeGuardTransferEntryIDL, guardOffsets) + count*sizeof(int));
  462. (*entry)->propId = propertyId;
  463. (*entry)->guardsCount = count;
  464. (*entry)->next = nullptr;
  465. auto& guardOffsets = (*entry)->guardOffsets;
  466. int guardIndex = 0;
  467. srcSet->Map([&guardOffsets, &guardIndex](Js::JitIndexedPropertyGuard* guard) -> void
  468. {
  469. guardOffsets[guardIndex++] = NativeCodeData::GetDataTotalOffset(guard);
  470. });
  471. Assert(guardIndex == count);
  472. entry = &(*entry)->next;
  473. });
  474. }
  475. }
  476. // Save all constructor caches on the JIT transfer data in a map keyed by property ID. We will use this map when installing the entry
  477. // point to register each cache for invalidation.
  478. if (this->m_func->ctorCachesByPropertyId != nullptr)
  479. {
  480. Assert(!isSimpleJit);
  481. AssertMsg(!(PHASE_OFF(Js::ObjTypeSpecPhase, this->m_func) && PHASE_OFF(Js::FixedMethodsPhase, this->m_func)),
  482. "Why do we have constructor cache guards if we don't do object type spec or fixed methods?");
  483. int propertyCount = this->m_func->ctorCachesByPropertyId->Count();
  484. Assert(propertyCount > 0);
  485. int cacheSlotCount = 0;
  486. this->m_func->ctorCachesByPropertyId->Map([&cacheSlotCount](Js::PropertyId propertyId, Func::CtorCacheSet* cacheSet) -> void
  487. {
  488. cacheSlotCount += cacheSet->Count();
  489. });
  490. if (m_func->IsOOPJIT())
  491. {
  492. Func* func = this->m_func;
  493. m_func->GetJITOutput()->GetOutputData()->ctorCachesCount = propertyCount;
  494. m_func->GetJITOutput()->GetOutputData()->ctorCacheEntries = (CtorCacheTransferEntryIDL**)midl_user_allocate(propertyCount * sizeof(CtorCacheTransferEntryIDL*));
  495. CtorCacheTransferEntryIDL** entries = m_func->GetJITOutput()->GetOutputData()->ctorCacheEntries;
  496. uint propIndex = 0;
  497. m_func->ctorCachesByPropertyId->Map([func, entries, &propIndex](Js::PropertyId propertyId, Func::CtorCacheSet* srcCacheSet) -> void
  498. {
  499. entries[propIndex] = (CtorCacheTransferEntryIDL*)midl_user_allocate(srcCacheSet->Count() * sizeof(intptr_t) + sizeof(CtorCacheTransferEntryIDL));
  500. entries[propIndex]->propId = propertyId;
  501. int cacheIndex = 0;
  502. srcCacheSet->Map([entries, propIndex, &cacheIndex](intptr_t cache) -> void
  503. {
  504. entries[propIndex]->caches[cacheIndex++] = cache;
  505. });
  506. entries[propIndex]->cacheCount = cacheIndex;
  507. propIndex++;
  508. });
  509. }
  510. else
  511. {
  512. Assert(m_func->GetInProcJITEntryPointInfo()->GetConstructorCacheCount() > 0);
  513. size_t ctorCachesTransferSize = // Reserve enough room for:
  514. propertyCount * sizeof(Js::CtorCacheGuardTransferEntry) + // each propertyId,
  515. propertyCount * sizeof(Js::ConstructorCache*) + // terminating null cache for each propertyId,
  516. cacheSlotCount * sizeof(Js::JitIndexedPropertyGuard*); // a pointer for each cache we counted above.
  517. // The extra room for sizeof(Js::CtorCacheGuardTransferEntry) allocated by HeapNewPlus will be used for the terminating invalid propertyId.
  518. // Review (jedmiad): Skip zeroing? This is heap allocated so there shouldn't be any false recycler references.
  519. Js::CtorCacheGuardTransferEntry* ctorCachesTransferRecord = HeapNewPlusZ(ctorCachesTransferSize, Js::CtorCacheGuardTransferEntry);
  520. Func* func = this->m_func;
  521. Js::CtorCacheGuardTransferEntry* dstEntry = ctorCachesTransferRecord;
  522. this->m_func->ctorCachesByPropertyId->Map([func, &dstEntry](Js::PropertyId propertyId, Func::CtorCacheSet* srcCacheSet) -> void
  523. {
  524. dstEntry->propertyId = propertyId;
  525. int cacheIndex = 0;
  526. srcCacheSet->Map([dstEntry, &cacheIndex](intptr_t cache) -> void
  527. {
  528. dstEntry->caches[cacheIndex++] = cache;
  529. });
  530. dstEntry->caches[cacheIndex++] = 0;
  531. dstEntry = reinterpret_cast<Js::CtorCacheGuardTransferEntry*>(&dstEntry->caches[cacheIndex]);
  532. });
  533. dstEntry->propertyId = Js::Constants::NoProperty;
  534. dstEntry++;
  535. Assert(reinterpret_cast<char*>(dstEntry) <= reinterpret_cast<char*>(ctorCachesTransferRecord) + ctorCachesTransferSize + sizeof(Js::CtorCacheGuardTransferEntry));
  536. m_func->GetInProcJITEntryPointInfo()->RecordCtorCacheGuards(ctorCachesTransferRecord, ctorCachesTransferSize);
  537. }
  538. }
  539. m_func->GetJITOutput()->FinalizeNativeCode(m_func, alloc);
  540. END_CODEGEN_PHASE(m_func, Js::EmitterPhase);
  541. #if DBG_DUMP
  542. m_func->m_codeSize = codeSize;
  543. if (PHASE_DUMP(Js::EncoderPhase, m_func) || PHASE_DUMP(Js::BackEndPhase, m_func))
  544. {
  545. bool dumpIRAddressesValue = Js::Configuration::Global.flags.DumpIRAddresses;
  546. Js::Configuration::Global.flags.DumpIRAddresses = true;
  547. this->m_func->DumpHeader();
  548. m_instrNumber = 0;
  549. FOREACH_INSTR_IN_FUNC(instr, m_func)
  550. {
  551. __analysis_assume(m_instrNumber < instrCount);
  552. instr->DumpGlobOptInstrString();
  553. #ifdef _WIN64
  554. Output::Print(_u("%12IX "), m_offsetBuffer[m_instrNumber++] + (BYTE *)m_func->GetJITOutput()->GetCodeAddress());
  555. #else
  556. Output::Print(_u("%8IX "), m_offsetBuffer[m_instrNumber++] + (BYTE *)m_func->GetJITOutput()->GetCodeAddress());
  557. #endif
  558. instr->Dump();
  559. } NEXT_INSTR_IN_FUNC;
  560. Output::Flush();
  561. Js::Configuration::Global.flags.DumpIRAddresses = dumpIRAddressesValue;
  562. }
  563. if (PHASE_DUMP(Js::EncoderPhase, m_func) && Js::Configuration::Global.flags.Verbose)
  564. {
  565. workItem->DumpNativeOffsetMaps();
  566. workItem->DumpNativeThrowSpanSequence();
  567. this->DumpInlineeFrameMap(m_func->GetJITOutput()->GetCodeAddress());
  568. Output::Flush();
  569. }
  570. #endif
  571. }
  572. bool Encoder::DoTrackAllStatementBoundary() const
  573. {
  574. #if DBG_DUMP | defined(VTUNE_PROFILING)
  575. return this->m_func->DoRecordNativeMap();
  576. #else
  577. return false;
  578. #endif
  579. }
  580. void Encoder::TryCopyAndAddRelocRecordsForSwitchJumpTableEntries(BYTE *codeStart, size_t codeSize, JmpTableList * jumpTableListForSwitchStatement, size_t totalJmpTableSizeInBytes)
  581. {
  582. if (jumpTableListForSwitchStatement == nullptr)
  583. {
  584. return;
  585. }
  586. BYTE * jmpTableStartAddress = codeStart + codeSize - totalJmpTableSizeInBytes;
  587. EncoderMD * encoderMD = &m_encoderMD;
  588. jumpTableListForSwitchStatement->Map([&](uint index, BranchJumpTableWrapper * branchJumpTableWrapper) -> void
  589. {
  590. Assert(branchJumpTableWrapper != nullptr);
  591. void ** srcJmpTable = branchJumpTableWrapper->jmpTable;
  592. size_t jmpTableSizeInBytes = branchJumpTableWrapper->tableSize * sizeof(void*);
  593. AssertMsg(branchJumpTableWrapper->labelInstr != nullptr, "Label not yet created?");
  594. Assert(branchJumpTableWrapper->labelInstr->GetPC() == nullptr);
  595. branchJumpTableWrapper->labelInstr->SetPC(jmpTableStartAddress);
  596. memcpy(jmpTableStartAddress, srcJmpTable, jmpTableSizeInBytes);
  597. for (int i = 0; i < branchJumpTableWrapper->tableSize; i++)
  598. {
  599. void * addressOfJmpTableEntry = jmpTableStartAddress + (i * sizeof(void*));
  600. Assert((ptrdiff_t) addressOfJmpTableEntry - (ptrdiff_t) jmpTableStartAddress < (ptrdiff_t) jmpTableSizeInBytes);
  601. #if defined(_M_ARM32_OR_ARM64)
  602. encoderMD->AddLabelReloc((byte*) addressOfJmpTableEntry);
  603. #else
  604. encoderMD->AppendRelocEntry(RelocTypeLabelUse, addressOfJmpTableEntry);
  605. #endif
  606. }
  607. jmpTableStartAddress += (jmpTableSizeInBytes);
  608. });
  609. Assert(jmpTableStartAddress == codeStart + codeSize);
  610. }
  611. uint32 Encoder::GetCurrentOffset() const
  612. {
  613. Assert(m_pc - m_encodeBuffer <= UINT_MAX); // encode buffer size is uint32
  614. return static_cast<uint32>(m_pc - m_encodeBuffer);
  615. }
  616. void Encoder::RecordInlineeFrame(Func* inlinee, uint32 currentOffset)
  617. {
  618. // The only restriction for not supporting loop bodies is that inlinee frame map is created on FunctionEntryPointInfo & not
  619. // the base class EntryPointInfo.
  620. if (!(this->m_func->IsLoopBody() && PHASE_OFF(Js::InlineInJitLoopBodyPhase, this->m_func)) && !this->m_func->IsSimpleJit())
  621. {
  622. InlineeFrameRecord* record = nullptr;
  623. if (inlinee->frameInfo && inlinee->m_hasInlineArgsOpt)
  624. {
  625. record = inlinee->frameInfo->record;
  626. Assert(record != nullptr);
  627. }
  628. if (m_inlineeFrameMap->Count() > 0)
  629. {
  630. // update existing record if the entry is the same.
  631. NativeOffsetInlineeFramePair& lastPair = m_inlineeFrameMap->Item(m_inlineeFrameMap->Count() - 1);
  632. if (lastPair.record == record)
  633. {
  634. lastPair.offset = currentOffset;
  635. return;
  636. }
  637. }
  638. NativeOffsetInlineeFramePair pair = { currentOffset, record };
  639. m_inlineeFrameMap->Add(pair);
  640. }
  641. }
  642. #if defined(_M_IX86) || defined(_M_X64)
  643. ///----------------------------------------------------------------------------
  644. ///
  645. /// EncoderMD::ShortenBranchesAndLabelAlign
  646. /// We try to shorten branches if the label instr is within 8-bits target range (-128 to 127)
  647. /// and fix the relocList accordingly.
  648. /// Also align LoopTop Label and TryCatchLabel
  649. ///----------------------------------------------------------------------------
  650. BOOL
  651. Encoder::ShortenBranchesAndLabelAlign(BYTE **codeStart, ptrdiff_t *codeSize)
  652. {
  653. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  654. static uint32 globalTotalBytesSaved = 0, globalTotalBytesWithoutShortening = 0;
  655. static uint32 globalTotalBytesInserted = 0; // loop alignment nops
  656. #endif
  657. uint32 brShortenedCount = 0;
  658. bool codeChange = false; // any overall BR shortened or label aligned ?
  659. BYTE* buffStart = *codeStart;
  660. BYTE* buffEnd = buffStart + *codeSize;
  661. ptrdiff_t newCodeSize = *codeSize;
  662. #if DBG
  663. // Sanity check
  664. m_encoderMD.VerifyRelocList(buffStart, buffEnd);
  665. #endif
  666. // Copy of original maps. Used to revert from BR shortening.
  667. OffsetList *m_origInlineeFrameRecords = nullptr,
  668. *m_origInlineeFrameMap = nullptr,
  669. *m_origPragmaInstrToRecordOffset = nullptr;
  670. OffsetList *m_origOffsetBuffer = nullptr;
  671. // we record the original maps, in case we have to revert.
  672. CopyMaps<false>(&m_origInlineeFrameRecords
  673. , &m_origInlineeFrameMap
  674. , &m_origPragmaInstrToRecordOffset
  675. , &m_origOffsetBuffer );
  676. RelocList* relocList = m_encoderMD.GetRelocList();
  677. Assert(relocList != nullptr);
  678. // Here we mark BRs to be shortened and adjust Labels and relocList entries offsets.
  679. uint32 offsetBuffIndex = 0, pragmaInstToRecordOffsetIndex = 0, inlineeFrameRecordsIndex = 0, inlineeFrameMapIndex = 0;
  680. int32 totalBytesSaved = 0;
  681. // loop over all BRs, find the ones we can convert to short form
  682. for (int32 j = 0; j < relocList->Count(); j++)
  683. {
  684. IR::LabelInstr *targetLabel;
  685. int32 relOffset;
  686. uint32 bytesSaved = 0;
  687. BYTE* labelPc, *opcodeByte;
  688. BYTE* shortBrPtr, *fixedBrPtr; // without shortening
  689. EncodeRelocAndLabels &reloc = relocList->Item(j);
  690. // If not a long branch, just fix the reloc entry and skip.
  691. if (!reloc.isLongBr())
  692. {
  693. // if loop alignment is required, total bytes saved can change
  694. int32 newTotalBytesSaved = m_encoderMD.FixRelocListEntry(j, totalBytesSaved, buffStart, buffEnd);
  695. if (newTotalBytesSaved != totalBytesSaved)
  696. {
  697. AssertMsg(reloc.isAlignedLabel(), "Expecting aligned label.");
  698. // we aligned a loop, fix maps
  699. m_encoderMD.FixMaps((uint32)(reloc.getLabelOrigPC() - buffStart), totalBytesSaved, &inlineeFrameRecordsIndex, &inlineeFrameMapIndex, &pragmaInstToRecordOffsetIndex, &offsetBuffIndex);
  700. codeChange = true;
  701. }
  702. totalBytesSaved = newTotalBytesSaved;
  703. continue;
  704. }
  705. AssertMsg(reloc.isLongBr(), "Cannot shorten already shortened branch.");
  706. // long branch
  707. opcodeByte = reloc.getBrOpCodeByte();
  708. targetLabel = reloc.getBrTargetLabel();
  709. AssertMsg(targetLabel != nullptr, "Branch to non-existing label");
  710. labelPc = targetLabel->GetPC();
  711. // compute the new offset of that Br because of previous shortening/alignment
  712. shortBrPtr = fixedBrPtr = (BYTE*)reloc.m_ptr - totalBytesSaved;
  713. if (*opcodeByte == 0xe9 /* JMP rel32 */)
  714. {
  715. bytesSaved = 3;
  716. }
  717. else if (*opcodeByte >= 0x80 && *opcodeByte < 0x90 /* Jcc rel32 */)
  718. {
  719. Assert(*(opcodeByte - 1) == 0x0f);
  720. bytesSaved = 4;
  721. // Jcc rel8 is one byte shorter in opcode, fix Br ptr to point to start of rel8
  722. shortBrPtr--;
  723. }
  724. else
  725. {
  726. Assert(false);
  727. }
  728. // compute current distance to label
  729. if (labelPc >= (BYTE*) reloc.m_ptr)
  730. {
  731. // forward Br. We compare using the unfixed m_ptr, because the label is ahead and its Pc is not fixed it.
  732. relOffset = (int32)(labelPc - ((BYTE*)reloc.m_ptr + 4));
  733. }
  734. else
  735. {
  736. // backward Br. We compute relOffset after fixing the Br, since the label is already fixed.
  737. // We also include the 3-4 bytes saved after shortening the Br since the Br itself is included in the relative offset.
  738. relOffset = (int32)(labelPc - (shortBrPtr + 1));
  739. }
  740. // update Br offset (overwritten later if Br is shortened)
  741. reloc.m_ptr = fixedBrPtr;
  742. // can we shorten ?
  743. if (relOffset >= -128 && relOffset <= 127)
  744. {
  745. uint32 brOffset;
  746. brShortenedCount++;
  747. // update with shortened br offset
  748. reloc.m_ptr = shortBrPtr;
  749. // fix all maps entries from last shortened br to this one, before updating total bytes saved.
  750. brOffset = (uint32) ((BYTE*)reloc.m_origPtr - buffStart);
  751. m_encoderMD.FixMaps(brOffset, totalBytesSaved, &inlineeFrameRecordsIndex, &inlineeFrameMapIndex, &pragmaInstToRecordOffsetIndex, &offsetBuffIndex);
  752. codeChange = true;
  753. totalBytesSaved += bytesSaved;
  754. // mark br reloc entry as shortened
  755. #ifdef _M_IX86
  756. reloc.setAsShortBr(targetLabel);
  757. #else
  758. reloc.setAsShortBr();
  759. #endif
  760. }
  761. }
  762. // Fix the rest of the maps, if needed.
  763. if (totalBytesSaved != 0)
  764. {
  765. m_encoderMD.FixMaps((uint32) -1, totalBytesSaved, &inlineeFrameRecordsIndex, &inlineeFrameMapIndex, &pragmaInstToRecordOffsetIndex, &offsetBuffIndex);
  766. codeChange = true;
  767. newCodeSize -= totalBytesSaved;
  768. }
  769. // no BR shortening or Label alignment happened, no need to copy code
  770. if (!codeChange)
  771. return codeChange;
  772. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  773. globalTotalBytesWithoutShortening += (uint32)(*codeSize);
  774. globalTotalBytesSaved += (uint32)(*codeSize - newCodeSize);
  775. if (PHASE_TRACE(Js::BrShortenPhase, this->m_func))
  776. {
  777. OUTPUT_VERBOSE_TRACE(Js::BrShortenPhase, _u("func: %s, bytes saved: %d, bytes saved %%:%.2f, total bytes saved: %d, total bytes saved%%: %.2f, BR shortened: %d\n"),
  778. this->m_func->GetJITFunctionBody()->GetDisplayName(), (*codeSize - newCodeSize), ((float)*codeSize - newCodeSize) / *codeSize * 100,
  779. globalTotalBytesSaved, ((float)globalTotalBytesSaved) / globalTotalBytesWithoutShortening * 100 , brShortenedCount);
  780. Output::Flush();
  781. }
  782. #endif
  783. // At this point BRs are marked to be shortened, and relocList offsets are adjusted to new instruction length.
  784. // Next, we re-write the code to shorten the BRs and adjust relocList offsets to point to new buffer.
  785. // We also write NOPs for aligned loops.
  786. BYTE* tmpBuffer = AnewArray(m_tempAlloc, BYTE, newCodeSize);
  787. // start copying to new buffer
  788. // this can possibly be done during fixing, but there is no evidence it is an overhead to justify the complexity.
  789. BYTE *from = buffStart, *to = nullptr;
  790. BYTE *dst_p = (BYTE*)tmpBuffer;
  791. size_t dst_size = newCodeSize;
  792. size_t src_size;
  793. for (int32 i = 0; i < relocList->Count(); i++)
  794. {
  795. EncodeRelocAndLabels &reloc = relocList->Item(i);
  796. // shorten BR and copy
  797. if (reloc.isShortBr())
  798. {
  799. // validate that short BR offset is within 1 byte offset range.
  800. // This handles the rare case with loop alignment breaks br shortening.
  801. // Consider:
  802. // BR $L1 // shortened
  803. // ...
  804. // L2: // aligned, and makes the BR $L1 non-shortable anymore
  805. // ...
  806. // BR $L2
  807. // ...
  808. // L1:
  809. // In this case, we simply give up and revert the relocList.
  810. if(!reloc.validateShortBrTarget())
  811. {
  812. revertRelocList();
  813. // restore maps
  814. CopyMaps<true>(&m_origInlineeFrameRecords
  815. , &m_origInlineeFrameMap
  816. , &m_origPragmaInstrToRecordOffset
  817. , &m_origOffsetBuffer
  818. );
  819. return false;
  820. }
  821. // m_origPtr points to imm32 field in the original buffer
  822. BYTE *opcodeByte = (BYTE*)reloc.m_origPtr - 1;
  823. if (*opcodeByte == 0xe9 /* JMP rel32 */)
  824. {
  825. to = opcodeByte - 1;
  826. }
  827. else if (*opcodeByte >= 0x80 && *opcodeByte < 0x90 /* Jcc rel32 */)
  828. {
  829. Assert(*(opcodeByte - 1) == 0x0f);
  830. to = opcodeByte - 2;
  831. }
  832. else
  833. {
  834. Assert(false);
  835. }
  836. src_size = to - from + 1;
  837. AnalysisAssert(dst_size >= src_size);
  838. memcpy_s(dst_p, dst_size, from, src_size);
  839. dst_p += src_size;
  840. dst_size -= src_size;
  841. // fix the BR
  842. // write new opcode
  843. AnalysisAssert(dst_p < tmpBuffer + newCodeSize);
  844. *dst_p = (*opcodeByte == 0xe9) ? (BYTE)0xeb : (BYTE)(*opcodeByte - 0x10);
  845. dst_p += 2; // 1 byte for opcode + 1 byte for imm8
  846. dst_size -= 2;
  847. from = (BYTE*)reloc.m_origPtr + 4;
  848. }
  849. // insert NOPs for aligned labels
  850. else if ((!PHASE_OFF(Js::LoopAlignPhase, m_func) && reloc.isAlignedLabel()) && reloc.getLabelNopCount() > 0)
  851. {
  852. IR::LabelInstr *label = reloc.getLabel();
  853. BYTE nop_count = reloc.getLabelNopCount();
  854. AssertMsg((BYTE*)label < buffStart || (BYTE*)label >= buffEnd, "Invalid label pointer.");
  855. AssertMsg((((uint32)(label->GetPC() - buffStart)) & 0xf) == 0, "Misaligned Label");
  856. to = reloc.getLabelOrigPC() - 1;
  857. CopyPartialBuffer(&dst_p, dst_size, from, to);
  858. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  859. if (PHASE_TRACE(Js::LoopAlignPhase, this->m_func))
  860. {
  861. globalTotalBytesInserted += nop_count;
  862. OUTPUT_VERBOSE_TRACE(Js::LoopAlignPhase, _u("func: %s, bytes inserted: %d, bytes inserted %%:%.4f, total bytes inserted:%d, total bytes inserted %%:%.4f\n"),
  863. this->m_func->GetJITFunctionBody()->GetDisplayName(), nop_count, (float)nop_count / newCodeSize * 100, globalTotalBytesInserted, (float)globalTotalBytesInserted / (globalTotalBytesWithoutShortening - globalTotalBytesSaved) * 100);
  864. Output::Flush();
  865. }
  866. #endif
  867. InsertNopsForLabelAlignment(nop_count, &dst_p);
  868. dst_size -= nop_count;
  869. from = to + 1;
  870. }
  871. }
  872. // copy last chunk
  873. CopyPartialBuffer(&dst_p, dst_size, from, buffStart + *codeSize - 1);
  874. m_encoderMD.UpdateRelocListWithNewBuffer(relocList, tmpBuffer, buffStart, buffEnd);
  875. // switch buffers
  876. *codeStart = tmpBuffer;
  877. *codeSize = newCodeSize;
  878. return true;
  879. }
  880. BYTE Encoder::FindNopCountFor16byteAlignment(size_t address)
  881. {
  882. return (16 - (BYTE) (address & 0xf)) % 16;
  883. }
  884. void Encoder::CopyPartialBuffer(BYTE ** ptrDstBuffer, size_t &dstSize, BYTE * srcStart, BYTE * srcEnd)
  885. {
  886. BYTE * destBuffer = *ptrDstBuffer;
  887. size_t srcSize = srcEnd - srcStart + 1;
  888. Assert(dstSize >= srcSize);
  889. memcpy_s(destBuffer, dstSize, srcStart, srcSize);
  890. *ptrDstBuffer += srcSize;
  891. dstSize -= srcSize;
  892. }
  893. void Encoder::InsertNopsForLabelAlignment(int nopCount, BYTE ** ptrDstBuffer)
  894. {
  895. // write NOPs
  896. for (int32 i = 0; i < nopCount; i++, (*ptrDstBuffer)++)
  897. {
  898. **ptrDstBuffer = 0x90;
  899. }
  900. }
  901. void Encoder::revertRelocList()
  902. {
  903. RelocList* relocList = m_encoderMD.GetRelocList();
  904. for (int32 i = 0; i < relocList->Count(); i++)
  905. {
  906. relocList->Item(i).revert();
  907. }
  908. }
  909. template <bool restore>
  910. void Encoder::CopyMaps(OffsetList **m_origInlineeFrameRecords
  911. , OffsetList **m_origInlineeFrameMap
  912. , OffsetList **m_origPragmaInstrToRecordOffset
  913. , OffsetList **m_origOffsetBuffer
  914. )
  915. {
  916. InlineeFrameRecords *recList = m_inlineeFrameRecords;
  917. InlineeFrameMap *mapList = m_inlineeFrameMap;
  918. PragmaInstrList *pInstrList = m_pragmaInstrToRecordOffset;
  919. OffsetList *origRecList, *origMapList, *origPInstrList;
  920. if (!restore)
  921. {
  922. Assert(*m_origInlineeFrameRecords == nullptr);
  923. Assert(*m_origInlineeFrameMap == nullptr);
  924. Assert(*m_origPragmaInstrToRecordOffset == nullptr);
  925. *m_origInlineeFrameRecords = origRecList = Anew(m_tempAlloc, OffsetList, m_tempAlloc);
  926. *m_origInlineeFrameMap = origMapList = Anew(m_tempAlloc, OffsetList, m_tempAlloc);
  927. *m_origPragmaInstrToRecordOffset = origPInstrList = Anew(m_tempAlloc, OffsetList, m_tempAlloc);
  928. #if DBG_DUMP
  929. Assert((*m_origOffsetBuffer) == nullptr);
  930. *m_origOffsetBuffer = Anew(m_tempAlloc, OffsetList, m_tempAlloc);
  931. #endif
  932. }
  933. else
  934. {
  935. Assert((*m_origInlineeFrameRecords) && (*m_origInlineeFrameMap) && (*m_origPragmaInstrToRecordOffset));
  936. origRecList = *m_origInlineeFrameRecords;
  937. origMapList = *m_origInlineeFrameMap;
  938. origPInstrList = *m_origPragmaInstrToRecordOffset;
  939. Assert(origRecList->Count() == recList->Count());
  940. Assert(origMapList->Count() == mapList->Count());
  941. Assert(origPInstrList->Count() == pInstrList->Count());
  942. #if DBG_DUMP
  943. Assert(m_origOffsetBuffer);
  944. Assert((uint32)(*m_origOffsetBuffer)->Count() == m_instrNumber);
  945. #endif
  946. }
  947. for (int i = 0; i < recList->Count(); i++)
  948. {
  949. if (!restore)
  950. {
  951. origRecList->Add(recList->Item(i)->inlineeStartOffset);
  952. }
  953. else
  954. {
  955. recList->Item(i)->inlineeStartOffset = origRecList->Item(i);
  956. }
  957. }
  958. for (int i = 0; i < mapList->Count(); i++)
  959. {
  960. if (!restore)
  961. {
  962. origMapList->Add(mapList->Item(i).offset);
  963. }
  964. else
  965. {
  966. mapList->Item(i).offset = origMapList->Item(i);
  967. }
  968. }
  969. for (int i = 0; i < pInstrList->Count(); i++)
  970. {
  971. if (!restore)
  972. {
  973. origPInstrList->Add(pInstrList->Item(i)->m_offsetInBuffer);
  974. }
  975. else
  976. {
  977. pInstrList->Item(i)->m_offsetInBuffer = origPInstrList->Item(i);
  978. }
  979. }
  980. if (restore)
  981. {
  982. (*m_origInlineeFrameRecords)->Delete();
  983. (*m_origInlineeFrameMap)->Delete();
  984. (*m_origPragmaInstrToRecordOffset)->Delete();
  985. (*m_origInlineeFrameRecords) = nullptr;
  986. (*m_origInlineeFrameMap) = nullptr;
  987. (*m_origPragmaInstrToRecordOffset) = nullptr;
  988. }
  989. #if DBG_DUMP
  990. for (uint i = 0; i < m_instrNumber; i++)
  991. {
  992. if (!restore)
  993. {
  994. (*m_origOffsetBuffer)->Add(m_offsetBuffer[i]);
  995. }
  996. else
  997. {
  998. m_offsetBuffer[i] = (*m_origOffsetBuffer)->Item(i);
  999. }
  1000. }
  1001. if (restore)
  1002. {
  1003. (*m_origOffsetBuffer)->Delete();
  1004. (*m_origOffsetBuffer) = nullptr;
  1005. }
  1006. #endif
  1007. }
  1008. #endif
  1009. void Encoder::RecordBailout(IR::Instr* instr, uint32 currentOffset)
  1010. {
  1011. BailOutInfo* bailoutInfo = instr->GetBailOutInfo();
  1012. if (bailoutInfo->bailOutRecord == nullptr)
  1013. {
  1014. return;
  1015. }
  1016. #if DBG_DUMP
  1017. if (PHASE_DUMP(Js::LazyBailoutPhase, m_func))
  1018. {
  1019. Output::Print(_u("Offset: %u Instr: "), currentOffset);
  1020. instr->Dump();
  1021. Output::Print(_u("Bailout label: "));
  1022. bailoutInfo->bailOutInstr->Dump();
  1023. }
  1024. #endif
  1025. Assert(bailoutInfo->bailOutInstr->IsLabelInstr());
  1026. LazyBailOutRecord record(currentOffset, (BYTE*)bailoutInfo->bailOutInstr, bailoutInfo->bailOutRecord);
  1027. m_bailoutRecordMap->Add(record);
  1028. }
  1029. #if DBG_DUMP
  1030. void Encoder::DumpInlineeFrameMap(size_t baseAddress)
  1031. {
  1032. Output::Print(_u("Inlinee frame info mapping\n"));
  1033. Output::Print(_u("---------------------------------------\n"));
  1034. m_inlineeFrameMap->Map([=](uint index, NativeOffsetInlineeFramePair& pair) {
  1035. Output::Print(_u("%Ix"), baseAddress + pair.offset);
  1036. Output::SkipToColumn(20);
  1037. if (pair.record)
  1038. {
  1039. pair.record->Dump();
  1040. }
  1041. else
  1042. {
  1043. Output::Print(_u("<NULL>"));
  1044. }
  1045. Output::Print(_u("\n"));
  1046. });
  1047. }
  1048. #endif