Encoder.cpp 49 KB

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