authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-04-11 19:33:19+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-04-12 22:23:36+02:00
log370401cf60f70d6b3a2b3f40253f601b0cb8589d
treeaeeb0914bb21db5c5aba505bc78ba6aeddd48a9e
parent3c27df6b135f998facce1aa09a9926e840671a05
signaturelock-open Commit is signed but in an unrecognized format.

wasm: generate unnamed constant for tag


3 files changed, 121 insertions(+), 72 deletions(-)

src/arch/wasm/CodeGen.zig+65-70
......@@ -6400,30 +6400,23 @@ fn callIntrinsic(
64006400fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
64016401 const un_op = func.air.instructions.items(.data)[inst].un_op;
64026402 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});
6403 // const operand = try func.resolveInst(un_op);
6403 const operand = try func.resolveInst(un_op);
64046404 const enum_ty = func.air.typeOf(un_op);
64056405
6406 _ = try func.getTagNameFunction(enum_ty);
6406 const func_sym_index = try func.getTagNameFunction(enum_ty);
64076407
6408 func.finishAir(inst, .none, &.{un_op});
6408 const result_ptr = try func.allocStack(func.air.typeOfIndex(inst));
6409 try func.lowerToStack(result_ptr);
6410 try func.emitWValue(operand);
6411 try func.addLabel(.call, func_sym_index);
6412
6413 return func.finishAir(inst, result_ptr, &.{un_op});
64096414}
64106415
64116416fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
64126417 const enum_decl_index = enum_ty.getOwnerDecl();
64136418 const module = func.bin_file.base.options.module.?;
64146419
6415 // check if we already generated code for this.
6416 if (func.bin_file.decls.get(enum_decl_index)) |decl_atom_index| {
6417 std.debug.print("Found atom index for Enum decl! {d}\n", .{decl_atom_index});
6418 const atom = func.bin_file.getAtom(decl_atom_index);
6419 return atom.getSymbolIndex().?;
6420 }
6421
6422 // Create an atom in which we will store all tag names.
6423 const func_atom_index = try func.bin_file.getOrCreateAtomForDecl(enum_decl_index);
6424 const func_atom = func.bin_file.getAtomPtr(func_atom_index);
6425 std.debug.print("Generated a new atom! {d}\n", .{func_atom_index});
6426
64276420 var arena_allocator = std.heap.ArenaAllocator.init(func.gpa);
64286421 defer arena_allocator.deinit();
64296422 const arena = arena_allocator.allocator();
......@@ -6432,11 +6425,11 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
64326425 defer module.gpa.free(fqn);
64336426 const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{fqn});
64346427
6428 // check if we already generated code for this.
64356429 if (func.bin_file.findGlobalSymbol(func_name)) |loc| {
64366430 return loc.index;
64376431 }
64386432
6439 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
64406433 var int_tag_type_buffer: Type.Payload.Bits = undefined;
64416434 const int_tag_ty = enum_ty.intTagType(&int_tag_type_buffer);
64426435
......@@ -6444,118 +6437,120 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
64446437 return func.fail("TODO: Implement @tagName for enums with tag size larger than 64 bits", .{});
64456438 }
64466439
6447 var func_type = try genFunctype(func.gpa, .Unspecified, &.{int_tag_ty}, slice_ty, func.target);
6448 defer func_type.deinit(func.gpa);
6449 try func.bin_file.storeDeclType(enum_decl_index, func_type);
6440 var relocs = std.ArrayList(link.File.Wasm.Relocation).init(func.gpa);
6441 defer relocs.deinit();
64506442
6451 var body_list = std.ArrayList(u8).init(arena);
6443 var body_list = std.ArrayList(u8).init(func.gpa);
6444 defer body_list.deinit();
64526445 var writer = body_list.writer();
64536446
6454 // The locals of the function body (always 2)
6455 try leb.writeULEB128(writer, @as(u32, 2));
6456 try leb.writeULEB128(writer, @as(u32, 1));
6457 try writer.writeByte(func.genValtype(slice_ty, func.target));
6458 try leb.writeULEB128(writer, @as(u32, 1));
6459 try writer.writeByte(func.genValtype(int_tag_ty, func.target));
6447 // The locals of the function body (always 0)
6448 try leb.writeULEB128(writer, @as(u32, 0));
64606449
64616450 // outer block
64626451 try writer.writeByte(std.wasm.opcode(.block));
6452 try writer.writeByte(std.wasm.block_empty);
64636453
64646454 // TODO: Make switch implementation generic so we can use a jump table for this when the tags are not sparse.
64656455 // generate an if-else chain for each tag value as well as constant.
64666456 for (enum_ty.enumFields().keys(), 0..) |tag_name, field_index| {
6467 // for each tag name, create an atom to store its name into,
6457 // for each tag name, create an unnamed const,
64686458 // and then get a pointer to its value.
6469 const tag_atom_index = try func.bin_file.createAtom();
6470 const tag_atom = func.bin_file.getAtomPtr(tag_atom_index);
6471 tag_atom.alignment = 1;
6472 try func.bin_file.parseAtom(tag_atom_index, .read_only);
6473 try tag_atom.code.appendSlice(func.gpa, tag_name);
6459 var name_ty_payload: Type.Payload.Len = .{
6460 .base = .{ .tag = .array_u8 },
6461 .data = @intCast(u64, tag_name.len),
6462 };
6463 const name_ty = Type.initPayload(&name_ty_payload.base);
6464 var name_val_payload: Value.Payload.Bytes = .{
6465 .base = .{ .tag = .bytes },
6466 .data = tag_name,
6467 };
6468 const name_val = Value.initPayload(&name_val_payload.base);
6469 const tag_sym_index = try func.bin_file.lowerUnnamedConst(
6470 .{ .ty = name_ty, .val = name_val },
6471 enum_decl_index,
6472 );
64746473
64756474 // block for this if case
64766475 try writer.writeByte(std.wasm.opcode(.block));
6476 try writer.writeByte(std.wasm.block_empty);
64776477
64786478 // get actual tag value (stored in 2nd parameter);
64796479 try writer.writeByte(std.wasm.opcode(.local_get));
64806480 try leb.writeULEB128(writer, @as(u32, 1));
64816481
6482 const tag_value = int: {
6483 var tag_val_payload: Value.Payload.U32 = .{
6484 .base = .{ .tag = .enum_field_index },
6485 .data = @intCast(u32, field_index),
6486 };
6487 break :int try func.lowerConstant(Value.initPayload(&tag_val_payload.base), enum_ty);
6482 var tag_val_payload: Value.Payload.U32 = .{
6483 .base = .{ .tag = .enum_field_index },
6484 .data = @intCast(u32, field_index),
64886485 };
6486 const tag_value = try func.lowerConstant(Value.initPayload(&tag_val_payload.base), enum_ty);
64896487
64906488 switch (tag_value) {
64916489 .imm32 => |value| {
64926490 try writer.writeByte(std.wasm.opcode(.i32_const));
64936491 try leb.writeULEB128(writer, value);
6494 try writer.writeByte(std.wasm.opcode(.i32_neq));
6492 try writer.writeByte(std.wasm.opcode(.i32_ne));
64956493 },
64966494 .imm64 => |value| {
64976495 try writer.writeByte(std.wasm.opcode(.i64_const));
64986496 try leb.writeULEB128(writer, value);
6499 try writer.writeByte(std.wasm.opcode(.i64_neq));
6497 try writer.writeByte(std.wasm.opcode(.i64_ne));
65006498 },
65016499 else => unreachable,
65026500 }
65036501 // if they're not equal, break out of current branch
65046502 try writer.writeByte(std.wasm.opcode(.br_if));
6503 try leb.writeULEB128(writer, @as(u32, 0));
65056504
65066505 // store the address of the tagname in the pointer field of the slice
6506 // get the address twice so we can also store the length.
6507 try writer.writeByte(std.wasm.opcode(.local_get));
6508 try leb.writeULEB128(writer, @as(u32, 0));
65076509 try writer.writeByte(std.wasm.opcode(.local_get));
65086510 try leb.writeULEB128(writer, @as(u32, 0));
65096511
6510 const is_wasm32 = func.arch() == .wasm32;
65116512 // get address of tagname and emit a relocation to it
6512 {
6513 if (is_wasm32) {
6514 try writer.writeByte(std.wasm.opcode(.i32_const));
6515 var buf: [5]u8 = undefined;
6516 leb.writeUnsignedFixed(5, &buf, mem.pointer);
6517 try writer.writeAll(&buf);
6518 } else {
6519 try writer.writeByte(std.wasm.opcode(.i64_const));
6520 var buf: [10]u8 = undefined;
6521 leb.writeUnsignedFixed(10, &buf, mem.pointer);
6522 try writer.writeAll(&buf);
6523 }
6524 try func_atom.relocs.append(func.gpa, .{
6525 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,
6513 if (func.arch() == .wasm32) {
6514 const encoded_alignment = @ctz(@as(u32, 4));
6515 try writer.writeByte(std.wasm.opcode(.i32_const));
6516 try relocs.append(.{
6517 .relocation_type = .R_WASM_MEMORY_ADDR_LEB,
65266518 .offset = @intCast(u32, body_list.items.len),
6527 .index = tag_atom.getSymbolIndex().?,
6519 .index = tag_sym_index,
65286520 });
6529 }
6521 try writer.writeAll(&[_]u8{0} ** 5); // will be relocated
65306522
6531 // call the actual store instructions
6532 if (is_wasm32) {
65336523 // store pointer
65346524 try writer.writeByte(std.wasm.opcode(.i32_store));
6535 try leb.writeULEB128(writer, @as(u32, 4));
6525 try leb.writeULEB128(writer, encoded_alignment);
65366526 try leb.writeULEB128(writer, @as(u32, 0));
65376527
65386528 // store length
6539 try writer.writeByte(std.wasm.opcode(.local_get));
6540 try leb.writeULEB128(writer, @as(u32, 0));
65416529 try writer.writeByte(std.wasm.opcode(.i32_const));
65426530 try leb.writeULEB128(writer, @intCast(u32, tag_name.len));
65436531 try writer.writeByte(std.wasm.opcode(.i32_store));
6544 try leb.writeULEB128(writer, @as(u32, 4));
6532 try leb.writeULEB128(writer, encoded_alignment);
65456533 try leb.writeULEB128(writer, @as(u32, 4));
65466534 } else {
6535 const encoded_alignment = @ctz(@as(u32, 8));
6536 try writer.writeByte(std.wasm.opcode(.i64_const));
6537 try relocs.append(.{
6538 .relocation_type = .R_WASM_MEMORY_ADDR_LEB64,
6539 .offset = @intCast(u32, body_list.items.len),
6540 .index = tag_sym_index,
6541 });
6542 try writer.writeAll(&[_]u8{0} ** 10); // will be relocated
6543
65476544 // store pointer
65486545 try writer.writeByte(std.wasm.opcode(.i64_store));
6549 try leb.writeULEB128(writer, @as(u32, 8));
6546 try leb.writeULEB128(writer, encoded_alignment);
65506547 try leb.writeULEB128(writer, @as(u32, 0));
65516548
65526549 // store length
6553 try writer.writeByte(std.wasm.opcode(.local_get));
6554 try leb.writeULEB128(writer, @as(u32, 0));
65556550 try writer.writeByte(std.wasm.opcode(.i64_const));
65566551 try leb.writeULEB128(writer, @intCast(u64, tag_name.len));
65576552 try writer.writeByte(std.wasm.opcode(.i64_store));
6558 try leb.writeULEB128(writer, @as(u32, 8));
6553 try leb.writeULEB128(writer, encoded_alignment);
65596554 try leb.writeULEB128(writer, @as(u32, 8));
65606555 }
65616556
......@@ -6573,7 +6568,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
65736568 // finish function body
65746569 try writer.writeByte(std.wasm.opcode(.end));
65756570
6576 try func_atom.code.appendSlice(func.gpa, body_list.items);
6577
6578 return func_atom.sym_index;
6571 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
6572 const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty}, slice_ty, func.target);
6573 return func.bin_file.createFunction(func_name, func_type, &body_list, &relocs);
65796574}
src/link/Wasm.zig+55-1
......@@ -30,6 +30,7 @@ const Symbol = @import("Wasm/Symbol.zig");
3030const Object = @import("Wasm/Object.zig");
3131const Archive = @import("Wasm/Archive.zig");
3232const types = @import("Wasm/types.zig");
33pub const Relocation = types.Relocation;
3334
3435pub const base_tag: link.File.Tag = .wasm;
3536
......@@ -178,6 +179,10 @@ debug_str_atom: ?Atom.Index = null,
178179debug_pubnames_atom: ?Atom.Index = null,
179180debug_pubtypes_atom: ?Atom.Index = null,
180181
182/// List of atom indexes of functions that are generated by the backend,
183/// rather than by the linker.
184synthetic_functions: std.ArrayListUnmanaged(Atom.Index) = .{},
185
181186pub const Segment = struct {
182187 alignment: u32,
183188 size: u32,
......@@ -577,7 +582,7 @@ pub fn getOrCreateAtomForDecl(wasm: *Wasm, decl_index: Module.Decl.Index) !Atom.
577582}
578583
579584/// Creates a new empty `Atom` and returns its `Atom.Index`
580pub fn createAtom(wasm: *Wasm) !Atom.Index {
585fn createAtom(wasm: *Wasm) !Atom.Index {
581586 const index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
582587 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
583588 atom.* = Atom.empty;
......@@ -1287,6 +1292,7 @@ pub fn deinit(wasm: *Wasm) void {
12871292 wasm.exports.deinit(gpa);
12881293
12891294 wasm.string_table.deinit(gpa);
1295 wasm.synthetic_functions.deinit(gpa);
12901296
12911297 if (wasm.dwarf) |*dwarf| {
12921298 dwarf.deinit();
......@@ -2272,6 +2278,49 @@ fn createSyntheticFunction(
22722278 atom.offset = prev_atom.offset + prev_atom.size;
22732279}
22742280
2281/// Unlike `createSyntheticFunction` this function is to be called by
2282/// the codegeneration backend. This will not allocate the created Atom yet,
2283/// but will instead be appended to `synthetic_functions` list and will be
2284/// parsed at the end of code generation.
2285/// Returns the index of the symbol.
2286pub fn createFunction(
2287 wasm: *Wasm,
2288 symbol_name: []const u8,
2289 func_ty: std.wasm.Type,
2290 function_body: *std.ArrayList(u8),
2291 relocations: *std.ArrayList(Relocation),
2292) !u32 {
2293 const loc = try wasm.createSyntheticSymbol(symbol_name, .function);
2294
2295 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
2296 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
2297 atom.* = .{
2298 .size = @intCast(u32, function_body.items.len),
2299 .offset = 0,
2300 .sym_index = loc.index,
2301 .file = null,
2302 .alignment = 1,
2303 .next = null,
2304 .prev = null,
2305 .code = function_body.moveToUnmanaged(),
2306 .relocs = relocations.moveToUnmanaged(),
2307 };
2308 const symbol = loc.getSymbol(wasm);
2309 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); // ensure function does not get exported
2310
2311 const section_index = wasm.code_section_index orelse idx: {
2312 const index = @intCast(u32, wasm.segments.items.len);
2313 try wasm.appendDummySegment();
2314 break :idx index;
2315 };
2316 try wasm.appendAtomAtIndex(section_index, atom_index);
2317 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
2318 try wasm.atom_types.put(wasm.base.allocator, atom_index, try wasm.putOrGetFuncType(func_ty));
2319 try wasm.synthetic_functions.append(wasm.base.allocator, atom_index);
2320
2321 return loc.index;
2322}
2323
22752324fn initializeTLSFunction(wasm: *Wasm) !void {
22762325 if (!wasm.base.options.shared_memory) return;
22772326
......@@ -3306,6 +3355,11 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
33063355 }
33073356 }
33083357
3358 // also parse any backend-generated functions
3359 for (wasm.synthetic_functions.items) |atom_index| {
3360 try wasm.parseAtom(atom_index, .function);
3361 }
3362
33093363 if (wasm.dwarf) |*dwarf| {
33103364 try dwarf.flushModule(wasm.base.options.module.?);
33113365 }
src/link/Wasm/Atom.zig+1-1
......@@ -43,7 +43,7 @@ pub const Index = u32;
4343
4444/// Represents a default empty wasm `Atom`
4545pub const empty: Atom = .{
46 .alignment = 0,
46 .alignment = 1,
4747 .file = null,
4848 .next = null,
4949 .offset = 0,