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(
54235423 const src = inst_data.src();
54245424
54255425 // 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 }
54265435 // Return the error code from the function.
5427
5428 _ = inst_data;
5429 _ = err_name;
5430 return sema.mod.fail(&block.base, src, "TODO: Sema.zirRetErrValueCode", .{});
5436 const kv = try sema.mod.getErrorValue(err_name);
5437 const result_inst = try sema.mod.constInst(sema.arena, src, .{
5438 .ty = try Type.Tag.error_set_single.create(sema.arena, kv.key),
5439 .val = try Value.Tag.@"error".create(sema.arena, .{ .name = kv.key }),
5440 });
5441 return sema.analyzeRet(block, result_inst, src, true);
54315442}
54325443
54335444fn zirRetCoerce(
......@@ -6411,6 +6422,15 @@ fn panicWithMsg(
64116422) !Zir.Inst.Index {
64126423 const mod = sema.mod;
64136424 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 }
64146434 const panic_fn = try sema.getBuiltin(block, src, "panic");
64156435 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
64166436 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
......@@ -6431,8 +6451,6 @@ fn safetyPanic(
64316451 src: LazySrcLoc,
64326452 panic_id: PanicId,
64336453) !Zir.Inst.Index {
6434 const mod = sema.mod;
6435 const arena = sema.arena;
64366454 const msg = switch (panic_id) {
64376455 .unreach => "reached unreachable code",
64386456 .unwrap_null => "attempt to use null value",
......@@ -6441,11 +6459,28 @@ fn safetyPanic(
64416459 .incorrect_alignment => "incorrect alignment",
64426460 .invalid_error_code => "invalid error code",
64436461 };
6444 const msg_inst = try mod.constInst(arena, src, .{
6445 .ty = Type.initTag(.const_slice_u8),
6446 .val = try Value.Tag.ref_val.create(arena, try Value.Tag.bytes.create(arena, msg)),
6447 });
6448 return sema.panicWithMsg(block, src, msg_inst);
6462
6463 const msg_inst = msg_inst: {
6464 // TODO instead of making a new decl for every panic in the entire compilation,
6465 // introduce the concept of a reference-counted decl for these
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);
64496484}
64506485
64516486fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void {
src/codegen.zig+129-85
......@@ -142,40 +142,52 @@ pub fn generateSymbol(
142142 ),
143143 };
144144 },
145 .Pointer => {
146 // TODO populate .debug_info for the pointer
147 if (typed_value.val.castTag(.decl_ref)) |payload| {
148 const decl = payload.data;
149 if (decl.analysis != .complete) return error.AnalysisFail;
150 // TODO handle the dependency of this symbol on the decl's vaddr.
151 // If the decl changes vaddr, then this symbol needs to get regenerated.
152 const vaddr = bin_file.getDeclVAddr(decl);
153 const endian = bin_file.options.target.cpu.arch.endian();
154 switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
155 16 => {
156 try code.resize(2);
157 mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);
158 },
159 32 => {
160 try code.resize(4);
161 mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);
162 },
163 64 => {
164 try code.resize(8);
165 mem.writeInt(u64, code.items[0..8], vaddr, endian);
166 },
167 else => unreachable,
145 .Pointer => switch (typed_value.ty.ptrSize()) {
146 .Slice => {
147 return Result{
148 .fail = try ErrorMsg.create(
149 bin_file.allocator,
150 src_loc,
151 "TODO implement generateSymbol for slice {}",
152 .{typed_value.val},
153 ),
154 };
155 },
156 else => {
157 // TODO populate .debug_info for the pointer
158 if (typed_value.val.castTag(.decl_ref)) |payload| {
159 const decl = payload.data;
160 if (decl.analysis != .complete) return error.AnalysisFail;
161 // TODO handle the dependency of this symbol on the decl's vaddr.
162 // If the decl changes vaddr, then this symbol needs to get regenerated.
163 const vaddr = bin_file.getDeclVAddr(decl);
164 const endian = bin_file.options.target.cpu.arch.endian();
165 switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
166 16 => {
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 = {} };
168181 }
169 return Result{ .appended = {} };
170 }
171 return Result{
172 .fail = try ErrorMsg.create(
173 bin_file.allocator,
174 src_loc,
175 "TODO implement generateSymbol for pointer {}",
176 .{typed_value.val},
177 ),
178 };
182 return Result{
183 .fail = try ErrorMsg.create(
184 bin_file.allocator,
185 src_loc,
186 "TODO implement generateSymbol for pointer {}",
187 .{typed_value.val},
188 ),
189 };
190 },
179191 },
180192 .Int => {
181193 // TODO populate .debug_info for the integer
......@@ -2244,10 +2256,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22442256 try self.register_manager.getReg(reg, null);
22452257 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
22462258 },
2247 .stack_offset => {
2259 .stack_offset => |off| {
22482260 // Here we need to emit instructions like this:
22492261 // 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);
22512263 },
22522264 .ptr_stack_offset => {
22532265 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 {
34443456 },
34453457 }
34463458 },
3447 .embedded_in_code => |code_offset| {
3448 _ = code_offset;
3449 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
3459 .embedded_in_code => {
3460 // TODO this and `.stack_offset` below need to get improved to support types greater than
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 });
34503464 },
34513465 .register => |reg| {
34523466 try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);
......@@ -3456,6 +3470,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
34563470 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
34573471 },
34583472 .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
34593476 if (stack_offset == off)
34603477 return; // Copy stack variable to itself; nothing to do.
34613478
......@@ -4161,33 +4178,48 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
41614178 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
41624179 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
41634180 switch (typed_value.ty.zigTypeTag()) {
4164 .Pointer => {
4165 if (typed_value.val.castTag(.decl_ref)) |payload| {
4166 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4167 const decl = payload.data;
4168 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
4169 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
4170 return MCValue{ .memory = got_addr };
4171 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4172 const decl = payload.data;
4173 const got_addr = blk: {
4174 const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
4175 const got = seg.sections.items[macho_file.got_section_index.?];
4176 break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes;
4177 };
4178 return MCValue{ .memory = got_addr };
4179 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4180 const decl = payload.data;
4181 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
4182 return MCValue{ .memory = got_addr };
4183 } else {
4184 return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
4181 .Pointer => switch (typed_value.ty.ptrSize()) {
4182 .Slice => {
4183 var buf: Type.Payload.ElemType = undefined;
4184 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
4185 const ptr_mcv = try self.genTypedValue(src, .{ .ty = ptr_type, .val = typed_value.val });
4186 const slice_len = typed_value.val.sliceLen();
4187 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean
4188 // the Sema code needs to use anonymous Decls or alloca instructions to store data.
4189 const ptr_imm = ptr_mcv.memory;
4190 _ = slice_len;
4191 _ = ptr_imm;
4192 // We need more general support for const data being stored in memory to make this work.
4193 return self.fail(src, "TODO codegen for const slices", .{});
4194 },
4195 else => {
4196 if (typed_value.val.castTag(.decl_ref)) |payload| {
4197 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4198 const decl = payload.data;
4199 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
4200 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
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 }
41854217 }
4186 }
4187 if (typed_value.val.tag() == .int_u64) {
4188 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
4189 }
4190 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
4218 if (typed_value.val.tag() == .int_u64) {
4219 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
4220 }
4221 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
4222 },
41914223 },
41924224 .Int => {
41934225 const info = typed_value.ty.intInfo(self.target.*);
......@@ -4264,27 +4296,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
42644296 var next_stack_offset: u32 = 0;
42654297
42664298 for (param_types) |ty, i| {
4267 switch (ty.zigTypeTag()) {
4268 .Bool, .Int => {
4269 if (!ty.hasCodeGenBits()) {
4270 assert(cc != .C);
4271 result.args[i] = .{ .none = {} };
4272 } else {
4273 const param_size = @intCast(u32, ty.abiSize(self.target.*));
4274 if (next_int_reg >= c_abi_int_param_regs.len) {
4275 result.args[i] = .{ .stack_offset = next_stack_offset };
4276 next_stack_offset += param_size;
4277 } else {
4278 const aliased_reg = registerAlias(
4279 c_abi_int_param_regs[next_int_reg],
4280 param_size,
4281 );
4282 result.args[i] = .{ .register = aliased_reg };
4283 next_int_reg += 1;
4284 }
4285 }
4286 },
4287 else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}),
4299 if (!ty.hasCodeGenBits()) {
4300 assert(cc != .C);
4301 result.args[i] = .{ .none = {} };
4302 continue;
4303 }
4304 const param_size = @intCast(u32, ty.abiSize(self.target.*));
4305 const pass_in_reg = switch (ty.zigTypeTag()) {
4306 .Bool => true,
4307 .Int => param_size <= 8,
4308 .Pointer => ty.ptrSize() != .Slice,
4309 .Optional => ty.isPtrLikeOptional(),
4310 else => false,
4311 };
4312 if (pass_in_reg) {
4313 if (next_int_reg >= c_abi_int_param_regs.len) {
4314 result.args[i] = .{ .stack_offset = next_stack_offset };
4315 next_stack_offset += param_size;
4316 } else {
4317 const aliased_reg = registerAlias(
4318 c_abi_int_param_regs[next_int_reg],
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;
42884332 }
42894333 }
42904334 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
25052505 abbrev_base_type,
25062506 DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1
25072507 1, // DW.AT_byte_size, DW.FORM_data1
2508 'b',
2509 'o',
2510 'o',
2511 'l',
2512 0, // DW.AT_name, DW.FORM_string
2508 'b', 'o', 'o', 'l', 0, // DW.AT_name, DW.FORM_string
25132509 });
25142510 },
25152511 .Int => {
......@@ -2526,8 +2522,23 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo
25262522 // DW.AT_name, DW.FORM_string
25272523 try dbg_info_buffer.writer().print("{}\x00", .{ty});
25282524 },
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 },
25292540 else => {
2530 std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty});
2541 log.err("TODO implement .debug_info for type '{}'", .{ty});
25312542 try dbg_info_buffer.append(abbrev_pad1);
25322543 },
25332544 }
test/stage2/wasm.zig-2
......@@ -587,8 +587,6 @@ pub fn addCases(ctx: *TestContext) !void {
587587 }
588588
589589 {
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
592590 var case = ctx.exe("wasm error union part 2", wasi);
593591
594592 case.addCompareOutput(