Skip to content

Commit

Permalink
Merge pull request ziglang#9597 from joachimschmidt557/stage2-arm-bit…
Browse files Browse the repository at this point in the history
…shift

stage2 ARM: implement bitshifts
  • Loading branch information
andrewrk authored Aug 20, 2021
2 parents 0cd3612 + 224fe49 commit a243835
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 4 deletions.
70 changes: 66 additions & 4 deletions src/codegen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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}) };
Expand Down Expand Up @@ -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}) };
Expand All @@ -1693,6 +1731,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
rhs_mcv,
swap_lhs_and_rhs,
op,
signedness,
);
return dst_mcv;
}
Expand All @@ -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);

Expand Down Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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 },
Expand Down
87 changes: 87 additions & 0 deletions src/codegen/arm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down Expand Up @@ -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| {
Expand Down
86 changes: 86 additions & 0 deletions test/stage2/arm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,48 @@ pub fn addCases(ctx: *TestContext) !void {
,
"123456",
);

// Bit Shift Left
case.addCompareOutput(
\\pub fn main() void {
\\ var x: u32 = 1;
\\ assert(x << 1 == 2);
\\
\\ x <<= 1;
\\ assert(x << 2 == 8);
\\ assert(x << 3 == 16);
\\}
\\
\\pub fn assert(ok: bool) void {
\\ if (!ok) unreachable; // assertion failure
\\}
,
"",
);

// Bit Shift Right
case.addCompareOutput(
\\pub fn main() void {
\\ var a: u32 = 1024;
\\ assert(a >> 1 == 512);
\\
\\ a >>= 1;
\\ assert(a >> 2 == 128);
\\ assert(a >> 3 == 64);
\\ assert(a >> 4 == 32);
\\ assert(a >> 5 == 16);
\\ assert(a >> 6 == 8);
\\ assert(a >> 7 == 4);
\\ assert(a >> 8 == 2);
\\ assert(a >> 9 == 1);
\\}
\\
\\pub fn assert(ok: bool) void {
\\ if (!ok) unreachable; // assertion failure
\\}
,
"",
);
}

{
Expand Down Expand Up @@ -429,4 +471,48 @@ pub fn addCases(ctx: *TestContext) !void {
"",
);
}

{
var case = ctx.exe("print u32s", linux_arm);
case.addCompareOutput(
\\pub fn main() void {
\\ printNumberHex(0x00000000);
\\ printNumberHex(0xaaaaaaaa);
\\ printNumberHex(0xdeadbeef);
\\ printNumberHex(0x31415926);
\\}
\\
\\fn printNumberHex(x: u32) void {
\\ var i: u5 = 28;
\\ while (true) : (i -= 4) {
\\ const digit = (x >> i) & 0xf;
\\ asm volatile ("svc #0"
\\ :
\\ : [number] "{r7}" (4),
\\ [arg1] "{r0}" (1),
\\ [arg2] "{r1}" (@ptrToInt("0123456789abcdef") + digit),
\\ [arg3] "{r2}" (1)
\\ : "memory"
\\ );
\\
\\ if (i == 0) break;
\\ }
\\ asm volatile ("svc #0"
\\ :
\\ : [number] "{r7}" (4),
\\ [arg1] "{r0}" (1),
\\ [arg2] "{r1}" (@ptrToInt("\n")),
\\ [arg3] "{r2}" (1)
\\ : "memory"
\\ );
\\}
,
\\00000000
\\aaaaaaaa
\\deadbeef
\\31415926
\\
,
);
}
}

0 comments on commit a243835

Please sign in to comment.