From 2e327d9b63036abf2556e21693350a5169b888fb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 2 Mar 2022 11:10:35 +0100 Subject: [PATCH 1/6] codegen: lower optionals and floats across linking backends --- src/codegen.zig | 106 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 13 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 62209700039ae468bed491a01209d7b11d7291e2..56888cab3654971cc23301b621fdae7bd0b4a79f 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -140,6 +140,29 @@ pub fn generateFunction( } } +fn writeFloat(comptime F: type, f: F, target: Target, endian: std.builtin.Endian, code: []u8) void { + if (F == f80) { + switch (target.cpu.arch) { + .i386, .x86_64 => { + const repr = math.break_f80(f); + mem.writeIntLittle(u64, code[0..8], repr.fraction); + mem.writeIntLittle(u16, code[8..10], repr.exp); + // TODO set the rest of the bytes to undefined. should we use 0xaa + // or is there a different way? + return; + }, + else => {}, + } + } else { + const Int = @Type(.{ .Int = .{ + .signedness = .unsigned, + .bits = @typeInfo(F).Float.bits, + } }); + const int = @bitCast(Int, f); + mem.writeInt(Int, code[0..@sizeOf(Int)], int, endian); + } +} + pub fn generateSymbol( bin_file: *link.File, src_loc: Module.SrcLoc, @@ -151,10 +174,12 @@ pub fn generateSymbol( const tracy = trace(@src()); defer tracy.end(); + const target = bin_file.options.target; + const endian = target.cpu.arch.endian(); + log.debug("generateSymbol: ty = {}, val = {}", .{ typed_value.ty, typed_value.val }); if (typed_value.val.isUndefDeep()) { - const target = bin_file.options.target; const abi_size = try math.cast(usize, typed_value.ty.abiSize(target)); try code.appendNTimes(0xaa, abi_size); return Result{ .appended = {} }; @@ -171,6 +196,18 @@ pub fn generateSymbol( ), }; }, + .Float => { + const float_bits = typed_value.ty.floatBits(target); + switch (float_bits) { + 16 => writeFloat(f16, typed_value.val.toFloat(f16), target, endian, try code.addManyAsArray(2)), + 32 => writeFloat(f32, typed_value.val.toFloat(f32), target, endian, try code.addManyAsArray(4)), + 64 => writeFloat(f64, typed_value.val.toFloat(f64), target, endian, try code.addManyAsArray(8)), + 80 => writeFloat(f80, typed_value.val.toFloat(f80), target, endian, try code.addManyAsArray(10)), + 128 => writeFloat(f128, typed_value.val.toFloat(f128), target, endian, try code.addManyAsArray(16)), + else => unreachable, + } + return Result{ .appended = {} }; + }, .Array => switch (typed_value.val.tag()) { .bytes => { // TODO populate .debug_info for the array @@ -311,7 +348,6 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .field_ptr => { - const target = bin_file.options.target; const field_ptr = typed_value.val.castTag(.field_ptr).?.data; const container_ptr = field_ptr.container_ptr; @@ -373,7 +409,6 @@ pub fn generateSymbol( }, .Int => { // TODO populate .debug_info for the integer - const endian = bin_file.options.target.cpu.arch.endian(); const info = typed_value.ty.intInfo(bin_file.options.target); if (info.bits <= 8) { const x = @intCast(u8, typed_value.val.toUnsignedInt()); @@ -423,7 +458,6 @@ pub fn generateSymbol( var int_buffer: Value.Payload.U64 = undefined; const int_val = typed_value.enumToInt(&int_buffer); - const target = bin_file.options.target; const info = typed_value.ty.intInfo(target); if (info.bits <= 8) { const x = @intCast(u8, int_val.toUnsignedInt()); @@ -440,7 +474,6 @@ pub fn generateSymbol( ), }; } - const endian = target.cpu.arch.endian(); switch (info.signedness) { .unsigned => { if (info.bits <= 16) { @@ -506,7 +539,6 @@ pub fn generateSymbol( const unpadded_field_end = code.items.len - struct_begin; // Pad struct members if required - const target = bin_file.options.target; const padded_field_end = typed_value.ty.structFieldOffset(index + 1, target); const padding = try math.cast(usize, padded_field_end - unpadded_field_end); @@ -519,7 +551,6 @@ pub fn generateSymbol( }, .Union => { // TODO generate debug info for unions - const target = bin_file.options.target; const union_obj = typed_value.val.castTag(.@"union").?.data; const layout = typed_value.ty.unionGetLayout(target); @@ -590,19 +621,69 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .Optional => { - // TODO generateSymbol for optionals - const target = bin_file.options.target; + // TODO generate debug info for optionals + var opt_buf: Type.Payload.ElemType = undefined; + const payload_type = typed_value.ty.optionalChild(&opt_buf); + const is_pl = !typed_value.val.isNull(); const abi_size = try math.cast(usize, typed_value.ty.abiSize(target)); - try code.writer().writeByteNTimes(0xaa, abi_size); + const offset = abi_size - try math.cast(usize, payload_type.abiSize(target)); + + if (!payload_type.hasRuntimeBits()) { + try code.writer().writeByteNTimes(@boolToInt(is_pl), abi_size); + return Result{ .appended = {} }; + } + + if (typed_value.ty.isPtrLikeOptional()) { + if (typed_value.val.castTag(.opt_payload)) |payload| { + switch (try generateSymbol(bin_file, src_loc, .{ + .ty = payload_type, + .val = payload.data, + }, code, debug_output, reloc_info)) { + .appended => {}, + .externally_managed => |external_slice| { + code.appendSliceAssumeCapacity(external_slice); + }, + .fail => |em| return Result{ .fail = em }, + } + } else if (!typed_value.val.isNull()) { + switch (try generateSymbol(bin_file, src_loc, .{ + .ty = payload_type, + .val = typed_value.val, + }, code, debug_output, reloc_info)) { + .appended => {}, + .externally_managed => |external_slice| { + code.appendSliceAssumeCapacity(external_slice); + }, + .fail => |em| return Result{ .fail = em }, + } + } else { + try code.writer().writeByteNTimes(0, abi_size); + } + + return Result{ .appended = {} }; + } + + const value = if (typed_value.val.castTag(.opt_payload)) |payload| payload.data else Value.initTag(.undef); + try code.writer().writeByteNTimes(@boolToInt(is_pl), offset); + switch (try generateSymbol(bin_file, src_loc, .{ + .ty = payload_type, + .val = value, + }, code, debug_output, reloc_info)) { + .appended => {}, + .externally_managed => |external_slice| { + code.appendSliceAssumeCapacity(external_slice); + }, + .fail => |em| return Result{ .fail = em }, + } return Result{ .appended = {} }; }, .ErrorUnion => { + // TODO generate debug info for error unions const error_ty = typed_value.ty.errorUnionSet(); const payload_ty = typed_value.ty.errorUnionPayload(); const is_payload = typed_value.val.errorUnionIsPayload(); - const target = bin_file.options.target; const abi_align = typed_value.ty.abiAlignment(target); const error_val = if (!is_payload) typed_value.val else Value.initTag(.zero); @@ -643,12 +724,11 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .ErrorSet => { - const target = bin_file.options.target; + // TODO generate debug info for error sets switch (typed_value.val.tag()) { .@"error" => { const name = typed_value.val.getError().?; const kv = try bin_file.options.module.?.getErrorValue(name); - const endian = target.cpu.arch.endian(); try code.writer().writeInt(u32, kv.value, endian); }, else => { -- 2.54.0 From d35cae551ed5f3e6082b2e599f9c258af9d2630e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 2 Mar 2022 13:46:34 +0100 Subject: [PATCH 2/6] 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 c511a26eaccb9676ea9619bbb8479281c188d9f2..3ecf4a70aaae762f4d88e50a18a52e1de3d28a07 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 b122314720c5921a5b84f2c264d3490d396c5976..2bb4bb3e447061fbf8db00ff2d49ff34464dbe5f 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 8bb70cb1987ce00454668aeca5a77fecfe379ec5..eec2232ca7f4f53cdfd7bb01e8cb4fedadc5d428 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 ebd8fd1ef3899a0323dacf6aacac355bcca70392..96c9249d673b9ebe2491602f676ffacef4b5af62 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 d6e8368e8e27ace86c675f17025954cfc769bf72..39c142f3f0d7034b0571e0725815cf0ea349227a 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 fda8cfe7454b011562e3f23558eb18d7be387882..161f63b1569844a48a8a0df655cfb2716beda797 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 d6963a6143389dad936e10bbfb5725722c54153a..ed0106188bd2a5659b5fba7720c68ffe4e73b5d2 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 { -- 2.54.0 From 350bf9db131dd5573da0d06d38e40746c99a8a34 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 2 Mar 2022 14:29:48 +0100 Subject: [PATCH 3/6] x64: fix intCast to properly clear out dest register --- src/arch/x86_64/CodeGen.zig | 24 +++++++++++++++++------- test/behavior/cast.zig | 10 +--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 3ecf4a70aaae762f4d88e50a18a52e1de3d28a07..8efb1042ef8ac240d76176fd9956a18fe9e118ad 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -981,7 +981,10 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { operand.freezeIfRegister(&self.register_manager); defer operand.unfreezeIfRegister(&self.register_manager); - break :blk try self.copyToRegisterWithInstTracking(inst, dest_ty, operand); + const reg = try self.register_manager.allocReg(inst); + try self.genSetReg(dest_ty, reg, .{ .immediate = 0 }); + try self.genSetReg(operand_ty, reg, operand); + break :blk MCValue{ .register = reg }; }; return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); @@ -1851,22 +1854,29 @@ 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 payload_ty = self.air.typeOf(ty_op.operand); + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); + } + + const payload_ty = self.air.typeOf(ty_op.operand); + const result: MCValue = result: { if (!payload_ty.hasRuntimeBits()) { break :result MCValue{ .immediate = 1 }; } const optional_ty = self.air.typeOfIndex(inst); const operand = try self.resolveInst(ty_op.operand); + operand.freezeIfRegister(&self.register_manager); + defer operand.unfreezeIfRegister(&self.register_manager); + if (optional_ty.isPtrLikeOptional()) { // TODO should we check if we can reuse the operand? - break :result operand; + if (self.reuseOperand(inst, ty_op.operand, 0, operand)) { + break :result operand; + } + break :result try self.copyToRegisterWithInstTracking(inst, payload_ty, 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.*)); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 39c142f3f0d7034b0571e0725815cf0ea349227a..30c3e12ce09f518dc805791065eef7dffe3c34df 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -302,7 +302,7 @@ fn implicitIntLitToOptional() void { test "return u8 coercing into ?u32 return type" { 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 doTheTest() !void { @@ -373,7 +373,6 @@ fn testPeerResolveArrayConstSlice(b: bool) !void { test "implicitly cast from T to anyerror!?T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try castToOptionalTypeError(1); comptime try castToOptionalTypeError(1); @@ -1036,7 +1035,6 @@ test "implicit cast from [*]T to ?*anyopaque" { 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 var a = [_]u8{ 3, 2, 1 }; var runtime_zero: usize = 0; @@ -1073,7 +1071,6 @@ test "implicit ptr to *anyopaque" { 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 var a: u32 = 1; var ptr: *align(@alignOf(u32)) anyopaque = &a; @@ -1087,7 +1084,6 @@ test "implicit ptr to *anyopaque" { test "return null from fn() anyerror!?&T" { 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 a = returnNullFromOptionalTypeErrorRef(); const b = returnNullLitFromOptionalTypeErrorRef(); @@ -1125,7 +1121,6 @@ test "implicitly cast from [N]T to ?[]const T" { 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 try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); @@ -1177,7 +1172,6 @@ test "implicit cast from *T to ?*anyopaque" { 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 var a: u8 = 1; incrementVoidPtrValue(&a); @@ -1213,7 +1207,6 @@ test "*const [N]null u8 to ?[]const u8" { 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 { fn doTheTest() !void { @@ -1249,7 +1242,6 @@ test "assignment to optional pointer result loc" { 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 var foo: struct { ptr: ?*anyopaque } = .{ .ptr = &global_struct }; try expect(foo.ptr.? == @ptrCast(*anyopaque, &global_struct)); -- 2.54.0 From c9d1db7e8eda6374bfc5ba51097e24d93ab91ddb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 2 Mar 2022 14:53:02 +0100 Subject: [PATCH 4/6] x64: fix incorrect calc of rdi spill stack loc for backpatching --- src/arch/x86_64/CodeGen.zig | 7 ++++++- test/behavior/cast.zig | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8efb1042ef8ac240d76176fd9956a18fe9e118ad..da98c1c90124da3a71f92fb893ffada6dea9bf68 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -472,10 +472,14 @@ fn gen(self: *Self) InnerError!void { .regs = 0, .disp = mem.alignForwardGeneric(u32, self.next_stack_offset, 8), }; + var disp = data.disp + 8; inline for (callee_preserved_regs) |reg, i| { if (self.register_manager.isRegAllocated(reg)) { if (reg.to64() == .rdi) { for (self.ret_backpatches.items) |inst| { + log.debug(".rdi was spilled, backpatching with mov from stack at offset {}", .{ + -@intCast(i32, disp), + }); const ops = Mir.Ops.decode(self.mir_instructions.items(.ops)[inst]); self.mir_instructions.set(inst, Mir.Inst{ .tag = .mov, @@ -484,12 +488,13 @@ fn gen(self: *Self) InnerError!void { .reg2 = .rbp, .flags = 0b01, }).encode(), - .data = .{ .imm = @bitCast(u32, -@intCast(i32, self.max_end_stack + 8)) }, + .data = .{ .imm = @bitCast(u32, -@intCast(i32, disp)) }, }); } } data.regs |= 1 << @intCast(u5, i); self.max_end_stack += 8; + disp += 8; } } break :blk try self.addExtra(data); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 30c3e12ce09f518dc805791065eef7dffe3c34df..9bdb0f35f63a9d4fb174f101f264b07952a60ea3 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -19,7 +19,7 @@ test "integer literal to pointer cast" { test "peer type resolution: ?T and T" { 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; try expect(peerTypeTAndOptionalT(true, false).? == 0); try expect(peerTypeTAndOptionalT(false, false).? == 3); -- 2.54.0 From b0bb1583cb03e2125b2b407d762baf42e7cfd527 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 2 Mar 2022 17:08:16 +0100 Subject: [PATCH 5/6] codegen: leave f80 explicitly unhandled for now --- src/codegen.zig | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 56888cab3654971cc23301b621fdae7bd0b4a79f..f0cf8da3b380eea7920e13c185bd23e86e3b5b02 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -141,26 +141,13 @@ pub fn generateFunction( } fn writeFloat(comptime F: type, f: F, target: Target, endian: std.builtin.Endian, code: []u8) void { - if (F == f80) { - switch (target.cpu.arch) { - .i386, .x86_64 => { - const repr = math.break_f80(f); - mem.writeIntLittle(u64, code[0..8], repr.fraction); - mem.writeIntLittle(u16, code[8..10], repr.exp); - // TODO set the rest of the bytes to undefined. should we use 0xaa - // or is there a different way? - return; - }, - else => {}, - } - } else { - const Int = @Type(.{ .Int = .{ - .signedness = .unsigned, - .bits = @typeInfo(F).Float.bits, - } }); - const int = @bitCast(Int, f); - mem.writeInt(Int, code[0..@sizeOf(Int)], int, endian); - } + _ = target; + const Int = @Type(.{ .Int = .{ + .signedness = .unsigned, + .bits = @typeInfo(F).Float.bits, + } }); + const int = @bitCast(Int, f); + mem.writeInt(Int, code[0..@sizeOf(Int)], int, endian); } pub fn generateSymbol( @@ -202,7 +189,14 @@ pub fn generateSymbol( 16 => writeFloat(f16, typed_value.val.toFloat(f16), target, endian, try code.addManyAsArray(2)), 32 => writeFloat(f32, typed_value.val.toFloat(f32), target, endian, try code.addManyAsArray(4)), 64 => writeFloat(f64, typed_value.val.toFloat(f64), target, endian, try code.addManyAsArray(8)), - 80 => writeFloat(f80, typed_value.val.toFloat(f80), target, endian, try code.addManyAsArray(10)), + 80 => return Result{ + .fail = try ErrorMsg.create( + bin_file.allocator, + src_loc, + "TODO handle f80 in generateSymbol", + .{}, + ), + }, 128 => writeFloat(f128, typed_value.val.toFloat(f128), target, endian, try code.addManyAsArray(16)), else => unreachable, } -- 2.54.0 From 77072d1a1785f389c3c9918c6d7c6179ac2e43f4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 2 Mar 2022 17:40:37 +0100 Subject: [PATCH 6/6] x64: fix bug in lowering optionals directly to immediate --- src/arch/x86_64/CodeGen.zig | 2 +- test/behavior/cast.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index da98c1c90124da3a71f92fb893ffada6dea9bf68..79e5c88050d32597736f112afd0d7b5b4e175ae0 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5688,7 +5688,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { .val = typed_value.val, }); } else if (typed_value.ty.abiSize(self.target.*) == 1) { - return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; + return MCValue{ .immediate = @boolToInt(!typed_value.val.isNull()) }; } }, .Enum => { diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 9bdb0f35f63a9d4fb174f101f264b07952a60ea3..60eacfee5d36288c1853120f71a62db7ed03273c 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -179,7 +179,7 @@ test "@floatCast comptime_int and comptime_float" { test "coerce undefined to optional" { 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; try expect(MakeType(void).getNull() == null); try expect(MakeType(void).getNonNull() != null); -- 2.54.0