authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2022-01-08 02:02:01+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-08 14:30:11-05:00
log4931b8dc93ee4a99a415dffab03d400e95d1a90a
tree8586196d647638d70dd7c3c3e657f9adeabdb39c
parent3f586781b6c8d698468cc58ab64797097323c253

stage2: @errorName sema+llvm


13 files changed, 197 insertions(+), 23 deletions(-)

src/Air.zig+5-1
......@@ -501,6 +501,10 @@ pub const Inst = struct {
501501 /// Uses the `un_op` field.
502502 tag_name,
503503
504 /// Given an error value, return the error name. Result type is always `[:0] const u8`.
505 /// Uses the `un_op` field.
506 error_name,
507
504508 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
505509 return switch (op) {
506510 .lt => .cmp_lt,
......@@ -816,7 +820,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
816820
817821 .bool_to_int => return Type.initTag(.u1),
818822
819 .tag_name => return Type.initTag(.const_slice_u8_sentinel_0),
823 .tag_name, .error_name => return Type.initTag(.const_slice_u8_sentinel_0),
820824
821825 .call => {
822826 const callee_ty = air.typeOf(datas[inst].pl_op.operand);
src/Liveness.zig+1
......@@ -334,6 +334,7 @@ fn analyzeInst(
334334 .ret,
335335 .ret_load,
336336 .tag_name,
337 .error_name,
337338 => {
338339 const operand = inst_datas[inst].un_op;
339340 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
src/Sema.zig+12-1
......@@ -10478,7 +10478,18 @@ fn zirBoolToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1047810478fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1047910479 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1048010480 const src = inst_data.src();
10481 return sema.fail(block, src, "TODO: Sema.zirErrorName", .{});
10481 _ = src;
10482 const operand = sema.resolveInst(inst_data.operand);
10483 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
10484
10485 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
10486 const bytes = val.castTag(.@"error").?.data.name;
10487 return sema.addStrLit(block, bytes);
10488 }
10489
10490 // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass
10491 // might be able to resolve the result at compile time.
10492 return block.addUnOp(.error_name, operand);
1048210493}
1048310494
1048410495fn zirUnaryMath(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
src/arch/aarch64/CodeGen.zig+11
......@@ -592,6 +592,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
592592 .ctz => try self.airCtz(inst),
593593 .popcount => try self.airPopcount(inst),
594594 .tag_name => try self.airTagName(inst),
595 .error_name => try self.airErrorName(inst),
595596
596597 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
597598 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -2557,6 +2558,16 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
25572558 return self.finishAir(inst, result, .{ un_op, .none, .none });
25582559}
25592560
2561fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
2562 const un_op = self.air.instructions.items(.data)[inst].un_op;
2563 const operand = try self.resolveInst(un_op);
2564 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
2565 _ = operand;
2566 return self.fail("TODO implement airErrorName for aarch64", .{});
2567 };
2568 return self.finishAir(inst, result, .{ un_op, .none, .none });
2569}
2570
25602571fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
25612572 // First section of indexes correspond to a set number of constant values.
25622573 const ref_int = @enumToInt(inst);
src/arch/arm/CodeGen.zig+11
......@@ -590,6 +590,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
590590 .ctz => try self.airCtz(inst),
591591 .popcount => try self.airPopcount(inst),
592592 .tag_name => try self.airTagName(inst),
593 .error_name => try self.airErrorName(inst),
593594
594595 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
595596 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -3682,6 +3683,16 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
36823683 return self.finishAir(inst, result, .{ un_op, .none, .none });
36833684}
36843685
3686fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
3687 const un_op = self.air.instructions.items(.data)[inst].un_op;
3688 const operand = try self.resolveInst(un_op);
3689 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
3690 _ = operand;
3691 return self.fail("TODO implement airErrorName for arm", .{});
3692 };
3693 return self.finishAir(inst, result, .{ un_op, .none, .none });
3694}
3695
36853696fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
36863697 // First section of indexes correspond to a set number of constant values.
36873698 const ref_int = @enumToInt(inst);
src/arch/riscv64/CodeGen.zig+11
......@@ -571,6 +571,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
571571 .ctz => try self.airCtz(inst),
572572 .popcount => try self.airPopcount(inst),
573573 .tag_name => try self.airTagName(inst),
574 .error_name => try self.airErrorName(inst),
574575
575576 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
576577 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -2056,6 +2057,16 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
20562057 return self.finishAir(inst, result, .{ un_op, .none, .none });
20572058}
20582059
2060fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
2061 const un_op = self.air.instructions.items(.data)[inst].un_op;
2062 const operand = try self.resolveInst(un_op);
2063 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
2064 _ = operand;
2065 return self.fail("TODO implement airErrorName for riscv64", .{});
2066 };
2067 return self.finishAir(inst, result, .{ un_op, .none, .none });
2068}
2069
20592070fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
20602071 // First section of indexes correspond to a set number of constant values.
20612072 const ref_int = @enumToInt(inst);
src/arch/x86_64/CodeGen.zig+11
......@@ -635,6 +635,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
635635 .ctz => try self.airCtz(inst),
636636 .popcount => try self.airPopcount(inst),
637637 .tag_name => try self.airTagName(inst),
638 .error_name, => try self.airErrorName(inst),
638639
639640 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
640641 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -3649,6 +3650,16 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
36493650 return self.finishAir(inst, result, .{ un_op, .none, .none });
36503651}
36513652
3653fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
3654 const un_op = self.air.instructions.items(.data)[inst].un_op;
3655 const operand = try self.resolveInst(un_op);
3656 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
3657 _ = operand;
3658 return self.fail("TODO implement airErrorName for x86_64", .{});
3659 };
3660 return self.finishAir(inst, result, .{ un_op, .none, .none });
3661}
3662
36523663fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
36533664 // First section of indexes correspond to a set number of constant values.
36543665 const ref_int = @enumToInt(inst);
src/codegen/c.zig+17
......@@ -1244,6 +1244,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
12441244 .ctz => try airBuiltinCall(f, inst, "ctz"),
12451245 .popcount => try airBuiltinCall(f, inst, "popcount"),
12461246 .tag_name => try airTagName(f, inst),
1247 .error_name => try airErrorName(f, inst),
12471248
12481249 .int_to_float,
12491250 .float_to_int,
......@@ -2998,6 +2999,22 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
29982999 //return local;
29993000}
30003001
3002fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
3003 if (f.liveness.isUnused(inst)) return CValue.none;
3004
3005 const un_op = f.air.instructions.items(.data)[inst].un_op;
3006 const writer = f.object.writer();
3007 const inst_ty = f.air.typeOfIndex(inst);
3008 const operand = try f.resolveInst(un_op);
3009 const local = try f.allocLocal(inst_ty, .Const);
3010
3011 try writer.writeAll(" = ");
3012
3013 _ = operand;
3014 _ = local;
3015 return f.fail("TODO: C backend: implement airErrorName", .{});
3016}
3017
30013018fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
30023019 return switch (order) {
30033020 .Unordered => "memory_order_relaxed",
src/codegen/llvm.zig+92
......@@ -181,6 +181,9 @@ pub const Object = struct {
181181 /// The backing memory for `type_map`. Periodically garbage collected after flush().
182182 /// The code for doing the periodical GC is not yet implemented.
183183 type_map_arena: std.heap.ArenaAllocator,
184 /// The LLVM global table which holds the names corresponding to Zig errors. Note that the values
185 /// are not added until flushModule, when all errors in the compilation are known.
186 error_name_table: ?*const llvm.Value,
184187
185188 pub const TypeMap = std.HashMapUnmanaged(
186189 Type,
......@@ -269,6 +272,7 @@ pub const Object = struct {
269272 .decl_map = .{},
270273 .type_map = .{},
271274 .type_map_arena = std.heap.ArenaAllocator.init(gpa),
275 .error_name_table = null,
272276 };
273277 }
274278
......@@ -298,7 +302,60 @@ pub const Object = struct {
298302 return slice.ptr;
299303 }
300304
305 fn genErrorNameTable(self: *Object, comp: *Compilation) !void {
306 // If self.error_name_table is null, there was no instruction that actually referenced the error table.
307 const error_name_table_ptr_global = self.error_name_table orelse return;
308
309 const mod = comp.bin_file.options.module.?;
310 const target = mod.getTarget();
311
312 const llvm_ptr_ty = self.context.intType(8).pointerType(0); // TODO: Address space
313 const llvm_usize_ty = self.context.intType(target.cpu.arch.ptrBitWidth());
314 const type_fields = [_]*const llvm.Type{
315 llvm_ptr_ty,
316 llvm_usize_ty,
317 };
318 const llvm_slice_ty = self.context.structType(&type_fields, type_fields.len, .False);
319 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
320 const slice_alignment = slice_ty.abiAlignment(target);
321
322 const error_name_list = mod.error_name_list.items;
323 const llvm_errors = try comp.gpa.alloc(*const llvm.Value, error_name_list.len);
324 defer comp.gpa.free(llvm_errors);
325
326 llvm_errors[0] = llvm_slice_ty.getUndef();
327 for (llvm_errors[1..]) |*llvm_error, i| {
328 const name = error_name_list[1..][i];
329 const str_init = self.context.constString(name.ptr, @intCast(c_uint, name.len), .False);
330 const str_global = self.llvm_module.addGlobal(str_init.typeOf(), "");
331 str_global.setInitializer(str_init);
332 str_global.setLinkage(.Private);
333 str_global.setGlobalConstant(.True);
334 str_global.setUnnamedAddr(.True);
335 str_global.setAlignment(1);
336
337 const slice_fields = [_]*const llvm.Value{
338 str_global.constBitCast(llvm_ptr_ty),
339 llvm_usize_ty.constInt(name.len, .False),
340 };
341 llvm_error.* = llvm_slice_ty.constNamedStruct(&slice_fields, slice_fields.len);
342 }
343
344 const error_name_table_init = llvm_slice_ty.constArray(llvm_errors.ptr, @intCast(c_uint, error_name_list.len));
345
346 const error_name_table_global = self.llvm_module.addGlobal(error_name_table_init.typeOf(), "");
347 error_name_table_global.setInitializer(error_name_table_init);
348 error_name_table_global.setLinkage(.Private);
349 error_name_table_global.setGlobalConstant(.True);
350 error_name_table_global.setUnnamedAddr(.True);
351 error_name_table_global.setAlignment(slice_alignment); // TODO: Dont hardcode
352
353 const error_name_table_ptr = error_name_table_global.constBitCast(llvm_slice_ty.pointerType(0)); // TODO: Address space
354 error_name_table_ptr_global.setInitializer(error_name_table_ptr);
355 }
356
301357 pub fn flushModule(self: *Object, comp: *Compilation) !void {
358 try self.genErrorNameTable(comp);
302359 if (comp.verbose_llvm_ir) {
303360 self.llvm_module.dump();
304361 }
......@@ -2031,6 +2088,7 @@ pub const FuncGen = struct {
20312088 .ctz => try self.airClzCtz(inst, "cttz"),
20322089 .popcount => try self.airPopCount(inst, "ctpop"),
20332090 .tag_name => try self.airTagName(inst),
2091 .error_name => try self.airErrorName(inst),
20342092
20352093 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
20362094 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -4279,6 +4337,40 @@ pub const FuncGen = struct {
42794337 return fn_val;
42804338 }
42814339
4340 fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
4341 if (self.liveness.isUnused(inst)) return null;
4342
4343 const un_op = self.air.instructions.items(.data)[inst].un_op;
4344 const operand = try self.resolveInst(un_op);
4345
4346 const error_name_table_ptr = try self.getErrorNameTable();
4347 const error_name_table = self.builder.buildLoad(error_name_table_ptr, "");
4348 const indices = [_]*const llvm.Value{operand};
4349 const error_name_ptr = self.builder.buildInBoundsGEP(error_name_table, &indices, indices.len, "");
4350 return self.builder.buildLoad(error_name_ptr, "");
4351 }
4352
4353 fn getErrorNameTable(self: *FuncGen) !*const llvm.Value {
4354 if (self.dg.object.error_name_table) |table| {
4355 return table;
4356 }
4357
4358 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
4359 const slice_alignment = slice_ty.abiAlignment(self.dg.module.getTarget());
4360 const llvm_slice_ty = try self.dg.llvmType(slice_ty);
4361 const llvm_slice_ptr_ty = llvm_slice_ty.pointerType(0); // TODO: Address space
4362
4363 const error_name_table_global = self.dg.object.llvm_module.addGlobal(llvm_slice_ptr_ty, "__zig_err_name_table");
4364 error_name_table_global.setInitializer(llvm_slice_ptr_ty.getUndef());
4365 error_name_table_global.setLinkage(.Private);
4366 error_name_table_global.setGlobalConstant(.True);
4367 error_name_table_global.setUnnamedAddr(.True);
4368 error_name_table_global.setAlignment(slice_alignment);
4369
4370 self.dg.object.error_name_table = error_name_table_global;
4371 return error_name_table_global;
4372 }
4373
42824374 /// Assumes the optional is not pointer-like and payload has bits.
42834375 fn optIsNonNull(self: *FuncGen, opt_handle: *const llvm.Value, is_by_ref: bool) *const llvm.Value {
42844376 if (is_by_ref) {
src/print_air.zig+1
......@@ -156,6 +156,7 @@ const Writer = struct {
156156 .ret,
157157 .ret_load,
158158 .tag_name,
159 .error_name,
159160 => try w.writeUnOp(s, inst),
160161
161162 .breakpoint,
test/behavior.zig+1
......@@ -90,6 +90,7 @@ test {
9090 _ = @import("behavior/bugs/9584.zig");
9191 _ = @import("behavior/cast_llvm.zig");
9292 _ = @import("behavior/enum_llvm.zig");
93 _ = @import("behavior/error_llvm.zig");
9394 _ = @import("behavior/eval.zig");
9495 _ = @import("behavior/floatop.zig");
9596 _ = @import("behavior/fn.zig");
test/behavior/error_llvm.zig created+24
......@@ -0,0 +1,24 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const mem = std.mem;
4
5fn gimmeItBroke() anyerror {
6 return error.ItBroke;
7}
8
9test "@errorName" {
10 try expect(mem.eql(u8, @errorName(error.AnError), "AnError"));
11 try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
12 try expect(mem.eql(u8, @errorName(gimmeItBroke()), "ItBroke"));
13}
14
15test "@errorName sentinel length matches slice length" {
16 const name = testBuiltinErrorName(error.FooBar);
17 const length: usize = 6;
18 try expect(length == std.mem.indexOfSentinel(u8, 0, name.ptr));
19 try expect(length == name.len);
20}
21
22pub fn testBuiltinErrorName(err: anyerror) [:0]const u8 {
23 return @errorName(err);
24}
test/behavior/error_stage1.zig-21
......@@ -4,27 +4,6 @@ const expectError = std.testing.expectError;
44const expectEqual = std.testing.expectEqual;
55const mem = std.mem;
66
7fn gimmeItBroke() anyerror {
8 return error.ItBroke;
9}
10
11test "@errorName" {
12 try expect(mem.eql(u8, @errorName(error.AnError), "AnError"));
13 try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
14 try expect(mem.eql(u8, @errorName(gimmeItBroke()), "ItBroke"));
15}
16
17test "@errorName sentinel length matches slice length" {
18 const name = testBuiltinErrorName(error.FooBar);
19 const length: usize = 6;
20 try expectEqual(length, std.mem.indexOfSentinel(u8, 0, name.ptr));
21 try expectEqual(length, name.len);
22}
23
24pub fn testBuiltinErrorName(err: anyerror) [:0]const u8 {
25 return @errorName(err);
26}
27
287test "error union type " {
298 try testErrorUnionType();
309 comptime try testErrorUnionType();