From 8c233687b4b0fdad725bf81b204dde7ccba45f56 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 18 Jan 2022 13:18:59 +0100 Subject: [PATCH] stage2: partially implement intcast on x86_64 * fix violating encoding invariant for memory encoding * enable some cast tests for x86_64 and arm --- src/arch/x86_64/CodeGen.zig | 22 +++++++++++++++++++--- src/arch/x86_64/Emit.zig | 2 +- test/behavior.zig | 2 +- test/behavior/cast.zig | 30 ++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 213e460588..5ae5fee6b0 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -877,10 +877,26 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { if (info_a.signedness != info_b.signedness) return self.fail("TODO gen intcast sign safety in semantic analysis", .{}); - if (info_a.bits == info_b.bits) - return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none }); + const operand_abi_size = operand_ty.abiSize(self.target.*); + const dest_ty = self.air.typeOfIndex(inst); + const dest_abi_size = dest_ty.abiSize(self.target.*); + const dst_mcv: MCValue = blk: { + if (info_a.bits == info_b.bits) { + break :blk operand; + } + if (operand_abi_size > 8 or dest_abi_size > 8) { + return self.fail("TODO implement intCast for abi sizes larger than 8", .{}); + } + const reg = switch (operand) { + .register => |src_reg| try self.register_manager.allocReg(inst, &.{src_reg}), + else => try self.register_manager.allocReg(inst, &.{}), + }; + try self.genSetReg(dest_ty, reg, .{ .immediate = 0 }); + try self.genSetReg(dest_ty, reg, operand); + break :blk .{ .register = registerAlias(reg, @intCast(u32, dest_abi_size)) }; + }; - return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); } fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 39d0c33975..4ec80dd1ba 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -1320,7 +1320,7 @@ const Memory = struct { encoder.disp32(@bitCast(i32, mem_op.disp)); } } else { - if (mem_op.disp == 0) { + if (mem_op.disp == 0 and dst != 5) { encoder.modRm_indirectDisp0(src, dst); } else if (immOpSize(mem_op.disp) == 8) { encoder.modRm_indirectDisp8(src, dst); diff --git a/test/behavior.zig b/test/behavior.zig index fe73529810..2e00927773 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -18,6 +18,7 @@ test { _ = @import("behavior/bool.zig"); _ = @import("behavior/align.zig"); _ = @import("behavior/array.zig"); + _ = @import("behavior/cast.zig"); if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) { // Tests that pass for stage1, llvm backend, C backend, wasm backend. @@ -36,7 +37,6 @@ test { _ = @import("behavior/bugs/4954.zig"); _ = @import("behavior/byval_arg_var.zig"); _ = @import("behavior/call.zig"); - _ = @import("behavior/cast.zig"); _ = @import("behavior/defer.zig"); _ = @import("behavior/enum.zig"); _ = @import("behavior/error.zig"); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 1e7a5a4687..05894be55e 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -5,6 +5,8 @@ const maxInt = std.math.maxInt; const builtin = @import("builtin"); test "int to ptr cast" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const x = @as(usize, 13); const y = @intToPtr(*u8, x); const z = @ptrToInt(y); @@ -12,11 +14,15 @@ test "int to ptr cast" { } test "integer literal to pointer cast" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const vga_mem = @intToPtr(*u16, 0xB8000); try expect(@ptrToInt(vga_mem) == 0xB8000); } test "peer type resolution: ?T and T" { + if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + try expect(peerTypeTAndOptionalT(true, false).? == 0); try expect(peerTypeTAndOptionalT(false, false).? == 3); comptime { @@ -33,6 +39,8 @@ fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { } test "resolve undefined with integer" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + try testResolveUndefWithInt(true, 1234); comptime try testResolveUndefWithInt(true, 1234); } @@ -88,6 +96,8 @@ test "comptime_int @intToFloat" { } test "@floatToInt" { + if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + try testFloatToInts(); comptime try testFloatToInts(); } @@ -107,6 +117,8 @@ fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { } test "implicitly cast indirect pointer to maybe-indirect pointer" { + if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + const S = struct { const Self = @This(); x: u8, @@ -163,6 +175,8 @@ test "@floatCast comptime_int and comptime_float" { } test "coerce undefined to optional" { + if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + try expect(MakeType(void).getNull() == null); try expect(MakeType(void).getNonNull() != null); } @@ -180,6 +194,8 @@ fn MakeType(comptime T: type) type { } test "implicit cast from *[N]T to [*c]T" { + if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + var x: [4]u16 = [4]u16{ 0, 1, 2, 3 }; var y: [*c]u16 = &x; @@ -190,6 +206,8 @@ test "implicit cast from *[N]T to [*c]T" { } test "*usize to *void" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + var i = @as(usize, 0); var v = @ptrCast(*void, &i); v.* = {}; @@ -202,6 +220,8 @@ test "@intToEnum passed a comptime_int to an enum with one item" { } test "@intCast to u0 and use the result" { + if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + const S = struct { fn doTheTest(zero: u1, one: u1, bigzero: i32) !void { try expect((one << @intCast(u0, bigzero)) == 1); @@ -213,6 +233,8 @@ test "@intCast to u0 and use the result" { } test "peer result null and comptime_int" { + if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + const S = struct { fn blah(n: i32) ?i32 { if (n == 0) { @@ -234,6 +256,8 @@ test "peer result null and comptime_int" { } test "*const ?[*]const T to [*c]const [*c]const T" { + if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + var array = [_]u8{ 'o', 'k' }; const opt_array_ptr: ?[*]const u8 = &array; const a: *const ?[*]const u8 = &opt_array_ptr; @@ -243,6 +267,8 @@ test "*const ?[*]const T to [*c]const [*c]const T" { } test "array coersion to undefined at runtime" { + if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + @setRuntimeSafety(true); // TODO implement @setRuntimeSafety in stage2 @@ -270,6 +296,8 @@ fn implicitIntLitToOptional() void { } test "return u8 coercing into ?u32 return type" { + if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + const S = struct { fn doTheTest() !void { try expect(foo(123).? == 123); @@ -288,6 +316,8 @@ test "cast from ?[*]T to ??[*]T" { } test "peer type unsigned int to signed" { + if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + var w: u31 = 5; var x: u8 = 7; var y: i32 = -5;