Commit 5816997ae7
Changed files (4)
src
test
stage2
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);
},
}
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;
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 {
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(