From d35cae551ed5f3e6082b2e599f9c258af9d2630e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 2 Mar 2022 13:46:34 +0100 Subject: [PATCH] x64: rectify and add missing optionals bits Includes changes/additions to: * `wrap_optional` * `optional_payload` * `isNull` helper --- src/arch/x86_64/CodeGen.zig | 70 +++++++++++++++++++++++++++++++------ test/behavior/basic.zig | 1 - test/behavior/bugs/2889.zig | 1 - test/behavior/bugs/3112.zig | 1 - test/behavior/cast.zig | 2 +- test/behavior/enum.zig | 1 - test/behavior/optional.zig | 4 --- 7 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index c511a26eac..3ecf4a70aa 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1704,12 +1704,36 @@ fn airShr(self: *Self, inst: Air.Inst.Index) !void { fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand = try self.resolveInst(ty_op.operand); - if (self.reuseOperand(inst, ty_op.operand, 0, operand)) { - break :result operand; + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); + } + + const payload_ty = self.air.typeOfIndex(inst); + const optional_ty = self.air.typeOf(ty_op.operand); + const operand = try self.resolveInst(ty_op.operand); + const result: MCValue = result: { + if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; + if (optional_ty.isPtrLikeOptional()) { + if (self.reuseOperand(inst, ty_op.operand, 0, operand)) { + break :result operand; + } + break :result try self.copyToRegisterWithInstTracking(inst, payload_ty, operand); + } + + const offset = optional_ty.abiSize(self.target.*) - payload_ty.abiSize(self.target.*); + switch (operand) { + .stack_offset => |off| { + break :result MCValue{ .stack_offset = off - @intCast(i32, offset) }; + }, + .register => { + // TODO reuse the operand + const result = try self.copyToRegisterWithInstTracking(inst, optional_ty, operand); + const shift = @intCast(u8, offset * 8); + try self.shiftRegister(result.register, @intCast(u8, shift)); + break :result result; + }, + else => return self.fail("TODO implement optional_payload when operand is {}", .{operand}), } - break :result try self.copyToRegisterWithInstTracking(inst, self.air.typeOfIndex(inst), operand); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -1828,13 +1852,30 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const optional_ty = self.air.typeOfIndex(inst); - - // Optional with a zero-bit payload type is just a boolean true - if (optional_ty.abiSize(self.target.*) == 1) + const payload_ty = self.air.typeOf(ty_op.operand); + if (!payload_ty.hasRuntimeBits()) { break :result MCValue{ .immediate = 1 }; + } - return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); + const optional_ty = self.air.typeOfIndex(inst); + const operand = try self.resolveInst(ty_op.operand); + if (optional_ty.isPtrLikeOptional()) { + // TODO should we check if we can reuse the operand? + break :result operand; + } + + operand.freezeIfRegister(&self.register_manager); + defer operand.unfreezeIfRegister(&self.register_manager); + + const optional_abi_size = @intCast(u32, optional_ty.abiSize(self.target.*)); + const optional_abi_align = optional_ty.abiAlignment(self.target.*); + const payload_abi_size = @intCast(u32, payload_ty.abiSize(self.target.*)); + const offset = optional_abi_size - payload_abi_size; + + const stack_offset = @intCast(i32, try self.allocMem(inst, optional_abi_size, optional_abi_align)); + try self.genSetStack(Type.bool, stack_offset, .{ .immediate = 1 }, .{}); + try self.genSetStack(payload_ty, stack_offset - @intCast(i32, offset), operand, .{}); + break :result MCValue{ .stack_offset = stack_offset }; }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -3761,7 +3802,14 @@ fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValu try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - try self.genBinMathOpMir(.cmp, ty, operand, MCValue{ .immediate = 0 }); + const cmp_ty: Type = if (!ty.isPtrLikeOptional()) blk: { + var buf: Type.Payload.ElemType = undefined; + const payload_ty = ty.optionalChild(&buf); + break :blk if (payload_ty.hasRuntimeBits()) Type.bool else ty; + } else ty; + + try self.genBinMathOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 }); + return MCValue{ .compare_flags_unsigned = .eq }; } diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index b122314720..2bb4bb3e44 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -728,7 +728,6 @@ test "thread local variable" { test "result location is optional inside error union" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/2889.zig b/test/behavior/bugs/2889.zig index 8bb70cb198..eec2232ca7 100644 --- a/test/behavior/bugs/2889.zig +++ b/test/behavior/bugs/2889.zig @@ -27,7 +27,6 @@ fn parseNote() ?i32 { } test "fixed" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; diff --git a/test/behavior/bugs/3112.zig b/test/behavior/bugs/3112.zig index ebd8fd1ef3..96c9249d67 100644 --- a/test/behavior/bugs/3112.zig +++ b/test/behavior/bugs/3112.zig @@ -16,7 +16,6 @@ test "zig test crash" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; var global: State = undefined; global.enter = prev; global.enter(null); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index d6e8368e8e..39c142f3f0 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -237,7 +237,7 @@ test "@intCast to u0 and use the result" { test "peer result null and comptime_int" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn blah(n: i32) ?i32 { diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index fda8cfe745..161f63b156 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -1083,7 +1083,6 @@ test "@tagName on enum literals" { } test "enum literal casting to optional" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index d6963a6143..ed0106188b 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -7,7 +7,6 @@ const expectEqual = testing.expectEqual; test "passing an optional integer as a parameter" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { fn entry() bool { @@ -60,7 +59,6 @@ fn testNullPtrsEql() !void { test "optional with void type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const Foo = struct { x: ?void, @@ -171,7 +169,6 @@ test "unwrap function call with optional pointer return value" { test "nested orelse" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { fn entry() !void { @@ -199,7 +196,6 @@ test "self-referential struct through a slice of optional" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { const Node = struct {