Commit 6242ae35f3
Changed files (3)
test
stage2
src-self-hosted/codegen/wasm.zig
@@ -62,58 +62,80 @@ pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void {
// TODO: check for and handle death of instructions
const tv = decl.typed_value.most_recent.typed_value;
const mod_fn = tv.val.cast(Value.Payload.Function).?.func;
- for (mod_fn.analysis.success.instructions) |inst| try genInst(writer, inst);
+ for (mod_fn.analysis.success.instructions) |inst| try genInst(buf, decl, inst);
// Write 'end' opcode
try writer.writeByte(0x0B);
// Fill in the size of the generated code to the reserved space at the
// beginning of the buffer.
- leb.writeUnsignedFixed(5, buf.items[0..5], @intCast(u32, buf.items.len - 5));
+ const size = buf.items.len - 5 + decl.fn_link.wasm.?.idx_refs.items.len * 5;
+ leb.writeUnsignedFixed(5, buf.items[0..5], @intCast(u32, size));
}
-fn genInst(writer: ArrayList(u8).Writer, inst: *Inst) !void {
+fn genInst(buf: *ArrayList(u8), decl: *Decl, inst: *Inst) !void {
return switch (inst.tag) {
+ .call => genCall(buf, decl, inst.castTag(.call).?),
+ .constant => genConstant(buf, decl, inst.castTag(.constant).?),
.dbg_stmt => {},
- .ret => genRet(writer, inst.castTag(.ret).?),
+ .ret => genRet(buf, decl, inst.castTag(.ret).?),
+ .retvoid => {},
else => error.TODOImplementMoreWasmCodegen,
};
}
-fn genRet(writer: ArrayList(u8).Writer, inst: *Inst.UnOp) !void {
- switch (inst.operand.tag) {
- .constant => {
- const constant = inst.operand.castTag(.constant).?;
- switch (inst.operand.ty.tag()) {
- .u32 => {
- try writer.writeByte(0x41); // i32.const
- try leb.writeILEB128(writer, constant.val.toUnsignedInt());
- },
- .i32 => {
- try writer.writeByte(0x41); // i32.const
- try leb.writeILEB128(writer, constant.val.toSignedInt());
- },
- .u64 => {
- try writer.writeByte(0x42); // i64.const
- try leb.writeILEB128(writer, constant.val.toUnsignedInt());
- },
- .i64 => {
- try writer.writeByte(0x42); // i64.const
- try leb.writeILEB128(writer, constant.val.toSignedInt());
- },
- .f32 => {
- try writer.writeByte(0x43); // f32.const
- // TODO: enforce LE byte order
- try writer.writeAll(mem.asBytes(&constant.val.toFloat(f32)));
- },
- .f64 => {
- try writer.writeByte(0x44); // f64.const
- // TODO: enforce LE byte order
- try writer.writeAll(mem.asBytes(&constant.val.toFloat(f64)));
- },
- else => return error.TODOImplementMoreWasmCodegen,
- }
+fn genConstant(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.Constant) !void {
+ const writer = buf.writer();
+ switch (inst.base.ty.tag()) {
+ .u32 => {
+ try writer.writeByte(0x41); // i32.const
+ try leb.writeILEB128(writer, inst.val.toUnsignedInt());
+ },
+ .i32 => {
+ try writer.writeByte(0x41); // i32.const
+ try leb.writeILEB128(writer, inst.val.toSignedInt());
+ },
+ .u64 => {
+ try writer.writeByte(0x42); // i64.const
+ try leb.writeILEB128(writer, inst.val.toUnsignedInt());
+ },
+ .i64 => {
+ try writer.writeByte(0x42); // i64.const
+ try leb.writeILEB128(writer, inst.val.toSignedInt());
},
+ .f32 => {
+ try writer.writeByte(0x43); // f32.const
+ // TODO: enforce LE byte order
+ try writer.writeAll(mem.asBytes(&inst.val.toFloat(f32)));
+ },
+ .f64 => {
+ try writer.writeByte(0x44); // f64.const
+ // TODO: enforce LE byte order
+ try writer.writeAll(mem.asBytes(&inst.val.toFloat(f64)));
+ },
+ .void => {},
else => return error.TODOImplementMoreWasmCodegen,
}
}
+
+fn genRet(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.UnOp) !void {
+ try genInst(buf, decl, inst.operand);
+}
+
+fn genCall(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.Call) !void {
+ const func_inst = inst.func.castTag(.constant).?;
+ const func_val = func_inst.val.cast(Value.Payload.Function).?;
+ const target = func_val.func.owner_decl;
+ const target_ty = target.typed_value.most_recent.typed_value.ty;
+
+ if (inst.args.len != 0) return error.TODOImplementMoreWasmCodegen;
+
+ try buf.append(0x10); // call
+
+ // The function index immediate argument will be filled in using this data
+ // in link.Wasm.flush().
+ try decl.fn_link.wasm.?.idx_refs.append(buf.allocator, .{
+ .offset = @intCast(u32, buf.items.len),
+ .decl = target,
+ });
+}
src-self-hosted/link/Wasm.zig
@@ -36,6 +36,9 @@ pub const FnData = struct {
functype: std.ArrayListUnmanaged(u8) = .{},
/// Generated code for the body of the function
code: std.ArrayListUnmanaged(u8) = .{},
+ /// Locations in the generated code where function indexes must be filled in.
+ /// This must be kept ordered by offset.
+ idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }) = .{},
};
base: link.File,
@@ -74,6 +77,7 @@ pub fn deinit(self: *Wasm) void {
for (self.funcs.items) |decl| {
decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
decl.fn_link.wasm.?.code.deinit(self.base.allocator);
+ decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
}
self.funcs.deinit(self.base.allocator);
}
@@ -87,6 +91,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
if (decl.fn_link.wasm) |*fn_data| {
fn_data.functype.items.len = 0;
fn_data.code.items.len = 0;
+ fn_data.idx_refs.items.len = 0;
} else {
decl.fn_link.wasm = .{};
try self.funcs.append(self.base.allocator, decl);
@@ -114,6 +119,7 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
_ = self.funcs.swapRemove(self.getFuncidx(decl).?);
decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
decl.fn_link.wasm.?.code.deinit(self.base.allocator);
+ decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
decl.fn_link.wasm = null;
}
@@ -190,7 +196,25 @@ pub fn flush(self: *Wasm, module: *Module) !void {
// Code section
{
const header_offset = try reserveVecSectionHeader(file);
- for (self.funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.code.items);
+ const writer = file.writer();
+ for (self.funcs.items) |decl| {
+ const fn_data = &decl.fn_link.wasm.?;
+
+ // Write the already generated code to the file, inserting
+ // function indexes where required.
+ var current: u32 = 0;
+ for (fn_data.idx_refs.items) |idx_ref| {
+ 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.
+ var buf: [5]u8 = undefined;
+ leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?);
+ try writer.writeAll(&buf);
+ }
+
+ try writer.writeAll(fn_data.code.items[current..]);
+ }
try writeVecSectionHeader(
file,
header_offset,
test/stage2/compare_output.zig
@@ -546,28 +546,53 @@ pub fn addCases(ctx: *TestContext) !void {
}
{
- var case = ctx.exe("wasm returns", wasi);
+ var case = ctx.exe("wasm function calls", wasi);
case.addCompareOutput(
\\export fn _start() u32 {
+ \\ foo();
+ \\ bar();
\\ return 42;
\\}
+ \\fn foo() void {
+ \\ bar();
+ \\ bar();
+ \\}
+ \\fn bar() void {}
,
"42\n",
);
case.addCompareOutput(
\\export fn _start() i64 {
+ \\ bar();
+ \\ foo();
+ \\ foo();
+ \\ bar();
+ \\ foo();
+ \\ bar();
\\ return 42;
\\}
+ \\fn foo() void {
+ \\ bar();
+ \\}
+ \\fn bar() void {}
,
"42\n",
);
case.addCompareOutput(
\\export fn _start() f32 {
+ \\ bar();
+ \\ foo();
\\ return 42.0;
\\}
+ \\fn foo() void {
+ \\ bar();
+ \\ bar();
+ \\ bar();
+ \\}
+ \\fn bar() void {}
,
// This is what you get when you take the bits of the IEE-754
// representation of 42.0 and reinterpret them as an unsigned