InlineeFrameInfo.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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. this->inlineeStartOffset = currentOffset;
  157. this->inlineDepth = inlinee->inlineDepth;
  158. #ifdef MD_GROW_LOCALS_AREA_UP
  159. Func* topFunc = inlinee->GetTopFunc();
  160. int32 inlineeArgStackSize = topFunc->GetInlineeArgumentStackSize();
  161. int localsSize = topFunc->m_localStackHeight + topFunc->m_ArgumentsOffset;
  162. this->MapOffsets([=](int& offset)
  163. {
  164. int realOffset = -(offset + BailOutInfo::StackSymBias);
  165. if (realOffset < 0)
  166. {
  167. // Not stack offset
  168. return;
  169. }
  170. // The locals size contains the inlined-arg-area size, so remove the inlined-arg-area size from the
  171. // adjustment for normal locals whose offsets are relative to the start of the locals area.
  172. realOffset -= (localsSize - inlineeArgStackSize);
  173. offset = realOffset;
  174. });
  175. #endif
  176. Assert(this->inlineDepth != 0);
  177. }
  178. void InlineeFrameRecord::Restore(Js::FunctionBody* functionBody, InlinedFrameLayout *inlinedFrame, Js::JavascriptCallStackLayout * layout) const
  179. {
  180. Assert(this->inlineDepth != 0);
  181. Assert(inlineeStartOffset != 0);
  182. BAILOUT_VERBOSE_TRACE(functionBody, _u("Restore function object: "));
  183. Js::Var varFunction = this->Restore(this->functionOffset, /*isFloat64*/ false, /*isInt32*/ false, layout, functionBody);
  184. Assert(Js::ScriptFunction::Is(varFunction));
  185. Js::ScriptFunction* function = Js::ScriptFunction::FromVar(varFunction);
  186. BAILOUT_VERBOSE_TRACE(functionBody, _u("Inlinee: %s [%d.%d] \n"), function->GetFunctionBody()->GetDisplayName(), function->GetFunctionBody()->GetSourceContextId(), function->GetFunctionBody()->GetLocalFunctionId());
  187. inlinedFrame->function = function;
  188. inlinedFrame->callInfo.InlineeStartOffset = inlineeStartOffset;
  189. inlinedFrame->callInfo.Count = this->argCount;
  190. inlinedFrame->MapArgs([=](uint i, Js::Var* varRef) {
  191. bool isFloat64 = floatArgs.Test(i) != 0;
  192. bool isInt32 = losslessInt32Args.Test(i) != 0;
  193. BAILOUT_VERBOSE_TRACE(functionBody, _u("Restore argument %d: "), i);
  194. Js::Var var = this->Restore(this->argOffsets[i], isFloat64, isInt32, layout, functionBody);
  195. #if DBG
  196. if (!Js::TaggedNumber::Is(var))
  197. {
  198. Js::RecyclableObject *const recyclableObject = Js::RecyclableObject::FromVar(var);
  199. Assert(!ThreadContext::IsOnStack(recyclableObject));
  200. }
  201. #endif
  202. *varRef = var;
  203. });
  204. inlinedFrame->arguments = nullptr;
  205. BAILOUT_FLUSH(functionBody);
  206. }
  207. void InlineeFrameRecord::RestoreFrames(Js::FunctionBody* functionBody, InlinedFrameLayout* outerMostFrame, Js::JavascriptCallStackLayout* callstack)
  208. {
  209. InlineeFrameRecord* innerMostRecord = this;
  210. class AutoReverse
  211. {
  212. public:
  213. InlineeFrameRecord* record;
  214. AutoReverse(InlineeFrameRecord* record)
  215. {
  216. this->record = record->Reverse();
  217. }
  218. ~AutoReverse()
  219. {
  220. record->Reverse();
  221. }
  222. } autoReverse(innerMostRecord);
  223. InlineeFrameRecord* currentRecord = autoReverse.record;
  224. InlinedFrameLayout* currentFrame = outerMostFrame;
  225. int inlineDepth = 1;
  226. // Find an inlined frame that needs to be restored.
  227. while (currentFrame->callInfo.Count != 0)
  228. {
  229. inlineDepth++;
  230. currentFrame = currentFrame->Next();
  231. }
  232. // Align the inline depth of the record with the frame that needs to be restored
  233. while (currentRecord && currentRecord->inlineDepth != inlineDepth)
  234. {
  235. currentRecord = currentRecord->parent;
  236. }
  237. while (currentRecord)
  238. {
  239. currentRecord->Restore(functionBody, currentFrame, callstack);
  240. currentRecord = currentRecord->parent;
  241. currentFrame = currentFrame->Next();
  242. }
  243. // Terminate the inlined stack
  244. currentFrame->callInfo.Count = 0;
  245. }
  246. Js::Var InlineeFrameRecord::Restore(int offset, bool isFloat64, bool isInt32, Js::JavascriptCallStackLayout * layout, Js::FunctionBody* functionBody) const
  247. {
  248. Js::Var value;
  249. bool boxStackInstance = true;
  250. double dblValue;
  251. if (offset >= 0)
  252. {
  253. Assert(static_cast<uint>(offset) < constantCount);
  254. value = this->constants[offset];
  255. boxStackInstance = false;
  256. }
  257. else
  258. {
  259. BAILOUT_VERBOSE_TRACE(functionBody, _u("Stack offset %10d"), offset);
  260. if (isFloat64)
  261. {
  262. dblValue = layout->GetDoubleAtOffset(offset);
  263. value = Js::JavascriptNumber::New(dblValue, functionBody->GetScriptContext());
  264. BAILOUT_VERBOSE_TRACE(functionBody, _u(", value: %f (ToVar: 0x%p)"), dblValue, value);
  265. }
  266. else if (isInt32)
  267. {
  268. value = (Js::Var)layout->GetInt32AtOffset(offset);
  269. }
  270. else
  271. {
  272. value = layout->GetOffset(offset);
  273. }
  274. }
  275. if (isInt32)
  276. {
  277. int32 int32Value = ::Math::PointerCastToIntegralTruncate<int32>(value);
  278. value = Js::JavascriptNumber::ToVar(int32Value, functionBody->GetScriptContext());
  279. BAILOUT_VERBOSE_TRACE(functionBody, _u(", value: %10d (ToVar: 0x%p)"), int32Value, value);
  280. }
  281. else
  282. {
  283. BAILOUT_VERBOSE_TRACE(functionBody, _u(", value: 0x%p"), value);
  284. if (boxStackInstance)
  285. {
  286. Js::Var oldValue = value;
  287. value = Js::JavascriptOperators::BoxStackInstance(oldValue, functionBody->GetScriptContext(), /* allowStackFunction */ true);
  288. #if ENABLE_DEBUG_CONFIG_OPTIONS
  289. if (oldValue != value)
  290. {
  291. BAILOUT_VERBOSE_TRACE(functionBody, _u(" (Boxed: 0x%p)"), value);
  292. }
  293. #endif
  294. }
  295. }
  296. BAILOUT_VERBOSE_TRACE(functionBody, _u("\n"));
  297. return value;
  298. }
  299. InlineeFrameRecord* InlineeFrameRecord::Reverse()
  300. {
  301. InlineeFrameRecord * prev = nullptr;
  302. InlineeFrameRecord * current = this;
  303. while (current)
  304. {
  305. InlineeFrameRecord * next = current->parent;
  306. current->parent = prev;
  307. prev = current;
  308. current = next;
  309. }
  310. return prev;
  311. }
  312. #if DBG_DUMP
  313. void InlineeFrameRecord::Dump() const
  314. {
  315. Output::Print(_u("%s [#%u.%u] args:"), this->functionBody->GetExternalDisplayName(), this->functionBody->GetSourceContextId(), this->functionBody->GetLocalFunctionId());
  316. for (uint i = 0; i < argCount; i++)
  317. {
  318. DumpOffset(argOffsets[i]);
  319. if (floatArgs.Test(i))
  320. {
  321. Output::Print(_u("f "));
  322. }
  323. else if (losslessInt32Args.Test(i))
  324. {
  325. Output::Print(_u("i "));
  326. }
  327. Output::Print(_u(", "));
  328. }
  329. this->frameInfo->Dump();
  330. Output::Print(_u("func: "));
  331. DumpOffset(functionOffset);
  332. if (this->parent)
  333. {
  334. parent->Dump();
  335. }
  336. }
  337. void InlineeFrameRecord::DumpOffset(int offset) const
  338. {
  339. if (offset >= 0)
  340. {
  341. Output::Print(_u("%p "), constants[offset]);
  342. }
  343. else
  344. {
  345. Output::Print(_u("<%d> "), offset);
  346. }
  347. }
  348. void InlineeFrameInfo::Dump() const
  349. {
  350. Output::Print(_u("func: "));
  351. if (this->function.type == InlineeFrameInfoValueType_Const)
  352. {
  353. Output::Print(_u("%p(Var) "), this->function.constValue);
  354. }
  355. else if (this->function.type == InlineeFrameInfoValueType_Sym)
  356. {
  357. this->function.sym->Dump();
  358. Output::Print(_u(" "));
  359. }
  360. Output::Print(_u("args: "));
  361. arguments->Map([=](uint i, InlineFrameInfoValue& value)
  362. {
  363. if (value.type == InlineeFrameInfoValueType_Const)
  364. {
  365. Output::Print(_u("%p(Var) "), value.constValue);
  366. }
  367. else if (value.type == InlineeFrameInfoValueType_Sym)
  368. {
  369. value.sym->Dump();
  370. Output::Print(_u(" "));
  371. }
  372. Output::Print(_u(", "));
  373. });
  374. }
  375. #endif