Skip to content

Commit 5049f07

Browse files
authored
[ctor-eval] Refactor an applyToModule() method instead of hacks [NFC] (#4425)
Previously this would hackishly apply all execution changes to the memory all the time, and then "undo" it by saving the state before and copying that in. Instead, this PR makes execution write into a side buffer, and now there is a clear method for when we want to actually apply the results to the module.
1 parent 5e3a53b commit 5049f07

File tree

1 file changed

+38
-19
lines changed

1 file changed

+38
-19
lines changed

src/tools/wasm-ctor-eval.cpp

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -199,12 +199,24 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
199199
EvallingModuleInstance* instance;
200200
std::map<Name, std::shared_ptr<EvallingModuleInstance>> linkedInstances;
201201

202+
// A representation of the contents of wasm memory as we execute.
203+
std::vector<char> memory;
204+
202205
CtorEvalExternalInterface(
203206
std::map<Name, std::shared_ptr<EvallingModuleInstance>> linkedInstances_ =
204207
{}) {
205208
linkedInstances.swap(linkedInstances_);
206209
}
207210

211+
// Called when we want to apply the current state of execution to the Module.
212+
// Until this is called the Module is never changed.
213+
void applyToModule() {
214+
// If nothing was ever written to memory then there is nothing to update.
215+
if (!memory.empty()) {
216+
applyMemoryToModule();
217+
}
218+
}
219+
208220
void init(Module& wasm_, EvallingModuleInstance& instance_) override {
209221
wasm = &wasm_;
210222
instance = &instance_;
@@ -365,22 +377,12 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
365377
// TODO: handle unaligned too, see shell-interface
366378

367379
template<typename T> T* getMemory(Address address) {
368-
// this must be in the singleton segment. resize as needed
369-
if (wasm->memory.segments.size() == 0) {
370-
std::vector<char> temp;
371-
Builder builder(*wasm);
372-
wasm->memory.segments.push_back(
373-
Memory::Segment(builder.makeConst(int32_t(0)), temp));
374-
}
375-
// memory should already have been flattened
376-
assert(wasm->memory.segments[0].offset->cast<Const>()->value.getInteger() ==
377-
0);
380+
// resize the memory buffer as needed.
378381
auto max = address + sizeof(T);
379-
auto& data = wasm->memory.segments[0].data;
380-
if (max > data.size()) {
381-
data.resize(max);
382+
if (max > memory.size()) {
383+
memory.resize(max);
382384
}
383-
return (T*)(&data[address]);
385+
return (T*)(&memory[address]);
384386
}
385387

386388
template<typename T> void doStore(Address address, T value) {
@@ -394,6 +396,23 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
394396
memcpy(&ret, getMemory<T>(address), sizeof(T));
395397
return ret;
396398
}
399+
400+
void applyMemoryToModule() {
401+
// Memory must have already been flattened into the standard form: one
402+
// segment at offset 0, or none.
403+
if (wasm->memory.segments.empty()) {
404+
Builder builder(*wasm);
405+
std::vector<char> empty;
406+
wasm->memory.segments.push_back(
407+
Memory::Segment(builder.makeConst(int32_t(0)), empty));
408+
}
409+
auto& segment = wasm->memory.segments[0];
410+
assert(segment.offset->cast<Const>()->value.getInteger() == 0);
411+
412+
// Copy the current memory contents after execution into the Module's
413+
// memory.
414+
segment.data = memory;
415+
}
397416
};
398417

399418
void evalCtors(Module& wasm, std::vector<std::string> ctors) {
@@ -422,8 +441,6 @@ void evalCtors(Module& wasm, std::vector<std::string> ctors) {
422441
// TODO: if we knew priorities, we could reorder?
423442
for (auto& ctor : ctors) {
424443
std::cerr << "trying to eval " << ctor << '\n';
425-
// snapshot memory, as either the entire function is done, or none
426-
auto memoryBefore = wasm.memory;
427444
// snapshot globals (note that STACKTOP might be modified, but should
428445
// be returned, so that works out)
429446
auto globalsBefore = instance.globals;
@@ -437,16 +454,18 @@ void evalCtors(Module& wasm, std::vector<std::string> ctors) {
437454
// that's it, we failed, so stop here, cleaning up partial
438455
// memory changes first
439456
std::cerr << " ...stopping since could not eval: " << fail.why << "\n";
440-
wasm.memory = memoryBefore;
441457
return;
442458
}
443459
if (instance.globals != globalsBefore) {
444460
std::cerr << " ...stopping since globals modified\n";
445-
wasm.memory = memoryBefore;
446461
return;
447462
}
448463
std::cerr << " ...success on " << ctor << ".\n";
449-
// success, the entire function was evalled!
464+
465+
// Success, the entire function was evalled! Apply the results of
466+
// execution to the module.
467+
interface.applyToModule();
468+
450469
// we can nop the function (which may be used elsewhere)
451470
// and remove the export
452471
auto* exp = wasm.getExport(ctor);

0 commit comments

Comments
 (0)