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
signature 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(...@@ -6400,30 +6400,23 @@ fn callIntrinsic(
6400fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6400fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6401 const un_op = func.air.instructions.items(.data)[inst].un_op;6401 const un_op = func.air.instructions.items(.data)[inst].un_op;
6402 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{un_op});6402 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);
6404 const enum_ty = func.air.typeOf(un_op);6404 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});
6409}6414}
64106415
6411fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {6416fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
6412 const enum_decl_index = enum_ty.getOwnerDecl();6417 const enum_decl_index = enum_ty.getOwnerDecl();
6413 const module = func.bin_file.base.options.module.?;6418 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
6427 var arena_allocator = std.heap.ArenaAllocator.init(func.gpa);6420 var arena_allocator = std.heap.ArenaAllocator.init(func.gpa);
6428 defer arena_allocator.deinit();6421 defer arena_allocator.deinit();
6429 const arena = arena_allocator.allocator();6422 const arena = arena_allocator.allocator();
...@@ -6432,11 +6425,11 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {...@@ -6432,11 +6425,11 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
6432 defer module.gpa.free(fqn);6425 defer module.gpa.free(fqn);
6433 const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{fqn});6426 const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{fqn});
64346427
6428 // check if we already generated code for this.
6435 if (func.bin_file.findGlobalSymbol(func_name)) |loc| {6429 if (func.bin_file.findGlobalSymbol(func_name)) |loc| {
6436 return loc.index;6430 return loc.index;
6437 }6431 }
64386432
6439 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
6440 var int_tag_type_buffer: Type.Payload.Bits = undefined;6433 var int_tag_type_buffer: Type.Payload.Bits = undefined;
6441 const int_tag_ty = enum_ty.intTagType(&int_tag_type_buffer);6434 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 {...@@ -6444,118 +6437,120 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
6444 return func.fail("TODO: Implement @tagName for enums with tag size larger than 64 bits", .{});6437 return func.fail("TODO: Implement @tagName for enums with tag size larger than 64 bits", .{});
6445 }6438 }
64466439
6447 var func_type = try genFunctype(func.gpa, .Unspecified, &.{int_tag_ty}, slice_ty, func.target);6440 var relocs = std.ArrayList(link.File.Wasm.Relocation).init(func.gpa);
6448 defer func_type.deinit(func.gpa);6441 defer relocs.deinit();
6449 try func.bin_file.storeDeclType(enum_decl_index, func_type);
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();
6452 var writer = body_list.writer();6445 var writer = body_list.writer();
64536446
6454 // The locals of the function body (always 2)6447 // The locals of the function body (always 0)
6455 try leb.writeULEB128(writer, @as(u32, 2));6448 try leb.writeULEB128(writer, @as(u32, 0));
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));
64606449
6461 // outer block6450 // outer block
6462 try writer.writeByte(std.wasm.opcode(.block));6451 try writer.writeByte(std.wasm.opcode(.block));
6452 try writer.writeByte(std.wasm.block_empty);
64636453
6464 // TODO: Make switch implementation generic so we can use a jump table for this when the tags are not sparse.6454 // TODO: Make switch implementation generic so we can use a jump table for this when the tags are not sparse.
6465 // generate an if-else chain for each tag value as well as constant.6455 // generate an if-else chain for each tag value as well as constant.
6466 for (enum_ty.enumFields().keys(), 0..) |tag_name, field_index| {6456 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,
6468 // and then get a pointer to its value.6458 // and then get a pointer to its value.
6469 const tag_atom_index = try func.bin_file.createAtom();6459 var name_ty_payload: Type.Payload.Len = .{
6470 const tag_atom = func.bin_file.getAtomPtr(tag_atom_index);6460 .base = .{ .tag = .array_u8 },
6471 tag_atom.alignment = 1;6461 .data = @intCast(u64, tag_name.len),
6472 try func.bin_file.parseAtom(tag_atom_index, .read_only);6462 };
6473 try tag_atom.code.appendSlice(func.gpa, tag_name);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
6475 // block for this if case6474 // block for this if case
6476 try writer.writeByte(std.wasm.opcode(.block));6475 try writer.writeByte(std.wasm.opcode(.block));
6476 try writer.writeByte(std.wasm.block_empty);
64776477
6478 // get actual tag value (stored in 2nd parameter);6478 // get actual tag value (stored in 2nd parameter);
6479 try writer.writeByte(std.wasm.opcode(.local_get));6479 try writer.writeByte(std.wasm.opcode(.local_get));
6480 try leb.writeULEB128(writer, @as(u32, 1));6480 try leb.writeULEB128(writer, @as(u32, 1));
64816481
6482 const tag_value = int: {6482 var tag_val_payload: Value.Payload.U32 = .{
6483 var tag_val_payload: Value.Payload.U32 = .{6483 .base = .{ .tag = .enum_field_index },
6484 .base = .{ .tag = .enum_field_index },6484 .data = @intCast(u32, field_index),
6485 .data = @intCast(u32, field_index),
6486 };
6487 break :int try func.lowerConstant(Value.initPayload(&tag_val_payload.base), enum_ty);
6488 };6485 };
6486 const tag_value = try func.lowerConstant(Value.initPayload(&tag_val_payload.base), enum_ty);
64896487
6490 switch (tag_value) {6488 switch (tag_value) {
6491 .imm32 => |value| {6489 .imm32 => |value| {
6492 try writer.writeByte(std.wasm.opcode(.i32_const));6490 try writer.writeByte(std.wasm.opcode(.i32_const));
6493 try leb.writeULEB128(writer, value);6491 try leb.writeULEB128(writer, value);
6494 try writer.writeByte(std.wasm.opcode(.i32_neq));6492 try writer.writeByte(std.wasm.opcode(.i32_ne));
6495 },6493 },
6496 .imm64 => |value| {6494 .imm64 => |value| {
6497 try writer.writeByte(std.wasm.opcode(.i64_const));6495 try writer.writeByte(std.wasm.opcode(.i64_const));
6498 try leb.writeULEB128(writer, value);6496 try leb.writeULEB128(writer, value);
6499 try writer.writeByte(std.wasm.opcode(.i64_neq));6497 try writer.writeByte(std.wasm.opcode(.i64_ne));
6500 },6498 },
6501 else => unreachable,6499 else => unreachable,
6502 }6500 }
6503 // if they're not equal, break out of current branch6501 // if they're not equal, break out of current branch
6504 try writer.writeByte(std.wasm.opcode(.br_if));6502 try writer.writeByte(std.wasm.opcode(.br_if));
6503 try leb.writeULEB128(writer, @as(u32, 0));
65056504
6506 // store the address of the tagname in the pointer field of the slice6505 // 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));
6507 try writer.writeByte(std.wasm.opcode(.local_get));6509 try writer.writeByte(std.wasm.opcode(.local_get));
6508 try leb.writeULEB128(writer, @as(u32, 0));6510 try leb.writeULEB128(writer, @as(u32, 0));
65096511
6510 const is_wasm32 = func.arch() == .wasm32;
6511 // get address of tagname and emit a relocation to it6512 // get address of tagname and emit a relocation to it
6512 {6513 if (func.arch() == .wasm32) {
6513 if (is_wasm32) {6514 const encoded_alignment = @ctz(@as(u32, 4));
6514 try writer.writeByte(std.wasm.opcode(.i32_const));6515 try writer.writeByte(std.wasm.opcode(.i32_const));
6515 var buf: [5]u8 = undefined;6516 try relocs.append(.{
6516 leb.writeUnsignedFixed(5, &buf, mem.pointer);6517 .relocation_type = .R_WASM_MEMORY_ADDR_LEB,
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,
6526 .offset = @intCast(u32, body_list.items.len),6518 .offset = @intCast(u32, body_list.items.len),
6527 .index = tag_atom.getSymbolIndex().?,6519 .index = tag_sym_index,
6528 });6520 });
6529 }6521 try writer.writeAll(&[_]u8{0} ** 5); // will be relocated
65306522
6531 // call the actual store instructions
6532 if (is_wasm32) {
6533 // store pointer6523 // store pointer
6534 try writer.writeByte(std.wasm.opcode(.i32_store));6524 try writer.writeByte(std.wasm.opcode(.i32_store));
6535 try leb.writeULEB128(writer, @as(u32, 4));6525 try leb.writeULEB128(writer, encoded_alignment);
6536 try leb.writeULEB128(writer, @as(u32, 0));6526 try leb.writeULEB128(writer, @as(u32, 0));
65376527
6538 // store length6528 // store length
6539 try writer.writeByte(std.wasm.opcode(.local_get));
6540 try leb.writeULEB128(writer, @as(u32, 0));
6541 try writer.writeByte(std.wasm.opcode(.i32_const));6529 try writer.writeByte(std.wasm.opcode(.i32_const));
6542 try leb.writeULEB128(writer, @intCast(u32, tag_name.len));6530 try leb.writeULEB128(writer, @intCast(u32, tag_name.len));
6543 try writer.writeByte(std.wasm.opcode(.i32_store));6531 try writer.writeByte(std.wasm.opcode(.i32_store));
6544 try leb.writeULEB128(writer, @as(u32, 4));6532 try leb.writeULEB128(writer, encoded_alignment);
6545 try leb.writeULEB128(writer, @as(u32, 4));6533 try leb.writeULEB128(writer, @as(u32, 4));
6546 } else {6534 } 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
6547 // store pointer6544 // store pointer
6548 try writer.writeByte(std.wasm.opcode(.i64_store));6545 try writer.writeByte(std.wasm.opcode(.i64_store));
6549 try leb.writeULEB128(writer, @as(u32, 8));6546 try leb.writeULEB128(writer, encoded_alignment);
6550 try leb.writeULEB128(writer, @as(u32, 0));6547 try leb.writeULEB128(writer, @as(u32, 0));
65516548
6552 // store length6549 // store length
6553 try writer.writeByte(std.wasm.opcode(.local_get));
6554 try leb.writeULEB128(writer, @as(u32, 0));
6555 try writer.writeByte(std.wasm.opcode(.i64_const));6550 try writer.writeByte(std.wasm.opcode(.i64_const));
6556 try leb.writeULEB128(writer, @intCast(u64, tag_name.len));6551 try leb.writeULEB128(writer, @intCast(u64, tag_name.len));
6557 try writer.writeByte(std.wasm.opcode(.i64_store));6552 try writer.writeByte(std.wasm.opcode(.i64_store));
6558 try leb.writeULEB128(writer, @as(u32, 8));6553 try leb.writeULEB128(writer, encoded_alignment);
6559 try leb.writeULEB128(writer, @as(u32, 8));6554 try leb.writeULEB128(writer, @as(u32, 8));
6560 }6555 }
65616556
...@@ -6573,7 +6568,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {...@@ -6573,7 +6568,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
6573 // finish function body6568 // finish function body
6574 try writer.writeByte(std.wasm.opcode(.end));6569 try writer.writeByte(std.wasm.opcode(.end));
65756570
6576 try func_atom.code.appendSlice(func.gpa, body_list.items);6571 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
65776572 const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty}, slice_ty, func.target);
6578 return func_atom.sym_index;6573 return func.bin_file.createFunction(func_name, func_type, &body_list, &relocs);
6579}6574}
src/link/Wasm.zig+55-1
...@@ -30,6 +30,7 @@ const Symbol = @import("Wasm/Symbol.zig");...@@ -30,6 +30,7 @@ const Symbol = @import("Wasm/Symbol.zig");
30const Object = @import("Wasm/Object.zig");30const Object = @import("Wasm/Object.zig");
31const Archive = @import("Wasm/Archive.zig");31const Archive = @import("Wasm/Archive.zig");
32const types = @import("Wasm/types.zig");32const types = @import("Wasm/types.zig");
33pub const Relocation = types.Relocation;
3334
34pub const base_tag: link.File.Tag = .wasm;35pub const base_tag: link.File.Tag = .wasm;
3536
...@@ -178,6 +179,10 @@ debug_str_atom: ?Atom.Index = null,...@@ -178,6 +179,10 @@ debug_str_atom: ?Atom.Index = null,
178debug_pubnames_atom: ?Atom.Index = null,179debug_pubnames_atom: ?Atom.Index = null,
179debug_pubtypes_atom: ?Atom.Index = null,180debug_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
181pub const Segment = struct {186pub const Segment = struct {
182 alignment: u32,187 alignment: u32,
183 size: u32,188 size: u32,
...@@ -577,7 +582,7 @@ pub fn getOrCreateAtomForDecl(wasm: *Wasm, decl_index: Module.Decl.Index) !Atom....@@ -577,7 +582,7 @@ pub fn getOrCreateAtomForDecl(wasm: *Wasm, decl_index: Module.Decl.Index) !Atom.
577}582}
578583
579/// Creates a new empty `Atom` and returns its `Atom.Index`584/// Creates a new empty `Atom` and returns its `Atom.Index`
580pub fn createAtom(wasm: *Wasm) !Atom.Index {585fn createAtom(wasm: *Wasm) !Atom.Index {
581 const index = @intCast(Atom.Index, wasm.managed_atoms.items.len);586 const index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
582 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);587 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
583 atom.* = Atom.empty;588 atom.* = Atom.empty;
...@@ -1287,6 +1292,7 @@ pub fn deinit(wasm: *Wasm) void {...@@ -1287,6 +1292,7 @@ pub fn deinit(wasm: *Wasm) void {
1287 wasm.exports.deinit(gpa);1292 wasm.exports.deinit(gpa);
12881293
1289 wasm.string_table.deinit(gpa);1294 wasm.string_table.deinit(gpa);
1295 wasm.synthetic_functions.deinit(gpa);
12901296
1291 if (wasm.dwarf) |*dwarf| {1297 if (wasm.dwarf) |*dwarf| {
1292 dwarf.deinit();1298 dwarf.deinit();
...@@ -2272,6 +2278,49 @@ fn createSyntheticFunction(...@@ -2272,6 +2278,49 @@ fn createSyntheticFunction(
2272 atom.offset = prev_atom.offset + prev_atom.size;2278 atom.offset = prev_atom.offset + prev_atom.size;
2273}2279}
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
2275fn initializeTLSFunction(wasm: *Wasm) !void {2324fn initializeTLSFunction(wasm: *Wasm) !void {
2276 if (!wasm.base.options.shared_memory) return;2325 if (!wasm.base.options.shared_memory) return;
22772326
...@@ -3306,6 +3355,11 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -3306,6 +3355,11 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
3306 }3355 }
3307 }3356 }
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
3309 if (wasm.dwarf) |*dwarf| {3363 if (wasm.dwarf) |*dwarf| {
3310 try dwarf.flushModule(wasm.base.options.module.?);3364 try dwarf.flushModule(wasm.base.options.module.?);
3311 }3365 }
src/link/Wasm/Atom.zig+1-1
...@@ -43,7 +43,7 @@ pub const Index = u32;...@@ -43,7 +43,7 @@ pub const Index = u32;
4343
44/// Represents a default empty wasm `Atom`44/// Represents a default empty wasm `Atom`
45pub const empty: Atom = .{45pub const empty: Atom = .{
46 .alignment = 0,46 .alignment = 1,
47 .file = null,47 .file = null,
48 .next = null,48 .next = null,
49 .offset = 0,49 .offset = 0,