Commit 5816997ae7

Andrew Kelley <andrew@ziglang.org>
2021-07-07 23:17:04
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.
1 parent 13f04e3
Changed files (4)
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(