Commit 8c233687b4

Jakub Konka <kubkon@jakubkonka.com>
2022-01-18 13:18:59
stage2: partially implement intcast on x86_64
* fix violating encoding invariant for memory encoding * enable some cast tests for x86_64 and arm
1 parent aaa641f
Changed files (4)
src
test
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 {
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);
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;
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");