-
Notifications
You must be signed in to change notification settings - Fork 745
/
GlobalRefining.cpp
163 lines (133 loc) · 4.92 KB
/
GlobalRefining.cpp
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
/*
* Copyright 2021 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
*
* https://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.
*/
//
// Apply more specific subtypes to global variables where possible.
//
#include "ir/export-utils.h"
#include "ir/find_all.h"
#include "ir/lubs.h"
#include "ir/module-utils.h"
#include "ir/utils.h"
#include "pass.h"
#include "wasm-type.h"
#include "wasm.h"
namespace wasm {
namespace {
struct GlobalRefining : public Pass {
// Only modifies globals and global.get operations.
bool requiresNonNullableLocalFixups() override { return false; }
void run(Module* module) override {
if (!module->features.hasGC()) {
return;
}
// First, find all the global.sets.
struct GlobalInfo {
std::vector<GlobalSet*> sets;
};
ModuleUtils::ParallelFunctionAnalysis<GlobalInfo> analysis(
*module, [&](Function* func, GlobalInfo& info) {
if (func->imported()) {
return;
}
info.sets = std::move(FindAll<GlobalSet>(func->body).list);
});
// A map of globals to the lub for that global.
std::unordered_map<Name, LUBFinder> lubs;
// Combine all the information we gathered and compute lubs.
for (auto& [func, info] : analysis.map) {
for (auto* set : info.sets) {
lubs[set->name].note(set->value->type);
}
}
// In closed world we cannot change the types of exports, as we might change
// from a public type to a private that would cause a validation error.
// TODO We could refine to a type that is still public, however.
//
// We are also limited in open world: in that mode we must assume that
// another module might import our exported globals with the current type
// (that type is a contract between them), and in such a case the type of
// mutable globals must match precisely (the same rule as for mutable struct
// fields in subtypes - the types must match exactly, or else a write in
// one place could store a type considered in valid in another place).
std::unordered_set<Name> unoptimizable;
for (auto* global : ExportUtils::getExportedGlobals(*module)) {
if (getPassOptions().closedWorld || global->mutable_) {
unoptimizable.insert(global->name);
}
}
bool optimized = false;
for (auto& global : module->globals) {
if (global->imported() || unoptimizable.count(global->name)) {
continue;
}
auto& lub = lubs[global->name];
// Note the initial value.
lub.note(global->init->type);
// The initial value cannot be unreachable, but it might be null, and all
// other values might be too. In that case, we've noted nothing useful
// and we can move on.
if (!lub.noted()) {
continue;
}
auto oldType = global->type;
auto newType = lub.getLUB();
if (newType != oldType) {
// We found an improvement!
assert(Type::isSubType(newType, oldType));
global->type = newType;
optimized = true;
}
}
if (!optimized) {
return;
}
// Update function contents for their new parameter types: global.gets must
// now return the new type for any globals that we modified.
struct GetUpdater : public WalkerPass<PostWalker<GetUpdater>> {
bool isFunctionParallel() override { return true; }
// Only modifies global.get operations.
bool requiresNonNullableLocalFixups() override { return false; }
GlobalRefining& parent;
Module& wasm;
GetUpdater(GlobalRefining& parent, Module& wasm)
: parent(parent), wasm(wasm) {}
std::unique_ptr<Pass> create() override {
return std::make_unique<GetUpdater>(parent, wasm);
}
// If we modify anything in a function then we must refinalize so that
// types propagate outwards.
bool modified = false;
void visitGlobalGet(GlobalGet* curr) {
auto oldType = curr->type;
auto newType = wasm.getGlobal(curr->name)->type;
if (newType != oldType) {
curr->type = newType;
modified = true;
}
}
void visitFunction(Function* curr) {
if (modified) {
ReFinalize().walkFunctionInModule(curr, &wasm);
}
}
} updater(*this, *module);
updater.run(getPassRunner(), module);
updater.runOnModuleCode(getPassRunner(), module);
}
};
} // anonymous namespace
Pass* createGlobalRefiningPass() { return new GlobalRefining(); }
} // namespace wasm