authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-09 21:13:50+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-11-09 21:13:50+01:00
log2d5fbbb44e15b07531251ee406a0df73321e8175
tree94a05b2dc1c74b0e34fc3799123e92ec9c85e3ae
parent41b7e40d75bdd415da0daef6fa6a71dc4686320f
parente83590d0e887e3b856e2e638c608fa821c1efa2e
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13485 from ziglang/arm64-non-null-actual

aarch64: optionals

2 files changed, 112 insertions(+), 28 deletions(-)

src/arch/aarch64/CodeGen.zig+112-21
...@@ -1548,7 +1548,7 @@ fn allocRegs(...@@ -1548,7 +1548,7 @@ fn allocRegs(
1548 };1548 };
1549 const raw_reg = try self.register_manager.allocReg(track_inst, gp);1549 const raw_reg = try self.register_manager.allocReg(track_inst, gp);
1550 arg.reg.* = self.registerAlias(raw_reg, arg.ty);1550 arg.reg.* = self.registerAlias(raw_reg, arg.ty);
1551 read_locks[i] = self.register_manager.lockReg(arg.reg.*);1551 read_locks[i] = self.register_manager.lockRegAssumeUnused(arg.reg.*);
1552 }1552 }
1553 }1553 }
15541554
...@@ -2882,10 +2882,65 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -2882,10 +2882,65 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
28822882
2883fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {2883fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
2884 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2884 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2885 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});2885 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2886 const optional_ty = self.air.typeOf(ty_op.operand);
2887 const mcv = try self.resolveInst(ty_op.operand);
2888 break :result try self.optionalPayload(inst, mcv, optional_ty);
2889 };
2886 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2890 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2887}2891}
28882892
2893fn optionalPayload(self: *Self, inst: Air.Inst.Index, mcv: MCValue, optional_ty: Type) !MCValue {
2894 var opt_buf: Type.Payload.ElemType = undefined;
2895 const payload_ty = optional_ty.optionalChild(&opt_buf);
2896 if (!payload_ty.hasRuntimeBits()) return MCValue.none;
2897 if (optional_ty.isPtrLikeOptional()) {
2898 // TODO should we reuse the operand here?
2899 const raw_reg = try self.register_manager.allocReg(inst, gp);
2900 const reg = self.registerAlias(raw_reg, payload_ty);
2901 try self.genSetReg(payload_ty, reg, mcv);
2902 return MCValue{ .register = reg };
2903 }
2904
2905 const offset = @intCast(u32, optional_ty.abiSize(self.target.*) - payload_ty.abiSize(self.target.*));
2906 switch (mcv) {
2907 .register => |source_reg| {
2908 // TODO should we reuse the operand here?
2909 const raw_reg = try self.register_manager.allocReg(inst, gp);
2910 const dest_reg = raw_reg.toX();
2911
2912 const shift = @intCast(u6, offset * 8);
2913 if (shift == 0) {
2914 try self.genSetReg(payload_ty, dest_reg, mcv);
2915 } else {
2916 _ = try self.addInst(.{
2917 .tag = if (payload_ty.isSignedInt())
2918 Mir.Inst.Tag.asr_immediate
2919 else
2920 Mir.Inst.Tag.lsr_immediate,
2921 .data = .{ .rr_shift = .{
2922 .rd = dest_reg,
2923 .rn = source_reg.toX(),
2924 .shift = shift,
2925 } },
2926 });
2927 }
2928
2929 return MCValue{ .register = self.registerAlias(dest_reg, payload_ty) };
2930 },
2931 .stack_argument_offset => |off| {
2932 return MCValue{ .stack_argument_offset = off + offset };
2933 },
2934 .stack_offset => |off| {
2935 return MCValue{ .stack_offset = off - offset };
2936 },
2937 .memory => |addr| {
2938 return MCValue{ .memory = addr + offset };
2939 },
2940 else => unreachable, // invalid MCValue for an error union
2941 }
2942}
2943
2889fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {2944fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
2890 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2945 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2891 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});2946 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
...@@ -3012,15 +3067,45 @@ fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {...@@ -3012,15 +3067,45 @@ fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void {
30123067
3013fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {3068fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
3014 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3069 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3015 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3016 const optional_ty = self.air.typeOfIndex(inst);
30173070
3018 // Optional with a zero-bit payload type is just a boolean true3071 if (self.liveness.isUnused(inst)) {
3019 if (optional_ty.abiSize(self.target.*) == 1)3072 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
3073 }
3074
3075 const result: MCValue = result: {
3076 const payload_ty = self.air.typeOf(ty_op.operand);
3077 if (!payload_ty.hasRuntimeBits()) {
3020 break :result MCValue{ .immediate = 1 };3078 break :result MCValue{ .immediate = 1 };
3079 }
3080
3081 const optional_ty = self.air.typeOfIndex(inst);
3082 const operand = try self.resolveInst(ty_op.operand);
3083 const operand_lock: ?RegisterLock = switch (operand) {
3084 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
3085 else => null,
3086 };
3087 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
3088
3089 if (optional_ty.isPtrLikeOptional()) {
3090 // TODO should we check if we can reuse the operand?
3091 const raw_reg = try self.register_manager.allocReg(inst, gp);
3092 const reg = self.registerAlias(raw_reg, payload_ty);
3093 try self.genSetReg(payload_ty, raw_reg, operand);
3094 break :result MCValue{ .register = reg };
3095 }
3096
3097 const optional_abi_size = @intCast(u32, optional_ty.abiSize(self.target.*));
3098 const optional_abi_align = optional_ty.abiAlignment(self.target.*);
3099 const payload_abi_size = @intCast(u32, payload_ty.abiSize(self.target.*));
3100 const offset = optional_abi_size - payload_abi_size;
3101
3102 const stack_offset = try self.allocMem(optional_abi_size, optional_abi_align, inst);
3103 try self.genSetStack(Type.bool, stack_offset, .{ .immediate = 1 });
3104 try self.genSetStack(payload_ty, stack_offset - @intCast(u32, offset), operand);
30213105
3022 return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch});3106 break :result MCValue{ .stack_offset = stack_offset };
3023 };3107 };
3108
3024 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3109 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
3025}3110}
30263111
...@@ -4562,18 +4647,20 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4562,18 +4647,20 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
4562 return self.finishAir(inst, .unreach, .{ .none, .none, .none });4647 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
4563}4648}
45644649
4565fn isNull(self: *Self, operand: MCValue) !MCValue {4650fn isNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue {
4566 _ = operand;4651 const sentinel_ty: Type = if (!operand_ty.isPtrLikeOptional()) blk: {
4567 // Here you can specialize this instruction if it makes sense to, otherwise the default4652 var buf: Type.Payload.ElemType = undefined;
4568 // will call isNonNull and invert the result.4653 const payload_ty = operand_ty.optionalChild(&buf);
4569 return self.fail("TODO call isNonNull and invert the result", .{});4654 break :blk if (payload_ty.hasRuntimeBitsIgnoreComptime()) Type.bool else operand_ty;
4655 } else operand_ty;
4656 const imm_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 0 } };
4657 return self.cmp(operand_bind, imm_bind, sentinel_ty, .eq);
4570}4658}
45714659
4572fn isNonNull(self: *Self, operand: MCValue) !MCValue {4660fn isNonNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue {
4573 _ = operand;4661 const is_null_res = try self.isNull(operand_bind, operand_ty);
4574 // Here you can specialize this instruction if it makes sense to, otherwise the default4662 assert(is_null_res.compare_flags == .eq);
4575 // will call isNull and invert the result.4663 return MCValue{ .compare_flags = is_null_res.compare_flags.negate() };
4576 return self.fail("TODO call isNull and invert the result", .{});
4577}4664}
45784665
4579fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {4666fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
...@@ -4606,7 +4693,9 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {...@@ -4606,7 +4693,9 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
4606 const un_op = self.air.instructions.items(.data)[inst].un_op;4693 const un_op = self.air.instructions.items(.data)[inst].un_op;
4607 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4694 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4608 const operand = try self.resolveInst(un_op);4695 const operand = try self.resolveInst(un_op);
4609 break :result try self.isNull(operand);4696 const operand_ty = self.air.typeOf(un_op);
4697
4698 break :result try self.isNull(.{ .mcv = operand }, operand_ty);
4610 };4699 };
4611 return self.finishAir(inst, result, .{ un_op, .none, .none });4700 return self.finishAir(inst, result, .{ un_op, .none, .none });
4612}4701}
...@@ -4621,7 +4710,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4621,7 +4710,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4621 const operand = try self.allocRegOrMem(elem_ty, true, null);4710 const operand = try self.allocRegOrMem(elem_ty, true, null);
4622 try self.load(operand, operand_ptr, ptr_ty);4711 try self.load(operand, operand_ptr, ptr_ty);
46234712
4624 break :result try self.isNull(operand);4713 break :result try self.isNull(.{ .mcv = operand }, elem_ty);
4625 };4714 };
4626 return self.finishAir(inst, result, .{ un_op, .none, .none });4715 return self.finishAir(inst, result, .{ un_op, .none, .none });
4627}4716}
...@@ -4630,7 +4719,9 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {...@@ -4630,7 +4719,9 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
4630 const un_op = self.air.instructions.items(.data)[inst].un_op;4719 const un_op = self.air.instructions.items(.data)[inst].un_op;
4631 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4720 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4632 const operand = try self.resolveInst(un_op);4721 const operand = try self.resolveInst(un_op);
4633 break :result try self.isNonNull(operand);4722 const operand_ty = self.air.typeOf(un_op);
4723
4724 break :result try self.isNonNull(.{ .mcv = operand }, operand_ty);
4634 };4725 };
4635 return self.finishAir(inst, result, .{ un_op, .none, .none });4726 return self.finishAir(inst, result, .{ un_op, .none, .none });
4636}4727}
...@@ -4645,7 +4736,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4645,7 +4736,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4645 const operand = try self.allocRegOrMem(elem_ty, true, null);4736 const operand = try self.allocRegOrMem(elem_ty, true, null);
4646 try self.load(operand, operand_ptr, ptr_ty);4737 try self.load(operand, operand_ptr, ptr_ty);
46474738
4648 break :result try self.isNonNull(operand);4739 break :result try self.isNonNull(.{ .mcv = operand }, elem_ty);
4649 };4740 };
4650 return self.finishAir(inst, result, .{ un_op, .none, .none });4741 return self.finishAir(inst, result, .{ un_op, .none, .none });
4651}4742}
test/behavior/optional.zig-7
...@@ -6,7 +6,6 @@ const expectEqual = testing.expectEqual;...@@ -6,7 +6,6 @@ const expectEqual = testing.expectEqual;
6const expectEqualStrings = std.testing.expectEqualStrings;6const expectEqualStrings = std.testing.expectEqualStrings;
77
8test "passing an optional integer as a parameter" {8test "passing an optional integer as a parameter" {
9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
10 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1110
12 const S = struct {11 const S = struct {
...@@ -26,8 +25,6 @@ test "passing an optional integer as a parameter" {...@@ -26,8 +25,6 @@ test "passing an optional integer as a parameter" {
26pub const EmptyStruct = struct {};25pub const EmptyStruct = struct {};
2726
28test "optional pointer to size zero struct" {27test "optional pointer to size zero struct" {
29 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
30
31 var e = EmptyStruct{};28 var e = EmptyStruct{};
32 var o: ?*EmptyStruct = &e;29 var o: ?*EmptyStruct = &e;
33 try expect(o != null);30 try expect(o != null);
...@@ -142,7 +139,6 @@ fn test_cmp_optional_non_optional() !void {...@@ -142,7 +139,6 @@ fn test_cmp_optional_non_optional() !void {
142}139}
143140
144test "unwrap function call with optional pointer return value" {141test "unwrap function call with optional pointer return value" {
145 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
146 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO142 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
147143
148 const S = struct {144 const S = struct {
...@@ -163,7 +159,6 @@ test "unwrap function call with optional pointer return value" {...@@ -163,7 +159,6 @@ test "unwrap function call with optional pointer return value" {
163}159}
164160
165test "nested orelse" {161test "nested orelse" {
166 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
167 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO162 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
168163
169 const S = struct {164 const S = struct {
...@@ -189,7 +184,6 @@ test "nested orelse" {...@@ -189,7 +184,6 @@ test "nested orelse" {
189}184}
190185
191test "self-referential struct through a slice of optional" {186test "self-referential struct through a slice of optional" {
192 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
193 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO187 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
194 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO188 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
195189
...@@ -270,7 +264,6 @@ test "0-bit child type coerced to optional return ptr result location" {...@@ -270,7 +264,6 @@ test "0-bit child type coerced to optional return ptr result location" {
270264
271test "0-bit child type coerced to optional" {265test "0-bit child type coerced to optional" {
272 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO266 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
273 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
274267
275 const S = struct {268 const S = struct {
276 fn doTheTest() !void {269 fn doTheTest() !void {