Commit 9c3d24ea0b

Jakub Konka <kubkon@jakubkonka.com>
2022-05-09 17:39:19
x64: add naive impl of shr
1 parent d7f8368
Changed files (2)
src
arch
test
behavior
src/arch/x86_64/CodeGen.zig
@@ -2052,11 +2052,76 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
 
 fn airShr(self: *Self, inst: Air.Inst.Index) !void {
     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
-    const result: MCValue = if (self.liveness.isUnused(inst))
-        .dead
-    else
-        return self.fail("TODO implement shr for {}", .{self.target.cpu.arch});
-    return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
+
+    if (self.liveness.isUnused(inst)) {
+        return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
+    }
+
+    const ty = self.air.typeOfIndex(inst);
+    const tag = self.air.instructions.items(.tag)[inst];
+    switch (tag) {
+        .shr_exact => return self.fail("TODO implement shr_exact for type {}", .{ty.fmtDebug()}),
+        .shr => {},
+        else => unreachable,
+    }
+
+    if (ty.zigTypeTag() != .Int) {
+        return self.fail("TODO implement shr for type {}", .{ty.fmtDebug()});
+    }
+    if (ty.abiSize(self.target.*) > 8) {
+        return self.fail("TODO implement shr for integers larger than 8 bytes", .{});
+    }
+
+    // TODO look into reusing the operands
+    // TODO audit register allocation mechanics
+    const shift = try self.resolveInst(bin_op.rhs);
+    const shift_ty = self.air.typeOf(bin_op.rhs);
+
+    blk: {
+        switch (shift) {
+            .register => |reg| {
+                if (reg.to64() == .rcx) break :blk;
+            },
+            else => {},
+        }
+        try self.register_manager.getReg(.rcx, null);
+        try self.genSetReg(shift_ty, .rcx, shift);
+    }
+    const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx);
+    defer self.register_manager.unlockReg(rcx_lock);
+
+    const value = try self.resolveInst(bin_op.lhs);
+    const value_lock: ?RegisterLock = switch (value) {
+        .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+        else => null,
+    };
+    defer if (value_lock) |lock| self.register_manager.unlockReg(lock);
+
+    const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, value);
+    switch (ty.intInfo(self.target.*).signedness) {
+        .signed => {
+            _ = try self.addInst(.{
+                .tag = .sar,
+                .ops = (Mir.Ops{
+                    .reg1 = dst_mcv.register,
+                    .flags = 0b01,
+                }).encode(),
+                .data = undefined,
+            });
+        },
+        .unsigned => {
+            _ = try self.addInst(.{
+                .tag = .shr,
+                .ops = (Mir.Ops{
+                    .reg1 = dst_mcv.register,
+                    .flags = 0b01,
+                }).encode(),
+                .data = undefined,
+            });
+        },
+    }
+
+    return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });
 }
 
 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
test/behavior/math.zig
@@ -570,8 +570,6 @@ test "bit shift a u1" {
 }
 
 test "truncating shift right" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
-
     try testShrTrunc(maxInt(u16));
     comptime try testShrTrunc(maxInt(u16));
 }