Commit e9f069f536
Changed files (2)
src
arch
x86_64
test
stage2
src/arch/x86_64/CodeGen.zig
@@ -415,9 +415,14 @@ fn gen(self: *Self) InnerError!void {
try self.genBody(self.air.getMainBody());
- if (self.exitlude_jump_relocs.items.len == 1) {
- self.mir_instructions.len -= 1;
- } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
+ // TODO can single exitlude jump reloc be elided? What if it is not at the end of the code?
+ // Example:
+ // pub fn main() void {
+ // maybeErr() catch return;
+ // unreachable;
+ // }
+ // Eliding the reloc will cause a miscompilation in this case.
+ for (self.exitlude_jump_relocs.items) |jmp_reloc| {
self.mir_instructions.items(.data)[jmp_reloc].inst = @intCast(u32, self.mir_instructions.len);
}
@@ -1180,19 +1185,24 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
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 error for {}", .{self.target.cpu.arch});
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
+ const err_union_ty = self.air.typeOf(ty_op.operand);
+ const payload_ty = err_union_ty.errorUnionPayload();
+ const mcv = try self.resolveInst(ty_op.operand);
+ if (!payload_ty.hasCodeGenBits()) break :result mcv;
+ return self.fail("TODO implement unwrap error union error for non-empty payloads", .{});
+ };
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
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_union_ty = self.air.typeOf(ty_op.operand);
+ const payload_ty = err_union_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 });
}
@@ -2396,19 +2406,35 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
}
fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
- _ = ty;
- _ = operand;
- // Here you can specialize this instruction if it makes sense to, otherwise the default
- // will call isNonErr and invert the result.
- return self.fail("TODO call isNonErr and invert the result", .{});
+ const err_type = ty.errorUnionSet();
+ const payload_type = ty.errorUnionPayload();
+ if (!err_type.hasCodeGenBits()) {
+ return MCValue{ .immediate = 0 }; // always false
+ } else if (!payload_type.hasCodeGenBits()) {
+ if (err_type.abiSize(self.target.*) <= 8) {
+ try self.genBinMathOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 });
+ return MCValue{ .compare_flags_unsigned = .gt };
+ } else {
+ return self.fail("TODO isErr for errors with size larger than register size", .{});
+ }
+ } else {
+ return self.fail("TODO isErr for non-empty payloads", .{});
+ }
}
fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
- _ = ty;
- _ = operand;
- // Here you can specialize this instruction if it makes sense to, otherwise the default
- // will call isErr and invert the result.
- return self.fail("TODO call isErr and invert the result", .{});
+ const is_err_res = try self.isErr(ty, operand);
+ switch (is_err_res) {
+ .compare_flags_unsigned => |op| {
+ 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 {
@@ -3435,31 +3461,32 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
}
},
.ErrorSet => {
- switch (typed_value.val.tag()) {
- .@"error" => {
- const err_name = typed_value.val.castTag(.@"error").?.data.name;
- const module = self.bin_file.options.module.?;
- const global_error_set = module.global_error_set;
- const error_index = global_error_set.get(err_name).?;
- 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 };
- },
- }
+ const err_name = typed_value.val.castTag(.@"error").?.data.name;
+ const module = self.bin_file.options.module.?;
+ const global_error_set = module.global_error_set;
+ const error_index = global_error_set.get(err_name).?;
+ return MCValue{ .immediate = error_index };
},
.ErrorUnion => {
const error_type = typed_value.ty.errorUnionSet();
const payload_type = typed_value.ty.errorUnionPayload();
- const sub_val = typed_value.val.castTag(.eu_payload).?.data;
- if (!payload_type.hasCodeGenBits()) {
- // We use the error type directly as the type.
- return self.genTypedValue(.{ .ty = error_type, .val = sub_val });
+ if (typed_value.val.castTag(.eu_payload)) |pl| {
+ if (!payload_type.hasCodeGenBits()) {
+ // We use the error type directly as the type.
+ return MCValue{ .immediate = 0 };
+ }
+
+ _ = 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 '{}'", .{typed_value.ty});
+ 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}),
}
test/stage2/x86_64.zig
@@ -1738,6 +1738,29 @@ pub fn addCases(ctx: *TestContext) !void {
\\}
, "");
}
+
+ {
+ var case = ctx.exe("unwrap error union - simple errors", target);
+ case.addCompareOutput(
+ \\pub fn main() void {
+ \\ maybeErr() catch unreachable;
+ \\}
+ \\
+ \\fn maybeErr() !void {
+ \\ return;
+ \\}
+ , "");
+ case.addCompareOutput(
+ \\pub fn main() void {
+ \\ maybeErr() catch return;
+ \\ unreachable;
+ \\}
+ \\
+ \\fn maybeErr() !void {
+ \\ return error.NoWay;
+ \\}
+ , "");
+ }
}
}