authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-29 22:19:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-29 22:19:06-07:00
log05947ea870f95ab90a75e174079492f546baaf72
tree10c9a048bb1d55ce553c42c8f8e4f969241925cc
parent83617eac5902a9e66449e8c409dfa9e560bf9f12

stage2: implement `@intToError` with safety

This commit introduces a new AIR instruction `cmp_lt_errors_len`. It's specific to this use case for two reasons: * The total number of errors is not stable during semantic analysis; it can only be reliably checked when flush() is called. So the backend that is lowering the instruction must emit a relocation of some kind and then populate it during flush(). * The fewer AIR instructions in memory, the better for compiler performance, so we squish complex meanings into AIR tags without hesitation. The instruction is implemented only in the LLVM backend so far. It does this by creating a simple function which is gutted and re-populated with each flush(). AstGen now uses ResultLoc.coerced_ty for `@intToError` and Sema does the coercion.

13 files changed, 146 insertions(+), 15 deletions(-)

src/Air.zig+12-2
...@@ -637,6 +637,15 @@ pub const Inst = struct {...@@ -637,6 +637,15 @@ pub const Inst = struct {
637 /// Uses the `pl_op` field, payload represents the index of the target memory.637 /// Uses the `pl_op` field, payload represents the index of the target memory.
638 wasm_memory_grow,638 wasm_memory_grow,
639639
640 /// Returns `true` if and only if the operand, an integer with
641 /// the same size as the error integer type, is less than the
642 /// total number of errors in the Module.
643 /// Result type is always `bool`.
644 /// Uses the `un_op` field.
645 /// Note that the number of errors in the Module cannot be considered stable until
646 /// flush().
647 cmp_lt_errors_len,
648
640 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {649 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
641 return switch (op) {650 return switch (op) {
642 .lt => .cmp_lt,651 .lt => .cmp_lt,
...@@ -928,6 +937,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -928,6 +937,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
928 .cmp_gte,937 .cmp_gte,
929 .cmp_gt,938 .cmp_gt,
930 .cmp_neq,939 .cmp_neq,
940 .cmp_lt_errors_len,
931 .is_null,941 .is_null,
932 .is_non_null,942 .is_non_null,
933 .is_null_ptr,943 .is_null_ptr,
...@@ -936,9 +946,9 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -936,9 +946,9 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
936 .is_non_err,946 .is_non_err,
937 .is_err_ptr,947 .is_err_ptr,
938 .is_non_err_ptr,948 .is_non_err_ptr,
939 => return Type.initTag(.bool),949 => return Type.bool,
940950
941 .const_ty => return Type.initTag(.type),951 .const_ty => return Type.type,
942952
943 .alloc,953 .alloc,
944 .ret_ptr,954 .ret_ptr,
src/AstGen.zig+1-1
...@@ -7250,7 +7250,7 @@ fn builtinCall(...@@ -7250,7 +7250,7 @@ fn builtinCall(
72507250
7251 .ptr_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ptr_to_int),7251 .ptr_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ptr_to_int),
7252 .error_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .error_to_int),7252 .error_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .error_to_int),
7253 .int_to_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u16_type }, params[0], .int_to_error),7253 .int_to_error => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .u16_type }, params[0], .int_to_error),
7254 .compile_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .compile_error),7254 .compile_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .compile_error),
7255 .set_eval_branch_quota => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .u32_type }, params[0], .set_eval_branch_quota),7255 .set_eval_branch_quota => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .u32_type }, params[0], .set_eval_branch_quota),
7256 .enum_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .enum_to_int),7256 .enum_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .enum_to_int),
src/Liveness.zig+1
...@@ -393,6 +393,7 @@ fn analyzeInst(...@@ -393,6 +393,7 @@ fn analyzeInst(
393 .ceil,393 .ceil,
394 .round,394 .round,
395 .trunc_float,395 .trunc_float,
396 .cmp_lt_errors_len,
396 => {397 => {
397 const operand = inst_datas[inst].un_op;398 const operand = inst_datas[inst].un_op;
398 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });399 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
src/Sema.zig+9-10
...@@ -5640,7 +5640,7 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!...@@ -5640,7 +5640,7 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
5640 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };5640 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
5641 const op = sema.resolveInst(inst_data.operand);5641 const op = sema.resolveInst(inst_data.operand);
5642 const op_coerced = try sema.coerce(block, Type.anyerror, op, operand_src);5642 const op_coerced = try sema.coerce(block, Type.anyerror, op, operand_src);
5643 const result_ty = Type.initTag(.u16);5643 const result_ty = Type.u16;
56445644
5645 if (try sema.resolveMaybeUndefVal(block, src, op_coerced)) |val| {5645 if (try sema.resolveMaybeUndefVal(block, src, op_coerced)) |val| {
5646 if (val.isUndef()) {5646 if (val.isUndef()) {
...@@ -5665,32 +5665,31 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!...@@ -5665,32 +5665,31 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
5665 const inst_data = sema.code.instructions.items(.data)[inst].un_node;5665 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5666 const src = inst_data.src();5666 const src = inst_data.src();
5667 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };5667 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
56685668 const uncasted_operand = sema.resolveInst(inst_data.operand);
5669 const op = sema.resolveInst(inst_data.operand);5669 const operand = try sema.coerce(block, Type.u16, uncasted_operand, operand_src);
5670 const target = sema.mod.getTarget();5670 const target = sema.mod.getTarget();
56715671
5672 if (try sema.resolveDefinedValue(block, operand_src, op)) |value| {5672 if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| {
5673 const int = value.toUnsignedInt(target);5673 const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(target));
5674 if (int > sema.mod.global_error_set.count() or int == 0)5674 if (int > sema.mod.global_error_set.count() or int == 0)
5675 return sema.fail(block, operand_src, "integer value {d} represents no error", .{int});5675 return sema.fail(block, operand_src, "integer value {d} represents no error", .{int});
5676 const payload = try sema.arena.create(Value.Payload.Error);5676 const payload = try sema.arena.create(Value.Payload.Error);
5677 payload.* = .{5677 payload.* = .{
5678 .base = .{ .tag = .@"error" },5678 .base = .{ .tag = .@"error" },
5679 .data = .{ .name = sema.mod.error_name_list.items[@intCast(usize, int)] },5679 .data = .{ .name = sema.mod.error_name_list.items[int] },
5680 };5680 };
5681 return sema.addConstant(Type.anyerror, Value.initPayload(&payload.base));5681 return sema.addConstant(Type.anyerror, Value.initPayload(&payload.base));
5682 }5682 }
5683 try sema.requireRuntimeBlock(block, src);5683 try sema.requireRuntimeBlock(block, src);
5684 if (block.wantSafety()) {5684 if (block.wantSafety()) {
5685 return sema.fail(block, src, "TODO: get max errors in compilation", .{});5685 const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand);
5686 // const is_gt_max = @panic("TODO get max errors in compilation");5686 try sema.addSafetyCheck(block, is_lt_len, .invalid_error_code);
5687 // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code);
5688 }5687 }
5689 return block.addInst(.{5688 return block.addInst(.{
5690 .tag = .bitcast,5689 .tag = .bitcast,
5691 .data = .{ .ty_op = .{5690 .data = .{ .ty_op = .{
5692 .ty = Air.Inst.Ref.anyerror_type,5691 .ty = Air.Inst.Ref.anyerror_type,
5693 .operand = op,5692 .operand = operand,
5694 } },5693 } },
5695 });5694 });
5696}5695}
src/arch/aarch64/CodeGen.zig+10
...@@ -570,7 +570,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -570,7 +570,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
570 .cmp_gte => try self.airCmp(inst, .gte),570 .cmp_gte => try self.airCmp(inst, .gte),
571 .cmp_gt => try self.airCmp(inst, .gt),571 .cmp_gt => try self.airCmp(inst, .gt),
572 .cmp_neq => try self.airCmp(inst, .neq),572 .cmp_neq => try self.airCmp(inst, .neq),
573
573 .cmp_vector => try self.airCmpVector(inst),574 .cmp_vector => try self.airCmpVector(inst),
575 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
574576
575 .bool_and => try self.airBinOp(inst),577 .bool_and => try self.airBinOp(inst),
576 .bool_or => try self.airBinOp(inst),578 .bool_or => try self.airBinOp(inst),
...@@ -2660,6 +2662,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {...@@ -2660,6 +2662,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
2660 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});2662 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
2661}2663}
26622664
2665fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
2666 const un_op = self.air.instructions.items(.data)[inst].un_op;
2667 const operand = try self.resolveInst(un_op);
2668 _ = operand;
2669 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
2670 return self.finishAir(inst, result, .{ un_op, .none, .none });
2671}
2672
2663fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {2673fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
2664 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;2674 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
26652675
src/arch/arm/CodeGen.zig+10
...@@ -567,7 +567,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -567,7 +567,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
567 .cmp_gte => try self.airCmp(inst, .gte),567 .cmp_gte => try self.airCmp(inst, .gte),
568 .cmp_gt => try self.airCmp(inst, .gt),568 .cmp_gt => try self.airCmp(inst, .gt),
569 .cmp_neq => try self.airCmp(inst, .neq),569 .cmp_neq => try self.airCmp(inst, .neq),
570
570 .cmp_vector => try self.airCmpVector(inst),571 .cmp_vector => try self.airCmpVector(inst),
572 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
571573
572 .bool_and => try self.airBinOp(inst),574 .bool_and => try self.airBinOp(inst),
573 .bool_or => try self.airBinOp(inst),575 .bool_or => try self.airBinOp(inst),
...@@ -3063,6 +3065,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {...@@ -3063,6 +3065,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
3063 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});3065 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
3064}3066}
30653067
3068fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
3069 const un_op = self.air.instructions.items(.data)[inst].un_op;
3070 const operand = try self.resolveInst(un_op);
3071 _ = operand;
3072 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
3073 return self.finishAir(inst, result, .{ un_op, .none, .none });
3074}
3075
3066fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {3076fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
3067 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;3077 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
30683078
src/arch/riscv64/CodeGen.zig+10
...@@ -537,7 +537,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -537,7 +537,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
537 .cmp_gte => try self.airCmp(inst, .gte),537 .cmp_gte => try self.airCmp(inst, .gte),
538 .cmp_gt => try self.airCmp(inst, .gt),538 .cmp_gt => try self.airCmp(inst, .gt),
539 .cmp_neq => try self.airCmp(inst, .neq),539 .cmp_neq => try self.airCmp(inst, .neq),
540
540 .cmp_vector => try self.airCmpVector(inst),541 .cmp_vector => try self.airCmpVector(inst),
542 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
541543
542 .bool_and => try self.airBoolOp(inst),544 .bool_and => try self.airBoolOp(inst),
543 .bool_or => try self.airBoolOp(inst),545 .bool_or => try self.airBoolOp(inst),
...@@ -1799,6 +1801,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {...@@ -1799,6 +1801,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
1799 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});1801 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
1800}1802}
18011803
1804fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
1805 const un_op = self.air.instructions.items(.data)[inst].un_op;
1806 const operand = try self.resolveInst(un_op);
1807 _ = operand;
1808 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
1809 return self.finishAir(inst, result, .{ un_op, .none, .none });
1810}
1811
1802fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {1812fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
1803 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;1813 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
18041814
src/arch/wasm/CodeGen.zig+12
...@@ -1319,7 +1319,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1319,7 +1319,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1319 .cmp_lte => self.airCmp(inst, .lte),1319 .cmp_lte => self.airCmp(inst, .lte),
1320 .cmp_lt => self.airCmp(inst, .lt),1320 .cmp_lt => self.airCmp(inst, .lt),
1321 .cmp_neq => self.airCmp(inst, .neq),1321 .cmp_neq => self.airCmp(inst, .neq),
1322
1322 .cmp_vector => self.airCmpVector(inst),1323 .cmp_vector => self.airCmpVector(inst),
1324 .cmp_lt_errors_len => self.airCmpLtErrorsLen(inst),
13231325
1324 .array_elem_val => self.airArrayElemVal(inst),1326 .array_elem_val => self.airArrayElemVal(inst),
1325 .array_to_slice => self.airArrayToSlice(inst),1327 .array_to_slice => self.airArrayToSlice(inst),
...@@ -2267,6 +2269,16 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2267,6 +2269,16 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2267 return self.fail("TODO implement airCmpVector for wasm", .{});2269 return self.fail("TODO implement airCmpVector for wasm", .{});
2268}2270}
22692271
2272fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2273 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2274
2275 const un_op = self.air.instructions.items(.data)[inst].un_op;
2276 const operand = try self.resolveInst(un_op);
2277
2278 _ = operand;
2279 return self.fail("TODO implement airCmpLtErrorsLen for wasm", .{});
2280}
2281
2270fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2282fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2271 const br = self.air.instructions.items(.data)[inst].br;2283 const br = self.air.instructions.items(.data)[inst].br;
2272 const block = self.blocks.get(br.block_inst).?;2284 const block = self.blocks.get(br.block_inst).?;
src/arch/x86_64/CodeGen.zig+10
...@@ -693,7 +693,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -693,7 +693,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
693 .cmp_gte => try self.airCmp(inst, .gte),693 .cmp_gte => try self.airCmp(inst, .gte),
694 .cmp_gt => try self.airCmp(inst, .gt),694 .cmp_gt => try self.airCmp(inst, .gt),
695 .cmp_neq => try self.airCmp(inst, .neq),695 .cmp_neq => try self.airCmp(inst, .neq),
696
696 .cmp_vector => try self.airCmpVector(inst),697 .cmp_vector => try self.airCmpVector(inst),
698 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
697699
698 .bool_and => try self.airBoolOp(inst),700 .bool_and => try self.airBoolOp(inst),
699 .bool_or => try self.airBoolOp(inst),701 .bool_or => try self.airBoolOp(inst),
...@@ -3818,6 +3820,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {...@@ -3818,6 +3820,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
3818 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});3820 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
3819}3821}
38203822
3823fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
3824 const un_op = self.air.instructions.items(.data)[inst].un_op;
3825 const operand = try self.resolveInst(un_op);
3826 _ = operand;
3827 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
3828 return self.finishAir(inst, result, .{ un_op, .none, .none });
3829}
3830
3821fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {3831fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
3822 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;3832 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
3823 const payload = try self.addExtra(Mir.DbgLineColumn{3833 const payload = try self.addExtra(Mir.DbgLineColumn{
src/codegen/c.zig+2-1
...@@ -1767,7 +1767,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1767,7 +1767,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1767 .cmp_eq => try airEquality(f, inst, "((", "=="),1767 .cmp_eq => try airEquality(f, inst, "((", "=="),
1768 .cmp_neq => try airEquality(f, inst, "!((", "!="),1768 .cmp_neq => try airEquality(f, inst, "!((", "!="),
17691769
1770 .cmp_vector => return f.fail("TODO: C backend: implement binary op for tag '{s}'", .{@tagName(Air.Inst.Tag.cmp_vector)}),1770 .cmp_vector => return f.fail("TODO: C backend: implement cmp_vector", .{}),
1771 .cmp_lt_errors_len => return f.fail("TODO: C backend: implement cmp_lt_errors_len", .{}),
17711772
1772 // bool_and and bool_or are non-short-circuit operations1773 // bool_and and bool_or are non-short-circuit operations
1773 .bool_and => try airBinOp(f, inst, " & "),1774 .bool_and => try airBinOp(f, inst, " & "),
src/codegen/llvm.zig+63
...@@ -440,8 +440,38 @@ pub const Object = struct {...@@ -440,8 +440,38 @@ pub const Object = struct {
440 error_name_table_ptr_global.setInitializer(error_name_table_ptr);440 error_name_table_ptr_global.setInitializer(error_name_table_ptr);
441 }441 }
442442
443 fn genCmpLtErrorsLenFunction(object: *Object, comp: *Compilation) !void {
444 // If there is no such function in the module, it means the source code does not need it.
445 const llvm_fn = object.llvm_module.getNamedFunction(lt_errors_fn_name) orelse return;
446 const mod = comp.bin_file.options.module.?;
447 const errors_len = mod.global_error_set.count();
448
449 // Delete previous implementation. We replace it with every flush() because the
450 // total number of errors may have changed.
451 while (llvm_fn.getFirstBasicBlock()) |bb| {
452 bb.deleteBasicBlock();
453 }
454
455 const builder = object.context.createBuilder();
456
457 const entry_block = object.context.appendBasicBlock(llvm_fn, "Entry");
458 builder.positionBuilderAtEnd(entry_block);
459 builder.clearCurrentDebugLocation();
460
461 // Example source of the following LLVM IR:
462 // fn __zig_lt_errors_len(index: u16) bool {
463 // return index < total_errors_len;
464 // }
465
466 const lhs = llvm_fn.getParam(0);
467 const rhs = lhs.typeOf().constInt(errors_len, .False);
468 const is_lt = builder.buildICmp(.ULT, lhs, rhs, "");
469 _ = builder.buildRet(is_lt);
470 }
471
443 pub fn flushModule(self: *Object, comp: *Compilation) !void {472 pub fn flushModule(self: *Object, comp: *Compilation) !void {
444 try self.genErrorNameTable(comp);473 try self.genErrorNameTable(comp);
474 try self.genCmpLtErrorsLenFunction(comp);
445475
446 if (self.di_builder) |dib| {476 if (self.di_builder) |dib| {
447 // When lowering debug info for pointers, we emitted the element types as477 // When lowering debug info for pointers, we emitted the element types as
...@@ -3457,7 +3487,9 @@ pub const FuncGen = struct {...@@ -3457,7 +3487,9 @@ pub const FuncGen = struct {
3457 .cmp_lt => try self.airCmp(inst, .lt),3487 .cmp_lt => try self.airCmp(inst, .lt),
3458 .cmp_lte => try self.airCmp(inst, .lte),3488 .cmp_lte => try self.airCmp(inst, .lte),
3459 .cmp_neq => try self.airCmp(inst, .neq),3489 .cmp_neq => try self.airCmp(inst, .neq),
3490
3460 .cmp_vector => try self.airCmpVector(inst),3491 .cmp_vector => try self.airCmpVector(inst),
3492 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
34613493
3462 .is_non_null => try self.airIsNonNull(inst, false, false, .NE),3494 .is_non_null => try self.airIsNonNull(inst, false, false, .NE),
3463 .is_non_null_ptr => try self.airIsNonNull(inst, true , false, .NE),3495 .is_non_null_ptr => try self.airIsNonNull(inst, true , false, .NE),
...@@ -3738,6 +3770,16 @@ pub const FuncGen = struct {...@@ -3738,6 +3770,16 @@ pub const FuncGen = struct {
3738 return self.cmp(lhs, rhs, vec_ty, cmp_op);3770 return self.cmp(lhs, rhs, vec_ty, cmp_op);
3739 }3771 }
37403772
3773 fn airCmpLtErrorsLen(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3774 if (self.liveness.isUnused(inst)) return null;
3775
3776 const un_op = self.air.instructions.items(.data)[inst].un_op;
3777 const operand = try self.resolveInst(un_op);
3778 const llvm_fn = try self.getCmpLtErrorsLenFunction();
3779 const args: [1]*const llvm.Value = .{operand};
3780 return self.builder.buildCall(llvm_fn, &args, args.len, .Fast, .Auto, "");
3781 }
3782
3741 fn cmp(3783 fn cmp(
3742 self: *FuncGen,3784 self: *FuncGen,
3743 lhs: *const llvm.Value,3785 lhs: *const llvm.Value,
...@@ -6392,6 +6434,25 @@ pub const FuncGen = struct {...@@ -6392,6 +6434,25 @@ pub const FuncGen = struct {
6392 return fn_val;6434 return fn_val;
6393 }6435 }
63946436
6437 fn getCmpLtErrorsLenFunction(self: *FuncGen) !*const llvm.Value {
6438 if (self.dg.object.llvm_module.getNamedFunction(lt_errors_fn_name)) |llvm_fn| {
6439 return llvm_fn;
6440 }
6441
6442 // Function signature: fn (anyerror) bool
6443
6444 const ret_llvm_ty = try self.dg.llvmType(Type.bool);
6445 const anyerror_llvm_ty = try self.dg.llvmType(Type.anyerror);
6446 const param_types = [_]*const llvm.Type{anyerror_llvm_ty};
6447
6448 const fn_type = llvm.functionType(ret_llvm_ty, &param_types, param_types.len, .False);
6449 const llvm_fn = self.dg.object.llvm_module.addFunction(lt_errors_fn_name, fn_type);
6450 llvm_fn.setLinkage(.Internal);
6451 llvm_fn.setFunctionCallConv(.Fast);
6452 self.dg.addCommonFnAttributes(llvm_fn);
6453 return llvm_fn;
6454 }
6455
6395 fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {6456 fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
6396 if (self.liveness.isUnused(inst)) return null;6457 if (self.liveness.isUnused(inst)) return null;
63976458
...@@ -7663,3 +7724,5 @@ const AnnotatedDITypePtr = enum(usize) {...@@ -7663,3 +7724,5 @@ const AnnotatedDITypePtr = enum(usize) {
7663 return @truncate(u1, @enumToInt(self)) != 0;7724 return @truncate(u1, @enumToInt(self)) != 0;
7664 }7725 }
7665};7726};
7727
7728const lt_errors_fn_name = "__zig_lt_errors_len";
src/print_air.zig+1
...@@ -166,6 +166,7 @@ const Writer = struct {...@@ -166,6 +166,7 @@ const Writer = struct {
166 .ceil,166 .ceil,
167 .round,167 .round,
168 .trunc_float,168 .trunc_float,
169 .cmp_lt_errors_len,
169 => try w.writeUnOp(s, inst),170 => try w.writeUnOp(s, inst),
170171
171 .breakpoint,172 .breakpoint,
test/behavior/cast.zig+5-1
...@@ -360,7 +360,11 @@ test "expected [*c]const u8, found [*:0]const u8" {...@@ -360,7 +360,11 @@ test "expected [*c]const u8, found [*:0]const u8" {
360}360}
361361
362test "explicit cast from integer to error type" {362test "explicit cast from integer to error type" {
363 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO363 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
364 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
365 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
366 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
367 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
364368
365 try testCastIntToErr(error.ItBroke);369 try testCastIntToErr(error.ItBroke);
366 comptime try testCastIntToErr(error.ItBroke);370 comptime try testCastIntToErr(error.ItBroke);