| ... | @@ -1944,6 +1944,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1944,6 +1944,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1944 | .memcpy => func.airMemcpy(inst), | 1944 | .memcpy => func.airMemcpy(inst), |
| 1945 | | 1945 | |
| 1946 | .ret_addr => func.airRetAddr(inst), | 1946 | .ret_addr => func.airRetAddr(inst), |
| | 1947 | .tag_name => func.airTagName(inst), |
| 1947 | | 1948 | |
| 1948 | .mul_sat, | 1949 | .mul_sat, |
| 1949 | .mod, | 1950 | .mod, |
| ... | @@ -1962,7 +1963,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1962,7 +1963,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1962 | .atomic_store_release, | 1963 | .atomic_store_release, |
| 1963 | .atomic_store_seq_cst, | 1964 | .atomic_store_seq_cst, |
| 1964 | .atomic_rmw, | 1965 | .atomic_rmw, |
| 1965 | .tag_name, | | |
| 1966 | .err_return_trace, | 1966 | .err_return_trace, |
| 1967 | .set_err_return_trace, | 1967 | .set_err_return_trace, |
| 1968 | .save_err_return_trace_index, | 1968 | .save_err_return_trace_index, |
| ... | @@ -6396,3 +6396,184 @@ fn callIntrinsic( | ... | @@ -6396,3 +6396,184 @@ fn callIntrinsic( |
| 6396 | return WValue{ .stack = {} }; | 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 | } |