Commit 4931b8dc93
Changed files (13)
src
arch
test
src/arch/aarch64/CodeGen.zig
@@ -592,6 +592,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.ctz => try self.airCtz(inst),
.popcount => try self.airPopcount(inst),
.tag_name => try self.airTagName(inst),
+ .error_name => try self.airErrorName(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -2557,6 +2558,16 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ un_op, .none, .none });
}
+fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
+ const un_op = self.air.instructions.items(.data)[inst].un_op;
+ const operand = try self.resolveInst(un_op);
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
+ _ = operand;
+ return self.fail("TODO implement airErrorName for aarch64", .{});
+ };
+ return self.finishAir(inst, result, .{ un_op, .none, .none });
+}
+
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);
src/arch/arm/CodeGen.zig
@@ -590,6 +590,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.ctz => try self.airCtz(inst),
.popcount => try self.airPopcount(inst),
.tag_name => try self.airTagName(inst),
+ .error_name => try self.airErrorName(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -3682,6 +3683,16 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ un_op, .none, .none });
}
+fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
+ const un_op = self.air.instructions.items(.data)[inst].un_op;
+ const operand = try self.resolveInst(un_op);
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
+ _ = operand;
+ return self.fail("TODO implement airErrorName for arm", .{});
+ };
+ return self.finishAir(inst, result, .{ un_op, .none, .none });
+}
+
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);
src/arch/riscv64/CodeGen.zig
@@ -571,6 +571,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.ctz => try self.airCtz(inst),
.popcount => try self.airPopcount(inst),
.tag_name => try self.airTagName(inst),
+ .error_name => try self.airErrorName(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -2056,6 +2057,16 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ un_op, .none, .none });
}
+fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
+ const un_op = self.air.instructions.items(.data)[inst].un_op;
+ const operand = try self.resolveInst(un_op);
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
+ _ = operand;
+ return self.fail("TODO implement airErrorName for riscv64", .{});
+ };
+ return self.finishAir(inst, result, .{ un_op, .none, .none });
+}
+
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);
src/arch/x86_64/CodeGen.zig
@@ -635,6 +635,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.ctz => try self.airCtz(inst),
.popcount => try self.airPopcount(inst),
.tag_name => try self.airTagName(inst),
+ .error_name, => try self.airErrorName(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -3649,6 +3650,16 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ un_op, .none, .none });
}
+fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
+ const un_op = self.air.instructions.items(.data)[inst].un_op;
+ const operand = try self.resolveInst(un_op);
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
+ _ = operand;
+ return self.fail("TODO implement airErrorName for x86_64", .{});
+ };
+ return self.finishAir(inst, result, .{ un_op, .none, .none });
+}
+
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);
src/codegen/c.zig
@@ -1244,6 +1244,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.ctz => try airBuiltinCall(f, inst, "ctz"),
.popcount => try airBuiltinCall(f, inst, "popcount"),
.tag_name => try airTagName(f, inst),
+ .error_name => try airErrorName(f, inst),
.int_to_float,
.float_to_int,
@@ -2998,6 +2999,22 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
//return local;
}
+fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.liveness.isUnused(inst)) return CValue.none;
+
+ const un_op = f.air.instructions.items(.data)[inst].un_op;
+ const writer = f.object.writer();
+ const inst_ty = f.air.typeOfIndex(inst);
+ const operand = try f.resolveInst(un_op);
+ const local = try f.allocLocal(inst_ty, .Const);
+
+ try writer.writeAll(" = ");
+
+ _ = operand;
+ _ = local;
+ return f.fail("TODO: C backend: implement airErrorName", .{});
+}
+
fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
return switch (order) {
.Unordered => "memory_order_relaxed",
src/codegen/llvm.zig
@@ -181,6 +181,9 @@ pub const Object = struct {
/// The backing memory for `type_map`. Periodically garbage collected after flush().
/// The code for doing the periodical GC is not yet implemented.
type_map_arena: std.heap.ArenaAllocator,
+ /// The LLVM global table which holds the names corresponding to Zig errors. Note that the values
+ /// are not added until flushModule, when all errors in the compilation are known.
+ error_name_table: ?*const llvm.Value,
pub const TypeMap = std.HashMapUnmanaged(
Type,
@@ -269,6 +272,7 @@ pub const Object = struct {
.decl_map = .{},
.type_map = .{},
.type_map_arena = std.heap.ArenaAllocator.init(gpa),
+ .error_name_table = null,
};
}
@@ -298,7 +302,60 @@ pub const Object = struct {
return slice.ptr;
}
+ fn genErrorNameTable(self: *Object, comp: *Compilation) !void {
+ // If self.error_name_table is null, there was no instruction that actually referenced the error table.
+ const error_name_table_ptr_global = self.error_name_table orelse return;
+
+ const mod = comp.bin_file.options.module.?;
+ const target = mod.getTarget();
+
+ const llvm_ptr_ty = self.context.intType(8).pointerType(0); // TODO: Address space
+ const llvm_usize_ty = self.context.intType(target.cpu.arch.ptrBitWidth());
+ const type_fields = [_]*const llvm.Type{
+ llvm_ptr_ty,
+ llvm_usize_ty,
+ };
+ const llvm_slice_ty = self.context.structType(&type_fields, type_fields.len, .False);
+ const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
+ const slice_alignment = slice_ty.abiAlignment(target);
+
+ const error_name_list = mod.error_name_list.items;
+ const llvm_errors = try comp.gpa.alloc(*const llvm.Value, error_name_list.len);
+ defer comp.gpa.free(llvm_errors);
+
+ llvm_errors[0] = llvm_slice_ty.getUndef();
+ for (llvm_errors[1..]) |*llvm_error, i| {
+ const name = error_name_list[1..][i];
+ const str_init = self.context.constString(name.ptr, @intCast(c_uint, name.len), .False);
+ const str_global = self.llvm_module.addGlobal(str_init.typeOf(), "");
+ str_global.setInitializer(str_init);
+ str_global.setLinkage(.Private);
+ str_global.setGlobalConstant(.True);
+ str_global.setUnnamedAddr(.True);
+ str_global.setAlignment(1);
+
+ const slice_fields = [_]*const llvm.Value{
+ str_global.constBitCast(llvm_ptr_ty),
+ llvm_usize_ty.constInt(name.len, .False),
+ };
+ llvm_error.* = llvm_slice_ty.constNamedStruct(&slice_fields, slice_fields.len);
+ }
+
+ const error_name_table_init = llvm_slice_ty.constArray(llvm_errors.ptr, @intCast(c_uint, error_name_list.len));
+
+ const error_name_table_global = self.llvm_module.addGlobal(error_name_table_init.typeOf(), "");
+ error_name_table_global.setInitializer(error_name_table_init);
+ error_name_table_global.setLinkage(.Private);
+ error_name_table_global.setGlobalConstant(.True);
+ error_name_table_global.setUnnamedAddr(.True);
+ error_name_table_global.setAlignment(slice_alignment); // TODO: Dont hardcode
+
+ const error_name_table_ptr = error_name_table_global.constBitCast(llvm_slice_ty.pointerType(0)); // TODO: Address space
+ error_name_table_ptr_global.setInitializer(error_name_table_ptr);
+ }
+
pub fn flushModule(self: *Object, comp: *Compilation) !void {
+ try self.genErrorNameTable(comp);
if (comp.verbose_llvm_ir) {
self.llvm_module.dump();
}
@@ -2031,6 +2088,7 @@ pub const FuncGen = struct {
.ctz => try self.airClzCtz(inst, "cttz"),
.popcount => try self.airPopCount(inst, "ctpop"),
.tag_name => try self.airTagName(inst),
+ .error_name => try self.airErrorName(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -4279,6 +4337,40 @@ pub const FuncGen = struct {
return fn_val;
}
+ fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const un_op = self.air.instructions.items(.data)[inst].un_op;
+ const operand = try self.resolveInst(un_op);
+
+ const error_name_table_ptr = try self.getErrorNameTable();
+ const error_name_table = self.builder.buildLoad(error_name_table_ptr, "");
+ const indices = [_]*const llvm.Value{operand};
+ const error_name_ptr = self.builder.buildInBoundsGEP(error_name_table, &indices, indices.len, "");
+ return self.builder.buildLoad(error_name_ptr, "");
+ }
+
+ fn getErrorNameTable(self: *FuncGen) !*const llvm.Value {
+ if (self.dg.object.error_name_table) |table| {
+ return table;
+ }
+
+ const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
+ const slice_alignment = slice_ty.abiAlignment(self.dg.module.getTarget());
+ const llvm_slice_ty = try self.dg.llvmType(slice_ty);
+ const llvm_slice_ptr_ty = llvm_slice_ty.pointerType(0); // TODO: Address space
+
+ const error_name_table_global = self.dg.object.llvm_module.addGlobal(llvm_slice_ptr_ty, "__zig_err_name_table");
+ error_name_table_global.setInitializer(llvm_slice_ptr_ty.getUndef());
+ error_name_table_global.setLinkage(.Private);
+ error_name_table_global.setGlobalConstant(.True);
+ error_name_table_global.setUnnamedAddr(.True);
+ error_name_table_global.setAlignment(slice_alignment);
+
+ self.dg.object.error_name_table = error_name_table_global;
+ return error_name_table_global;
+ }
+
/// Assumes the optional is not pointer-like and payload has bits.
fn optIsNonNull(self: *FuncGen, opt_handle: *const llvm.Value, is_by_ref: bool) *const llvm.Value {
if (is_by_ref) {
src/Air.zig
@@ -501,6 +501,10 @@ pub const Inst = struct {
/// Uses the `un_op` field.
tag_name,
+ /// Given an error value, return the error name. Result type is always `[:0] const u8`.
+ /// Uses the `un_op` field.
+ error_name,
+
pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
return switch (op) {
.lt => .cmp_lt,
@@ -816,7 +820,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.bool_to_int => return Type.initTag(.u1),
- .tag_name => return Type.initTag(.const_slice_u8_sentinel_0),
+ .tag_name, .error_name => return Type.initTag(.const_slice_u8_sentinel_0),
.call => {
const callee_ty = air.typeOf(datas[inst].pl_op.operand);
src/Liveness.zig
@@ -334,6 +334,7 @@ fn analyzeInst(
.ret,
.ret_load,
.tag_name,
+ .error_name,
=> {
const operand = inst_datas[inst].un_op;
return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
src/print_air.zig
@@ -156,6 +156,7 @@ const Writer = struct {
.ret,
.ret_load,
.tag_name,
+ .error_name,
=> try w.writeUnOp(s, inst),
.breakpoint,
src/Sema.zig
@@ -10478,7 +10478,18 @@ fn zirBoolToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirErrorName", .{});
+ _ = src;
+ const operand = sema.resolveInst(inst_data.operand);
+ const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+
+ if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
+ const bytes = val.castTag(.@"error").?.data.name;
+ return sema.addStrLit(block, bytes);
+ }
+
+ // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass
+ // might be able to resolve the result at compile time.
+ return block.addUnOp(.error_name, operand);
}
fn zirUnaryMath(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
test/behavior/error_llvm.zig
@@ -0,0 +1,24 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const mem = std.mem;
+
+fn gimmeItBroke() anyerror {
+ return error.ItBroke;
+}
+
+test "@errorName" {
+ try expect(mem.eql(u8, @errorName(error.AnError), "AnError"));
+ try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
+ try expect(mem.eql(u8, @errorName(gimmeItBroke()), "ItBroke"));
+}
+
+test "@errorName sentinel length matches slice length" {
+ const name = testBuiltinErrorName(error.FooBar);
+ const length: usize = 6;
+ try expect(length == std.mem.indexOfSentinel(u8, 0, name.ptr));
+ try expect(length == name.len);
+}
+
+pub fn testBuiltinErrorName(err: anyerror) [:0]const u8 {
+ return @errorName(err);
+}
test/behavior/error_stage1.zig
@@ -4,27 +4,6 @@ const expectError = std.testing.expectError;
const expectEqual = std.testing.expectEqual;
const mem = std.mem;
-fn gimmeItBroke() anyerror {
- return error.ItBroke;
-}
-
-test "@errorName" {
- try expect(mem.eql(u8, @errorName(error.AnError), "AnError"));
- try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
- try expect(mem.eql(u8, @errorName(gimmeItBroke()), "ItBroke"));
-}
-
-test "@errorName sentinel length matches slice length" {
- const name = testBuiltinErrorName(error.FooBar);
- const length: usize = 6;
- try expectEqual(length, std.mem.indexOfSentinel(u8, 0, name.ptr));
- try expectEqual(length, name.len);
-}
-
-pub fn testBuiltinErrorName(err: anyerror) [:0]const u8 {
- return @errorName(err);
-}
-
test "error union type " {
try testErrorUnionType();
comptime try testErrorUnionType();
test/behavior.zig
@@ -90,6 +90,7 @@ test {
_ = @import("behavior/bugs/9584.zig");
_ = @import("behavior/cast_llvm.zig");
_ = @import("behavior/enum_llvm.zig");
+ _ = @import("behavior/error_llvm.zig");
_ = @import("behavior/eval.zig");
_ = @import("behavior/floatop.zig");
_ = @import("behavior/fn.zig");