InlineeFrameInfo.cpp 13 KB

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