Commit 65838fcabe

Jacob Young <jacobly0@users.noreply.github.com>
2023-03-27 09:11:34
x86_64: implement some binary ops for large values
1 parent 3a51643
Changed files (1)
src
arch
src/arch/x86_64/CodeGen.zig
@@ -4010,9 +4010,6 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue
         .register_overflow => unreachable,
         .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)),
         .ptr_stack_offset, .stack_offset => |off| {
-            if (off > math.maxInt(i32)) {
-                return self.fail("stack offset too large", .{});
-            }
             if (abi_size > 8) {
                 return self.fail("TODO implement {} for stack dst with large ABI", .{mir_tag});
             }
@@ -4454,9 +4451,6 @@ fn genBinOp(
     if (lhs_ty.zigTypeTag() == .Vector) {
         return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmt(self.bin_file.options.module.?)});
     }
-    if (lhs_ty.abiSize(self.target.*) > 8) {
-        return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmt(self.bin_file.options.module.?)});
-    }
 
     switch (lhs) {
         .immediate => |imm| switch (imm) {
@@ -4686,7 +4680,6 @@ fn genBinOp(
                         const addr_reg = (try self.register_manager.allocReg(null, gp)).to64();
                         const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
                         defer self.register_manager.unlockReg(addr_reg_lock);
-
                         try self.loadMemPtrIntoRegister(addr_reg, Type.usize, mat_src_mcv);
 
                         // To get the actual address of the value we want to modify we
@@ -4801,9 +4794,6 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s
                     return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg });
                 },
                 .stack_offset => |off| {
-                    if (off > math.maxInt(i32)) {
-                        return self.fail("stack offset too large", .{});
-                    }
                     try self.asmRegisterMemory(
                         mir_tag,
                         registerAlias(dst_reg, abi_size),
@@ -4812,78 +4802,155 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s
                 },
             }
         },
