From 3c27df6b135f998facce1aa09a9926e840671a05 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 9 Apr 2023 19:24:24 +0200 Subject: [PATCH 1/4] wasm: generate function to get tag name When we lower the instruction for `@tagName` we generate a new function if it doesn't exist yet for that decl. This function creates an if-else chain to determine which value was provided. Each tag generates a constant that contains its name as the value. For each tag we generate a case where the pointer of the string is stored in the result slice. The length of the tagname is comptime-known, therefore will be stored in the slice directly without having it being part of the tagname symbol. In the future this can use a jump table instead of an if-else chain, similar to the `switch` instruction. --- src/arch/wasm/CodeGen.zig | 183 +++++++++++++++++++++++++++++++++++++- src/link/Wasm.zig | 4 +- 2 files changed, 184 insertions(+), 3 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 8eb767fbb5..3282d1304a 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1944,6 +1944,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .memcpy => func.airMemcpy(inst), .ret_addr => func.airRetAddr(inst), + .tag_name => func.airTagName(inst), .mul_sat, .mod, @@ -1962,7 +1963,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .atomic_store_release, .atomic_store_seq_cst, .atomic_rmw, - .tag_name, .err_return_trace, .set_err_return_trace, .save_err_return_trace_index, @@ -6396,3 +6396,184 @@ fn callIntrinsic( return WValue{ .stack = {} }; } } + +fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { + const un_op = func.air.instructions.items(.data)[inst].un_op; + if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op}); + // const operand = try func.resolveInst(un_op); + const enum_ty = func.air.typeOf(un_op); + + _ = try func.getTagNameFunction(enum_ty); + + func.finishAir(inst, .none, &.{un_op}); +} + +fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { + const enum_decl_index = enum_ty.getOwnerDecl(); + const module = func.bin_file.base.options.module.?; + + // check if we already generated code for this. + if (func.bin_file.decls.get(enum_decl_index)) |decl_atom_index| { + std.debug.print("Found atom index for Enum decl! {d}\n", .{decl_atom_index}); + const atom = func.bin_file.getAtom(decl_atom_index); + return atom.getSymbolIndex().?; + } + + // Create an atom in which we will store all tag names. + const func_atom_index = try func.bin_file.getOrCreateAtomForDecl(enum_decl_index); + const func_atom = func.bin_file.getAtomPtr(func_atom_index); + std.debug.print("Generated a new atom! {d}\n", .{func_atom_index}); + + var arena_allocator = std.heap.ArenaAllocator.init(func.gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const fqn = try module.declPtr(enum_decl_index).getFullyQualifiedName(module); + defer module.gpa.free(fqn); + const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{fqn}); + + if (func.bin_file.findGlobalSymbol(func_name)) |loc| { + return loc.index; + } + + const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); + var int_tag_type_buffer: Type.Payload.Bits = undefined; + const int_tag_ty = enum_ty.intTagType(&int_tag_type_buffer); + + if (int_tag_ty.bitSize(func.target) > 64) { + return func.fail("TODO: Implement @tagName for enums with tag size larger than 64 bits", .{}); + } + + var func_type = try genFunctype(func.gpa, .Unspecified, &.{int_tag_ty}, slice_ty, func.target); + defer func_type.deinit(func.gpa); + try func.bin_file.storeDeclType(enum_decl_index, func_type); + + var body_list = std.ArrayList(u8).init(arena); + var writer = body_list.writer(); + + // The locals of the function body (always 2) + try leb.writeULEB128(writer, @as(u32, 2)); + try leb.writeULEB128(writer, @as(u32, 1)); + try writer.writeByte(func.genValtype(slice_ty, func.target)); + try leb.writeULEB128(writer, @as(u32, 1)); + try writer.writeByte(func.genValtype(int_tag_ty, func.target)); + + // outer block + try writer.writeByte(std.wasm.opcode(.block)); + + // TODO: Make switch implementation generic so we can use a jump table for this when the tags are not sparse. + // generate an if-else chain for each tag value as well as constant. + for (enum_ty.enumFields().keys(), 0..) |tag_name, field_index| { + // for each tag name, create an atom to store its name into, + // and then get a pointer to its value. + const tag_atom_index = try func.bin_file.createAtom(); + const tag_atom = func.bin_file.getAtomPtr(tag_atom_index); + tag_atom.alignment = 1; + try func.bin_file.parseAtom(tag_atom_index, .read_only); + try tag_atom.code.appendSlice(func.gpa, tag_name); + + // block for this if case + try writer.writeByte(std.wasm.opcode(.block)); + + // get actual tag value (stored in 2nd parameter); + try writer.writeByte(std.wasm.opcode(.local_get)); + try leb.writeULEB128(writer, @as(u32, 1)); + + const tag_value = int: { + var tag_val_payload: Value.Payload.U32 = .{ + .base = .{ .tag = .enum_field_index }, + .data = @intCast(u32, field_index), + }; + break :int try func.lowerConstant(Value.initPayload(&tag_val_payload.base), enum_ty); + }; + + switch (tag_value) { + .imm32 => |value| { + try writer.writeByte(std.wasm.opcode(.i32_const)); + try leb.writeULEB128(writer, value); + try writer.writeByte(std.wasm.opcode(.i32_neq)); + }, + .imm64 => |value| { + try writer.writeByte(std.wasm.opcode(.i64_const)); + try leb.writeULEB128(writer, value); + try writer.writeByte(std.wasm.opcode(.i64_neq)); + }, + else => unreachable, + } + // if they're not equal, break out of current branch + try writer.writeByte(std.wasm.opcode(.br_if)); + + // store the address of the tagname in the pointer field of the slice + try writer.writeByte(std.wasm.opcode(.local_get)); + try leb.writeULEB128(writer, @as(u32, 0)); + + const is_wasm32 = func.arch() == .wasm32; + // get address of tagname and emit a relocation to it + { + if (is_wasm32) { + try writer.writeByte(std.wasm.opcode(.i32_const)); + var buf: [5]u8 = undefined; + leb.writeUnsignedFixed(5, &buf, mem.pointer); + try writer.writeAll(&buf); + } else { + try writer.writeByte(std.wasm.opcode(.i64_const)); + var buf: [10]u8 = undefined; + leb.writeUnsignedFixed(10, &buf, mem.pointer); + try writer.writeAll(&buf); + } + try func_atom.relocs.append(func.gpa, .{ + .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64, + .offset = @intCast(u32, body_list.items.len), + .index = tag_atom.getSymbolIndex().?, + }); + } + + // call the actual store instructions + if (is_wasm32) { + // store pointer + try writer.writeByte(std.wasm.opcode(.i32_store)); + try leb.writeULEB128(writer, @as(u32, 4)); + try leb.writeULEB128(writer, @as(u32, 0)); + + // store length + try writer.writeByte(std.wasm.opcode(.local_get)); + try leb.writeULEB128(writer, @as(u32, 0)); + try writer.writeByte(std.wasm.opcode(.i32_const)); + try leb.writeULEB128(writer, @intCast(u32, tag_name.len)); + try writer.writeByte(std.wasm.opcode(.i32_store)); + try leb.writeULEB128(writer, @as(u32, 4)); + try leb.writeULEB128(writer, @as(u32, 4)); + } else { + // store pointer + try writer.writeByte(std.wasm.opcode(.i64_store)); + try leb.writeULEB128(writer, @as(u32, 8)); + try leb.writeULEB128(writer, @as(u32, 0)); + + // store length + try writer.writeByte(std.wasm.opcode(.local_get)); + try leb.writeULEB128(writer, @as(u32, 0)); + try writer.writeByte(std.wasm.opcode(.i64_const)); + try leb.writeULEB128(writer, @intCast(u64, tag_name.len)); + try writer.writeByte(std.wasm.opcode(.i64_store)); + try leb.writeULEB128(writer, @as(u32, 8)); + try leb.writeULEB128(writer, @as(u32, 8)); + } + + // break outside blocks + try writer.writeByte(std.wasm.opcode(.br)); + try leb.writeULEB128(writer, @as(u32, 1)); + + // end the block for this case + try writer.writeByte(std.wasm.opcode(.end)); + } + + try writer.writeByte(std.wasm.opcode(.@"unreachable")); // tag value does not have a name + // finish outer block + try writer.writeByte(std.wasm.opcode(.end)); + // finish function body + try writer.writeByte(std.wasm.opcode(.end)); + + try func_atom.code.appendSlice(func.gpa, body_list.items); + + return func_atom.sym_index; +} diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index b1fc25bf26..639b7f0776 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -577,7 +577,7 @@ pub fn getOrCreateAtomForDecl(wasm: *Wasm, decl_index: Module.Decl.Index) !Atom. } /// Creates a new empty `Atom` and returns its `Atom.Index` -fn createAtom(wasm: *Wasm) !Atom.Index { +pub fn createAtom(wasm: *Wasm) !Atom.Index { const index = @intCast(Atom.Index, wasm.managed_atoms.items.len); const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); atom.* = Atom.empty; @@ -1208,7 +1208,7 @@ fn resolveLazySymbols(wasm: *Wasm) !void { // Tries to find a global symbol by its name. Returns null when not found, /// and its location when it is found. -fn findGlobalSymbol(wasm: *Wasm, name: []const u8) ?SymbolLoc { +pub fn findGlobalSymbol(wasm: *Wasm, name: []const u8) ?SymbolLoc { const offset = wasm.string_table.getOffset(name) orelse return null; return wasm.globals.get(offset); } From 370401cf60f70d6b3a2b3f40253f601b0cb8589d Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 11 Apr 2023 19:33:19 +0200 Subject: [PATCH 2/4] wasm: generate unnamed constant for tag --- src/arch/wasm/CodeGen.zig | 135 ++++++++++++++++++-------------------- src/link/Wasm.zig | 56 +++++++++++++++- src/link/Wasm/Atom.zig | 2 +- 3 files changed, 121 insertions(+), 72 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 3282d1304a..4a61f2d846 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -6400,30 +6400,23 @@ fn callIntrinsic( fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const un_op = func.air.instructions.items(.data)[inst].un_op; if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op}); - // const operand = try func.resolveInst(un_op); + const operand = try func.resolveInst(un_op); const enum_ty = func.air.typeOf(un_op); - _ = try func.getTagNameFunction(enum_ty); + const func_sym_index = try func.getTagNameFunction(enum_ty); - func.finishAir(inst, .none, &.{un_op}); + const result_ptr = try func.allocStack(func.air.typeOfIndex(inst)); + try func.lowerToStack(result_ptr); + try func.emitWValue(operand); + try func.addLabel(.call, func_sym_index); + + return func.finishAir(inst, result_ptr, &.{un_op}); } fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { const enum_decl_index = enum_ty.getOwnerDecl(); const module = func.bin_file.base.options.module.?; - // check if we already generated code for this. - if (func.bin_file.decls.get(enum_decl_index)) |decl_atom_index| { - std.debug.print("Found atom index for Enum decl! {d}\n", .{decl_atom_index}); - const atom = func.bin_file.getAtom(decl_atom_index); - return atom.getSymbolIndex().?; - } - - // Create an atom in which we will store all tag names. - const func_atom_index = try func.bin_file.getOrCreateAtomForDecl(enum_decl_index); - const func_atom = func.bin_file.getAtomPtr(func_atom_index); - std.debug.print("Generated a new atom! {d}\n", .{func_atom_index}); - var arena_allocator = std.heap.ArenaAllocator.init(func.gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); @@ -6432,11 +6425,11 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { defer module.gpa.free(fqn); const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{fqn}); + // check if we already generated code for this. if (func.bin_file.findGlobalSymbol(func_name)) |loc| { return loc.index; } - const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); var int_tag_type_buffer: Type.Payload.Bits = undefined; const int_tag_ty = enum_ty.intTagType(&int_tag_type_buffer); @@ -6444,118 +6437,120 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { return func.fail("TODO: Implement @tagName for enums with tag size larger than 64 bits", .{}); } - var func_type = try genFunctype(func.gpa, .Unspecified, &.{int_tag_ty}, slice_ty, func.target); - defer func_type.deinit(func.gpa); - try func.bin_file.storeDeclType(enum_decl_index, func_type); + var relocs = std.ArrayList(link.File.Wasm.Relocation).init(func.gpa); + defer relocs.deinit(); - var body_list = std.ArrayList(u8).init(arena); + var body_list = std.ArrayList(u8).init(func.gpa); + defer body_list.deinit(); var writer = body_list.writer(); - // The locals of the function body (always 2) - try leb.writeULEB128(writer, @as(u32, 2)); - try leb.writeULEB128(writer, @as(u32, 1)); - try writer.writeByte(func.genValtype(slice_ty, func.target)); - try leb.writeULEB128(writer, @as(u32, 1)); - try writer.writeByte(func.genValtype(int_tag_ty, func.target)); + // The locals of the function body (always 0) + try leb.writeULEB128(writer, @as(u32, 0)); // outer block try writer.writeByte(std.wasm.opcode(.block)); + try writer.writeByte(std.wasm.block_empty); // TODO: Make switch implementation generic so we can use a jump table for this when the tags are not sparse. // generate an if-else chain for each tag value as well as constant. for (enum_ty.enumFields().keys(), 0..) |tag_name, field_index| { - // for each tag name, create an atom to store its name into, + // for each tag name, create an unnamed const, // and then get a pointer to its value. - const tag_atom_index = try func.bin_file.createAtom(); - const tag_atom = func.bin_file.getAtomPtr(tag_atom_index); - tag_atom.alignment = 1; - try func.bin_file.parseAtom(tag_atom_index, .read_only); - try tag_atom.code.appendSlice(func.gpa, tag_name); + var name_ty_payload: Type.Payload.Len = .{ + .base = .{ .tag = .array_u8 }, + .data = @intCast(u64, tag_name.len), + }; + const name_ty = Type.initPayload(&name_ty_payload.base); + var name_val_payload: Value.Payload.Bytes = .{ + .base = .{ .tag = .bytes }, + .data = tag_name, + }; + const name_val = Value.initPayload(&name_val_payload.base); + const tag_sym_index = try func.bin_file.lowerUnnamedConst( + .{ .ty = name_ty, .val = name_val }, + enum_decl_index, + ); // block for this if case try writer.writeByte(std.wasm.opcode(.block)); + try writer.writeByte(std.wasm.block_empty); // get actual tag value (stored in 2nd parameter); try writer.writeByte(std.wasm.opcode(.local_get)); try leb.writeULEB128(writer, @as(u32, 1)); - const tag_value = int: { - var tag_val_payload: Value.Payload.U32 = .{ - .base = .{ .tag = .enum_field_index }, - .data = @intCast(u32, field_index), - }; - break :int try func.lowerConstant(Value.initPayload(&tag_val_payload.base), enum_ty); + var tag_val_payload: Value.Payload.U32 = .{ + .base = .{ .tag = .enum_field_index }, + .data = @intCast(u32, field_index), }; + const tag_value = try func.lowerConstant(Value.initPayload(&tag_val_payload.base), enum_ty); switch (tag_value) { .imm32 => |value| { try writer.writeByte(std.wasm.opcode(.i32_const)); try leb.writeULEB128(writer, value); - try writer.writeByte(std.wasm.opcode(.i32_neq)); + try writer.writeByte(std.wasm.opcode(.i32_ne)); }, .imm64 => |value| { try writer.writeByte(std.wasm.opcode(.i64_const)); try leb.writeULEB128(writer, value); - try writer.writeByte(std.wasm.opcode(.i64_neq)); + try writer.writeByte(std.wasm.opcode(.i64_ne)); }, else => unreachable, } // if they're not equal, break out of current branch try writer.writeByte(std.wasm.opcode(.br_if)); + try leb.writeULEB128(writer, @as(u32, 0)); // store the address of the tagname in the pointer field of the slice + // get the address twice so we can also store the length. + try writer.writeByte(std.wasm.opcode(.local_get)); + try leb.writeULEB128(writer, @as(u32, 0)); try writer.writeByte(std.wasm.opcode(.local_get)); try leb.writeULEB128(writer, @as(u32, 0)); - const is_wasm32 = func.arch() == .wasm32; // get address of tagname and emit a relocation to it - { - if (is_wasm32) { - try writer.writeByte(std.wasm.opcode(.i32_const)); - var buf: [5]u8 = undefined; - leb.writeUnsignedFixed(5, &buf, mem.pointer); - try writer.writeAll(&buf); - } else { - try writer.writeByte(std.wasm.opcode(.i64_const)); - var buf: [10]u8 = undefined; - leb.writeUnsignedFixed(10, &buf, mem.pointer); - try writer.writeAll(&buf); - } - try func_atom.relocs.append(func.gpa, .{ - .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64, + if (func.arch() == .wasm32) { + const encoded_alignment = @ctz(@as(u32, 4)); + try writer.writeByte(std.wasm.opcode(.i32_const)); + try relocs.append(.{ + .relocation_type = .R_WASM_MEMORY_ADDR_LEB, .offset = @intCast(u32, body_list.items.len), - .index = tag_atom.getSymbolIndex().?, + .index = tag_sym_index, }); - } + try writer.writeAll(&[_]u8{0} ** 5); // will be relocated - // call the actual store instructions - if (is_wasm32) { // store pointer try writer.writeByte(std.wasm.opcode(.i32_store)); - try leb.writeULEB128(writer, @as(u32, 4)); + try leb.writeULEB128(writer, encoded_alignment); try leb.writeULEB128(writer, @as(u32, 0)); // store length - try writer.writeByte(std.wasm.opcode(.local_get)); - try leb.writeULEB128(writer, @as(u32, 0)); try writer.writeByte(std.wasm.opcode(.i32_const)); try leb.writeULEB128(writer, @intCast(u32, tag_name.len)); try writer.writeByte(std.wasm.opcode(.i32_store)); - try leb.writeULEB128(writer, @as(u32, 4)); + try leb.writeULEB128(writer, encoded_alignment); try leb.writeULEB128(writer, @as(u32, 4)); } else { + const encoded_alignment = @ctz(@as(u32, 8)); + try writer.writeByte(std.wasm.opcode(.i64_const)); + try relocs.append(.{ + .relocation_type = .R_WASM_MEMORY_ADDR_LEB64, + .offset = @intCast(u32, body_list.items.len), + .index = tag_sym_index, + }); + try writer.writeAll(&[_]u8{0} ** 10); // will be relocated + // store pointer try writer.writeByte(std.wasm.opcode(.i64_store)); - try leb.writeULEB128(writer, @as(u32, 8)); + try leb.writeULEB128(writer, encoded_alignment); try leb.writeULEB128(writer, @as(u32, 0)); // store length - try writer.writeByte(std.wasm.opcode(.local_get)); - try leb.writeULEB128(writer, @as(u32, 0)); try writer.writeByte(std.wasm.opcode(.i64_const)); try leb.writeULEB128(writer, @intCast(u64, tag_name.len)); try writer.writeByte(std.wasm.opcode(.i64_store)); - try leb.writeULEB128(writer, @as(u32, 8)); + try leb.writeULEB128(writer, encoded_alignment); try leb.writeULEB128(writer, @as(u32, 8)); } @@ -6573,7 +6568,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { // finish function body try writer.writeByte(std.wasm.opcode(.end)); - try func_atom.code.appendSlice(func.gpa, body_list.items); - - return func_atom.sym_index; + const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); + const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty}, slice_ty, func.target); + return func.bin_file.createFunction(func_name, func_type, &body_list, &relocs); } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 639b7f0776..2125a8faaa 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -30,6 +30,7 @@ const Symbol = @import("Wasm/Symbol.zig"); const Object = @import("Wasm/Object.zig"); const Archive = @import("Wasm/Archive.zig"); const types = @import("Wasm/types.zig"); +pub const Relocation = types.Relocation; pub const base_tag: link.File.Tag = .wasm; @@ -178,6 +179,10 @@ debug_str_atom: ?Atom.Index = null, debug_pubnames_atom: ?Atom.Index = null, debug_pubtypes_atom: ?Atom.Index = null, +/// List of atom indexes of functions that are generated by the backend, +/// rather than by the linker. +synthetic_functions: std.ArrayListUnmanaged(Atom.Index) = .{}, + pub const Segment = struct { alignment: u32, size: u32, @@ -577,7 +582,7 @@ pub fn getOrCreateAtomForDecl(wasm: *Wasm, decl_index: Module.Decl.Index) !Atom. } /// Creates a new empty `Atom` and returns its `Atom.Index` -pub fn createAtom(wasm: *Wasm) !Atom.Index { +fn createAtom(wasm: *Wasm) !Atom.Index { const index = @intCast(Atom.Index, wasm.managed_atoms.items.len); const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); atom.* = Atom.empty; @@ -1287,6 +1292,7 @@ pub fn deinit(wasm: *Wasm) void { wasm.exports.deinit(gpa); wasm.string_table.deinit(gpa); + wasm.synthetic_functions.deinit(gpa); if (wasm.dwarf) |*dwarf| { dwarf.deinit(); @@ -2272,6 +2278,49 @@ fn createSyntheticFunction( atom.offset = prev_atom.offset + prev_atom.size; } +/// Unlike `createSyntheticFunction` this function is to be called by +/// the codegeneration backend. This will not allocate the created Atom yet, +/// but will instead be appended to `synthetic_functions` list and will be +/// parsed at the end of code generation. +/// Returns the index of the symbol. +pub fn createFunction( + wasm: *Wasm, + symbol_name: []const u8, + func_ty: std.wasm.Type, + function_body: *std.ArrayList(u8), + relocations: *std.ArrayList(Relocation), +) !u32 { + const loc = try wasm.createSyntheticSymbol(symbol_name, .function); + + const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); + const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); + atom.* = .{ + .size = @intCast(u32, function_body.items.len), + .offset = 0, + .sym_index = loc.index, + .file = null, + .alignment = 1, + .next = null, + .prev = null, + .code = function_body.moveToUnmanaged(), + .relocs = relocations.moveToUnmanaged(), + }; + const symbol = loc.getSymbol(wasm); + symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); // ensure function does not get exported + + const section_index = wasm.code_section_index orelse idx: { + const index = @intCast(u32, wasm.segments.items.len); + try wasm.appendDummySegment(); + break :idx index; + }; + try wasm.appendAtomAtIndex(section_index, atom_index); + try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); + try wasm.atom_types.put(wasm.base.allocator, atom_index, try wasm.putOrGetFuncType(func_ty)); + try wasm.synthetic_functions.append(wasm.base.allocator, atom_index); + + return loc.index; +} + fn initializeTLSFunction(wasm: *Wasm) !void { if (!wasm.base.options.shared_memory) return; @@ -3306,6 +3355,11 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } } + // also parse any backend-generated functions + for (wasm.synthetic_functions.items) |atom_index| { + try wasm.parseAtom(atom_index, .function); + } + if (wasm.dwarf) |*dwarf| { try dwarf.flushModule(wasm.base.options.module.?); } diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index 7d2f5a6696..f8092c6db1 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -43,7 +43,7 @@ pub const Index = u32; /// Represents a default empty wasm `Atom` pub const empty: Atom = .{ - .alignment = 0, + .alignment = 1, .file = null, .next = null, .offset = 0, From 5c451b6e63e52c82bde247b0b5db658409c6db30 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 12 Apr 2023 06:37:55 +0200 Subject: [PATCH 3/4] wasm: make tagName null-terminated --- src/arch/wasm/CodeGen.zig | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 4a61f2d846..83af640b82 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -6457,13 +6457,28 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { // for each tag name, create an unnamed const, // and then get a pointer to its value. var name_ty_payload: Type.Payload.Len = .{ - .base = .{ .tag = .array_u8 }, + .base = .{ .tag = .array_u8_sentinel_0 }, .data = @intCast(u64, tag_name.len), }; const name_ty = Type.initPayload(&name_ty_payload.base); - var name_val_payload: Value.Payload.Bytes = .{ - .base = .{ .tag = .bytes }, - .data = tag_name, + const string_bytes = &module.string_literal_bytes; + try string_bytes.ensureUnusedCapacity(module.gpa, tag_name.len); + const gop = try module.string_literal_table.getOrPutContextAdapted(module.gpa, tag_name, Module.StringLiteralAdapter{ + .bytes = string_bytes, + }, Module.StringLiteralContext{ + .bytes = string_bytes, + }); + if (!gop.found_existing) { + gop.key_ptr.* = .{ + .index = @intCast(u32, string_bytes.items.len), + .len = @intCast(u32, tag_name.len), + }; + string_bytes.appendSliceAssumeCapacity(tag_name); + gop.value_ptr.* = .none; + } + var name_val_payload: Value.Payload.StrLit = .{ + .base = .{ .tag = .str_lit }, + .data = gop.key_ptr.*, }; const name_val = Value.initPayload(&name_val_payload.base); const tag_sym_index = try func.bin_file.lowerUnnamedConst( From abc1e52e0885c8e46d743d5e48ba260b35ae2769 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 12 Apr 2023 06:41:18 +0200 Subject: [PATCH 4/4] wasm: enable `@tagName` behavior tests --- test/behavior/enum.zig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 9076f9f9ac..fb14265361 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -979,7 +979,6 @@ fn test3_2(f: Test3Foo) !void { } test "@tagName" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -996,7 +995,6 @@ fn testEnumTagNameBare(n: anytype) []const u8 { const BareNumber = enum { One, Two, Three }; test "@tagName non-exhaustive enum" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -1008,7 +1006,6 @@ test "@tagName non-exhaustive enum" { const NonExhaustive = enum(u8) { A, B, _ }; test "@tagName is null-terminated" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -1024,7 +1021,6 @@ test "@tagName is null-terminated" { } test "tag name with assigned enum values" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;