aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-08-20 19:05:05 -0400
committerGitHub <noreply@github.com>2021-08-20 19:05:05 -0400
commita2438357e1f0f3853886a128f272cd27c714b176 (patch)
treef5b200eedeee3aab50b0bfe69e50e2e1a72bb8eb /src
parent0cd361219c107bce48f2d7b44c6f3dd05ea6ccf4 (diff)
parent224fe49be23c44095d39297a4986782bd19ce8b2 (diff)
downloadzig-a2438357e1f0f3853886a128f272cd27c714b176.tar.gz
zig-a2438357e1f0f3853886a128f272cd27c714b176.zip
Merge pull request #9597 from joachimschmidt557/stage2-arm-bitshift
stage2 ARM: implement bitshifts
Diffstat (limited to 'src')
-rw-r--r--src/codegen.zig70
-rw-r--r--src/codegen/arm.zig87
2 files changed, 153 insertions, 4 deletions
diff --git a/src/codegen.zig b/src/codegen.zig
index b2f0e453ea..bc815c1f8d 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -1602,15 +1602,53 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue {
+ // In the case of bitshifts, the type of rhs is different
+ // from the resulting type
+ const ty = self.air.typeOf(op_lhs);
+
+ switch (ty.zigTypeTag()) {
+ .Float => return self.fail("TODO ARM binary operations on floats", .{}),
+ .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
+ .Bool => {
+ return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, 1, .unsigned);
+ },
+ .Int => {
+ const int_info = ty.intInfo(self.target.*);
+ return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, int_info.bits, int_info.signedness);
+ },
+ else => unreachable,
+ }
+ }
+
+ fn genArmBinIntOp(
+ self: *Self,
+ inst: Air.Inst.Index,
+ op_lhs: Air.Inst.Ref,
+ op_rhs: Air.Inst.Ref,
+ op: Air.Inst.Tag,
+ bits: u16,
+ signedness: std.builtin.Signedness,
+ ) !MCValue {
+ if (bits > 32) {
+ return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
+ }
+
const lhs = try self.resolveInst(op_lhs);
const rhs = try self.resolveInst(op_rhs);
const lhs_is_register = lhs == .register;
const rhs_is_register = rhs == .register;
- const lhs_should_be_register = try self.armOperandShouldBeRegister(lhs);
+ const lhs_should_be_register = switch (op) {
+ .shr, .shl => true,
+ else => try self.armOperandShouldBeRegister(lhs),
+ };
const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
+ const can_swap_lhs_and_rhs = switch (op) {
+ .shr, .shl => false,
+ else => true,
+ };
// Destination must be a register
var dst_mcv: MCValue = undefined;
@@ -1627,7 +1665,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
}
dst_mcv = lhs;
- } else if (reuse_rhs) {
+ } else if (reuse_rhs and can_swap_lhs_and_rhs) {
// Allocate 0 or 1 registers
if (!lhs_is_register and lhs_should_be_register) {
lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) };
@@ -1666,7 +1704,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) };
lhs_mcv = dst_mcv;
}
- } else if (rhs_should_be_register) {
+ } else if (rhs_should_be_register and can_swap_lhs_and_rhs) {
// LHS is immediate
if (rhs_is_register) {
dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) };
@@ -1693,6 +1731,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
rhs_mcv,
swap_lhs_and_rhs,
op,
+ signedness,
);
return dst_mcv;
}
@@ -1704,6 +1743,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
rhs_mcv: MCValue,
swap_lhs_and_rhs: bool,
op: Air.Inst.Tag,
+ signedness: std.builtin.Signedness,
) !void {
assert(lhs_mcv == .register or rhs_mcv == .register);
@@ -1749,6 +1789,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.cmp_eq => {
writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, op1, operand).toU32());
},
+ .shl => {
+ assert(!swap_lhs_and_rhs);
+ const shift_amout = switch (operand) {
+ .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
+ .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
+ };
+ writeInt(u32, try self.code.addManyAsArray(4), Instruction.lsl(.al, dst_reg, op1, shift_amout).toU32());
+ },
+ .shr => {
+ assert(!swap_lhs_and_rhs);
+ const shift_amout = switch (operand) {
+ .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
+ .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
+ };
+
+ const shr = switch (signedness) {
+ .signed => Instruction.asr,
+ .unsigned => Instruction.lsr,
+ };
+ writeInt(u32, try self.code.addManyAsArray(4), shr(.al, dst_reg, op1, shift_amout).toU32());
+ },
else => unreachable, // not a binary instruction
}
}
@@ -2999,7 +3060,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
// The destination register is not present in the cmp instruction
- try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq);
+ // The signedness of the integer does not matter for the cmp instruction
+ try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq, undefined);
break :result switch (ty.isSignedInt()) {
true => MCValue{ .compare_flags_signed = op },
diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig
index 891a9e100b..d30479e1f1 100644
--- a/src/codegen/arm.zig
+++ b/src/codegen/arm.zig
@@ -1142,6 +1142,79 @@ pub const Instruction = union(enum) {
return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list));
}
}
+
+ pub const ShiftAmount = union(enum) {
+ immediate: u5,
+ register: Register,
+
+ pub fn imm(immediate: u5) ShiftAmount {
+ return .{
+ .immediate = immediate,
+ };
+ }
+
+ pub fn reg(register: Register) ShiftAmount {
+ return .{
+ .register = register,
+ };
+ }
+ };
+
+ pub fn lsl(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))),
+ .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))),
+ };
+ }
+
+ pub fn lsr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))),
+ .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))),
+ };
+ }
+
+ pub fn asr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))),
+ .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))),
+ };
+ }
+
+ pub fn ror(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))),
+ .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))),
+ };
+ }
+
+ pub fn lsls(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))),
+ .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))),
+ };
+ }
+
+ pub fn lsrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))),
+ .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))),
+ };
+ }
+
+ pub fn asrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))),
+ .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))),
+ };
+ }
+
+ pub fn rors(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))),
+ .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))),
+ };
+ }
};
test "serialize instructions" {
@@ -1262,6 +1335,20 @@ test "aliases" {
.actual = Instruction.push(.al, .{ .r0, .r2 }),
.expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }),
},
+ .{ // lsl r4, r5, #5
+ .actual = Instruction.lsl(.al, .r4, .r5, Instruction.ShiftAmount.imm(5)),
+ .expected = Instruction.mov(.al, .r4, Instruction.Operand.reg(
+ .r5,
+ Instruction.Operand.Shift.imm(5, .logical_left),
+ )),
+ },
+ .{ // asrs r1, r1, r3
+ .actual = Instruction.asrs(.al, .r1, .r1, Instruction.ShiftAmount.reg(.r3)),
+ .expected = Instruction.movs(.al, .r1, Instruction.Operand.reg(
+ .r1,
+ Instruction.Operand.Shift.reg(.r3, .arithmetic_right),
+ )),
+ },
};
for (testcases) |case| {