authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-07 14:17:04-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-07 14:17:04-07:00
log5816997ae79c6025d5f85aab0c9ab456fecadec9
tree66e90395fe170f84e1567787538d4cbc67a63fe4
parent13f04e3012b6b2eee141923f9780fce55f7a999d

stage2: get tests passing

* implement enough of ret_err_value to pass wasm tests * only do the proper `@panic` implementation for the backends which support it, which is currently only the C backend. The other backends will see `@breakpoint(); unreachable;` same as before. - I plan to do AIR memory layout reworking as a prerequisite to fixing other backends, because that will help me put all the constants up front, which will allow the codegen to lower to memory without jumps. * `@panic` is implemented using anon decls for the message. Makes it easier on the backends. Might want to look into re-using decls for this in the future. * implement DWARF .debug_info for pointer-like optionals.

4 files changed, 192 insertions(+), 104 deletions(-)

src/Sema.zig+46-11
...@@ -5423,11 +5423,22 @@ fn zirRetErrValue(...@@ -5423,11 +5423,22 @@ fn zirRetErrValue(
5423 const src = inst_data.src();5423 const src = inst_data.src();
54245424
5425 // Add the error tag to the inferred error set of the in-scope function.5425 // Add the error tag to the inferred error set of the in-scope function.
5426 if (sema.func) |func| {
5427 const fn_ty = func.owner_decl.ty;
5428 const fn_ret_ty = fn_ty.fnReturnType();
5429 if (fn_ret_ty.zigTypeTag() == .ErrorUnion and
5430 fn_ret_ty.errorUnionSet().tag() == .error_set_inferred)
5431 {
5432 return sema.mod.fail(&block.base, src, "TODO: Sema.zirRetErrValue", .{});
5433 }
5434 }
5426 // Return the error code from the function.5435 // Return the error code from the function.
54275436 const kv = try sema.mod.getErrorValue(err_name);
5428 _ = inst_data;5437 const result_inst = try sema.mod.constInst(sema.arena, src, .{
5429 _ = err_name;5438 .ty = try Type.Tag.error_set_single.create(sema.arena, kv.key),
5430 return sema.mod.fail(&block.base, src, "TODO: Sema.zirRetErrValueCode", .{});5439 .val = try Value.Tag.@"error".create(sema.arena, .{ .name = kv.key }),
5440 });
5441 return sema.analyzeRet(block, result_inst, src, true);
5431}5442}
54325443
5433fn zirRetCoerce(5444fn zirRetCoerce(
...@@ -6411,6 +6422,15 @@ fn panicWithMsg(...@@ -6411,6 +6422,15 @@ fn panicWithMsg(
6411) !Zir.Inst.Index {6422) !Zir.Inst.Index {
6412 const mod = sema.mod;6423 const mod = sema.mod;
6413 const arena = sema.arena;6424 const arena = sema.arena;
6425
6426 const this_feature_is_implemented_in_the_backend =
6427 mod.comp.bin_file.options.object_format == .c;
6428 if (!this_feature_is_implemented_in_the_backend) {
6429 // TODO implement this feature in all the backends and then delete this branch
6430 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
6431 _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
6432 return always_noreturn;
6433 }
6414 const panic_fn = try sema.getBuiltin(block, src, "panic");6434 const panic_fn = try sema.getBuiltin(block, src, "panic");
6415 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");6435 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
6416 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);6436 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
...@@ -6431,8 +6451,6 @@ fn safetyPanic(...@@ -6431,8 +6451,6 @@ fn safetyPanic(
6431 src: LazySrcLoc,6451 src: LazySrcLoc,
6432 panic_id: PanicId,6452 panic_id: PanicId,
6433) !Zir.Inst.Index {6453) !Zir.Inst.Index {
6434 const mod = sema.mod;
6435 const arena = sema.arena;
6436 const msg = switch (panic_id) {6454 const msg = switch (panic_id) {
6437 .unreach => "reached unreachable code",6455 .unreach => "reached unreachable code",
6438 .unwrap_null => "attempt to use null value",6456 .unwrap_null => "attempt to use null value",
...@@ -6441,11 +6459,28 @@ fn safetyPanic(...@@ -6441,11 +6459,28 @@ fn safetyPanic(
6441 .incorrect_alignment => "incorrect alignment",6459 .incorrect_alignment => "incorrect alignment",
6442 .invalid_error_code => "invalid error code",6460 .invalid_error_code => "invalid error code",
6443 };6461 };
6444 const msg_inst = try mod.constInst(arena, src, .{6462
6445 .ty = Type.initTag(.const_slice_u8),6463 const msg_inst = msg_inst: {
6446 .val = try Value.Tag.ref_val.create(arena, try Value.Tag.bytes.create(arena, msg)),6464 // TODO instead of making a new decl for every panic in the entire compilation,
6447 });6465 // introduce the concept of a reference-counted decl for these
6448 return sema.panicWithMsg(block, src, msg_inst);6466 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
6467 errdefer new_decl_arena.deinit();
6468
6469 const decl_ty = try Type.Tag.array_u8.create(&new_decl_arena.allocator, msg.len);
6470 const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, msg);
6471
6472 const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{
6473 .ty = decl_ty,
6474 .val = decl_val,
6475 });
6476 errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
6477 try new_decl.finalizeNewArena(&new_decl_arena);
6478 break :msg_inst try sema.analyzeDeclRef(block, .unneeded, new_decl);
6479 };
6480
6481 const casted_msg_inst = try sema.coerce(block, Type.initTag(.const_slice_u8), msg_inst, src);
6482
6483 return sema.panicWithMsg(block, src, casted_msg_inst);
6449}6484}
64506485
6451fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void {6486fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void {
src/codegen.zig+129-85
...@@ -142,40 +142,52 @@ pub fn generateSymbol(...@@ -142,40 +142,52 @@ pub fn generateSymbol(
142 ),142 ),
143 };143 };
144 },144 },
145 .Pointer => {145 .Pointer => switch (typed_value.ty.ptrSize()) {
146 // TODO populate .debug_info for the pointer146 .Slice => {
147 if (typed_value.val.castTag(.decl_ref)) |payload| {147 return Result{
148 const decl = payload.data;148 .fail = try ErrorMsg.create(
149 if (decl.analysis != .complete) return error.AnalysisFail;149 bin_file.allocator,
150 // TODO handle the dependency of this symbol on the decl's vaddr.150 src_loc,
151 // If the decl changes vaddr, then this symbol needs to get regenerated.151 "TODO implement generateSymbol for slice {}",
152 const vaddr = bin_file.getDeclVAddr(decl);152 .{typed_value.val},
153 const endian = bin_file.options.target.cpu.arch.endian();153 ),
154 switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {154 };
155 16 => {155 },
156 try code.resize(2);156 else => {
157 mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);157 // TODO populate .debug_info for the pointer
158 },158 if (typed_value.val.castTag(.decl_ref)) |payload| {
159 32 => {159 const decl = payload.data;
160 try code.resize(4);160 if (decl.analysis != .complete) return error.AnalysisFail;
161 mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);161 // TODO handle the dependency of this symbol on the decl's vaddr.
162 },162 // If the decl changes vaddr, then this symbol needs to get regenerated.
163 64 => {163 const vaddr = bin_file.getDeclVAddr(decl);
164 try code.resize(8);164 const endian = bin_file.options.target.cpu.arch.endian();
165 mem.writeInt(u64, code.items[0..8], vaddr, endian);165 switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
166 },166 16 => {
167 else => unreachable,167 try code.resize(2);
168 mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);
169 },
170 32 => {
171 try code.resize(4);
172 mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);
173 },
174 64 => {
175 try code.resize(8);
176 mem.writeInt(u64, code.items[0..8], vaddr, endian);
177 },
178 else => unreachable,
179 }
180 return Result{ .appended = {} };
168 }181 }
169 return Result{ .appended = {} };182 return Result{
170 }183 .fail = try ErrorMsg.create(
171 return Result{184 bin_file.allocator,
172 .fail = try ErrorMsg.create(185 src_loc,
173 bin_file.allocator,186 "TODO implement generateSymbol for pointer {}",
174 src_loc,187 .{typed_value.val},
175 "TODO implement generateSymbol for pointer {}",188 ),
176 .{typed_value.val},189 };
177 ),190 },
178 };
179 },191 },
180 .Int => {192 .Int => {
181 // TODO populate .debug_info for the integer193 // TODO populate .debug_info for the integer
...@@ -2244,10 +2256,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2244,10 +2256,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2244 try self.register_manager.getReg(reg, null);2256 try self.register_manager.getReg(reg, null);
2245 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2257 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2246 },2258 },
2247 .stack_offset => {2259 .stack_offset => |off| {
2248 // Here we need to emit instructions like this:2260 // Here we need to emit instructions like this:
2249 // mov qword ptr [rsp + stack_offset], x2261 // mov qword ptr [rsp + stack_offset], x
2250 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});2262 try self.genSetStack(arg.src, arg.ty, off, arg_mcv);
2251 },2263 },
2252 .ptr_stack_offset => {2264 .ptr_stack_offset => {
2253 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});2265 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 {...@@ -3444,9 +3456,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3444 },3456 },
3445 }3457 }
3446 },3458 },
3447 .embedded_in_code => |code_offset| {3459 .embedded_in_code => {
3448 _ = code_offset;3460 // TODO this and `.stack_offset` below need to get improved to support types greater than
3449 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});3461 // register size, and do general memcpy
3462 const reg = try self.copyToTmpRegister(src, ty, mcv);
3463 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
3450 },3464 },
3451 .register => |reg| {3465 .register => |reg| {
3452 try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);3466 try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);
...@@ -3456,6 +3470,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3456,6 +3470,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3456 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});3470 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
3457 },3471 },
3458 .stack_offset => |off| {3472 .stack_offset => |off| {
3473 // TODO this and `.embedded_in_code` above need to get improved to support types greater than
3474 // register size, and do general memcpy
3475
3459 if (stack_offset == off)3476 if (stack_offset == off)
3460 return; // Copy stack variable to itself; nothing to do.3477 return; // Copy stack variable to itself; nothing to do.
34613478
...@@ -4161,33 +4178,48 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4161,33 +4178,48 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4161 const ptr_bits = self.target.cpu.arch.ptrBitWidth();4178 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4162 const ptr_bytes: u64 = @divExact(ptr_bits, 8);4179 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4163 switch (typed_value.ty.zigTypeTag()) {4180 switch (typed_value.ty.zigTypeTag()) {
4164 .Pointer => {4181 .Pointer => switch (typed_value.ty.ptrSize()) {
4165 if (typed_value.val.castTag(.decl_ref)) |payload| {4182 .Slice => {
4166 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4183 var buf: Type.Payload.ElemType = undefined;
4167 const decl = payload.data;4184 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
4168 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];4185 const ptr_mcv = try self.genTypedValue(src, .{ .ty = ptr_type, .val = typed_value.val });
4169 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;4186 const slice_len = typed_value.val.sliceLen();
4170 return MCValue{ .memory = got_addr };4187 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean
4171 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {4188 // the Sema code needs to use anonymous Decls or alloca instructions to store data.
4172 const decl = payload.data;4189 const ptr_imm = ptr_mcv.memory;
4173 const got_addr = blk: {4190 _ = slice_len;
4174 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;4191 _ = ptr_imm;
4175 const got = seg.sections.items[macho_file.got_section_index.?];4192 // We need more general support for const data being stored in memory to make this work.
4176 break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes;4193 return self.fail(src, "TODO codegen for const slices", .{});
4177 };4194 },
4178 return MCValue{ .memory = got_addr };4195 else => {
4179 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {4196 if (typed_value.val.castTag(.decl_ref)) |payload| {
4180 const decl = payload.data;4197 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4181 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;4198 const decl = payload.data;
4182 return MCValue{ .memory = got_addr };4199 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
4183 } else {4200 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
4184 return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});4201 return MCValue{ .memory = got_addr };
4202 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4203 const decl = payload.data;
4204 const got_addr = blk: {
4205 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
4206 const got = seg.sections.items[macho_file.got_section_index.?];
4207 break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes;
4208 };
4209 return MCValue{ .memory = got_addr };
4210 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4211 const decl = payload.data;
4212 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
4213 return MCValue{ .memory = got_addr };
4214 } else {
4215 return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
4216 }
4185 }4217 }
4186 }4218 if (typed_value.val.tag() == .int_u64) {
4187 if (typed_value.val.tag() == .int_u64) {4219 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
4188 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };4220 }
4189 }4221 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
4190 return self.fail(src, "TODO codegen more kinds of const pointers", .{});4222 },
4191 },4223 },
4192 .Int => {4224 .Int => {
4193 const info = typed_value.ty.intInfo(self.target.*);4225 const info = typed_value.ty.intInfo(self.target.*);
...@@ -4264,27 +4296,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4264,27 +4296,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4264 var next_stack_offset: u32 = 0;4296 var next_stack_offset: u32 = 0;
42654297
4266 for (param_types) |ty, i| {4298 for (param_types) |ty, i| {
4267 switch (ty.zigTypeTag()) {4299 if (!ty.hasCodeGenBits()) {
4268 .Bool, .Int => {4300 assert(cc != .C);
4269 if (!ty.hasCodeGenBits()) {4301 result.args[i] = .{ .none = {} };
4270 assert(cc != .C);4302 continue;
4271 result.args[i] = .{ .none = {} };4303 }
4272 } else {4304 const param_size = @intCast(u32, ty.abiSize(self.target.*));
4273 const param_size = @intCast(u32, ty.abiSize(self.target.*));4305 const pass_in_reg = switch (ty.zigTypeTag()) {
4274 if (next_int_reg >= c_abi_int_param_regs.len) {4306 .Bool => true,
4275 result.args[i] = .{ .stack_offset = next_stack_offset };4307 .Int => param_size <= 8,
4276 next_stack_offset += param_size;4308 .Pointer => ty.ptrSize() != .Slice,
4277 } else {4309 .Optional => ty.isPtrLikeOptional(),
4278 const aliased_reg = registerAlias(4310 else => false,
4279 c_abi_int_param_regs[next_int_reg],4311 };
4280 param_size,4312 if (pass_in_reg) {
4281 );4313 if (next_int_reg >= c_abi_int_param_regs.len) {
4282 result.args[i] = .{ .register = aliased_reg };4314 result.args[i] = .{ .stack_offset = next_stack_offset };
4283 next_int_reg += 1;4315 next_stack_offset += param_size;
4284 }4316 } else {
4285 }4317 const aliased_reg = registerAlias(
4286 },4318 c_abi_int_param_regs[next_int_reg],
4287 else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}),4319 param_size,
4320 );
4321 result.args[i] = .{ .register = aliased_reg };
4322 next_int_reg += 1;
4323 }
4324 } else {
4325 // For simplicity of codegen, slices and other types are always pushed onto the stack.
4326 // TODO: look into optimizing this by passing things as registers sometimes,
4327 // such as ptr and len of slices as separate registers.
4328 // TODO: also we need to honor the C ABI for relevant types rather than passing on
4329 // the stack here.
4330 result.args[i] = .{ .stack_offset = next_stack_offset };
4331 next_stack_offset += param_size;
4288 }4332 }
4289 }4333 }
4290 result.stack_byte_count = next_stack_offset;4334 result.stack_byte_count = next_stack_offset;
src/link/Elf.zig+17-6
...@@ -2505,11 +2505,7 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo...@@ -2505,11 +2505,7 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo
2505 abbrev_base_type,2505 abbrev_base_type,
2506 DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data12506 DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1
2507 1, // DW.AT_byte_size, DW.FORM_data12507 1, // DW.AT_byte_size, DW.FORM_data1
2508 'b',2508 'b', 'o', 'o', 'l', 0, // DW.AT_name, DW.FORM_string
2509 'o',
2510 'o',
2511 'l',
2512 0, // DW.AT_name, DW.FORM_string
2513 });2509 });
2514 },2510 },
2515 .Int => {2511 .Int => {
...@@ -2526,8 +2522,23 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo...@@ -2526,8 +2522,23 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo
2526 // DW.AT_name, DW.FORM_string2522 // DW.AT_name, DW.FORM_string
2527 try dbg_info_buffer.writer().print("{}\x00", .{ty});2523 try dbg_info_buffer.writer().print("{}\x00", .{ty});
2528 },2524 },
2525 .Optional => {
2526 if (ty.isPtrLikeOptional()) {
2527 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
2528 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
2529 // DW.AT_encoding, DW.FORM_data1
2530 dbg_info_buffer.appendAssumeCapacity(DW.ATE_address);
2531 // DW.AT_byte_size, DW.FORM_data1
2532 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target)));
2533 // DW.AT_name, DW.FORM_string
2534 try dbg_info_buffer.writer().print("{}\x00", .{ty});
2535 } else {
2536 log.err("TODO implement .debug_info for type '{}'", .{ty});
2537 try dbg_info_buffer.append(abbrev_pad1);
2538 }
2539 },
2529 else => {2540 else => {
2530 std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty});2541 log.err("TODO implement .debug_info for type '{}'", .{ty});
2531 try dbg_info_buffer.append(abbrev_pad1);2542 try dbg_info_buffer.append(abbrev_pad1);
2532 },2543 },
2533 }2544 }
test/stage2/wasm.zig-2
...@@ -587,8 +587,6 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -587,8 +587,6 @@ pub fn addCases(ctx: *TestContext) !void {
587 }587 }
588588
589 {589 {
590 // TODO implement Type equality comparison of error unions in SEMA
591 // before we can incrementally compile functions with an error union as return type
592 var case = ctx.exe("wasm error union part 2", wasi);590 var case = ctx.exe("wasm error union part 2", wasi);
593591
594 case.addCompareOutput(592 case.addCompareOutput(