WasmByteCodeGenerator.cpp 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "WasmReaderPch.h"
  6. #ifdef ENABLE_WASM
  7. #include "Language/WebAssemblySource.h"
  8. #include "ByteCode/WasmByteCodeWriter.h"
  9. #include "EmptyWasmByteCodeWriter.h"
  10. #if DBG_DUMP
  11. uint32 opId = 0;
  12. uint32 lastOpId = 1;
  13. #define DebugPrintOp(op) if (PHASE_TRACE(Js::WasmReaderPhase, GetFunctionBody())) { PrintOpBegin(op); }
  14. #define DebugPrintOpEnd() if (PHASE_TRACE(Js::WasmReaderPhase, GetFunctionBody())) { PrintOpEnd(); }
  15. #else
  16. #define DebugPrintOp(op)
  17. #define DebugPrintOpEnd()
  18. #endif
  19. namespace Wasm
  20. {
  21. #define WASM_SIGNATURE(id, nTypes, ...) const WasmTypes::WasmType WasmOpCodeSignatures::id[] = {__VA_ARGS__};
  22. #include "WasmBinaryOpCodes.h"
  23. #if DBG_DUMP
  24. void
  25. PrintTypeStack(const JsUtil::Stack<EmitInfo>& stack)
  26. {
  27. Output::Print(_u("["));
  28. int i = 0;
  29. while (stack.Peek(i).type != WasmTypes::Limit)
  30. {
  31. ++i;
  32. }
  33. --i;
  34. bool isFirst = true;
  35. while (i >= 0)
  36. {
  37. EmitInfo info = stack.Peek(i--);
  38. if (!isFirst)
  39. {
  40. Output::Print(_u(", "));
  41. }
  42. isFirst = false;
  43. switch (info.type)
  44. {
  45. case WasmTypes::I32: Output::Print(_u("i32")); break;
  46. case WasmTypes::I64: Output::Print(_u("i64")); break;
  47. case WasmTypes::F32: Output::Print(_u("f32")); break;
  48. case WasmTypes::F64: Output::Print(_u("f64")); break;
  49. default: Output::Print(_u("any")); break;
  50. }
  51. }
  52. Output::Print(_u("]"));
  53. }
  54. void
  55. WasmBytecodeGenerator::PrintOpBegin(WasmOp op) const
  56. {
  57. if (lastOpId == opId) Output::Print(_u("\r\n"));
  58. lastOpId = ++opId;
  59. const int depth = m_blockInfos.Count() - 1;
  60. if (depth > 0)
  61. {
  62. Output::SkipToColumn(depth);
  63. }
  64. switch (op)
  65. {
  66. #define WASM_OPCODE(opname, opcode, sig, nyi) \
  67. case wb##opname: \
  68. Output::Print(_u(#opname)); \
  69. break;
  70. #include "WasmBinaryOpCodes.h"
  71. }
  72. Output::SkipToColumn(20);
  73. PrintTypeStack(m_evalStack);
  74. }
  75. void
  76. WasmBytecodeGenerator::PrintOpEnd() const
  77. {
  78. if (lastOpId == opId)
  79. {
  80. ++opId;
  81. Output::Print(_u(" -> "));
  82. PrintTypeStack(m_evalStack);
  83. Output::Print(_u("\r\n"));
  84. }
  85. }
  86. #endif
  87. /* static */
  88. Js::AsmJsRetType
  89. WasmToAsmJs::GetAsmJsReturnType(WasmTypes::WasmType wasmType)
  90. {
  91. switch (wasmType)
  92. {
  93. case WasmTypes::I32: return Js::AsmJsRetType::Signed;
  94. case WasmTypes::I64: return Js::AsmJsRetType::Int64;
  95. case WasmTypes::F32: return Js::AsmJsRetType::Float;
  96. case WasmTypes::F64: return Js::AsmJsRetType::Double;
  97. case WasmTypes::Void: return Js::AsmJsRetType::Void;
  98. default:
  99. throw WasmCompilationException(_u("Unknown return type %u"), wasmType);
  100. }
  101. }
  102. /* static */
  103. Js::AsmJsVarType
  104. WasmToAsmJs::GetAsmJsVarType(WasmTypes::WasmType wasmType)
  105. {
  106. Js::AsmJsVarType asmType = Js::AsmJsVarType::Int;
  107. switch (wasmType)
  108. {
  109. case WasmTypes::I32: return Js::AsmJsVarType::Int;
  110. case WasmTypes::I64: return Js::AsmJsVarType::Int64;
  111. case WasmTypes::F32: return Js::AsmJsVarType::Float;
  112. case WasmTypes::F64: return Js::AsmJsVarType::Double;
  113. default:
  114. throw WasmCompilationException(_u("Unknown var type %u"), wasmType);
  115. }
  116. }
  117. typedef bool(*SectionProcessFunc)(WasmModuleGenerator*);
  118. typedef void(*AfterSectionCallback)(WasmModuleGenerator*);
  119. WasmModuleGenerator::WasmModuleGenerator(Js::ScriptContext* scriptContext, Js::WebAssemblySource* src) :
  120. m_sourceInfo(src->GetSourceInfo()),
  121. m_scriptContext(scriptContext),
  122. m_recycler(scriptContext->GetRecycler())
  123. {
  124. m_module = RecyclerNewFinalized(m_recycler, Js::WebAssemblyModule, scriptContext, src->GetBuffer(), src->GetBufferLength(), scriptContext->GetLibrary()->GetWebAssemblyModuleType());
  125. m_sourceInfo->EnsureInitialized(0);
  126. m_sourceInfo->GetSrcInfo()->sourceContextInfo->EnsureInitialized();
  127. }
  128. Js::WebAssemblyModule*
  129. WasmModuleGenerator::GenerateModule()
  130. {
  131. m_module->GetReader()->InitializeReader();
  132. BVStatic<bSectLimit + 1> visitedSections;
  133. SectionCode nextExpectedSection = bSectCustom;
  134. while (true)
  135. {
  136. SectionHeader sectionHeader = GetReader()->ReadNextSection();
  137. SectionCode sectionCode = sectionHeader.code;
  138. if (sectionCode == bSectLimit)
  139. {
  140. TRACE_WASM_SECTION(_u("Done reading module's sections"));
  141. break;
  142. }
  143. // Make sure dependency for this section has been seen
  144. SectionCode precedent = SectionInfo::All[sectionCode].precedent;
  145. if (precedent != bSectLimit && !visitedSections.Test(precedent))
  146. {
  147. throw WasmCompilationException(_u("%s section missing before %s"),
  148. SectionInfo::All[precedent].name,
  149. sectionHeader.name);
  150. }
  151. visitedSections.Set(sectionCode);
  152. // Custom section are allowed in any order
  153. if (sectionCode != bSectCustom)
  154. {
  155. if (sectionCode < nextExpectedSection)
  156. {
  157. throw WasmCompilationException(_u("Invalid Section %s"), sectionHeader.name);
  158. }
  159. nextExpectedSection = SectionCode(sectionCode + 1);
  160. }
  161. if (!GetReader()->ProcessCurrentSection())
  162. {
  163. throw WasmCompilationException(_u("Error while reading section %s"), sectionHeader.name);
  164. }
  165. }
  166. uint32 funcCount = m_module->GetWasmFunctionCount();
  167. for (uint32 i = 0; i < funcCount; ++i)
  168. {
  169. GenerateFunctionHeader(i);
  170. }
  171. #if ENABLE_DEBUG_CONFIG_OPTIONS
  172. WasmFunctionInfo* firstThunk = nullptr, *lastThunk = nullptr;
  173. for (uint32 i = 0; i < funcCount; ++i)
  174. {
  175. WasmFunctionInfo* info = m_module->GetWasmFunctionInfo(i);
  176. Assert(info->GetBody());
  177. if (PHASE_TRACE(Js::WasmInOutPhase, info->GetBody()))
  178. {
  179. uint32 index = m_module->GetWasmFunctionCount();
  180. WasmFunctionInfo* newInfo = m_module->AddWasmFunctionInfo(info->GetSignature());
  181. if (!firstThunk)
  182. {
  183. firstThunk = newInfo;
  184. }
  185. lastThunk = newInfo;
  186. GenerateFunctionHeader(index);
  187. m_module->SwapWasmFunctionInfo(i, index);
  188. m_module->AttachCustomInOutTracingReader(newInfo, index);
  189. }
  190. }
  191. if (firstThunk)
  192. {
  193. int sourceId = (int)firstThunk->GetBody()->GetSourceContextId();
  194. char16 range[64];
  195. swprintf_s(range, 64, _u("%d.%d-%d.%d"),
  196. sourceId, firstThunk->GetBody()->GetLocalFunctionId(),
  197. sourceId, lastThunk->GetBody()->GetLocalFunctionId());
  198. char16 offFullJit[128];
  199. swprintf_s(offFullJit, 128, _u("-off:fulljit:%s"), range);
  200. char16 offSimpleJit[128];
  201. swprintf_s(offSimpleJit, 128, _u("-off:simplejit:%s"), range);
  202. char16 offLoopJit[128];
  203. swprintf_s(offLoopJit, 128, _u("-off:jitloopbody:%s"), range);
  204. char16* argv[] = { nullptr, offFullJit, offSimpleJit, offLoopJit };
  205. CmdLineArgsParser parser(nullptr);
  206. parser.Parse(ARRAYSIZE(argv), argv);
  207. }
  208. #endif
  209. #if DBG_DUMP
  210. if (PHASE_TRACE1(Js::WasmReaderPhase))
  211. {
  212. GetReader()->PrintOps();
  213. }
  214. #endif
  215. // If we see a FunctionSignatures section we need to see a FunctionBodies section
  216. if (visitedSections.Test(bSectFunction) && !visitedSections.Test(bSectFunctionBodies))
  217. {
  218. throw WasmCompilationException(_u("Missing required section: %s"), SectionInfo::All[bSectFunctionBodies].name);
  219. }
  220. return m_module;
  221. }
  222. WasmBinaryReader*
  223. WasmModuleGenerator::GetReader() const
  224. {
  225. return m_module->GetReader();
  226. }
  227. void
  228. WasmModuleGenerator::GenerateFunctionHeader(uint32 index)
  229. {
  230. WasmFunctionInfo* wasmInfo = m_module->GetWasmFunctionInfo(index);
  231. if (!wasmInfo)
  232. {
  233. throw WasmCompilationException(_u("Invalid function index %u"), index);
  234. }
  235. const char16* functionName = nullptr;
  236. int nameLength = 0;
  237. if (wasmInfo->GetNameLength() > 0)
  238. {
  239. functionName = wasmInfo->GetName();
  240. nameLength = wasmInfo->GetNameLength();
  241. }
  242. else
  243. {
  244. for (uint32 iExport = 0; iExport < m_module->GetExportCount(); ++iExport)
  245. {
  246. Wasm::WasmExport* wasmExport = m_module->GetExport(iExport);
  247. if (wasmExport &&
  248. wasmExport->kind == ExternalKinds::Function &&
  249. wasmExport->nameLength > 0 &&
  250. m_module->GetFunctionIndexType(wasmExport->index) == FunctionIndexTypes::Function &&
  251. wasmExport->index == wasmInfo->GetNumber())
  252. {
  253. nameLength = wasmExport->nameLength + 16;
  254. char16 * autoName = RecyclerNewArrayLeafZ(m_recycler, char16, nameLength);
  255. nameLength = swprintf_s(autoName, nameLength, _u("%s[%u]"), wasmExport->name, wasmInfo->GetNumber());
  256. functionName = autoName;
  257. break;
  258. }
  259. }
  260. }
  261. if (!functionName)
  262. {
  263. char16 * autoName = RecyclerNewArrayLeafZ(m_recycler, char16, 32);
  264. nameLength = swprintf_s(autoName, 32, _u("wasm-function[%u]"), wasmInfo->GetNumber());
  265. functionName = autoName;
  266. }
  267. Js::FunctionBody* body = Js::FunctionBody::NewFromRecycler(
  268. m_scriptContext,
  269. functionName,
  270. nameLength,
  271. 0,
  272. 0,
  273. m_sourceInfo,
  274. m_sourceInfo->GetSrcInfo()->sourceContextInfo->sourceContextId,
  275. wasmInfo->GetNumber(),
  276. nullptr,
  277. Js::FunctionInfo::Attributes::None,
  278. Js::FunctionBody::Flags_None
  279. #ifdef PERF_COUNTERS
  280. , false /* is function from deferred deserialized proxy */
  281. #endif
  282. );
  283. wasmInfo->SetBody(body);
  284. // TODO (michhol): numbering
  285. body->SetSourceInfo(0);
  286. body->AllocateAsmJsFunctionInfo();
  287. body->SetIsAsmJsFunction(true);
  288. body->SetIsAsmjsMode(true);
  289. body->SetIsWasmFunction(true);
  290. WasmReaderInfo* readerInfo = RecyclerNew(m_recycler, WasmReaderInfo);
  291. readerInfo->m_funcInfo = wasmInfo;
  292. readerInfo->m_module = m_module;
  293. Js::AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo();
  294. info->SetWasmReaderInfo(readerInfo);
  295. info->SetWebAssemblyModule(m_module);
  296. Js::ArgSlot paramCount = wasmInfo->GetParamCount();
  297. info->SetArgCount(paramCount);
  298. info->SetWasmSignature(wasmInfo->GetSignature());
  299. Js::ArgSlot argSizeLength = max(paramCount, 3ui16);
  300. info->SetArgSizeArrayLength(argSizeLength);
  301. uint32* argSizeArray = RecyclerNewArrayLeafZ(m_recycler, uint32, argSizeLength);
  302. info->SetArgsSizesArray(argSizeArray);
  303. if (paramCount > 0)
  304. {
  305. // +1 here because asm.js includes the this pointer
  306. body->SetInParamsCount(paramCount + 1);
  307. body->SetReportedInParamsCount(paramCount + 1);
  308. info->SetArgTypeArray(RecyclerNewArrayLeaf(m_recycler, Js::AsmJsVarType::Which, paramCount));
  309. }
  310. else
  311. {
  312. // overwrite default value in this case
  313. body->SetHasImplicitArgIns(false);
  314. }
  315. for (Js::ArgSlot i = 0; i < paramCount; ++i)
  316. {
  317. WasmTypes::WasmType type = wasmInfo->GetSignature()->GetParam(i);
  318. info->SetArgType(WasmToAsmJs::GetAsmJsVarType(type), i);
  319. argSizeArray[i] = wasmInfo->GetSignature()->GetParamSize(i);
  320. }
  321. info->SetArgByteSize(wasmInfo->GetSignature()->GetParamsSize());
  322. info->SetReturnType(WasmToAsmJs::GetAsmJsReturnType(wasmInfo->GetResultType()));
  323. }
  324. WAsmJs::RegisterSpace*
  325. AllocateRegisterSpace(ArenaAllocator* alloc, WAsmJs::Types)
  326. {
  327. return Anew(alloc, WAsmJs::RegisterSpace, 1);
  328. }
  329. void
  330. WasmBytecodeGenerator::GenerateFunctionBytecode(Js::ScriptContext* scriptContext, WasmReaderInfo* readerinfo, bool validateOnly /*= false*/)
  331. {
  332. WasmBytecodeGenerator generator(scriptContext, readerinfo, validateOnly);
  333. generator.GenerateFunction();
  334. if (!generator.GetReader()->IsCurrentFunctionCompleted())
  335. {
  336. throw WasmCompilationException(_u("Invalid function format"));
  337. }
  338. }
  339. void WasmBytecodeGenerator::ValidateFunction(Js::ScriptContext* scriptContext, WasmReaderInfo* readerinfo)
  340. {
  341. GenerateFunctionBytecode(scriptContext, readerinfo, true);
  342. }
  343. WasmBytecodeGenerator::WasmBytecodeGenerator(Js::ScriptContext* scriptContext, WasmReaderInfo* readerInfo, bool validateOnly) :
  344. m_scriptContext(scriptContext),
  345. m_alloc(_u("WasmBytecodeGen"), scriptContext->GetThreadContext()->GetPageAllocator(), Js::Throw::OutOfMemory),
  346. m_evalStack(&m_alloc),
  347. mTypedRegisterAllocator(&m_alloc, AllocateRegisterSpace, 1 << WAsmJs::SIMD),
  348. m_blockInfos(&m_alloc),
  349. isUnreachable(false)
  350. {
  351. m_emptyWriter = Anew(&m_alloc, Js::EmptyWasmByteCodeWriter);
  352. m_writer = m_originalWriter = validateOnly ? m_emptyWriter : Anew(&m_alloc, Js::WasmByteCodeWriter);
  353. m_writer->Create();
  354. m_funcInfo = readerInfo->m_funcInfo;
  355. m_module = readerInfo->m_module;
  356. // Init reader to current func offset
  357. GetReader()->SeekToFunctionBody(m_funcInfo);
  358. // Use binary size to estimate bytecode size
  359. const uint32 astSize = readerInfo->m_funcInfo->m_readerInfo.size;
  360. m_writer->InitData(&m_alloc, astSize);
  361. }
  362. void
  363. WasmBytecodeGenerator::GenerateFunction()
  364. {
  365. TRACE_WASM_DECODER(_u("GenerateFunction %u \n"), m_funcInfo->GetNumber());
  366. if (PHASE_OFF(Js::WasmBytecodePhase, GetFunctionBody()))
  367. {
  368. throw WasmCompilationException(_u("Compilation skipped"));
  369. }
  370. Js::AutoProfilingPhase functionProfiler(m_scriptContext, Js::WasmFunctionBodyPhase);
  371. Unused(functionProfiler);
  372. m_maxArgOutDepth = 0;
  373. m_writer->Begin(GetFunctionBody(), &m_alloc);
  374. try
  375. {
  376. Js::ByteCodeLabel exitLabel = m_writer->DefineLabel();
  377. m_funcInfo->SetExitLabel(exitLabel);
  378. EnregisterLocals();
  379. EnterEvalStackScope();
  380. // The function's yield type is the return type
  381. GetReader()->m_currentNode.block.sig = m_funcInfo->GetResultType();
  382. EmitInfo lastInfo = EmitBlock();
  383. if (lastInfo.type != WasmTypes::Void || m_funcInfo->GetResultType() == WasmTypes::Void)
  384. {
  385. EmitReturnExpr(&lastInfo);
  386. }
  387. DebugPrintOpEnd();
  388. ExitEvalStackScope();
  389. SetUnreachableState(false);
  390. m_writer->MarkAsmJsLabel(exitLabel);
  391. m_writer->EmptyAsm(Js::OpCodeAsmJs::Ret);
  392. m_writer->End();
  393. GetReader()->FunctionEnd();
  394. }
  395. catch (...)
  396. {
  397. GetReader()->FunctionEnd();
  398. m_originalWriter->Reset();
  399. throw;
  400. }
  401. #if DBG_DUMP
  402. if (PHASE_DUMP(Js::ByteCodePhase, GetFunctionBody()) && !IsValidating())
  403. {
  404. Js::AsmJsByteCodeDumper::Dump(GetFunctionBody(), &mTypedRegisterAllocator, nullptr);
  405. }
  406. #endif
  407. Js::AsmJsFunctionInfo * info = GetFunctionBody()->GetAsmJsFunctionInfo();
  408. mTypedRegisterAllocator.CommitToFunctionBody(GetFunctionBody());
  409. mTypedRegisterAllocator.CommitToFunctionInfo(info, GetFunctionBody());
  410. GetFunctionBody()->CheckAndSetOutParamMaxDepth(m_maxArgOutDepth);
  411. }
  412. void
  413. WasmBytecodeGenerator::EnregisterLocals()
  414. {
  415. uint32 nLocals = m_funcInfo->GetLocalCount();
  416. m_locals = AnewArray(&m_alloc, WasmLocal, nLocals);
  417. m_funcInfo->GetBody()->SetFirstTmpReg(nLocals);
  418. for (uint32 i = 0; i < nLocals; ++i)
  419. {
  420. WasmTypes::WasmType type = m_funcInfo->GetLocal(i);
  421. WasmRegisterSpace * regSpace = GetRegisterSpace(type);
  422. if (regSpace == nullptr)
  423. {
  424. throw WasmCompilationException(_u("Unable to find local register space"));
  425. }
  426. m_locals[i] = WasmLocal(regSpace->AcquireRegister(), type);
  427. // Zero only the locals not corresponding to formal parameters.
  428. if (i >= m_funcInfo->GetParamCount()) {
  429. switch (type)
  430. {
  431. case WasmTypes::F32:
  432. m_writer->AsmFloat1Const1(Js::OpCodeAsmJs::Ld_FltConst, m_locals[i].location, 0.0f);
  433. break;
  434. case WasmTypes::F64:
  435. m_writer->AsmDouble1Const1(Js::OpCodeAsmJs::Ld_DbConst, m_locals[i].location, 0.0);
  436. break;
  437. case WasmTypes::I32:
  438. m_writer->AsmInt1Const1(Js::OpCodeAsmJs::Ld_IntConst, m_locals[i].location, 0);
  439. break;
  440. case WasmTypes::I64:
  441. m_writer->AsmLong1Const1(Js::OpCodeAsmJs::Ld_LongConst, m_locals[i].location, 0);
  442. break;
  443. default:
  444. Assume(UNREACHED);
  445. }
  446. }
  447. }
  448. }
  449. void
  450. WasmBytecodeGenerator::EmitExpr(WasmOp op)
  451. {
  452. DebugPrintOp(op);
  453. switch (op)
  454. {
  455. #define WASM_OPCODE(opname, opcode, sig, nyi) \
  456. case opcode: \
  457. if (nyi) throw WasmCompilationException(_u("Operator %s NYI"), _u(#opname)); break;
  458. #include "WasmBinaryOpCodes.h"
  459. default:
  460. break;
  461. }
  462. EmitInfo info;
  463. switch (op)
  464. {
  465. case wbGetGlobal:
  466. info = EmitGetGlobal();
  467. break;
  468. case wbSetGlobal:
  469. info = EmitSetGlobal();
  470. break;
  471. case wbGetLocal:
  472. info = EmitGetLocal();
  473. break;
  474. case wbSetLocal:
  475. info = EmitSetLocal(false);
  476. break;
  477. case wbTeeLocal:
  478. info = EmitSetLocal(true);
  479. break;
  480. case wbReturn:
  481. EmitReturnExpr();
  482. info.type = WasmTypes::Any;
  483. break;
  484. case wbF32Const:
  485. info = EmitConst(WasmTypes::F32, GetReader()->m_currentNode.cnst);
  486. break;
  487. case wbF64Const:
  488. info = EmitConst(WasmTypes::F64, GetReader()->m_currentNode.cnst);
  489. break;
  490. case wbI32Const:
  491. info = EmitConst(WasmTypes::I32, GetReader()->m_currentNode.cnst);
  492. break;
  493. case wbI64Const:
  494. info = EmitConst(WasmTypes::I64, GetReader()->m_currentNode.cnst);
  495. break;
  496. case wbBlock:
  497. info = EmitBlock();
  498. break;
  499. case wbLoop:
  500. info = EmitLoop();
  501. break;
  502. case wbCall:
  503. info = EmitCall<wbCall>();
  504. break;
  505. case wbCallIndirect:
  506. info = EmitCall<wbCallIndirect>();
  507. break;
  508. case wbIf:
  509. info = EmitIfElseExpr();
  510. break;
  511. case wbElse:
  512. throw WasmCompilationException(_u("Unexpected else opcode"));
  513. case wbEnd:
  514. throw WasmCompilationException(_u("Unexpected end opcode"));
  515. case wbBr:
  516. EmitBr();
  517. info.type = WasmTypes::Any;
  518. break;
  519. case wbBrIf:
  520. info = EmitBrIf();
  521. break;
  522. case wbSelect:
  523. info = EmitSelect();
  524. break;
  525. case wbBrTable:
  526. EmitBrTable();
  527. info.type = WasmTypes::Any;
  528. break;
  529. case wbDrop:
  530. info = EmitDrop();
  531. break;
  532. case wbNop:
  533. return;
  534. case wbCurrentMemory:
  535. {
  536. SetUsesMemory(0);
  537. Js::RegSlot tempReg = GetRegisterSpace(WasmTypes::I32)->AcquireTmpRegister();
  538. info = EmitInfo(tempReg, WasmTypes::I32);
  539. m_writer->AsmReg1(Js::OpCodeAsmJs::CurrentMemory_Int, tempReg);
  540. break;
  541. }
  542. case wbGrowMemory:
  543. {
  544. info = EmitGrowMemory();
  545. break;
  546. }
  547. case wbUnreachable:
  548. m_writer->EmptyAsm(Js::OpCodeAsmJs::Unreachable_Void);
  549. SetUnreachableState(true);
  550. info.type = WasmTypes::Any;
  551. break;
  552. #define WASM_MEMREAD_OPCODE(opname, opcode, sig, nyi, viewtype) \
  553. case wb##opname: \
  554. Assert(WasmOpCodeSignatures::n##sig > 0);\
  555. info = EmitMemAccess(wb##opname, WasmOpCodeSignatures::sig, viewtype, false); \
  556. break;
  557. #define WASM_MEMSTORE_OPCODE(opname, opcode, sig, nyi, viewtype) \
  558. case wb##opname: \
  559. Assert(WasmOpCodeSignatures::n##sig > 0);\
  560. info = EmitMemAccess(wb##opname, WasmOpCodeSignatures::sig, viewtype, true); \
  561. break;
  562. #define WASM_BINARY_OPCODE(opname, opcode, sig, asmjsop, nyi) \
  563. case wb##opname: \
  564. Assert(WasmOpCodeSignatures::n##sig == 3);\
  565. info = EmitBinExpr(Js::OpCodeAsmJs::##asmjsop, WasmOpCodeSignatures::sig); \
  566. break;
  567. #define WASM_UNARY__OPCODE(opname, opcode, sig, asmjsop, nyi) \
  568. case wb##opname: \
  569. Assert(WasmOpCodeSignatures::n##sig == 2);\
  570. info = EmitUnaryExpr(Js::OpCodeAsmJs::##asmjsop, WasmOpCodeSignatures::sig); \
  571. break;
  572. #define WASM_EMPTY__OPCODE(opname, opcode, asmjsop, nyi) \
  573. case wb##opname: \
  574. m_writer->EmptyAsm(Js::OpCodeAsmJs::##asmjsop);\
  575. break;
  576. #include "WasmBinaryOpCodes.h"
  577. default:
  578. throw WasmCompilationException(_u("Unknown expression's op 0x%X"), op);
  579. }
  580. if (info.type != WasmTypes::Void)
  581. {
  582. PushEvalStack(info);
  583. }
  584. DebugPrintOpEnd();
  585. }
  586. EmitInfo
  587. WasmBytecodeGenerator::EmitGetGlobal()
  588. {
  589. uint32 globalIndex = GetReader()->m_currentNode.var.num;
  590. WasmGlobal* global = m_module->GetGlobal(globalIndex);
  591. WasmTypes::WasmType type = global->GetType();
  592. Js::RegSlot slot = m_module->GetOffsetForGlobal(global);
  593. CompileAssert(WasmTypes::I32 == 1);
  594. CompileAssert(WasmTypes::I64 == 2);
  595. CompileAssert(WasmTypes::F32 == 3);
  596. CompileAssert(WasmTypes::F64 == 4);
  597. static const Js::OpCodeAsmJs globalOpcodes[] = {
  598. Js::OpCodeAsmJs::LdSlot_Int,
  599. Js::OpCodeAsmJs::LdSlot_Long,
  600. Js::OpCodeAsmJs::LdSlot_Flt,
  601. Js::OpCodeAsmJs::LdSlot_Db
  602. };
  603. WasmRegisterSpace * regSpace = GetRegisterSpace(type);
  604. Js::RegSlot tmpReg = regSpace->AcquireTmpRegister();
  605. EmitInfo info(tmpReg, type);
  606. m_writer->AsmSlot(globalOpcodes[type - 1], tmpReg, WasmBytecodeGenerator::ModuleEnvRegister, slot);
  607. return info;
  608. }
  609. EmitInfo
  610. WasmBytecodeGenerator::EmitSetGlobal()
  611. {
  612. uint32 globalIndex = GetReader()->m_currentNode.var.num;
  613. WasmGlobal* global = m_module->GetGlobal(globalIndex);
  614. Js::RegSlot slot = m_module->GetOffsetForGlobal(global);
  615. WasmTypes::WasmType type = global->GetType();
  616. EmitInfo info = PopEvalStack(type);
  617. CompileAssert(WasmTypes::I32 == 1);
  618. CompileAssert(WasmTypes::I64 == 2);
  619. CompileAssert(WasmTypes::F32 == 3);
  620. CompileAssert(WasmTypes::F64 == 4);
  621. static const Js::OpCodeAsmJs globalOpcodes[] = {
  622. Js::OpCodeAsmJs::StSlot_Int,
  623. Js::OpCodeAsmJs::StSlot_Long,
  624. Js::OpCodeAsmJs::StSlot_Flt,
  625. Js::OpCodeAsmJs::StSlot_Db
  626. };
  627. m_writer->AsmSlot(globalOpcodes[type - 1], info.location, WasmBytecodeGenerator::ModuleEnvRegister, slot);
  628. ReleaseLocation(&info);
  629. return EmitInfo();
  630. }
  631. EmitInfo
  632. WasmBytecodeGenerator::EmitGetLocal()
  633. {
  634. uint32 localIndex = GetReader()->m_currentNode.var.num;
  635. if (m_funcInfo->GetLocalCount() <= localIndex)
  636. {
  637. throw WasmCompilationException(_u("%u is not a valid local"), localIndex);
  638. }
  639. WasmLocal local = m_locals[localIndex];
  640. Js::OpCodeAsmJs op = GetLoadOp(local.type);
  641. WasmRegisterSpace * regSpace = GetRegisterSpace(local.type);
  642. Js::RegSlot tmpReg = regSpace->AcquireTmpRegister();
  643. m_writer->AsmReg2(op, tmpReg, local.location);
  644. return EmitInfo(tmpReg, local.type);
  645. }
  646. EmitInfo
  647. WasmBytecodeGenerator::EmitSetLocal(bool tee)
  648. {
  649. uint32 localNum = GetReader()->m_currentNode.var.num;
  650. if (localNum >= m_funcInfo->GetLocalCount())
  651. {
  652. throw WasmCompilationException(_u("%u is not a valid local"), localNum);
  653. }
  654. WasmLocal local = m_locals[localNum];
  655. EmitInfo info = PopEvalStack(local.type);
  656. m_writer->AsmReg2(GetLoadOp(local.type), local.location, info.location);
  657. if (tee)
  658. {
  659. return info;
  660. }
  661. else
  662. {
  663. ReleaseLocation(&info);
  664. return EmitInfo();
  665. }
  666. }
  667. EmitInfo
  668. WasmBytecodeGenerator::EmitConst(WasmTypes::WasmType type, WasmConstLitNode cnst)
  669. {
  670. Js::RegSlot tmpReg = GetRegisterSpace(type)->AcquireTmpRegister();
  671. EmitInfo dst(tmpReg, type);
  672. EmitLoadConst(dst, cnst);
  673. return dst;
  674. }
  675. void
  676. WasmBytecodeGenerator::EmitLoadConst(EmitInfo dst, WasmConstLitNode cnst)
  677. {
  678. switch (dst.type)
  679. {
  680. case WasmTypes::F32:
  681. m_writer->AsmFloat1Const1(Js::OpCodeAsmJs::Ld_FltConst, dst.location, cnst.f32);
  682. break;
  683. case WasmTypes::F64:
  684. m_writer->AsmDouble1Const1(Js::OpCodeAsmJs::Ld_DbConst, dst.location, cnst.f64);
  685. break;
  686. case WasmTypes::I32:
  687. m_writer->AsmInt1Const1(Js::OpCodeAsmJs::Ld_IntConst, dst.location, cnst.i32);
  688. break;
  689. case WasmTypes::I64:
  690. m_writer->AsmLong1Const1(Js::OpCodeAsmJs::Ld_LongConst, dst.location, cnst.i64);
  691. break;
  692. default:
  693. throw WasmCompilationException(_u("Unknown type %u"), dst.type);
  694. }
  695. }
  696. WasmConstLitNode
  697. WasmBytecodeGenerator::GetZeroCnst()
  698. {
  699. WasmConstLitNode cnst = {0};
  700. return cnst;
  701. }
  702. void
  703. WasmBytecodeGenerator::EnsureStackAvailable()
  704. {
  705. if (!ThreadContext::IsCurrentStackAvailable(Js::Constants::MinStackCompile))
  706. {
  707. throw WasmCompilationException(_u("Maximum supported nested blocks reached"));
  708. }
  709. }
  710. void
  711. WasmBytecodeGenerator::EmitBlockCommon(BlockInfo* blockInfo, bool* endOnElse /*= nullptr*/)
  712. {
  713. EnsureStackAvailable();
  714. bool canResetUnreachable = !IsUnreachable();
  715. WasmOp op;
  716. EnterEvalStackScope();
  717. if(endOnElse) *endOnElse = false;
  718. do {
  719. op = GetReader()->ReadExpr();
  720. if (op == wbEnd)
  721. {
  722. break;
  723. }
  724. if (endOnElse && op == wbElse)
  725. {
  726. *endOnElse = true;
  727. break;
  728. }
  729. EmitExpr(op);
  730. } while (true);
  731. DebugPrintOp(op);
  732. if (blockInfo && blockInfo->HasYield())
  733. {
  734. EmitInfo info = PopEvalStack();
  735. YieldToBlock(*blockInfo, info);
  736. ReleaseLocation(&info);
  737. }
  738. ExitEvalStackScope();
  739. if (canResetUnreachable)
  740. {
  741. SetUnreachableState(false);
  742. }
  743. }
  744. EmitInfo
  745. WasmBytecodeGenerator::EmitBlock()
  746. {
  747. Js::ByteCodeLabel blockLabel = m_writer->DefineLabel();
  748. BlockInfo blockInfo = PushLabel(blockLabel);
  749. EmitBlockCommon(&blockInfo);
  750. m_writer->MarkAsmJsLabel(blockLabel);
  751. EmitInfo yieldInfo = PopLabel(blockLabel);
  752. // block yields last value
  753. return yieldInfo;
  754. }
  755. EmitInfo
  756. WasmBytecodeGenerator::EmitLoop()
  757. {
  758. Js::ByteCodeLabel loopTailLabel = m_writer->DefineLabel();
  759. Js::ByteCodeLabel loopHeadLabel = m_writer->DefineLabel();
  760. Js::ByteCodeLabel loopLandingPadLabel = m_writer->DefineLabel();
  761. uint32 loopId = m_writer->EnterLoop(loopHeadLabel);
  762. // Internally we create a block for loop to exit, but semantically, they don't exist so pop it
  763. BlockInfo implicitBlockInfo = PushLabel(loopTailLabel);
  764. m_blockInfos.Pop();
  765. // We don't want nested block to jump directly to the loop header
  766. // instead, jump to the landing pad and let it jump back to the loop header
  767. PushLabel(loopLandingPadLabel, false);
  768. EmitBlockCommon(&implicitBlockInfo);
  769. PopLabel(loopLandingPadLabel);
  770. // By default we don't loop, jump over the landing pad
  771. m_writer->AsmBr(loopTailLabel);
  772. m_writer->MarkAsmJsLabel(loopLandingPadLabel);
  773. m_writer->AsmBr(loopHeadLabel);
  774. // Put the implicit block back on the stack and yield the last expression to it
  775. m_blockInfos.Push(implicitBlockInfo);
  776. m_writer->MarkAsmJsLabel(loopTailLabel);
  777. // Pop the implicit block to resolve the yield correctly
  778. EmitInfo loopInfo = PopLabel(loopTailLabel);
  779. m_writer->ExitLoop(loopId);
  780. return loopInfo;
  781. }
  782. template<WasmOp wasmOp>
  783. EmitInfo
  784. WasmBytecodeGenerator::EmitCall()
  785. {
  786. uint32 funcNum = Js::Constants::UninitializedValue;
  787. uint32 signatureId = Js::Constants::UninitializedValue;
  788. WasmSignature * calleeSignature = nullptr;
  789. EmitInfo indirectIndexInfo;
  790. const bool isImportCall = GetReader()->m_currentNode.call.funcType == FunctionIndexTypes::Import;
  791. Assert(isImportCall || GetReader()->m_currentNode.call.funcType == FunctionIndexTypes::Function || GetReader()->m_currentNode.call.funcType == FunctionIndexTypes::ImportThunk);
  792. switch (wasmOp)
  793. {
  794. case wbCall:
  795. funcNum = GetReader()->m_currentNode.call.num;
  796. calleeSignature = m_module->GetWasmFunctionInfo(funcNum)->GetSignature();
  797. break;
  798. case wbCallIndirect:
  799. indirectIndexInfo = PopEvalStack(WasmTypes::I32, _u("Indirect call index must be int type"));
  800. signatureId = GetReader()->m_currentNode.call.num;
  801. calleeSignature = m_module->GetSignature(signatureId);
  802. ReleaseLocation(&indirectIndexInfo);
  803. break;
  804. default:
  805. Assume(UNREACHED);
  806. }
  807. const auto argOverflow = []
  808. {
  809. throw WasmCompilationException(_u("Argument size too big"));
  810. };
  811. // emit start call
  812. Js::ArgSlot argSize;
  813. Js::OpCodeAsmJs startCallOp;
  814. if (isImportCall)
  815. {
  816. argSize = ArgSlotMath::Mul(calleeSignature->GetParamCount(), sizeof(Js::Var), argOverflow);
  817. startCallOp = Js::OpCodeAsmJs::StartCall;
  818. }
  819. else
  820. {
  821. startCallOp = Js::OpCodeAsmJs::I_StartCall;
  822. argSize = calleeSignature->GetParamsSize();
  823. }
  824. // Add return value
  825. argSize = ArgSlotMath::Add(argSize, sizeof(Js::Var), argOverflow);
  826. if (!Math::IsAligned<Js::ArgSlot>(argSize, sizeof(Js::Var)))
  827. {
  828. AssertMsg(UNREACHED, "Wasm argument size should always be Var aligned");
  829. throw WasmCompilationException(_u("Internal Error"));
  830. }
  831. m_writer->AsmStartCall(startCallOp, argSize);
  832. Js::ArgSlot nArgs = calleeSignature->GetParamCount();
  833. // copy args into a list so they could be generated in the right order (FIFO)
  834. EmitInfo* argsList = AnewArray(&m_alloc, EmitInfo, nArgs);
  835. for (int i = int(nArgs) - 1; i >= 0; --i)
  836. {
  837. EmitInfo info = PopEvalStack(calleeSignature->GetParam((Js::ArgSlot)i), _u("Call argument does not match formal type"));
  838. // We can release the location of the arguments now, because we won't create new temps between start/call
  839. ReleaseLocation(&info);
  840. argsList[i] = info;
  841. }
  842. // Skip the this pointer (aka undefined)
  843. uint32 argLoc = 1;
  844. for (Js::ArgSlot i = 0; i < nArgs; ++i)
  845. {
  846. EmitInfo info = argsList[i];
  847. Js::OpCodeAsmJs argOp = Js::OpCodeAsmJs::Nop;
  848. switch (info.type)
  849. {
  850. case WasmTypes::F32:
  851. argOp = isImportCall ? Js::OpCodeAsmJs::ArgOut_Flt : Js::OpCodeAsmJs::I_ArgOut_Flt;
  852. break;
  853. case WasmTypes::F64:
  854. argOp = isImportCall ? Js::OpCodeAsmJs::ArgOut_Db : Js::OpCodeAsmJs::I_ArgOut_Db;
  855. break;
  856. case WasmTypes::I32:
  857. argOp = isImportCall ? Js::OpCodeAsmJs::ArgOut_Int : Js::OpCodeAsmJs::I_ArgOut_Int;
  858. break;
  859. case WasmTypes::I64:
  860. argOp = isImportCall ? Js::OpCodeAsmJs::ArgOut_Long : Js::OpCodeAsmJs::I_ArgOut_Long;
  861. break;
  862. case WasmTypes::Any:
  863. // In unreachable mode allow any type as argument since we won't actually emit the call
  864. Assert(IsUnreachable());
  865. if (IsUnreachable())
  866. {
  867. argOp = Js::OpCodeAsmJs::ArgOut_Int;
  868. break;
  869. }
  870. // Fall through
  871. default:
  872. throw WasmCompilationException(_u("Unknown argument type %u"), info.type);
  873. }
  874. m_writer->AsmReg2(argOp, argLoc, info.location);
  875. // Calculated next argument Js::Var location
  876. if (isImportCall)
  877. {
  878. ++argLoc;
  879. }
  880. else
  881. {
  882. const Js::ArgSlot currentArgSize = calleeSignature->GetParamSize(i);
  883. Assert(Math::IsAligned<Js::ArgSlot>(currentArgSize, sizeof(Js::Var)));
  884. argLoc += currentArgSize / sizeof(Js::Var);
  885. }
  886. }
  887. AdeleteArray(&m_alloc, nArgs, argsList);
  888. // emit call
  889. switch (wasmOp)
  890. {
  891. case wbCall:
  892. {
  893. uint32 offset = isImportCall ? m_module->GetImportFuncOffset() : m_module->GetFuncOffset();
  894. uint32 index = UInt32Math::Add(offset, funcNum);
  895. m_writer->AsmSlot(Js::OpCodeAsmJs::LdSlot, 0, 1, index);
  896. break;
  897. }
  898. case wbCallIndirect:
  899. m_writer->AsmSlot(Js::OpCodeAsmJs::LdSlotArr, 0, 1, m_module->GetTableEnvironmentOffset());
  900. m_writer->AsmSlot(Js::OpCodeAsmJs::LdArr_WasmFunc, 0, 0, indirectIndexInfo.location);
  901. m_writer->AsmReg1IntConst1(Js::OpCodeAsmJs::CheckSignature, 0, calleeSignature->GetSignatureId());
  902. break;
  903. default:
  904. Assume(UNREACHED);
  905. }
  906. // calculate number of RegSlots(Js::Var) the call consumes
  907. Js::ArgSlot args;
  908. Js::OpCodeAsmJs callOp = Js::OpCodeAsmJs::Nop;
  909. if (isImportCall)
  910. {
  911. args = calleeSignature->GetParamCount();
  912. ArgSlotMath::Inc(args, argOverflow);
  913. callOp = Js::OpCodeAsmJs::Call;
  914. }
  915. else
  916. {
  917. Assert(Math::IsAligned<Js::ArgSlot>(argSize, sizeof(Js::Var)));
  918. args = argSize / sizeof(Js::Var);
  919. callOp = Js::OpCodeAsmJs::I_Call;
  920. }
  921. m_writer->AsmCall(callOp, 0, 0, args, WasmToAsmJs::GetAsmJsReturnType(calleeSignature->GetResultType()));
  922. // emit result coercion
  923. EmitInfo retInfo;
  924. retInfo.type = calleeSignature->GetResultType();
  925. if (retInfo.type != WasmTypes::Void)
  926. {
  927. Js::OpCodeAsmJs convertOp = Js::OpCodeAsmJs::Nop;
  928. retInfo.location = GetRegisterSpace(retInfo.type)->AcquireTmpRegister();
  929. switch (retInfo.type)
  930. {
  931. case WasmTypes::F32:
  932. convertOp = isImportCall ? Js::OpCodeAsmJs::Conv_VTF : Js::OpCodeAsmJs::Ld_Flt;
  933. break;
  934. case WasmTypes::F64:
  935. convertOp = isImportCall ? Js::OpCodeAsmJs::Conv_VTD : Js::OpCodeAsmJs::Ld_Db;
  936. break;
  937. case WasmTypes::I32:
  938. convertOp = isImportCall ? Js::OpCodeAsmJs::Conv_VTI : Js::OpCodeAsmJs::Ld_Int;
  939. break;
  940. case WasmTypes::I64:
  941. convertOp = isImportCall ? Js::OpCodeAsmJs::Conv_VTL : Js::OpCodeAsmJs::Ld_Long;
  942. break;
  943. default:
  944. throw WasmCompilationException(_u("Unknown call return type %u"), retInfo.type);
  945. }
  946. m_writer->AsmReg2(convertOp, retInfo.location, 0);
  947. }
  948. // track stack requirements for out params
  949. // + 1 for return address
  950. uint32 maxDepthForLevel = args + 1;
  951. if (maxDepthForLevel > m_maxArgOutDepth)
  952. {
  953. m_maxArgOutDepth = maxDepthForLevel;
  954. }
  955. return retInfo;
  956. }
  957. EmitInfo
  958. WasmBytecodeGenerator::EmitIfElseExpr()
  959. {
  960. Js::ByteCodeLabel falseLabel = m_writer->DefineLabel();
  961. Js::ByteCodeLabel endLabel = m_writer->DefineLabel();
  962. EmitInfo checkExpr = PopEvalStack(WasmTypes::I32, _u("If expression must have type i32"));
  963. ReleaseLocation(&checkExpr);
  964. m_writer->AsmBrReg1(Js::OpCodeAsmJs::BrFalse_Int, falseLabel, checkExpr.location);
  965. BlockInfo blockInfo = PushLabel(endLabel);
  966. bool endOnElse = false;
  967. EmitBlockCommon(&blockInfo, &endOnElse);
  968. EnsureYield(blockInfo);
  969. m_writer->AsmBr(endLabel);
  970. m_writer->MarkAsmJsLabel(falseLabel);
  971. EmitInfo retInfo;
  972. EmitInfo falseExpr;
  973. if (endOnElse)
  974. {
  975. if (blockInfo.yieldInfo)
  976. {
  977. blockInfo.yieldInfo->didYield = false;
  978. }
  979. EmitBlockCommon(&blockInfo);
  980. EnsureYield(blockInfo);
  981. }
  982. m_writer->MarkAsmJsLabel(endLabel);
  983. return PopLabel(endLabel);
  984. }
  985. void
  986. WasmBytecodeGenerator::EmitBrTable()
  987. {
  988. const uint32 numTargets = GetReader()->m_currentNode.brTable.numTargets;
  989. const uint32* targetTable = GetReader()->m_currentNode.brTable.targetTable;
  990. const uint32 defaultEntry = GetReader()->m_currentNode.brTable.defaultTarget;
  991. // Compile scrutinee
  992. EmitInfo scrutineeInfo = PopEvalStack(WasmTypes::I32, _u("br_table expression must be of type i32"));
  993. m_writer->AsmReg2(Js::OpCodeAsmJs::BeginSwitch_Int, scrutineeInfo.location, scrutineeInfo.location);
  994. EmitInfo yieldInfo;
  995. if (ShouldYieldToBlock(defaultEntry))
  996. {
  997. // If the scrutinee is any then check the stack before popping
  998. if (scrutineeInfo.type == WasmTypes::Any && m_evalStack.Peek().type == WasmTypes::Limit)
  999. {
  1000. yieldInfo = scrutineeInfo;
  1001. }
  1002. else
  1003. {
  1004. yieldInfo = PopEvalStack();
  1005. }
  1006. }
  1007. // Compile cases
  1008. for (uint32 i = 0; i < numTargets; i++)
  1009. {
  1010. uint32 target = targetTable[i];
  1011. YieldToBlock(target, yieldInfo);
  1012. Js::ByteCodeLabel targetLabel = GetLabel(target);
  1013. m_writer->AsmBrReg1Const1(Js::OpCodeAsmJs::Case_IntConst, targetLabel, scrutineeInfo.location, i);
  1014. }
  1015. YieldToBlock(defaultEntry, yieldInfo);
  1016. m_writer->AsmBr(GetLabel(defaultEntry), Js::OpCodeAsmJs::EndSwitch_Int);
  1017. ReleaseLocation(&scrutineeInfo);
  1018. ReleaseLocation(&yieldInfo);
  1019. SetUnreachableState(true);
  1020. }
  1021. EmitInfo
  1022. WasmBytecodeGenerator::EmitGrowMemory()
  1023. {
  1024. SetUsesMemory(0);
  1025. EmitInfo info = PopEvalStack(WasmTypes::I32, _u("Invalid type for GrowMemory"));
  1026. m_writer->AsmReg2(Js::OpCodeAsmJs::GrowMemory, info.location, info.location);
  1027. return info;
  1028. }
  1029. EmitInfo
  1030. WasmBytecodeGenerator::EmitDrop()
  1031. {
  1032. EmitInfo info = PopEvalStack();
  1033. ReleaseLocation(&info);
  1034. return EmitInfo();
  1035. }
  1036. EmitInfo
  1037. WasmBytecodeGenerator::EmitBinExpr(Js::OpCodeAsmJs op, const WasmTypes::WasmType* signature)
  1038. {
  1039. WasmTypes::WasmType resultType = signature[0];
  1040. WasmTypes::WasmType lhsType = signature[1];
  1041. WasmTypes::WasmType rhsType = signature[2];
  1042. EmitInfo rhs = PopEvalStack(lhsType);
  1043. EmitInfo lhs = PopEvalStack(rhsType);
  1044. ReleaseLocation(&rhs);
  1045. ReleaseLocation(&lhs);
  1046. Js::RegSlot resultReg = GetRegisterSpace(resultType)->AcquireTmpRegister();
  1047. m_writer->AsmReg3(op, resultReg, lhs.location, rhs.location);
  1048. return EmitInfo(resultReg, resultType);
  1049. }
  1050. EmitInfo
  1051. WasmBytecodeGenerator::EmitUnaryExpr(Js::OpCodeAsmJs op, const WasmTypes::WasmType* signature)
  1052. {
  1053. WasmTypes::WasmType resultType = signature[0];
  1054. WasmTypes::WasmType inputType = signature[1];
  1055. EmitInfo info = PopEvalStack(inputType);
  1056. ReleaseLocation(&info);
  1057. if (resultType == WasmTypes::Void)
  1058. {
  1059. m_writer->AsmReg2(op, 0, info.location);
  1060. return EmitInfo();
  1061. }
  1062. Js::RegSlot resultReg = GetRegisterSpace(resultType)->AcquireTmpRegister();
  1063. m_writer->AsmReg2(op, resultReg, info.location);
  1064. return EmitInfo(resultReg, resultType);
  1065. }
  1066. EmitInfo
  1067. WasmBytecodeGenerator::EmitMemAccess(WasmOp wasmOp, const WasmTypes::WasmType* signature, Js::ArrayBufferView::ViewType viewType, bool isStore)
  1068. {
  1069. WasmTypes::WasmType type = signature[0];
  1070. SetUsesMemory(0);
  1071. const uint32 mask = Js::ArrayBufferView::ViewMask[viewType];
  1072. const uint32 alignment = GetReader()->m_currentNode.mem.alignment;
  1073. const uint32 offset = GetReader()->m_currentNode.mem.offset;
  1074. if ((mask << 1) & (1 << alignment))
  1075. {
  1076. throw WasmCompilationException(_u("alignment must not be larger than natural"));
  1077. }
  1078. EmitInfo rhsInfo;
  1079. if (isStore)
  1080. {
  1081. rhsInfo = PopEvalStack(type, _u("Invalid type for store op"));
  1082. }
  1083. EmitInfo exprInfo = PopEvalStack(WasmTypes::I32, _u("Index expression must be of type i32"));
  1084. if (isStore) // Stores
  1085. {
  1086. m_writer->WasmMemAccess(Js::OpCodeAsmJs::StArrWasm, rhsInfo.location, exprInfo.location, offset, viewType);
  1087. ReleaseLocation(&rhsInfo);
  1088. ReleaseLocation(&exprInfo);
  1089. return EmitInfo();
  1090. }
  1091. ReleaseLocation(&exprInfo);
  1092. Js::RegSlot resultReg = GetRegisterSpace(type)->AcquireTmpRegister();
  1093. m_writer->WasmMemAccess(Js::OpCodeAsmJs::LdArrWasm, resultReg, exprInfo.location, offset, viewType);
  1094. EmitInfo yieldInfo;
  1095. if (!isStore)
  1096. {
  1097. // Yield only on load
  1098. yieldInfo = EmitInfo(resultReg, type);
  1099. }
  1100. return yieldInfo;
  1101. }
  1102. void
  1103. WasmBytecodeGenerator::EmitReturnExpr(EmitInfo* explicitRetInfo)
  1104. {
  1105. if (m_funcInfo->GetResultType() == WasmTypes::Void)
  1106. {
  1107. // TODO (michhol): consider moving off explicit 0 for return reg
  1108. m_writer->AsmReg1(Js::OpCodeAsmJs::LdUndef, 0);
  1109. }
  1110. else
  1111. {
  1112. EmitInfo retExprInfo = explicitRetInfo ? *explicitRetInfo : PopEvalStack();
  1113. if (retExprInfo.type != WasmTypes::Any && m_funcInfo->GetResultType() != retExprInfo.type)
  1114. {
  1115. throw WasmCompilationException(_u("Result type must match return type"));
  1116. }
  1117. Js::OpCodeAsmJs retOp = GetReturnOp(retExprInfo.type);
  1118. m_writer->Conv(retOp, 0, retExprInfo.location);
  1119. ReleaseLocation(&retExprInfo);
  1120. }
  1121. m_writer->AsmBr(m_funcInfo->GetExitLabel());
  1122. SetUnreachableState(true);
  1123. }
  1124. EmitInfo
  1125. WasmBytecodeGenerator::EmitSelect()
  1126. {
  1127. EmitInfo conditionInfo = PopEvalStack(WasmTypes::I32, _u("select condition must have i32 type"));
  1128. EmitInfo falseInfo = PopEvalStack();
  1129. EmitInfo trueInfo = PopEvalStack(falseInfo.type, _u("select operands must both have same type"));
  1130. ReleaseLocation(&conditionInfo);
  1131. ReleaseLocation(&falseInfo);
  1132. ReleaseLocation(&trueInfo);
  1133. if (IsUnreachable())
  1134. {
  1135. if (trueInfo.type == WasmTypes::Any)
  1136. {
  1137. // Report the type of falseInfo for type checking
  1138. return EmitInfo(falseInfo.type);
  1139. }
  1140. // Otherwise report the type of trueInfo for type checking
  1141. return EmitInfo(trueInfo.type);
  1142. }
  1143. WasmTypes::WasmType selectType = trueInfo.type;
  1144. EmitInfo resultInfo = EmitInfo(GetRegisterSpace(selectType)->AcquireTmpRegister(), selectType);
  1145. Js::ByteCodeLabel falseLabel = m_writer->DefineLabel();
  1146. Js::ByteCodeLabel doneLabel = m_writer->DefineLabel();
  1147. Js::OpCodeAsmJs loadOp = GetLoadOp(resultInfo.type);
  1148. // var result;
  1149. // if (!condition) goto:condFalse
  1150. // result = trueRes;
  1151. // goto:done;
  1152. //:condFalse
  1153. // result = falseRes;
  1154. //:done
  1155. m_writer->AsmBrReg1(Js::OpCodeAsmJs::BrFalse_Int, falseLabel, conditionInfo.location);
  1156. m_writer->AsmReg2(loadOp, resultInfo.location, trueInfo.location);
  1157. m_writer->AsmBr(doneLabel);
  1158. m_writer->MarkAsmJsLabel(falseLabel);
  1159. m_writer->AsmReg2(loadOp, resultInfo.location, falseInfo.location);
  1160. m_writer->MarkAsmJsLabel(doneLabel);
  1161. return resultInfo;
  1162. }
  1163. void
  1164. WasmBytecodeGenerator::EmitBr()
  1165. {
  1166. uint32 depth = GetReader()->m_currentNode.br.depth;
  1167. if (ShouldYieldToBlock(depth))
  1168. {
  1169. EmitInfo info = PopEvalStack();
  1170. YieldToBlock(depth, info);
  1171. ReleaseLocation(&info);
  1172. }
  1173. Js::ByteCodeLabel target = GetLabel(depth);
  1174. m_writer->AsmBr(target);
  1175. SetUnreachableState(true);
  1176. }
  1177. EmitInfo
  1178. WasmBytecodeGenerator::EmitBrIf()
  1179. {
  1180. uint32 depth = GetReader()->m_currentNode.br.depth;
  1181. EmitInfo conditionInfo = PopEvalStack(WasmTypes::I32, _u("br_if condition must have i32 type"));
  1182. ReleaseLocation(&conditionInfo);
  1183. EmitInfo info;
  1184. if (ShouldYieldToBlock(depth))
  1185. {
  1186. info = PopEvalStack();
  1187. YieldToBlock(depth, info);
  1188. }
  1189. Js::ByteCodeLabel target = GetLabel(depth);
  1190. m_writer->AsmBrReg1(Js::OpCodeAsmJs::BrTrue_Int, target, conditionInfo.location);
  1191. return info;
  1192. }
  1193. Js::OpCodeAsmJs
  1194. WasmBytecodeGenerator::GetLoadOp(WasmTypes::WasmType wasmType)
  1195. {
  1196. switch (wasmType)
  1197. {
  1198. case WasmTypes::F32:
  1199. return Js::OpCodeAsmJs::Ld_Flt;
  1200. case WasmTypes::F64:
  1201. return Js::OpCodeAsmJs::Ld_Db;
  1202. case WasmTypes::I32:
  1203. return Js::OpCodeAsmJs::Ld_Int;
  1204. case WasmTypes::I64:
  1205. return Js::OpCodeAsmJs::Ld_Long;
  1206. case WasmTypes::Any:
  1207. // In unreachable mode load the any type like an int since we won't actually emit the load
  1208. Assert(IsUnreachable());
  1209. if (IsUnreachable())
  1210. {
  1211. return Js::OpCodeAsmJs::Ld_Int;
  1212. }
  1213. default:
  1214. throw WasmCompilationException(_u("Unknown load operator %u"), wasmType);
  1215. }
  1216. }
  1217. Js::OpCodeAsmJs
  1218. WasmBytecodeGenerator::GetReturnOp(WasmTypes::WasmType type)
  1219. {
  1220. Js::OpCodeAsmJs retOp = Js::OpCodeAsmJs::Nop;
  1221. switch (type)
  1222. {
  1223. case WasmTypes::F32:
  1224. retOp = Js::OpCodeAsmJs::Return_Flt;
  1225. break;
  1226. case WasmTypes::F64:
  1227. retOp = Js::OpCodeAsmJs::Return_Db;
  1228. break;
  1229. case WasmTypes::I32:
  1230. retOp = Js::OpCodeAsmJs::Return_Int;
  1231. break;
  1232. case WasmTypes::I64:
  1233. retOp = Js::OpCodeAsmJs::Return_Long;
  1234. break;
  1235. case WasmTypes::Any:
  1236. // In unreachable mode load the any type like an int since we won't actually emit the load
  1237. Assert(IsUnreachable());
  1238. if (IsUnreachable())
  1239. {
  1240. return Js::OpCodeAsmJs::Return_Int;
  1241. }
  1242. default:
  1243. throw WasmCompilationException(_u("Unknown return type %u"), type);
  1244. }
  1245. return retOp;
  1246. }
  1247. void
  1248. WasmBytecodeGenerator::ReleaseLocation(EmitInfo * info)
  1249. {
  1250. if (WasmTypes::IsLocalType(info->type))
  1251. {
  1252. GetRegisterSpace(info->type)->ReleaseLocation(info);
  1253. }
  1254. }
  1255. EmitInfo
  1256. WasmBytecodeGenerator::EnsureYield(BlockInfo info)
  1257. {
  1258. EmitInfo yieldEmitInfo;
  1259. if (info.HasYield())
  1260. {
  1261. yieldEmitInfo = info.yieldInfo->info;
  1262. if (!info.DidYield())
  1263. {
  1264. // Emit a load to the yield location to make sure we have a dest there
  1265. // Most likely we can't reach this code so the value doesn't matter
  1266. info.yieldInfo->didYield = true;
  1267. EmitLoadConst(yieldEmitInfo, GetZeroCnst());
  1268. }
  1269. }
  1270. return yieldEmitInfo;
  1271. }
  1272. EmitInfo
  1273. WasmBytecodeGenerator::PopLabel(Js::ByteCodeLabel labelValidation)
  1274. {
  1275. Assert(m_blockInfos.Count() > 0);
  1276. BlockInfo info = m_blockInfos.Pop();
  1277. UNREFERENCED_PARAMETER(labelValidation);
  1278. Assert(info.label == labelValidation);
  1279. return EnsureYield(info);
  1280. }
  1281. BlockInfo
  1282. WasmBytecodeGenerator::PushLabel(Js::ByteCodeLabel label, bool addBlockYieldInfo /*= true*/)
  1283. {
  1284. BlockInfo info;
  1285. info.label = label;
  1286. if (addBlockYieldInfo)
  1287. {
  1288. WasmTypes::WasmType type = GetReader()->m_currentNode.block.sig;
  1289. if (type != WasmTypes::Void)
  1290. {
  1291. info.yieldInfo = Anew(&m_alloc, BlockInfo::YieldInfo);
  1292. info.yieldInfo->info = EmitInfo(GetRegisterSpace(type)->AcquireTmpRegister(), type);
  1293. info.yieldInfo->didYield = false;
  1294. }
  1295. }
  1296. m_blockInfos.Push(info);
  1297. return info;
  1298. }
  1299. void
  1300. WasmBytecodeGenerator::YieldToBlock(uint32 relativeDepth, EmitInfo expr)
  1301. {
  1302. BlockInfo blockInfo = GetBlockInfo(relativeDepth);
  1303. YieldToBlock(blockInfo, expr);
  1304. }
  1305. void WasmBytecodeGenerator::YieldToBlock(BlockInfo blockInfo, EmitInfo expr)
  1306. {
  1307. if (blockInfo.HasYield() && expr.type != WasmTypes::Any)
  1308. {
  1309. EmitInfo yieldInfo = blockInfo.yieldInfo->info;
  1310. if (yieldInfo.type != expr.type)
  1311. {
  1312. throw WasmCompilationException(_u("Invalid yield type"));
  1313. }
  1314. if (!IsUnreachable())
  1315. {
  1316. blockInfo.yieldInfo->didYield = true;
  1317. m_writer->AsmReg2(GetLoadOp(expr.type), yieldInfo.location, expr.location);
  1318. }
  1319. }
  1320. }
  1321. bool
  1322. WasmBytecodeGenerator::ShouldYieldToBlock(uint32 relativeDepth) const
  1323. {
  1324. return GetBlockInfo(relativeDepth).HasYield();
  1325. }
  1326. Wasm::BlockInfo
  1327. WasmBytecodeGenerator::GetBlockInfo(uint32 relativeDepth) const
  1328. {
  1329. if (relativeDepth >= (uint32)m_blockInfos.Count())
  1330. {
  1331. throw WasmCompilationException(_u("Invalid branch target"));
  1332. }
  1333. return m_blockInfos.Peek(relativeDepth);
  1334. }
  1335. Js::ByteCodeLabel
  1336. WasmBytecodeGenerator::GetLabel(uint32 relativeDepth)
  1337. {
  1338. return GetBlockInfo(relativeDepth).label;
  1339. }
  1340. WasmRegisterSpace *
  1341. WasmBytecodeGenerator::GetRegisterSpace(WasmTypes::WasmType type)
  1342. {
  1343. switch (type)
  1344. {
  1345. case WasmTypes::I32: return mTypedRegisterAllocator.GetRegisterSpace(WAsmJs::INT32);
  1346. case WasmTypes::I64: return mTypedRegisterAllocator.GetRegisterSpace(WAsmJs::INT64);
  1347. case WasmTypes::F32: return mTypedRegisterAllocator.GetRegisterSpace(WAsmJs::FLOAT32);
  1348. case WasmTypes::F64: return mTypedRegisterAllocator.GetRegisterSpace(WAsmJs::FLOAT64);
  1349. default:
  1350. return nullptr;
  1351. }
  1352. }
  1353. EmitInfo
  1354. WasmBytecodeGenerator::PopEvalStack(WasmTypes::WasmType expectedType, const char16* mismatchMessage)
  1355. {
  1356. // The scope marker should at least be there
  1357. Assert(!m_evalStack.Empty());
  1358. EmitInfo info = m_evalStack.Pop();
  1359. if (info.type == WasmTypes::Limit)
  1360. {
  1361. throw WasmCompilationException(_u("Reached end of stack"));
  1362. }
  1363. if (expectedType != WasmTypes::Any &&
  1364. info.type != WasmTypes::Any &&
  1365. info.type != expectedType)
  1366. {
  1367. if (!mismatchMessage)
  1368. {
  1369. mismatchMessage = _u("Type mismatch");
  1370. }
  1371. throw WasmCompilationException(mismatchMessage);
  1372. }
  1373. Assert(info.type != WasmTypes::Any || IsUnreachable());
  1374. return info;
  1375. }
  1376. void
  1377. WasmBytecodeGenerator::PushEvalStack(EmitInfo info)
  1378. {
  1379. Assert(!m_evalStack.Empty());
  1380. m_evalStack.Push(info);
  1381. }
  1382. void
  1383. WasmBytecodeGenerator::EnterEvalStackScope()
  1384. {
  1385. m_evalStack.Push(EmitInfo(WasmTypes::Limit));
  1386. }
  1387. void
  1388. WasmBytecodeGenerator::ExitEvalStackScope()
  1389. {
  1390. Assert(!m_evalStack.Empty());
  1391. EmitInfo info = m_evalStack.Pop();
  1392. // It is possible to have unconsumed Any type left on the stack, simply remove them
  1393. while (info.type == WasmTypes::Any)
  1394. {
  1395. Assert(!m_evalStack.Empty());
  1396. info = m_evalStack.Pop();
  1397. }
  1398. if (info.type != WasmTypes::Limit)
  1399. {
  1400. uint32 nElemLeftOnStack = 1;
  1401. while(m_evalStack.Pop().type != WasmTypes::Limit) { ++nElemLeftOnStack; }
  1402. throw WasmCompilationException(_u("Expected stack to be empty, but has %d"), nElemLeftOnStack);
  1403. }
  1404. }
  1405. void WasmBytecodeGenerator::SetUnreachableState(bool isUnreachable)
  1406. {
  1407. m_writer = isUnreachable ? m_emptyWriter : m_originalWriter;
  1408. if (isUnreachable)
  1409. {
  1410. // Replace the current stack with the any type
  1411. Assert(!m_evalStack.Empty());
  1412. uint32 popped = 0;
  1413. while (m_evalStack.Top().type != WasmTypes::Limit)
  1414. {
  1415. EmitInfo info = m_evalStack.Pop();
  1416. ReleaseLocation(&info);
  1417. ++popped;
  1418. }
  1419. while (popped-- > 0)
  1420. {
  1421. m_evalStack.Push(EmitInfo(WasmTypes::Any));
  1422. }
  1423. }
  1424. this->isUnreachable = isUnreachable;
  1425. }
  1426. void
  1427. WasmBytecodeGenerator::SetUsesMemory(uint32 memoryIndex)
  1428. {
  1429. // Only support one memory at this time
  1430. Assert(memoryIndex == 0);
  1431. if (!m_module->HasMemory() && !m_module->HasMemoryImport())
  1432. {
  1433. throw WasmCompilationException(_u("unknown memory"));
  1434. }
  1435. GetFunctionBody()->GetAsmJsFunctionInfo()->SetUsesHeapBuffer(true);
  1436. }
  1437. Wasm::WasmReaderBase*
  1438. WasmBytecodeGenerator::GetReader() const
  1439. {
  1440. if (m_funcInfo->GetCustomReader())
  1441. {
  1442. return m_funcInfo->GetCustomReader();
  1443. }
  1444. return m_module->GetReader();
  1445. }
  1446. void
  1447. WasmCompilationException::FormatError(const char16* _msg, va_list arglist)
  1448. {
  1449. char16 buf[2048];
  1450. _vsnwprintf_s(buf, _countof(buf), _TRUNCATE, _msg, arglist);
  1451. errorMsg = SysAllocString(buf);
  1452. }
  1453. WasmCompilationException::WasmCompilationException(const char16* _msg, ...) : errorMsg(nullptr)
  1454. {
  1455. va_list arglist;
  1456. va_start(arglist, _msg);
  1457. FormatError(_msg, arglist);
  1458. }
  1459. WasmCompilationException::WasmCompilationException(const char16* _msg, va_list arglist) : errorMsg(nullptr)
  1460. {
  1461. FormatError(_msg, arglist);
  1462. }
  1463. } // namespace Wasm
  1464. #endif // ENABLE_WASM