Skip to content

Commit

Permalink
Merge pull request #10240 from Luukdegram/stage2-wasm-behaviour
Browse files Browse the repository at this point in the history
Stage2: wasm - Implement 'zig test'
  • Loading branch information
kubkon committed Nov 29, 2021
2 parents 2ca5a85 + adf059f commit 7a7df39
Show file tree
Hide file tree
Showing 9 changed files with 623 additions and 244 deletions.
18 changes: 18 additions & 0 deletions lib/std/start.zig
Expand Up @@ -30,6 +30,8 @@ comptime {
}
} else if (builtin.os.tag == .windows) {
@export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });
} else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) {
@export(wasmMain2, .{ .name = "_start" });
} else {
if (!@hasDecl(root, "_start")) {
@export(_start2, .{ .name = "_start" });
Expand Down Expand Up @@ -98,6 +100,22 @@ fn callMain2() noreturn {
exit2(0);
}

fn wasmMain2() u8 {
switch (@typeInfo(@typeInfo(@TypeOf(root.main)).Fn.return_type.?)) {
.Void => {
root.main();
return 0;
},
.Int => |info| {
if (info.bits != 8 or info.signedness == .signed) {
@compileError(bad_main_ret);
}
return root.main();
},
else => @compileError("Bad return type main"),
}
}

fn wWinMainCRTStartup2() callconv(.C) noreturn {
root.main();
exit2(0);
Expand Down
355 changes: 315 additions & 40 deletions src/arch/wasm/CodeGen.zig

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion src/arch/wasm/Emit.zig
Expand Up @@ -47,6 +47,7 @@ pub fn emitMir(emit: *Emit) InnerError!void {

// relocatables
.call => try emit.emitCall(inst),
.call_indirect => try emit.emitCallIndirect(inst),
.global_get => try emit.emitGlobal(tag, inst),
.global_set => try emit.emitGlobal(tag, inst),
.memory_address => try emit.emitMemAddress(inst),
Expand Down Expand Up @@ -256,7 +257,7 @@ fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
try emit.code.append(@enumToInt(tag));

// wasm encodes alignment as power of 2, rather than natural alignment
const encoded_alignment = mem_arg.alignment >> 1;
const encoded_alignment = @ctz(u32, mem_arg.alignment);
try leb128.writeULEB128(emit.code.writer(), encoded_alignment);
try leb128.writeULEB128(emit.code.writer(), mem_arg.offset);
}
Expand All @@ -276,6 +277,13 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
});
}

fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {
const label = emit.mir.instructions.items(.data)[inst].label;
try emit.code.append(std.wasm.opcode(.call_indirect));
try leb128.writeULEB128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index
try leb128.writeULEB128(emit.code.writer(), label);
}

fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
const symbol_index = emit.mir.instructions.items(.data)[inst].label;
try emit.code.append(std.wasm.opcode(.i32_const));
Expand Down
5 changes: 5 additions & 0 deletions src/arch/wasm/Mir.zig
Expand Up @@ -69,6 +69,11 @@ pub const Inst = struct {
///
/// Uses `label`
call = 0x10,
/// Calls a function pointer by its function signature
/// and index into the function table.
///
/// Uses `label`
call_indirect = 0x11,
/// Loads a local at given index onto the stack.
///
/// Uses `label`
Expand Down
80 changes: 65 additions & 15 deletions src/link/Wasm.zig
Expand Up @@ -79,7 +79,9 @@ memories: wasm.Memory = .{ .limits = .{ .min = 0, .max = null } },
/// Indirect function table, used to call function pointers
/// When this is non-zero, we must emit a table entry,
/// as well as an 'elements' section.
function_table: std.ArrayListUnmanaged(Symbol) = .{},
///
/// Note: Key is symbol index, value represents the index into the table
function_table: std.AutoHashMapUnmanaged(u32, u32) = .{},

pub const Segment = struct {
alignment: u32,
Expand Down Expand Up @@ -276,7 +278,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
defer codegen.deinit();

// generate the 'code' section for the function declaration
const result = codegen.gen(decl.ty, decl.val) catch |err| switch (err) {
const result = codegen.genDecl(decl.ty, decl.val) catch |err| switch (err) {
error.CodegenFail => {
decl.analysis = .codegen_failure;
try module.failed_decls.put(module.gpa, decl, codegen.err_msg);
Expand Down Expand Up @@ -334,6 +336,25 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
else => unreachable,
}
}

// maybe remove from function table if needed
if (decl.ty.zigTypeTag() == .Fn) {
_ = self.function_table.remove(atom.sym_index);
}
}

/// Appends a new entry to the indirect function table
pub fn addTableFunction(self: *Wasm, symbol_index: u32) !void {
const index = @intCast(u32, self.function_table.count());
try self.function_table.put(self.base.allocator, symbol_index, index);
}

fn mapFunctionTable(self: *Wasm) void {
var it = self.function_table.valueIterator();
var index: u32 = 0;
while (it.next()) |value_ptr| : (index += 1) {
value_ptr.* = index;
}
}

fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
Expand Down Expand Up @@ -583,6 +604,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {

try self.setupMemory();
try self.allocateAtoms();
self.mapFunctionTable();

const file = self.base.file.?;
const header_size = 5 + 1;
Expand Down Expand Up @@ -662,6 +684,22 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
);
}

if (self.function_table.count() > 0) {
const header_offset = try reserveVecSectionHeader(file);
const writer = file.writer();

try leb.writeULEB128(writer, wasm.reftype(.funcref));
try emitLimits(writer, .{ .min = 1, .max = null });

try writeVecSectionHeader(
file,
header_offset,
.table,
@intCast(u32, (try file.getPos()) - header_offset - header_size),
@as(u32, 1),
);
}

// Memory section
if (!self.base.options.import_memory) {
const header_offset = try reserveVecSectionHeader(file);
Expand Down Expand Up @@ -743,6 +781,31 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
);
}

