aboutsummaryrefslogtreecommitdiff
path: root/src/arch
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2022-03-06 20:44:51 +0100
committerGitHub <noreply@github.com>2022-03-06 20:44:51 +0100
commit27c084065abcc404b7f58562f802999ae3ebce10 (patch)
tree3979a57d5ed16c8dc76534e43fbcddf373c4e2db /src/arch
parent9154a8606996ce34e5f1d805672c83e2b733f5a7 (diff)
parent13fca53b925e7de00b63efbf6ac3723a4df732a8 (diff)
downloadzig-27c084065abcc404b7f58562f802999ae3ebce10.tar.gz
zig-27c084065abcc404b7f58562f802999ae3ebce10.zip
Merge pull request #11070 from Luukdegram/wasm-unify
stage2: wasm - unify codegen with other backends
Diffstat (limited to 'src/arch')
-rw-r--r--src/arch/wasm/CodeGen.zig485
1 files changed, 46 insertions, 439 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index ca571370ad..b293d20db9 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -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;
}
@@ -639,7 +636,7 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
}
// When we need to pass the value by reference (such as a struct), we will
- // leverage `genTypedValue` to lower the constant to bytes and emit it
+ // leverage `generateSymbol` to lower the constant to bytes and emit it
// to the 'rodata' section. We then return the index into the section as `WValue`.
//
// In the other cases, we will simply lower the constant to a value that fits
@@ -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,
};
@@ -903,433 +933,6 @@ pub fn genFunc(self: *Self) InnerError!void {
};
}
-pub const DeclGen = struct {
- /// The decl we are generating code for.
- decl: *Decl,
- /// The symbol we're generating code for.
- /// This can either be the symbol of the Decl itself,
- /// or one of its locals.
- symbol_index: u32,
- gpa: Allocator,
- /// A reference to the linker, that will process the decl's
- /// code and create any relocations it deems neccesary.
- bin_file: *link.File.Wasm,
- /// This will be set when `InnerError` has been returned.
- /// In any other case, this will be 'undefined'.
- err_msg: *Module.ErrorMsg,
- /// Reference to the Module that is being compiled.
- /// Used to find the error value of an error.
- module: *Module,
- /// The list of bytes that have been generated so far,
- /// can be used to calculate the offset into a section.
- code: *std.ArrayList(u8),
-
- /// Sets `err_msg` on `DeclGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig
- fn fail(self: *DeclGen, comptime fmt: []const u8, args: anytype) InnerError {
- const src: LazySrcLoc = .{ .node_offset = 0 };
- const src_loc = src.toSrcLoc(self.decl);
- self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args);
- return error.CodegenFail;
- }
-
- fn target(self: *const DeclGen) std.Target {
- return self.bin_file.base.options.target;
- }
-
- pub fn genDecl(self: *DeclGen) InnerError!Result {
- const decl = self.decl;
- assert(decl.has_tv);
-
- log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val });
-
- if (decl.val.castTag(.function)) |func_payload| {
- _ = func_payload;
- return self.fail("TODO wasm backend genDecl function pointer", .{});
- } else if (decl.val.castTag(.extern_fn)) |extern_fn| {
- const ext_decl = extern_fn.data.owner_decl;
- var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target());
- defer func_type.deinit(self.gpa);
- ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
- return Result{ .appended = {} };
- } else {
- const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {
- break :init_val payload.data.init;
- } else decl.val;
- if (init_val.tag() != .unreachable_value) {
- return self.genTypedValue(decl.ty, init_val);
- }
- return Result{ .appended = {} };
- }
- }
-
- /// Generates the wasm bytecode for the declaration belonging to `Context`
- pub fn genTypedValue(self: *DeclGen, ty: Type, val: Value) InnerError!Result {
- log.debug("genTypedValue: ty = {}, val = {}", .{ ty, val });
-
- const writer = self.code.writer();
- if (val.isUndef()) {
- try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));
- return Result{ .appended = {} };
- }
- switch (ty.zigTypeTag()) {
- .Fn => {
- const fn_decl = switch (val.tag()) {
- .extern_fn => val.castTag(.extern_fn).?.data.owner_decl,
- .function => val.castTag(.function).?.data.owner_decl,
- else => unreachable,
- };
- return try self.lowerDeclRefValue(ty, val, fn_decl, 0);
- },
- .Optional => {
- var opt_buf: Type.Payload.ElemType = undefined;
- const payload_type = ty.optionalChild(&opt_buf);
- const is_pl = !val.isNull();
- const abi_size = @intCast(usize, ty.abiSize(self.target()));
- const offset = abi_size - @intCast(usize, payload_type.abiSize(self.target()));
-
- if (!payload_type.hasRuntimeBits()) {
- try writer.writeByteNTimes(@boolToInt(is_pl), abi_size);
- return Result{ .appended = {} };
- }
-
- if (ty.isPtrLikeOptional()) {
- if (val.castTag(.opt_payload)) |payload| {
- return self.genTypedValue(payload_type, payload.data);
- } else if (!val.isNull()) {
- return self.genTypedValue(payload_type, val);
- } else {
- try writer.writeByteNTimes(0, abi_size);
- return Result{ .appended = {} };
- }
- }
-
- // `null-tag` bytes
- try writer.writeByteNTimes(@boolToInt(is_pl), offset);
- switch (try self.genTypedValue(
- payload_type,
- if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
- )) {
- .appended => {},
- .externally_managed => |payload| try writer.writeAll(payload),
- }
- return Result{ .appended = {} };
- },
- .Array => switch (val.tag()) {
- .bytes => {
- const payload = val.castTag(.bytes).?;
- return Result{ .externally_managed = payload.data };
- },
- .array => {
- const elem_vals = val.castTag(.array).?.data;
- const elem_ty = ty.childType();
- for (elem_vals) |elem_val| {
- switch (try self.genTypedValue(elem_ty, elem_val)) {
- .appended => {},
- .externally_managed => |data| try writer.writeAll(data),
- }
- }
- return Result{ .appended = {} };
- },
- .repeated => {
- const array = val.castTag(.repeated).?.data;
- const elem_ty = ty.childType();
- const sentinel = ty.sentinel();
- const len = ty.arrayLen();
-
- var index: u32 = 0;
- while (index < len) : (index += 1) {
- switch (try self.genTypedValue(elem_ty, array)) {
- .externally_managed => |data| try writer.writeAll(data),
- .appended => {},
- }
- }
- if (sentinel) |sentinel_value| {
- return self.genTypedValue(elem_ty, sentinel_value);
- }
- return Result{ .appended = {} };
- },
- .empty_array_sentinel => {
- const elem_ty = ty.childType();
- const sent_val = ty.sentinel().?;
- return self.genTypedValue(elem_ty, sent_val);
- },
- else => unreachable,
- },
- .Int => {
- const info = ty.intInfo(self.target());
- const abi_size = @intCast(usize, ty.abiSize(self.target()));
- if (info.bits <= 64) {
- var buf: [8]u8 = undefined;
- if (info.signedness == .unsigned) {
- std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt());
- } else std.mem.writeIntLittle(i64, &buf, val.toSignedInt());
- try writer.writeAll(buf[0..abi_size]);
- return Result{ .appended = {} };
- }
- var space: Value.BigIntSpace = undefined;
- const bigint = val.toBigInt(&space);
- const iterations = @divExact(abi_size, @sizeOf(usize));
- for (bigint.limbs) |_, index| {
- const limb = bigint.limbs[bigint.limbs.len - index - 1];
- try writer.writeIntLittle(usize, limb);
- } else if (bigint.limbs.len < iterations) {
- // When the value is saved in less limbs than the required
- // abi size, we fill the remaining parts with 0's.
- var it_left = iterations - bigint.limbs.len;
- while (it_left > 0) {
- it_left -= 1;
- try writer.writeIntLittle(usize, 0);
- }
- }
- return Result{ .appended = {} };
- },
- .Float => {
- const float_bits = ty.floatBits(self.target());
- if (float_bits > 64) {
- return self.fail("Wasm TODO: Implement f80 and f128", .{});
- }
-
- switch (float_bits) {
- 16, 32 => try writer.writeIntLittle(u32, @bitCast(u32, val.toFloat(f32))),
- 64 => try writer.writeIntLittle(u64, @bitCast(u64, val.toFloat(f64))),
- else => unreachable,
- }
-
- return Result{ .appended = {} };
- },
- .Enum => {
- var int_buffer: Value.Payload.U64 = undefined;
- const int_val = val.enumToInt(ty, &int_buffer);
- var buf: Type.Payload.Bits = undefined;
- const int_ty = ty.intTagType(&buf);
- return self.genTypedValue(int_ty, int_val);
- },
- .Bool => {
- try writer.writeByte(@boolToInt(val.toBool()));
- return Result{ .appended = {} };
- },
- .Struct => {
- const struct_obj = ty.castTag(.@"struct").?.data;
- if (struct_obj.layout == .Packed) {
- return self.fail("TODO: Packed structs for wasm", .{});
- }
-
- const struct_begin = self.code.items.len;
- const field_vals = val.castTag(.@"struct").?.data;
- for (field_vals) |field_val, index| {
- const field_ty = ty.structFieldType(index);
- if (!field_ty.hasRuntimeBits()) continue;
-
- switch (try self.genTypedValue(field_ty, field_val)) {
- .appended => {},
- .externally_managed => |payload| try writer.writeAll(payload),
- }
- const unpadded_field_len = self.code.items.len - struct_begin;
-
- // Pad struct members if required
- const padded_field_end = ty.structFieldOffset(index + 1, self.target());
- const padding = try std.math.cast(usize, padded_field_end - unpadded_field_len);
-
- if (padding > 0) {
- try writer.writeByteNTimes(0, padding);
- }
- }
- return Result{ .appended = {} };
- },
- .Union => {
- const union_val = val.castTag(.@"union").?.data;
- const layout = ty.unionGetLayout(self.target());
-
- if (layout.payload_size == 0) {
- return self.genTypedValue(ty.unionTagType().?, union_val.tag);
- }
-
- // Check if we should store the tag first, in which case, do so now:
- if (layout.tag_align >= layout.payload_align) {
- switch (try self.genTypedValue(ty.unionTagType().?, union_val.tag)) {
- .appended => {},
- .externally_managed => |payload| try writer.writeAll(payload),
- }
- }
-
- const union_ty = ty.cast(Type.Payload.Union).?.data;
- const field_index = union_ty.tag_ty.enumTagFieldIndex(union_val.tag).?;
- assert(union_ty.haveFieldTypes());
- const field_ty = union_ty.fields.values()[field_index].ty;
- if (!field_ty.hasRuntimeBits()) {
- try writer.writeByteNTimes(0xaa, @intCast(usize, layout.payload_size));
- } else {
- switch (try self.genTypedValue(field_ty, union_val.val)) {
- .appended => {},
- .externally_managed => |payload| try writer.writeAll(payload),
- }
-
- // Unions have the size of the largest field, so we must pad
- // whenever the active field has a smaller size.
- const diff = layout.payload_size - field_ty.abiSize(self.target());
- if (diff > 0) {
- try writer.writeByteNTimes(0xaa, @intCast(usize, diff));
- }
- }
-
- if (layout.tag_size == 0) {
- return Result{ .appended = {} };
- }
- return self.genTypedValue(union_ty.tag_ty, union_val.tag);
- },
- .Pointer => switch (val.tag()) {
- .variable => {
- const decl = val.castTag(.variable).?.data.owner_decl;
- return self.lowerDeclRefValue(ty, val, decl, 0);
- },
- .decl_ref => {
- const decl = val.castTag(.decl_ref).?.data;
- return self.lowerDeclRefValue(ty, val, decl, 0);
- },
- .slice => {
- const slice = val.castTag(.slice).?.data;
- var buf: Type.SlicePtrFieldTypeBuffer = undefined;
- const ptr_ty = ty.slicePtrFieldType(&buf);
- switch (try self.genTypedValue(ptr_ty, slice.ptr)) {
- .externally_managed => |data| try writer.writeAll(data),
- .appended => {},
- }
- switch (try self.genTypedValue(Type.usize, slice.len)) {
- .externally_managed => |data| try writer.writeAll(data),
- .appended => {},
- }
- return Result{ .appended = {} };
- },
- .zero => {
- try writer.writeByteNTimes(0, @divExact(self.target().cpu.arch.ptrBitWidth(), 8));
- return Result{ .appended = {} };
- },
- .elem_ptr => {
- const elem_ptr = val.castTag(.elem_ptr).?.data;
- const elem_size = ty.childType().abiSize(self.target());
- const offset = elem_ptr.index * elem_size;
- return self.lowerParentPtr(elem_ptr.array_ptr, @intCast(usize, offset));
- },
- .int_u64 => return self.genTypedValue(Type.usize, val),
- else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),
- },
- .ErrorUnion => {
- const error_ty = ty.errorUnionSet();
- const payload_ty = ty.errorUnionPayload();
- const is_pl = val.errorUnionIsPayload();
- const abi_align = ty.abiAlignment(self.target());
-
- {
- const err_val = if (!is_pl) val else Value.initTag(.zero);
- const start = self.code.items.len;
- switch (try self.genTypedValue(error_ty, err_val)) {
- .externally_managed => |data| try writer.writeAll(data),
- .appended => {},
- }
- const unpadded_end = self.code.items.len - start;
- const padded_end = mem.alignForwardGeneric(usize, unpadded_end, abi_align);
- const padding = padded_end - unpadded_end;
- if (padding > 0) {
- try writer.writeByteNTimes(0, padding);
- }
- }
-
- if (payload_ty.hasRuntimeBits()) {
- const start = self.code.items.len;
- const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef);
- switch (try self.genTypedValue(payload_ty, pl_val)) {
- .externally_managed => |data| try writer.writeAll(data),
- .appended => {},
- }
-
- const unpadded_end = self.code.items.len - start;
- const padded_end = mem.alignForwardGeneric(usize, unpadded_end, abi_align);
- const padding = padded_end - unpadded_end;
- if (padding > 0) {
- try writer.writeByteNTimes(0, padding);
- }
- }
-
- return Result{ .appended = {} };
- },
- .ErrorSet => {
- switch (val.tag()) {
- .@"error" => {
- const name = val.castTag(.@"error").?.data.name;
- const kv = try self.module.getErrorValue(name);
- try writer.writeIntLittle(u32, kv.value);
- },
- else => {
- try writer.writeByteNTimes(0, @intCast(usize, ty.abiSize(self.target())));
- },
- }
- return Result{ .appended = {} };
- },
- else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
- }
- }
-
- fn lowerParentPtr(self: *DeclGen, ptr_value: Value, offset: usize) InnerError!Result {
- switch (ptr_value.tag()) {
- .decl_ref => {
- const decl = ptr_value.castTag(.decl_ref).?.data;
- return self.lowerParentPtrDecl(ptr_value, decl, offset);
- },
- else => |tag| return self.fail("TODO: Implement lowerParentPtr for pointer value tag: {s}", .{tag}),
- }
- }
-
- fn lowerParentPtrDecl(self: *DeclGen, ptr_val: Value, decl: *Module.Decl, offset: usize) InnerError!Result {
- decl.markAlive();
- var ptr_ty_payload: Type.Payload.ElemType = .{
- .base = .{ .tag = .single_mut_pointer },
- .data = decl.ty,
- };
- const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
- return self.lowerDeclRefValue(ptr_ty, ptr_val, decl, offset);
- }
-
- fn lowerDeclRefValue(
- self: *DeclGen,
- ty: Type,
- val: Value,
- /// The target decl that is being pointed to
- decl: *Module.Decl,
- /// When lowering to an indexed pointer, we can specify the offset
- /// which will then be used as 'addend' to the relocation.
- offset: usize,
- ) InnerError!Result {
- const writer = self.code.writer();
- if (ty.isSlice()) {
- var buf: Type.SlicePtrFieldTypeBuffer = undefined;
- const slice_ty = ty.slicePtrFieldType(&buf);
- switch (try self.genTypedValue(slice_ty, val)) {
- .appended => {},
- .externally_managed => |payload| try writer.writeAll(payload),
- }
- var slice_len: Value.Payload.U64 = .{
- .base = .{ .tag = .int_u64 },
- .data = val.sliceLen(),
- };
- return self.genTypedValue(Type.usize, Value.initPayload(&slice_len.base));
- }
-
- decl.markAlive();
- if (decl.link.wasm.sym_index == 0) {
- try writer.writeIntLittle(u32, 0);
- } else {
- try writer.writeIntLittle(u32, try self.bin_file.getDeclVAddr(
- self.decl, // parent decl that owns the atom of the symbol
- self.symbol_index, // source symbol index
- decl, // target decl that contains the target symbol
- @intCast(u32, self.code.items.len), // offset
- @intCast(u32, offset), // addend
- ));
- }
- return Result{ .appended = {} };
- }
-};
-
const CallWValues = struct {
args: []WValue,
return_value: WValue,
@@ -1809,8 +1412,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
if (func_val.castTag(.function)) |func| {
break :blk func.data.owner_decl;
- } else if (func_val.castTag(.extern_fn)) |ext_fn| {
- break :blk ext_fn.data.owner_decl;
+ } else if (func_val.castTag(.extern_fn)) |extern_fn| {
+ const ext_decl = extern_fn.data.owner_decl;
+ var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target);
+ defer func_type.deinit(self.gpa);
+ ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
+ break :blk ext_decl;
} else if (func_val.castTag(.decl_ref)) |decl_ref| {
break :blk decl_ref.data;
}
@@ -2184,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 },
@@ -2275,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