| ... | ... | @@ -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,194 @@ 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 | const func_sym_index = try func.getTagNameFunction(enum_ty); |
| 6407 | |
| 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}); |
| 6414 | } |
| 6415 | |
| 6416 | fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { |
| 6417 | const enum_decl_index = enum_ty.getOwnerDecl(); |
| 6418 | const module = func.bin_file.base.options.module.?; |
| 6419 | |
| 6420 | var arena_allocator = std.heap.ArenaAllocator.init(func.gpa); |
| 6421 | defer arena_allocator.deinit(); |
| 6422 | const arena = arena_allocator.allocator(); |
| 6423 | |
| 6424 | const fqn = try module.declPtr(enum_decl_index).getFullyQualifiedName(module); |
| 6425 | defer module.gpa.free(fqn); |
| 6426 | const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{fqn}); |
| 6427 | |
| 6428 | // check if we already generated code for this. |
| 6429 | if (func.bin_file.findGlobalSymbol(func_name)) |loc| { |
| 6430 | return loc.index; |
| 6431 | } |
| 6432 | |
| 6433 | var int_tag_type_buffer: Type.Payload.Bits = undefined; |
| 6434 | const int_tag_ty = enum_ty.intTagType(&int_tag_type_buffer); |
| 6435 | |
| 6436 | if (int_tag_ty.bitSize(func.target) > 64) { |
| 6437 | return func.fail("TODO: Implement @tagName for enums with tag size larger than 64 bits", .{}); |
| 6438 | } |
| 6439 | |
| 6440 | var relocs = std.ArrayList(link.File.Wasm.Relocation).init(func.gpa); |
| 6441 | defer relocs.deinit(); |
| 6442 | |
| 6443 | var body_list = std.ArrayList(u8).init(func.gpa); |
| 6444 | defer body_list.deinit(); |
| 6445 | var writer = body_list.writer(); |
| 6446 | |
| 6447 | // The locals of the function body (always 0) |
| 6448 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 6449 | |
| 6450 | // outer block |
| 6451 | try writer.writeByte(std.wasm.opcode(.block)); |
| 6452 | try writer.writeByte(std.wasm.block_empty); |
| 6453 | |
| 6454 | // TODO: Make switch implementation generic so we can use a jump table for this when the tags are not sparse. |
| 6455 | // generate an if-else chain for each tag value as well as constant. |
| 6456 | for (enum_ty.enumFields().keys(), 0..) |tag_name, field_index| { |
| 6457 | // for each tag name, create an unnamed const, |
| 6458 | // and then get a pointer to its value. |
| 6459 | var name_ty_payload: Type.Payload.Len = .{ |
| 6460 | .base = .{ .tag = .array_u8_sentinel_0 }, |
| 6461 | .data = @intCast(u64, tag_name.len), |
| 6462 | }; |
| 6463 | const name_ty = Type.initPayload(&name_ty_payload.base); |
| 6464 | const string_bytes = &module.string_literal_bytes; |
| 6465 | try string_bytes.ensureUnusedCapacity(module.gpa, tag_name.len); |
| 6466 | const gop = try module.string_literal_table.getOrPutContextAdapted(module.gpa, tag_name, Module.StringLiteralAdapter{ |
| 6467 | .bytes = string_bytes, |
| 6468 | }, Module.StringLiteralContext{ |
| 6469 | .bytes = string_bytes, |
| 6470 | }); |
| 6471 | if (!gop.found_existing) { |
| 6472 | gop.key_ptr.* = .{ |
| 6473 | .index = @intCast(u32, string_bytes.items.len), |
| 6474 | .len = @intCast(u32, tag_name.len), |
| 6475 | }; |
| 6476 | string_bytes.appendSliceAssumeCapacity(tag_name); |
| 6477 | gop.value_ptr.* = .none; |
| 6478 | } |
| 6479 | var name_val_payload: Value.Payload.StrLit = .{ |
| 6480 | .base = .{ .tag = .str_lit }, |
| 6481 | .data = gop.key_ptr.*, |
| 6482 | }; |
| 6483 | const name_val = Value.initPayload(&name_val_payload.base); |
| 6484 | const tag_sym_index = try func.bin_file.lowerUnnamedConst( |
| 6485 | .{ .ty = name_ty, .val = name_val }, |
| 6486 | enum_decl_index, |
| 6487 | ); |
| 6488 | |
| 6489 | // block for this if case |
| 6490 | try writer.writeByte(std.wasm.opcode(.block)); |
| 6491 | try writer.writeByte(std.wasm.block_empty); |
| 6492 | |
| 6493 | // get actual tag value (stored in 2nd parameter); |
| 6494 | try writer.writeByte(std.wasm.opcode(.local_get)); |
| 6495 | try leb.writeULEB128(writer, @as(u32, 1)); |
| 6496 | |
| 6497 | var tag_val_payload: Value.Payload.U32 = .{ |
| 6498 | .base = .{ .tag = .enum_field_index }, |
| 6499 | .data = @intCast(u32, field_index), |
| 6500 | }; |
| 6501 | const tag_value = try func.lowerConstant(Value.initPayload(&tag_val_payload.base), enum_ty); |
| 6502 | |
| 6503 | switch (tag_value) { |
| 6504 | .imm32 => |value| { |
| 6505 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| 6506 | try leb.writeULEB128(writer, value); |
| 6507 | try writer.writeByte(std.wasm.opcode(.i32_ne)); |
| 6508 | }, |
| 6509 | .imm64 => |value| { |
| 6510 | try writer.writeByte(std.wasm.opcode(.i64_const)); |
| 6511 | try leb.writeULEB128(writer, value); |
| 6512 | try writer.writeByte(std.wasm.opcode(.i64_ne)); |
| 6513 | }, |
| 6514 | else => unreachable, |
| 6515 | } |
| 6516 | // if they're not equal, break out of current branch |
| 6517 | try writer.writeByte(std.wasm.opcode(.br_if)); |
| 6518 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 6519 | |
| 6520 | // store the address of the tagname in the pointer field of the slice |
| 6521 | // get the address twice so we can also store the length. |
| 6522 | try writer.writeByte(std.wasm.opcode(.local_get)); |
| 6523 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 6524 | try writer.writeByte(std.wasm.opcode(.local_get)); |
| 6525 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 6526 | |
| 6527 | // get address of tagname and emit a relocation to it |
| 6528 | if (func.arch() == .wasm32) { |
| 6529 | const encoded_alignment = @ctz(@as(u32, 4)); |
| 6530 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| 6531 | try relocs.append(.{ |
| 6532 | .relocation_type = .R_WASM_MEMORY_ADDR_LEB, |
| 6533 | .offset = @intCast(u32, body_list.items.len), |
| 6534 | .index = tag_sym_index, |
| 6535 | }); |
| 6536 | try writer.writeAll(&[_]u8{0} ** 5); // will be relocated |
| 6537 | |
| 6538 | // store pointer |
| 6539 | try writer.writeByte(std.wasm.opcode(.i32_store)); |
| 6540 | try leb.writeULEB128(writer, encoded_alignment); |
| 6541 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 6542 | |
| 6543 | // store length |
| 6544 | try writer.writeByte(std.wasm.opcode(.i32_const)); |
| 6545 | try leb.writeULEB128(writer, @intCast(u32, tag_name.len)); |
| 6546 | try writer.writeByte(std.wasm.opcode(.i32_store)); |
| 6547 | try leb.writeULEB128(writer, encoded_alignment); |
| 6548 | try leb.writeULEB128(writer, @as(u32, 4)); |
| 6549 | } else { |
| 6550 | const encoded_alignment = @ctz(@as(u32, 8)); |
| 6551 | try writer.writeByte(std.wasm.opcode(.i64_const)); |
| 6552 | try relocs.append(.{ |
| 6553 | .relocation_type = .R_WASM_MEMORY_ADDR_LEB64, |
| 6554 | .offset = @intCast(u32, body_list.items.len), |
| 6555 | .index = tag_sym_index, |
| 6556 | }); |
| 6557 | try writer.writeAll(&[_]u8{0} ** 10); // will be relocated |
| 6558 | |
| 6559 | // store pointer |
| 6560 | try writer.writeByte(std.wasm.opcode(.i64_store)); |
| 6561 | try leb.writeULEB128(writer, encoded_alignment); |
| 6562 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 6563 | |
| 6564 | // store length |
| 6565 | try writer.writeByte(std.wasm.opcode(.i64_const)); |
| 6566 | try leb.writeULEB128(writer, @intCast(u64, tag_name.len)); |
| 6567 | try writer.writeByte(std.wasm.opcode(.i64_store)); |
| 6568 | try leb.writeULEB128(writer, encoded_alignment); |
| 6569 | try leb.writeULEB128(writer, @as(u32, 8)); |
| 6570 | } |
| 6571 | |
| 6572 | // break outside blocks |
| 6573 | try writer.writeByte(std.wasm.opcode(.br)); |
| 6574 | try leb.writeULEB128(writer, @as(u32, 1)); |
| 6575 | |
| 6576 | // end the block for this case |
| 6577 | try writer.writeByte(std.wasm.opcode(.end)); |
| 6578 | } |
| 6579 | |
| 6580 | try writer.writeByte(std.wasm.opcode(.@"unreachable")); // tag value does not have a name |
| 6581 | // finish outer block |
| 6582 | try writer.writeByte(std.wasm.opcode(.end)); |
| 6583 | // finish function body |
| 6584 | try writer.writeByte(std.wasm.opcode(.end)); |
| 6585 | |
| 6586 | const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); |
| 6587 | const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty}, slice_ty, func.target); |
| 6588 | return func.bin_file.createFunction(func_name, func_type, &body_list, &relocs); |
| 6589 | } |