diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index ca571370adb99f84809d083aa4d465e91cea8276..b293d20db9d704fac8024ca2597d1b432b2e940d 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 27f1891caf2e7e28c42ed6c2be770c20aa97f4ad..20b2968c4882b5dbb515dfa00e79333c353cb549 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 996622443af5853ce14ed0a4ba21b619c9583393..6e33b11669964228394d1942c857c39e86357e27 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 c1ff64965fd036bcc0ada3cd19d5750a0f3012cf..ca9d06e0ecf40a9e2aa311786f2ff5cfaefe6c03 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(); + 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 @@ -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 a3e1c25190f0157f5ca7360bf457491760937946..e89ed37123508411b4be086595a159054a03b8d6 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 b0ec835a86af28e6ca2bb1e9d2bbfd0b3cdcfcaf..011ec2e9e46a77de52c034d44b13050d78a96c89 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); diff --git a/test/behavior/align.zig b/test/behavior/align.zig index a0f05e5c1c50fe2a6ea13370ff85a7e08490e38b..429169a3fefbb34d8d75ac19934cf3276e8f13df 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -149,6 +149,7 @@ test "return error union with 128-bit integer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; try expect(3 == try give()); } diff --git a/test/behavior/bugs/7250.zig b/test/behavior/bugs/7250.zig index ee04847e51c42d0dfdd19d80887e5ef3fb0860c2..ea05cf70e35fad58ce7a67d07b339a1b9ee760d6 100644 --- a/test/behavior/bugs/7250.zig +++ b/test/behavior/bugs/7250.zig @@ -18,5 +18,6 @@ test "reference a global threadlocal variable" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; _ = nrfx_uart_rx(&g_uart0); } diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index f3037c2d099c356441b8d340506dadc832f3864f..a3138ff88a91323490b42a662a097e74fab07a72 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1060,6 +1060,7 @@ test "compile time int to ptr of function" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO try foobar(FUNCTION_CONSTANT); } @@ -1141,6 +1142,7 @@ test "cast u128 to f128 and back" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO comptime try testCast128(); try testCast128(); diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index edb14e7f619264d68148ef0c60a57681ac1cbd9f..740eaefc90e60a5f62c4f70594ae080973bce24a 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -209,6 +209,7 @@ test "compile time slice of pointer to hard coded address" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; try expect(@ptrToInt(x) == 0x1000); try expect(x.len == 0x500); diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 1663966f53c7ac0ea8434002e569992a62b0194a..ae480d70756457707f88e1526d63bd1898dd9277 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -857,6 +857,7 @@ test "non-packed struct with u128 entry in union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const U = union(enum) { Num: u128,