| //------------------------------------------------------------------------------------------------------- |
| // Copyright (C) Microsoft. All rights reserved. |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. |
| //------------------------------------------------------------------------------------------------------- |
| #pragma once |
| |
| namespace Js |
| { |
| class WebAssemblySource; |
| struct IWasmByteCodeWriter; |
| } |
| |
| namespace Wasm |
| { |
| struct EmitInfo : WAsmJs::EmitInfoBase |
| { |
| EmitInfo(Js::RegSlot location_, const WasmTypes::WasmType& type_) : |
| WAsmJs::EmitInfoBase(location_), type(type_) |
| { |
| } |
| EmitInfo(const WasmTypes::WasmType& type_) : type(type_) {} |
| EmitInfo() : type(WasmTypes::Void) {} |
| |
| WasmTypes::WasmType type; |
| }; |
| typedef WAsmJs::RegisterSpace WasmRegisterSpace; |
| |
| struct WasmLocal |
| { |
| WasmLocal() : |
| location(Js::Constants::NoRegister), type(WasmTypes::Limit) |
| { |
| } |
| WasmLocal(Js::RegSlot loc, WasmTypes::WasmType tp) : |
| location(loc), type(tp) |
| { |
| } |
| Js::RegSlot location; |
| WasmTypes::WasmType type; |
| }; |
| |
| class WasmToAsmJs |
| { |
| public: |
| static Js::AsmJsRetType GetAsmJsReturnType(WasmTypes::WasmType wasmType); |
| static Js::AsmJsVarType GetAsmJsVarType(WasmTypes::WasmType wasmType); |
| }; |
| |
| class WasmCompilationException |
| { |
| void FormatError(const char16* _msg, va_list arglist); |
| BSTR errorMsg; |
| // We need to explicitly delete these; simply not including them makes compilers do |
| // generation of simple copy-construct and copy-assign functions, which incorrectly |
| // copy around the errorMsg pointer, making it harder to work with the lifetime. If |
| // we just use the PREVENT_COPY macro (which defines the functions as private) then |
| // we get linker errors, since MSVC doesn't check the accessibility of the function |
| // references when templating, and tries to link to the undefined functions. Explit |
| // deletion of the functions is therefore required here. |
| #if !defined(_MSC_VER) || _MSC_VER >= 1900 |
| private: |
| WasmCompilationException(const WasmCompilationException&) = delete; |
| WasmCompilationException& operator=(const WasmCompilationException& other) = delete; |
| #else //if defined(_MSC_VER) && _MSC_VER < 1900 |
| // For older versions of VS, we need to provide copy construct/assign operators due |
| // to the lack of the ability to throw-by-move. |
| public: |
| WasmCompilationException(const WasmCompilationException& other) |
| { |
| errorMsg = SysAllocString(other.errorMsg); |
| } |
| WasmCompilationException& operator=(const WasmCompilationException& other) |
| { |
| if(this != &other) |
| { |
| SysFreeString(errorMsg); |
| errorMsg = SysAllocString(other.errorMsg); |
| } |
| return *this; |
| } |
| #endif |
| public: |
| WasmCompilationException(const char16* _msg, ...); |
| WasmCompilationException(const char16* _msg, va_list arglist); |
| WasmCompilationException(WasmCompilationException&& other) |
| { |
| errorMsg = other.errorMsg; |
| other.errorMsg = nullptr; |
| } |
| |
| ~WasmCompilationException() |
| { |
| SysFreeString(errorMsg); |
| } |
| |
| BSTR ReleaseErrorMessage() |
| { |
| Assert(errorMsg); |
| BSTR msg = errorMsg; |
| errorMsg = nullptr; |
| return msg; |
| } |
| |
| WasmCompilationException& operator=(WasmCompilationException&& other) |
| { |
| if (this != &other) |
| { |
| SysFreeString(errorMsg); |
| errorMsg = other.errorMsg; |
| other.errorMsg = nullptr; |
| } |
| return *this; |
| } |
| |
| BSTR GetTempErrorMessageRef() |
| { |
| // This is basically a work-around for some odd lifetime scoping with throw |
| return errorMsg; |
| } |
| }; |
| |
| struct BlockInfo |
| { |
| struct YieldInfo |
| { |
| EmitInfo info; |
| bool didYield = false; |
| } *yieldInfo = nullptr; |
| Js::ByteCodeLabel label; |
| bool DidYield() const { return HasYield() && yieldInfo->didYield; } |
| bool HasYield() const { return yieldInfo != nullptr; } |
| }; |
| |
| typedef JsUtil::BaseDictionary<uint32, LPCUTF8, ArenaAllocator> WasmExportDictionary; |
| |
| struct WasmReaderInfo |
| { |
| Field(WasmFunctionInfo*) m_funcInfo; |
| Field(Js::WebAssemblyModule*) m_module; |
| Field(Js::Var) m_bufferSrc; |
| }; |
| |
| class WasmModuleGenerator |
| { |
| public: |
| WasmModuleGenerator(Js::ScriptContext* scriptContext, Js::WebAssemblySource* src); |
| Js::WebAssemblyModule* GenerateModule(); |
| void GenerateFunctionHeader(uint32 index); |
| private: |
| WasmBinaryReader* GetReader() const; |
| |
| Memory::Recycler* m_recycler; |
| Js::Utf8SourceInfo* m_sourceInfo; |
| Js::ScriptContext* m_scriptContext; |
| Js::WebAssemblyModule* m_module; |
| }; |
| |
| class WasmBytecodeGenerator |
| { |
| public: |
| static const Js::RegSlot ModuleSlotRegister = 0; |
| static const Js::RegSlot ReturnRegister = 0; |
| |
| static const Js::RegSlot FunctionRegister = 0; |
| static const Js::RegSlot CallReturnRegister = 0; |
| static const Js::RegSlot ModuleEnvRegister = 1; |
| static const Js::RegSlot ArrayBufferRegister = 2; |
| static const Js::RegSlot ArraySizeRegister = 3; |
| static const Js::RegSlot ScriptContextBufferRegister = 4; |
| static const Js::RegSlot ReservedRegisterCount = 5; |
| |
| WasmBytecodeGenerator(Js::ScriptContext* scriptContext, WasmReaderInfo* readerinfo, bool validateOnly); |
| static void GenerateFunctionBytecode(Js::ScriptContext* scriptContext, WasmReaderInfo* readerinfo, bool validateOnly = false); |
| static void ValidateFunction(Js::ScriptContext* scriptContext, WasmReaderInfo* readerinfo); |
| |
| private: |
| void GenerateFunction(); |
| |
| void EmitExpr(WasmOp op); |
| EmitInfo EmitBlock(); |
| void EmitBlockCommon(BlockInfo* blockInfo, bool* endOnElse = nullptr); |
| EmitInfo EmitLoop(); |
| |
| template<WasmOp wasmOp> |
| EmitInfo EmitCall(); |
| EmitInfo EmitIfElseExpr(); |
| void EmitBrTable(); |
| EmitInfo EmitDrop(); |
| EmitInfo EmitGrowMemory(); |
| EmitInfo EmitGetLocal(); |
| EmitInfo EmitGetGlobal(); |
| EmitInfo EmitSetGlobal(); |
| EmitInfo EmitSetLocal(bool tee); |
| void EmitReturnExpr(EmitInfo* explicitRetInfo = nullptr); |
| EmitInfo EmitSelect(); |
| #if DBG_DUMP |
| void PrintOpBegin(WasmOp op) const; |
| void PrintOpEnd() const; |
| #endif |
| void EmitBr(); |
| EmitInfo EmitBrIf(); |
| |
| EmitInfo EmitMemAccess(WasmOp wasmOp, const WasmTypes::WasmType* signature, Js::ArrayBufferView::ViewType viewType, bool isStore); |
| EmitInfo EmitBinExpr(Js::OpCodeAsmJs op, const WasmTypes::WasmType* signature); |
| EmitInfo EmitUnaryExpr(Js::OpCodeAsmJs op, const WasmTypes::WasmType* signature); |
| |
| EmitInfo EmitConst(WasmTypes::WasmType type, WasmConstLitNode cnst); |
| void EmitLoadConst(EmitInfo dst, WasmConstLitNode cnst); |
| WasmConstLitNode GetZeroCnst(); |
| |
| void EnsureStackAvailable(); |
| void EnregisterLocals(); |
| void ReleaseLocation(EmitInfo* info); |
| |
| EmitInfo PopLabel(Js::ByteCodeLabel labelValidation); |
| BlockInfo PushLabel(Js::ByteCodeLabel label, bool addBlockYieldInfo = true); |
| void YieldToBlock(BlockInfo blockInfo, EmitInfo expr); |
| void YieldToBlock(uint32 relativeDepth, EmitInfo expr); |
| bool ShouldYieldToBlock(uint32 relativeDepth) const; |
| BlockInfo GetBlockInfo(uint32 relativeDepth) const; |
| Js::ByteCodeLabel GetLabel(uint32 relativeDepth); |
| |
| Js::OpCodeAsmJs GetLoadOp(WasmTypes::WasmType type); |
| Js::OpCodeAsmJs GetReturnOp(WasmTypes::WasmType type); |
| WasmRegisterSpace* GetRegisterSpace(WasmTypes::WasmType type); |
| |
| EmitInfo PopEvalStack(WasmTypes::WasmType expectedType = WasmTypes::Any, const char16* mismatchMessage = nullptr); |
| void PushEvalStack(EmitInfo); |
| EmitInfo EnsureYield(BlockInfo); |
| void EnterEvalStackScope(); |
| // The caller needs to release the location of the returned EmitInfo |
| void ExitEvalStackScope(); |
| void SetUnreachableState(bool isUnreachable); |
| bool IsUnreachable() const { return this->isUnreachable; } |
| void SetUsesMemory(uint32 memoryIndex); |
| |
| Js::FunctionBody* GetFunctionBody() const { return m_funcInfo->GetBody(); } |
| WasmReaderBase* GetReader() const; |
| |
| bool IsValidating() const { return m_originalWriter == m_emptyWriter; } |
| |
| ArenaAllocator m_alloc; |
| |
| bool isUnreachable; |
| WasmLocal* m_locals; |
| |
| WasmFunctionInfo* m_funcInfo; |
| Js::WebAssemblyModule* m_module; |
| |
| uint32 m_maxArgOutDepth; |
| |
| Js::IWasmByteCodeWriter* m_writer; |
| Js::IWasmByteCodeWriter* m_emptyWriter; |
| Js::IWasmByteCodeWriter* m_originalWriter; |
| Js::ScriptContext* m_scriptContext; |
| |
| WAsmJs::TypedRegisterAllocator mTypedRegisterAllocator; |
| |
| JsUtil::Stack<BlockInfo> m_blockInfos; |
| JsUtil::Stack<EmitInfo> m_evalStack; |
| }; |
| } |