mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
Merge pull request #7895 from Luukdegram/wasm-control-flow
stage2: wasm control flow
This commit is contained in:
commit
1517ed0a5e
@ -4,6 +4,7 @@ const ArrayList = std.ArrayList;
|
||||
const assert = std.debug.assert;
|
||||
const leb = std.leb;
|
||||
const mem = std.mem;
|
||||
const wasm = std.wasm;
|
||||
|
||||
const Module = @import("../Module.zig");
|
||||
const Decl = Module.Decl;
|
||||
@ -12,6 +13,7 @@ const Inst = ir.Inst;
|
||||
const Type = @import("../type.zig").Type;
|
||||
const Value = @import("../value.zig").Value;
|
||||
const Compilation = @import("../Compilation.zig");
|
||||
const AnyMCValue = @import("../codegen.zig").AnyMCValue;
|
||||
|
||||
/// Wasm Value, created when generating an instruction
|
||||
const WValue = union(enum) {
|
||||
@ -20,23 +22,14 @@ const WValue = union(enum) {
|
||||
local: u32,
|
||||
/// Instruction holding a constant `Value`
|
||||
constant: *Inst,
|
||||
/// Block label
|
||||
/// Offset position in the list of bytecode instructions
|
||||
code_offset: usize,
|
||||
/// The label of the block, used by breaks to find its relative distance
|
||||
block_idx: u32,
|
||||
};
|
||||
|
||||
/// Hashmap to store generated `WValue` for each `Inst`
|
||||
pub const ValueTable = std.AutoHashMap(*Inst, WValue);
|
||||
|
||||
/// Using a given `Type`, returns the corresponding wasm value type
|
||||
fn genValtype(ty: Type) ?u8 {
|
||||
return switch (ty.tag()) {
|
||||
.f32 => 0x7D,
|
||||
.f64 => 0x7C,
|
||||
.u32, .i32 => 0x7F,
|
||||
.u64, .i64 => 0x7E,
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
pub const ValueTable = std.AutoHashMapUnmanaged(*Inst, WValue);
|
||||
|
||||
/// Code represents the `Code` section of wasm that
|
||||
/// belongs to a function
|
||||
@ -58,13 +51,25 @@ pub const Context = struct {
|
||||
local_index: u32 = 0,
|
||||
/// If codegen fails, an error messages will be allocated and saved in `err_msg`
|
||||
err_msg: *Module.ErrorMsg,
|
||||
/// Current block depth. Used to calculate the relative difference between a break
|
||||
/// and block
|
||||
block_depth: u32 = 0,
|
||||
/// List of all locals' types generated throughout this declaration
|
||||
/// used to emit locals count at start of 'code' section.
|
||||
locals: std.ArrayListUnmanaged(u8),
|
||||
|
||||
const InnerError = error{
|
||||
OutOfMemory,
|
||||
CodegenFail,
|
||||
};
|
||||
|
||||
/// Sets `err_msg` on `Context` and returns `error.CodegenFail` which is caught in link/Wasm.zig
|
||||
pub fn deinit(self: *Context) void {
|
||||
self.values.deinit(self.gpa);
|
||||
self.locals.deinit(self.gpa);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
/// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig
|
||||
fn fail(self: *Context, src: usize, comptime fmt: []const u8, args: anytype) InnerError {
|
||||
self.err_msg = try Module.ErrorMsg.create(self.gpa, .{
|
||||
.file_scope = self.decl.getFileScope(),
|
||||
@ -85,13 +90,35 @@ pub const Context = struct {
|
||||
return self.values.get(inst).?; // Instruction does not dominate all uses!
|
||||
}
|
||||
|
||||
/// Using a given `Type`, returns the corresponding wasm value type
|
||||
fn genValtype(self: *Context, src: usize, ty: Type) InnerError!u8 {
|
||||
return switch (ty.tag()) {
|
||||
.f32 => wasm.valtype(.f32),
|
||||
.f64 => wasm.valtype(.f64),
|
||||
.u32, .i32 => wasm.valtype(.i32),
|
||||
.u64, .i64 => wasm.valtype(.i64),
|
||||
else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}),
|
||||
};
|
||||
}
|
||||
|
||||
/// Using a given `Type`, returns the corresponding wasm value type
|
||||
/// Differently from `genValtype` this also allows `void` to create a block
|
||||
/// with no return type
|
||||
fn genBlockType(self: *Context, src: usize, ty: Type) InnerError!u8 {
|
||||
return switch (ty.tag()) {
|
||||
.void, .noreturn => wasm.block_empty,
|
||||
else => self.genValtype(src, ty),
|
||||
};
|
||||
}
|
||||
|
||||
/// Writes the bytecode depending on the given `WValue` in `val`
|
||||
fn emitWValue(self: *Context, val: WValue) InnerError!void {
|
||||
const writer = self.code.writer();
|
||||
switch (val) {
|
||||
.none, .block_idx => {},
|
||||
.block_idx => unreachable,
|
||||
.none, .code_offset => {},
|
||||
.local => |idx| {
|
||||
try writer.writeByte(0x20); // local.get
|
||||
try writer.writeByte(wasm.opcode(.local_get));
|
||||
try leb.writeULEB128(writer, idx);
|
||||
},
|
||||
.constant => |inst| try self.emitConstant(inst.castTag(.constant).?), // creates a new constant onto the stack
|
||||
@ -102,8 +129,7 @@ pub const Context = struct {
|
||||
const ty = self.decl.typed_value.most_recent.typed_value.ty;
|
||||
const writer = self.func_type_data.writer();
|
||||
|
||||
// functype magic
|
||||
try writer.writeByte(0x60);
|
||||
try writer.writeByte(wasm.function_type);
|
||||
|
||||
// param types
|
||||
try leb.writeULEB128(writer, @intCast(u32, ty.fnParamLen()));
|
||||
@ -112,8 +138,8 @@ pub const Context = struct {
|
||||
defer self.gpa.free(params);
|
||||
ty.fnParamTypes(params);
|
||||
for (params) |param_type| {
|
||||
const val_type = genValtype(param_type) orelse
|
||||
return self.fail(self.decl.src(), "TODO: Wasm codegen - arg type value for type '{s}'", .{param_type.tag()});
|
||||
// Can we maybe get the source index of each param?
|
||||
const val_type = try self.genValtype(self.decl.src(), param_type);
|
||||
try writer.writeByte(val_type);
|
||||
}
|
||||
}
|
||||
@ -124,8 +150,8 @@ pub const Context = struct {
|
||||
.void, .noreturn => try leb.writeULEB128(writer, @as(u32, 0)),
|
||||
else => |ret_type| {
|
||||
try leb.writeULEB128(writer, @as(u32, 1));
|
||||
const val_type = genValtype(return_type) orelse
|
||||
return self.fail(self.decl.src(), "TODO: Wasm codegen - return type value for type '{s}'", .{ret_type});
|
||||
// Can we maybe get the source index of the return type?
|
||||
const val_type = try self.genValtype(self.decl.src(), return_type);
|
||||
try writer.writeByte(val_type);
|
||||
},
|
||||
}
|
||||
@ -137,40 +163,33 @@ 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);
|
||||
// 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
|
||||
const tv = self.decl.typed_value.most_recent.typed_value;
|
||||
const mod_fn = tv.val.castTag(.function).?.data;
|
||||
|
||||
var locals = std.ArrayList(u8).init(self.gpa);
|
||||
defer locals.deinit();
|
||||
|
||||
for (mod_fn.body.instructions) |inst| {
|
||||
if (inst.tag != .alloc) continue;
|
||||
|
||||
const alloc: *Inst.NoOp = inst.castTag(.alloc).?;
|
||||
const elem_type = alloc.base.ty.elemType();
|
||||
|
||||
const wasm_type = genValtype(elem_type) orelse
|
||||
return self.fail(inst.src, "TODO: Wasm codegen - valtype for type '{s}'", .{elem_type.tag()});
|
||||
|
||||
try locals.append(wasm_type);
|
||||
}
|
||||
|
||||
try leb.writeULEB128(writer, @intCast(u32, locals.items.len));
|
||||
|
||||
// emit the actual locals amount
|
||||
for (locals.items) |local| {
|
||||
try leb.writeULEB128(writer, @as(u32, 1));
|
||||
try leb.writeULEB128(writer, local); // valtype
|
||||
}
|
||||
|
||||
try self.genBody(mod_fn.body);
|
||||
|
||||
try writer.writeByte(0x0B); // end
|
||||
// finally, write our local types at the 'offset' position
|
||||
{
|
||||
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| {
|
||||
var buf: [6]u8 = undefined;
|
||||
leb.writeUnsignedFixed(5, buf[0..5], @as(u32, 1));
|
||||
buf[5] = local;
|
||||
try self.code.insertSlice(local_offset, &buf);
|
||||
local_offset += 6;
|
||||
}
|
||||
}
|
||||
|
||||
try writer.writeByte(wasm.opcode(.end));
|
||||
|
||||
// Fill in the size of the generated code to the reserved space at the
|
||||
// beginning of the buffer.
|
||||
@ -183,10 +202,20 @@ pub const Context = struct {
|
||||
.add => self.genAdd(inst.castTag(.add).?),
|
||||
.alloc => self.genAlloc(inst.castTag(.alloc).?),
|
||||
.arg => self.genArg(inst.castTag(.arg).?),
|
||||
.block => self.genBlock(inst.castTag(.block).?),
|
||||
.br => self.genBr(inst.castTag(.br).?),
|
||||
.call => self.genCall(inst.castTag(.call).?),
|
||||
.cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
|
||||
.cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte),
|
||||
.cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt),
|
||||
.cmp_lte => self.genCmp(inst.castTag(.cmp_lte).?, .lte),
|
||||
.cmp_lt => self.genCmp(inst.castTag(.cmp_lt).?, .lt),
|
||||
.cmp_neq => self.genCmp(inst.castTag(.cmp_neq).?, .neq),
|
||||
.condbr => self.genCondBr(inst.castTag(.condbr).?),
|
||||
.constant => unreachable,
|
||||
.dbg_stmt => WValue.none,
|
||||
.load => self.genLoad(inst.castTag(.load).?),
|
||||
.loop => self.genLoop(inst.castTag(.loop).?),
|
||||
.ret => self.genRet(inst.castTag(.ret).?),
|
||||
.retvoid => WValue.none,
|
||||
.store => self.genStore(inst.castTag(.store).?),
|
||||
@ -197,7 +226,7 @@ pub const Context = struct {
|
||||
fn genBody(self: *Context, body: ir.Body) InnerError!void {
|
||||
for (body.instructions) |inst| {
|
||||
const result = try self.genInst(inst);
|
||||
try self.values.putNoClobber(inst, result);
|
||||
try self.values.putNoClobber(self.gpa, inst, result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,7 +234,7 @@ pub const Context = struct {
|
||||
// TODO: Implement tail calls
|
||||
const operand = self.resolveInst(inst.operand);
|
||||
try self.emitWValue(operand);
|
||||
return WValue.none;
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue {
|
||||
@ -219,7 +248,7 @@ pub const Context = struct {
|
||||
try self.emitWValue(arg_val);
|
||||
}
|
||||
|
||||
try self.code.append(0x10); // call
|
||||
try self.code.append(wasm.opcode(.call));
|
||||
|
||||
// The function index immediate argument will be filled in using this data
|
||||
// in link.Wasm.flush().
|
||||
@ -228,10 +257,14 @@ pub const Context = struct {
|
||||
.decl = target,
|
||||
});
|
||||
|
||||
return WValue.none;
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue {
|
||||
const elem_type = inst.base.ty.elemType();
|
||||
const valtype = try self.genValtype(inst.base.src, elem_type);
|
||||
try self.locals.append(self.gpa, valtype);
|
||||
|
||||
defer self.local_index += 1;
|
||||
return WValue{ .local = self.local_index };
|
||||
}
|
||||
@ -243,15 +276,13 @@ pub const Context = struct {
|
||||
const rhs = self.resolveInst(inst.rhs);
|
||||
try self.emitWValue(rhs);
|
||||
|
||||
try writer.writeByte(0x21); // local.set
|
||||
try writer.writeByte(wasm.opcode(.local_set));
|
||||
try leb.writeULEB128(writer, lhs.local);
|
||||
return WValue.none;
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn genLoad(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
|
||||
const operand = self.resolveInst(inst.operand);
|
||||
try self.emitWValue(operand);
|
||||
return WValue.none;
|
||||
return self.resolveInst(inst.operand);
|
||||
}
|
||||
|
||||
fn genArg(self: *Context, inst: *Inst.Arg) InnerError!WValue {
|
||||
@ -267,44 +298,44 @@ pub const Context = struct {
|
||||
try self.emitWValue(lhs);
|
||||
try self.emitWValue(rhs);
|
||||
|
||||
const opcode: u8 = switch (inst.base.ty.tag()) {
|
||||
.u32, .i32 => 0x6A, //i32.add
|
||||
.u64, .i64 => 0x7C, //i64.add
|
||||
.f32 => 0x92, //f32.add
|
||||
.f64 => 0xA0, //f64.add
|
||||
const opcode: wasm.Opcode = switch (inst.base.ty.tag()) {
|
||||
.u32, .i32 => .i32_add,
|
||||
.u64, .i64 => .i64_add,
|
||||
.f32 => .f32_add,
|
||||
.f64 => .f64_add,
|
||||
else => return self.fail(inst.base.src, "TODO - Implement wasm genAdd for type '{s}'", .{inst.base.ty.tag()}),
|
||||
};
|
||||
|
||||
try self.code.append(opcode);
|
||||
return WValue.none;
|
||||
try self.code.append(wasm.opcode(opcode));
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void {
|
||||
const writer = self.code.writer();
|
||||
switch (inst.base.ty.tag()) {
|
||||
.u32 => {
|
||||
try writer.writeByte(0x41); // i32.const
|
||||
try writer.writeByte(wasm.opcode(.i32_const));
|
||||
try leb.writeILEB128(writer, inst.val.toUnsignedInt());
|
||||
},
|
||||
.i32 => {
|
||||
try writer.writeByte(0x41); // i32.const
|
||||
try writer.writeByte(wasm.opcode(.i32_const));
|
||||
try leb.writeILEB128(writer, inst.val.toSignedInt());
|
||||
},
|
||||
.u64 => {
|
||||
try writer.writeByte(0x42); // i64.const
|
||||
try writer.writeByte(wasm.opcode(.i64_const));
|
||||
try leb.writeILEB128(writer, inst.val.toUnsignedInt());
|
||||
},
|
||||
.i64 => {
|
||||
try writer.writeByte(0x42); // i64.const
|
||||
try writer.writeByte(wasm.opcode(.i64_const));
|
||||
try leb.writeILEB128(writer, inst.val.toSignedInt());
|
||||
},
|
||||
.f32 => {
|
||||
try writer.writeByte(0x43); // f32.const
|
||||
try writer.writeByte(wasm.opcode(.f32_const));
|
||||
// TODO: enforce LE byte order
|
||||
try writer.writeAll(mem.asBytes(&inst.val.toFloat(f32)));
|
||||
},
|
||||
.f64 => {
|
||||
try writer.writeByte(0x44); // f64.const
|
||||
try writer.writeByte(wasm.opcode(.f64_const));
|
||||
// TODO: enforce LE byte order
|
||||
try writer.writeAll(mem.asBytes(&inst.val.toFloat(f64)));
|
||||
},
|
||||
@ -312,4 +343,172 @@ pub const Context = struct {
|
||||
else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for type {s}", .{ty}),
|
||||
}
|
||||
}
|
||||
|
||||
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 breaks know the depth to jump
|
||||
// to when breaking out.
|
||||
.mcv = @bitCast(AnyMCValue, WValue{ .block_idx = self.block_depth }),
|
||||
};
|
||||
try self.genBody(block.body);
|
||||
try self.endBlock();
|
||||
|
||||
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.startBlock(.loop, loop_ty, null);
|
||||
try self.genBody(loop.body);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
fn genCondBr(self: *Context, condbr: *Inst.CondBr) InnerError!WValue {
|
||||
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;
|
||||
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 then codepath
|
||||
try writer.writeByte(wasm.opcode(.br_if));
|
||||
try leb.writeULEB128(writer, @as(u32, 0));
|
||||
|
||||
try self.genBody(condbr.else_body);
|
||||
try self.endBlock();
|
||||
|
||||
// Outer block that matches the condition
|
||||
try self.genBody(condbr.then_body);
|
||||
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn genCmp(self: *Context, inst: *Inst.BinOp, op: std.math.CompareOperator) InnerError!WValue {
|
||||
const ty = inst.lhs.ty.tag();
|
||||
|
||||
// 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;
|
||||
|
||||
const lhs = self.resolveInst(inst.lhs);
|
||||
const rhs = self.resolveInst(inst.rhs);
|
||||
|
||||
try self.emitWValue(lhs);
|
||||
try self.emitWValue(rhs);
|
||||
|
||||
const opcode_maybe: ?wasm.Opcode = switch (op) {
|
||||
.lt => @as(?wasm.Opcode, switch (ty) {
|
||||
.i32 => .i32_lt_s,
|
||||
.u32 => .i32_lt_u,
|
||||
.i64 => .i64_lt_s,
|
||||
.u64 => .i64_lt_u,
|
||||
.f32 => .f32_lt,
|
||||
.f64 => .f64_lt,
|
||||
else => null,
|
||||
}),
|
||||
.lte => @as(?wasm.Opcode, switch (ty) {
|
||||
.i32 => .i32_le_s,
|
||||
.u32 => .i32_le_u,
|
||||
.i64 => .i64_le_s,
|
||||
.u64 => .i64_le_u,
|
||||
.f32 => .f32_le,
|
||||
.f64 => .f64_le,
|
||||
else => null,
|
||||
}),
|
||||
.eq => @as(?wasm.Opcode, switch (ty) {
|
||||
.i32, .u32 => .i32_eq,
|
||||
.i64, .u64 => .i64_eq,
|
||||
.f32 => .f32_eq,
|
||||
.f64 => .f64_eq,
|
||||
else => null,
|
||||
}),
|
||||
.gte => @as(?wasm.Opcode, switch (ty) {
|
||||
.i32 => .i32_ge_s,
|
||||
.u32 => .i32_ge_u,
|
||||
.i64 => .i64_ge_s,
|
||||
.u64 => .i64_ge_u,
|
||||
.f32 => .f32_ge,
|
||||
.f64 => .f64_ge,
|
||||
else => null,
|
||||
}),
|
||||
.gt => @as(?wasm.Opcode, switch (ty) {
|
||||
.i32 => .i32_gt_s,
|
||||
.u32 => .i32_gt_u,
|
||||
.i64 => .i64_gt_s,
|
||||
.u64 => .i64_gt_u,
|
||||
.f32 => .f32_gt,
|
||||
.f64 => .f64_gt,
|
||||
else => null,
|
||||
}),
|
||||
.neq => @as(?wasm.Opcode, switch (ty) {
|
||||
.i32, .u32 => .i32_ne,
|
||||
.i64, .u64 => .i64_ne,
|
||||
.f32 => .f32_ne,
|
||||
.f64 => .f64_ne,
|
||||
else => null,
|
||||
}),
|
||||
};
|
||||
|
||||
const opcode = opcode_maybe orelse
|
||||
return self.fail(inst.base.src, "TODO - Wasm genCmp for type '{s}' and operator '{s}'", .{ ty, @tagName(op) });
|
||||
|
||||
try self.code.append(wasm.opcode(opcode));
|
||||
return WValue{ .code_offset = offset };
|
||||
}
|
||||
|
||||
fn genBr(self: *Context, br: *Inst.Br) InnerError!WValue {
|
||||
// of operand has codegen bits we should break with a value
|
||||
if (br.operand.ty.hasCodeGenBits()) {
|
||||
const operand = self.resolveInst(br.operand);
|
||||
try self.emitWValue(operand);
|
||||
}
|
||||
|
||||
// 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 = self.block_depth - wvalue.block_idx;
|
||||
const writer = self.code.writer();
|
||||
try writer.writeByte(wasm.opcode(.br));
|
||||
try leb.writeULEB128(writer, idx);
|
||||
|
||||
return .none;
|
||||
}
|
||||
};
|
||||
|
||||
@ -103,13 +103,14 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
|
||||
|
||||
var context = codegen.Context{
|
||||
.gpa = self.base.allocator,
|
||||
.values = codegen.ValueTable.init(self.base.allocator),
|
||||
.values = .{},
|
||||
.code = managed_code,
|
||||
.func_type_data = managed_functype,
|
||||
.decl = decl,
|
||||
.err_msg = undefined,
|
||||
.locals = .{},
|
||||
};
|
||||
defer context.values.deinit();
|
||||
defer context.deinit();
|
||||
|
||||
// generate the 'code' section for the function declaration
|
||||
context.gen() catch |err| switch (err) {
|
||||
@ -121,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();
|
||||
}
|
||||
@ -237,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