WAsmjsUtils.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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 "RuntimeLanguagePch.h"
  6. #if defined(ASMJS_PLAT) || defined(ENABLE_WASM)
  7. #include "InterpreterStackFrame.h"
  8. namespace WAsmJs
  9. {
  10. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  11. namespace Tracing
  12. {
  13. // This can be broken if exception are thrown from wasm frames
  14. int callDepth = 0;
  15. int GetPrintCol()
  16. {
  17. return callDepth;
  18. }
  19. void PrintArgSeparator()
  20. {
  21. Output::Print(_u(", "));
  22. }
  23. void PrintBeginCall()
  24. {
  25. ++callDepth;
  26. Output::Print(_u(") {\n"));
  27. }
  28. void PrintNewLine()
  29. {
  30. Output::Print(_u("\n"));
  31. }
  32. void PrintEndCall(int hasReturn)
  33. {
  34. callDepth = callDepth > 0 ? callDepth - 1 : 0;
  35. Output::Print(_u("%*s}"), GetPrintCol(), _u(""));
  36. if (hasReturn)
  37. {
  38. Output::Print(_u(" = "));
  39. }
  40. }
  41. int PrintI32(int val)
  42. {
  43. Output::Print(_u("%d"), val);
  44. return val;
  45. }
  46. int64 PrintI64(int64 val)
  47. {
  48. Output::Print(_u("%lld"), val);
  49. return val;
  50. }
  51. float PrintF32(float val)
  52. {
  53. Output::Print(_u("%.4f"), val);
  54. return val;
  55. }
  56. double PrintF64(double val)
  57. {
  58. Output::Print(_u("%.4f"), val);
  59. return val;
  60. }
  61. }
  62. #endif
  63. void JitFunctionIfReady(Js::ScriptFunction* func, uint interpretedCount /*= 0*/)
  64. {
  65. #if ENABLE_NATIVE_CODEGEN
  66. Js::FunctionBody* body = func->GetFunctionBody();
  67. if (WAsmJs::ShouldJitFunction(body, interpretedCount))
  68. {
  69. if (PHASE_TRACE(Js::AsmjsEntryPointInfoPhase, body))
  70. {
  71. Output::Print(_u("Scheduling %s For Full JIT at callcount:%d\n"), body->GetDisplayName(), interpretedCount);
  72. }
  73. GenerateFunction(body->GetScriptContext()->GetNativeCodeGenerator(), body, func);
  74. body->SetIsAsmJsFullJitScheduled(true);
  75. }
  76. #endif
  77. }
  78. bool ShouldJitFunction(Js::FunctionBody* body, uint interpretedCount)
  79. {
  80. #if ENABLE_NATIVE_CODEGEN
  81. if (PHASE_OFF(Js::BackEndPhase, body) ||
  82. PHASE_OFF(Js::FullJitPhase, body) ||
  83. body->GetScriptContext()->GetConfig()->IsNoNative() ||
  84. body->GetIsAsmJsFullJitScheduled())
  85. {
  86. return false;
  87. }
  88. #if ENABLE_OOP_NATIVE_CODEGEN
  89. if (JITManager::GetJITManager()->IsOOPJITEnabled() && !JITManager::GetJITManager()->IsConnected())
  90. {
  91. return false;
  92. }
  93. #endif
  94. const bool forceNative = CONFIG_ISENABLED(Js::ForceNativeFlag);
  95. const uint minAsmJsInterpretRunCount = (uint)CONFIG_FLAG(MinAsmJsInterpreterRunCount);
  96. const uint maxAsmJsInterpretRunCount = (uint)CONFIG_FLAG(MaxAsmJsInterpreterRunCount);
  97. return forceNative || interpretedCount >= minAsmJsInterpretRunCount || interpretedCount >= maxAsmJsInterpretRunCount;
  98. #else
  99. return false;
  100. #endif
  101. }
  102. uint32 ConvertOffset(uint32 offset, uint32 fromSize, uint32 toSize)
  103. {
  104. if (fromSize == toSize)
  105. {
  106. return offset;
  107. }
  108. uint64 tmp = (uint64)offset * (uint64)fromSize;
  109. tmp = Math::Align<uint64>(tmp, toSize);
  110. tmp /= (uint64)toSize;
  111. if (tmp > (uint64)UINT32_MAX)
  112. {
  113. Math::DefaultOverflowPolicy();
  114. }
  115. return (uint32)tmp;
  116. }
  117. uint32 GetTypeByteSize(Types type)
  118. {
  119. // Since this needs to be done manually for each type, this assert will make sure to not forget to update this if a new type is added
  120. CompileAssert(WAsmJs::LIMIT == 5);
  121. switch (type)
  122. {
  123. case INT32 : return sizeof(int32);
  124. case INT64 : return sizeof(int64);
  125. case FLOAT32: return sizeof(float);
  126. case FLOAT64: return sizeof(double);
  127. case SIMD : return sizeof(AsmJsSIMDValue);
  128. default : break;
  129. }
  130. Js::Throw::InternalError();
  131. }
  132. WAsmJs::Types FromIRType(IRType irType)
  133. {
  134. switch(irType)
  135. {
  136. case TyInt8:
  137. case TyInt16:
  138. case TyInt32:
  139. case TyUint8:
  140. case TyUint16:
  141. case TyUint32:
  142. return WAsmJs::INT32;
  143. case TyInt64:
  144. case TyUint64:
  145. return WAsmJs::INT64;
  146. case TyFloat32:
  147. return WAsmJs::FLOAT32;
  148. case TyFloat64:
  149. return WAsmJs::FLOAT64;
  150. case TySimd128F4:
  151. case TySimd128I4:
  152. case TySimd128I8:
  153. case TySimd128I16:
  154. case TySimd128U4:
  155. case TySimd128U8:
  156. case TySimd128U16:
  157. case TySimd128B4:
  158. case TySimd128B8:
  159. case TySimd128B16:
  160. case TySimd128D2:
  161. return WAsmJs::SIMD;
  162. }
  163. return WAsmJs::LIMIT;
  164. }
  165. template<> Types FromPrimitiveType<int32>() { return WAsmJs::INT32; }
  166. template<> Types FromPrimitiveType<int64>() { return WAsmJs::INT64; }
  167. template<> Types FromPrimitiveType<float>() { return WAsmJs::FLOAT32; }
  168. template<> Types FromPrimitiveType<double>() { return WAsmJs::FLOAT64; }
  169. template<> Types FromPrimitiveType<AsmJsSIMDValue>() { return WAsmJs::SIMD; }
  170. #if DBG_DUMP
  171. void RegisterSpace::GetTypeDebugName(Types type, char16* buf, uint bufsize, bool shortName)
  172. {
  173. // Since this needs to be done manually for each type, this assert will make sure to not forget to update this if a new type is added
  174. CompileAssert(LIMIT == 5);
  175. switch (type)
  176. {
  177. case INT32: wcscpy_s(buf, bufsize , shortName ? _u("I"): _u("INT32")); break;
  178. case INT64: wcscpy_s(buf, bufsize , shortName ? _u("L"): _u("INT64")); break;
  179. case FLOAT32: wcscpy_s(buf, bufsize, shortName ? _u("F"): _u("FLOAT32")); break;
  180. case FLOAT64: wcscpy_s(buf, bufsize, shortName ? _u("D"): _u("FLOAT64")); break;
  181. case SIMD: wcscpy_s(buf, bufsize , _u("SIMD")); break;
  182. default: wcscpy_s(buf, bufsize , _u("UNKNOWN")); break;
  183. }
  184. }
  185. void RegisterSpace::PrintTmpRegisterAllocation(RegSlot loc, bool deallocation)
  186. {
  187. if (PHASE_TRACE1(Js::AsmjsTmpRegisterAllocationPhase))
  188. {
  189. char16 buf[16];
  190. GetTypeDebugName(mType, buf, 16, true);
  191. Output::Print(_u("%s%s %d\n"), deallocation ? _u("-") : _u("+"), buf, loc);
  192. }
  193. }
  194. #endif
  195. TypedRegisterAllocator::TypedRegisterAllocator(ArenaAllocator* allocator, AllocateRegisterSpaceFunc allocateFunc, uint32 excludedMask/* = 0*/)
  196. {
  197. Assert(allocateFunc);
  198. AssertMsg(excludedMask >> WAsmJs::LIMIT == 0, "Invalid type in the excluded mask");
  199. mExcludedMask = excludedMask;
  200. for (int i = 0; i < WAsmJs::LIMIT; ++i)
  201. {
  202. Types type = (Types)i;
  203. mTypeSpaces[i] = nullptr;
  204. if (!IsTypeExcluded(type))
  205. {
  206. mTypeSpaces[i] = allocateFunc(allocator, type);
  207. #if DBG_DUMP
  208. mTypeSpaces[i]->mType = type;
  209. #endif
  210. }
  211. }
  212. }
  213. void TypedRegisterAllocator::CommitToFunctionInfo(Js::AsmJsFunctionInfo* funcInfo, Js::FunctionBody* body) const
  214. {
  215. uint32 offset = Js::AsmJsFunctionMemory::RequiredVarConstants * sizeof(Js::Var);
  216. WAsmJs::TypedConstSourcesInfo constSourcesInfo = GetConstSourceInfos();
  217. #if DBG_DUMP
  218. if (PHASE_TRACE(Js::AsmjsInterpreterStackPhase, body))
  219. {
  220. Output::Print(_u("ASMFunctionInfo Stack Data for %s\n"), body->GetDisplayName());
  221. Output::Print(_u("==========================\n"));
  222. Output::Print(_u("RequiredVarConstants:%d\n"), Js::AsmJsFunctionMemory::RequiredVarConstants);
  223. }
  224. #endif
  225. for (int i = 0; i < WAsmJs::LIMIT; ++i)
  226. {
  227. Types type = (Types)i;
  228. TypedSlotInfo& slotInfo = *funcInfo->GetTypedSlotInfo(type);
  229. // Check if we don't want to commit this type
  230. if (!IsTypeExcluded(type))
  231. {
  232. RegisterSpace* registerSpace = GetRegisterSpace(type);
  233. slotInfo.constCount = registerSpace->GetConstCount();
  234. slotInfo.varCount = registerSpace->GetVarCount();
  235. slotInfo.tmpCount = registerSpace->GetTmpCount();
  236. slotInfo.constSrcByteOffset = constSourcesInfo.srcByteOffsets[i];
  237. offset = Math::AlignOverflowCheck(offset, GetTypeByteSize(type));
  238. slotInfo.byteOffset = offset;
  239. // Update offset for next type
  240. uint32 totalTypeCount = 0;
  241. totalTypeCount = UInt32Math::Add(totalTypeCount, slotInfo.constCount);
  242. totalTypeCount = UInt32Math::Add(totalTypeCount, slotInfo.varCount);
  243. totalTypeCount = UInt32Math::Add(totalTypeCount, slotInfo.tmpCount);
  244. offset = UInt32Math::Add(offset, UInt32Math::Mul(totalTypeCount, GetTypeByteSize(type)));
  245. #if DBG_DUMP
  246. if (PHASE_TRACE(Js::AsmjsInterpreterStackPhase, body))
  247. {
  248. char16 buf[16];
  249. RegisterSpace::GetTypeDebugName(type, buf, 16);
  250. Output::Print(_u("%s Offset:%d ConstCount:%d VarCount:%d TmpCount:%d = %d * %d = 0x%x bytes\n"),
  251. buf,
  252. slotInfo.byteOffset,
  253. slotInfo.constCount,
  254. slotInfo.varCount,
  255. slotInfo.tmpCount,
  256. totalTypeCount,
  257. GetTypeByteSize(type),
  258. totalTypeCount * GetTypeByteSize(type));
  259. }
  260. #endif
  261. }
  262. }
  263. // The offset currently carries the total size of the funcInfo after handling the last type
  264. funcInfo->SetTotalSizeinBytes(offset);
  265. // These bytes offset already calculated the alignment, used them to determine how many Js::Var we need to do the allocation
  266. uint32 stackByteSize = offset;
  267. uint32 bytesUsedForConst = constSourcesInfo.bytesUsed;
  268. uint32 jsVarUsedForConstsTable = ConvertOffset<byte, Js::Var>(bytesUsedForConst);
  269. uint32 totalVarsNeeded = ConvertOffset<byte, Js::Var>(stackByteSize);
  270. uint32 jsVarNeededForVars = totalVarsNeeded - jsVarUsedForConstsTable;
  271. if (totalVarsNeeded < jsVarUsedForConstsTable)
  272. {
  273. // If for some reason we allocated more space in the const table than what we need, just don't allocate anymore vars
  274. jsVarNeededForVars = 0;
  275. }
  276. Assert((jsVarUsedForConstsTable + jsVarNeededForVars) >= totalVarsNeeded);
  277. body->CheckAndSetVarCount(jsVarNeededForVars);
  278. #if DBG_DUMP
  279. if (PHASE_TRACE(Js::AsmjsInterpreterStackPhase, body))
  280. {
  281. Output::Print(_u("Total memory required: (%u consts + %u vars) * sizeof(Js::Var) >= %u * sizeof(Js::Var) = 0x%x bytes\n"),
  282. jsVarUsedForConstsTable,
  283. jsVarNeededForVars,
  284. totalVarsNeeded,
  285. stackByteSize);
  286. }
  287. #endif
  288. }
  289. void TypedRegisterAllocator::CommitToFunctionBody(Js::FunctionBody* body)
  290. {
  291. // this value is the number of Var slots needed to allocate all the const
  292. uint32 bytesUsedForConst = GetConstSourceInfos().bytesUsed;
  293. // Add the registers not included in the const table
  294. uint32 nbConst = ConvertOffset<byte, Js::Var>(bytesUsedForConst) + Js::FunctionBody::FirstRegSlot;
  295. body->CheckAndSetConstantCount(nbConst);
  296. }
  297. WAsmJs::TypedConstSourcesInfo TypedRegisterAllocator::GetConstSourceInfos() const
  298. {
  299. WAsmJs::TypedConstSourcesInfo infos;
  300. // The const table doesn't contain the first reg slots (aka return register)
  301. uint32 offset = ConvertOffset<Js::Var, byte>(Js::AsmJsFunctionMemory::RequiredVarConstants - Js::FunctionBody::FirstRegSlot);
  302. for (int i = 0; i < WAsmJs::LIMIT; ++i)
  303. {
  304. Types type = (Types)i;
  305. if (!IsTypeExcluded(type))
  306. {
  307. infos.srcByteOffsets[i] = offset;
  308. uint32 typeSize = GetTypeByteSize(type);
  309. uint32 constCount = GetRegisterSpace(type)->GetConstCount();
  310. uint32 typeBytesUsage = ConvertOffset<byte>(constCount, typeSize);
  311. offset = Math::AlignOverflowCheck(offset, typeSize);
  312. offset = UInt32Math::Add(offset, typeBytesUsage);
  313. }
  314. else
  315. {
  316. infos.srcByteOffsets[i] = (uint32)Js::Constants::InvalidOffset;
  317. }
  318. }
  319. infos.bytesUsed = offset;
  320. return infos;
  321. }
  322. bool TypedRegisterAllocator::IsValidType(Types type) const
  323. {
  324. return (uint)type < WAsmJs::LIMIT;
  325. }
  326. bool TypedRegisterAllocator::IsTypeExcluded(Types type) const
  327. {
  328. return !IsValidType(type) || (mExcludedMask & (1 << type)) != 0;
  329. }
  330. #if DBG_DUMP
  331. void TypedRegisterAllocator::DumpLocalsInfo() const
  332. {
  333. for (int i = 0; i < WAsmJs::LIMIT; ++i)
  334. {
  335. Types type = (Types)i;
  336. if (!IsTypeExcluded(type))
  337. {
  338. char16 typeName[16];
  339. char16 shortTypeName[16];
  340. RegisterSpace::GetTypeDebugName(type, typeName, 16);
  341. RegisterSpace::GetTypeDebugName(type, shortTypeName, 16, true);
  342. RegisterSpace* registerSpace = GetRegisterSpace(type);
  343. Output::Print(
  344. _u(" %-10s : %u locals (%u temps from %s%u)\n"),
  345. typeName,
  346. registerSpace->GetVarCount(),
  347. registerSpace->GetTmpCount(),
  348. shortTypeName,
  349. registerSpace->GetFirstTmpRegister());
  350. }
  351. }
  352. }
  353. void TypedRegisterAllocator::GetArgumentStartIndex(uint32* indexes) const
  354. {
  355. for (int i = 0; i < WAsmJs::LIMIT; ++i)
  356. {
  357. Types type = (Types)i;
  358. if (!IsTypeExcluded(type))
  359. {
  360. // Arguments starts right after the consts
  361. indexes[i] = GetRegisterSpace(type)->GetConstCount();
  362. }
  363. }
  364. }
  365. #endif
  366. RegisterSpace* TypedRegisterAllocator::GetRegisterSpace(Types type) const
  367. {
  368. if (!IsValidType(type))
  369. {
  370. Assert("Invalid type for RegisterSpace in TypedMemoryStructure");
  371. Js::Throw::InternalError();
  372. }
  373. Assert(!IsTypeExcluded(type));
  374. return mTypeSpaces[type];
  375. }
  376. };
  377. #endif