authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-12-21 23:12:33+01:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-12-21 23:13:30+01:00
logc55f58d8bb38ef356282e43fa010b5e3f8da8a00
tree89fbf0136dea7415ed6872f8e37b346b1c1d0490
parentedcebe701339453bf49379d865a8049c5b22a49e
signaturelock-open Commit is signed but in an unrecognized format.

stage2 ARM: implement is_err and is_non_err for simple error unions


2 files changed, 122 insertions(+), 35 deletions(-)

src/arch/arm/CodeGen.zig+73-35
......@@ -1104,7 +1104,13 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
11041104
11051105fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
11061106 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1107 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch});
1107 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1108 const err_ty = self.air.typeOf(ty_op.operand);
1109 const payload_ty = err_ty.errorUnionPayload();
1110 if (!payload_ty.hasCodeGenBits()) break :result MCValue.none;
1111
1112 return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{});
1113 };
11081114 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11091115}
11101116
......@@ -2358,18 +2364,45 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
23582364 return MCValue{ .compare_flags_unsigned = .neq };
23592365}
23602366
2361fn isErr(self: *Self, operand: MCValue) !MCValue {
2367fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
23622368 _ = operand;
2363 // Here you can specialize this instruction if it makes sense to, otherwise the default
2364 // will call isNonNull and invert the result.
2365 return self.fail("TODO call isNonErr and invert the result", .{});
2369
2370 const error_type = ty.errorUnionSet();
2371 const payload_type = ty.errorUnionPayload();
2372
2373 if (!error_type.hasCodeGenBits()) {
2374 return MCValue{ .immediate = 0 }; // always false
2375 } else if (!payload_type.hasCodeGenBits()) {
2376 if (error_type.abiSize(self.target.*) <= 4) {
2377 const reg_mcv: MCValue = switch (operand) {
2378 .register => operand,
2379 else => .{ .register = try self.copyToTmpRegister(error_type, operand) },
2380 };
2381
2382 try self.genArmBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined);
2383
2384 return MCValue{ .compare_flags_unsigned = .gt };
2385 } else {
2386 return self.fail("TODO isErr for errors with size > 4", .{});
2387 }
2388 } else {
2389 return self.fail("TODO isErr for non-empty payloads", .{});
2390 }
23662391}
23672392
2368fn isNonErr(self: *Self, operand: MCValue) !MCValue {
2369 _ = operand;
2370 // Here you can specialize this instruction if it makes sense to, otherwise the default
2371 // will call isNull and invert the result.
2372 return self.fail("TODO call isErr and invert the result", .{});
2393fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
2394 const is_err_result = try self.isErr(ty, operand);
2395 switch (is_err_result) {
2396 .compare_flags_unsigned => |op| {
2397 assert(op == .gt);
2398 return MCValue{ .compare_flags_unsigned = .lte };
2399 },
2400 .immediate => |imm| {
2401 assert(imm == 0);
2402 return MCValue{ .immediate = 1 };
2403 },
2404 else => unreachable,
2405 }
23732406}
23742407
23752408fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
......@@ -2438,7 +2471,8 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
24382471 const un_op = self.air.instructions.items(.data)[inst].un_op;
24392472 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
24402473 const operand = try self.resolveInst(un_op);
2441 break :result try self.isErr(operand);
2474 const ty = self.air.typeOf(un_op);
2475 break :result try self.isErr(ty, operand);
24422476 };
24432477 return self.finishAir(inst, result, .{ un_op, .none, .none });
24442478}
......@@ -2447,6 +2481,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
24472481 const un_op = self.air.instructions.items(.data)[inst].un_op;
24482482 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
24492483 const operand_ptr = try self.resolveInst(un_op);
2484 const ptr_ty = self.air.typeOf(un_op);
24502485 const operand: MCValue = blk: {
24512486 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
24522487 // 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 {
24552490 break :blk try self.allocRegOrMem(inst, true);
24562491 }
24572492 };
2458 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
2459 break :result try self.isErr(operand);
2493 try self.load(operand, operand_ptr, ptr_ty);
2494 break :result try self.isErr(ptr_ty.elemType(), operand);
24602495 };
24612496 return self.finishAir(inst, result, .{ un_op, .none, .none });
24622497}
......@@ -2465,7 +2500,8 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
24652500 const un_op = self.air.instructions.items(.data)[inst].un_op;
24662501 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
24672502 const operand = try self.resolveInst(un_op);
2468 break :result try self.isNonErr(operand);
2503 const ty = self.air.typeOf(un_op);
2504 break :result try self.isNonErr(ty, operand);
24692505 };
24702506 return self.finishAir(inst, result, .{ un_op, .none, .none });
24712507}
......@@ -2474,6 +2510,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
24742510 const un_op = self.air.instructions.items(.data)[inst].un_op;
24752511 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
24762512 const operand_ptr = try self.resolveInst(un_op);
2513 const ptr_ty = self.air.typeOf(un_op);
24772514 const operand: MCValue = blk: {
24782515 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
24792516 // 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 {
24822519 break :blk try self.allocRegOrMem(inst, true);
24832520 }
24842521 };
2485 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
2486 break :result try self.isNonErr(operand);
2522 try self.load(operand, operand_ptr, ptr_ty);
2523 break :result try self.isNonErr(ptr_ty.elemType(), operand);
24872524 };
24882525 return self.finishAir(inst, result, .{ un_op, .none, .none });
24892526}
......@@ -3383,31 +3420,32 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
33833420 }
33843421 },
33853422 .ErrorSet => {
3386 switch (typed_value.val.tag()) {
3387 .@"error" => {
3388 const err_name = typed_value.val.castTag(.@"error").?.data.name;
3389 const module = self.bin_file.options.module.?;
3390 const global_error_set = module.global_error_set;
3391 const error_index = global_error_set.get(err_name).?;
3392 return MCValue{ .immediate = error_index };
3393 },
3394 else => {
3395 // In this case we are rendering an error union which has a 0 bits payload.
3396 return MCValue{ .immediate = 0 };
3397 },
3398 }
3423 const err_name = typed_value.val.castTag(.@"error").?.data.name;
3424 const module = self.bin_file.options.module.?;
3425 const global_error_set = module.global_error_set;
3426 const error_index = global_error_set.get(err_name).?;
3427 return MCValue{ .immediate = error_index };
33993428 },
34003429 .ErrorUnion => {
34013430 const error_type = typed_value.ty.errorUnionSet();
34023431 const payload_type = typed_value.ty.errorUnionPayload();
3403 const sub_val = typed_value.val.castTag(.eu_payload).?.data;
34043432
3405 if (!payload_type.hasCodeGenBits()) {
3406 // We use the error type directly as the type.
3407 return self.genTypedValue(.{ .ty = error_type, .val = sub_val });
3408 }
3433 if (typed_value.val.castTag(.eu_payload)) |pl| {
3434 if (!payload_type.hasCodeGenBits()) {
3435 // We use the error type directly as the type.
3436 return MCValue{ .immediate = 0 };
3437 }
34093438
3410 return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty});
3439 _ = pl;
3440 return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty});
3441 } else {
3442 if (!payload_type.hasCodeGenBits()) {
3443 // We use the error type directly as the type.
3444 return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val });
3445 }
3446
3447 return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty});
3448 }
34113449 },
34123450 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),
34133451 }
test/stage2/arm.zig+49
......@@ -588,4 +588,53 @@ pub fn addCases(ctx: *TestContext) !void {
588588 "",
589589 );
590590 }
591
592 {
593 var case = ctx.exe("errors", linux_arm);
594 case.addCompareOutput(
595 \\pub fn main() void {
596 \\ foo() catch print();
597 \\}
598 \\
599 \\fn foo() anyerror!void {}
600 \\
601 \\fn print() void {
602 \\ asm volatile ("svc #0"
603 \\ :
604 \\ : [number] "{r7}" (4),
605 \\ [arg1] "{r0}" (1),
606 \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
607 \\ [arg3] "{r2}" ("Hello, World!\n".len),
608 \\ : "memory"
609 \\ );
610 \\ return;
611 \\}
612 ,
613 "",
614 );
615
616 case.addCompareOutput(
617 \\pub fn main() void {
618 \\ foo() catch print();
619 \\}
620 \\
621 \\fn foo() anyerror!void {
622 \\ return error.Test;
623 \\}
624 \\
625 \\fn print() void {
626 \\ asm volatile ("svc #0"
627 \\ :
628 \\ : [number] "{r7}" (4),
629 \\ [arg1] "{r0}" (1),
630 \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
631 \\ [arg3] "{r2}" ("Hello, World!\n".len),
632 \\ : "memory"
633 \\ );
634 \\ return;
635 \\}
636 ,
637 "Hello, World!\n",
638 );
639 }
591640}