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(
15481548 };
15491549 const raw_reg = try self.register_manager.allocReg(track_inst, gp);
15501550 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.*);
15521552 }
15531553 }
15541554
......@@ -2882,10 +2882,65 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
28822882
28832883fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
28842884 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 };
28862890 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
28872891}
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
28892944fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
28902945 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
28912946 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 {
30123067
30133068fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
30143069 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 true
3019 if (optional_ty.abiSize(self.target.*) == 1)
3071 if (self.liveness.isUnused(inst)) {
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()) {
30203078 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 };
30233107 };
3108
30243109 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
30253110}
30263111
......@@ -4562,18 +4647,20 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
45624647 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
45634648}
45644649
4565fn isNull(self: *Self, operand: MCValue) !MCValue {
4566 _ = operand;
4567 // Here you can specialize this instruction if it makes sense to, otherwise the default
4568 // will call isNonNull and invert the result.
4569 return self.fail("TODO call isNonNull and invert the result", .{});
4650fn isNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue {
4651 const sentinel_ty: Type = if (!operand_ty.isPtrLikeOptional()) blk: {
4652 var buf: Type.Payload.ElemType = undefined;
4653 const payload_ty = operand_ty.optionalChild(&buf);
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);
45704658}
45714659
4572fn isNonNull(self: *Self, operand: MCValue) !MCValue {
4573 _ = operand;
4574 // Here you can specialize this instruction if it makes sense to, otherwise the default
4575 // will call isNull and invert the result.
4576 return self.fail("TODO call isNull and invert the result", .{});
4660fn isNonNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue {
4661 const is_null_res = try self.isNull(operand_bind, operand_ty);
4662 assert(is_null_res.compare_flags == .eq);
4663 return MCValue{ .compare_flags = is_null_res.compare_flags.negate() };
45774664}
45784665
45794666fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
......@@ -4606,7 +4693,9 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
46064693 const un_op = self.air.instructions.items(.data)[inst].un_op;
46074694 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
46084695 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);
46104699 };
46114700 return self.finishAir(inst, result, .{ un_op, .none, .none });
46124701}
......@@ -4621,7 +4710,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
46214710 const operand = try self.allocRegOrMem(elem_ty, true, null);
46224711 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);
46254714 };
46264715 return self.finishAir(inst, result, .{ un_op, .none, .none });
46274716}
......@@ -4630,7 +4719,9 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
46304719 const un_op = self.air.instructions.items(.data)[inst].un_op;
46314720 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
46324721 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);
46344725 };
46354726 return self.finishAir(inst, result, .{ un_op, .none, .none });
46364727}
......@@ -4645,7 +4736,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
46454736 const operand = try self.allocRegOrMem(elem_ty, true, null);
46464737 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);
46494740 };
46504741 return self.finishAir(inst, result, .{ un_op, .none, .none });
46514742}
test/behavior/optional.zig-7
......@@ -6,7 +6,6 @@ const expectEqual = testing.expectEqual;
66const expectEqualStrings = std.testing.expectEqualStrings;
77
88test "passing an optional integer as a parameter" {
9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
109 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1110
1211 const S = struct {
......@@ -26,8 +25,6 @@ test "passing an optional integer as a parameter" {
2625pub const EmptyStruct = struct {};
2726
2827test "optional pointer to size zero struct" {
29 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
30
3128 var e = EmptyStruct{};
3229 var o: ?*EmptyStruct = &e;
3330 try expect(o != null);
......@@ -142,7 +139,6 @@ fn test_cmp_optional_non_optional() !void {
142139}
143140
144141test "unwrap function call with optional pointer return value" {
145 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
146142 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
147143
148144 const S = struct {
......@@ -163,7 +159,6 @@ test "unwrap function call with optional pointer return value" {
163159}
164160
165161test "nested orelse" {
166 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
167162 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
168163
169164 const S = struct {
......@@ -189,7 +184,6 @@ test "nested orelse" {
189184}
190185
191186test "self-referential struct through a slice of optional" {
192 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
193187 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
194188 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" {
270264
271265test "0-bit child type coerced to optional" {
272266 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
273 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
274267
275268 const S = struct {
276269 fn doTheTest() !void {