aboutsummaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/arch/wasm/CodeGen.zig485
-rw-r--r--src/codegen.zig5
-rw-r--r--src/link.zig2
-rw-r--r--src/link/Wasm.zig168
-rw-r--r--src/link/Wasm/Atom.zig7
-rw-r--r--src/link/Wasm/Object.zig1
6 files changed, 140 insertions, 528 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
diff --git a/src/codegen.zig b/src/codegen.zig
index 27f1891caf..20b2968c48 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -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."),
}
}
diff --git a/src/link.zig b/src/link.zig
index 996622443a..6e33b11669 100644
--- a/src/link.zig
+++ b/src/link.zig
@@ -702,7 +702,7 @@ pub const File = struct {
.macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl, reloc_info),
.plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl, reloc_info),
.c => unreachable,
- .wasm => unreachable,
+ .wasm => return @fieldParentPtr(Wasm, "base", base).getDeclVAddr(decl, reloc_info),
.spirv => unreachable,
.nvptx => unreachable,
}
diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig
index c1ff64965f..ca9d06e0ec 100644
--- a/src/link/Wasm.zig
+++ b/src/link/Wasm.zig
@@ -14,6 +14,7 @@ const Atom = @import("Wasm/Atom.zig");
const Module = @import("../Module.zig");
const Compilation = @import("../Compilation.zig");
const CodeGen = @import("../arch/wasm/CodeGen.zig");
+const codegen = @import("../codegen.zig");
const link = @import("../link.zig");
const lldMain = @import("../main.zig").lldMain;
const trace = @import("../tracy.zig").trace;
@@ -489,10 +490,8 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
self.symbols.appendAssumeCapacity(symbol);
}
- try self.resolved_symbols.putNoClobber(self.base.allocator, .{
- .index = atom.sym_index,
- .file = null,
- }, {});
+ try self.resolved_symbols.putNoClobber(self.base.allocator, atom.symbolLoc(), {});
+ try self.symbol_atom.putNoClobber(self.base.allocator, atom.symbolLoc(), atom);
}
pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
@@ -505,33 +504,30 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
const decl = func.owner_decl;
assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes()
- decl.link.wasm.clear();
-
- 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();
+ decl.link.wasm.clear(self.base.allocator);
+
+ 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
@@ -546,33 +542,39 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes()
- decl.link.wasm.clear();
+ decl.link.wasm.clear(self.base.allocator);
+
+ if (decl.isExtern()) {
+ return self.addOrUpdateImport(decl);
+ }
+
+ if (decl.val.castTag(.function)) |_| {
+ return;
+ } else if (decl.val.castTag(.extern_fn)) |_| {
+ return;
+ }
+ const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
var code_writer = std.ArrayList(u8).init(self.base.allocator);
defer code_writer.deinit();
- var decl_gen: CodeGen.DeclGen = .{
- .gpa = self.base.allocator,
- .decl = decl,
- .symbol_index = decl.link.wasm.sym_index,
- .bin_file = self,
- .err_msg = undefined,
- .code = &code_writer,
- .module = module,
- };
- // generate the 'code' section for the function declaration
- const result = decl_gen.genDecl() catch |err| switch (err) {
- error.CodegenFail => {
+ const res = try codegen.generateSymbol(
+ &self.base,
+ decl.srcLoc(),
+ .{ .ty = decl.ty, .val = val },
+ &code_writer,
+ .none,
+ .{ .parent_atom_index = decl.link.wasm.sym_index },
+ );
+
+ const code = switch (res) {
+ .externally_managed => |x| x,
+ .appended => code_writer.items,
+ .fail => |em| {
decl.analysis = .codegen_failure;
- try module.failed_decls.put(module.gpa, decl, decl_gen.err_msg);
+ try module.failed_decls.put(module.gpa, decl, em);
return;
},
- else => |e| return e,
- };
-
- const code = switch (result) {
- .externally_managed => |data| data,
- .appended => code_writer.items,
};
return self.finishUpdateDecl(decl, code);
@@ -603,7 +605,9 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 {
// Create and initialize a new local symbol and atom
const local_index = decl.link.wasm.locals.items.len;
- const name = try std.fmt.allocPrintZ(self.base.allocator, "__unnamed_{s}_{d}", .{ decl.name, local_index });
+ const fqdn = try decl.getFullyQualifiedName(self.base.allocator);
+ defer self.base.allocator.free(fqdn);
+ const name = try std.fmt.allocPrintZ(self.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index });
defer self.base.allocator.free(name);
var symbol: Symbol = .{
.name = try self.string_table.put(self.base.allocator, name),
@@ -625,36 +629,32 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 {
atom.sym_index = @intCast(u32, self.symbols.items.len);
self.symbols.appendAssumeCapacity(symbol);
}
- try self.resolved_symbols.putNoClobber(self.base.allocator, .{
- .file = null,
- .index = atom.sym_index,
- }, {});
+ try self.resolved_symbols.putNoClobber(self.base.allocator, atom.symbolLoc(), {});
+ try self.symbol_atom.putNoClobber(self.base.allocator, atom.symbolLoc(), atom);
var value_bytes = std.ArrayList(u8).init(self.base.allocator);
defer value_bytes.deinit();
const module = self.base.options.module.?;
- var decl_gen: CodeGen.DeclGen = .{
- .bin_file = self,
- .decl = decl,
- .err_msg = undefined,
- .gpa = self.base.allocator,
- .module = module,
- .code = &value_bytes,
- .symbol_index = atom.sym_index,
- };
-
- const result = decl_gen.genTypedValue(tv.ty, tv.val) catch |err| switch (err) {
- error.CodegenFail => {
- decl.analysis = .codegen_failure;
- try module.failed_decls.put(module.gpa, decl, decl_gen.err_msg);
- return error.AnalysisFail;
+ const result = try codegen.generateSymbol(
+ &self.base,
+ decl.srcLoc(),
+ tv,
+ &value_bytes,
+ .none,
+ .{
+ .parent_atom_index = atom.sym_index,
+ .addend = null,
},
- else => |e| return e,
- };
+ );
const code = switch (result) {
+ .externally_managed => |x| x,
.appended => value_bytes.items,
- .externally_managed => |data| data,
+ .fail => |em| {
+ decl.analysis = .codegen_failure;
+ try module.failed_decls.put(module.gpa, decl, em);
+ return error.AnalysisFail;
+ },
};
atom.size = @intCast(u32, code.len);
@@ -666,35 +666,31 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 {
/// Returns the given pointer address
pub fn getDeclVAddr(
self: *Wasm,
- decl: *Module.Decl,
- symbol_index: u32,
- target_decl: *Module.Decl,
- offset: u32,
- addend: u32,
-) !u32 {
- const target_symbol_index = target_decl.link.wasm.sym_index;
+ decl: *const Module.Decl,
+ reloc_info: link.File.RelocInfo,
+) !u64 {
+ const target_symbol_index = decl.link.wasm.sym_index;
assert(target_symbol_index != 0);
- assert(symbol_index != 0);
-
- const atom = decl.link.wasm.symbolAtom(symbol_index);
+ assert(reloc_info.parent_atom_index != 0);
+ const atom = self.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?;
const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;
- if (target_decl.ty.zigTypeTag() == .Fn) {
- assert(addend == 0); // addend not allowed for function relocations
+ if (decl.ty.zigTypeTag() == .Fn) {
+ assert(reloc_info.addend == 0); // addend not allowed for function relocations
// We found a function pointer, so add it to our table,
// as function pointers are not allowed to be stored inside the data section.
// They are instead stored in a function table which are called by index.
try self.addTableFunction(target_symbol_index);
try atom.relocs.append(self.base.allocator, .{
.index = target_symbol_index,
- .offset = offset,
+ .offset = @intCast(u32, reloc_info.offset),
.relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64,
});
} else {
try atom.relocs.append(self.base.allocator, .{
.index = target_symbol_index,
- .offset = offset,
+ .offset = @intCast(u32, reloc_info.offset),
.relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
- .addend = addend,
+ .addend = reloc_info.addend,
});
}
// we do not know the final address at this point,
@@ -824,12 +820,14 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
local_symbol.tag = .dead; // also for any local symbol
self.symbols_free_list.append(self.base.allocator, local_atom.sym_index) catch {};
assert(self.resolved_symbols.swapRemove(local_atom.symbolLoc()));
+ assert(self.symbol_atom.remove(local_atom.symbolLoc()));
}
if (decl.isExtern()) {
assert(self.imports.remove(atom.symbolLoc()));
}
assert(self.resolved_symbols.swapRemove(atom.symbolLoc()));
+ assert(self.symbol_atom.remove(atom.symbolLoc()));
atom.deinit(self.base.allocator);
}
@@ -989,7 +987,7 @@ fn allocateAtoms(self: *Wasm) !void {
atom.size,
});
offset += atom.size;
- try self.symbol_atom.putNoClobber(self.base.allocator, symbol_loc, atom);
+ self.symbol_atom.putAssumeCapacity(atom.symbolLoc(), atom); // Update atom pointers
atom = atom.next orelse break;
}
}
diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig
index a3e1c25190..e89ed37123 100644
--- a/src/link/Wasm/Atom.zig
+++ b/src/link/Wasm/Atom.zig
@@ -62,9 +62,14 @@ pub fn deinit(self: *Atom, gpa: Allocator) void {
/// Sets the length of relocations and code to '0',
/// effectively resetting them and allowing them to be re-populated.
-pub fn clear(self: *Atom) void {
+pub fn clear(self: *Atom, gpa: Allocator) void {
self.relocs.clearRetainingCapacity();
self.code.clearRetainingCapacity();
+
+ // locals will be re-generated
+ for (self.locals.items) |*local| {
+ local.deinit(gpa);
+ }
}
pub fn format(self: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig
index b0ec835a86..011ec2e9e4 100644
--- a/src/link/Wasm/Object.zig
+++ b/src/link/Wasm/Object.zig
@@ -861,6 +861,7 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin
}
try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]);
+ try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom);
const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index];
segment.alignment = std.math.max(segment.alignment, atom.alignment);