mirror of
https://github.com/ziglang/zig.git
synced 2026-01-28 02:05:31 +00:00
stage2 AArch64: introduce logical immediate instructions
This commit is contained in:
parent
f2a5d0bf94
commit
e3121accac
@ -950,10 +950,69 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
|
||||
switch (operand_ty.zigTypeTag()) {
|
||||
.Bool => {
|
||||
// TODO convert this to mvn + and
|
||||
const dest = try self.binOp(.xor, null, operand, .{ .immediate = 1 }, operand_ty, Type.bool);
|
||||
break :result dest;
|
||||
const op_reg = switch (operand) {
|
||||
.register => |r| r,
|
||||
else => try self.copyToTmpRegister(operand_ty, operand),
|
||||
};
|
||||
self.register_manager.freezeRegs(&.{op_reg});
|
||||
defer self.register_manager.unfreezeRegs(&.{op_reg});
|
||||
|
||||
const dest_reg = blk: {
|
||||
if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
|
||||
break :blk op_reg;
|
||||
}
|
||||
|
||||
break :blk try self.register_manager.allocReg(null);
|
||||
};
|
||||
|
||||
_ = try self.addInst(.{
|
||||
.tag = .eor_immediate,
|
||||
.data = .{ .rr_bitmask = .{
|
||||
.rd = dest_reg,
|
||||
.rn = op_reg,
|
||||
.imms = 0b000000,
|
||||
.immr = 0b000000,
|
||||
.n = 0b1,
|
||||
} },
|
||||
});
|
||||
|
||||
break :result MCValue{ .register = dest_reg };
|
||||
},
|
||||
else => return self.fail("TODO bitwise not", .{}),
|
||||
.Vector => return self.fail("TODO bitwise not for vectors", .{}),
|
||||
.Int => {
|
||||
const int_info = operand_ty.intInfo(self.target.*);
|
||||
if (int_info.bits <= 64) {
|
||||
const op_reg = switch (operand) {
|
||||
.register => |r| r,
|
||||
else => try self.copyToTmpRegister(operand_ty, operand),
|
||||
};
|
||||
self.register_manager.freezeRegs(&.{op_reg});
|
||||
defer self.register_manager.unfreezeRegs(&.{op_reg});
|
||||
|
||||
const dest_reg = blk: {
|
||||
if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
|
||||
break :blk op_reg;
|
||||
}
|
||||
|
||||
break :blk try self.register_manager.allocReg(null);
|
||||
};
|
||||
|
||||
_ = try self.addInst(.{
|
||||
.tag = .mvn,
|
||||
.data = .{ .rr_imm6_shift = .{
|
||||
.rd = dest_reg,
|
||||
.rm = op_reg,
|
||||
.imm6 = 0,
|
||||
.shift = .lsl,
|
||||
} },
|
||||
});
|
||||
|
||||
break :result MCValue{ .register = dest_reg };
|
||||
} else {
|
||||
return self.fail("TODO AArch64 not on integers > u64/i64", .{});
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -1259,15 +1318,13 @@ fn binOp(
|
||||
}
|
||||
},
|
||||
// Bitwise operations on integers
|
||||
.xor => {
|
||||
.bit_and,
|
||||
.bit_or,
|
||||
.xor,
|
||||
=> {
|
||||
switch (lhs_ty.zigTypeTag()) {
|
||||
.Vector => return self.fail("TODO binary operations on vectors", .{}),
|
||||
.Int => return self.fail("TODO binary operations on vectors", .{}),
|
||||
.Bool => {
|
||||
assert(lhs_ty.eql(rhs_ty));
|
||||
// TODO boolean operations with immediates
|
||||
return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
||||
},
|
||||
.Int => return self.fail("TODO binary operations on integers", .{}),
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
@ -3136,11 +3193,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
4, 8 => .str_stack,
|
||||
else => unreachable, // unexpected abi size
|
||||
};
|
||||
const rt: Register = switch (abi_size) {
|
||||
1, 2, 4 => reg.to32(),
|
||||
8 => reg.to64(),
|
||||
else => unreachable, // unexpected abi size
|
||||
};
|
||||
const rt = registerAlias(reg, abi_size);
|
||||
|
||||
_ = try self.addInst(.{
|
||||
.tag = tag,
|
||||
@ -3622,7 +3675,6 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
|
||||
fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
|
||||
if (typed_value.val.isUndef())
|
||||
return MCValue{ .undef = {} };
|
||||
const ptr_bits = self.target.cpu.arch.ptrBitWidth();
|
||||
|
||||
if (typed_value.val.castTag(.decl_ref)) |payload| {
|
||||
return self.lowerDeclRef(typed_value, payload.data);
|
||||
@ -3652,13 +3704,19 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
|
||||
},
|
||||
.Int => {
|
||||
const info = typed_value.ty.intInfo(self.target.*);
|
||||
if (info.bits <= ptr_bits and info.signedness == .signed) {
|
||||
return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt()) };
|
||||
if (info.bits <= 64) {
|
||||
const unsigned = switch (info.signedness) {
|
||||
.signed => blk: {
|
||||
const signed = typed_value.val.toSignedInt();
|
||||
break :blk @bitCast(u64, signed);
|
||||
},
|
||||
.unsigned => typed_value.val.toUnsignedInt(),
|
||||
};
|
||||
|
||||
return MCValue{ .immediate = unsigned };
|
||||
} else {
|
||||
return self.lowerUnnamedConst(typed_value);
|
||||
}
|
||||
if (info.bits > ptr_bits or info.signedness == .signed) {
|
||||
return self.fail("TODO const int bigger than ptr and signed int", .{});
|
||||
}
|
||||
return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
|
||||
},
|
||||
.Bool => {
|
||||
return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
|
||||
@ -3875,7 +3933,7 @@ fn parseRegName(name: []const u8) ?Register {
|
||||
return std.meta.stringToEnum(Register, name);
|
||||
}
|
||||
|
||||
fn registerAlias(reg: Register, size_bytes: u32) Register {
|
||||
fn registerAlias(reg: Register, size_bytes: u64) Register {
|
||||
if (size_bytes == 0) {
|
||||
unreachable; // should be comptime known
|
||||
} else if (size_bytes <= 4) {
|
||||
|
||||
@ -95,6 +95,8 @@ pub fn emitMir(
|
||||
|
||||
.call_extern => try emit.mirCallExtern(inst),
|
||||
|
||||
.eor_immediate => try emit.mirLogicalImmediate(inst),
|
||||
|
||||
.add_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
|
||||
.cmp_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
|
||||
.sub_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
|
||||
@ -605,6 +607,21 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn mirLogicalImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||
const rr_bitmask = emit.mir.instructions.items(.data)[inst].rr_bitmask;
|
||||
const rd = rr_bitmask.rd;
|
||||
const rn = rr_bitmask.rn;
|
||||
const imms = rr_bitmask.imms;
|
||||
const immr = rr_bitmask.immr;
|
||||
const n = rr_bitmask.n;
|
||||
|
||||
switch (tag) {
|
||||
.eor_immediate => try emit.writeInstruction(Instruction.eorImmediate(rd, rn, imms, immr, n)),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||
const rrr_imm6_shift = emit.mir.instructions.items(.data)[inst].rrr_imm6_shift;
|
||||
@ -643,7 +660,7 @@ fn mirLogicalShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
const imm6 = rrr_imm6_logical_shift.imm6;
|
||||
|
||||
switch (tag) {
|
||||
.eor_shifted_register => try emit.writeInstruction(Instruction.eor(rd, rn, rm, shift, imm6)),
|
||||
.eor_shifted_register => try emit.writeInstruction(Instruction.eorShiftedRegister(rd, rn, rm, shift, imm6)),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
@ -844,7 +861,7 @@ fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
switch (tag) {
|
||||
.mov_register => {
|
||||
const rr = emit.mir.instructions.items(.data)[inst].rr;
|
||||
try emit.writeInstruction(Instruction.orr(rr.rd, .xzr, rr.rn, .lsl, 0));
|
||||
try emit.writeInstruction(Instruction.orrShiftedRegister(rr.rd, .xzr, rr.rn, .lsl, 0));
|
||||
},
|
||||
.mov_to_from_sp => {
|
||||
const rr = emit.mir.instructions.items(.data)[inst].rr;
|
||||
@ -852,7 +869,7 @@ fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
},
|
||||
.mvn => {
|
||||
const rr_imm6_shift = emit.mir.instructions.items(.data)[inst].rr_imm6_shift;
|
||||
try emit.writeInstruction(Instruction.orn(rr_imm6_shift.rd, .xzr, rr_imm6_shift.rm, .lsl, 0));
|
||||
try emit.writeInstruction(Instruction.ornShiftedRegister(rr_imm6_shift.rd, .xzr, rr_imm6_shift.rm, rr_imm6_shift.shift, rr_imm6_shift.imm6));
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
@ -54,6 +54,8 @@ pub const Inst = struct {
|
||||
dbg_epilogue_begin,
|
||||
/// Pseudo-instruction: Update debug line
|
||||
dbg_line,
|
||||
/// Bitwise Exclusive OR (immediate)
|
||||
eor_immediate,
|
||||
/// Bitwise Exclusive OR (shifted register)
|
||||
eor_shifted_register,
|
||||
/// Loads the contents into a register
|
||||
@ -231,14 +233,25 @@ pub const Inst = struct {
|
||||
imm12: u12,
|
||||
sh: u1 = 0,
|
||||
},
|
||||
/// Two registers and a shift (shift type and 6-bit amount)
|
||||
/// Two registers and a shift (logical instruction version)
|
||||
/// (shift type and 6-bit amount)
|
||||
///
|
||||
/// Used by e.g. mvn
|
||||
rr_imm6_shift: struct {
|
||||
rd: Register,
|
||||
rm: Register,
|
||||
imm6: u6,
|
||||
shift: bits.Instruction.AddSubtractShiftedRegisterShift,
|
||||
shift: bits.Instruction.LogicalShiftedRegisterShift,
|
||||
},
|
||||
/// Two registers and a bitmask immediate
|
||||
///
|
||||
/// Used by e.g. eor_immediate
|
||||
rr_bitmask: struct {
|
||||
rd: Register,
|
||||
rn: Register,
|
||||
imms: u6,
|
||||
immr: u6,
|
||||
n: u1,
|
||||
},
|
||||
/// Two registers
|
||||
///
|
||||
|
||||
@ -323,6 +323,16 @@ pub const Instruction = union(enum) {
|
||||
op: u1,
|
||||
sf: u1,
|
||||
},
|
||||
logical_immediate: packed struct {
|
||||
rd: u5,
|
||||
rn: u5,
|
||||
imms: u6,
|
||||
immr: u6,
|
||||
n: u1,
|
||||
fixed: u6 = 0b100100,
|
||||
opc: u2,
|
||||
sf: u1,
|
||||
},
|
||||
add_subtract_shifted_register: packed struct {
|
||||
rd: u5,
|
||||
rn: u5,
|
||||
@ -487,6 +497,7 @@ pub const Instruction = union(enum) {
|
||||
.no_operation => |v| @bitCast(u32, v),
|
||||
.logical_shifted_register => |v| @bitCast(u32, v),
|
||||
.add_subtract_immediate => |v| @bitCast(u32, v),
|
||||
.logical_immediate => |v| @bitCast(u32, v),
|
||||
.add_subtract_shifted_register => |v| @bitCast(u32, v),
|
||||
// TODO once packed structs work, this can be refactored
|
||||
.conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25),
|
||||
@ -900,6 +911,31 @@ pub const Instruction = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
fn logicalImmediate(
|
||||
opc: u2,
|
||||
rd: Register,
|
||||
rn: Register,
|
||||
imms: u6,
|
||||
immr: u6,
|
||||
n: u1,
|
||||
) Instruction {
|
||||
return Instruction{
|
||||
.logical_immediate = .{
|
||||
.rd = rd.enc(),
|
||||
.rn = rn.enc(),
|
||||
.imms = imms,
|
||||
.immr = immr,
|
||||
.n = n,
|
||||
.opc = opc,
|
||||
.sf = switch (rd.size()) {
|
||||
32 => 0b0,
|
||||
64 => 0b1,
|
||||
else => unreachable, // unexpected register size
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub const AddSubtractShiftedRegisterShift = enum(u2) { lsl, lsr, asr, _ };
|
||||
|
||||
fn addSubtractShiftedRegister(
|
||||
@ -1173,7 +1209,7 @@ pub const Instruction = union(enum) {
|
||||
|
||||
// Logical (shifted register)
|
||||
|
||||
pub fn @"and"(
|
||||
pub fn andShiftedRegister(
|
||||
rd: Register,
|
||||
rn: Register,
|
||||
rm: Register,
|
||||
@ -1183,7 +1219,7 @@ pub const Instruction = union(enum) {
|
||||
return logicalShiftedRegister(0b00, 0b0, rd, rn, rm, shift, amount);
|
||||
}
|
||||
|
||||
pub fn bic(
|
||||
pub fn bicShiftedRegister(
|
||||
rd: Register,
|
||||
rn: Register,
|
||||
rm: Register,
|
||||
@ -1193,7 +1229,7 @@ pub const Instruction = union(enum) {
|
||||
return logicalShiftedRegister(0b00, 0b1, rd, rn, rm, shift, amount);
|
||||
}
|
||||
|
||||
pub fn orr(
|
||||
pub fn orrShiftedRegister(
|
||||
rd: Register,
|
||||
rn: Register,
|
||||
rm: Register,
|
||||
@ -1203,7 +1239,7 @@ pub const Instruction = union(enum) {
|
||||
return logicalShiftedRegister(0b01, 0b0, rd, rn, rm, shift, amount);
|
||||
}
|
||||
|
||||
pub fn orn(
|
||||
pub fn ornShiftedRegister(
|
||||
rd: Register,
|
||||
rn: Register,
|
||||
rm: Register,
|
||||
@ -1213,7 +1249,7 @@ pub const Instruction = union(enum) {
|
||||
return logicalShiftedRegister(0b01, 0b1, rd, rn, rm, shift, amount);
|
||||
}
|
||||
|
||||
pub fn eor(
|
||||
pub fn eorShiftedRegister(
|
||||
rd: Register,
|
||||
rn: Register,
|
||||
rm: Register,
|
||||
@ -1223,7 +1259,7 @@ pub const Instruction = union(enum) {
|
||||
return logicalShiftedRegister(0b10, 0b0, rd, rn, rm, shift, amount);
|
||||
}
|
||||
|
||||
pub fn eon(
|
||||
pub fn eonShiftedRegister(
|
||||
rd: Register,
|
||||
rn: Register,
|
||||
rm: Register,
|
||||
@ -1233,7 +1269,7 @@ pub const Instruction = union(enum) {
|
||||
return logicalShiftedRegister(0b10, 0b1, rd, rn, rm, shift, amount);
|
||||
}
|
||||
|
||||
pub fn ands(
|
||||
pub fn andsShiftedRegister(
|
||||
rd: Register,
|
||||
rn: Register,
|
||||
rm: Register,
|
||||
@ -1243,7 +1279,7 @@ pub const Instruction = union(enum) {
|
||||
return logicalShiftedRegister(0b11, 0b0, rd, rn, rm, shift, amount);
|
||||
}
|
||||
|
||||
pub fn bics(
|
||||
pub fn bicsShiftedRegister(
|
||||
rd: Register,
|
||||
rn: Register,
|
||||
rm: Register,
|
||||
@ -1271,6 +1307,24 @@ pub const Instruction = union(enum) {
|
||||
return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift);
|
||||
}
|
||||
|
||||
// Logical (immediate)
|
||||
|
||||
pub fn andImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction {
|
||||
return logicalImmediate(0b00, rd, rn, imms, immr, n);
|
||||
}
|
||||
|
||||
pub fn orrImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction {
|
||||
return logicalImmediate(0b01, rd, rn, imms, immr, n);
|
||||
}
|
||||
|
||||
pub fn eorImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction {
|
||||
return logicalImmediate(0b10, rd, rn, imms, immr, n);
|
||||
}
|
||||
|
||||
pub fn andsImmediate(rd: Register, rn: Register, imms: u6, immr: u6, n: u1) Instruction {
|
||||
return logicalImmediate(0b11, rd, rn, imms, immr, n);
|
||||
}
|
||||
|
||||
// Add/subtract (shifted register)
|
||||
|
||||
pub fn addShiftedRegister(
|
||||
@ -1378,11 +1432,11 @@ test "serialize instructions" {
|
||||
|
||||
const testcases = [_]Testcase{
|
||||
.{ // orr x0, xzr, x1
|
||||
.inst = Instruction.orr(.x0, .xzr, .x1, .lsl, 0),
|
||||
.inst = Instruction.orrShiftedRegister(.x0, .xzr, .x1, .lsl, 0),
|
||||
.expected = 0b1_01_01010_00_0_00001_000000_11111_00000,
|
||||
},
|
||||
.{ // orn x0, xzr, x1
|
||||
.inst = Instruction.orn(.x0, .xzr, .x1, .lsl, 0),
|
||||
.inst = Instruction.ornShiftedRegister(.x0, .xzr, .x1, .lsl, 0),
|
||||
.expected = 0b1_01_01010_00_1_00001_000000_11111_00000,
|
||||
},
|
||||
.{ // movz x1, #4
|
||||
@ -1502,11 +1556,11 @@ test "serialize instructions" {
|
||||
.expected = 0b10_101_0_001_1_0000010_00010_11111_00001,
|
||||
},
|
||||
.{ // and x0, x4, x2
|
||||
.inst = Instruction.@"and"(.x0, .x4, .x2, .lsl, 0),
|
||||
.inst = Instruction.andShiftedRegister(.x0, .x4, .x2, .lsl, 0),
|
||||
.expected = 0b1_00_01010_00_0_00010_000000_00100_00000,
|
||||
},
|
||||
.{ // and x0, x4, x2, lsl #0x8
|
||||
.inst = Instruction.@"and"(.x0, .x4, .x2, .lsl, 0x8),
|
||||
.inst = Instruction.andShiftedRegister(.x0, .x4, .x2, .lsl, 0x8),
|
||||
.expected = 0b1_00_01010_00_0_00010_001000_00100_00000,
|
||||
},
|
||||
.{ // add x0, x10, #10
|
||||
@ -1537,6 +1591,10 @@ test "serialize instructions" {
|
||||
.inst = Instruction.mul(.x1, .x4, .x9),
|
||||
.expected = 0b1_00_11011_000_01001_0_11111_00100_00001,
|
||||
},
|
||||
.{ // eor x3, x5, #1
|
||||
.inst = Instruction.eorImmediate(.x3, .x5, 0b000000, 0b000000, 0b1),
|
||||
.expected = 0b1_10_100100_1_000000_000000_00101_00011,
|
||||
},
|
||||
};
|
||||
|
||||
for (testcases) |case| {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user