Merge pull request #8339 from Luukdegram/wasm-control-flow

stage2: Wasm control flow
This commit is contained in:
Andrew Kelley
2021-03-22 19:52:31 -07:00
committed by GitHub
2 changed files with 76 additions and 3 deletions

View File

@@ -95,7 +95,7 @@ pub const Context = struct {
return switch (ty.tag()) {
.f32 => wasm.valtype(.f32),
.f64 => wasm.valtype(.f64),
.u32, .i32 => wasm.valtype(.i32),
.u32, .i32, .bool => wasm.valtype(.i32),
.u64, .i64 => wasm.valtype(.i64),
else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}),
};
@@ -208,6 +208,7 @@ pub const Context = struct {
.alloc => self.genAlloc(inst.castTag(.alloc).?),
.arg => self.genArg(inst.castTag(.arg).?),
.block => self.genBlock(inst.castTag(.block).?),
.breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),
.br => self.genBr(inst.castTag(.br).?),
.call => self.genCall(inst.castTag(.call).?),
.cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
@@ -221,9 +222,11 @@ pub const Context = struct {
.dbg_stmt => WValue.none,
.load => self.genLoad(inst.castTag(.load).?),
.loop => self.genLoop(inst.castTag(.loop).?),
.not => self.genNot(inst.castTag(.not).?),
.ret => self.genRet(inst.castTag(.ret).?),
.retvoid => WValue.none,
.store => self.genStore(inst.castTag(.store).?),
.unreach => self.genUnreachable(inst.castTag(.unreach).?),
else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}),
};
}
@@ -329,7 +332,7 @@ pub const Context = struct {
try writer.writeByte(wasm.opcode(.i32_const));
try leb.writeILEB128(writer, inst.val.toUnsignedInt());
},
.i32 => {
.i32, .bool => {
try writer.writeByte(wasm.opcode(.i32_const));
try leb.writeILEB128(writer, inst.val.toSignedInt());
},
@@ -414,7 +417,14 @@ pub const Context = struct {
// insert blocks at the position of `offset` so
// the condition can jump to it
const offset = condition.code_offset;
const offset = switch (condition) {
.code_offset => |offset| offset,
else => blk: {
const offset = self.code.items.len;
try self.emitWValue(condition);
break :blk offset;
},
};
const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty);
try self.startBlock(.block, block_ty, offset);
@@ -523,4 +533,32 @@ pub const Context = struct {
return .none;
}
fn genNot(self: *Context, not: *Inst.UnOp) InnerError!WValue {
const offset = self.code.items.len;
const operand = self.resolveInst(not.operand);
try self.emitWValue(operand);
// wasm does not have booleans nor the `not` instruction, therefore compare with 0
// to create the same logic
const writer = self.code.writer();
try writer.writeByte(wasm.opcode(.i32_const));
try leb.writeILEB128(writer, @as(i32, 0));
try writer.writeByte(wasm.opcode(.i32_eq));
return WValue{ .code_offset = offset };
}
fn genBreakpoint(self: *Context, breakpoint: *Inst.NoOp) InnerError!WValue {
// unsupported by wasm itself. Can be implemented once we support DWARF
// for wasm
return .none;
}
fn genUnreachable(self: *Context, unreach: *Inst.NoOp) InnerError!WValue {
try self.code.append(wasm.opcode(.@"unreachable"));
return .none;
}
};

View File

@@ -175,6 +175,41 @@ pub fn addCases(ctx: *TestContext) !void {
\\ return i;
\\}
, "31\n");
case.addCompareOutput(
\\export fn _start() void {
\\ assert(foo(true) != @as(i32, 30));
\\}
\\
\\fn assert(ok: bool) void {
\\ if (!ok) unreachable;
\\}
\\
\\fn foo(ok: bool) i32 {
\\ const x = if(ok) @as(i32, 20) else @as(i32, 10);
\\ return x;
\\}
, "");
case.addCompareOutput(
\\export fn _start() void {
\\ assert(foo(false) == @as(i32, 20));
\\ assert(foo(true) == @as(i32, 30));
\\}
\\
\\fn assert(ok: bool) void {
\\ if (!ok) unreachable;
\\}
\\
\\fn foo(ok: bool) i32 {
\\ const val: i32 = blk: {
\\ var x: i32 = 1;
\\ if (!ok) break :blk x + @as(i32, 9);
\\ break :blk x + @as(i32, 19);
\\ };
\\ return val + 10;
\\}
, "");
}
{