Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
if (curr->name.is()) {
o << ' ' << curr->name;
}
if (isConcreteWasmType(curr->type)) {
o << ' ' << printWasmType(curr->type);
}
incIndent();
auto block = curr->body->dynCast<Block>();
if (!full && block && block->name.isNull()) {
Expand Down
93 changes: 67 additions & 26 deletions src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ enum ElementType {
};

namespace UserSections {
extern const char* Names;
extern const char* Name;
}

enum ASTNodes {
Expand Down Expand Up @@ -424,7 +424,7 @@ enum ASTNodes {
GetGlobal = 0x1a,
SetGlobal = 0x1b,

Nop = 0x00,
Unreachable = 0x00,
Block = 0x01,
Loop = 0x02,
If = 0x03,
Expand All @@ -434,7 +434,7 @@ enum ASTNodes {
BrIf = 0x07,
TableSwitch = 0x08,
Return = 0x09,
Unreachable = 0x0a,
Nop = 0x0a,
Drop = 0x0b,
End = 0x0f
};
Expand All @@ -451,6 +451,20 @@ enum TypeForms {

} // namespace BinaryConsts


struct ArityChecker : public PostWalker<ArityChecker, Visitor<ArityChecker>> {
std::unordered_map<cashew::IString, bool> arities;

ArityChecker(Expression* function) {
walk(function);
}

void visitBreak(Break* curr) {
// Assume the module has already beeen type-checked, and that all breaks have matching arity.
if (curr->value) arities[curr->name] = true;
}
};

inline int8_t binaryWasmType(WasmType type) {
switch (type) {
case none: return 0;
Expand All @@ -468,6 +482,8 @@ class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> {
bool debug;
bool debugInfo = true;

std::unordered_map<cashew::IString, bool> brTargetArities;

MixedArena allocator;

void prepare() {
Expand Down Expand Up @@ -701,6 +717,10 @@ class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> {
if (numLocalsByType[i64]) o << U32LEB(numLocalsByType[i64]) << binaryWasmType(i64);
if (numLocalsByType[f32]) o << U32LEB(numLocalsByType[f32]) << binaryWasmType(f32);
if (numLocalsByType[f64]) o << U32LEB(numLocalsByType[f64]) << binaryWasmType(f64);

ArityChecker ar(function->body);
brTargetArities = std::move(ar.arities);

writeExpression(function->body);
size_t size = o.size() - start;
assert(size <= std::numeric_limits<uint32_t>::max());
Expand Down Expand Up @@ -825,7 +845,7 @@ class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> {
if (wasm->functions.size() == 0) return;
if (debug) std::cerr << "== writeNames" << std::endl;
auto start = startSection(BinaryConsts::Section::User);
writeInlineString("names");
writeInlineString(BinaryConsts::UserSections::Name);
o << U32LEB(wasm->functions.size());
for (auto& curr : wasm->functions) {
writeInlineString(curr->name.str);
Expand Down Expand Up @@ -898,6 +918,18 @@ class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> {
void visitBlock(Block *curr) {
if (debug) std::cerr << "zz node: Block" << std::endl;
o << int8_t(BinaryConsts::Block);

int arity = curr->type != none && curr->type != unreachable;
if (brTargetArities.count(curr->name)) {
if (curr->type == unreachable) {
arity = brTargetArities[curr->name];
} else {
assert((curr->type != none) == brTargetArities[curr->name]);
}
}
// For blocks with type unreachable but whose breaks have arity 1, encode i32 as their
// signature so that the decoder knows to pop a value for the breaks' values.
o << binaryWasmType(curr->type != unreachable ? curr->type : arity ? i32 : none);
breakStack.push_back(curr->name);
size_t i = 0;
for (auto* child : curr->list) {
Expand All @@ -924,6 +956,7 @@ class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> {
if (debug) std::cerr << "zz node: If" << std::endl;
recurse(curr->condition);
o << int8_t(BinaryConsts::If);
o << binaryWasmType(curr->type != unreachable ? curr->type : none);
breakStack.push_back(IMPOSSIBLE_CONTINUE); // the binary format requires this; we have a block if we need one; TODO: optimize
recursePossibleBlockContents(curr->ifTrue); // TODO: emit block contents directly, if possible
breakStack.pop_back();
Expand All @@ -938,6 +971,7 @@ class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> {
void visitLoop(Loop *curr) {
if (debug) std::cerr << "zz node: Loop" << std::endl;
o << int8_t(BinaryConsts::Loop);
o << binaryWasmType(curr->type != unreachable ? curr->type : none);
breakStack.push_back(curr->name);
recursePossibleBlockContents(curr->body);
breakStack.pop_back();
Expand All @@ -961,19 +995,19 @@ class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> {
}
if (curr->condition) recurse(curr->condition);
o << int8_t(curr->condition ? BinaryConsts::BrIf : BinaryConsts::Br)
<< U32LEB(curr->value ? 1 : 0) << U32LEB(getBreakIndex(curr->name));
<< U32LEB(getBreakIndex(curr->name));
}
void visitSwitch(Switch *curr) {
if (debug) std::cerr << "zz node: Switch" << std::endl;
if (curr->value) {
recurse(curr->value);
}
recurse(curr->condition);
o << int8_t(BinaryConsts::TableSwitch) << U32LEB(curr->value ? 1 : 0) << U32LEB(curr->targets.size());
o << int8_t(BinaryConsts::TableSwitch) << U32LEB(curr->targets.size());
for (auto target : curr->targets) {
o << uint32_t(getBreakIndex(target));
o << U32LEB(getBreakIndex(target));
}
o << uint32_t(getBreakIndex(curr->default_));
o << U32LEB(getBreakIndex(curr->default_));
}
void visitCall(Call *curr) {
if (debug) std::cerr << "zz node: Call" << std::endl;
Expand Down Expand Up @@ -1340,7 +1374,7 @@ class WasmBinaryBuilder {

bool readUserSection() {
Name sectionName = getInlineString();
if (sectionName.equals(BinaryConsts::UserSections::Names)) {
if (sectionName.equals(BinaryConsts::UserSections::Name)) {
readNames();
return true;
}
Expand Down Expand Up @@ -1720,7 +1754,8 @@ class WasmBinaryBuilder {
}
}

std::vector<Name> breakStack;
struct BreakTarget { Name name; int arity;};
std::vector<BreakTarget> breakStack;

std::vector<Expression*> expressionStack;

Expand Down Expand Up @@ -1908,8 +1943,9 @@ class WasmBinaryBuilder {
// a common pattern that can be very highly nested.
std::vector<Block*> stack;
while (1) {
curr->type = getWasmType();
curr->name = getNextLabel();
breakStack.push_back(curr->name);
breakStack.push_back({curr->name, curr->type != none});
stack.push_back(curr);
if (getInt8() == BinaryConsts::Block) {
// a recursion
Expand Down Expand Up @@ -1960,9 +1996,9 @@ class WasmBinaryBuilder {
return block;
}

Expression* getBlock() {
Expression* getBlock(WasmType ty) {
Name label = getNextLabel();
breakStack.push_back(label);
breakStack.push_back({label, ty != none && ty != unreachable});
auto* block = Builder(wasm).blockify(getMaybeBlock());
breakStack.pop_back();
block->cast<Block>()->name = label;
Expand All @@ -1971,48 +2007,53 @@ class WasmBinaryBuilder {

void visitIf(If *curr) {
if (debug) std::cerr << "zz node: If" << std::endl;
curr->type = getWasmType();
curr->condition = popExpression();
curr->ifTrue = getBlock();
curr->ifTrue = getBlock(curr->type);
if (lastSeparator == BinaryConsts::Else) {
curr->ifFalse = getBlock();
curr->ifFalse = getBlock(curr->type);
curr->finalize();
}
assert(lastSeparator == BinaryConsts::End);
}
void visitLoop(Loop *curr) {
if (debug) std::cerr << "zz node: Loop" << std::endl;
curr->type = getWasmType();
curr->name = getNextLabel();
breakStack.push_back(curr->name);
breakStack.push_back({curr->name, 0});
curr->body = getMaybeBlock();
breakStack.pop_back();
curr->finalize();
}

Name getBreakName(int32_t offset) {
BreakTarget getBreakTarget(int32_t offset) {
if (debug) std::cerr << "getBreakTarget "<<offset<<std::endl;
assert(breakStack.size() - 1 - offset < breakStack.size());
if (debug) std::cerr <<"breaktarget "<< breakStack[breakStack.size() - 1 - offset].name<< " arity "<<breakStack[breakStack.size() - 1 - offset].arity<< std::endl;
return breakStack[breakStack.size() - 1 - offset];
}

void visitBreak(Break *curr, uint8_t code) {
if (debug) std::cerr << "zz node: Break" << std::endl;
auto arity = getU32LEB();
assert(arity == 0 || arity == 1);
curr->name = getBreakName(getU32LEB());
BreakTarget target = getBreakTarget(getU32LEB());
curr->name = target.name;
if (code == BinaryConsts::BrIf) curr->condition = popExpression();
if (arity == 1) curr->value = popExpression();
if (target.arity) curr->value = popExpression();
curr->finalize();
}
void visitSwitch(Switch *curr) {
if (debug) std::cerr << "zz node: Switch" << std::endl;
auto arity = getU32LEB();
assert(arity == 0 || arity == 1);
curr->condition = popExpression();
if (arity == 1) curr->value = popExpression();

auto numTargets = getU32LEB();
if (debug) std::cerr << "targets: "<< numTargets<<std::endl;
for (size_t i = 0; i < numTargets; i++) {
curr->targets.push_back(getBreakName(getInt32()));
curr->targets.push_back(getBreakTarget(getU32LEB()).name);
}
curr->default_ = getBreakName(getInt32());
auto defaultTarget = getBreakTarget(getU32LEB());
curr->default_ = defaultTarget.name;
if (debug) std::cerr << "default: "<< curr->default_<<std::endl;
if (defaultTarget.arity) curr->value = popExpression();
}

template<typename T>
Expand Down
2 changes: 1 addition & 1 deletion src/wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Name WASM("wasm"),

namespace BinaryConsts {
namespace UserSections {
const char* Names = "names";
const char* Name = "name";
}
}

Expand Down
8 changes: 4 additions & 4 deletions test/example/c-api-kitchen-sink.txt
Original file line number Diff line number Diff line change
Expand Up @@ -415,12 +415,12 @@ BinaryenFloat64: 4
)
)
(drop
(loop $in
(loop $in i32
(i32.const 0)
)
)
(drop
(loop
(loop i32
(i32.const 0)
)
)
Expand Down Expand Up @@ -1958,12 +1958,12 @@ int main() {
)
)
(drop
(loop $in
(loop $in i32
(i32.const 0)
)
)
(drop
(loop
(loop i32
(i32.const 0)
)
)
Expand Down
4 changes: 2 additions & 2 deletions test/example/c-api-kitchen-sink.txt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -410,12 +410,12 @@
)
)
(drop
(loop $in
(loop $in i32
(i32.const 0)
)
)
(drop
(loop
(loop i32
(i32.const 0)
)
)
Expand Down
2 changes: 1 addition & 1 deletion test/passes/nm.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
)
(func $b (type $0)
(drop
(loop $loop-in1
(loop $loop-in1 i32
(nop)
(i32.const 1000)
)
Expand Down
2 changes: 1 addition & 1 deletion test/passes/simplify-locals.txt
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@
(i32.const 1337)
)
(drop
(loop $loop-in5
(loop $loop-in5 i32
(drop
(get_local $a)
)
Expand Down
2 changes: 1 addition & 1 deletion test/unit.wast
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@
(i32.const 0)
)
(func $loop-roundtrip (type $7) (param $0 f64) (result f64)
(loop $loop-in1
(loop $loop-in1 f64
(drop
(get_local $0)
)
Expand Down
2 changes: 1 addition & 1 deletion test/unit.wast.fromBinary
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@
)
)
(func $loop-roundtrip (type $7) (param $var$0 f64) (result f64)
(loop $label$0
(loop $label$0 f64
(drop
(get_local $var$0)
)
Expand Down
2 changes: 1 addition & 1 deletion test/unit.wast.fromBinary.noDebugInfo
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@
)
)
(func $20 (type $7) (param $var$0 f64) (result f64)
(loop $label$0
(loop $label$0 f64
(drop
(get_local $var$0)
)
Expand Down