authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-12-29 12:21:31+08:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-12-29 11:12:08+01:00
log1caf56c5fbbb10fa28f8bf204d073983ce2a6dd5
tree024193b17c0c447172874a360d4fde8a4e10bf67
parent34887cf136878c87357fa0eec52a12db300d8f27

stage2 AArch64: implement errUnion{Err,Payload} for registers


6 files changed, 139 insertions(+), 41 deletions(-)

src/arch/aarch64/CodeGen.zig+139-31
......@@ -3050,19 +3050,60 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
30503050}
30513051
30523052/// Given an error union, returns the error
3053fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue {
3053fn errUnionErr(
3054 self: *Self,
3055 error_union_bind: ReadArg.Bind,
3056 error_union_ty: Type,
3057 maybe_inst: ?Air.Inst.Index,
3058) !MCValue {
30543059 const err_ty = error_union_ty.errorUnionSet();
30553060 const payload_ty = error_union_ty.errorUnionPayload();
30563061 if (err_ty.errorSetIsEmpty()) {
30573062 return MCValue{ .immediate = 0 };
30583063 }
30593064 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3060 return error_union_mcv;
3065 return try error_union_bind.resolveToMcv(self);
30613066 }
30623067
30633068 const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*));
3064 switch (error_union_mcv) {
3065 .register => return self.fail("TODO errUnionErr for registers", .{}),
3069 switch (try error_union_bind.resolveToMcv(self)) {
3070 .register => {
3071 var operand_reg: Register = undefined;
3072 var dest_reg: Register = undefined;
3073
3074 const read_args = [_]ReadArg{
3075 .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg },
3076 };
3077 const write_args = [_]WriteArg{
3078 .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg },
3079 };
3080 try self.allocRegs(
3081 &read_args,
3082 &write_args,
3083 if (maybe_inst) |inst| .{
3084 .corresponding_inst = inst,
3085 .operand_mapping = &.{0},
3086 } else null,
3087 );
3088
3089 const err_bit_offset = err_offset * 8;
3090 const err_bit_size = @intCast(u32, err_ty.abiSize(self.target.*)) * 8;
3091
3092 _ = try self.addInst(.{
3093 .tag = .ubfx, // errors are unsigned integers
3094 .data = .{
3095 .rr_lsb_width = .{
3096 // Set both registers to the X variant to get the full width
3097 .rd = dest_reg.toX(),
3098 .rn = operand_reg.toX(),
3099 .lsb = @intCast(u6, err_bit_offset),
3100 .width = @intCast(u7, err_bit_size),
3101 },
3102 },
3103 });
3104
3105 return MCValue{ .register = dest_reg };
3106 },
30663107 .stack_argument_offset => |off| {
30673108 return MCValue{ .stack_argument_offset = off + err_offset };
30683109 },
......@@ -3079,27 +3120,69 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV
30793120fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
30803121 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
30813122 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3123 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
30823124 const error_union_ty = self.air.typeOf(ty_op.operand);
3083 const mcv = try self.resolveInst(ty_op.operand);
3084 break :result try self.errUnionErr(mcv, error_union_ty);
3125
3126 break :result try self.errUnionErr(error_union_bind, error_union_ty, inst);
30853127 };
30863128 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
30873129}
30883130
30893131/// Given an error union, returns the payload
3090fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue {
3132fn errUnionPayload(
3133 self: *Self,
3134 error_union_bind: ReadArg.Bind,
3135 error_union_ty: Type,
3136 maybe_inst: ?Air.Inst.Index,
3137) !MCValue {
30913138 const err_ty = error_union_ty.errorUnionSet();
30923139 const payload_ty = error_union_ty.errorUnionPayload();
30933140 if (err_ty.errorSetIsEmpty()) {
3094 return error_union_mcv;
3141 return try error_union_bind.resolveToMcv(self);
30953142 }
30963143 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
30973144 return MCValue.none;
30983145 }
30993146
31003147 const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*));
3101 switch (error_union_mcv) {
3102 .register => return self.fail("TODO errUnionPayload for registers", .{}),
3148 switch (try error_union_bind.resolveToMcv(self)) {
3149 .register => {
3150 var operand_reg: Register = undefined;
3151 var dest_reg: Register = undefined;
3152
3153 const read_args = [_]ReadArg{
3154 .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg },
3155 };
3156 const write_args = [_]WriteArg{
3157 .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg },
3158 };
3159 try self.allocRegs(
3160 &read_args,
3161 &write_args,
3162 if (maybe_inst) |inst| .{
3163 .corresponding_inst = inst,
3164 .operand_mapping = &.{0},
3165 } else null,
3166 );
3167
3168 const payload_bit_offset = payload_offset * 8;
3169 const payload_bit_size = @intCast(u32, payload_ty.abiSize(self.target.*)) * 8;
3170
3171 _ = try self.addInst(.{
3172 .tag = if (payload_ty.isSignedInt()) Mir.Inst.Tag.sbfx else .ubfx,
3173 .data = .{
3174 .rr_lsb_width = .{
3175 // Set both registers to the X variant to get the full width
3176 .rd = dest_reg.toX(),
3177 .rn = operand_reg.toX(),
3178 .lsb = @intCast(u5, payload_bit_offset),
3179 .width = @intCast(u6, payload_bit_size),
3180 },
3181 },
3182 });
3183
3184 return MCValue{ .register = dest_reg };
3185 },
31033186 .stack_argument_offset => |off| {
31043187 return MCValue{ .stack_argument_offset = off + payload_offset };
31053188 },
......@@ -3116,9 +3199,10 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)
31163199fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
31173200 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
31183201 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3202 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
31193203 const error_union_ty = self.air.typeOf(ty_op.operand);
3120 const error_union = try self.resolveInst(ty_op.operand);
3121 break :result try self.errUnionPayload(error_union, error_union_ty);
3204
3205 break :result try self.errUnionPayload(error_union_bind, error_union_ty, inst);
31223206 };
31233207 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
31243208}
......@@ -3399,9 +3483,14 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
33993483}
34003484
34013485fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
3402 const is_volatile = false; // TODO
34033486 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3404 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_val for {}", .{self.target.cpu.arch});
3487 const ptr_ty = self.air.typeOf(bin_op.lhs);
3488 const result: MCValue = if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: {
3489 const base_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
3490 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
3491
3492 break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst);
3493 };
34053494 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
34063495}
34073496
......@@ -4792,19 +4881,27 @@ fn isNonNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue
47924881 return MCValue{ .compare_flags = is_null_res.compare_flags.negate() };
47934882}
47944883
4795fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
4796 const error_type = ty.errorUnionSet();
4884fn isErr(
4885 self: *Self,
4886 error_union_bind: ReadArg.Bind,
4887 error_union_ty: Type,
4888) !MCValue {
4889 const error_type = error_union_ty.errorUnionSet();
47974890
47984891 if (error_type.errorSetIsEmpty()) {
47994892 return MCValue{ .immediate = 0 }; // always false
48004893 }
48014894
4802 const error_mcv = try self.errUnionErr(operand, ty);
4895 const error_mcv = try self.errUnionErr(error_union_bind, error_union_ty, null);
48034896 return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt);
48044897}
48054898
4806fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
4807 const is_err_result = try self.isErr(ty, operand);
4899fn isNonErr(
4900 self: *Self,
4901 error_union_bind: ReadArg.Bind,
4902 error_union_ty: Type,
4903) !MCValue {
4904 const is_err_result = try self.isErr(error_union_bind, error_union_ty);
48084905 switch (is_err_result) {
48094906 .compare_flags => |cond| {
48104907 assert(cond == .hi);
......@@ -4873,9 +4970,10 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
48734970fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
48744971 const un_op = self.air.instructions.items(.data)[inst].un_op;
48754972 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4876 const operand = try self.resolveInst(un_op);
4877 const ty = self.air.typeOf(un_op);
4878 break :result try self.isErr(ty, operand);
4973 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
4974 const error_union_ty = self.air.typeOf(un_op);
4975
4976 break :result try self.isErr(error_union_bind, error_union_ty);
48794977 };
48804978 return self.finishAir(inst, result, .{ un_op, .none, .none });
48814979}
......@@ -4890,7 +4988,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
48904988 const operand = try self.allocRegOrMem(elem_ty, true, null);
48914989 try self.load(operand, operand_ptr, ptr_ty);
48924990
4893 break :result try self.isErr(elem_ty, operand);
4991 break :result try self.isErr(.{ .mcv = operand }, elem_ty);
48944992 };
48954993 return self.finishAir(inst, result, .{ un_op, .none, .none });
48964994}
......@@ -4898,9 +4996,10 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
48984996fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
48994997 const un_op = self.air.instructions.items(.data)[inst].un_op;
49004998 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4901 const operand = try self.resolveInst(un_op);
4902 const ty = self.air.typeOf(un_op);
4903 break :result try self.isNonErr(ty, operand);
4999 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
5000 const error_union_ty = self.air.typeOf(un_op);
5001
5002 break :result try self.isNonErr(error_union_bind, error_union_ty);
49045003 };
49055004 return self.finishAir(inst, result, .{ un_op, .none, .none });
49065005}
......@@ -4915,7 +5014,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
49155014 const operand = try self.allocRegOrMem(elem_ty, true, null);
49165015 try self.load(operand, operand_ptr, ptr_ty);
49175016
4918 break :result try self.isNonErr(elem_ty, operand);
5017 break :result try self.isNonErr(.{ .mcv = operand }, elem_ty);
49195018 };
49205019 return self.finishAir(inst, result, .{ un_op, .none, .none });
49215020}
......@@ -5960,15 +6059,24 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void {
59606059 const extra = self.air.extraData(Air.Try, pl_op.payload);
59616060 const body = self.air.extra[extra.end..][0..extra.data.body_len];
59626061 const result: MCValue = result: {
6062 const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand };
59636063 const error_union_ty = self.air.typeOf(pl_op.operand);
5964 const error_union = try self.resolveInst(pl_op.operand);
5965 const is_err_result = try self.isErr(error_union_ty, error_union);
6064 const error_union_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
6065 const error_union_align = error_union_ty.abiAlignment(self.target.*);
6066
6067 // The error union will die in the body. However, we need the
6068 // error union after the body in order to extract the payload
6069 // of the error union, so we create a copy of it
6070 const error_union_copy = try self.allocMem(error_union_size, error_union_align, null);
6071 try self.genSetStack(error_union_ty, error_union_copy, try error_union_bind.resolveToMcv(self));
6072
6073 const is_err_result = try self.isErr(error_union_bind, error_union_ty);
59666074 const reloc = try self.condBr(is_err_result);
59676075
59686076 try self.genBody(body);
5969
59706077 try self.performReloc(reloc);
5971 break :result try self.errUnionPayload(error_union, error_union_ty);
6078
6079 break :result try self.errUnionPayload(.{ .mcv = .{ .stack_offset = error_union_copy } }, error_union_ty, null);
59726080 };
59736081 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
59746082}
test/behavior/error.zig-3
......@@ -60,7 +60,6 @@ pub fn baz() anyerror!i32 {
6060}
6161
6262test "error wrapping" {
63 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
6463 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
6564
6665 try expect((baz() catch unreachable) == 15);
......@@ -100,7 +99,6 @@ test "syntax: optional operator in front of error union operator" {
10099
101100test "widen cast integer payload of error union function call" {
102101 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
103 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
104102 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
105103
106104 const S = struct {
......@@ -715,7 +713,6 @@ test "ret_ptr doesn't cause own inferred error set to be resolved" {
715713}
716714
717715test "simple else prong allowed even when all errors handled" {
718 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
719716 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
720717 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
721718
test/behavior/if.zig-1
......@@ -44,7 +44,6 @@ var global_with_val: anyerror!u32 = 0;
4444var global_with_err: anyerror!u32 = error.SomeError;
4545
4646test "unwrap mutable global var" {
47 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
4847 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
4948
5049 if (global_with_val) |v| {
test/behavior/switch.zig-2
......@@ -390,7 +390,6 @@ fn switchWithUnreachable(x: i32) i32 {
390390}
391391
392392test "capture value of switch with all unreachable prongs" {
393 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
394393 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
395394
396395 const x = return_a_number() catch |err| switch (err) {
......@@ -494,7 +493,6 @@ test "switch prongs with error set cases make a new error set type for capture v
494493}
495494
496495test "return result loc and then switch with range implicit casted to error union" {
497 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
498496 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
499497
500498 const S = struct {
test/behavior/try.zig-1
......@@ -3,7 +3,6 @@ const builtin = @import("builtin");
33const expect = std.testing.expect;
44
55test "try on error union" {
6 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
76 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
87
98 try tryOnErrorUnionImpl();
test/behavior/while.zig-3
......@@ -175,7 +175,6 @@ test "while with optional as condition with else" {
175175
176176test "while with error union condition" {
177177 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
178 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
179178 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
180179
181180 numbers_left = 10;
......@@ -258,7 +257,6 @@ fn returnWithImplicitCastFromWhileLoopTest() anyerror!void {
258257}
259258
260259test "while on error union with else result follow else prong" {
261 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
262260 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
263261
264262 const result = while (returnError()) |value| {
......@@ -268,7 +266,6 @@ test "while on error union with else result follow else prong" {
268266}
269267
270268test "while on error union with else result follow break prong" {
271 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
272269 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
273270
274271 const result = while (returnSuccess(10)) |value| {