Commit 05947ea870
Changed files (13)
src
arch
test
behavior
src/arch/aarch64/CodeGen.zig
@@ -570,7 +570,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.cmp_gte => try self.airCmp(inst, .gte),
.cmp_gt => try self.airCmp(inst, .gt),
.cmp_neq => try self.airCmp(inst, .neq),
+
.cmp_vector => try self.airCmpVector(inst),
+ .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
.bool_and => try self.airBinOp(inst),
.bool_or => try self.airBinOp(inst),
@@ -2660,6 +2662,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
}
+fn airCmpLtErrorsLen(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);
+ _ = operand;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ un_op, .none, .none });
+}
+
fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
src/arch/arm/CodeGen.zig
@@ -567,7 +567,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.cmp_gte => try self.airCmp(inst, .gte),
.cmp_gt => try self.airCmp(inst, .gt),
.cmp_neq => try self.airCmp(inst, .neq),
+
.cmp_vector => try self.airCmpVector(inst),
+ .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
.bool_and => try self.airBinOp(inst),
.bool_or => try self.airBinOp(inst),
@@ -3063,6 +3065,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
}
+fn airCmpLtErrorsLen(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);
+ _ = operand;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ un_op, .none, .none });
+}
+
fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
src/arch/riscv64/CodeGen.zig
@@ -537,7 +537,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.cmp_gte => try self.airCmp(inst, .gte),
.cmp_gt => try self.airCmp(inst, .gt),
.cmp_neq => try self.airCmp(inst, .neq),
+
.cmp_vector => try self.airCmpVector(inst),
+ .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
.bool_and => try self.airBoolOp(inst),
.bool_or => try self.airBoolOp(inst),
@@ -1799,6 +1801,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
}
+fn airCmpLtErrorsLen(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);
+ _ = operand;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ un_op, .none, .none });
+}
+
fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
src/arch/wasm/CodeGen.zig
@@ -1319,7 +1319,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.cmp_lte => self.airCmp(inst, .lte),
.cmp_lt => self.airCmp(inst, .lt),
.cmp_neq => self.airCmp(inst, .neq),
+
.cmp_vector => self.airCmpVector(inst),
+ .cmp_lt_errors_len => self.airCmpLtErrorsLen(inst),
.array_elem_val => self.airArrayElemVal(inst),
.array_to_slice => self.airArrayToSlice(inst),
@@ -2267,6 +2269,16 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
return self.fail("TODO implement airCmpVector for wasm", .{});
}
+fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+ if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
+
+ const un_op = self.air.instructions.items(.data)[inst].un_op;
+ const operand = try self.resolveInst(un_op);
+
+ _ = operand;
+ return self.fail("TODO implement airCmpLtErrorsLen for wasm", .{});
+}
+
fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const br = self.air.instructions.items(.data)[inst].br;
const block = self.blocks.get(br.block_inst).?;
src/arch/x86_64/CodeGen.zig
@@ -693,7 +693,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.cmp_gte => try self.airCmp(inst, .gte),
.cmp_gt => try self.airCmp(inst, .gt),
.cmp_neq => try self.airCmp(inst, .neq),
+
.cmp_vector => try self.airCmpVector(inst),
+ .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
.bool_and => try self.airBoolOp(inst),
.bool_or => try self.airBoolOp(inst),
@@ -3818,6 +3820,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch});
}
+fn airCmpLtErrorsLen(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);
+ _ = operand;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ un_op, .none, .none });
+}
+
fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
const payload = try self.addExtra(Mir.DbgLineColumn{
src/codegen/c.zig
@@ -1767,7 +1767,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.cmp_eq => try airEquality(f, inst, "((", "=="),
.cmp_neq => try airEquality(f, inst, "!((", "!="),
- .cmp_vector => return f.fail("TODO: C backend: implement binary op for tag '{s}'", .{@tagName(Air.Inst.Tag.cmp_vector)}),
+ .cmp_vector => return f.fail("TODO: C backend: implement cmp_vector", .{}),
+ .cmp_lt_errors_len => return f.fail("TODO: C backend: implement cmp_lt_errors_len", .{}),
// bool_and and bool_or are non-short-circuit operations
.bool_and => try airBinOp(f, inst, " & "),
src/codegen/llvm.zig
@@ -440,8 +440,38 @@ pub const Object = struct {
error_name_table_ptr_global.setInitializer(error_name_table_ptr);
}
+ fn genCmpLtErrorsLenFunction(object: *Object, comp: *Compilation) !void {
+ // If there is no such function in the module, it means the source code does not need it.
+ const llvm_fn = object.llvm_module.getNamedFunction(lt_errors_fn_name) orelse return;
+ const mod = comp.bin_file.options.module.?;
+ const errors_len = mod.global_error_set.count();
+
+ // Delete previous implementation. We replace it with every flush() because the
+ // total number of errors may have changed.
+ while (llvm_fn.getFirstBasicBlock()) |bb| {
+ bb.deleteBasicBlock();
+ }
+
+ const builder = object.context.createBuilder();
+
+ const entry_block = object.context.appendBasicBlock(llvm_fn, "Entry");
+ builder.positionBuilderAtEnd(entry_block);
+ builder.clearCurrentDebugLocation();
+
+ // Example source of the following LLVM IR:
+ // fn __zig_lt_errors_len(index: u16) bool {
+ // return index < total_errors_len;
+ // }
+
+ const lhs = llvm_fn.getParam(0);
+ const rhs = lhs.typeOf().constInt(errors_len, .False);
+ const is_lt = builder.buildICmp(.ULT, lhs, rhs, "");
+ _ = builder.buildRet(is_lt);
+ }
+
pub fn flushModule(self: *Object, comp: *Compilation) !void {
try self.genErrorNameTable(comp);
+ try self.genCmpLtErrorsLenFunction(comp);
if (self.di_builder) |dib| {
// When lowering debug info for pointers, we emitted the element types as
@@ -3457,7 +3487,9 @@ pub const FuncGen = struct {
.cmp_lt => try self.airCmp(inst, .lt),
.cmp_lte => try self.airCmp(inst, .lte),
.cmp_neq => try self.airCmp(inst, .neq),
+
.cmp_vector => try self.airCmpVector(inst),
+ .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
.is_non_null => try self.airIsNonNull(inst, false, false, .NE),
.is_non_null_ptr => try self.airIsNonNull(inst, true , false, .NE),
@@ -3738,6 +3770,16 @@ pub const FuncGen = struct {
return self.cmp(lhs, rhs, vec_ty, cmp_op);
}
+ fn airCmpLtErrorsLen(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 llvm_fn = try self.getCmpLtErrorsLenFunction();
+ const args: [1]*const llvm.Value = .{operand};
+ return self.builder.buildCall(llvm_fn, &args, args.len, .Fast, .Auto, "");
+ }
+
fn cmp(
self: *FuncGen,
lhs: *const llvm.Value,
@@ -6392,6 +6434,25 @@ pub const FuncGen = struct {
return fn_val;
}
+ fn getCmpLtErrorsLenFunction(self: *FuncGen) !*const llvm.Value {
+ if (self.dg.object.llvm_module.getNamedFunction(lt_errors_fn_name)) |llvm_fn| {
+ return llvm_fn;
+ }
+
+ // Function signature: fn (anyerror) bool
+
+ const ret_llvm_ty = try self.dg.llvmType(Type.bool);
+ const anyerror_llvm_ty = try self.dg.llvmType(Type.anyerror);
+ const param_types = [_]*const llvm.Type{anyerror_llvm_ty};
+
+ const fn_type = llvm.functionType(ret_llvm_ty, ¶m_types, param_types.len, .False);
+ const llvm_fn = self.dg.object.llvm_module.addFunction(lt_errors_fn_name, fn_type);
+ llvm_fn.setLinkage(.Internal);
+ llvm_fn.setFunctionCallConv(.Fast);
+ self.dg.addCommonFnAttributes(llvm_fn);
+ return llvm_fn;
+ }
+
fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
if (self.liveness.isUnused(inst)) return null;
@@ -7663,3 +7724,5 @@ const AnnotatedDITypePtr = enum(usize) {
return @truncate(u1, @enumToInt(self)) != 0;
}
};
+
+const lt_errors_fn_name = "__zig_lt_errors_len";
src/Air.zig
@@ -637,6 +637,15 @@ pub const Inst = struct {
/// Uses the `pl_op` field, payload represents the index of the target memory.
wasm_memory_grow,
+ /// Returns `true` if and only if the operand, an integer with
+ /// the same size as the error integer type, is less than the
+ /// total number of errors in the Module.
+ /// Result type is always `bool`.
+ /// Uses the `un_op` field.
+ /// Note that the number of errors in the Module cannot be considered stable until
+ /// flush().
+ cmp_lt_errors_len,
+
pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
return switch (op) {
.lt => .cmp_lt,
@@ -928,6 +937,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.cmp_gte,
.cmp_gt,
.cmp_neq,
+ .cmp_lt_errors_len,
.is_null,
.is_non_null,
.is_null_ptr,
@@ -936,9 +946,9 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.is_non_err,
.is_err_ptr,
.is_non_err_ptr,
- => return Type.initTag(.bool),
+ => return Type.bool,
- .const_ty => return Type.initTag(.type),
+ .const_ty => return Type.type,
.alloc,
.ret_ptr,
src/AstGen.zig
@@ -7250,7 +7250,7 @@ fn builtinCall(
.ptr_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ptr_to_int),
.error_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .error_to_int),
- .int_to_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u16_type }, params[0], .int_to_error),
+ .int_to_error => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .u16_type }, params[0], .int_to_error),
.compile_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .compile_error),
.set_eval_branch_quota => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .u32_type }, params[0], .set_eval_branch_quota),
.enum_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .enum_to_int),
src/Liveness.zig
@@ -393,6 +393,7 @@ fn analyzeInst(
.ceil,
.round,
.trunc_float,
+ .cmp_lt_errors_len,
=> {
const operand = inst_datas[inst].un_op;
return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
src/print_air.zig
@@ -166,6 +166,7 @@ const Writer = struct {
.ceil,
.round,
.trunc_float,
+ .cmp_lt_errors_len,
=> try w.writeUnOp(s, inst),
.breakpoint,
src/Sema.zig
@@ -5640,7 +5640,7 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const op = sema.resolveInst(inst_data.operand);
const op_coerced = try sema.coerce(block, Type.anyerror, op, operand_src);
- const result_ty = Type.initTag(.u16);
+ const result_ty = Type.u16;
if (try sema.resolveMaybeUndefVal(block, src, op_coerced)) |val| {
if (val.isUndef()) {
@@ -5665,32 +5665,31 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
-
- const op = sema.resolveInst(inst_data.operand);
+ const uncasted_operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.coerce(block, Type.u16, uncasted_operand, operand_src);
const target = sema.mod.getTarget();
- if (try sema.resolveDefinedValue(block, operand_src, op)) |value| {
- const int = value.toUnsignedInt(target);
+ if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| {
+ const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(target));
if (int > sema.mod.global_error_set.count() or int == 0)
return sema.fail(block, operand_src, "integer value {d} represents no error", .{int});
const payload = try sema.arena.create(Value.Payload.Error);
payload.* = .{
.base = .{ .tag = .@"error" },
- .data = .{ .name = sema.mod.error_name_list.items[@intCast(usize, int)] },
+ .data = .{ .name = sema.mod.error_name_list.items[int] },
};
return sema.addConstant(Type.anyerror, Value.initPayload(&payload.base));
}
try sema.requireRuntimeBlock(block, src);
if (block.wantSafety()) {
- return sema.fail(block, src, "TODO: get max errors in compilation", .{});
- // const is_gt_max = @panic("TODO get max errors in compilation");
- // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code);
+ const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand);
+ try sema.addSafetyCheck(block, is_lt_len, .invalid_error_code);
}
return block.addInst(.{
.tag = .bitcast,
.data = .{ .ty_op = .{
.ty = Air.Inst.Ref.anyerror_type,
- .operand = op,
+ .operand = operand,
} },
});
}
test/behavior/cast.zig
@@ -360,7 +360,11 @@ test "expected [*c]const u8, found [*:0]const u8" {
}
test "explicit cast from integer to error type" {
- if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
try testCastIntToErr(error.ItBroke);
comptime try testCastIntToErr(error.ItBroke);