mirror of
https://github.com/ziglang/zig.git
synced 2026-01-03 03:53:20 +00:00
Add tests, fix locals that are created in blocks like loops, and handle all breaks correctly
This commit is contained in:
parent
a0d81caec9
commit
cc46c1b902
@ -163,11 +163,8 @@ pub const Context = struct {
|
||||
try self.genFunctype();
|
||||
const writer = self.code.writer();
|
||||
|
||||
// Reserve space to write the size after generating the code
|
||||
try self.code.resize(5);
|
||||
|
||||
// offset into 'code' section where we will put our locals count
|
||||
var local_offset = self.code.items.len;
|
||||
// Reserve space to write the size after generating the code as well as space for locals count
|
||||
try self.code.resize(10);
|
||||
|
||||
// Write instructions
|
||||
// TODO: check for and handle death of instructions
|
||||
@ -177,10 +174,10 @@ pub const Context = struct {
|
||||
|
||||
// finally, write our local types at the 'offset' position
|
||||
{
|
||||
var totals_buffer: [5]u8 = undefined;
|
||||
leb.writeUnsignedFixed(5, totals_buffer[0..5], @intCast(u32, self.locals.items.len));
|
||||
try self.code.insertSlice(local_offset, &totals_buffer);
|
||||
local_offset += 5;
|
||||
leb.writeUnsignedFixed(5, self.code.items[5..10], @intCast(u32, self.locals.items.len));
|
||||
|
||||
// offset into 'code' section where we will put our locals types
|
||||
var local_offset: usize = 10;
|
||||
|
||||
// emit the actual locals amount
|
||||
for (self.locals.items) |local| {
|
||||
@ -285,8 +282,7 @@ pub const Context = struct {
|
||||
}
|
||||
|
||||
fn genLoad(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
|
||||
const operand = self.resolveInst(inst.operand);
|
||||
return operand;
|
||||
return self.resolveInst(inst.operand);
|
||||
}
|
||||
|
||||
fn genArg(self: *Context, inst: *Inst.Arg) InnerError!WValue {
|
||||
@ -351,35 +347,49 @@ pub const Context = struct {
|
||||
fn genBlock(self: *Context, block: *Inst.Block) InnerError!WValue {
|
||||
const block_ty = try self.genBlockType(block.base.src, block.base.ty);
|
||||
|
||||
try self.startBlock(.block, block_ty, null);
|
||||
block.codegen = .{
|
||||
// we don't use relocs, so using `relocs` is illegal behaviour.
|
||||
.relocs = undefined,
|
||||
// Here we set the current block idx, so conditions know the depth to jump
|
||||
// to when breaking out. This will be set to .none when it is found again within
|
||||
// the same block
|
||||
// Here we set the current block idx, so breaks know the depth to jump
|
||||
// to when breaking out.
|
||||
.mcv = @bitCast(AnyMCValue, WValue{ .block_idx = self.block_depth }),
|
||||
};
|
||||
self.block_depth += 1;
|
||||
|
||||
try self.code.append(wasm.opcode(.block));
|
||||
try self.code.append(block_ty);
|
||||
try self.genBody(block.body);
|
||||
try self.code.append(wasm.opcode(.end));
|
||||
try self.endBlock();
|
||||
|
||||
self.block_depth -= 1;
|
||||
return .none;
|
||||
}
|
||||
|
||||
/// appends a new wasm block to the code section and increases the `block_depth` by 1
|
||||
fn startBlock(self: *Context, block_type: wasm.Opcode, valtype: u8, with_offset: ?usize) !void {
|
||||
self.block_depth += 1;
|
||||
if (with_offset) |offset| {
|
||||
try self.code.insert(offset, wasm.opcode(block_type));
|
||||
try self.code.insert(offset + 1, valtype);
|
||||
} else {
|
||||
try self.code.append(wasm.opcode(block_type));
|
||||
try self.code.append(valtype);
|
||||
}
|
||||
}
|
||||
|
||||
/// Ends the current wasm block and decreases the `block_depth` by 1
|
||||
fn endBlock(self: *Context) !void {
|
||||
try self.code.append(wasm.opcode(.end));
|
||||
self.block_depth -= 1;
|
||||
}
|
||||
|
||||
fn genLoop(self: *Context, loop: *Inst.Loop) InnerError!WValue {
|
||||
const loop_ty = try self.genBlockType(loop.base.src, loop.base.ty);
|
||||
|
||||
try self.code.append(wasm.opcode(.loop));
|
||||
try self.code.append(loop_ty);
|
||||
self.block_depth += 1;
|
||||
try self.startBlock(.loop, loop_ty, null);
|
||||
try self.genBody(loop.body);
|
||||
self.block_depth -= 1;
|
||||
|
||||
try self.code.append(wasm.opcode(.end));
|
||||
// breaking to the index of a loop block will continue the loop instead
|
||||
try self.code.append(wasm.opcode(.br));
|
||||
try leb.writeULEB128(self.code.writer(), @as(u32, 0));
|
||||
|
||||
try self.endBlock();
|
||||
|
||||
return .none;
|
||||
}
|
||||
@ -388,23 +398,22 @@ pub const Context = struct {
|
||||
const condition = self.resolveInst(condbr.condition);
|
||||
const writer = self.code.writer();
|
||||
|
||||
// TODO: Handle death instructions for then and else body
|
||||
|
||||
// insert blocks at the position of `offset` so
|
||||
// the condition can jump to it
|
||||
const offset = condition.code_offset;
|
||||
try self.code.insert(offset, wasm.opcode(.block));
|
||||
try self.code.insert(offset, try self.genBlockType(condbr.base.src, condbr.base.ty));
|
||||
const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty);
|
||||
try self.startBlock(.block, block_ty, offset);
|
||||
|
||||
// we inserted the block in front of the condition
|
||||
// so now check if condition matches. If not, break outside this block
|
||||
// and continue with the regular codepath
|
||||
// and continue with the then codepath
|
||||
try writer.writeByte(wasm.opcode(.br_if));
|
||||
try leb.writeULEB128(writer, @as(u32, 0));
|
||||
|
||||
// else body in case condition does not match
|
||||
try self.genBody(condbr.else_body);
|
||||
|
||||
// finally, tell wasm we have reached the end of the block we inserted above
|
||||
try writer.writeByte(wasm.opcode(.end));
|
||||
try self.endBlock();
|
||||
|
||||
// Outer block that matches the condition
|
||||
try self.genBody(condbr.then_body);
|
||||
@ -417,7 +426,7 @@ pub const Context = struct {
|
||||
|
||||
// save offset, so potential conditions can insert blocks in front of
|
||||
// the comparison that we can later jump back to
|
||||
const offset = self.code.items.len - 1;
|
||||
const offset = self.code.items.len;
|
||||
|
||||
const lhs = self.resolveInst(inst.lhs);
|
||||
const rhs = self.resolveInst(inst.rhs);
|
||||
@ -492,17 +501,14 @@ pub const Context = struct {
|
||||
try self.emitWValue(operand);
|
||||
}
|
||||
|
||||
// if the block contains a block_idx, do a relative jump to it
|
||||
// if `wvalue` was already 'consumed', simply break out of current block
|
||||
// every block contains a `WValue` with its block index.
|
||||
// We then determine how far we have to jump to it by substracting it from current block depth
|
||||
const wvalue = @bitCast(WValue, br.block.codegen.mcv);
|
||||
const idx: u32 = if (wvalue == .block_idx) blk: {
|
||||
br.block.codegen.mcv = @bitCast(AnyMCValue, WValue{ .none = {} });
|
||||
break :blk self.block_depth - wvalue.block_idx;
|
||||
} else 0;
|
||||
|
||||
const idx: u32 = self.block_depth - wvalue.block_idx;
|
||||
const writer = self.code.writer();
|
||||
try writer.writeByte(wasm.opcode(.br));
|
||||
try leb.writeULEB128(writer, idx);
|
||||
return WValue.none;
|
||||
|
||||
return .none;
|
||||
}
|
||||
};
|
||||
|
||||
@ -122,6 +122,13 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
|
||||
else => |e| return err,
|
||||
};
|
||||
|
||||
// as locals are patched afterwards, the offsets of funcidx's are off,
|
||||
// here we update them to correct them
|
||||
for (decl.fn_link.wasm.?.idx_refs.items) |*func| {
|
||||
// For each local, add 6 bytes (count + type)
|
||||
func.offset += @intCast(u32, context.locals.items.len * 6);
|
||||
}
|
||||
|
||||
fn_data.functype = context.func_type_data.toUnmanaged();
|
||||
fn_data.code = context.code.toUnmanaged();
|
||||
}
|
||||
@ -238,7 +245,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
|
||||
try writer.writeAll(fn_data.code.items[current..idx_ref.offset]);
|
||||
current = idx_ref.offset;
|
||||
// Use a fixed width here to make calculating the code size
|
||||
// in codegen.wasm.genCode() simpler.
|
||||
// in codegen.wasm.gen() simpler.
|
||||
var buf: [5]u8 = undefined;
|
||||
leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?);
|
||||
try writer.writeAll(&buf);
|
||||
|
||||
@ -122,4 +122,96 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\}
|
||||
, "35\n");
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("wasm conditions", wasi);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() u32 {
|
||||
\\ var i: u32 = 5;
|
||||
\\ if (i > @as(u32, 4)) {
|
||||
\\ i += 10;
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
, "15\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() u32 {
|
||||
\\ var i: u32 = 5;
|
||||
\\ if (i < @as(u32, 4)) {
|
||||
\\ i += 10;
|
||||
\\ } else {
|
||||
\\ i = 2;
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
, "2\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() u32 {
|
||||
\\ var i: u32 = 5;
|
||||
\\ if (i < @as(u32, 4)) {
|
||||
\\ i += 10;
|
||||
\\ } else if(i == @as(u32, 5)) {
|
||||
\\ i = 20;
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
, "20\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() u32 {
|
||||
\\ var i: u32 = 11;
|
||||
\\ if (i < @as(u32, 4)) {
|
||||
\\ i += 10;
|
||||
\\ } else {
|
||||
\\ if (i > @as(u32, 10)) {
|
||||
\\ i += 20;
|
||||
\\ } else {
|
||||
\\ i = 20;
|
||||
\\ }
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
, "31\n");
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("wasm while loops", wasi);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() u32 {
|
||||
\\ var i: u32 = 0;
|
||||
\\ while(i < @as(u32, 5)){
|
||||
\\ i += 1;
|
||||
\\ }
|
||||
\\
|
||||
\\ return i;
|
||||
\\}
|
||||
, "5\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() u32 {
|
||||
\\ var i: u32 = 0;
|
||||
\\ while(i < @as(u32, 10)){
|
||||
\\ var x: u32 = 1;
|
||||
\\ i += x;
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
, "10\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() u32 {
|
||||
\\ var i: u32 = 0;
|
||||
\\ while(i < @as(u32, 10)){
|
||||
\\ var x: u32 = 1;
|
||||
\\ i += x;
|
||||
\\ if (i == @as(u32, 5)) break;
|
||||
\\ }
|
||||
\\ return i;
|
||||
\\}
|
||||
, "5\n");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user