mirror of
https://github.com/ziglang/zig.git
synced 2025-12-11 08:43:09 +00:00
stage2 ARM: implement is_err and is_non_err for simple error unions
This commit is contained in:
parent
edcebe7013
commit
c55f58d8bb
@ -1104,7 +1104,13 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
|
|||||||
|
|
||||||
fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
|
fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch});
|
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||||
|
const err_ty = self.air.typeOf(ty_op.operand);
|
||||||
|
const payload_ty = err_ty.errorUnionPayload();
|
||||||
|
if (!payload_ty.hasCodeGenBits()) break :result MCValue.none;
|
||||||
|
|
||||||
|
return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{});
|
||||||
|
};
|
||||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2358,18 +2364,45 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
|
|||||||
return MCValue{ .compare_flags_unsigned = .neq };
|
return MCValue{ .compare_flags_unsigned = .neq };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn isErr(self: *Self, operand: MCValue) !MCValue {
|
fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
|
||||||
_ = operand;
|
_ = operand;
|
||||||
// Here you can specialize this instruction if it makes sense to, otherwise the default
|
|
||||||
// will call isNonNull and invert the result.
|
const error_type = ty.errorUnionSet();
|
||||||
return self.fail("TODO call isNonErr and invert the result", .{});
|
const payload_type = ty.errorUnionPayload();
|
||||||
|
|
||||||
|
if (!error_type.hasCodeGenBits()) {
|
||||||
|
return MCValue{ .immediate = 0 }; // always false
|
||||||
|
} else if (!payload_type.hasCodeGenBits()) {
|
||||||
|
if (error_type.abiSize(self.target.*) <= 4) {
|
||||||
|
const reg_mcv: MCValue = switch (operand) {
|
||||||
|
.register => operand,
|
||||||
|
else => .{ .register = try self.copyToTmpRegister(error_type, operand) },
|
||||||
|
};
|
||||||
|
|
||||||
|
try self.genArmBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined);
|
||||||
|
|
||||||
|
return MCValue{ .compare_flags_unsigned = .gt };
|
||||||
|
} else {
|
||||||
|
return self.fail("TODO isErr for errors with size > 4", .{});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return self.fail("TODO isErr for non-empty payloads", .{});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn isNonErr(self: *Self, operand: MCValue) !MCValue {
|
fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
|
||||||
_ = operand;
|
const is_err_result = try self.isErr(ty, operand);
|
||||||
// Here you can specialize this instruction if it makes sense to, otherwise the default
|
switch (is_err_result) {
|
||||||
// will call isNull and invert the result.
|
.compare_flags_unsigned => |op| {
|
||||||
return self.fail("TODO call isErr and invert the result", .{});
|
assert(op == .gt);
|
||||||
|
return MCValue{ .compare_flags_unsigned = .lte };
|
||||||
|
},
|
||||||
|
.immediate => |imm| {
|
||||||
|
assert(imm == 0);
|
||||||
|
return MCValue{ .immediate = 1 };
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
|
fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
@ -2438,7 +2471,8 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
|
|||||||
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
||||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||||
const operand = try self.resolveInst(un_op);
|
const operand = try self.resolveInst(un_op);
|
||||||
break :result try self.isErr(operand);
|
const ty = self.air.typeOf(un_op);
|
||||||
|
break :result try self.isErr(ty, operand);
|
||||||
};
|
};
|
||||||
return self.finishAir(inst, result, .{ un_op, .none, .none });
|
return self.finishAir(inst, result, .{ un_op, .none, .none });
|
||||||
}
|
}
|
||||||
@ -2447,6 +2481,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
|
|||||||
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
||||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||||
const operand_ptr = try self.resolveInst(un_op);
|
const operand_ptr = try self.resolveInst(un_op);
|
||||||
|
const ptr_ty = self.air.typeOf(un_op);
|
||||||
const operand: MCValue = blk: {
|
const operand: MCValue = blk: {
|
||||||
if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
|
if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
|
||||||
// The MCValue that holds the pointer can be re-used as the value.
|
// The MCValue that holds the pointer can be re-used as the value.
|
||||||
@ -2455,8 +2490,8 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
|
|||||||
break :blk try self.allocRegOrMem(inst, true);
|
break :blk try self.allocRegOrMem(inst, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try self.load(operand, operand_ptr, self.air.typeOf(un_op));
|
try self.load(operand, operand_ptr, ptr_ty);
|
||||||
break :result try self.isErr(operand);
|
break :result try self.isErr(ptr_ty.elemType(), operand);
|
||||||
};
|
};
|
||||||
return self.finishAir(inst, result, .{ un_op, .none, .none });
|
return self.finishAir(inst, result, .{ un_op, .none, .none });
|
||||||
}
|
}
|
||||||
@ -2465,7 +2500,8 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
|
|||||||
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
||||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||||
const operand = try self.resolveInst(un_op);
|
const operand = try self.resolveInst(un_op);
|
||||||
break :result try self.isNonErr(operand);
|
const ty = self.air.typeOf(un_op);
|
||||||
|
break :result try self.isNonErr(ty, operand);
|
||||||
};
|
};
|
||||||
return self.finishAir(inst, result, .{ un_op, .none, .none });
|
return self.finishAir(inst, result, .{ un_op, .none, .none });
|
||||||
}
|
}
|
||||||
@ -2474,6 +2510,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
|
|||||||
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
||||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||||
const operand_ptr = try self.resolveInst(un_op);
|
const operand_ptr = try self.resolveInst(un_op);
|
||||||
|
const ptr_ty = self.air.typeOf(un_op);
|
||||||
const operand: MCValue = blk: {
|
const operand: MCValue = blk: {
|
||||||
if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
|
if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
|
||||||
// The MCValue that holds the pointer can be re-used as the value.
|
// The MCValue that holds the pointer can be re-used as the value.
|
||||||
@ -2482,8 +2519,8 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
|
|||||||
break :blk try self.allocRegOrMem(inst, true);
|
break :blk try self.allocRegOrMem(inst, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try self.load(operand, operand_ptr, self.air.typeOf(un_op));
|
try self.load(operand, operand_ptr, ptr_ty);
|
||||||
break :result try self.isNonErr(operand);
|
break :result try self.isNonErr(ptr_ty.elemType(), operand);
|
||||||
};
|
};
|
||||||
return self.finishAir(inst, result, .{ un_op, .none, .none });
|
return self.finishAir(inst, result, .{ un_op, .none, .none });
|
||||||
}
|
}
|
||||||
@ -3383,31 +3420,32 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
.ErrorSet => {
|
.ErrorSet => {
|
||||||
switch (typed_value.val.tag()) {
|
|
||||||
.@"error" => {
|
|
||||||
const err_name = typed_value.val.castTag(.@"error").?.data.name;
|
const err_name = typed_value.val.castTag(.@"error").?.data.name;
|
||||||
const module = self.bin_file.options.module.?;
|
const module = self.bin_file.options.module.?;
|
||||||
const global_error_set = module.global_error_set;
|
const global_error_set = module.global_error_set;
|
||||||
const error_index = global_error_set.get(err_name).?;
|
const error_index = global_error_set.get(err_name).?;
|
||||||
return MCValue{ .immediate = error_index };
|
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 => {
|
.ErrorUnion => {
|
||||||
const error_type = typed_value.ty.errorUnionSet();
|
const error_type = typed_value.ty.errorUnionSet();
|
||||||
const payload_type = typed_value.ty.errorUnionPayload();
|
const payload_type = typed_value.ty.errorUnionPayload();
|
||||||
const sub_val = typed_value.val.castTag(.eu_payload).?.data;
|
|
||||||
|
|
||||||
|
if (typed_value.val.castTag(.eu_payload)) |pl| {
|
||||||
if (!payload_type.hasCodeGenBits()) {
|
if (!payload_type.hasCodeGenBits()) {
|
||||||
// We use the error type directly as the type.
|
// We use the error type directly as the type.
|
||||||
return self.genTypedValue(.{ .ty = error_type, .val = sub_val });
|
return MCValue{ .immediate = 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty});
|
_ = pl;
|
||||||
|
return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty});
|
||||||
|
} else {
|
||||||
|
if (!payload_type.hasCodeGenBits()) {
|
||||||
|
// We use the error type directly as the type.
|
||||||
|
return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val });
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),
|
else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -588,4 +588,53 @@ pub fn addCases(ctx: *TestContext) !void {
|
|||||||
"",
|
"",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var case = ctx.exe("errors", linux_arm);
|
||||||
|
case.addCompareOutput(
|
||||||
|
\\pub fn main() void {
|
||||||
|
\\ foo() catch print();
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
\\fn foo() anyerror!void {}
|
||||||
|
\\
|
||||||
|
\\fn print() void {
|
||||||
|
\\ asm volatile ("svc #0"
|
||||||
|
\\ :
|
||||||
|
\\ : [number] "{r7}" (4),
|
||||||
|
\\ [arg1] "{r0}" (1),
|
||||||
|
\\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
|
||||||
|
\\ [arg3] "{r2}" ("Hello, World!\n".len),
|
||||||
|
\\ : "memory"
|
||||||
|
\\ );
|
||||||
|
\\ return;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
|
case.addCompareOutput(
|
||||||
|
\\pub fn main() void {
|
||||||
|
\\ foo() catch print();
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
\\fn foo() anyerror!void {
|
||||||
|
\\ return error.Test;
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
\\fn print() void {
|
||||||
|
\\ asm volatile ("svc #0"
|
||||||
|
\\ :
|
||||||
|
\\ : [number] "{r7}" (4),
|
||||||
|
\\ [arg1] "{r0}" (1),
|
||||||
|
\\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
|
||||||
|
\\ [arg3] "{r2}" ("Hello, World!\n".len),
|
||||||
|
\\ : "memory"
|
||||||
|
\\ );
|
||||||
|
\\ return;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
"Hello, World!\n",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user