Skip to content

Commit b5b40c9

Browse files
authored
SSA pass (#1049)
* Add SSA pass which ensures a single assign for each local, except for merged locals where we ensure exactly a single assign from one of the paths leading to that use * Also add InstrumentLocals pass, useful for debugging locals (similar to InstrumentMemory but for locals) * Fix a PickLoadSigns bug with tees not being ignored, which was not noticed until now because we ran it on flatter output by default, but the ssa pass uncovered the bug
1 parent 61b409b commit b5b40c9

19 files changed

Lines changed: 1886 additions & 29 deletions

src/asm2wasm.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,9 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
11621162
} else if (curr[0] == DEFUN) {
11631163
// function
11641164
auto* func = processFunction(curr);
1165+
if (wasm.getFunctionOrNull(func->name)) {
1166+
Fatal() << "duplicate function: " << func->name;
1167+
}
11651168
if (runOptimizationPasses) {
11661169
optimizingBuilder->addFunction(func);
11671170
} else {

src/ast/literal-utils.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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_literl_utils_h
18+
#define wasm_ast_literl_utils_h
19+
20+
#include "wasm.h"
21+
22+
namespace wasm {
23+
24+
namespace LiteralUtils {
25+
26+
inline Expression* makeZero(WasmType type, Module& wasm) {
27+
Literal value;
28+
switch (type) {
29+
case i32: value = Literal(int32_t(0)); break;
30+
case i64: value = Literal(int64_t(0)); break;
31+
case f32: value = Literal(float(0)); break;
32+
case f64: value = Literal(double(0)); break;
33+
default: WASM_UNREACHABLE();
34+
}
35+
auto* ret = wasm.allocator.alloc<Const>();
36+
ret->value = value;
37+
ret->type = value.type;
38+
return ret;
39+
}
40+
41+
} // namespace LiteralUtils
42+
43+
} // namespace wasm
44+
45+
#endif // wasm_ast_literl_utils_h
46+

src/passes/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ SET(passes_SOURCES
1010
LegalizeJSInterface.cpp
1111
LocalCSE.cpp
1212
LogExecution.cpp
13+
InstrumentLocals.cpp
1314
InstrumentMemory.cpp
1415
MemoryPacking.cpp
1516
MergeBlocks.cpp
@@ -32,6 +33,7 @@ SET(passes_SOURCES
3233
ReorderLocals.cpp
3334
ReorderFunctions.cpp
3435
SimplifyLocals.cpp
36+
SSAify.cpp
3537
Vacuum.cpp
3638
)
3739
ADD_LIBRARY(passes STATIC ${passes_SOURCES})

src/passes/CoalesceLocals.cpp

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "cfg/cfg-traversal.h"
3333
#include "wasm-builder.h"
3434
#include "support/learning.h"
35+
#include "support/permutations.h"
3536
#ifdef CFG_PROFILE
3637
#include "support/timing.h"
3738
#endif
@@ -533,35 +534,6 @@ void CoalesceLocals::pickIndicesFromOrder(std::vector<Index>& order, std::vector
533534
}
534535
}
535536

536-
// Utilities for operating on permutation vectors
537-
538-
static std::vector<Index> makeIdentity(Index num) {
539-
std::vector<Index> ret;
540-
ret.resize(num);
541-
for (Index i = 0; i < num; i++) {
542-
ret[i] = i;
543-
}
544-
return ret;
545-
}
546-
547-
static void setIdentity(std::vector<Index>& ret) {
548-
auto num = ret.size();
549-
assert(num > 0); // must already be of the right size
550-
for (Index i = 0; i < num; i++) {
551-
ret[i] = i;
552-
}
553-
}
554-
555-
static std::vector<Index> makeReversed(std::vector<Index>& original) {
556-
std::vector<Index> ret;
557-
auto num = original.size();
558-
ret.resize(num);
559-
for (Index i = 0; i < num; i++) {
560-
ret[original[i]] = i;
561-
}
562-
return ret;
563-
}
564-
565537
// given a baseline order, adjust it based on an important order of priorities (higher values
566538
// are higher priority). The priorities take precedence, unless they are equal and then
567539
// the original order should be kept.

src/passes/InstrumentLocals.cpp

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
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+
//
18+
// Instruments the build with code to intercept all local reads and writes.
19+
//
20+
// gets:
21+
//
22+
// Before:
23+
// (get_local $x)
24+
//
25+
// After:
26+
// (call $get_TYPE
27+
// (i32.const n) // call id
28+
// (i32.const n) // local id
29+
// (get_local $x)
30+
// )
31+
//
32+
// sets:
33+
//
34+
// Before:
35+
// (set_local $x (i32.const 1))
36+
//
37+
// After:
38+
// (set_local $x
39+
// (call $set_TYPE
40+
// (i32.const n) // call id
41+
// (i32.const n) // local id
42+
// (i32.const 1) // value
43+
// )
44+
// )
45+
46+
#include <wasm.h>
47+
#include <wasm-builder.h>
48+
#include <pass.h>
49+
#include "shared-constants.h"
50+
#include "asmjs/shared-constants.h"
51+
#include "asm_v_wasm.h"
52+
53+
namespace wasm {
54+
55+
Name get_i32("get_i32");
56+
Name get_i64("get_i64");
57+
Name get_f32("get_f32");
58+
Name get_f64("get_f64");
59+
60+
Name set_i32("set_i32");
61+
Name set_i64("set_i64");
62+
Name set_f32("set_f32");
63+
Name set_f64("set_f64");
64+
65+
struct InstrumentLocals : public WalkerPass<PostWalker<InstrumentLocals>> {
66+
void visitGetLocal(GetLocal* curr) {
67+
Builder builder(*getModule());
68+
Name import;
69+
switch (curr->type) {
70+
case i32: import = get_i32; break;
71+
case i64: return; // TODO
72+
case f32: import = get_f32; break;
73+
case f64: import = get_f64; break;
74+
default: WASM_UNREACHABLE();
75+
}
76+
replaceCurrent(
77+
builder.makeCallImport(
78+
import,
79+
{
80+
builder.makeConst(Literal(int32_t(id++))),
81+
builder.makeConst(Literal(int32_t(curr->index))),
82+
curr
83+
},
84+
curr->type
85+
)
86+
);
87+
}
88+
89+
void visitSetLocal(SetLocal* curr) {
90+
Builder builder(*getModule());
91+
Name import;
92+
switch (curr->value->type) {
93+
case i32: import = set_i32; break;
94+
case i64: return; // TODO
95+
case f32: import = set_f32; break;
96+
case f64: import = set_f64; break;
97+
case unreachable: return; // nothing to do here
98+
default: WASM_UNREACHABLE();
99+
}
100+
curr->value = builder.makeCallImport(
101+
import,
102+
{
103+
builder.makeConst(Literal(int32_t(id++))),
104+
builder.makeConst(Literal(int32_t(curr->index))),
105+
curr->value
106+
},
107+
curr->value->type
108+
);
109+
}
110+
111+
void visitModule(Module* curr) {
112+
addImport(curr, get_i32, "iiii");
113+
addImport(curr, get_i64, "jiij");
114+
addImport(curr, get_f32, "fiif");
115+
addImport(curr, get_f64, "diid");
116+
addImport(curr, set_i32, "iiii");
117+
addImport(curr, set_i64, "jiij");
118+
addImport(curr, set_f32, "fiif");
119+
addImport(curr, set_f64, "diid");
120+
}
121+
122+
private:
123+
Index id = 0;
124+
125+
void addImport(Module* wasm, Name name, std::string sig) {
126+
auto import = new Import;
127+
import->name = name;
128+
import->module = INSTRUMENT;
129+
import->base = name;
130+
import->functionType = ensureFunctionType(sig, wasm)->name;
131+
import->kind = ExternalKind::Function;
132+
wasm->addImport(import);
133+
}
134+
};
135+
136+
Pass* createInstrumentLocalsPass() {
137+
return new InstrumentLocals();
138+
}
139+
140+
} // namespace wasm

src/passes/PickLoadSigns.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ struct PickLoadSigns : public WalkerPass<ExpressionStackWalker<PickLoadSigns>> {
9494
}
9595

9696
void visitSetLocal(SetLocal* curr) {
97+
if (curr->isTee()) {
98+
// we can't modify a tee, the value is used elsewhere
99+
return;
100+
}
97101
if (auto* load = curr->value->dynCast<Load>()) {
98102
loads[load] = curr->index;
99103
}

0 commit comments

Comments
 (0)