Skip to content

Commit a88d9b8

Browse files
authored
ctor evaller (#982)
Add wasm-ctor-eval, which evaluates functions at compile time - typically static constructor functions - and applies their effects into memory, saving work at startup. If we encounter something we can't evaluate at compile time in our interpreter, stop there. This is similar to ctor_evaller.py in emscripten (which was for asm.js).
1 parent 5d4f9eb commit a88d9b8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1248
-83
lines changed

CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,14 @@ TARGET_LINK_LIBRARIES(wasm-dis passes wasm asmjs ast support)
265265
SET_PROPERTY(TARGET wasm-dis PROPERTY CXX_STANDARD 11)
266266
SET_PROPERTY(TARGET wasm-dis PROPERTY CXX_STANDARD_REQUIRED ON)
267267
INSTALL(TARGETS wasm-dis DESTINATION ${CMAKE_INSTALL_BINDIR})
268+
269+
SET(wasm-ctor-eval_SOURCES
270+
src/tools/wasm-ctor-eval.cpp
271+
)
272+
ADD_EXECUTABLE(wasm-ctor-eval
273+
${wasm-ctor-eval_SOURCES})
274+
TARGET_LINK_LIBRARIES(wasm-ctor-eval wasm asmjs emscripten-optimizer ${all_passes} ast support)
275+
SET_PROPERTY(TARGET wasm-ctor-eval PROPERTY CXX_STANDARD 11)
276+
SET_PROPERTY(TARGET wasm-ctor-eval PROPERTY CXX_STANDARD_REQUIRED ON)
277+
INSTALL(TARGETS wasm-ctor-eval DESTINATION bin)
278+

auto_update_tests.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,4 +226,17 @@
226226
out = run_command(cmd, stderr=subprocess.STDOUT)
227227
open(os.path.join('test', 'binaryen.js', s + '.txt'), 'w').write(out)
228228

229+
print '\n[ checking wasm-ctor-eval... ]\n'
230+
231+
for t in os.listdir(os.path.join('test', 'ctor-eval')):
232+
if t.endswith(('.wast', '.wasm')):
233+
print '..', t
234+
t = os.path.join('test', 'ctor-eval', t)
235+
ctors = open(t + '.ctors').read().strip()
236+
cmd = [os.path.join('bin', 'wasm-ctor-eval'), t, '-o', 'a.wast', '-S', '--ctors', ctors]
237+
stdout = run_command(cmd)
238+
actual = open('a.wast').read()
239+
out = t + '.out'
240+
with open(out, 'w') as o: o.write(actual)
241+
229242
print '\n[ success! ]'

check.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,20 @@
237237
with open(out + '.stdout') as f:
238238
fail_if_not_identical(f.read(), stdout)
239239

240+
print '\n[ checking wasm-ctor-eval... ]\n'
241+
242+
for t in os.listdir(os.path.join('test', 'ctor-eval')):
243+
if t.endswith(('.wast', '.wasm')):
244+
print '..', t
245+
t = os.path.join('test', 'ctor-eval', t)
246+
ctors = open(t + '.ctors').read().strip()
247+
cmd = [os.path.join('bin', 'wasm-ctor-eval'), t, '-o', 'a.wast', '-S', '--ctors', ctors]
248+
stdout = run_command(cmd)
249+
actual = open('a.wast').read()
250+
out = t + '.out'
251+
with open(out) as f:
252+
fail_if_not_identical(f.read(), actual)
253+
240254
print '\n[ checking wasm-shell spec testcases... ]\n'
241255

242256
if len(requested) == 0:

src/ast/memory-utils.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2017 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef wasm_ast_memory_h
18+
#define wasm_ast_memory_h
19+
20+
#include <algorithm>
21+
#include <vector>
22+
23+
#include "literal.h"
24+
#include "wasm.h"
25+
26+
namespace wasm {
27+
28+
namespace MemoryUtils {
29+
// flattens memory into a single data segment. returns true if successful
30+
inline bool flatten(Memory& memory) {
31+
if (memory.segments.size() == 0) return true;
32+
std::vector<char> data;
33+
for (auto& segment : memory.segments) {
34+
auto* offset = segment.offset->dynCast<Const>();
35+
if (!offset) return false;
36+
}
37+
for (auto& segment : memory.segments) {
38+
auto* offset = segment.offset->dynCast<Const>();
39+
auto start = offset->value.getInteger();
40+
auto end = start + segment.data.size();
41+
if (end > data.size()) {
42+
data.resize(end);
43+
}
44+
std::copy(segment.data.begin(), segment.data.end(), data.begin() + start);
45+
}
46+
memory.segments.resize(1);
47+
memory.segments[0].offset->cast<Const>()->value = Literal(int32_t(0));
48+
memory.segments[0].data.swap(data);
49+
return true;
50+
}
51+
};
52+
53+
} // namespace wasm
54+
55+
#endif // wasm_ast_memory_h
56+

src/passes/Precompute.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
namespace wasm {
2828

29-
Name NONSTANDALONE_FLOW("Binaryen|nonstandalone");
29+
static const Name NONSTANDALONE_FLOW("Binaryen|nonstandalone");
3030

3131
// Execute an expression by itself. Errors if we hit anything we need anything not in the expression itself standalone.
3232
class StandaloneExpressionRunner : public ExpressionRunner<StandaloneExpressionRunner> {

src/shell-interface.h

Lines changed: 16 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
9595
memory.resize(wasm.memory.initial * wasm::Memory::kPageSize);
9696
// apply memory segments
9797
for (auto& segment : wasm.memory.segments) {
98-
Address offset = ConstantExpressionRunner(instance.globals).visit(segment.offset).value.geti32();
98+
Address offset = ConstantExpressionRunner<TrivialGlobalManager>(instance.globals).visit(segment.offset).value.geti32();
9999
assert(offset + segment.data.size() <= wasm.memory.initial * wasm::Memory::kPageSize);
100100
for (size_t i = 0; i != segment.data.size(); ++i) {
101101
memory.set(offset + i, segment.data[i]);
@@ -104,7 +104,7 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
104104

105105
table.resize(wasm.table.initial);
106106
for (auto& segment : wasm.table.segments) {
107-
Address offset = ConstantExpressionRunner(instance.globals).visit(segment.offset).value.geti32();
107+
Address offset = ConstantExpressionRunner<TrivialGlobalManager>(instance.globals).visit(segment.offset).value.geti32();
108108
assert(offset + segment.data.size() <= wasm.table.initial);
109109
for (size_t i = 0; i != segment.data.size(); ++i) {
110110
table[offset + i] = segment.data[i];
@@ -143,7 +143,7 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
143143
throw ExitException();
144144
}
145145
std::cout << "callImport " << import->name.str << "\n";
146-
abort();
146+
WASM_UNREACHABLE();
147147
}
148148

149149
Literal callTable(Index index, LiteralList& arguments, WasmType result, ModuleInstance& instance) override {
@@ -159,60 +159,19 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
159159
return instance.callFunctionInternal(func->name, arguments);
160160
}
161161

162-
Literal load(Load* load, Address addr) override {
163-
switch (load->type) {
164-
case i32: {
165-
switch (load->bytes) {
166-
case 1: return load->signed_ ? Literal((int32_t)memory.get<int8_t>(addr)) : Literal((int32_t)memory.get<uint8_t>(addr));
167-
case 2: return load->signed_ ? Literal((int32_t)memory.get<int16_t>(addr)) : Literal((int32_t)memory.get<uint16_t>(addr));
168-
case 4: return load->signed_ ? Literal((int32_t)memory.get<int32_t>(addr)) : Literal((int32_t)memory.get<uint32_t>(addr));
169-
default: abort();
170-
}
171-
break;
172-
}
173-
case i64: {
174-
switch (load->bytes) {
175-
case 1: return load->signed_ ? Literal((int64_t)memory.get<int8_t>(addr)) : Literal((int64_t)memory.get<uint8_t>(addr));
176-
case 2: return load->signed_ ? Literal((int64_t)memory.get<int16_t>(addr)) : Literal((int64_t)memory.get<uint16_t>(addr));
177-
case 4: return load->signed_ ? Literal((int64_t)memory.get<int32_t>(addr)) : Literal((int64_t)memory.get<uint32_t>(addr));
178-
case 8: return load->signed_ ? Literal((int64_t)memory.get<int64_t>(addr)) : Literal((int64_t)memory.get<uint64_t>(addr));
179-
default: abort();
180-
}
181-
break;
182-
}
183-
case f32: return Literal(memory.get<float>(addr));
184-
case f64: return Literal(memory.get<double>(addr));
185-
default: abort();
186-
}
187-
}
188-
189-
void store(Store* store, Address addr, Literal value) override {
190-
switch (store->valueType) {
191-
case i32: {
192-
switch (store->bytes) {
193-
case 1: memory.set<int8_t>(addr, value.geti32()); break;
194-
case 2: memory.set<int16_t>(addr, value.geti32()); break;
195-
case 4: memory.set<int32_t>(addr, value.geti32()); break;
196-
default: abort();
197-
}
198-
break;
199-
}
200-
case i64: {
201-
switch (store->bytes) {
202-
case 1: memory.set<int8_t>(addr, (int8_t)value.geti64()); break;
203-
case 2: memory.set<int16_t>(addr, (int16_t)value.geti64()); break;
204-
case 4: memory.set<int32_t>(addr, (int32_t)value.geti64()); break;
205-
case 8: memory.set<int64_t>(addr, value.geti64()); break;
206-
default: abort();
207-
}
208-
break;
209-
}
210-
// write floats carefully, ensuring all bits reach memory
211-
case f32: memory.set<int32_t>(addr, value.reinterpreti32()); break;
212-
case f64: memory.set<int64_t>(addr, value.reinterpreti64()); break;
213-
default: abort();
214-
}
215-
}
162+
int8_t load8s(Address addr) override { return memory.get<int8_t>(addr); }
163+
uint8_t load8u(Address addr) override { return memory.get<uint8_t>(addr); }
164+
int16_t load16s(Address addr) override { return memory.get<int16_t>(addr); }
165+
uint16_t load16u(Address addr) override { return memory.get<uint16_t>(addr); }
166+
int32_t load32s(Address addr) override { return memory.get<int32_t>(addr); }
167+
uint32_t load32u(Address addr) override { return memory.get<uint32_t>(addr); }
168+
int64_t load64s(Address addr) override { return memory.get<int64_t>(addr); }
169+
uint64_t load64u(Address addr) override { return memory.get<uint64_t>(addr); }
170+
171+
void store8(Address addr, int8_t value) override { memory.set<int8_t>(addr, value); }
172+
void store16(Address addr, int16_t value) override { memory.set<int16_t>(addr, value); }
173+
void store32(Address addr, int32_t value) override { memory.set<int32_t>(addr, value); }
174+
void store64(Address addr, int64_t value) override { memory.set<int64_t>(addr, value); }
216175

217176
void growMemory(Address /*oldSize*/, Address newSize) override {
218177
memory.resize(newSize);

0 commit comments

Comments
 (0)