aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/arch/aarch64/CodeGen.zig219
-rw-r--r--src/arch/arm/CodeGen.zig196
-rw-r--r--src/arch/riscv64/CodeGen.zig168
-rw-r--r--src/arch/sparc64/CodeGen.zig174
-rw-r--r--src/arch/wasm/CodeGen.zig4
-rw-r--r--src/arch/x86_64/CodeGen.zig220
-rw-r--r--src/codegen.zig306
-rw-r--r--src/link/Coff.zig2
-rw-r--r--src/link/Elf.zig13
-rw-r--r--src/link/MachO.zig2
-rw-r--r--src/link/Plan9.zig2
-rw-r--r--src/link/Wasm.zig2
-rw-r--r--src/register_manager.zig3
13 files changed, 409 insertions, 902 deletions
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig
index 818b04f890..28f8370bd9 100644
--- a/src/arch/aarch64/CodeGen.zig
+++ b/src/arch/aarch64/CodeGen.zig
@@ -23,7 +23,7 @@ const leb128 = std.leb;
const log = std.log.scoped(.codegen);
const build_options = @import("build_options");
-const GenerateSymbolError = codegen.GenerateSymbolError;
+const CodeGenError = codegen.CodeGenError;
const Result = codegen.Result;
const DebugInfoOutput = codegen.DebugInfoOutput;
@@ -41,11 +41,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs;
const c_abi_int_return_regs = abi.c_abi_int_return_regs;
const gp = abi.RegisterClass.gp;
-const InnerError = error{
- OutOfMemory,
- CodegenFail,
- OutOfRegisters,
-};
+const InnerError = CodeGenError || error{OutOfRegisters};
gpa: Allocator,
air: Air,
@@ -337,7 +333,7 @@ pub fn generate(
liveness: Liveness,
code: *std.ArrayList(u8),
debug_output: DebugInfoOutput,
-) GenerateSymbolError!Result {
+) CodeGenError!Result {
if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
@panic("Attempted to compile for architecture that was disabled by build configuration");
}
@@ -6137,201 +6133,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
}
}
-fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
-
- // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
- if (tv.ty.zigTypeTag() == .Pointer) blk: {
- if (tv.ty.castPtrToFn()) |_| break :blk;
- if (!tv.ty.elemType2().hasRuntimeBits()) {
- return MCValue.none;
- }
- }
-
- const mod = self.bin_file.options.module.?;
- const decl = mod.declPtr(decl_index);
- mod.markDeclAlive(decl);
-
- if (self.bin_file.cast(link.File.Elf)) |elf_file| {
- const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
- const atom = elf_file.getAtom(atom_index);
- return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
- } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
- const atom = try macho_file.getOrCreateAtomForDecl(decl_index);
- const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;
- return MCValue{ .linker_load = .{
- .type = .got,
- .sym_index = sym_index,
- } };
- } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
- const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
- const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
- return MCValue{ .linker_load = .{
- .type = .got,
- .sym_index = sym_index,
- } };
- } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
- const decl_block_index = try p9.seeDecl(decl_index);
- const decl_block = p9.getDeclBlock(decl_block_index);
- const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
- return MCValue{ .memory = got_addr };
- } else {
- return self.fail("TODO codegen non-ELF const Decl pointer", .{});
- }
-}
-
-fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
- log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
- const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| {
- return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
- };
- if (self.bin_file.cast(link.File.Elf)) |elf_file| {
- return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
- } else if (self.bin_file.cast(link.File.MachO)) |_| {
- return MCValue{ .linker_load = .{
- .type = .direct,
- .sym_index = local_sym_index,
- } };
- } else if (self.bin_file.cast(link.File.Coff)) |_| {
- return MCValue{ .linker_load = .{
- .type = .direct,
- .sym_index = local_sym_index,
- } };
- } else if (self.bin_file.cast(link.File.Plan9)) |_| {
- return self.fail("TODO lower unnamed const in Plan9", .{});
- } else {
- return self.fail("TODO lower unnamed const", .{});
- }
-}
-
fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
- var typed_value = arg_tv;
- if (typed_value.val.castTag(.runtime_value)) |rt| {
- typed_value.val = rt.data;
- }
- log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });
- if (typed_value.val.isUndef())
- return MCValue{ .undef = {} };
-
- if (typed_value.val.castTag(.decl_ref)) |payload| {
- return self.lowerDeclRef(typed_value, payload.data);
- }
- if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
- return self.lowerDeclRef(typed_value, payload.data.decl_index);
- }
- const target = self.target.*;
-
- switch (typed_value.ty.zigTypeTag()) {
- .Pointer => switch (typed_value.ty.ptrSize()) {
- .Slice => {},
- else => {
- switch (typed_value.val.tag()) {
- .int_u64 => {
- return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
- },
- else => {},
- }
- },
- },
- .Int => {
- const info = typed_value.ty.intInfo(self.target.*);
- if (info.bits <= 64) {
- const unsigned = switch (info.signedness) {
- .signed => blk: {
- const signed = typed_value.val.toSignedInt(target);
- break :blk @bitCast(u64, signed);
- },
- .unsigned => typed_value.val.toUnsignedInt(target),
- };
-
- return MCValue{ .immediate = unsigned };
- }
- },
- .Bool => {
- return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
+ const mcv: MCValue = switch (try codegen.genTypedValue(
+ self.bin_file,
+ self.src_loc,
+ arg_tv,
+ self.mod_fn.owner_decl,
+ )) {
+ .mcv => |mcv| switch (mcv) {
+ .none => .none,
+ .undef => .undef,
+ .linker_load => |ll| .{ .linker_load = ll },
+ .immediate => |imm| .{ .immediate = imm },
+ .memory => |addr| .{ .memory = addr },
},
- .Optional => {
- if (typed_value.ty.isPtrLikeOptional()) {
- if (typed_value.val.isNull())
- return MCValue{ .immediate = 0 };
-
- var buf: Type.Payload.ElemType = undefined;
- return self.genTypedValue(.{
- .ty = typed_value.ty.optionalChild(&buf),
- .val = typed_value.val,
- });
- } else if (typed_value.ty.abiSize(self.target.*) == 1) {
- return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
- }
- },
- .Enum => {
- if (typed_value.val.castTag(.enum_field_index)) |field_index| {
- switch (typed_value.ty.tag()) {
- .enum_simple => {
- return MCValue{ .immediate = field_index.data };
- },
- .enum_full, .enum_nonexhaustive => {
- const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
- if (enum_full.values.count() != 0) {
- const tag_val = enum_full.values.keys()[field_index.data];
- return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
- } else {
- return MCValue{ .immediate = field_index.data };
- }
- },
- else => unreachable,
- }
- } else {
- var int_tag_buffer: Type.Payload.Bits = undefined;
- const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
- return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
- }
+ .fail => |msg| {
+ self.err_msg = msg;
+ return error.CodegenFail;
},
- .ErrorSet => {
- switch (typed_value.val.tag()) {
- .@"error" => {
- const err_name = typed_value.val.castTag(.@"error").?.data.name;
- const module = self.bin_file.options.module.?;
- const global_error_set = module.global_error_set;
- const error_index = global_error_set.get(err_name).?;
- return MCValue{ .immediate = error_index };
- },
- else => {
- // In this case we are rendering an error union which has a 0 bits payload.
- return MCValue{ .immediate = 0 };
- },
- }
- },
- .ErrorUnion => {
- const error_type = typed_value.ty.errorUnionSet();
- const payload_type = typed_value.ty.errorUnionPayload();
-
- const is_pl = typed_value.val.errorUnionIsPayload();
-
- if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
- // We use the error type directly as the type.
- const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero);
- return self.genTypedValue(.{ .ty = error_type, .val = err_val });
- }
-
- return self.lowerUnnamedConst(typed_value);
- },
-
- .ComptimeInt => unreachable, // semantic analysis prevents this
- .ComptimeFloat => unreachable, // semantic analysis prevents this
- .Type => unreachable,
- .EnumLiteral => unreachable,
- .Void => unreachable,
- .NoReturn => unreachable,
- .Undefined => unreachable,
- .Null => unreachable,
- .Opaque => unreachable,
-
- else => {},
- }
-
- return self.lowerUnnamedConst(typed_value);
+ };
+ return mcv;
}
const CallMCValues = struct {
diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig
index ceabe70438..7d8708c44d 100644
--- a/src/arch/arm/CodeGen.zig
+++ b/src/arch/arm/CodeGen.zig
@@ -24,7 +24,7 @@ const log = std.log.scoped(.codegen);
const build_options = @import("build_options");
const Result = codegen.Result;
-const GenerateSymbolError = codegen.GenerateSymbolError;
+const CodeGenError = codegen.CodeGenError;
const DebugInfoOutput = codegen.DebugInfoOutput;
const bits = @import("bits.zig");
@@ -42,11 +42,7 @@ const c_abi_int_param_regs = abi.c_abi_int_param_regs;
const c_abi_int_return_regs = abi.c_abi_int_return_regs;
const gp = abi.RegisterClass.gp;
-const InnerError = error{
- OutOfMemory,
- CodegenFail,
- OutOfRegisters,
-};
+const InnerError = CodeGenError || error{OutOfRegisters};
gpa: Allocator,
air: Air,
@@ -343,7 +339,7 @@ pub fn generate(
liveness: Liveness,
code: *std.ArrayList(u8),
debug_output: DebugInfoOutput,
-) GenerateSymbolError!Result {
+) CodeGenError!Result {
if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
@panic("Attempted to compile for architecture that was disabled by build configuration");
}
@@ -6087,178 +6083,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
}
}
-fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
-
- const mod = self.bin_file.options.module.?;
- const decl = mod.declPtr(decl_index);
- mod.markDeclAlive(decl);
-
- if (self.bin_file.cast(link.File.Elf)) |elf_file| {
- const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
- const atom = elf_file.getAtom(atom_index);
- return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
- } else if (self.bin_file.cast(link.File.MachO)) |_| {
- unreachable; // unsupported architecture for MachO
- } else if (self.bin_file.cast(link.File.Coff)) |_| {
- return self.fail("TODO codegen COFF const Decl pointer", .{});
- } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
- const decl_block_index = try p9.seeDecl(decl_index);
- const decl_block = p9.getDeclBlock(decl_block_index);
- const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
- return MCValue{ .memory = got_addr };
- } else {
- return self.fail("TODO codegen non-ELF const Decl pointer", .{});
- }
-
- _ = tv;
-}
-
-fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
- const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| {
- return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
- };
- if (self.bin_file.cast(link.File.Elf)) |elf_file| {
- return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
- } else if (self.bin_file.cast(link.File.MachO)) |_| {
- unreachable;
- } else if (self.bin_file.cast(link.File.Coff)) |_| {
- return self.fail("TODO lower unnamed const in COFF", .{});
- } else if (self.bin_file.cast(link.File.Plan9)) |_| {
- return self.fail("TODO lower unnamed const in Plan9", .{});
- } else {
- return self.fail("TODO lower unnamed const", .{});
- }
-}
-
fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
- var typed_value = arg_tv;
- if (typed_value.val.castTag(.runtime_value)) |rt| {
- typed_value.val = rt.data;
- }
- log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });
- if (typed_value.val.isUndef())
- return MCValue{ .undef = {} };
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
-
- if (typed_value.val.castTag(.decl_ref)) |payload| {
- return self.lowerDeclRef(typed_value, payload.data);
- }
- if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
- return self.lowerDeclRef(typed_value, payload.data.decl_index);
- }
- const target = self.target.*;
-
- switch (typed_value.ty.zigTypeTag()) {
- .Pointer => switch (typed_value.ty.ptrSize()) {
- .Slice => {},
- else => {
- switch (typed_value.val.tag()) {
- .int_u64 => {
- return MCValue{ .immediate = @intCast(u32, typed_value.val.toUnsignedInt(target)) };
- },
- else => {},
- }
- },
- },
- .Int => {
- const info = typed_value.ty.intInfo(self.target.*);
- if (info.bits <= ptr_bits) {
- const unsigned = switch (info.signedness) {
- .signed => blk: {
- const signed = @intCast(i32, typed_value.val.toSignedInt(target));
- break :blk @bitCast(u32, signed);
- },
- .unsigned => @intCast(u32, typed_value.val.toUnsignedInt(target)),
- };
-
- return MCValue{ .immediate = unsigned };
- } else {
- return self.lowerUnnamedConst(typed_value);
- }
+ const mcv: MCValue = switch (try codegen.genTypedValue(
+ self.bin_file,
+ self.src_loc,
+ arg_tv,
+ self.mod_fn.owner_decl,
+ )) {
+ .mcv => |mcv| switch (mcv) {
+ .none => .none,
+ .undef => .undef,
+ .linker_load => unreachable, // TODO
+ .immediate => |imm| .{ .immediate = @truncate(u32, imm) },
+ .memory => |addr| .{ .memory = addr },
},
- .Bool => {
- return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
- },
- .Optional => {
- if (typed_value.ty.isPtrLikeOptional()) {
- if (typed_value.val.isNull())
- return MCValue{ .immediate = 0 };
-
- var buf: Type.Payload.ElemType = undefined;
- return self.genTypedValue(.{
- .ty = typed_value.ty.optionalChild(&buf),
- .val = typed_value.val,
- });
- } else if (typed_value.ty.abiSize(self.target.*) == 1) {
- return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
- }
+ .fail => |msg| {
+ self.err_msg = msg;
+ return error.CodegenFail;
},
- .Enum => {
- if (typed_value.val.castTag(.enum_field_index)) |field_index| {
- switch (typed_value.ty.tag()) {
- .enum_simple => {
- return MCValue{ .immediate = field_index.data };
- },
- .enum_full, .enum_nonexhaustive => {
- const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
- if (enum_full.values.count() != 0) {
- const tag_val = enum_full.values.keys()[field_index.data];
- return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
- } else {
- return MCValue{ .immediate = field_index.data };
- }
- },
- else => unreachable,
- }
- } else {
- var int_tag_buffer: Type.Payload.Bits = undefined;
- const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
- return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
- }
- },
- .ErrorSet => {
- switch (typed_value.val.tag()) {
- .@"error" => {
- const err_name = typed_value.val.castTag(.@"error").?.data.name;
- const module = self.bin_file.options.module.?;
- const global_error_set = module.global_error_set;
- const error_index = global_error_set.get(err_name).?;
- return MCValue{ .immediate = error_index };
- },
- else => {
- // In this case we are rendering an error union which has a 0 bits payload.
- return MCValue{ .immediate = 0 };
- },
- }
- },
- .ErrorUnion => {
- const error_type = typed_value.ty.errorUnionSet();
- const payload_type = typed_value.ty.errorUnionPayload();
- const is_pl = typed_value.val.errorUnionIsPayload();
-
- if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
- // We use the error type directly as the type.
- const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero);
- return self.genTypedValue(.{ .ty = error_type, .val = err_val });
- }
- },
-
- .ComptimeInt => unreachable, // semantic analysis prevents this
- .ComptimeFloat => unreachable, // semantic analysis prevents this
- .Type => unreachable,
- .EnumLiteral => unreachable,
- .Void => unreachable,
- .NoReturn => unreachable,
- .Undefined => unreachable,
- .Null => unreachable,
- .Opaque => unreachable,
-
- else => {},
- }
-
- return self.lowerUnnamedConst(typed_value);
+ };
+ return mcv;
}
const CallMCValues = struct {
diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig
index afcf4b0bb7..c7191145f9 100644
--- a/src/arch/riscv64/CodeGen.zig
+++ b/src/arch/riscv64/CodeGen.zig
@@ -21,10 +21,11 @@ const DW = std.dwarf;
const leb128 = std.leb;
const log = std.log.scoped(.codegen);
const build_options = @import("build_options");
+const codegen = @import("../../codegen.zig");
-const Result = @import("../../codegen.zig").Result;
-const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
-const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
+const CodeGenError = codegen.CodeGenError;
+const Result = codegen.Result;
+const DebugInfoOutput = codegen.DebugInfoOutput;
const bits = @import("bits.zig");
const abi = @import("abi.zig");
@@ -35,11 +36,7 @@ const Instruction = abi.Instruction;
const callee_preserved_regs = abi.callee_preserved_regs;
const gp = abi.RegisterClass.gp;
-const InnerError = error{
- OutOfMemory,
- CodegenFail,
- OutOfRegisters,
-};
+const InnerError = CodeGenError || error{OutOfRegisters};
gpa: Allocator,
air: Air,
@@ -225,7 +222,7 @@ pub fn generate(
liveness: Liveness,
code: *std.ArrayList(u8),
debug_output: DebugInfoOutput,
-) GenerateSymbolError!Result {
+) CodeGenError!Result {
if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
@panic("Attempted to compile for architecture that was disabled by build configuration");
}
@@ -2552,145 +2549,26 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
}
}
-fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
- const mod = self.bin_file.options.module.?;
- const decl = mod.declPtr(decl_index);
- mod.markDeclAlive(decl);
- if (self.bin_file.cast(link.File.Elf)) |elf_file| {
- const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
- const atom = elf_file.getAtom(atom_index);
- return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
- } else if (self.bin_file.cast(link.File.MachO)) |_| {
- unreachable;
- } else if (self.bin_file.cast(link.File.Coff)) |_| {
- return self.fail("TODO codegen COFF const Decl pointer", .{});
- } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
- const decl_block_index = try p9.seeDecl(decl_index);
- const decl_block = p9.getDeclBlock(decl_block_index);
- const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
- return MCValue{ .memory = got_addr };
- } else {
- return self.fail("TODO codegen non-ELF const Decl pointer", .{});
- }
- _ = tv;
-}
-
fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
- if (typed_value.val.isUndef())
- return MCValue{ .undef = {} };
-
- if (typed_value.val.castTag(.decl_ref)) |payload| {
- return self.lowerDeclRef(typed_value, payload.data);
- }
- if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
- return self.lowerDeclRef(typed_value, payload.data.decl_index);
- }
- const target = self.target.*;
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- switch (typed_value.ty.zigTypeTag()) {
- .Pointer => switch (typed_value.ty.ptrSize()) {
- .Slice => {
- var buf: Type.SlicePtrFieldTypeBuffer = undefined;
- const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
- const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
- const mod = self.bin_file.options.module.?;
- const slice_len = typed_value.val.sliceLen(mod);
- // 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("TODO codegen for const slices", .{});
- },
- else => {
- if (typed_value.val.tag() == .int_u64) {
- return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
- }
- return self.fail("TODO codegen more kinds of const pointers", .{});
- },
- },
- .Int => {
- const info = typed_value.ty.intInfo(self.target.*);
- if (info.bits > ptr_bits or info.signedness == .signed) {
- return self.fail("TODO const int bigger than ptr and signed int", .{});
- }
- return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
+ const mcv: MCValue = switch (try codegen.genTypedValue(
+ self.bin_file,
+ self.src_loc,
+ typed_value,
+ self.mod_fn.owner_decl,
+ )) {
+ .mcv => |mcv| switch (mcv) {
+ .none => .none,
+ .undef => .undef,
+ .linker_load => unreachable, // TODO
+ .immediate => |imm| .{ .immediate = imm },
+ .memory => |addr| .{ .memory = addr },
},
- .Bool => {
- return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
+ .fail => |msg| {
+ self.err_msg = msg;
+ return error.CodegenFail;
},
- .ComptimeInt => unreachable, // semantic analysis prevents this
- .ComptimeFloat => unreachable, // semantic analysis prevents this
- .Optional => {
- if (typed_value.ty.isPtrLikeOptional()) {
- if (typed_value.val.isNull())
- return MCValue{ .immediate = 0 };
-
- var buf: Type.Payload.ElemType = undefined;
- return self.genTypedValue(.{
- .ty = typed_value.ty.optionalChild(&buf),
- .val = typed_value.val,
- });
- } else if (typed_value.ty.abiSize(self.target.*) == 1) {
- return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
- }
- return self.fail("TODO non pointer optionals", .{});
- },
- .Enum => {
- if (typed_value.val.castTag(.enum_field_index)) |field_index| {
- switch (typed_value.ty.tag()) {
- .enum_simple => {
- return MCValue{ .immediate = field_index.data };
- },
- .enum_full, .enum_nonexhaustive => {
- const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
- if (enum_full.values.count() != 0) {
- const tag_val = enum_full.values.keys()[field_index.data];
- return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
- } else {
- return MCValue{ .immediate = field_index.data };
- }
- },
- else => unreachable,
- }
- } else {
- var int_tag_buffer: Type.Payload.Bits = undefined;
- const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
- return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
- }
- },
- .ErrorSet => {
- switch (typed_value.val.tag()) {
- .@"error" => {
- const err_name = typed_value.val.castTag(.@"error").?.data.name;
- const module = self.bin_file.options.module.?;
- const global_error_set = module.global_error_set;
- const error_index = global_error_set.get(err_name).?;
- return MCValue{ .immediate = error_index };
- },
- else => {
- // In this case we are rendering an error union which has a 0 bits payload.
- return MCValue{ .immediate = 0 };
- },
- }
- },
- .ErrorUnion => {
- const error_type = typed_value.ty.errorUnionSet();
- const payload_type = typed_value.ty.errorUnionPayload();
- const sub_val = typed_value.val.castTag(.eu_payload).?.data;
-
- if (!payload_type.hasRuntimeBits()) {
- // We use the error type directly as the type.
- return self.genTypedValue(.{ .ty = error_type, .val = sub_val });
- }
-
- return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty.fmtDebug()});
- },
- else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}),
- }
+ };
+ return mcv;
}
const CallMCValues = struct {
diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig
index c8f77fe702..dc1a450e9a 100644
--- a/src/arch/sparc64/CodeGen.zig
+++ b/src/arch/sparc64/CodeGen.zig
@@ -19,7 +19,7 @@ const Mir = @import("Mir.zig");
const Emit = @import("Emit.zig");
const Liveness = @import("../../Liveness.zig");
const Type = @import("../../type.zig").Type;
-const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
+const CodeGenError = codegen.CodeGenError;
const Result = @import("../../codegen.zig").Result;
const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
@@ -38,11 +38,7 @@ const gp = abi.RegisterClass.gp;
const Self = @This();
-const InnerError = error{
- OutOfMemory,
- CodegenFail,
- OutOfRegisters,
-};
+const InnerError = CodeGenError || error{OutOfRegisters};
const RegisterView = enum(u1) {
caller,
@@ -265,7 +261,7 @@ pub fn generate(
liveness: Liveness,
code: *std.ArrayList(u8),
debug_output: DebugInfoOutput,
-) GenerateSymbolError!Result {
+) CodeGenError!Result {
if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
@panic("Attempted to compile for architecture that was disabled by build configuration");
}
@@ -3898,133 +3894,25 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t
}
fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
- var tv = typed_value;
- log.debug("genTypedValue: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
-
- if (tv.val.castTag(.runtime_value)) |rt| {
- tv.val = rt.data;
- }
-
- if (tv.val.isUndef())
- return MCValue{ .undef = {} };
-
- if (tv.val.castTag(.decl_ref)) |payload| {
- return self.lowerDeclRef(tv, payload.data);
- }
- if (tv.val.castTag(.decl_ref_mut)) |payload| {
- return self.lowerDeclRef(tv, payload.data.decl_index);
- }
- const target = self.target.*;
-
- switch (tv.ty.zigTypeTag()) {
- .Pointer => switch (tv.ty.ptrSize()) {
- .Slice => {},
- else => {
- switch (tv.val.tag()) {
- .int_u64 => {
- return MCValue{ .immediate = tv.val.toUnsignedInt(target) };
- },
- else => {},
- }
- },
- },
- .Bool => {
- return MCValue{ .immediate = @boolToInt(tv.val.toBool()) };
- },
- .Int => {
- const info = tv.ty.intInfo(self.target.*);
- if (info.bits <= 64) {
- const unsigned = switch (info.signedness) {
- .signed => blk: {
- const signed = tv.val.toSignedInt(target);
- break :blk @bitCast(u64, signed);
- },
- .unsigned => tv.val.toUnsignedInt(target),
- };
-
- return MCValue{ .immediate = unsigned };
- } else {
- return self.fail("TODO implement int genTypedValue of > 64 bits", .{});
- }
- },
- .Optional => {
- if (tv.ty.isPtrLikeOptional()) {
- if (tv.val.isNull())
- return MCValue{ .immediate = 0 };
-
- var buf: Type.Payload.ElemType = undefined;
- return self.genTypedValue(.{
- .ty = tv.ty.optionalChild(&buf),
- .val = tv.val,
- });
- } else if (tv.ty.abiSize(self.target.*) == 1) {
- return MCValue{ .immediate = @boolToInt(tv.val.isNull()) };
- }
- },
- .Enum => {
- if (tv.val.castTag(.enum_field_index)) |field_index| {
- switch (tv.ty.tag()) {
- .enum_simple => {
- return MCValue{ .immediate = field_index.data };
- },
- .enum_full, .enum_nonexhaustive => {
- const enum_full = tv.ty.cast(Type.Payload.EnumFull).?.data;
- if (enum_full.values.count() != 0) {
- const tag_val = enum_full.values.keys()[field_index.data];
- return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
- } else {
- return MCValue{ .immediate = field_index.data };
- }
- },
- else => unreachable,
- }
- } else {
- var int_tag_buffer: Type.Payload.Bits = undefined;
- const int_tag_ty = tv.ty.intTagType(&int_tag_buffer);
- return self.genTypedValue(.{ .ty = int_tag_ty, .val = tv.val });
- }
- },
- .ErrorSet => {
- const err_name = tv.val.castTag(.@"error").?.data.name;
- const module = self.bin_file.options.module.?;
- const global_error_set = module.global_error_set;
- const error_index = global_error_set.get(err_name).?;
- return MCValue{ .immediate = error_index };
+ const mcv: MCValue = switch (try codegen.genTypedValue(
+ self.bin_file,
+ self.src_loc,
+ typed_value,
+ self.mod_fn.owner_decl,
+ )) {
+ .mcv => |mcv| switch (mcv) {
+ .none => .none,
+ .undef => .undef,
+ .linker_load => unreachable, // TODO
+ .immediate => |imm| .{ .immediate = imm },
+ .memory => |addr| .{ .memory = addr },
},
- .ErrorUnion => {
- const error_type = tv.ty.errorUnionSet();
- const payload_type = tv.ty.errorUnionPayload();
-
- if (tv.val.castTag(.eu_payload)) |pl| {
- if (!payload_type.hasRuntimeBits()) {
- // We use the error type directly as the type.
- return MCValue{ .immediate = 0 };
- }
-
- _ = pl;
- return self.fail("TODO implement error union const of type '{}' (non-error)", .{tv.ty.fmtDebug()});
- } else {
- if (!payload_type.hasRuntimeBits()) {
- // We use the error type directly as the type.
- return self.genTypedValue(.{ .ty = error_type, .val = tv.val });
- }
-
- return self.fail("TODO implement error union const of type '{}' (error)", .{tv.ty.fmtDebug()});
- }
+ .fail => |msg| {
+ self.err_msg = msg;
+ return error.CodegenFail;
},
- .ComptimeInt => unreachable, // semantic analysis prevents this
- .ComptimeFloat => unreachable, // semantic analysis prevents this
- .Type => unreachable,
- .EnumLiteral => unreachable,
- .Void => unreachable,
- .NoReturn => unreachable,
- .Undefined => unreachable,
- .Null => unreachable,
- .Opaque => unreachable,
- else => {},
- }
-
- return self.fail("TODO implement const of type '{}'", .{tv.ty.fmtDebug()});
+ };
+ return mcv;
}
fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
@@ -4200,28 +4088,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
}
}
-fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
- // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
- if (tv.ty.zigTypeTag() == .Pointer) blk: {
- if (tv.ty.castPtrToFn()) |_| break :blk;
- if (!tv.ty.elemType2().hasRuntimeBits()) {
- return MCValue.none;
- }
- }
-
- const mod = self.bin_file.options.module.?;
- const decl = mod.declPtr(decl_index);
-
- mod.markDeclAlive(decl);
- if (self.bin_file.cast(link.File.Elf)) |elf_file| {
- const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
- const atom = elf_file.getAtom(atom_index);
- return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
- } else {
- return self.fail("TODO codegen non-ELF const Decl pointer", .{});
- }
-}
-
fn minMax(
self: *Self,
tag: Air.Inst.Tag,
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index 2f191fd834..5cd6c95690 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -733,8 +733,6 @@ const InnerError = error{
OutOfMemory,
/// An error occurred when trying to lower AIR to MIR.
CodegenFail,
- /// Can occur when dereferencing a pointer that points to a `Decl` of which the analysis has failed
- AnalysisFail,
/// Compiler implementation could not handle a large integer.
Overflow,
};
@@ -1164,7 +1162,7 @@ pub fn generate(
liveness: Liveness,
code: *std.ArrayList(u8),
debug_output: codegen.DebugInfoOutput,
-) codegen.GenerateSymbolError!codegen.Result {
+) codegen.CodeGenError!codegen.Result {
_ = src_loc;
var code_gen: CodeGen = .{
.gpa = bin_file.allocator,
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index 53d38f520a..a2c11b332b 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -12,12 +12,12 @@ const trace = @import("../../tracy.zig").trace;
const Air = @import("../../Air.zig");
const Allocator = mem.Allocator;
+const CodeGenError = codegen.CodeGenError;
const Compilation = @import("../../Compilation.zig");
const DebugInfoOutput = codegen.DebugInfoOutput;
const DW = std.dwarf;
const ErrorMsg = Module.ErrorMsg;
const Result = codegen.Result;
-const GenerateSymbolError = codegen.GenerateSymbolError;
const Emit = @import("Emit.zig");
const Liveness = @import("../../Liveness.zig");
const Mir = @import("Mir.zig");
@@ -40,11 +40,7 @@ const Register = bits.Register;
const gp = abi.RegisterClass.gp;
const sse = abi.RegisterClass.sse;
-const InnerError = error{
- OutOfMemory,
- CodegenFail,
- OutOfRegisters,
-};
+const InnerError = CodeGenError || error{OutOfRegisters};
gpa: Allocator,
air: Air,
@@ -257,7 +253,7 @@ pub fn generate(
liveness: Liveness,
code: *std.ArrayList(u8),
debug_output: DebugInfoOutput,
-) GenerateSymbolError!Result {
+) CodeGenError!Result {
if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
@panic("Attempted to compile for architecture that was disabled by build configuration");
}
@@ -6683,7 +6679,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
}
-pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
+fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
// First section of indexes correspond to a set number of constant values.
const ref_int = @enumToInt(inst);
if (ref_int < Air.Inst.Ref.typed_value_map.len) {
@@ -6752,200 +6748,26 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV
return mcv;
}
-fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
- log.debug("lowerDeclRef: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
-
- // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
- if (tv.ty.zigTypeTag() == .Pointer) blk: {
- if (tv.ty.castPtrToFn()) |_| break :blk;
- if (!tv.ty.elemType2().hasRuntimeBits()) {
- return MCValue.none;
- }
- }
-
- const module = self.bin_file.options.module.?;
- const decl = module.declPtr(decl_index);
- module.markDeclAlive(decl);
-
- if (self.bin_file.cast(link.File.Elf)) |elf_file| {
- const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
- const atom = elf_file.getAtom(atom_index);
- return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
- } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
- const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
- const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
- return MCValue{ .linker_load = .{
- .type = .got,
- .sym_index = sym_index,
- } };
- } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
- const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
- const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
- return MCValue{ .linker_load = .{
- .type = .got,
- .sym_index = sym_index,
- } };
- } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
- const decl_block_index = try p9.seeDecl(decl_index);
- const decl_block = p9.getDeclBlock(decl_block_index);
- const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
- return MCValue{ .memory = got_addr };
- } else {
- return self.fail("TODO codegen non-ELF const Decl pointer", .{});
- }
-}
-
-fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
- log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
- const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| {
- return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
- };
- if (self.bin_file.cast(link.File.Elf)) |elf_file| {
- return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
- } else if (self.bin_file.cast(link.File.MachO)) |_| {
- return MCValue{ .linker_load = .{
- .type = .direct,
- .sym_index = local_sym_index,
- } };
- } else if (self.bin_file.cast(link.File.Coff)) |_| {
- return MCValue{ .linker_load = .{
- .type = .direct,
- .sym_index = local_sym_index,
- } };
- } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
- const got_index = local_sym_index; // the plan9 backend returns the got_index
- const got_addr = p9.bases.data + got_index * ptr_bytes;
- return MCValue{ .memory = got_addr };
- } else {
- return self.fail("TODO lower unnamed const", .{});
- }
-}
-
fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
- var typed_value = arg_tv;
- if (typed_value.val.castTag(.runtime_value)) |rt| {
- typed_value.val = rt.data;
- }
- log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });
- if (typed_value.val.isUndef())
- return MCValue{ .undef = {} };
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
-
- if (typed_value.val.castTag(.decl_ref)) |payload| {
- return self.lowerDeclRef(typed_value, payload.data);
- }
- if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
- return self.lowerDeclRef(typed_value, payload.data.decl_index);
- }
-
- const target = self.target.*;
-
- switch (typed_value.ty.zigTypeTag()) {
- .Void => return MCValue{ .none = {} },
- .Pointer => switch (typed_value.ty.ptrSize()) {
- .Slice => {},
- else => {
- switch (typed_value.val.tag()) {
- .int_u64 => {
- return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
- },
- else => {},
- }
- },
+ const mcv: MCValue = switch (try codegen.genTypedValue(
+ self.bin_file,
+ self.src_loc,
+ arg_tv,
+ self.mod_fn.owner_decl,
+ )) {
+ .mcv => |mcv| switch (mcv) {
+ .none => .none,
+ .undef => .undef,
+ .linker_load => |ll| .{ .linker_load = ll },
+ .immediate => |imm| .{ .immediate = imm },
+ .memory => |addr| .{ .memory = addr },
},
- .Int => {
- const info = typed_value.ty.intInfo(self.target.*);
- if (info.bits <= ptr_bits and info.signedness == .signed) {
- return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt(target)) };
- }
- if (!(info.bits > ptr_bits or info.signedness == .signed)) {
- return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
- }
- },
- .Bool => {
- return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
- },
- .Optional => {
- if (typed_value.ty.isPtrLikeOptional()) {
- if (typed_value.val.isNull())
- return MCValue{ .immediate = 0 };
-
- var buf: Type.Payload.ElemType = undefined;
- return self.genTypedValue(.{
- .ty = typed_value.ty.optionalChild(&buf),
- .val = typed_value.val,
- });
- } else if (typed_value.ty.abiSize(self.target.*) == 1) {
- return MCValue{ .immediate = @boolToInt(!typed_value.val.isNull()) };
- }
- },
- .Enum => {
- if (typed_value.val.castTag(.enum_field_index)) |field_index| {
- switch (typed_value.ty.tag()) {
- .enum_simple => {
- return MCValue{ .immediate = field_index.data };
- },
- .enum_full, .enum_nonexhaustive => {
- const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
- if (enum_full.values.count() != 0) {
- const tag_val = enum_full.values.keys()[field_index.data];
- return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
- } else {
- return MCValue{ .immediate = field_index.data };
- }
- },
- else => unreachable,
- }
- } else {
- var int_tag_buffer: Type.Payload.Bits = undefined;
- const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
- return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
- }
+ .fail => |msg| {
+ self.err_msg = msg;
+ return error.CodegenFail;
},
- .ErrorSet => {
- switch (typed_value.val.tag()) {
- .@"error" => {
- const err_name = typed_value.val.castTag(.@"error").?.data.name;
- const module = self.bin_file.options.module.?;
- const global_error_set = module.global_error_set;
- const error_index = global_error_set.get(err_name).?;
- return MCValue{ .immediate = error_index };
- },
- else => {
- // In this case we are rendering an error union which has a 0 bits payload.
- return MCValue{ .immediate = 0 };
- },
- }
- },
- .ErrorUnion => {
- const error_type = typed_value.ty.errorUnionSet();
- const payload_type = typed_value.ty.errorUnionPayload();
- const is_pl = typed_value.val.errorUnionIsPayload();
-
- if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
- // We use the error type directly as the type.
- const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero);
- return self.genTypedValue(.{ .ty = error_type, .val = err_val });
- }
- },
-
- .ComptimeInt => unreachable,
- .ComptimeFloat => unreachable,
- .Type => unreachable,
- .EnumLiteral => unreachable,
- .NoReturn => unreachable,
- .Undefined => unreachable,
- .Null => unreachable,
- .Opaque => unreachable,
-
- else => {},
- }
-
- return self.lowerUnnamedConst(typed_value);
+ };
+ return mcv;
}
const CallMCValues = struct {
diff --git a/src/codegen.zig b/src/codegen.zig
index df7ceff1f0..a91795841c 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -29,11 +29,10 @@ pub const Result = union(enum) {
fail: *ErrorMsg,
};
-pub const GenerateSymbolError = error{
+pub const CodeGenError = error{
OutOfMemory,
Overflow,
- /// A Decl that this symbol depends on had a semantic analysis failure.
- AnalysisFail,
+ CodegenFail,
};
pub const DebugInfoOutput = union(enum) {
@@ -63,19 +62,6 @@ pub const DebugInfoOutput = union(enum) {
none,
};
-/// Helper struct to denote that the value is in memory but requires a linker relocation fixup:
-/// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc)
-/// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc)
-/// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc)
-pub const LinkerLoad = struct {
- type: enum {
- got,
- direct,
- import,
- },
- sym_index: u32,
-};
-
pub fn generateFunction(
bin_file: *link.File,
src_loc: Module.SrcLoc,
@@ -84,7 +70,7 @@ pub fn generateFunction(
liveness: Liveness,
code: *std.ArrayList(u8),
debug_output: DebugInfoOutput,
-) GenerateSymbolError!Result {
+) CodeGenError!Result {
switch (bin_file.options.target.cpu.arch) {
.arm,
.armeb,
@@ -120,7 +106,7 @@ pub fn generateSymbol(
code: *std.ArrayList(u8),
debug_output: DebugInfoOutput,
reloc_info: RelocInfo,
-) GenerateSymbolError!Result {
+) CodeGenError!Result {
const tracy = trace(@src());
defer tracy.end();
@@ -823,7 +809,7 @@ fn lowerDeclRef(
code: *std.ArrayList(u8),
debug_output: DebugInfoOutput,
reloc_info: RelocInfo,
-) GenerateSymbolError!Result {
+) CodeGenError!Result {
const target = bin_file.options.target;
const module = bin_file.options.module.?;
if (typed_value.ty.isSlice()) {
@@ -880,6 +866,288 @@ fn lowerDeclRef(
return Result.ok;
}
+/// Helper struct to denote that the value is in memory but requires a linker relocation fixup:
+/// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc)
+/// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc)
+/// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc)
+pub const LinkerLoad = struct {
+ type: enum {
+ got,
+ direct,
+ import,
+ },
+ sym_index: u32,
+};
+
+pub const GenResult = union(enum) {
+ mcv: MCValue,
+ fail: *ErrorMsg,
+
+ const MCValue = union(enum) {
+ none,
+ undef,
+ /// The bit-width of the immediate may be smaller than `u64`. For example, on 32-bit targets
+ /// such as ARM, the immediate will never exceed 32-bits.
+ immediate: u64,
+ linker_load: LinkerLoad,
+ /// Direct by-address reference to memory location.
+ memory: u64,
+ };
+
+ fn mcv(val: MCValue) GenResult {
+ return .{ .mcv = val };
+ }
+
+ fn fail(
+ gpa: Allocator,
+ src_loc: Module.SrcLoc,
+ comptime format: []const u8,
+ args: anytype,
+ ) Allocator.Error!GenResult {
+ const msg = try ErrorMsg.create(gpa, src_loc, format, args);
+ return .{ .fail = msg };
+ }
+};
+
+fn genDeclRef(
+ bin_file: *link.File,
+ src_loc: Module.SrcLoc,
+ tv: TypedValue,
+ decl_index: Module.Decl.Index,
+) CodeGenError!GenResult {
+ log.debug("genDeclRef: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
+
+ const target = bin_file.options.target;
+ const ptr_bits = target.cpu.arch.ptrBitWidth();
+ const ptr_bytes: u64 = @divExact(ptr_bits, 8);
+
+ const module = bin_file.options.module.?;
+ const decl = module.declPtr(decl_index);
+
+ if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) {
+ const imm: u64 = switch (ptr_bytes) {
+ 1 => 0xaa,
+ 2 => 0xaaaa,
+ 4 => 0xaaaaaaaa,
+ 8 => 0xaaaaaaaaaaaaaaaa,
+ else => unreachable,
+ };
+ return GenResult.mcv(.{ .immediate = imm });
+ }
+
+ // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
+ if (tv.ty.zigTypeTag() == .Pointer) blk: {
+ if (tv.ty.castPtrToFn()) |_| break :blk;
+ if (!tv.ty.elemType2().hasRuntimeBits()) {
+ return GenResult.mcv(.none);
+ }
+ }
+
+ module.markDeclAlive(decl);
+
+ if (bin_file.cast(link.File.Elf)) |elf_file| {
+ const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
+ const atom = elf_file.getAtom(atom_index);
+ return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) });
+ } else if (bin_file.cast(link.File.MachO)) |macho_file| {
+ const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
+ const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
+ return GenResult.mcv(.{ .linker_load = .{
+ .type = .got,
+ .sym_index = sym_index,
+ } });
+ } else if (bin_file.cast(link.File.Coff)) |coff_file| {
+ const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
+ const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
+ return GenResult.mcv(.{ .linker_load = .{
+ .type = .got,
+ .sym_index = sym_index,
+ } });
+ } else if (bin_file.cast(link.File.Plan9)) |p9| {
+ const decl_block_index = try p9.seeDecl(decl_index);
+ const decl_block = p9.getDeclBlock(decl_block_index);
+ const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
+ return GenResult.mcv(.{ .memory = got_addr });
+ } else {
+ return GenResult.fail(bin_file.allocator, src_loc, "TODO genDeclRef for target {}", .{target});
+ }
+}
+
+fn genUnnamedConst(
+ bin_file: *link.File,
+ src_loc: Module.SrcLoc,
+ tv: TypedValue,
+ owner_decl_index: Module.Decl.Index,
+) CodeGenError!GenResult {
+ log.debug("genUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
+
+ const target = bin_file.options.target;
+ const local_sym_index = bin_file.lowerUnnamedConst(tv, owner_decl_index) catch |err| {
+ return GenResult.fail(bin_file.allocator, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)});
+ };
+ if (bin_file.cast(link.File.Elf)) |elf_file| {
+ return GenResult.mcv(.{ .memory = elf_file.getSymbol(local_sym_index).st_value });
+ } else if (bin_file.cast(link.File.MachO)) |_| {
+ return GenResult.mcv(.{ .linker_load = .{
+ .type = .direct,
+ .sym_index = local_sym_index,
+ } });
+ } else if (bin_file.cast(link.File.Coff)) |_| {
+ return GenResult.mcv(.{ .linker_load = .{
+ .type = .direct,
+ .sym_index = local_sym_index,
+ } });
+ } else if (bin_file.cast(link.File.Plan9)) |p9| {
+ const ptr_bits = target.cpu.arch.ptrBitWidth();
+ const ptr_bytes: u64 = @divExact(ptr_bits, 8);
+ const got_index = local_sym_index; // the plan9 backend returns the got_index
+ const got_addr = p9.bases.data + got_index * ptr_bytes;
+ return GenResult.mcv(.{ .memory = got_addr });
+ } else {
+ return GenResult.fail(bin_file.allocator, src_loc, "TODO genUnnamedConst for target {}", .{target});
+ }
+}
+
+pub fn genTypedValue(
+ bin_file: *link.File,
+ src_loc: Module.SrcLoc,
+ arg_tv: TypedValue,
+ owner_decl_index: Module.Decl.Index,
+) CodeGenError!GenResult {
+ var typed_value = arg_tv;
+ if (typed_value.val.castTag(.runtime_value)) |rt| {
+ typed_value.val = rt.data;
+ }
+
+ log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });
+
+ if (typed_value.val.isUndef())
+ return GenResult.mcv(.undef);
+
+ const target = bin_file.options.target;
+ const ptr_bits = target.cpu.arch.ptrBitWidth();
+
+ if (typed_value.val.castTag(.decl_ref)) |payload| {
+ return genDeclRef(bin_file, src_loc, typed_value, payload.data);
+ }
+ if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
+ return genDeclRef(bin_file, src_loc, typed_value, payload.data.decl_index);
+ }
+
+ switch (typed_value.ty.zigTypeTag()) {
+ .Void => return GenResult.mcv(.none),
+ .Pointer => switch (typed_value.ty.ptrSize()) {
+ .Slice => {},
+ else => {
+ switch (typed_value.val.tag()) {
+ .int_u64 => {
+ return GenResult.mcv(.{ .immediate = typed_value.val.toUnsignedInt(target) });
+ },
+ else => {},
+ }
+ },
+ },
+ .Int => {
+ const info = typed_value.ty.intInfo(target);
+ if (info.bits <= ptr_bits) {
+ const unsigned = switch (info.signedness) {
+ .signed => @bitCast(u64, typed_value.val.toSignedInt(target)),
+ .unsigned => typed_value.val.toUnsignedInt(target),
+ };
+ return GenResult.mcv(.{ .immediate = unsigned });
+ }
+ },
+ .Bool => {
+ return GenResult.mcv(.{ .immediate = @boolToInt(typed_value.val.toBool()) });
+ },
+ .Optional => {
+ if (typed_value.ty.isPtrLikeOptional()) {
+ if (typed_value.val.isNull())
+ return GenResult.mcv(.{ .immediate = 0 });
+
+ var buf: Type.Payload.ElemType = undefined;
+ return genTypedValue(bin_file, src_loc, .{
+ .ty = typed_value.ty.optionalChild(&buf),
+ .val = typed_value.val,
+ }, owner_decl_index);
+ } else if (typed_value.ty.abiSize(target) == 1) {
+ return GenResult.mcv(.{ .immediate = @boolToInt(!typed_value.val.isNull()) });
+ }
+ },
+ .Enum => {
+ if (typed_value.val.castTag(.enum_field_index)) |field_index| {
+ switch (typed_value.ty.tag()) {
+ .enum_simple => {
+ return GenResult.mcv(.{ .immediate = field_index.data });
+ },
+ .enum_full, .enum_nonexhaustive => {
+ const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
+ if (enum_full.values.count() != 0) {
+ const tag_val = enum_full.values.keys()[field_index.data];
+ return genTypedValue(bin_file, src_loc, .{
+ .ty = enum_full.tag_ty,
+ .val = tag_val,
+ }, owner_decl_index);
+ } else {
+ return GenResult.mcv(.{ .immediate = field_index.data });
+ }
+ },
+ else => unreachable,
+ }
+ } else {
+ var int_tag_buffer: Type.Payload.Bits = undefined;
+ const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
+ return genTypedValue(bin_file, src_loc, .{
+ .ty = int_tag_ty,
+ .val = typed_value.val,
+ }, owner_decl_index);
+ }
+ },
+ .ErrorSet => {
+ switch (typed_value.val.tag()) {
+ .@"error" => {
+ const err_name = typed_value.val.castTag(.@"error").?.data.name;
+ const module = bin_file.options.module.?;
+ const global_error_set = module.global_error_set;
+ const error_index = global_error_set.get(err_name).?;
+ return GenResult.mcv(.{ .immediate = error_index });
+ },
+ else => {
+ // In this case we are rendering an error union which has a 0 bits payload.
+ return GenResult.mcv(.{ .immediate = 0 });
+ },
+ }
+ },
+ .ErrorUnion => {
+ const error_type = typed_value.ty.errorUnionSet();
+ const payload_type = typed_value.ty.errorUnionPayload();
+ const is_pl = typed_value.val.errorUnionIsPayload();
+
+ if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
+ // We use the error type directly as the type.
+ const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero);
+ return genTypedValue(bin_file, src_loc, .{
+ .ty = error_type,
+ .val = err_val,
+ }, owner_decl_index);
+ }
+ },
+
+ .ComptimeInt => unreachable,
+ .ComptimeFloat => unreachable,
+ .Type => unreachable,
+ .EnumLiteral => unreachable,
+ .NoReturn => unreachable,
+ .Undefined => unreachable,
+ .Null => unreachable,
+ .Opaque => unreachable,
+
+ else => {},
+ }
+
+ return genUnnamedConst(bin_file, src_loc, typed_value, owner_decl_index);
+}
+
pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 {
const payload_align = payload_ty.abiAlignment(target);
const error_align = Type.anyerror.abiAlignment(target);
diff --git a/src/link/Coff.zig b/src/link/Coff.zig
index c0ac7e0b88..f210f2f2b3 100644
--- a/src/link/Coff.zig
+++ b/src/link/Coff.zig
@@ -1060,7 +1060,7 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
decl.analysis = .codegen_failure;
try mod.failed_decls.put(mod.gpa, decl_index, em);
log.err("{s}", .{em.msg});
- return error.AnalysisFail;
+ return error.CodegenFail;
},
};
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
index 1a9d594c56..a91722d072 100644
--- a/src/link/Elf.zig
+++ b/src/link/Elf.zig
@@ -2097,9 +2097,16 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void {
// Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
const local_sym_index = atom.getSymbolIndex().?;
+ log.debug("adding %{d} to local symbols free list", .{local_sym_index});
self.local_symbol_free_list.append(gpa, local_sym_index) catch {};
- self.local_symbols.items[local_sym_index].st_info = 0;
- self.local_symbols.items[local_sym_index].st_shndx = 0;
+ self.local_symbols.items[local_sym_index] = .{
+ .st_name = 0,
+ .st_info = 0,
+ .st_other = 0,
+ .st_shndx = 0,
+ .st_value = 0,
+ .st_size = 0,
+ };
_ = self.atom_by_index_table.remove(local_sym_index);
self.getAtomPtr(atom_index).local_sym_index = 0;
@@ -2618,7 +2625,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
decl.analysis = .codegen_failure;
try mod.failed_decls.put(mod.gpa, decl_index, em);
log.err("{s}", .{em.msg});
- return error.AnalysisFail;
+ return error.CodegenFail;
},
};
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index 7c1d4776af..eaf16e4009 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -2089,7 +2089,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
decl.analysis = .codegen_failure;
try module.failed_decls.put(module.gpa, decl_index, em);
log.err("{s}", .{em.msg});
- return error.AnalysisFail;
+ return error.CodegenFail;
},
};
diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig
index 87e3ca5c22..cf6e4f8418 100644
--- a/src/link/Plan9.zig
+++ b/src/link/Plan9.zig
@@ -377,7 +377,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I
decl.analysis = .codegen_failure;
try mod.failed_decls.put(mod.gpa, decl_index, em);
log.err("{s}", .{em.msg});
- return error.AnalysisFail;
+ return error.CodegenFail;
},
};
// duped_code is freed when the unnamed const is freed
diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig
index 74525138a1..513b567210 100644
--- a/src/link/Wasm.zig
+++ b/src/link/Wasm.zig
@@ -1265,7 +1265,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
.fail => |em| {
decl.analysis = .codegen_failure;
try mod.failed_decls.put(mod.gpa, decl_index, em);
- return error.AnalysisFail;
+ return error.CodegenFail;
},
};
};
diff --git a/src/register_manager.zig b/src/register_manager.zig
index 2fe0cd2b6a..4d16348c27 100644
--- a/src/register_manager.zig
+++ b/src/register_manager.zig
@@ -19,6 +19,9 @@ pub const AllocateRegistersError = error{
/// Can happen when spilling an instruction in codegen runs out of
/// memory, so we propagate that error
OutOfMemory,
+ /// Can happen when spilling an instruction in codegen triggers integer
+ /// overflow, so we propagate that error
+ Overflow,
/// Can happen when spilling an instruction triggers a codegen
/// error, so we propagate that error
CodegenFail,