InlineeFrameInfo.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  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. #if ENABLE_DEBUG_CONFIG_OPTIONS
  7. #define BAILOUT_VERBOSE_TRACE(functionBody, ...) \
  8. if (Js::Configuration::Global.flags.Verbose && Js::Configuration::Global.flags.Trace.IsEnabled(Js::BailOutPhase,functionBody->GetSourceContextId(),functionBody->GetLocalFunctionId())) \
  9. { \
  10. Output::Print(__VA_ARGS__); \
  11. }
  12. #define BAILOUT_FLUSH(functionBody) \
  13. if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::BailOutPhase, functionBody->GetSourceContextId(),functionBody->GetLocalFunctionId()) || \
  14. Js::Configuration::Global.flags.Trace.IsEnabled(Js::BailOutPhase, functionBody->GetSourceContextId(),functionBody->GetLocalFunctionId())) \
  15. { \
  16. Output::Flush(); \
  17. }
  18. #else
  19. #define BAILOUT_VERBOSE_TRACE(functionBody, bailOutKind, ...)
  20. #define BAILOUT_FLUSH(functionBody)
  21. #endif
  22. unsigned int NativeOffsetInlineeFrameRecordOffset::InvalidRecordOffset = (unsigned int)(-1);
  23. void BailoutConstantValue::InitVarConstValue(Js::Var value)
  24. {
  25. this->type = TyVar;
  26. this->u.varConst.value = value;
  27. }
  28. Js::Var BailoutConstantValue::ToVar(Func* func) const
  29. {
  30. Assert(this->type == TyVar || this->type == TyFloat64 || IRType_IsSignedInt(this->type));
  31. Js::Var varValue;
  32. if (this->type == TyVar)
  33. {
  34. varValue = this->u.varConst.value;
  35. }
  36. else if (this->type == TyFloat64)
  37. {
  38. varValue = func->AllocateNumber((double)this->u.floatConst.value);
  39. }
  40. else if (IRType_IsSignedInt(this->type) && TySize[this->type] <= 4 && !Js::TaggedInt::IsOverflow((int32)this->u.intConst.value))
  41. {
  42. varValue = Js::TaggedInt::ToVarUnchecked((int32)this->u.intConst.value);
  43. }
  44. else
  45. {
  46. varValue = func->AllocateNumber((double)this->u.intConst.value);
  47. }
  48. return varValue;
  49. }
  50. bool BailoutConstantValue::IsEqual(const BailoutConstantValue & bailoutConstValue)
  51. {
  52. if (this->type == bailoutConstValue.type)
  53. {
  54. if (this->type == TyInt32)
  55. {
  56. return this->u.intConst.value == bailoutConstValue.u.intConst.value;
  57. }
  58. else if (this->type == TyVar)
  59. {
  60. return this->u.varConst.value == bailoutConstValue.u.varConst.value;
  61. }
  62. else
  63. {
  64. return this->u.floatConst.value == bailoutConstValue.u.floatConst.value;
  65. }
  66. }
  67. return false;
  68. }
  69. void InlineeFrameInfo::AllocateRecord(Func* func, intptr_t functionBodyAddr)
  70. {
  71. uint constantCount = 0;
  72. // If there are no helper calls there is a chance that frame record is not required after all;
  73. arguments->Map([&](uint index, InlineFrameInfoValue& value){
  74. if (value.IsConst())
  75. {
  76. constantCount++;
  77. }
  78. });
  79. if (function.IsConst())
  80. {
  81. constantCount++;
  82. }
  83. // For InlineeEnd's that have been cloned we can result in multiple calls
  84. // to allocate the record - do not allocate a new record - instead update the existing one.
  85. // In particular, if the first InlineeEnd resulted in no calls and spills, subsequent might still spill - so it's a good idea to
  86. // update the record
  87. if (!this->record)
  88. {
  89. this->record = InlineeFrameRecord::New(func->GetNativeCodeDataAllocator(), (uint)arguments->Count(), constantCount, functionBodyAddr, this);
  90. }
  91. uint i = 0;
  92. uint constantIndex = 0;
  93. arguments->Map([&](uint index, InlineFrameInfoValue& value){
  94. Assert(value.type != InlineeFrameInfoValueType_None);
  95. if (value.type == InlineeFrameInfoValueType_Sym)
  96. {
  97. int offset;
  98. #ifdef MD_GROW_LOCALS_AREA_UP
  99. offset = -((int)value.sym->m_offset + BailOutInfo::StackSymBias);
  100. #else
  101. // Stack offset are negative, includes the PUSH EBP and return address
  102. offset = value.sym->m_offset - (2 * MachPtr);
  103. #endif
  104. Assert(offset < 0);
  105. this->record->argOffsets[i] = offset;
  106. if (value.sym->IsFloat64())
  107. {
  108. this->record->floatArgs.Set(i);
  109. }
  110. else if (value.sym->IsInt32())
  111. {
  112. this->record->losslessInt32Args.Set(i);
  113. }
  114. }
  115. else
  116. {
  117. // Constants
  118. Assert(constantIndex < constantCount);
  119. this->record->constants[constantIndex] = value.constValue.ToVar(func);
  120. this->record->argOffsets[i] = constantIndex;
  121. constantIndex++;
  122. }
  123. i++;
  124. });
  125. if (function.type == InlineeFrameInfoValueType_Sym)
  126. {
  127. int offset;
  128. #ifdef MD_GROW_LOCALS_AREA_UP
  129. offset = -((int)function.sym->m_offset + BailOutInfo::StackSymBias);
  130. #else
  131. // Stack offset are negative, includes the PUSH EBP and return address
  132. offset = function.sym->m_offset - (2 * MachPtr);
  133. #endif
  134. this->record->functionOffset = offset;
  135. }
  136. else
  137. {
  138. Assert(constantIndex < constantCount);
  139. this->record->constants[constantIndex] = function.constValue.ToVar(func);
  140. this->record->functionOffset = constantIndex;
  141. }
  142. }
  143. void InlineeFrameRecord::PopulateParent(Func* func)
  144. {
  145. Assert(this->parent == nullptr);
  146. Assert(!func->IsTopFunc());
  147. if (func->GetParentFunc()->m_hasInlineArgsOpt)
  148. {
  149. this->parent = func->GetParentFunc()->frameInfo->record;
  150. Assert(this->parent != nullptr);
  151. }
  152. }
  153. void InlineeFrameRecord::Finalize(Func* inlinee, uint32 currentOffset)
  154. {
  155. this->PopulateParent(inlinee);
  156. #if TARGET_32
  157. const uint32 offsetMask = (~(uint32)0) >> (sizeof(uint) * CHAR_BIT - Js::InlineeCallInfo::ksizeofInlineeStartOffset);
  158. AssertOrFailFast(currentOffset == (currentOffset & offsetMask));
  159. #endif
  160. this->inlineeStartOffset = currentOffset;
  161. this->inlineDepth = inlinee->inlineDepth;
  162. #ifdef MD_GROW_LOCALS_AREA_UP
  163. Func* topFunc = inlinee->GetTopFunc();
  164. int32 inlineeArgStackSize = topFunc->GetInlineeArgumentStackSize();
  165. int localsSize = topFunc->m_localStackHeight + topFunc->m_ArgumentsOffset;
  166. this->MapOffsets([=](int& offset)
  167. {
  168. int realOffset = -(offset + BailOutInfo::StackSymBias);
  169. if (realOffset < 0)
  170. {
  171. // Not stack offset
  172. return;
  173. }
  174. // The locals size contains the inlined-arg-area size, so remove the inlined-arg-area size from the
  175. // adjustment for normal locals whose offsets are relative to the start of the locals area.
  176. realOffset -= (localsSize - inlineeArgStackSize);
  177. offset = realOffset;
  178. });
  179. #endif
  180. Assert(this->inlineDepth != 0);
  181. }
  182. void InlineeFrameRecord::Restore(Js::FunctionBody* functionBody, InlinedFrameLayout *inlinedFrame, Js::JavascriptCallStackLayout * layout, bool boxValues) const
  183. {
  184. Assert(this->inlineDepth != 0);
  185. Assert(inlineeStartOffset != 0);
  186. BAILOUT_VERBOSE_TRACE(functionBody, _u("Restore function object: "));
  187. // No deepCopy needed for just the function
  188. Js::Var varFunction = this->Restore(this->functionOffset, /*isFloat64*/ false, /*isInt32*/ false, layout, functionBody, boxValues);
  189. Assert(Js::VarIs<Js::ScriptFunction>(varFunction));
  190. Js::ScriptFunction* function = Js::VarTo<Js::ScriptFunction>(varFunction);
  191. BAILOUT_VERBOSE_TRACE(functionBody, _u("Inlinee: %s [%d.%d] \n"), function->GetFunctionBody()->GetDisplayName(), function->GetFunctionBody()->GetSourceContextId(), function->GetFunctionBody()->GetLocalFunctionId());
  192. inlinedFrame->function = function;
  193. inlinedFrame->callInfo.InlineeStartOffset = inlineeStartOffset;
  194. inlinedFrame->callInfo.Count = this->argCount;
  195. inlinedFrame->MapArgs([=](uint i, Js::Var* varRef) {
  196. bool isFloat64 = floatArgs.Test(i) != 0;
  197. bool isInt32 = losslessInt32Args.Test(i) != 0;
  198. BAILOUT_VERBOSE_TRACE(functionBody, _u("Restore argument %d: "), i);
  199. // Forward deepCopy flag for the arguments in case their data must be guaranteed
  200. // to have its own lifetime
  201. Js::Var var = this->Restore(this->argOffsets[i], isFloat64, isInt32, layout, functionBody, boxValues);
  202. #if DBG
  203. if (boxValues && !Js::TaggedNumber::Is(var))
  204. {
  205. Js::RecyclableObject *const recyclableObject = Js::VarTo<Js::RecyclableObject>(var);
  206. Assert(!ThreadContext::IsOnStack(recyclableObject));
  207. }
  208. #endif
  209. *varRef = var;
  210. });
  211. inlinedFrame->arguments = nullptr;
  212. BAILOUT_FLUSH(functionBody);
  213. }
  214. // Note: the boxValues parameter should be true when this is called from a Bailout codepath to ensure that multiple vars to
  215. // the same object reuse the cached value during the transition to the interpreter.
  216. // Otherwise, this parameter should be false as the values are not required to be moved to the heap to restore the frame.
  217. void InlineeFrameRecord::RestoreFrames(Js::FunctionBody* functionBody, InlinedFrameLayout* outerMostFrame, Js::JavascriptCallStackLayout* callstack, bool boxValues, InlinedFrameLayout **ppInlinedFrameToRestore, Js::ArgSlot *pClearedCallInfoCount)
  218. {
  219. InlineeFrameRecord* innerMostRecord = this;
  220. class AutoReverse
  221. {
  222. public:
  223. InlineeFrameRecord* record;
  224. AutoReverse(InlineeFrameRecord* record)
  225. {
  226. this->record = record->Reverse();
  227. }
  228. ~AutoReverse()
  229. {
  230. record->Reverse();
  231. }
  232. } autoReverse(innerMostRecord);
  233. InlineeFrameRecord* currentRecord = autoReverse.record;
  234. InlinedFrameLayout* currentFrame = outerMostFrame;
  235. int inlineDepth = 1;
  236. // Find an inlined frame that needs to be restored.
  237. while (currentFrame->callInfo.Count != 0)
  238. {
  239. inlineDepth++;
  240. currentFrame = currentFrame->Next();
  241. }
  242. // Align the inline depth of the record with the frame that needs to be restored
  243. while (currentRecord && currentRecord->inlineDepth != inlineDepth)
  244. {
  245. currentRecord = currentRecord->parent;
  246. }
  247. while (currentRecord)
  248. {
  249. currentRecord->Restore(functionBody, currentFrame, callstack, boxValues);
  250. currentRecord = currentRecord->parent;
  251. currentFrame = currentFrame->Next();
  252. }
  253. // Terminate the inlined stack
  254. if (ppInlinedFrameToRestore)
  255. {
  256. // Tell the caller that we need to restore this frame's callinfo count before using it to create an interpreter instance
  257. *ppInlinedFrameToRestore = currentFrame;
  258. *pClearedCallInfoCount = currentFrame->callInfo.Count;
  259. }
  260. currentFrame->callInfo.Count = 0;
  261. }
  262. Js::Var InlineeFrameRecord::Restore(int offset, bool isFloat64, bool isInt32, Js::JavascriptCallStackLayout * layout, Js::FunctionBody* functionBody, bool boxValue) const
  263. {
  264. Js::Var value;
  265. bool boxStackInstance = boxValue;
  266. double dblValue;
  267. if (offset >= 0)
  268. {
  269. Assert(static_cast<uint>(offset) < constantCount);
  270. value = this->constants[offset];
  271. boxStackInstance = false;
  272. }
  273. else
  274. {
  275. BAILOUT_VERBOSE_TRACE(functionBody, _u("Stack offset %10d"), offset);
  276. if (isFloat64)
  277. {
  278. dblValue = layout->GetDoubleAtOffset(offset);
  279. value = Js::JavascriptNumber::New(dblValue, functionBody->GetScriptContext());
  280. BAILOUT_VERBOSE_TRACE(functionBody, _u(", value: %f (ToVar: 0x%p)"), dblValue, value);
  281. }
  282. else if (isInt32)
  283. {
  284. value = (Js::Var)layout->GetInt32AtOffset(offset);
  285. }
  286. else
  287. {
  288. value = layout->GetOffset(offset);
  289. }
  290. }
  291. if (isInt32)
  292. {
  293. int32 int32Value = ::Math::PointerCastToIntegralTruncate<int32>(value);
  294. value = Js::JavascriptNumber::ToVar(int32Value, functionBody->GetScriptContext());
  295. BAILOUT_VERBOSE_TRACE(functionBody, _u(", value: %10d (ToVar: 0x%p)"), int32Value, value);
  296. }
  297. else
  298. {
  299. BAILOUT_VERBOSE_TRACE(functionBody, _u(", value: 0x%p"), value);
  300. if (boxStackInstance)
  301. {
  302. // Do not deepCopy in this call to BoxStackInstance because this should be used for
  303. // bailing out, where a shallow copy that is cached is needed to ensure that multiple
  304. // vars pointing to the same boxed object reuse the new boxed value.
  305. Js::Var oldValue = value;
  306. value = Js::JavascriptOperators::BoxStackInstance(oldValue, functionBody->GetScriptContext(), /* allowStackFunction */ true, false /* deepCopy */);
  307. #if ENABLE_DEBUG_CONFIG_OPTIONS
  308. if (oldValue != value)
  309. {
  310. BAILOUT_VERBOSE_TRACE(functionBody, _u(" (Boxed: 0x%p)"), value);
  311. }
  312. #endif
  313. }
  314. }
  315. BAILOUT_VERBOSE_TRACE(functionBody, _u("\n"));
  316. return value;
  317. }
  318. InlineeFrameRecord* InlineeFrameRecord::Reverse()
  319. {
  320. InlineeFrameRecord * prev = nullptr;
  321. InlineeFrameRecord * current = this;
  322. while (current)
  323. {
  324. InlineeFrameRecord * next = current->parent;
  325. current->parent = prev;
  326. prev = current;
  327. current = next;
  328. }
  329. return prev;
  330. }
  331. #if DBG_DUMP
  332. void InlineeFrameRecord::Dump() const
  333. {
  334. Output::Print(_u("%s [#%u.%u] args:"), this->functionBody->GetExternalDisplayName(), this->functionBody->GetSourceContextId(), this->functionBody->GetLocalFunctionId());
  335. for (uint i = 0; i < argCount; i++)
  336. {
  337. DumpOffset(argOffsets[i]);
  338. if (floatArgs.Test(i))
  339. {
  340. Output::Print(_u("f "));
  341. }
  342. else if (losslessInt32Args.Test(i))
  343. {
  344. Output::Print(_u("i "));
  345. }
  346. Output::Print(_u(", "));
  347. }
  348. this->frameInfo->Dump();
  349. Output::Print(_u("func: "));
  350. DumpOffset(functionOffset);
  351. if (this->parent)
  352. {
  353. parent->Dump();
  354. }
  355. }
  356. void InlineeFrameRecord::DumpOffset(int offset) const
  357. {
  358. if (offset >= 0)
  359. {
  360. Output::Print(_u("%p "), constants[offset]);
  361. }
  362. else
  363. {
  364. Output::Print(_u("<%d> "), offset);
  365. }
  366. }
  367. void InlineeFrameInfo::Dump() const
  368. {
  369. Output::Print(_u("func: "));
  370. if (this->function.type == InlineeFrameInfoValueType_Const)
  371. {
  372. Output::Print(_u("%p(Var) "), this->function.constValue);
  373. }
  374. else if (this->function.type == InlineeFrameInfoValueType_Sym)
  375. {
  376. this->function.sym->Dump();
  377. Output::Print(_u(" "));
  378. }
  379. Output::Print(_u("args: "));
  380. arguments->Map([=](uint i, InlineFrameInfoValue& value)
  381. {
  382. if (value.type == InlineeFrameInfoValueType_Const)
  383. {
  384. Output::Print(_u("%p(Var) "), value.constValue);
  385. }
  386. else if (value.type == InlineeFrameInfoValueType_Sym)
  387. {
  388. value.sym->Dump();
  389. Output::Print(_u(" "));
  390. }
  391. Output::Print(_u(", "));
  392. });
  393. }
  394. #endif