| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "WasmReaderPch.h"
- #ifdef ENABLE_WASM
- #include "Language/WebAssemblySource.h"
- #include "ByteCode/WasmByteCodeWriter.h"
- #include "EmptyWasmByteCodeWriter.h"
- #if DBG_DUMP
- uint32 opId = 0;
- uint32 lastOpId = 1;
- #define DebugPrintOp(op) if (PHASE_TRACE(Js::WasmReaderPhase, GetFunctionBody())) { PrintOpBegin(op); }
- #define DebugPrintOpEnd() if (PHASE_TRACE(Js::WasmReaderPhase, GetFunctionBody())) { PrintOpEnd(); }
- #else
- #define DebugPrintOp(op)
- #define DebugPrintOpEnd()
- #endif
- namespace Wasm
- {
- #define WASM_SIGNATURE(id, nTypes, ...) const WasmTypes::WasmType WasmOpCodeSignatures::id[] = {__VA_ARGS__};
- #include "WasmBinaryOpCodes.h"
- #if DBG_DUMP
- void
- PrintTypeStack(const JsUtil::Stack<EmitInfo>& stack)
- {
- Output::Print(_u("["));
- int i = 0;
- while (stack.Peek(i).type != WasmTypes::Limit)
- {
- ++i;
- }
- --i;
- bool isFirst = true;
- while (i >= 0)
- {
- EmitInfo info = stack.Peek(i--);
- if (!isFirst)
- {
- Output::Print(_u(", "));
- }
- isFirst = false;
- switch (info.type)
- {
- case WasmTypes::I32: Output::Print(_u("i32")); break;
- case WasmTypes::I64: Output::Print(_u("i64")); break;
- case WasmTypes::F32: Output::Print(_u("f32")); break;
- case WasmTypes::F64: Output::Print(_u("f64")); break;
- default: Output::Print(_u("any")); break;
- }
- }
- Output::Print(_u("]"));
- }
- void
- WasmBytecodeGenerator::PrintOpBegin(WasmOp op) const
- {
- if (lastOpId == opId) Output::Print(_u("\r\n"));
- lastOpId = ++opId;
- const int depth = m_blockInfos.Count() - 1;
- if (depth > 0)
- {
- Output::SkipToColumn(depth);
- }
- switch (op)
- {
- #define WASM_OPCODE(opname, opcode, sig, nyi) \
- case wb##opname: \
- Output::Print(_u(#opname)); \
- break;
- #include "WasmBinaryOpCodes.h"
- }
- Output::SkipToColumn(20);
- PrintTypeStack(m_evalStack);
- }
- void
- WasmBytecodeGenerator::PrintOpEnd() const
- {
- if (lastOpId == opId)
- {
- ++opId;
- Output::Print(_u(" -> "));
- PrintTypeStack(m_evalStack);
- Output::Print(_u("\r\n"));
- }
- }
- #endif
- /* static */
- Js::AsmJsRetType
- WasmToAsmJs::GetAsmJsReturnType(WasmTypes::WasmType wasmType)
- {
- switch (wasmType)
- {
- case WasmTypes::I32: return Js::AsmJsRetType::Signed;
- case WasmTypes::I64: return Js::AsmJsRetType::Int64;
- case WasmTypes::F32: return Js::AsmJsRetType::Float;
- case WasmTypes::F64: return Js::AsmJsRetType::Double;
- case WasmTypes::Void: return Js::AsmJsRetType::Void;
- default:
- throw WasmCompilationException(_u("Unknown return type %u"), wasmType);
- }
- }
- /* static */
- Js::AsmJsVarType
- WasmToAsmJs::GetAsmJsVarType(WasmTypes::WasmType wasmType)
- {
- Js::AsmJsVarType asmType = Js::AsmJsVarType::Int;
- switch (wasmType)
- {
- case WasmTypes::I32: return Js::AsmJsVarType::Int;
- case WasmTypes::I64: return Js::AsmJsVarType::Int64;
- case WasmTypes::F32: return Js::AsmJsVarType::Float;
- case WasmTypes::F64: return Js::AsmJsVarType::Double;
- default:
- throw WasmCompilationException(_u("Unknown var type %u"), wasmType);
- }
- }
- typedef bool(*SectionProcessFunc)(WasmModuleGenerator*);
- typedef void(*AfterSectionCallback)(WasmModuleGenerator*);
- WasmModuleGenerator::WasmModuleGenerator(Js::ScriptContext* scriptContext, Js::WebAssemblySource* src) :
- m_sourceInfo(src->GetSourceInfo()),
- m_scriptContext(scriptContext),
- m_recycler(scriptContext->GetRecycler())
- {
- m_module = RecyclerNewFinalized(m_recycler, Js::WebAssemblyModule, scriptContext, src->GetBuffer(), src->GetBufferLength(), scriptContext->GetLibrary()->GetWebAssemblyModuleType());
- m_sourceInfo->EnsureInitialized(0);
- m_sourceInfo->GetSrcInfo()->sourceContextInfo->EnsureInitialized();
- }
- Js::WebAssemblyModule*
- WasmModuleGenerator::GenerateModule()
- {
- m_module->GetReader()->InitializeReader();
- BVStatic<bSectLimit + 1> visitedSections;
- SectionCode nextExpectedSection = bSectCustom;
- while (true)
- {
- SectionHeader sectionHeader = GetReader()->ReadNextSection();
- SectionCode sectionCode = sectionHeader.code;
- if (sectionCode == bSectLimit)
- {
- TRACE_WASM_SECTION(_u("Done reading module's sections"));
- break;
- }
- // Make sure dependency for this section has been seen
- SectionCode precedent = SectionInfo::All[sectionCode].precedent;
- if (precedent != bSectLimit && !visitedSections.Test(precedent))
- {
- throw WasmCompilationException(_u("%s section missing before %s"),
- SectionInfo::All[precedent].name,
- sectionHeader.name);
- }
- visitedSections.Set(sectionCode);
- // Custom section are allowed in any order
- if (sectionCode != bSectCustom)
- {
- if (sectionCode < nextExpectedSection)
- {
- throw WasmCompilationException(_u("Invalid Section %s"), sectionHeader.name);
- }
- nextExpectedSection = SectionCode(sectionCode + 1);
- }
- if (!GetReader()->ProcessCurrentSection())
- {
- throw WasmCompilationException(_u("Error while reading section %s"), sectionHeader.name);
- }
- }
- uint32 funcCount = m_module->GetWasmFunctionCount();
- for (uint32 i = 0; i < funcCount; ++i)
- {
- GenerateFunctionHeader(i);
- }
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- WasmFunctionInfo* firstThunk = nullptr, *lastThunk = nullptr;
- for (uint32 i = 0; i < funcCount; ++i)
- {
- WasmFunctionInfo* info = m_module->GetWasmFunctionInfo(i);
- Assert(info->GetBody());
- if (PHASE_TRACE(Js::WasmInOutPhase, info->GetBody()))
- {
- uint32 index = m_module->GetWasmFunctionCount();
- WasmFunctionInfo* newInfo = m_module->AddWasmFunctionInfo(info->GetSignature());
- if (!firstThunk)
- {
- firstThunk = newInfo;
- }
- lastThunk = newInfo;
- GenerateFunctionHeader(index);
- m_module->SwapWasmFunctionInfo(i, index);
- m_module->AttachCustomInOutTracingReader(newInfo, index);
- }
- }
- if (firstThunk)
- {
- int sourceId = (int)firstThunk->GetBody()->GetSourceContextId();
- char16 range[64];
- swprintf_s(range, 64, _u("%d.%d-%d.%d"),
- sourceId, firstThunk->GetBody()->GetLocalFunctionId(),
- sourceId, lastThunk->GetBody()->GetLocalFunctionId());
- char16 offFullJit[128];
- swprintf_s(offFullJit, 128, _u("-off:fulljit:%s"), range);
- char16 offSimpleJit[128];
- swprintf_s(offSimpleJit, 128, _u("-off:simplejit:%s"), range);
- char16 offLoopJit[128];
- swprintf_s(offLoopJit, 128, _u("-off:jitloopbody:%s"), range);
- char16* argv[] = { nullptr, offFullJit, offSimpleJit, offLoopJit };
- CmdLineArgsParser parser(nullptr);
- parser.Parse(ARRAYSIZE(argv), argv);
- }
- #endif
- #if DBG_DUMP
- if (PHASE_TRACE1(Js::WasmReaderPhase))
- {
- GetReader()->PrintOps();
- }
- #endif
- // If we see a FunctionSignatures section we need to see a FunctionBodies section
- if (visitedSections.Test(bSectFunction) && !visitedSections.Test(bSectFunctionBodies))
- {
- throw WasmCompilationException(_u("Missing required section: %s"), SectionInfo::All[bSectFunctionBodies].name);
- }
- return m_module;
- }
- WasmBinaryReader*
- WasmModuleGenerator::GetReader() const
- {
- return m_module->GetReader();
- }
- void
- WasmModuleGenerator::GenerateFunctionHeader(uint32 index)
- {
- WasmFunctionInfo* wasmInfo = m_module->GetWasmFunctionInfo(index);
- if (!wasmInfo)
- {
- throw WasmCompilationException(_u("Invalid function index %u"), index);
- }
- const char16* functionName = nullptr;
- int nameLength = 0;
- if (wasmInfo->GetNameLength() > 0)
- {
- functionName = wasmInfo->GetName();
- nameLength = wasmInfo->GetNameLength();
- }
- else
- {
- for (uint32 iExport = 0; iExport < m_module->GetExportCount(); ++iExport)
- {
- Wasm::WasmExport* wasmExport = m_module->GetExport(iExport);
- if (wasmExport &&
- wasmExport->kind == ExternalKinds::Function &&
- wasmExport->nameLength > 0 &&
- m_module->GetFunctionIndexType(wasmExport->index) == FunctionIndexTypes::Function &&
- wasmExport->index == wasmInfo->GetNumber())
- {
- nameLength = wasmExport->nameLength + 16;
- char16 * autoName = RecyclerNewArrayLeafZ(m_recycler, char16, nameLength);
- nameLength = swprintf_s(autoName, nameLength, _u("%s[%u]"), wasmExport->name, wasmInfo->GetNumber());
- functionName = autoName;
- break;
- }
- }
- }
- if (!functionName)
- {
- char16 * autoName = RecyclerNewArrayLeafZ(m_recycler, char16, 32);
- nameLength = swprintf_s(autoName, 32, _u("wasm-function[%u]"), wasmInfo->GetNumber());
- functionName = autoName;
- }
- Js::FunctionBody* body = Js::FunctionBody::NewFromRecycler(
- m_scriptContext,
- functionName,
- nameLength,
- 0,
- 0,
- m_sourceInfo,
- m_sourceInfo->GetSrcInfo()->sourceContextInfo->sourceContextId,
- wasmInfo->GetNumber(),
- nullptr,
- Js::FunctionInfo::Attributes::None,
- Js::FunctionBody::Flags_None
- #ifdef PERF_COUNTERS
- , false /* is function from deferred deserialized proxy */
- #endif
- );
- wasmInfo->SetBody(body);
- // TODO (michhol): numbering
- body->SetSourceInfo(0);
- body->AllocateAsmJsFunctionInfo();
- body->SetIsAsmJsFunction(true);
- body->SetIsAsmjsMode(true);
- body->SetIsWasmFunction(true);
- WasmReaderInfo* readerInfo = RecyclerNew(m_recycler, WasmReaderInfo);
- readerInfo->m_funcInfo = wasmInfo;
- readerInfo->m_module = m_module;
- Js::AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo();
- info->SetWasmReaderInfo(readerInfo);
- info->SetWebAssemblyModule(m_module);
- Js::ArgSlot paramCount = wasmInfo->GetParamCount();
- info->SetArgCount(paramCount);
- info->SetWasmSignature(wasmInfo->GetSignature());
- Js::ArgSlot argSizeLength = max(paramCount, 3ui16);
- info->SetArgSizeArrayLength(argSizeLength);
- uint32* argSizeArray = RecyclerNewArrayLeafZ(m_recycler, uint32, argSizeLength);
- info->SetArgsSizesArray(argSizeArray);
- if (paramCount > 0)
- {
- // +1 here because asm.js includes the this pointer
- body->SetInParamsCount(paramCount + 1);
- body->SetReportedInParamsCount(paramCount + 1);
- info->SetArgTypeArray(RecyclerNewArrayLeaf(m_recycler, Js::AsmJsVarType::Which, paramCount));
- }
- else
- {
- // overwrite default value in this case
- body->SetHasImplicitArgIns(false);
- }
- for (Js::ArgSlot i = 0; i < paramCount; ++i)
- {
- WasmTypes::WasmType type = wasmInfo->GetSignature()->GetParam(i);
- info->SetArgType(WasmToAsmJs::GetAsmJsVarType(type), i);
- argSizeArray[i] = wasmInfo->GetSignature()->GetParamSize(i);
- }
- info->SetArgByteSize(wasmInfo->GetSignature()->GetParamsSize());
- info->SetReturnType(WasmToAsmJs::GetAsmJsReturnType(wasmInfo->GetResultType()));
- }
- WAsmJs::RegisterSpace*
- AllocateRegisterSpace(ArenaAllocator* alloc, WAsmJs::Types)
- {
- return Anew(alloc, WAsmJs::RegisterSpace, 1);
- }
- void
- WasmBytecodeGenerator::GenerateFunctionBytecode(Js::ScriptContext* scriptContext, WasmReaderInfo* readerinfo, bool validateOnly /*= false*/)
- {
- WasmBytecodeGenerator generator(scriptContext, readerinfo, validateOnly);
- generator.GenerateFunction();
- if (!generator.GetReader()->IsCurrentFunctionCompleted())
- {
- throw WasmCompilationException(_u("Invalid function format"));
- }
- }
- void WasmBytecodeGenerator::ValidateFunction(Js::ScriptContext* scriptContext, WasmReaderInfo* readerinfo)
- {
- GenerateFunctionBytecode(scriptContext, readerinfo, true);
- }
- WasmBytecodeGenerator::WasmBytecodeGenerator(Js::ScriptContext* scriptContext, WasmReaderInfo* readerInfo, bool validateOnly) :
- m_scriptContext(scriptContext),
- m_alloc(_u("WasmBytecodeGen"), scriptContext->GetThreadContext()->GetPageAllocator(), Js::Throw::OutOfMemory),
- m_evalStack(&m_alloc),
- mTypedRegisterAllocator(&m_alloc, AllocateRegisterSpace, 1 << WAsmJs::SIMD),
- m_blockInfos(&m_alloc),
- isUnreachable(false)
- {
- m_emptyWriter = Anew(&m_alloc, Js::EmptyWasmByteCodeWriter);
- m_writer = m_originalWriter = validateOnly ? m_emptyWriter : Anew(&m_alloc, Js::WasmByteCodeWriter);
- m_writer->Create();
- m_funcInfo = readerInfo->m_funcInfo;
- m_module = readerInfo->m_module;
- // Init reader to current func offset
- GetReader()->SeekToFunctionBody(m_funcInfo);
- // Use binary size to estimate bytecode size
- const uint32 astSize = readerInfo->m_funcInfo->m_readerInfo.size;
- m_writer->InitData(&m_alloc, astSize);
- }
- void
- WasmBytecodeGenerator::GenerateFunction()
- {
- TRACE_WASM_DECODER(_u("GenerateFunction %u \n"), m_funcInfo->GetNumber());
- if (PHASE_OFF(Js::WasmBytecodePhase, GetFunctionBody()))
- {
- throw WasmCompilationException(_u("Compilation skipped"));
- }
- Js::AutoProfilingPhase functionProfiler(m_scriptContext, Js::WasmFunctionBodyPhase);
- Unused(functionProfiler);
- m_maxArgOutDepth = 0;
- m_writer->Begin(GetFunctionBody(), &m_alloc);
- try
- {
- Js::ByteCodeLabel exitLabel = m_writer->DefineLabel();
- m_funcInfo->SetExitLabel(exitLabel);
- EnregisterLocals();
- EnterEvalStackScope();
- // The function's yield type is the return type
- GetReader()->m_currentNode.block.sig = m_funcInfo->GetResultType();
- EmitInfo lastInfo = EmitBlock();
- if (lastInfo.type != WasmTypes::Void || m_funcInfo->GetResultType() == WasmTypes::Void)
- {
- EmitReturnExpr(&lastInfo);
- }
- DebugPrintOpEnd();
- ExitEvalStackScope();
- SetUnreachableState(false);
- m_writer->MarkAsmJsLabel(exitLabel);
- m_writer->EmptyAsm(Js::OpCodeAsmJs::Ret);
- m_writer->End();
- GetReader()->FunctionEnd();
- }
- catch (...)
- {
- GetReader()->FunctionEnd();
- m_originalWriter->Reset();
- throw;
- }
- #if DBG_DUMP
- if (PHASE_DUMP(Js::ByteCodePhase, GetFunctionBody()) && !IsValidating())
- {
- Js::AsmJsByteCodeDumper::Dump(GetFunctionBody(), &mTypedRegisterAllocator, nullptr);
- }
- #endif
- Js::AsmJsFunctionInfo * info = GetFunctionBody()->GetAsmJsFunctionInfo();
- mTypedRegisterAllocator.CommitToFunctionBody(GetFunctionBody());
- mTypedRegisterAllocator.CommitToFunctionInfo(info, GetFunctionBody());
- GetFunctionBody()->CheckAndSetOutParamMaxDepth(m_maxArgOutDepth);
- }
- void
- WasmBytecodeGenerator::EnregisterLocals()
- {
- uint32 nLocals = m_funcInfo->GetLocalCount();
- m_locals = AnewArray(&m_alloc, WasmLocal, nLocals);
- m_funcInfo->GetBody()->SetFirstTmpReg(nLocals);
- for (uint32 i = 0; i < nLocals; ++i)
- {
- WasmTypes::WasmType type = m_funcInfo->GetLocal(i);
- WasmRegisterSpace * regSpace = GetRegisterSpace(type);
- if (regSpace == nullptr)
- {
- throw WasmCompilationException(_u("Unable to find local register space"));
- }
- m_locals[i] = WasmLocal(regSpace->AcquireRegister(), type);
- // Zero only the locals not corresponding to formal parameters.
- if (i >= m_funcInfo->GetParamCount()) {
- switch (type)
- {
- case WasmTypes::F32:
- m_writer->AsmFloat1Const1(Js::OpCodeAsmJs::Ld_FltConst, m_locals[i].location, 0.0f);
- break;
- case WasmTypes::F64:
- m_writer->AsmDouble1Const1(Js::OpCodeAsmJs::Ld_DbConst, m_locals[i].location, 0.0);
- break;
- case WasmTypes::I32:
- m_writer->AsmInt1Const1(Js::OpCodeAsmJs::Ld_IntConst, m_locals[i].location, 0);
- break;
- case WasmTypes::I64:
- m_writer->AsmLong1Const1(Js::OpCodeAsmJs::Ld_LongConst, m_locals[i].location, 0);
- break;
- default:
- Assume(UNREACHED);
- }
- }
- }
- }
- void
- WasmBytecodeGenerator::EmitExpr(WasmOp op)
- {
- DebugPrintOp(op);
- switch (op)
- {
- #define WASM_OPCODE(opname, opcode, sig, nyi) \
- case opcode: \
- if (nyi) throw WasmCompilationException(_u("Operator %s NYI"), _u(#opname)); break;
- #include "WasmBinaryOpCodes.h"
- default:
- break;
- }
- EmitInfo info;
- switch (op)
- {
- case wbGetGlobal:
- info = EmitGetGlobal();
- break;
- case wbSetGlobal:
- info = EmitSetGlobal();
- break;
- case wbGetLocal:
- info = EmitGetLocal();
- break;
- case wbSetLocal:
- info = EmitSetLocal(false);
- break;
- case wbTeeLocal:
- info = EmitSetLocal(true);
- break;
- case wbReturn:
- EmitReturnExpr();
- info.type = WasmTypes::Any;
- break;
- case wbF32Const:
- info = EmitConst(WasmTypes::F32, GetReader()->m_currentNode.cnst);
- break;
- case wbF64Const:
- info = EmitConst(WasmTypes::F64, GetReader()->m_currentNode.cnst);
- break;
- case wbI32Const:
- info = EmitConst(WasmTypes::I32, GetReader()->m_currentNode.cnst);
- break;
- case wbI64Const:
- info = EmitConst(WasmTypes::I64, GetReader()->m_currentNode.cnst);
- break;
- case wbBlock:
- info = EmitBlock();
- break;
- case wbLoop:
- info = EmitLoop();
- break;
- case wbCall:
- info = EmitCall<wbCall>();
- break;
- case wbCallIndirect:
- info = EmitCall<wbCallIndirect>();
- break;
- case wbIf:
- info = EmitIfElseExpr();
- break;
- case wbElse:
- throw WasmCompilationException(_u("Unexpected else opcode"));
- case wbEnd:
- throw WasmCompilationException(_u("Unexpected end opcode"));
- case wbBr:
- EmitBr();
- info.type = WasmTypes::Any;
- break;
- case wbBrIf:
- info = EmitBrIf();
- break;
- case wbSelect:
- info = EmitSelect();
- break;
- case wbBrTable:
- EmitBrTable();
- info.type = WasmTypes::Any;
- break;
- case wbDrop:
- info = EmitDrop();
- break;
- case wbNop:
- return;
- case wbCurrentMemory:
- {
- SetUsesMemory(0);
- Js::RegSlot tempReg = GetRegisterSpace(WasmTypes::I32)->AcquireTmpRegister();
- info = EmitInfo(tempReg, WasmTypes::I32);
- m_writer->AsmReg1(Js::OpCodeAsmJs::CurrentMemory_Int, tempReg);
- break;
- }
- case wbGrowMemory:
- {
- info = EmitGrowMemory();
- break;
- }
- case wbUnreachable:
- m_writer->EmptyAsm(Js::OpCodeAsmJs::Unreachable_Void);
- SetUnreachableState(true);
- info.type = WasmTypes::Any;
- break;
- #define WASM_MEMREAD_OPCODE(opname, opcode, sig, nyi, viewtype) \
- case wb##opname: \
- Assert(WasmOpCodeSignatures::n##sig > 0);\
- info = EmitMemAccess(wb##opname, WasmOpCodeSignatures::sig, viewtype, false); \
- break;
- #define WASM_MEMSTORE_OPCODE(opname, opcode, sig, nyi, viewtype) \
- case wb##opname: \
- Assert(WasmOpCodeSignatures::n##sig > 0);\
- info = EmitMemAccess(wb##opname, WasmOpCodeSignatures::sig, viewtype, true); \
- break;
- #define WASM_BINARY_OPCODE(opname, opcode, sig, asmjsop, nyi) \
- case wb##opname: \
- Assert(WasmOpCodeSignatures::n##sig == 3);\
- info = EmitBinExpr(Js::OpCodeAsmJs::##asmjsop, WasmOpCodeSignatures::sig); \
- break;
- #define WASM_UNARY__OPCODE(opname, opcode, sig, asmjsop, nyi) \
- case wb##opname: \
- Assert(WasmOpCodeSignatures::n##sig == 2);\
- info = EmitUnaryExpr(Js::OpCodeAsmJs::##asmjsop, WasmOpCodeSignatures::sig); \
- break;
- #define WASM_EMPTY__OPCODE(opname, opcode, asmjsop, nyi) \
- case wb##opname: \
- m_writer->EmptyAsm(Js::OpCodeAsmJs::##asmjsop);\
- break;
- #include "WasmBinaryOpCodes.h"
- default:
- throw WasmCompilationException(_u("Unknown expression's op 0x%X"), op);
- }
- if (info.type != WasmTypes::Void)
- {
- PushEvalStack(info);
- }
- DebugPrintOpEnd();
- }
- EmitInfo
- WasmBytecodeGenerator::EmitGetGlobal()
- {
- uint32 globalIndex = GetReader()->m_currentNode.var.num;
- WasmGlobal* global = m_module->GetGlobal(globalIndex);
- WasmTypes::WasmType type = global->GetType();
- Js::RegSlot slot = m_module->GetOffsetForGlobal(global);
- CompileAssert(WasmTypes::I32 == 1);
- CompileAssert(WasmTypes::I64 == 2);
- CompileAssert(WasmTypes::F32 == 3);
- CompileAssert(WasmTypes::F64 == 4);
- static const Js::OpCodeAsmJs globalOpcodes[] = {
- Js::OpCodeAsmJs::LdSlot_Int,
- Js::OpCodeAsmJs::LdSlot_Long,
- Js::OpCodeAsmJs::LdSlot_Flt,
- Js::OpCodeAsmJs::LdSlot_Db
- };
- WasmRegisterSpace * regSpace = GetRegisterSpace(type);
- Js::RegSlot tmpReg = regSpace->AcquireTmpRegister();
- EmitInfo info(tmpReg, type);
- m_writer->AsmSlot(globalOpcodes[type - 1], tmpReg, WasmBytecodeGenerator::ModuleEnvRegister, slot);
- return info;
- }
- EmitInfo
- WasmBytecodeGenerator::EmitSetGlobal()
- {
- uint32 globalIndex = GetReader()->m_currentNode.var.num;
- WasmGlobal* global = m_module->GetGlobal(globalIndex);
- Js::RegSlot slot = m_module->GetOffsetForGlobal(global);
- WasmTypes::WasmType type = global->GetType();
- EmitInfo info = PopEvalStack(type);
- CompileAssert(WasmTypes::I32 == 1);
- CompileAssert(WasmTypes::I64 == 2);
- CompileAssert(WasmTypes::F32 == 3);
- CompileAssert(WasmTypes::F64 == 4);
- static const Js::OpCodeAsmJs globalOpcodes[] = {
- Js::OpCodeAsmJs::StSlot_Int,
- Js::OpCodeAsmJs::StSlot_Long,
- Js::OpCodeAsmJs::StSlot_Flt,
- Js::OpCodeAsmJs::StSlot_Db
- };
- m_writer->AsmSlot(globalOpcodes[type - 1], info.location, WasmBytecodeGenerator::ModuleEnvRegister, slot);
- ReleaseLocation(&info);
- return EmitInfo();
- }
- EmitInfo
- WasmBytecodeGenerator::EmitGetLocal()
- {
- uint32 localIndex = GetReader()->m_currentNode.var.num;
- if (m_funcInfo->GetLocalCount() <= localIndex)
- {
- throw WasmCompilationException(_u("%u is not a valid local"), localIndex);
- }
- WasmLocal local = m_locals[localIndex];
- Js::OpCodeAsmJs op = GetLoadOp(local.type);
- WasmRegisterSpace * regSpace = GetRegisterSpace(local.type);
- Js::RegSlot tmpReg = regSpace->AcquireTmpRegister();
- m_writer->AsmReg2(op, tmpReg, local.location);
- return EmitInfo(tmpReg, local.type);
- }
- EmitInfo
- WasmBytecodeGenerator::EmitSetLocal(bool tee)
- {
- uint32 localNum = GetReader()->m_currentNode.var.num;
- if (localNum >= m_funcInfo->GetLocalCount())
- {
- throw WasmCompilationException(_u("%u is not a valid local"), localNum);
- }
- WasmLocal local = m_locals[localNum];
- EmitInfo info = PopEvalStack(local.type);
- m_writer->AsmReg2(GetLoadOp(local.type), local.location, info.location);
- if (tee)
- {
- return info;
- }
- else
- {
- ReleaseLocation(&info);
- return EmitInfo();
- }
- }
- EmitInfo
- WasmBytecodeGenerator::EmitConst(WasmTypes::WasmType type, WasmConstLitNode cnst)
- {
- Js::RegSlot tmpReg = GetRegisterSpace(type)->AcquireTmpRegister();
- EmitInfo dst(tmpReg, type);
- EmitLoadConst(dst, cnst);
- return dst;
- }
- void
- WasmBytecodeGenerator::EmitLoadConst(EmitInfo dst, WasmConstLitNode cnst)
- {
- switch (dst.type)
- {
- case WasmTypes::F32:
- m_writer->AsmFloat1Const1(Js::OpCodeAsmJs::Ld_FltConst, dst.location, cnst.f32);
- break;
- case WasmTypes::F64:
- m_writer->AsmDouble1Const1(Js::OpCodeAsmJs::Ld_DbConst, dst.location, cnst.f64);
- break;
- case WasmTypes::I32:
- m_writer->AsmInt1Const1(Js::OpCodeAsmJs::Ld_IntConst, dst.location, cnst.i32);
- break;
- case WasmTypes::I64:
- m_writer->AsmLong1Const1(Js::OpCodeAsmJs::Ld_LongConst, dst.location, cnst.i64);
- break;
- default:
- throw WasmCompilationException(_u("Unknown type %u"), dst.type);
- }
- }
- WasmConstLitNode
- WasmBytecodeGenerator::GetZeroCnst()
- {
- WasmConstLitNode cnst = {0};
- return cnst;
- }
- void
- WasmBytecodeGenerator::EnsureStackAvailable()
- {
- if (!ThreadContext::IsCurrentStackAvailable(Js::Constants::MinStackCompile))
- {
- throw WasmCompilationException(_u("Maximum supported nested blocks reached"));
- }
- }
- void
- WasmBytecodeGenerator::EmitBlockCommon(BlockInfo* blockInfo, bool* endOnElse /*= nullptr*/)
- {
- EnsureStackAvailable();
- bool canResetUnreachable = !IsUnreachable();
- WasmOp op;
- EnterEvalStackScope();
- if(endOnElse) *endOnElse = false;
- do {
- op = GetReader()->ReadExpr();
- if (op == wbEnd)
- {
- break;
- }
- if (endOnElse && op == wbElse)
- {
- *endOnElse = true;
- break;
- }
- EmitExpr(op);
- } while (true);
- DebugPrintOp(op);
- if (blockInfo && blockInfo->HasYield())
- {
- EmitInfo info = PopEvalStack();
- YieldToBlock(*blockInfo, info);
- ReleaseLocation(&info);
- }
- ExitEvalStackScope();
- if (canResetUnreachable)
- {
- SetUnreachableState(false);
- }
- }
- EmitInfo
- WasmBytecodeGenerator::EmitBlock()
- {
- Js::ByteCodeLabel blockLabel = m_writer->DefineLabel();
- BlockInfo blockInfo = PushLabel(blockLabel);
- EmitBlockCommon(&blockInfo);
- m_writer->MarkAsmJsLabel(blockLabel);
- EmitInfo yieldInfo = PopLabel(blockLabel);
- // block yields last value
- return yieldInfo;
- }
- EmitInfo
- WasmBytecodeGenerator::EmitLoop()
- {
- Js::ByteCodeLabel loopTailLabel = m_writer->DefineLabel();
- Js::ByteCodeLabel loopHeadLabel = m_writer->DefineLabel();
- Js::ByteCodeLabel loopLandingPadLabel = m_writer->DefineLabel();
- uint32 loopId = m_writer->EnterLoop(loopHeadLabel);
- // Internally we create a block for loop to exit, but semantically, they don't exist so pop it
- BlockInfo implicitBlockInfo = PushLabel(loopTailLabel);
- m_blockInfos.Pop();
- // We don't want nested block to jump directly to the loop header
- // instead, jump to the landing pad and let it jump back to the loop header
- PushLabel(loopLandingPadLabel, false);
- EmitBlockCommon(&implicitBlockInfo);
- PopLabel(loopLandingPadLabel);
- // By default we don't loop, jump over the landing pad
- m_writer->AsmBr(loopTailLabel);
- m_writer->MarkAsmJsLabel(loopLandingPadLabel);
- m_writer->AsmBr(loopHeadLabel);
- // Put the implicit block back on the stack and yield the last expression to it
- m_blockInfos.Push(implicitBlockInfo);
- m_writer->MarkAsmJsLabel(loopTailLabel);
- // Pop the implicit block to resolve the yield correctly
- EmitInfo loopInfo = PopLabel(loopTailLabel);
- m_writer->ExitLoop(loopId);
- return loopInfo;
- }
- template<WasmOp wasmOp>
- EmitInfo
- WasmBytecodeGenerator::EmitCall()
- {
- uint32 funcNum = Js::Constants::UninitializedValue;
- uint32 signatureId = Js::Constants::UninitializedValue;
- WasmSignature * calleeSignature = nullptr;
- EmitInfo indirectIndexInfo;
- const bool isImportCall = GetReader()->m_currentNode.call.funcType == FunctionIndexTypes::Import;
- Assert(isImportCall || GetReader()->m_currentNode.call.funcType == FunctionIndexTypes::Function || GetReader()->m_currentNode.call.funcType == FunctionIndexTypes::ImportThunk);
- switch (wasmOp)
- {
- case wbCall:
- funcNum = GetReader()->m_currentNode.call.num;
- calleeSignature = m_module->GetWasmFunctionInfo(funcNum)->GetSignature();
- break;
- case wbCallIndirect:
- indirectIndexInfo = PopEvalStack(WasmTypes::I32, _u("Indirect call index must be int type"));
- signatureId = GetReader()->m_currentNode.call.num;
- calleeSignature = m_module->GetSignature(signatureId);
- ReleaseLocation(&indirectIndexInfo);
- break;
- default:
- Assume(UNREACHED);
- }
- const auto argOverflow = []
- {
- throw WasmCompilationException(_u("Argument size too big"));
- };
- // emit start call
- Js::ArgSlot argSize;
- Js::OpCodeAsmJs startCallOp;
- if (isImportCall)
- {
- argSize = ArgSlotMath::Mul(calleeSignature->GetParamCount(), sizeof(Js::Var), argOverflow);
- startCallOp = Js::OpCodeAsmJs::StartCall;
- }
- else
- {
- startCallOp = Js::OpCodeAsmJs::I_StartCall;
- argSize = calleeSignature->GetParamsSize();
- }
- // Add return value
- argSize = ArgSlotMath::Add(argSize, sizeof(Js::Var), argOverflow);
- if (!Math::IsAligned<Js::ArgSlot>(argSize, sizeof(Js::Var)))
- {
- AssertMsg(UNREACHED, "Wasm argument size should always be Var aligned");
- throw WasmCompilationException(_u("Internal Error"));
- }
- m_writer->AsmStartCall(startCallOp, argSize);
- Js::ArgSlot nArgs = calleeSignature->GetParamCount();
- // copy args into a list so they could be generated in the right order (FIFO)
- EmitInfo* argsList = AnewArray(&m_alloc, EmitInfo, nArgs);
- for (int i = int(nArgs) - 1; i >= 0; --i)
- {
- EmitInfo info = PopEvalStack(calleeSignature->GetParam((Js::ArgSlot)i), _u("Call argument does not match formal type"));
- // We can release the location of the arguments now, because we won't create new temps between start/call
- ReleaseLocation(&info);
- argsList[i] = info;
- }
- // Skip the this pointer (aka undefined)
- uint32 argLoc = 1;
- for (Js::ArgSlot i = 0; i < nArgs; ++i)
- {
- EmitInfo info = argsList[i];
- Js::OpCodeAsmJs argOp = Js::OpCodeAsmJs::Nop;
- switch (info.type)
- {
- case WasmTypes::F32:
- argOp = isImportCall ? Js::OpCodeAsmJs::ArgOut_Flt : Js::OpCodeAsmJs::I_ArgOut_Flt;
- break;
- case WasmTypes::F64:
- argOp = isImportCall ? Js::OpCodeAsmJs::ArgOut_Db : Js::OpCodeAsmJs::I_ArgOut_Db;
- break;
- case WasmTypes::I32:
- argOp = isImportCall ? Js::OpCodeAsmJs::ArgOut_Int : Js::OpCodeAsmJs::I_ArgOut_Int;
- break;
- case WasmTypes::I64:
- argOp = isImportCall ? Js::OpCodeAsmJs::ArgOut_Long : Js::OpCodeAsmJs::I_ArgOut_Long;
- break;
- case WasmTypes::Any:
- // In unreachable mode allow any type as argument since we won't actually emit the call
- Assert(IsUnreachable());
- if (IsUnreachable())
- {
- argOp = Js::OpCodeAsmJs::ArgOut_Int;
- break;
- }
- // Fall through
- default:
- throw WasmCompilationException(_u("Unknown argument type %u"), info.type);
- }
- m_writer->AsmReg2(argOp, argLoc, info.location);
- // Calculated next argument Js::Var location
- if (isImportCall)
- {
- ++argLoc;
- }
- else
- {
- const Js::ArgSlot currentArgSize = calleeSignature->GetParamSize(i);
- Assert(Math::IsAligned<Js::ArgSlot>(currentArgSize, sizeof(Js::Var)));
- argLoc += currentArgSize / sizeof(Js::Var);
- }
- }
- AdeleteArray(&m_alloc, nArgs, argsList);
- // emit call
- switch (wasmOp)
- {
- case wbCall:
- {
- uint32 offset = isImportCall ? m_module->GetImportFuncOffset() : m_module->GetFuncOffset();
- uint32 index = UInt32Math::Add(offset, funcNum);
- m_writer->AsmSlot(Js::OpCodeAsmJs::LdSlot, 0, 1, index);
- break;
- }
- case wbCallIndirect:
- m_writer->AsmSlot(Js::OpCodeAsmJs::LdSlotArr, 0, 1, m_module->GetTableEnvironmentOffset());
- m_writer->AsmSlot(Js::OpCodeAsmJs::LdArr_WasmFunc, 0, 0, indirectIndexInfo.location);
- m_writer->AsmReg1IntConst1(Js::OpCodeAsmJs::CheckSignature, 0, calleeSignature->GetSignatureId());
- break;
- default:
- Assume(UNREACHED);
- }
- // calculate number of RegSlots(Js::Var) the call consumes
- Js::ArgSlot args;
- Js::OpCodeAsmJs callOp = Js::OpCodeAsmJs::Nop;
- if (isImportCall)
- {
- args = calleeSignature->GetParamCount();
- ArgSlotMath::Inc(args, argOverflow);
- callOp = Js::OpCodeAsmJs::Call;
- }
- else
- {
- Assert(Math::IsAligned<Js::ArgSlot>(argSize, sizeof(Js::Var)));
- args = argSize / sizeof(Js::Var);
- callOp = Js::OpCodeAsmJs::I_Call;
- }
- m_writer->AsmCall(callOp, 0, 0, args, WasmToAsmJs::GetAsmJsReturnType(calleeSignature->GetResultType()));
- // emit result coercion
- EmitInfo retInfo;
- retInfo.type = calleeSignature->GetResultType();
- if (retInfo.type != WasmTypes::Void)
- {
- Js::OpCodeAsmJs convertOp = Js::OpCodeAsmJs::Nop;
- retInfo.location = GetRegisterSpace(retInfo.type)->AcquireTmpRegister();
- switch (retInfo.type)
- {
- case WasmTypes::F32:
- convertOp = isImportCall ? Js::OpCodeAsmJs::Conv_VTF : Js::OpCodeAsmJs::Ld_Flt;
- break;
- case WasmTypes::F64:
- convertOp = isImportCall ? Js::OpCodeAsmJs::Conv_VTD : Js::OpCodeAsmJs::Ld_Db;
- break;
- case WasmTypes::I32:
- convertOp = isImportCall ? Js::OpCodeAsmJs::Conv_VTI : Js::OpCodeAsmJs::Ld_Int;
- break;
- case WasmTypes::I64:
- convertOp = isImportCall ? Js::OpCodeAsmJs::Conv_VTL : Js::OpCodeAsmJs::Ld_Long;
- break;
- default:
- throw WasmCompilationException(_u("Unknown call return type %u"), retInfo.type);
- }
- m_writer->AsmReg2(convertOp, retInfo.location, 0);
- }
- // track stack requirements for out params
- // + 1 for return address
- uint32 maxDepthForLevel = args + 1;
- if (maxDepthForLevel > m_maxArgOutDepth)
- {
- m_maxArgOutDepth = maxDepthForLevel;
- }
- return retInfo;
- }
- EmitInfo
- WasmBytecodeGenerator::EmitIfElseExpr()
- {
- Js::ByteCodeLabel falseLabel = m_writer->DefineLabel();
- Js::ByteCodeLabel endLabel = m_writer->DefineLabel();
- EmitInfo checkExpr = PopEvalStack(WasmTypes::I32, _u("If expression must have type i32"));
- ReleaseLocation(&checkExpr);
- m_writer->AsmBrReg1(Js::OpCodeAsmJs::BrFalse_Int, falseLabel, checkExpr.location);
- BlockInfo blockInfo = PushLabel(endLabel);
- bool endOnElse = false;
- EmitBlockCommon(&blockInfo, &endOnElse);
- EnsureYield(blockInfo);
- m_writer->AsmBr(endLabel);
- m_writer->MarkAsmJsLabel(falseLabel);
- EmitInfo retInfo;
- EmitInfo falseExpr;
- if (endOnElse)
- {
- if (blockInfo.yieldInfo)
- {
- blockInfo.yieldInfo->didYield = false;
- }
- EmitBlockCommon(&blockInfo);
- EnsureYield(blockInfo);
- }
- m_writer->MarkAsmJsLabel(endLabel);
- return PopLabel(endLabel);
- }
- void
- WasmBytecodeGenerator::EmitBrTable()
- {
- const uint32 numTargets = GetReader()->m_currentNode.brTable.numTargets;
- const uint32* targetTable = GetReader()->m_currentNode.brTable.targetTable;
- const uint32 defaultEntry = GetReader()->m_currentNode.brTable.defaultTarget;
- // Compile scrutinee
- EmitInfo scrutineeInfo = PopEvalStack(WasmTypes::I32, _u("br_table expression must be of type i32"));
- m_writer->AsmReg2(Js::OpCodeAsmJs::BeginSwitch_Int, scrutineeInfo.location, scrutineeInfo.location);
- EmitInfo yieldInfo;
- if (ShouldYieldToBlock(defaultEntry))
- {
- // If the scrutinee is any then check the stack before popping
- if (scrutineeInfo.type == WasmTypes::Any && m_evalStack.Peek().type == WasmTypes::Limit)
- {
- yieldInfo = scrutineeInfo;
- }
- else
- {
- yieldInfo = PopEvalStack();
- }
- }
- // Compile cases
- for (uint32 i = 0; i < numTargets; i++)
- {
- uint32 target = targetTable[i];
- YieldToBlock(target, yieldInfo);
- Js::ByteCodeLabel targetLabel = GetLabel(target);
- m_writer->AsmBrReg1Const1(Js::OpCodeAsmJs::Case_IntConst, targetLabel, scrutineeInfo.location, i);
- }
- YieldToBlock(defaultEntry, yieldInfo);
- m_writer->AsmBr(GetLabel(defaultEntry), Js::OpCodeAsmJs::EndSwitch_Int);
- ReleaseLocation(&scrutineeInfo);
- ReleaseLocation(&yieldInfo);
- SetUnreachableState(true);
- }
- EmitInfo
- WasmBytecodeGenerator::EmitGrowMemory()
- {
- SetUsesMemory(0);
- EmitInfo info = PopEvalStack(WasmTypes::I32, _u("Invalid type for GrowMemory"));
- m_writer->AsmReg2(Js::OpCodeAsmJs::GrowMemory, info.location, info.location);
- return info;
- }
- EmitInfo
- WasmBytecodeGenerator::EmitDrop()
- {
- EmitInfo info = PopEvalStack();
- ReleaseLocation(&info);
- return EmitInfo();
- }
- EmitInfo
- WasmBytecodeGenerator::EmitBinExpr(Js::OpCodeAsmJs op, const WasmTypes::WasmType* signature)
- {
- WasmTypes::WasmType resultType = signature[0];
- WasmTypes::WasmType lhsType = signature[1];
- WasmTypes::WasmType rhsType = signature[2];
- EmitInfo rhs = PopEvalStack(lhsType);
- EmitInfo lhs = PopEvalStack(rhsType);
- ReleaseLocation(&rhs);
- ReleaseLocation(&lhs);
- Js::RegSlot resultReg = GetRegisterSpace(resultType)->AcquireTmpRegister();
- m_writer->AsmReg3(op, resultReg, lhs.location, rhs.location);
- return EmitInfo(resultReg, resultType);
- }
- EmitInfo
- WasmBytecodeGenerator::EmitUnaryExpr(Js::OpCodeAsmJs op, const WasmTypes::WasmType* signature)
- {
- WasmTypes::WasmType resultType = signature[0];
- WasmTypes::WasmType inputType = signature[1];
- EmitInfo info = PopEvalStack(inputType);
- ReleaseLocation(&info);
- if (resultType == WasmTypes::Void)
- {
- m_writer->AsmReg2(op, 0, info.location);
- return EmitInfo();
- }
- Js::RegSlot resultReg = GetRegisterSpace(resultType)->AcquireTmpRegister();
- m_writer->AsmReg2(op, resultReg, info.location);
- return EmitInfo(resultReg, resultType);
- }
- EmitInfo
- WasmBytecodeGenerator::EmitMemAccess(WasmOp wasmOp, const WasmTypes::WasmType* signature, Js::ArrayBufferView::ViewType viewType, bool isStore)
- {
- WasmTypes::WasmType type = signature[0];
- SetUsesMemory(0);
- const uint32 mask = Js::ArrayBufferView::ViewMask[viewType];
- const uint32 alignment = GetReader()->m_currentNode.mem.alignment;
- const uint32 offset = GetReader()->m_currentNode.mem.offset;
- if ((mask << 1) & (1 << alignment))
- {
- throw WasmCompilationException(_u("alignment must not be larger than natural"));
- }
- EmitInfo rhsInfo;
- if (isStore)
- {
- rhsInfo = PopEvalStack(type, _u("Invalid type for store op"));
- }
- EmitInfo exprInfo = PopEvalStack(WasmTypes::I32, _u("Index expression must be of type i32"));
- if (isStore) // Stores
- {
- m_writer->WasmMemAccess(Js::OpCodeAsmJs::StArrWasm, rhsInfo.location, exprInfo.location, offset, viewType);
- ReleaseLocation(&rhsInfo);
- ReleaseLocation(&exprInfo);
- return EmitInfo();
- }
- ReleaseLocation(&exprInfo);
- Js::RegSlot resultReg = GetRegisterSpace(type)->AcquireTmpRegister();
- m_writer->WasmMemAccess(Js::OpCodeAsmJs::LdArrWasm, resultReg, exprInfo.location, offset, viewType);
- EmitInfo yieldInfo;
- if (!isStore)
- {
- // Yield only on load
- yieldInfo = EmitInfo(resultReg, type);
- }
- return yieldInfo;
- }
- void
- WasmBytecodeGenerator::EmitReturnExpr(EmitInfo* explicitRetInfo)
- {
- if (m_funcInfo->GetResultType() == WasmTypes::Void)
- {
- // TODO (michhol): consider moving off explicit 0 for return reg
- m_writer->AsmReg1(Js::OpCodeAsmJs::LdUndef, 0);
- }
- else
- {
- EmitInfo retExprInfo = explicitRetInfo ? *explicitRetInfo : PopEvalStack();
- if (retExprInfo.type != WasmTypes::Any && m_funcInfo->GetResultType() != retExprInfo.type)
- {
- throw WasmCompilationException(_u("Result type must match return type"));
- }
- Js::OpCodeAsmJs retOp = GetReturnOp(retExprInfo.type);
- m_writer->Conv(retOp, 0, retExprInfo.location);
- ReleaseLocation(&retExprInfo);
- }
- m_writer->AsmBr(m_funcInfo->GetExitLabel());
- SetUnreachableState(true);
- }
- EmitInfo
- WasmBytecodeGenerator::EmitSelect()
- {
- EmitInfo conditionInfo = PopEvalStack(WasmTypes::I32, _u("select condition must have i32 type"));
- EmitInfo falseInfo = PopEvalStack();
- EmitInfo trueInfo = PopEvalStack(falseInfo.type, _u("select operands must both have same type"));
- ReleaseLocation(&conditionInfo);
- ReleaseLocation(&falseInfo);
- ReleaseLocation(&trueInfo);
- if (IsUnreachable())
- {
- if (trueInfo.type == WasmTypes::Any)
- {
- // Report the type of falseInfo for type checking
- return EmitInfo(falseInfo.type);
- }
- // Otherwise report the type of trueInfo for type checking
- return EmitInfo(trueInfo.type);
- }
- WasmTypes::WasmType selectType = trueInfo.type;
- EmitInfo resultInfo = EmitInfo(GetRegisterSpace(selectType)->AcquireTmpRegister(), selectType);
- Js::ByteCodeLabel falseLabel = m_writer->DefineLabel();
- Js::ByteCodeLabel doneLabel = m_writer->DefineLabel();
- Js::OpCodeAsmJs loadOp = GetLoadOp(resultInfo.type);
- // var result;
- // if (!condition) goto:condFalse
- // result = trueRes;
- // goto:done;
- //:condFalse
- // result = falseRes;
- //:done
- m_writer->AsmBrReg1(Js::OpCodeAsmJs::BrFalse_Int, falseLabel, conditionInfo.location);
- m_writer->AsmReg2(loadOp, resultInfo.location, trueInfo.location);
- m_writer->AsmBr(doneLabel);
- m_writer->MarkAsmJsLabel(falseLabel);
- m_writer->AsmReg2(loadOp, resultInfo.location, falseInfo.location);
- m_writer->MarkAsmJsLabel(doneLabel);
- return resultInfo;
- }
- void
- WasmBytecodeGenerator::EmitBr()
- {
- uint32 depth = GetReader()->m_currentNode.br.depth;
- if (ShouldYieldToBlock(depth))
- {
- EmitInfo info = PopEvalStack();
- YieldToBlock(depth, info);
- ReleaseLocation(&info);
- }
- Js::ByteCodeLabel target = GetLabel(depth);
- m_writer->AsmBr(target);
- SetUnreachableState(true);
- }
- EmitInfo
- WasmBytecodeGenerator::EmitBrIf()
- {
- uint32 depth = GetReader()->m_currentNode.br.depth;
- EmitInfo conditionInfo = PopEvalStack(WasmTypes::I32, _u("br_if condition must have i32 type"));
- ReleaseLocation(&conditionInfo);
- EmitInfo info;
- if (ShouldYieldToBlock(depth))
- {
- info = PopEvalStack();
- YieldToBlock(depth, info);
- }
- Js::ByteCodeLabel target = GetLabel(depth);
- m_writer->AsmBrReg1(Js::OpCodeAsmJs::BrTrue_Int, target, conditionInfo.location);
- return info;
- }
- Js::OpCodeAsmJs
- WasmBytecodeGenerator::GetLoadOp(WasmTypes::WasmType wasmType)
- {
- switch (wasmType)
- {
- case WasmTypes::F32:
- return Js::OpCodeAsmJs::Ld_Flt;
- case WasmTypes::F64:
- return Js::OpCodeAsmJs::Ld_Db;
- case WasmTypes::I32:
- return Js::OpCodeAsmJs::Ld_Int;
- case WasmTypes::I64:
- return Js::OpCodeAsmJs::Ld_Long;
- case WasmTypes::Any:
- // In unreachable mode load the any type like an int since we won't actually emit the load
- Assert(IsUnreachable());
- if (IsUnreachable())
- {
- return Js::OpCodeAsmJs::Ld_Int;
- }
- default:
- throw WasmCompilationException(_u("Unknown load operator %u"), wasmType);
- }
- }
- Js::OpCodeAsmJs
- WasmBytecodeGenerator::GetReturnOp(WasmTypes::WasmType type)
- {
- Js::OpCodeAsmJs retOp = Js::OpCodeAsmJs::Nop;
- switch (type)
- {
- case WasmTypes::F32:
- retOp = Js::OpCodeAsmJs::Return_Flt;
- break;
- case WasmTypes::F64:
- retOp = Js::OpCodeAsmJs::Return_Db;
- break;
- case WasmTypes::I32:
- retOp = Js::OpCodeAsmJs::Return_Int;
- break;
- case WasmTypes::I64:
- retOp = Js::OpCodeAsmJs::Return_Long;
- break;
- case WasmTypes::Any:
- // In unreachable mode load the any type like an int since we won't actually emit the load
- Assert(IsUnreachable());
- if (IsUnreachable())
- {
- return Js::OpCodeAsmJs::Return_Int;
- }
- default:
- throw WasmCompilationException(_u("Unknown return type %u"), type);
- }
- return retOp;
- }
- void
- WasmBytecodeGenerator::ReleaseLocation(EmitInfo * info)
- {
- if (WasmTypes::IsLocalType(info->type))
- {
- GetRegisterSpace(info->type)->ReleaseLocation(info);
- }
- }
- EmitInfo
- WasmBytecodeGenerator::EnsureYield(BlockInfo info)
- {
- EmitInfo yieldEmitInfo;
- if (info.HasYield())
- {
- yieldEmitInfo = info.yieldInfo->info;
- if (!info.DidYield())
- {
- // Emit a load to the yield location to make sure we have a dest there
- // Most likely we can't reach this code so the value doesn't matter
- info.yieldInfo->didYield = true;
- EmitLoadConst(yieldEmitInfo, GetZeroCnst());
- }
- }
- return yieldEmitInfo;
- }
- EmitInfo
- WasmBytecodeGenerator::PopLabel(Js::ByteCodeLabel labelValidation)
- {
- Assert(m_blockInfos.Count() > 0);
- BlockInfo info = m_blockInfos.Pop();
- UNREFERENCED_PARAMETER(labelValidation);
- Assert(info.label == labelValidation);
- return EnsureYield(info);
- }
- BlockInfo
- WasmBytecodeGenerator::PushLabel(Js::ByteCodeLabel label, bool addBlockYieldInfo /*= true*/)
- {
- BlockInfo info;
- info.label = label;
- if (addBlockYieldInfo)
- {
- WasmTypes::WasmType type = GetReader()->m_currentNode.block.sig;
- if (type != WasmTypes::Void)
- {
- info.yieldInfo = Anew(&m_alloc, BlockInfo::YieldInfo);
- info.yieldInfo->info = EmitInfo(GetRegisterSpace(type)->AcquireTmpRegister(), type);
- info.yieldInfo->didYield = false;
- }
- }
- m_blockInfos.Push(info);
- return info;
- }
- void
- WasmBytecodeGenerator::YieldToBlock(uint32 relativeDepth, EmitInfo expr)
- {
- BlockInfo blockInfo = GetBlockInfo(relativeDepth);
- YieldToBlock(blockInfo, expr);
- }
- void WasmBytecodeGenerator::YieldToBlock(BlockInfo blockInfo, EmitInfo expr)
- {
- if (blockInfo.HasYield() && expr.type != WasmTypes::Any)
- {
- EmitInfo yieldInfo = blockInfo.yieldInfo->info;
- if (yieldInfo.type != expr.type)
- {
- throw WasmCompilationException(_u("Invalid yield type"));
- }
- if (!IsUnreachable())
- {
- blockInfo.yieldInfo->didYield = true;
- m_writer->AsmReg2(GetLoadOp(expr.type), yieldInfo.location, expr.location);
- }
- }
- }
- bool
- WasmBytecodeGenerator::ShouldYieldToBlock(uint32 relativeDepth) const
- {
- return GetBlockInfo(relativeDepth).HasYield();
- }
- Wasm::BlockInfo
- WasmBytecodeGenerator::GetBlockInfo(uint32 relativeDepth) const
- {
- if (relativeDepth >= (uint32)m_blockInfos.Count())
- {
- throw WasmCompilationException(_u("Invalid branch target"));
- }
- return m_blockInfos.Peek(relativeDepth);
- }
- Js::ByteCodeLabel
- WasmBytecodeGenerator::GetLabel(uint32 relativeDepth)
- {
- return GetBlockInfo(relativeDepth).label;
- }
- WasmRegisterSpace *
- WasmBytecodeGenerator::GetRegisterSpace(WasmTypes::WasmType type)
- {
- switch (type)
- {
- case WasmTypes::I32: return mTypedRegisterAllocator.GetRegisterSpace(WAsmJs::INT32);
- case WasmTypes::I64: return mTypedRegisterAllocator.GetRegisterSpace(WAsmJs::INT64);
- case WasmTypes::F32: return mTypedRegisterAllocator.GetRegisterSpace(WAsmJs::FLOAT32);
- case WasmTypes::F64: return mTypedRegisterAllocator.GetRegisterSpace(WAsmJs::FLOAT64);
- default:
- return nullptr;
- }
- }
- EmitInfo
- WasmBytecodeGenerator::PopEvalStack(WasmTypes::WasmType expectedType, const char16* mismatchMessage)
- {
- // The scope marker should at least be there
- Assert(!m_evalStack.Empty());
- EmitInfo info = m_evalStack.Pop();
- if (info.type == WasmTypes::Limit)
- {
- throw WasmCompilationException(_u("Reached end of stack"));
- }
- if (expectedType != WasmTypes::Any &&
- info.type != WasmTypes::Any &&
- info.type != expectedType)
- {
- if (!mismatchMessage)
- {
- mismatchMessage = _u("Type mismatch");
- }
- throw WasmCompilationException(mismatchMessage);
- }
- Assert(info.type != WasmTypes::Any || IsUnreachable());
- return info;
- }
- void
- WasmBytecodeGenerator::PushEvalStack(EmitInfo info)
- {
- Assert(!m_evalStack.Empty());
- m_evalStack.Push(info);
- }
- void
- WasmBytecodeGenerator::EnterEvalStackScope()
- {
- m_evalStack.Push(EmitInfo(WasmTypes::Limit));
- }
- void
- WasmBytecodeGenerator::ExitEvalStackScope()
- {
- Assert(!m_evalStack.Empty());
- EmitInfo info = m_evalStack.Pop();
- // It is possible to have unconsumed Any type left on the stack, simply remove them
- while (info.type == WasmTypes::Any)
- {
- Assert(!m_evalStack.Empty());
- info = m_evalStack.Pop();
- }
- if (info.type != WasmTypes::Limit)
- {
- uint32 nElemLeftOnStack = 1;
- while(m_evalStack.Pop().type != WasmTypes::Limit) { ++nElemLeftOnStack; }
- throw WasmCompilationException(_u("Expected stack to be empty, but has %d"), nElemLeftOnStack);
- }
- }
- void WasmBytecodeGenerator::SetUnreachableState(bool isUnreachable)
- {
- m_writer = isUnreachable ? m_emptyWriter : m_originalWriter;
- if (isUnreachable)
- {
- // Replace the current stack with the any type
- Assert(!m_evalStack.Empty());
- uint32 popped = 0;
- while (m_evalStack.Top().type != WasmTypes::Limit)
- {
- EmitInfo info = m_evalStack.Pop();
- ReleaseLocation(&info);
- ++popped;
- }
- while (popped-- > 0)
- {
- m_evalStack.Push(EmitInfo(WasmTypes::Any));
- }
- }
- this->isUnreachable = isUnreachable;
- }
- void
- WasmBytecodeGenerator::SetUsesMemory(uint32 memoryIndex)
- {
- // Only support one memory at this time
- Assert(memoryIndex == 0);
- if (!m_module->HasMemory() && !m_module->HasMemoryImport())
- {
- throw WasmCompilationException(_u("unknown memory"));
- }
- GetFunctionBody()->GetAsmJsFunctionInfo()->SetUsesHeapBuffer(true);
- }
- Wasm::WasmReaderBase*
- WasmBytecodeGenerator::GetReader() const
- {
- if (m_funcInfo->GetCustomReader())
- {
- return m_funcInfo->GetCustomReader();
- }
- return m_module->GetReader();
- }
- void
- WasmCompilationException::FormatError(const char16* _msg, va_list arglist)
- {
- char16 buf[2048];
- _vsnwprintf_s(buf, _countof(buf), _TRUNCATE, _msg, arglist);
- errorMsg = SysAllocString(buf);
- }
- WasmCompilationException::WasmCompilationException(const char16* _msg, ...) : errorMsg(nullptr)
- {
- va_list arglist;
- va_start(arglist, _msg);
- FormatError(_msg, arglist);
- }
- WasmCompilationException::WasmCompilationException(const char16* _msg, va_list arglist) : errorMsg(nullptr)
- {
- FormatError(_msg, arglist);
- }
- } // namespace Wasm
- #endif // ENABLE_WASM
|