forked from chakra-core/ChakraCore
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWasmByteCodeGenerator.h
More file actions
309 lines (272 loc) · 11.2 KB
/
WasmByteCodeGenerator.h
File metadata and controls
309 lines (272 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
//-------------------------------------------------------------------------------------------------------
// 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.
//-------------------------------------------------------------------------------------------------------
#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;
};
struct PolymorphicEmitInfo
{
private:
uint32 count = 0;
union
{
EmitInfo singleInfo;
EmitInfo* infos;
};
public:
PolymorphicEmitInfo(): count(0), infos(nullptr) {}
PolymorphicEmitInfo(EmitInfo info)
{
Init(info);
}
uint32 Count() const { return count; }
void Init(EmitInfo info);
void Init(uint32 count, ArenaAllocator* alloc);
void SetInfo(EmitInfo info, uint32 index);
EmitInfo GetInfo(uint32 index) const;
bool IsUnreachable() const;
bool IsEquivalent(PolymorphicEmitInfo other) const;
};
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
{
PolymorphicEmitInfo paramInfo;
PolymorphicEmitInfo yieldInfo;
bool didYield = false;
Js::ByteCodeLabel label;
bool DidYield() const { return didYield; }
bool HasYield() const
{
return yieldInfo.Count() > 0;
}
bool IsEquivalent(const BlockInfo* other) const
{
if (HasYield() != other->HasYield())
{
return false;
}
if (HasYield())
{
return yieldInfo.IsEquivalent(other->yieldInfo);
}
return true;
}
};
typedef JsUtil::BaseDictionary<uint32, LPCUTF8, ArenaAllocator> WasmExportDictionary;
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 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);
PolymorphicEmitInfo EmitBlock();
void EmitBlockCommon(BlockInfo* blockInfo, bool* endOnElse = nullptr);
PolymorphicEmitInfo EmitLoop();
template<WasmOp wasmOp>
PolymorphicEmitInfo EmitCall();
PolymorphicEmitInfo EmitIfElseExpr();
void EmitBrTable();
EmitInfo EmitDrop();
EmitInfo EmitGrowMemory();
EmitInfo EmitGetLocal();
EmitInfo EmitGetGlobal();
EmitInfo EmitSetGlobal();
EmitInfo EmitSetLocal(bool tee);
void EmitReturnExpr(PolymorphicEmitInfo* explicitRetInfo = nullptr);
EmitInfo EmitSelect();
template<typename WriteFn>
void WriteTypeStack(WriteFn fn) const;
uint32 WriteTypeStackToString(_Out_writes_(maxlen) char16* out, uint32 maxlen) const;
#if DBG_DUMP
uint32 opId = 0;
uint32 lastOpId = 1;
void PrintOpBegin(WasmOp op);
void PrintTypeStack() const;
void PrintOpEnd();
#endif
void EmitBr();
PolymorphicEmitInfo EmitBrIf();
template<bool isStore, bool isAtomic>
EmitInfo EmitMemAccess(WasmOp wasmOp, const WasmTypes::WasmType* signature, Js::ArrayBufferView::ViewType viewType);
EmitInfo EmitSimdMemAccess(Js::OpCodeAsmJs op, const WasmTypes::WasmType* signature, Js::ArrayBufferView::ViewType viewType, uint8 dataWidth, bool isStore);
EmitInfo EmitBinExpr(Js::OpCodeAsmJs op, const WasmTypes::WasmType* signature);
EmitInfo EmitUnaryExpr(Js::OpCodeAsmJs op, const WasmTypes::WasmType* signature);
EmitInfo EmitV128BitSelect();
EmitInfo EmitV8X16Shuffle();
EmitInfo EmitExtractLaneExpr(Js::OpCodeAsmJs op, const WasmTypes::WasmType* signature);
EmitInfo EmitReplaceLaneExpr(Js::OpCodeAsmJs op, const WasmTypes::WasmType* signature);
void CheckLaneIndex(Js::OpCodeAsmJs op, const uint index);
EmitInfo EmitLaneIndex(Js::OpCodeAsmJs op);
EmitInfo EmitConst(WasmTypes::WasmType type, WasmConstLitNode cnst);
void EmitLoadConst(EmitInfo dst, WasmConstLitNode cnst);
WasmConstLitNode GetZeroCnst();
void EnsureStackAvailable();
void EnregisterLocals();
void ReleaseLocation(EmitInfo* info);
void ReleaseLocation(PolymorphicEmitInfo* info);
PolymorphicEmitInfo PopLabel(Js::ByteCodeLabel labelValidation);
BlockInfo* PushLabel(WasmBlock blockData, Js::ByteCodeLabel label, bool addBlockYieldInfo = true, bool checkInParams = true);
void YieldToBlock(BlockInfo* blockInfo, PolymorphicEmitInfo expr);
BlockInfo* GetBlockInfo(uint32 relativeDepth) const;
Js::OpCodeAsmJs GetLoadOp(WasmTypes::WasmType type);
Js::OpCodeAsmJs GetReturnOp(WasmTypes::WasmType type);
WasmRegisterSpace* GetRegisterSpace(WasmTypes::WasmType type);
EmitInfo PopValuePolymorphic() { return PopEvalStack(); }
PolymorphicEmitInfo PopStackPolymorphic(PolymorphicEmitInfo expectedTypes, const char16* mismatchMessage = nullptr);
PolymorphicEmitInfo PopStackPolymorphic(const BlockInfo* blockInfo, const char16* mismatchMessage = nullptr);
EmitInfo PopEvalStack(WasmTypes::WasmType expectedType = WasmTypes::Any, const char16* mismatchMessage = nullptr);
void PushEvalStack(PolymorphicEmitInfo);
PolymorphicEmitInfo EnsureYield(BlockInfo*);
void EnterEvalStackScope(const BlockInfo*);
// The caller needs to release the location of the returned EmitInfo
void ExitEvalStackScope(const BlockInfo*);
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;
Js::ProfileId GetNextProfileId();
bool IsValidating() const { return m_originalWriter == m_emptyWriter; }
ArenaAllocator m_alloc;
bool isUnreachable;
WasmLocal* m_locals;
WasmFunctionInfo* m_funcInfo;
BlockInfo* m_funcBlock;
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;
Js::ProfileId currentProfileId;
JsUtil::Stack<BlockInfo*> m_blockInfos;
JsUtil::Stack<EmitInfo> m_evalStack;
};
}