authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-22 19:52:31-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-03-22 19:52:31-07:00
log749c3f014ba36a6dc824419f835790f9c181aad3
tree01a192666ff8fde99b9d74b22f21755187886bba
parent3b48ea874ed4d0231a080ed583c7740478e58ed8
parent4b854b75d2d0add085ac202c611bbe10fa79b51c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

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

stage2: Wasm control flow

2 files changed, 76 insertions(+), 3 deletions(-)

src/codegen/wasm.zig+41-3
......@@ -95,7 +95,7 @@ pub const Context = struct {
9595 return switch (ty.tag()) {
9696 .f32 => wasm.valtype(.f32),
9797 .f64 => wasm.valtype(.f64),
98 .u32, .i32 => wasm.valtype(.i32),
98 .u32, .i32, .bool => wasm.valtype(.i32),
9999 .u64, .i64 => wasm.valtype(.i64),
100100 else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}),
101101 };
......@@ -208,6 +208,7 @@ pub const Context = struct {
208208 .alloc => self.genAlloc(inst.castTag(.alloc).?),
209209 .arg => self.genArg(inst.castTag(.arg).?),
210210 .block => self.genBlock(inst.castTag(.block).?),
211 .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),
211212 .br => self.genBr(inst.castTag(.br).?),
212213 .call => self.genCall(inst.castTag(.call).?),
213214 .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
......@@ -221,9 +222,11 @@ pub const Context = struct {
221222 .dbg_stmt => WValue.none,
222223 .load => self.genLoad(inst.castTag(.load).?),
223224 .loop => self.genLoop(inst.castTag(.loop).?),
225 .not => self.genNot(inst.castTag(.not).?),
224226 .ret => self.genRet(inst.castTag(.ret).?),
225227 .retvoid => WValue.none,
226228 .store => self.genStore(inst.castTag(.store).?),
229 .unreach => self.genUnreachable(inst.castTag(.unreach).?),
227230 else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}),
228231 };
229232 }
......@@ -329,7 +332,7 @@ pub const Context = struct {
329332 try writer.writeByte(wasm.opcode(.i32_const));
330333 try leb.writeILEB128(writer, inst.val.toUnsignedInt());
331334 },
332 .i32 => {
335 .i32, .bool => {
333336 try writer.writeByte(wasm.opcode(.i32_const));
334337 try leb.writeILEB128(writer, inst.val.toSignedInt());
335338 },
......@@ -414,7 +417,14 @@ pub const Context = struct {
414417
415418 // insert blocks at the position of `offset` so
416419 // the condition can jump to it
417 const offset = condition.code_offset;
420 const offset = switch (condition) {
421 .code_offset => |offset| offset,
422 else => blk: {
423 const offset = self.code.items.len;
424 try self.emitWValue(condition);
425 break :blk offset;
426 },
427 };
418428 const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty);
419429 try self.startBlock(.block, block_ty, offset);
420430
......@@ -523,4 +533,32 @@ pub const Context = struct {
523533
524534 return .none;
525535 }
536
537 fn genNot(self: *Context, not: *Inst.UnOp) InnerError!WValue {
538 const offset = self.code.items.len;
539
540 const operand = self.resolveInst(not.operand);
541 try self.emitWValue(operand);
542
543 // wasm does not have booleans nor the `not` instruction, therefore compare with 0
544 // to create the same logic
545 const writer = self.code.writer();
546 try writer.writeByte(wasm.opcode(.i32_const));
547 try leb.writeILEB128(writer, @as(i32, 0));
548
549 try writer.writeByte(wasm.opcode(.i32_eq));
550
551 return WValue{ .code_offset = offset };
552 }
553
554 fn genBreakpoint(self: *Context, breakpoint: *Inst.NoOp) InnerError!WValue {
555 // unsupported by wasm itself. Can be implemented once we support DWARF
556 // for wasm
557 return .none;
558 }
559
560 fn genUnreachable(self: *Context, unreach: *Inst.NoOp) InnerError!WValue {
561 try self.code.append(wasm.opcode(.@"unreachable"));
562 return .none;
563 }
526564};
test/stage2/wasm.zig+35
......@@ -175,6 +175,41 @@ pub fn addCases(ctx: *TestContext) !void {
175175 \\ return i;
176176 \\}
177177 , "31\n");
178
179 case.addCompareOutput(
180 \\export fn _start() void {
181 \\ assert(foo(true) != @as(i32, 30));
182 \\}
183 \\
184 \\fn assert(ok: bool) void {
185 \\ if (!ok) unreachable;
186 \\}
187 \\
188 \\fn foo(ok: bool) i32 {
189 \\ const x = if(ok) @as(i32, 20) else @as(i32, 10);
190 \\ return x;
191 \\}
192 , "");
193
194 case.addCompareOutput(
195 \\export fn _start() void {
196 \\ assert(foo(false) == @as(i32, 20));
197 \\ assert(foo(true) == @as(i32, 30));
198 \\}
199 \\
200 \\fn assert(ok: bool) void {
201 \\ if (!ok) unreachable;
202 \\}
203 \\
204 \\fn foo(ok: bool) i32 {
205 \\ const val: i32 = blk: {
206 \\ var x: i32 = 1;
207 \\ if (!ok) break :blk x + @as(i32, 9);
208 \\ break :blk x + @as(i32, 19);
209 \\ };
210 \\ return val + 10;
211 \\}
212 , "");
178213 }
179214
180215 {