// element section (function table)
if (self.function_table.count() > 0) {
const header_offset = try reserveVecSectionHeader(file);
const writer = file.writer();

var flags: u32 = 0x2; // Yes we have a table
try leb.writeULEB128(writer, flags);
try leb.writeULEB128(writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols
try emitInit(writer, .{ .i32_const = 0 });
try leb.writeULEB128(writer, @as(u8, 0));
try leb.writeULEB128(writer, @intCast(u32, self.function_table.count()));
var symbol_it = self.function_table.keyIterator();
while (symbol_it.next()) |symbol_index_ptr| {
try leb.writeULEB128(writer, self.symbols.items[symbol_index_ptr.*].index);
}

try writeVecSectionHeader(
file,
header_offset,
.element,
@intCast(u32, (try file.getPos()) - header_offset - header_size),
@as(u32, 1),
);
}

// Code section
if (self.code_section_index) |code_index| {
const header_offset = try reserveVecSectionHeader(file);
Expand Down Expand Up @@ -1233,16 +1296,3 @@ pub fn putOrGetFuncType(self: *Wasm, func_type: wasm.Type) !u32 {
});
return index;
}

/// From a given index and an `ExternalKind`, finds the corresponding Import.
/// This is due to indexes for imports being unique per type, rather than across all imports.
fn findImport(self: Wasm, index: u32, external_type: wasm.ExternalKind) ?*wasm.Import {
var current_index: u32 = 0;
for (self.imports.items) |*import| {
if (import.kind == external_type) {
if (current_index == index) return import;
current_index += 1;
}
}
return null;
}
8 changes: 3 additions & 5 deletions src/link/Wasm/Atom.zig
Expand Up @@ -83,7 +83,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {

for (self.relocs.items) |reloc| {
const value = try relocationValue(reloc, wasm_bin);
log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}\n", .{
log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
wasm_bin.symbols.items[reloc.index].name,
symbol.name,
reloc.offset,
Expand Down Expand Up @@ -129,7 +129,7 @@ fn relocationValue(relocation: types.Relocation, wasm_bin: *const Wasm) !u64 {
.R_WASM_TABLE_INDEX_I64,
.R_WASM_TABLE_INDEX_SLEB,
.R_WASM_TABLE_INDEX_SLEB64,
=> return error.TodoImplementTableIndex, // find table index from a function symbol
=> return wasm_bin.function_table.get(relocation.index) orelse 0,
.R_WASM_TYPE_INDEX_LEB => wasm_bin.functions.items[symbol.index].type_index,
.R_WASM_GLOBAL_INDEX_I32,
.R_WASM_GLOBAL_INDEX_LEB,
Expand All @@ -152,9 +152,7 @@ fn relocationValue(relocation: types.Relocation, wasm_bin: *const Wasm) !u64 {
target_atom = target_atom.next orelse break;
}
const segment = wasm_bin.segments.items[atom_index];
const base = wasm_bin.base.options.global_base orelse 1024;
const offset = target_atom.offset + segment.offset;
break :blk offset + base + (relocation.addend orelse 0);
break :blk target_atom.offset + segment.offset + (relocation.addend orelse 0);
},
.R_WASM_EVENT_INDEX_LEB => symbol.index,
.R_WASM_SECTION_OFFSET_I32,
Expand Down
2 changes: 1 addition & 1 deletion test/cases.zig
Expand Up @@ -26,7 +26,7 @@ pub fn addCases(ctx: *TestContext) !void {
var case = ctx.exe("hello world with updates", linux_x64);

case.addError("", &[_][]const u8{
":97:9: error: struct 'tmp.tmp' has no member named 'main'",
":99:9: error: struct 'tmp.tmp' has no member named 'main'",
});

// Incorrect return type
Expand Down
2 changes: 1 addition & 1 deletion test/stage2/darwin.zig
Expand Up @@ -14,7 +14,7 @@ pub fn addCases(ctx: *TestContext) !void {
{
var case = ctx.exe("darwin hello world with updates", target);
case.addError("", &[_][]const u8{
":97:9: error: struct 'tmp.tmp' has no member named 'main'",
":99:9: error: struct 'tmp.tmp' has no member named 'main'",
});

// Incorrect return type
Expand Down

0 comments on commit 7a7df39

Please sign in to comment.