-        .ptr_stack_offset, .stack_offset => |off| {
-            if (off > math.maxInt(i32)) {
-                return self.fail("stack offset too large", .{});
-            }
-            if (abi_size > 8) {
-                return self.fail("TODO implement {} for stack dst with large ABI", .{mir_tag});
-            }
+        .ptr_stack_offset, .stack_offset => |dst_off| {
+            const src: ?struct {
+                limb_reg: Register,
+                limb_lock: RegisterLock,
+                addr_reg: Register,
+                addr_lock: RegisterLock,
+            } = switch (src_mcv) {
+                else => null,
+                .memory, .linker_load => addr: {
+                    const src_limb_reg = try self.register_manager.allocReg(null, gp);
+                    const src_limb_lock = self.register_manager.lockRegAssumeUnused(src_limb_reg);
+                    errdefer self.register_manager.unlockReg(src_limb_lock);
+
+                    const src_addr_reg = try self.register_manager.allocReg(null, gp);
+                    const src_addr_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg);
+                    errdefer self.register_manager.unlockReg(src_addr_lock);
+
+                    try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_mcv);
+                    // To get the actual address of the value we want to modify we
+                    // we have to go through the GOT
+                    try self.asmRegisterMemory(
+                        .mov,
+                        src_addr_reg,
+                        Memory.sib(.qword, .{ .base = src_addr_reg }),
+                    );
 
-            switch (src_mcv) {
-                .none => unreachable,
-                .undef => unreachable,
-                .dead, .unreach => unreachable,
-                .register_overflow => unreachable,
-                .register => |src_reg| {
-                    try self.asmMemoryRegister(mir_tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
-                        .base = .rbp,
-                        .disp = -off,
-                    }), registerAlias(src_reg, abi_size));
+                    break :addr .{
+                        .addr_reg = src_addr_reg,
+                        .addr_lock = src_addr_lock,
+                        .limb_reg = src_limb_reg,
+                        .limb_lock = src_limb_lock,
+                    };
                 },
-                .immediate => |imm| {
-                    switch (self.regBitSize(ty)) {
-                        8, 16, 32 => {
-                            try self.asmMemoryImmediate(
-                                mir_tag,
-                                Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
-                                    .base = .rbp,
-                                    .disp = -off,
-                                }),
-                                if (math.cast(i32, @bitCast(i64, imm))) |small|
-                                    Immediate.s(small)
-                                else
-                                    Immediate.u(@intCast(u32, imm)),
-                            );
-                        },
-                        64 => {
-                            if (math.cast(i32, @bitCast(i64, imm))) |small| {
+            };
+            defer if (src) |locks| {
+                self.register_manager.unlockReg(locks.limb_lock);
+                self.register_manager.unlockReg(locks.addr_lock);
+            };
+
+            const ty_signedness =
+                if (ty.isAbiInt()) ty.intInfo(self.target.*).signedness else .unsigned;
+            const limb_ty = if (abi_size <= 8) ty else switch (ty_signedness) {
+                .signed => Type.usize,
+                .unsigned => Type.isize,
+            };
+            const limb_abi_size = @min(abi_size, 8);
+            var off: i32 = 0;
+            while (off < abi_size) : (off += 8) {
+                const mir_limb_tag = switch (off) {
+                    0 => mir_tag,
+                    else => switch (mir_tag) {
+                        .add => .adc,
+                        .sub => .sbb,
+                        .@"or", .@"and", .xor => mir_tag,
+                        else => return self.fail("TODO genBinOpMir implement large ABI for {s}", .{
+                            @tagName(mir_tag),
+                        }),
+                    },
+                };
+                const dst_limb_mem = Memory.sib(
+                    Memory.PtrSize.fromSize(limb_abi_size),
+                    .{ .base = .rbp, .disp = off - dst_off },
+                );
+                switch (src_mcv) {
+                    .none => unreachable,
+                    .undef => unreachable,
+                    .dead, .unreach => unreachable,
+                    .register_overflow => unreachable,
+                    .register => |src_reg| {
+                        assert(off == 0);
+                        try self.asmMemoryRegister(
+                            mir_limb_tag,
+                            dst_limb_mem,
+                            registerAlias(src_reg, limb_abi_size),
+                        );
+                    },
+                    .immediate => |src_imm| {
+                        const imm = if (off == 0) src_imm else switch (ty_signedness) {
+                            .signed => @bitCast(u64, @bitCast(i64, src_imm) >> 63),
+                            .unsigned => 0,
+                        };
+                        switch (self.regBitSize(limb_ty)) {
+                            8, 16, 32 => {
                                 try self.asmMemoryImmediate(
-                                    mir_tag,
-                                    Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
-                                        .base = .rbp,
-                                        .disp = -off,
-                                    }),
-                                    Immediate.s(small),
+                                    mir_limb_tag,
+                                    dst_limb_mem,
+                                    if (math.cast(i32, @bitCast(i64, imm))) |small|
+                                        Immediate.s(small)
+                                    else
+                                        Immediate.u(@intCast(u32, imm)),
                                 );
-                            } else {
-                                try self.asmMemoryRegister(
-                                    mir_tag,
-                                    Memory.sib(Memory.PtrSize.fromSize(abi_size), .{
-                                        .base = .rbp,
-                                        .disp = -off,
-                                    }),
-                                    registerAlias(try self.copyToTmpRegister(ty, src_mcv), abi_size),
-                                );
-                            }
-                        },
-                        else => return self.fail("TODO genBinOpMir implement large immediate ABI", .{}),
-                    }
-                },
-                .memory,
-                .linker_load,
-                .stack_offset,
-                .ptr_stack_offset,
-                .eflags,
-                => {
-                    assert(abi_size <= 8);
-
-                    const tmp_reg = try self.copyToTmpRegister(ty, src_mcv);
-                    const tmp_lock = self.register_manager.lockReg(tmp_reg);
-                    defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock);
+                            },
+                            64 => {
+                                if (math.cast(i32, @bitCast(i64, imm))) |small| {
+                                    try self.asmMemoryImmediate(
+                                        mir_limb_tag,
+                                        dst_limb_mem,
+                                        Immediate.s(small),
+                                    );
+                                } else {
+                                    try self.asmMemoryRegister(
+                                        mir_limb_tag,
+                                        dst_limb_mem,
+                                        registerAlias(
+                                            try self.copyToTmpRegister(limb_ty, .{ .immediate = imm }),
+                                            limb_abi_size,
+                                        ),
+                                    );
+                                }
+                            },
+                            else => unreachable,
+                        }
+                    },
+                    .memory, .linker_load => {
+                        try self.asmRegisterMemory(
+                            .mov,
+                            registerAlias(src.?.limb_reg, limb_abi_size),
+                            Memory.sib(
+                                Memory.PtrSize.fromSize(limb_abi_size),
+                                .{ .base = src.?.addr_reg, .disp = off },
+                            ),
+                        );
+                        try self.asmMemoryRegister(
+                            mir_limb_tag,
+                            dst_limb_mem,
+                            registerAlias(src.?.limb_reg, limb_abi_size),
+                        );
+                    },
+                    .stack_offset, .ptr_stack_offset, .eflags => {
+                        const src_limb_reg = try self.copyToTmpRegister(limb_ty, switch (src_mcv) {
+                            .stack_offset => |src_off| .{ .stack_offset = src_off - off },
+                            .ptr_stack_offset,
+                            .eflags,
+                            => off: {
+                                assert(off == 0);
+                                break :off src_mcv;
+                            },
+                            else => unreachable,
+                        });
+                        const src_limb_lock = self.register_manager.lockReg(src_limb_reg);
+                        defer if (src_limb_lock) |lock| self.register_manager.unlockReg(lock);
 
-                    return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = tmp_reg });
-                },
+                        try self.asmMemoryRegister(
+                            mir_limb_tag,
+                            dst_limb_mem,
+                            registerAlias(src_limb_reg, limb_abi_size),
+                        );
+                    },
+                }
             }
         },
         .memory => {
@@ -6735,10 +6802,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
             }
         },
         .register => |reg| {
-            if (stack_offset > math.maxInt(i32)) {
-                return self.fail("stack offset too large", .{});
-            }
-
             const base_reg = opts.dest_stack_base orelse .rbp;
 
             switch (ty.zigTypeTag()) {
@@ -6973,13 +7036,11 @@ fn genInlineMemset(
 
 fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
     const abi_size = @intCast(u32, ty.abiSize(self.target.*));
+    if (abi_size > 8) return self.fail("genSetReg called with a value larger than one register", .{});
     switch (mcv) {
         .dead => unreachable,
         .register_overflow => unreachable,
         .ptr_stack_offset => |off| {
-            if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
-                return self.fail("stack offset too large", .{});
-            }
             try self.asmRegisterMemory(
                 .lea,
                 registerAlias(reg, abi_size),
@@ -7158,10 +7219,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
             },
         },
         .stack_offset => |off| {
-            if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
-                return self.fail("stack offset too large", .{});
-            }
-
             switch (ty.zigTypeTag()) {
                 .Int => switch (ty.intInfo(self.target.*).signedness) {
                     .signed => {