authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-03 17:05:56+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-04 01:21:24+01:00
loge9f069f53627f1e647a200da7bb75e7663705c5f
tree0e7ea14a58224101a863c1b9492df3c032c4f4b7
parent818672312ffa068e0272483a03e7a5ca774bcca5

stage2: implement isErr/isNonErr and unwrap error


2 files changed, 89 insertions(+), 39 deletions(-)

src/arch/x86_64/CodeGen.zig+66-39
...@@ -415,9 +415,14 @@ fn gen(self: *Self) InnerError!void {...@@ -415,9 +415,14 @@ fn gen(self: *Self) InnerError!void {
415415
416 try self.genBody(self.air.getMainBody());416 try self.genBody(self.air.getMainBody());
417417
418 if (self.exitlude_jump_relocs.items.len == 1) {418 // TODO can single exitlude jump reloc be elided? What if it is not at the end of the code?
419 self.mir_instructions.len -= 1;419 // Example:
420 } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {420 // pub fn main() void {
421 // maybeErr() catch return;
422 // unreachable;
423 // }
424 // Eliding the reloc will cause a miscompilation in this case.
425 for (self.exitlude_jump_relocs.items) |jmp_reloc| {
421 self.mir_instructions.items(.data)[jmp_reloc].inst = @intCast(u32, self.mir_instructions.len);426 self.mir_instructions.items(.data)[jmp_reloc].inst = @intCast(u32, self.mir_instructions.len);
422 }427 }
423428
...@@ -1180,19 +1185,24 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -1180,19 +1185,24 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
11801185
1181fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {1186fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
1182 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1187 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1183 const result: MCValue = if (self.liveness.isUnused(inst))1188 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1184 .dead1189 const err_union_ty = self.air.typeOf(ty_op.operand);
1185 else1190 const payload_ty = err_union_ty.errorUnionPayload();
1186 return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch});1191 const mcv = try self.resolveInst(ty_op.operand);
1192 if (!payload_ty.hasCodeGenBits()) break :result mcv;
1193 return self.fail("TODO implement unwrap error union error for non-empty payloads", .{});
1194 };
1187 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1195 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1188}1196}
11891197
1190fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {1198fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
1191 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1199 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1192 const result: MCValue = if (self.liveness.isUnused(inst))1200 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1193 .dead1201 const err_union_ty = self.air.typeOf(ty_op.operand);
1194 else1202 const payload_ty = err_union_ty.errorUnionPayload();
1195 return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch});1203 if (!payload_ty.hasCodeGenBits()) break :result MCValue.none;
1204 return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{});
1205 };
1196 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1206 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1197}1207}
11981208
...@@ -2396,19 +2406,35 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue {...@@ -2396,19 +2406,35 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
2396}2406}
23972407
2398fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {2408fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
2399 _ = ty;2409 const err_type = ty.errorUnionSet();
2400 _ = operand;2410 const payload_type = ty.errorUnionPayload();
2401 // Here you can specialize this instruction if it makes sense to, otherwise the default2411 if (!err_type.hasCodeGenBits()) {
2402 // will call isNonErr and invert the result.2412 return MCValue{ .immediate = 0 }; // always false
2403 return self.fail("TODO call isNonErr and invert the result", .{});2413 } else if (!payload_type.hasCodeGenBits()) {
2414 if (err_type.abiSize(self.target.*) <= 8) {
2415 try self.genBinMathOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 });
2416 return MCValue{ .compare_flags_unsigned = .gt };
2417 } else {
2418 return self.fail("TODO isErr for errors with size larger than register size", .{});
2419 }
2420 } else {
2421 return self.fail("TODO isErr for non-empty payloads", .{});
2422 }
2404}2423}
24052424
2406fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {2425fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
2407 _ = ty;2426 const is_err_res = try self.isErr(ty, operand);
2408 _ = operand;2427 switch (is_err_res) {
2409 // Here you can specialize this instruction if it makes sense to, otherwise the default2428 .compare_flags_unsigned => |op| {
2410 // will call isErr and invert the result.2429 assert(op == .gt);
2411 return self.fail("TODO call isErr and invert the result", .{});2430 return MCValue{ .compare_flags_unsigned = .lte };
2431 },
2432 .immediate => |imm| {
2433 assert(imm == 0);
2434 return MCValue{ .immediate = 1 };
2435 },
2436 else => unreachable,
2437 }
2412}2438}
24132439
2414fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {2440fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
...@@ -3435,31 +3461,32 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {...@@ -3435,31 +3461,32 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
3435 }3461 }
3436 },3462 },
3437 .ErrorSet => {3463 .ErrorSet => {
3438 switch (typed_value.val.tag()) {3464 const err_name = typed_value.val.castTag(.@"error").?.data.name;
3439 .@"error" => {3465 const module = self.bin_file.options.module.?;
3440 const err_name = typed_value.val.castTag(.@"error").?.data.name;3466 const global_error_set = module.global_error_set;
3441 const module = self.bin_file.options.module.?;3467 const error_index = global_error_set.get(err_name).?;
3442 const global_error_set = module.global_error_set;3468 return MCValue{ .immediate = error_index };
3443 const error_index = global_error_set.get(err_name).?;
3444 return MCValue{ .immediate = error_index };
3445 },
3446 else => {
3447 // In this case we are rendering an error union which has a 0 bits payload.
3448 return MCValue{ .immediate = 0 };
3449 },
3450 }
3451 },3469 },
3452 .ErrorUnion => {3470 .ErrorUnion => {
3453 const error_type = typed_value.ty.errorUnionSet();3471 const error_type = typed_value.ty.errorUnionSet();
3454 const payload_type = typed_value.ty.errorUnionPayload();3472 const payload_type = typed_value.ty.errorUnionPayload();
3455 const sub_val = typed_value.val.castTag(.eu_payload).?.data;
34563473
3457 if (!payload_type.hasCodeGenBits()) {3474 if (typed_value.val.castTag(.eu_payload)) |pl| {
3458 // We use the error type directly as the type.3475 if (!payload_type.hasCodeGenBits()) {
3459 return self.genTypedValue(.{ .ty = error_type, .val = sub_val });3476 // We use the error type directly as the type.
3477 return MCValue{ .immediate = 0 };
3478 }
3479
3480 _ = pl;
3481 return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty});
3482 } else {
3483 if (!payload_type.hasCodeGenBits()) {
3484 // We use the error type directly as the type.
3485 return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val });
3486 }
3460 }3487 }
34613488
3462 return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty});3489 return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty});
3463 },3490 },
3464 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),3491 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),
3465 }3492 }
test/stage2/x86_64.zig+23
...@@ -1738,6 +1738,29 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1738,6 +1738,29 @@ pub fn addCases(ctx: *TestContext) !void {
1738 \\}1738 \\}
1739 , "");1739 , "");
1740 }1740 }
1741
1742 {
1743 var case = ctx.exe("unwrap error union - simple errors", target);
1744 case.addCompareOutput(
1745 \\pub fn main() void {
1746 \\ maybeErr() catch unreachable;
1747 \\}
1748 \\
1749 \\fn maybeErr() !void {
1750 \\ return;
1751 \\}
1752 , "");
1753 case.addCompareOutput(
1754 \\pub fn main() void {
1755 \\ maybeErr() catch return;
1756 \\ unreachable;
1757 \\}
1758 \\
1759 \\fn maybeErr() !void {
1760 \\ return error.NoWay;
1761 \\}
1762 , "");
1763 }
1741 }1764 }
1742}1765}
17431766