wasm: Unify function generation

Like decl code generation, also unify the wasm backend and the wasm linker to call into
the general purpose `codegen.zig` to generate the code for a function.
This commit is contained in:
Luuk de Gram 2022-03-06 17:54:26 +01:00
parent 2faba4092a
commit 13fca53b92
No known key found for this signature in database
GPG Key ID: A8CFE58E4DC7D664
3 changed files with 59 additions and 31 deletions

View File

@ -8,6 +8,7 @@ const mem = std.mem;
const wasm = std.wasm;
const log = std.log.scoped(.codegen);
const codegen = @import("../../codegen.zig");
const Module = @import("../../Module.zig");
const Decl = Module.Decl;
const Type = @import("../../type.zig").Type;
@ -546,7 +547,7 @@ blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct {
value: WValue,
}) = .{},
/// `bytes` contains the wasm bytecode belonging to the 'code' section.
code: ArrayList(u8),
code: *ArrayList(u8),
/// The index the next local generated will have
/// NOTE: arguments share the index with locals therefore the first variable
/// will have the index that comes after the last argument's index
@ -566,9 +567,6 @@ locals: std.ArrayListUnmanaged(u8),
target: std.Target,
/// Represents the wasm binary file that is being linked.
bin_file: *link.File.Wasm,
/// Reference to the Module that this decl is part of.
/// Used to find the error value.
module: *Module,
/// List of MIR Instructions
mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
/// Contains extra data for MIR
@ -611,7 +609,6 @@ pub fn deinit(self: *Self) void {
self.locals.deinit(self.gpa);
self.mir_instructions.deinit(self.gpa);
self.mir_extra.deinit(self.gpa);
self.code.deinit();
self.* = undefined;
}
@ -822,7 +819,40 @@ fn genFunctype(gpa: Allocator, fn_ty: Type, target: std.Target) !wasm.Type {
};
}
pub fn genFunc(self: *Self) InnerError!void {
pub fn generate(
bin_file: *link.File,
src_loc: Module.SrcLoc,
func: *Module.Fn,
air: Air,
liveness: Liveness,
code: *std.ArrayList(u8),
debug_output: codegen.DebugInfoOutput,
) codegen.GenerateSymbolError!codegen.FnResult {
_ = debug_output; // TODO
_ = src_loc;
var code_gen: Self = .{
.gpa = bin_file.allocator,
.air = air,
.liveness = liveness,
.values = .{},
.code = code,
.decl = func.owner_decl,
.err_msg = undefined,
.locals = .{},
.target = bin_file.options.target,
.bin_file = bin_file.cast(link.File.Wasm).?,
};
defer code_gen.deinit();
genFunc(&code_gen) catch |err| switch (err) {
error.CodegenFail => return codegen.FnResult{ .fail = code_gen.err_msg },
else => |e| return e,
};
return codegen.FnResult{ .appended = {} };
}
fn genFunc(self: *Self) InnerError!void {
var func_type = try genFunctype(self.gpa, self.decl.ty, self.target);
defer func_type.deinit(self.gpa);
self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
@ -889,7 +919,7 @@ pub fn genFunc(self: *Self) InnerError!void {
var emit: Emit = .{
.mir = mir,
.bin_file = &self.bin_file.base,
.code = &self.code,
.code = self.code,
.locals = self.locals.items,
.decl = self.decl,
};
@ -1761,7 +1791,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
},
.ErrorSet => switch (val.tag()) {
.@"error" => {
const kv = try self.module.getErrorValue(val.getError().?);
const kv = try self.bin_file.base.options.module.?.getErrorValue(val.getError().?);
return WValue{ .imm32 = kv.value };
},
else => return WValue{ .imm32 = 0 },
@ -1852,7 +1882,7 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 {
.unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt())),
},
.ErrorSet => {
const kv = self.module.getErrorValue(val.getError().?) catch unreachable; // passed invalid `Value` to function
const kv = self.bin_file.base.options.module.?.getErrorValue(val.getError().?) catch unreachable; // passed invalid `Value` to function
return @bitCast(i32, kv.value);
},
else => unreachable, // Programmer called this function for an illegal type

View File

@ -83,8 +83,6 @@ pub fn generateFunction(
debug_output: DebugInfoOutput,
) GenerateSymbolError!FnResult {
switch (bin_file.options.target.cpu.arch) {
.wasm32 => unreachable, // has its own code path
.wasm64 => unreachable, // has its own code path
.arm,
.armeb,
=> return @import("arch/arm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
@ -136,6 +134,9 @@ pub fn generateFunction(
//.renderscript32 => return Function(.renderscript32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
//.renderscript64 => return Function(.renderscript64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
//.ve => return Function(.ve).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
.wasm32,
.wasm64,
=> return @import("arch/wasm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),
}
}

View File

@ -505,31 +505,28 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
decl.link.wasm.clear(self.base.allocator);
var codegen_: CodeGen = .{
.gpa = self.base.allocator,
.air = air,
.liveness = liveness,
.values = .{},
.code = std.ArrayList(u8).init(self.base.allocator),
.decl = decl,
.err_msg = undefined,
.locals = .{},
.target = self.base.options.target,
.bin_file = self,
.module = module,
};
defer codegen_.deinit();
var code_writer = std.ArrayList(u8).init(self.base.allocator);
defer code_writer.deinit();
const result = try codegen.generateFunction(
&self.base,
decl.srcLoc(),
func,
air,
liveness,
&code_writer,
.none,
);
// generate the 'code' section for the function declaration
codegen_.genFunc() catch |err| switch (err) {
error.CodegenFail => {
const code = switch (result) {
.appended => code_writer.items,
.fail => |em| {
decl.analysis = .codegen_failure;
try module.failed_decls.put(module.gpa, decl, codegen_.err_msg);
try module.failed_decls.put(module.gpa, decl, em);
return;
},
else => |e| return e,
};
return self.finishUpdateDecl(decl, codegen_.code.items);
return self.finishUpdateDecl(decl, code);
}
// Generate code for the Decl, storing it in memory to be later written to