diff --git a/src/Sema.zig b/src/Sema.zig index 1538e54208..24c51bdc46 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5423,11 +5423,22 @@ fn zirRetErrValue( const src = inst_data.src(); // Add the error tag to the inferred error set of the in-scope function. + if (sema.func) |func| { + const fn_ty = func.owner_decl.ty; + const fn_ret_ty = fn_ty.fnReturnType(); + if (fn_ret_ty.zigTypeTag() == .ErrorUnion and + fn_ret_ty.errorUnionSet().tag() == .error_set_inferred) + { + return sema.mod.fail(&block.base, src, "TODO: Sema.zirRetErrValue", .{}); + } + } // Return the error code from the function. - - _ = inst_data; - _ = err_name; - return sema.mod.fail(&block.base, src, "TODO: Sema.zirRetErrValueCode", .{}); + const kv = try sema.mod.getErrorValue(err_name); + const result_inst = try sema.mod.constInst(sema.arena, src, .{ + .ty = try Type.Tag.error_set_single.create(sema.arena, kv.key), + .val = try Value.Tag.@"error".create(sema.arena, .{ .name = kv.key }), + }); + return sema.analyzeRet(block, result_inst, src, true); } fn zirRetCoerce( @@ -6411,6 +6422,15 @@ fn panicWithMsg( ) !Zir.Inst.Index { const mod = sema.mod; const arena = sema.arena; + + const this_feature_is_implemented_in_the_backend = + mod.comp.bin_file.options.object_format == .c; + if (!this_feature_is_implemented_in_the_backend) { + // TODO implement this feature in all the backends and then delete this branch + _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint); + _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach); + return always_noreturn; + } const panic_fn = try sema.getBuiltin(block, src, "panic"); const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); @@ -6431,8 +6451,6 @@ fn safetyPanic( src: LazySrcLoc, panic_id: PanicId, ) !Zir.Inst.Index { - const mod = sema.mod; - const arena = sema.arena; const msg = switch (panic_id) { .unreach => "reached unreachable code", .unwrap_null => "attempt to use null value", @@ -6441,11 +6459,28 @@ fn safetyPanic( .incorrect_alignment => "incorrect alignment", .invalid_error_code => "invalid error code", }; - const msg_inst = try mod.constInst(arena, src, .{ - .ty = Type.initTag(.const_slice_u8), - .val = try Value.Tag.ref_val.create(arena, try Value.Tag.bytes.create(arena, msg)), - }); - return sema.panicWithMsg(block, src, msg_inst); + + const msg_inst = msg_inst: { + // TODO instead of making a new decl for every panic in the entire compilation, + // introduce the concept of a reference-counted decl for these + var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); + errdefer new_decl_arena.deinit(); + + const decl_ty = try Type.Tag.array_u8.create(&new_decl_arena.allocator, msg.len); + const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, msg); + + const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{ + .ty = decl_ty, + .val = decl_val, + }); + errdefer sema.mod.deleteAnonDecl(&block.base, new_decl); + try new_decl.finalizeNewArena(&new_decl_arena); + break :msg_inst try sema.analyzeDeclRef(block, .unneeded, new_decl); + }; + + const casted_msg_inst = try sema.coerce(block, Type.initTag(.const_slice_u8), msg_inst, src); + + return sema.panicWithMsg(block, src, casted_msg_inst); } fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { diff --git a/src/codegen.zig b/src/codegen.zig index 0b63222242..f9f5a861fb 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -142,40 +142,52 @@ pub fn generateSymbol( ), }; }, - .Pointer => { - // TODO populate .debug_info for the pointer - if (typed_value.val.castTag(.decl_ref)) |payload| { - const decl = payload.data; - if (decl.analysis != .complete) return error.AnalysisFail; - // TODO handle the dependency of this symbol on the decl's vaddr. - // If the decl changes vaddr, then this symbol needs to get regenerated. - const vaddr = bin_file.getDeclVAddr(decl); - const endian = bin_file.options.target.cpu.arch.endian(); - switch (bin_file.options.target.cpu.arch.ptrBitWidth()) { - 16 => { - try code.resize(2); - mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian); - }, - 32 => { - try code.resize(4); - mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian); - }, - 64 => { - try code.resize(8); - mem.writeInt(u64, code.items[0..8], vaddr, endian); - }, - else => unreachable, + .Pointer => switch (typed_value.ty.ptrSize()) { + .Slice => { + return Result{ + .fail = try ErrorMsg.create( + bin_file.allocator, + src_loc, + "TODO implement generateSymbol for slice {}", + .{typed_value.val}, + ), + }; + }, + else => { + // TODO populate .debug_info for the pointer + if (typed_value.val.castTag(.decl_ref)) |payload| { + const decl = payload.data; + if (decl.analysis != .complete) return error.AnalysisFail; + // TODO handle the dependency of this symbol on the decl's vaddr. + // If the decl changes vaddr, then this symbol needs to get regenerated. + const vaddr = bin_file.getDeclVAddr(decl); + const endian = bin_file.options.target.cpu.arch.endian(); + switch (bin_file.options.target.cpu.arch.ptrBitWidth()) { + 16 => { + try code.resize(2); + mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian); + }, + 32 => { + try code.resize(4); + mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian); + }, + 64 => { + try code.resize(8); + mem.writeInt(u64, code.items[0..8], vaddr, endian); + }, + else => unreachable, + } + return Result{ .appended = {} }; } - return Result{ .appended = {} }; - } - return Result{ - .fail = try ErrorMsg.create( - bin_file.allocator, - src_loc, - "TODO implement generateSymbol for pointer {}", - .{typed_value.val}, - ), - }; + return Result{ + .fail = try ErrorMsg.create( + bin_file.allocator, + src_loc, + "TODO implement generateSymbol for pointer {}", + .{typed_value.val}, + ), + }; + }, }, .Int => { // TODO populate .debug_info for the integer @@ -2244,10 +2256,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { try self.register_manager.getReg(reg, null); try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); }, - .stack_offset => { + .stack_offset => |off| { // Here we need to emit instructions like this: // mov qword ptr [rsp + stack_offset], x - return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{}); + try self.genSetStack(arg.src, arg.ty, off, arg_mcv); }, .ptr_stack_offset => { return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{}); @@ -3444,9 +3456,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }, } }, - .embedded_in_code => |code_offset| { - _ = code_offset; - return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{}); + .embedded_in_code => { + // TODO this and `.stack_offset` below need to get improved to support types greater than + // register size, and do general memcpy + const reg = try self.copyToTmpRegister(src, ty, mcv); + return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); }, .register => |reg| { try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89); @@ -3456,6 +3470,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(src, "TODO implement set stack variable from memory vaddr", .{}); }, .stack_offset => |off| { + // TODO this and `.embedded_in_code` above need to get improved to support types greater than + // register size, and do general memcpy + if (stack_offset == off) return; // Copy stack variable to itself; nothing to do. @@ -4161,33 +4178,48 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); switch (typed_value.ty.zigTypeTag()) { - .Pointer => { - if (typed_value.val.castTag(.decl_ref)) |payload| { - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const decl = payload.data; - const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; - const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; - return MCValue{ .memory = got_addr }; - } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const decl = payload.data; - const got_addr = blk: { - const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment; - const got = seg.sections.items[macho_file.got_section_index.?]; - break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes; - }; - return MCValue{ .memory = got_addr }; - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const decl = payload.data; - const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; - return MCValue{ .memory = got_addr }; - } else { - return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{}); + .Pointer => switch (typed_value.ty.ptrSize()) { + .Slice => { + var buf: Type.Payload.ElemType = undefined; + const ptr_type = typed_value.ty.slicePtrFieldType(&buf); + const ptr_mcv = try self.genTypedValue(src, .{ .ty = ptr_type, .val = typed_value.val }); + const slice_len = typed_value.val.sliceLen(); + // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean + // the Sema code needs to use anonymous Decls or alloca instructions to store data. + const ptr_imm = ptr_mcv.memory; + _ = slice_len; + _ = ptr_imm; + // We need more general support for const data being stored in memory to make this work. + return self.fail(src, "TODO codegen for const slices", .{}); + }, + else => { + if (typed_value.val.castTag(.decl_ref)) |payload| { + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + const decl = payload.data; + const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; + const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; + return MCValue{ .memory = got_addr }; + } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { + const decl = payload.data; + const got_addr = blk: { + const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment; + const got = seg.sections.items[macho_file.got_section_index.?]; + break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes; + }; + return MCValue{ .memory = got_addr }; + } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { + const decl = payload.data; + const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; + return MCValue{ .memory = got_addr }; + } else { + return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{}); + } } - } - if (typed_value.val.tag() == .int_u64) { - return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; - } - return self.fail(src, "TODO codegen more kinds of const pointers", .{}); + if (typed_value.val.tag() == .int_u64) { + return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; + } + return self.fail(src, "TODO codegen more kinds of const pointers", .{}); + }, }, .Int => { const info = typed_value.ty.intInfo(self.target.*); @@ -4264,27 +4296,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { var next_stack_offset: u32 = 0; for (param_types) |ty, i| { - switch (ty.zigTypeTag()) { - .Bool, .Int => { - if (!ty.hasCodeGenBits()) { - assert(cc != .C); - result.args[i] = .{ .none = {} }; - } else { - const param_size = @intCast(u32, ty.abiSize(self.target.*)); - if (next_int_reg >= c_abi_int_param_regs.len) { - result.args[i] = .{ .stack_offset = next_stack_offset }; - next_stack_offset += param_size; - } else { - const aliased_reg = registerAlias( - c_abi_int_param_regs[next_int_reg], - param_size, - ); - result.args[i] = .{ .register = aliased_reg }; - next_int_reg += 1; - } - } - }, - else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}), + if (!ty.hasCodeGenBits()) { + assert(cc != .C); + result.args[i] = .{ .none = {} }; + continue; + } + const param_size = @intCast(u32, ty.abiSize(self.target.*)); + const pass_in_reg = switch (ty.zigTypeTag()) { + .Bool => true, + .Int => param_size <= 8, + .Pointer => ty.ptrSize() != .Slice, + .Optional => ty.isPtrLikeOptional(), + else => false, + }; + if (pass_in_reg) { + if (next_int_reg >= c_abi_int_param_regs.len) { + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_stack_offset += param_size; + } else { + const aliased_reg = registerAlias( + c_abi_int_param_regs[next_int_reg], + param_size, + ); + result.args[i] = .{ .register = aliased_reg }; + next_int_reg += 1; + } + } else { + // For simplicity of codegen, slices and other types are always pushed onto the stack. + // TODO: look into optimizing this by passing things as registers sometimes, + // such as ptr and len of slices as separate registers. + // TODO: also we need to honor the C ABI for relevant types rather than passing on + // the stack here. + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_stack_offset += param_size; } } result.stack_byte_count = next_stack_offset; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 722077b8b8..d754b478b9 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2505,11 +2505,7 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo abbrev_base_type, DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1 1, // DW.AT_byte_size, DW.FORM_data1 - 'b', - 'o', - 'o', - 'l', - 0, // DW.AT_name, DW.FORM_string + 'b', 'o', 'o', 'l', 0, // DW.AT_name, DW.FORM_string }); }, .Int => { @@ -2526,8 +2522,23 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo // DW.AT_name, DW.FORM_string try dbg_info_buffer.writer().print("{}\x00", .{ty}); }, + .Optional => { + if (ty.isPtrLikeOptional()) { + try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12); + dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); + // DW.AT_encoding, DW.FORM_data1 + dbg_info_buffer.appendAssumeCapacity(DW.ATE_address); + // DW.AT_byte_size, DW.FORM_data1 + dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target))); + // DW.AT_name, DW.FORM_string + try dbg_info_buffer.writer().print("{}\x00", .{ty}); + } else { + log.err("TODO implement .debug_info for type '{}'", .{ty}); + try dbg_info_buffer.append(abbrev_pad1); + } + }, else => { - std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty}); + log.err("TODO implement .debug_info for type '{}'", .{ty}); try dbg_info_buffer.append(abbrev_pad1); }, } diff --git a/test/stage2/wasm.zig b/test/stage2/wasm.zig index 07386e8212..60c746f4e0 100644 --- a/test/stage2/wasm.zig +++ b/test/stage2/wasm.zig @@ -587,8 +587,6 @@ pub fn addCases(ctx: *TestContext) !void { } { - // TODO implement Type equality comparison of error unions in SEMA - // before we can incrementally compile functions with an error union as return type var case = ctx.exe("wasm error union part 2", wasi); case.addCompareOutput(