forked from WebAssembly/binaryen
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathshell-interface.h
191 lines (171 loc) · 7.26 KB
/
shell-interface.h
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
/*
* Copyright 2016 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// Implementation of the shell interpreter execution environment
//
#ifndef wasm_shell_interface_h
#define wasm_shell_interface_h
#include "shared-constants.h"
#include "asmjs/shared-constants.h"
#include "support/name.h"
#include "wasm.h"
#include "wasm-interpreter.h"
namespace wasm {
struct ExitException {};
struct TrapException {};
struct ShellExternalInterface : ModuleInstance::ExternalInterface {
// The underlying memory can be accessed through unaligned pointers which
// isn't well-behaved in C++. WebAssembly nonetheless expects it to behave
// properly. Avoid emitting unaligned load/store by checking for alignment
// explicitly, and performing memcpy if unaligned.
//
// The allocated memory tries to have the same alignment as the memory being
// simulated.
class Memory {
// Use char because it doesn't run afoul of aliasing rules.
std::vector<char> memory;
template <typename T>
static bool aligned(const char* address) {
static_assert(!(sizeof(T) & (sizeof(T) - 1)), "must be a power of 2");
return 0 == (reinterpret_cast<uintptr_t>(address) & (sizeof(T) - 1));
}
Memory(Memory&) = delete;
Memory& operator=(const Memory&) = delete;
public:
Memory() {}
void resize(size_t newSize) {
// Ensure the smallest allocation is large enough that most allocators
// will provide page-aligned storage. This hopefully allows the
// interpreter's memory to be as aligned as the memory being simulated,
// ensuring that the performance doesn't needlessly degrade.
//
// The code is optimistic this will work until WG21's p0035r0 happens.
const size_t minSize = 1 << 12;
size_t oldSize = memory.size();
memory.resize(std::max(minSize, newSize));
if (newSize < oldSize && newSize < minSize) {
std::memset(&memory[newSize], 0, minSize - newSize);
}
}
template <typename T>
void set(size_t address, T value) {
if (aligned<T>(&memory[address])) {
*reinterpret_cast<T*>(&memory[address]) = value;
} else {
std::memcpy(&memory[address], &value, sizeof(T));
}
}
template <typename T>
T get(size_t address) {
if (aligned<T>(&memory[address])) {
return *reinterpret_cast<T*>(&memory[address]);
} else {
T loaded;
std::memcpy(&loaded, &memory[address], sizeof(T));
return loaded;
}
}
} memory;
std::vector<Name> table;
ShellExternalInterface() : memory() {}
void init(Module& wasm, ModuleInstance& instance) override {
memory.resize(wasm.memory.initial * wasm::Memory::kPageSize);
// apply memory segments
for (auto& segment : wasm.memory.segments) {
Address offset = ConstantExpressionRunner<TrivialGlobalManager>(instance.globals).visit(segment.offset).value.geti32();
assert(offset + segment.data.size() <= wasm.memory.initial * wasm::Memory::kPageSize);
for (size_t i = 0; i != segment.data.size(); ++i) {
memory.set(offset + i, segment.data[i]);
}
}
table.resize(wasm.table.initial);
for (auto& segment : wasm.table.segments) {
Address offset = ConstantExpressionRunner<TrivialGlobalManager>(instance.globals).visit(segment.offset).value.geti32();
assert(offset + segment.data.size() <= wasm.table.initial);
for (size_t i = 0; i != segment.data.size(); ++i) {
table[offset + i] = segment.data[i];
}
}
}
void importGlobals(std::map<Name, Literal>& globals, Module& wasm) override {
// add spectest globals
for (auto& import : wasm.imports) {
if (import->kind == ExternalKind::Global && import->module == SPECTEST && import->base == GLOBAL) {
switch (import->globalType) {
case i32: globals[import->name] = Literal(int32_t(666)); break;
case i64: globals[import->name] = Literal(int64_t(666)); break;
case f32: globals[import->name] = Literal(float(666.6)); break;
case f64: globals[import->name] = Literal(double(666.6)); break;
default: WASM_UNREACHABLE();
}
} else if (import->kind == ExternalKind::Memory && import->module == SPECTEST && import->base == MEMORY) {
// imported memory has initial 1 and max 2
wasm.memory.initial = 1;
wasm.memory.max = 2;
}
}
}
Literal callImport(Import *import, LiteralList& arguments) override {
if (import->module == SPECTEST && import->base == PRINT) {
for (auto argument : arguments) {
std::cout << argument << '\n';
}
return Literal();
} else if (import->module == ENV && import->base == EXIT) {
// XXX hack for torture tests
std::cout << "exit()\n";
throw ExitException();
}
std::cout << "callImport " << import->name.str << "\n";
WASM_UNREACHABLE();
}
Literal callTable(Index index, LiteralList& arguments, WasmType result, ModuleInstance& instance) override {
if (index >= table.size()) trap("callTable overflow");
auto* func = instance.wasm.getFunctionOrNull(table[index]);
if (!func) trap("uninitialized table element");
if (func->params.size() != arguments.size()) trap("callIndirect: bad # of arguments");
for (size_t i = 0; i < func->params.size(); i++) {
if (func->params[i] != arguments[i].type) {
trap("callIndirect: bad argument type");
}
}
if (func->result != result) {
trap("callIndirect: bad result type");
}
return instance.callFunctionInternal(func->name, arguments);
}
int8_t load8s(Address addr) override { return memory.get<int8_t>(addr); }
uint8_t load8u(Address addr) override { return memory.get<uint8_t>(addr); }
int16_t load16s(Address addr) override { return memory.get<int16_t>(addr); }
uint16_t load16u(Address addr) override { return memory.get<uint16_t>(addr); }
int32_t load32s(Address addr) override { return memory.get<int32_t>(addr); }
uint32_t load32u(Address addr) override { return memory.get<uint32_t>(addr); }
int64_t load64s(Address addr) override { return memory.get<int64_t>(addr); }
uint64_t load64u(Address addr) override { return memory.get<uint64_t>(addr); }
void store8(Address addr, int8_t value) override { memory.set<int8_t>(addr, value); }
void store16(Address addr, int16_t value) override { memory.set<int16_t>(addr, value); }
void store32(Address addr, int32_t value) override { memory.set<int32_t>(addr, value); }
void store64(Address addr, int64_t value) override { memory.set<int64_t>(addr, value); }
void growMemory(Address /*oldSize*/, Address newSize) override {
memory.resize(newSize);
}
void trap(const char* why) override {
std::cerr << "[trap " << why << "]\n";
throw TrapException();
}
};
}
#endif // wasm_shell_interface_h