| ... | ... | @@ -1944,6 +1944,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1944 | 1944 | .memcpy => func.airMemcpy(inst), |
| 1945 | 1945 | |
| 1946 | 1946 | .ret_addr => func.airRetAddr(inst), |
| 1947 | .tag_name => func.airTagName(inst), |
| 1947 | 1948 | |
| 1948 | 1949 | .mul_sat, |
| 1949 | 1950 | .mod, |
| ... | ... | @@ -1962,7 +1963,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1962 | 1963 | .atomic_store_release, |
| 1963 | 1964 | .atomic_store_seq_cst, |
| 1964 | 1965 | .atomic_rmw, |
| 1965 | | .tag_name, |
| 1966 | 1966 | .err_return_trace, |
| 1967 | 1967 | .set_err_return_trace, |
| 1968 | 1968 | .save_err_return_trace_index, |
| ... | ... | @@ -6396,3 +6396,184 @@ fn callIntrinsic( |
| 6396 | 6396 | return WValue{ .stack = {} }; |
| 6397 | 6397 | } |
| 6398 | 6398 | } |
| 6399 | |
| 6400 | fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 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}); |
| 6403 | // const operand = try func.resolveInst(un_op); |
| 6404 | const enum_ty = func.air.typeOf(un_op); |
| 6405 | |
| 6406 | _ = try func.getTagNameFunction(enum_ty); |
| 6407 | |
| 6408 | func.finishAir(inst, .none, &.{un_op}); |
| 6409 | } |
| 6410 | |
| 6411 | fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { |
| 6412 | const enum_decl_index = enum_ty.getOwnerDecl(); |
| 6413 | const module = func.bin_file.base.options.module.?; |
| 6414 | |
| 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); |
| 6428 | defer arena_allocator.deinit(); |
| 6429 | const arena = arena_allocator.allocator(); |
| 6430 | |
| 6431 | const fqn = try module.declPtr(enum_decl_index).getFullyQualifiedName(module); |
| 6432 | defer module.gpa.free(fqn); |
| 6433 | const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{fqn}); |
| 6434 | |
| 6435 | if (func.bin_file.findGlobalSymbol(func_name)) |loc| { |
| 6436 | return loc.index; |
| 6437 | } |
| 6438 | |
| 6439 | const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); |
| 6440 | var int_tag_type_buffer: Type.Payload.Bits = undefined; |
| 6441 | const int_tag_ty = enum_ty.intTagType(&int_tag_type_buffer); |
| 6442 | |
| 6443 | if (int_tag_ty.bitSize(func.target) > 64) { |
| 6444 | return func.fail("TODO: Implement @tagName for enums with tag size larger than 64 bits", .{}); |
| 6445 | } |
| 6446 | |
| 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); |
| 6450 | |
| 6451 | var body_list = std.ArrayList(u8).init(arena); |
| 6452 | var writer = body_list.writer(); |
| 6453 | |
| 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)); |
| 6460 | |
| 6461 | // outer block |
| 6462 | try writer.writeByte(std.wasm.opcode(.block)); |
| 6463 | |
| 6464 | // 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. |
| 6466 | for (enum_ty.enumFields().keys(), 0..) |tag_name, field_index| { |
| 6467 | // for each tag name, create an atom to store its name into, |
| 6468 | // 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); |
| 6474 | |
| 6475 | // block for this if case |
| 6476 | try writer.writeByte(std.wasm.opcode(.block)); |
| 6477 | |
| 6478 | // get actual tag value (stored in 2nd parameter); |
| 6479 | try writer.writeByte(std.wasm.opcode(.local_get)); |
| 6480 | try leb.writeULEB128(writer, @as(u32, 1)); |
| 6481 | |
| 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); |
| 6488 | }; |
| 6489 | |
| 6490 | switch (tag_value) { |
| 6491 | .imm32 => |value| { |
| 6492 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| 6493 | try leb.writeULEB128(writer, value); |
| 6494 | try writer.writeByte(std.wasm.opcode(.i32_neq)); |
| 6495 | }, |
| 6496 | .imm64 => |value| { |
| 6497 | try writer.writeByte(std.wasm.opcode(.i64_const)); |
| 6498 | try leb.writeULEB128(writer, value); |
| 6499 | try writer.writeByte(std.wasm.opcode(.i64_neq)); |
| 6500 | }, |
| 6501 | else => unreachable, |
| 6502 | } |
| 6503 | // if they're not equal, break out of current branch |
| 6504 | try writer.writeByte(std.wasm.opcode(.br_if)); |
| 6505 | |
| 6506 | // store the address of the tagname in the pointer field of the slice |
| 6507 | try writer.writeByte(std.wasm.opcode(.local_get)); |
| 6508 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 6509 | |
| 6510 | const is_wasm32 = func.arch() == .wasm32; |
| 6511 | // 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, |
| 6526 | .offset = @intCast(u32, body_list.items.len), |
| 6527 | .index = tag_atom.getSymbolIndex().?, |
| 6528 | }); |
| 6529 | } |
| 6530 | |
| 6531 | // call the actual store instructions |
| 6532 | if (is_wasm32) { |
| 6533 | // store pointer |
| 6534 | try writer.writeByte(std.wasm.opcode(.i32_store)); |
| 6535 | try leb.writeULEB128(writer, @as(u32, 4)); |
| 6536 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 6537 | |
| 6538 | // 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)); |
| 6542 | try leb.writeULEB128(writer, @intCast(u32, tag_name.len)); |
| 6543 | try writer.writeByte(std.wasm.opcode(.i32_store)); |
| 6544 | try leb.writeULEB128(writer, @as(u32, 4)); |
| 6545 | try leb.writeULEB128(writer, @as(u32, 4)); |
| 6546 | } else { |
| 6547 | // store pointer |
| 6548 | try writer.writeByte(std.wasm.opcode(.i64_store)); |
| 6549 | try leb.writeULEB128(writer, @as(u32, 8)); |
| 6550 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 6551 | |
| 6552 | // 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)); |
| 6556 | try leb.writeULEB128(writer, @intCast(u64, tag_name.len)); |
| 6557 | try writer.writeByte(std.wasm.opcode(.i64_store)); |
| 6558 | try leb.writeULEB128(writer, @as(u32, 8)); |
| 6559 | try leb.writeULEB128(writer, @as(u32, 8)); |
| 6560 | } |
| 6561 | |
| 6562 | // break outside blocks |
| 6563 | try writer.writeByte(std.wasm.opcode(.br)); |
| 6564 | try leb.writeULEB128(writer, @as(u32, 1)); |
| 6565 | |
| 6566 | // end the block for this case |
| 6567 | try writer.writeByte(std.wasm.opcode(.end)); |
| 6568 | } |
| 6569 | |
| 6570 | try writer.writeByte(std.wasm.opcode(.@"unreachable")); // tag value does not have a name |
| 6571 | // finish outer block |
| 6572 | try writer.writeByte(std.wasm.opcode(.end)); |
| 6573 | // finish function body |
| 6574 | try writer.writeByte(std.wasm.opcode(.end)); |
| 6575 | |
| 6576 | try func_atom.code.appendSlice(func.gpa, body_list.items); |
| 6577 | |
| 6578 | return func_atom.sym_index; |
| 6579 | } |