mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
Merge pull request #10959 from joachimschmidt557/stage2-aarch64
stage2 AArch64: misc improvements
This commit is contained in:
commit
6dc5ce931c
@ -372,7 +372,7 @@ fn gen(self: *Self) !void {
|
||||
.data = .{ .load_store_register_pair = .{
|
||||
.rt = .x29,
|
||||
.rt2 = .x30,
|
||||
.rn = Register.sp,
|
||||
.rn = .sp,
|
||||
.offset = Instruction.LoadStorePairOffset.pre_index(-16),
|
||||
} },
|
||||
});
|
||||
@ -407,7 +407,7 @@ fn gen(self: *Self) !void {
|
||||
self.saved_regs_stack_space = 16;
|
||||
inline for (callee_preserved_regs) |reg| {
|
||||
if (self.register_manager.isRegAllocated(reg)) {
|
||||
saved_regs |= @as(u32, 1) << reg.id();
|
||||
saved_regs |= @as(u32, 1) << @intCast(u5, reg.id());
|
||||
self.saved_regs_stack_space += 8;
|
||||
}
|
||||
}
|
||||
@ -449,7 +449,7 @@ fn gen(self: *Self) !void {
|
||||
// the code. Therefore, we can just delete
|
||||
// the space initially reserved for the
|
||||
// jump
|
||||
self.mir_instructions.len -= 1;
|
||||
self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.items[0]);
|
||||
} else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
|
||||
self.mir_instructions.set(jmp_reloc, .{
|
||||
.tag = .b,
|
||||
@ -475,7 +475,7 @@ fn gen(self: *Self) !void {
|
||||
.data = .{ .load_store_register_pair = .{
|
||||
.rt = .x29,
|
||||
.rt2 = .x30,
|
||||
.rn = Register.sp,
|
||||
.rn = .sp,
|
||||
.offset = Instruction.LoadStorePairOffset.post_index(16),
|
||||
} },
|
||||
});
|
||||
@ -1041,6 +1041,7 @@ fn binOpRegister(
|
||||
const mir_tag: Mir.Inst.Tag = switch (tag) {
|
||||
.add, .ptr_add => .add_shifted_register,
|
||||
.sub, .ptr_sub => .sub_shifted_register,
|
||||
.mul => .mul,
|
||||
.xor => .eor_shifted_register,
|
||||
else => unreachable,
|
||||
};
|
||||
@ -1056,6 +1057,11 @@ fn binOpRegister(
|
||||
.imm6 = 0,
|
||||
.shift = .lsl,
|
||||
} },
|
||||
.mul => .{ .rrr = .{
|
||||
.rd = dest_reg,
|
||||
.rn = lhs_reg,
|
||||
.rm = rhs_reg,
|
||||
} },
|
||||
.xor => .{ .rrr_imm6_logical_shift = .{
|
||||
.rd = dest_reg,
|
||||
.rn = lhs_reg,
|
||||
@ -1222,6 +1228,24 @@ fn binOp(
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.mul => {
|
||||
switch (lhs_ty.zigTypeTag()) {
|
||||
.Vector => return self.fail("TODO binary operations on vectors", .{}),
|
||||
.Int => {
|
||||
assert(lhs_ty.eql(rhs_ty));
|
||||
const int_info = lhs_ty.intInfo(self.target.*);
|
||||
if (int_info.bits <= 64) {
|
||||
// TODO add optimisations for multiplication
|
||||
// with immediates, for example a * 2 can be
|
||||
// lowered to a << 1
|
||||
return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
||||
} else {
|
||||
return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
// Bitwise operations on integers
|
||||
.xor => {
|
||||
switch (lhs_ty.zigTypeTag()) {
|
||||
@ -1551,88 +1575,37 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
|
||||
};
|
||||
self.register_manager.freezeRegs(&.{base_mcv.register});
|
||||
|
||||
// TODO implement optimized ldr for airSliceElemVal
|
||||
const dst_mcv = try self.allocRegOrMem(inst, true);
|
||||
switch (elem_size) {
|
||||
else => {
|
||||
const dst_mcv = try self.allocRegOrMem(inst, true);
|
||||
|
||||
const offset_mcv = try self.genMulConstant(bin_op.rhs, @intCast(u32, elem_size));
|
||||
assert(offset_mcv == .register); // result of multiplication should always be register
|
||||
self.register_manager.freezeRegs(&.{offset_mcv.register});
|
||||
const offset_mcv = try self.binOp(
|
||||
.mul,
|
||||
null,
|
||||
index_mcv,
|
||||
.{ .immediate = elem_size },
|
||||
Type.usize,
|
||||
Type.usize,
|
||||
);
|
||||
assert(offset_mcv == .register); // result of multiplication should always be register
|
||||
self.register_manager.freezeRegs(&.{offset_mcv.register});
|
||||
|
||||
const addr_reg = try self.register_manager.allocReg(null);
|
||||
self.register_manager.freezeRegs(&.{addr_reg});
|
||||
defer self.register_manager.unfreezeRegs(&.{addr_reg});
|
||||
const addr_mcv = try self.binOp(.add, null, base_mcv, offset_mcv, Type.usize, Type.usize);
|
||||
|
||||
_ = try self.addInst(.{
|
||||
.tag = .add_shifted_register,
|
||||
.data = .{ .rrr_imm6_shift = .{
|
||||
.rd = addr_reg,
|
||||
.rn = base_mcv.register,
|
||||
.rm = offset_mcv.register,
|
||||
.imm6 = 0,
|
||||
.shift = .lsl,
|
||||
} },
|
||||
});
|
||||
// At this point in time, neither the base register
|
||||
// nor the offset register contains any valuable data
|
||||
// anymore.
|
||||
self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register });
|
||||
|
||||
// At this point in time, neither the base register
|
||||
// nor the offset register contains any valuable data
|
||||
// anymore.
|
||||
self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register });
|
||||
try self.load(dst_mcv, addr_mcv, slice_ptr_field_type);
|
||||
|
||||
try self.load(dst_mcv, .{ .register = addr_reg }, slice_ptr_field_type);
|
||||
|
||||
break :result dst_mcv;
|
||||
break :result dst_mcv;
|
||||
},
|
||||
}
|
||||
};
|
||||
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
||||
}
|
||||
|
||||
fn genMulConstant(self: *Self, op: Air.Inst.Ref, imm: u32) !MCValue {
|
||||
const lhs = try self.resolveInst(op);
|
||||
const rhs = MCValue{ .immediate = imm };
|
||||
|
||||
const lhs_is_register = lhs == .register;
|
||||
|
||||
if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
|
||||
defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register});
|
||||
|
||||
// Destination must be a register
|
||||
// LHS must be a register
|
||||
// RHS must be a register
|
||||
var dst_mcv: MCValue = undefined;
|
||||
var lhs_mcv: MCValue = lhs;
|
||||
var rhs_mcv: MCValue = rhs;
|
||||
|
||||
// Allocate registers for operands and/or destination
|
||||
// Allocate 1 or 2 registers
|
||||
if (lhs_is_register) {
|
||||
// Move RHS to register
|
||||
dst_mcv = MCValue{ .register = try self.register_manager.allocReg(null) };
|
||||
rhs_mcv = dst_mcv;
|
||||
} else {
|
||||
// Move LHS and RHS to register
|
||||
const regs = try self.register_manager.allocRegs(2, .{ null, null });
|
||||
lhs_mcv = MCValue{ .register = regs[0] };
|
||||
rhs_mcv = MCValue{ .register = regs[1] };
|
||||
dst_mcv = lhs_mcv;
|
||||
}
|
||||
|
||||
// Move the operands to the newly allocated registers
|
||||
if (!lhs_is_register) {
|
||||
try self.genSetReg(self.air.typeOf(op), lhs_mcv.register, lhs);
|
||||
}
|
||||
try self.genSetReg(Type.initTag(.usize), rhs_mcv.register, rhs);
|
||||
|
||||
_ = try self.addInst(.{
|
||||
.tag = .mul,
|
||||
.data = .{ .rrr = .{
|
||||
.rd = dst_mcv.register,
|
||||
.rn = lhs_mcv.register,
|
||||
.rm = rhs_mcv.register,
|
||||
} },
|
||||
});
|
||||
|
||||
return dst_mcv;
|
||||
}
|
||||
|
||||
fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
|
||||
@ -1763,23 +1736,17 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
|
||||
self.register_manager.freezeRegs(&.{addr_reg});
|
||||
defer self.register_manager.unfreezeRegs(&.{addr_reg});
|
||||
|
||||
const abi_size = elem_ty.abiSize(self.target.*);
|
||||
switch (dst_mcv) {
|
||||
.dead => unreachable,
|
||||
.undef => unreachable,
|
||||
.compare_flags_signed, .compare_flags_unsigned => unreachable,
|
||||
.embedded_in_code => unreachable,
|
||||
.register => |dst_reg| {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .ldr_immediate,
|
||||
.data = .{ .load_store_register_immediate = .{
|
||||
.rt = dst_reg,
|
||||
.rn = addr_reg,
|
||||
.offset = Instruction.LoadStoreOffset.none.immediate,
|
||||
} },
|
||||
});
|
||||
try self.genLdrRegister(dst_reg, addr_reg, abi_size);
|
||||
},
|
||||
.stack_offset => |off| {
|
||||
if (elem_ty.abiSize(self.target.*) <= 8) {
|
||||
if (abi_size <= 8) {
|
||||
const tmp_reg = try self.register_manager.allocReg(null);
|
||||
self.register_manager.freezeRegs(&.{tmp_reg});
|
||||
defer self.register_manager.unfreezeRegs(&.{tmp_reg});
|
||||
@ -1940,6 +1907,100 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, abi_size: u64) !void {
|
||||
switch (abi_size) {
|
||||
1 => {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .ldrb_immediate,
|
||||
.data = .{ .load_store_register_immediate = .{
|
||||
.rt = value_reg.to32(),
|
||||
.rn = addr_reg,
|
||||
.offset = Instruction.LoadStoreOffset.none.immediate,
|
||||
} },
|
||||
});
|
||||
},
|
||||
2 => {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .ldrh_immediate,
|
||||
.data = .{ .load_store_register_immediate = .{
|
||||
.rt = value_reg.to32(),
|
||||
.rn = addr_reg,
|
||||
.offset = Instruction.LoadStoreOffset.none.immediate,
|
||||
} },
|
||||
});
|
||||
},
|
||||
4 => {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .ldr_immediate,
|
||||
.data = .{ .load_store_register_immediate = .{
|
||||
.rt = value_reg.to32(),
|
||||
.rn = addr_reg,
|
||||
.offset = Instruction.LoadStoreOffset.none.immediate,
|
||||
} },
|
||||
});
|
||||
},
|
||||
8 => {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .ldr_immediate,
|
||||
.data = .{ .load_store_register_immediate = .{
|
||||
.rt = value_reg.to64(),
|
||||
.rn = addr_reg,
|
||||
.offset = Instruction.LoadStoreOffset.none.immediate,
|
||||
} },
|
||||
});
|
||||
},
|
||||
3, 5, 6, 7 => return self.fail("TODO: genLdrRegister for more abi_sizes", .{}),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, abi_size: u64) !void {
|
||||
switch (abi_size) {
|
||||
1 => {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .strb_immediate,
|
||||
.data = .{ .load_store_register_immediate = .{
|
||||
.rt = value_reg.to32(),
|
||||
.rn = addr_reg,
|
||||
.offset = Instruction.LoadStoreOffset.none.immediate,
|
||||
} },
|
||||
});
|
||||
},
|
||||
2 => {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .strh_immediate,
|
||||
.data = .{ .load_store_register_immediate = .{
|
||||
.rt = value_reg.to32(),
|
||||
.rn = addr_reg,
|
||||
.offset = Instruction.LoadStoreOffset.none.immediate,
|
||||
} },
|
||||
});
|
||||
},
|
||||
4 => {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .str_immediate,
|
||||
.data = .{ .load_store_register_immediate = .{
|
||||
.rt = value_reg.to32(),
|
||||
.rn = addr_reg,
|
||||
.offset = Instruction.LoadStoreOffset.none.immediate,
|
||||
} },
|
||||
});
|
||||
},
|
||||
8 => {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .str_immediate,
|
||||
.data = .{ .load_store_register_immediate = .{
|
||||
.rt = value_reg.to64(),
|
||||
.rn = addr_reg,
|
||||
.offset = Instruction.LoadStoreOffset.none.immediate,
|
||||
} },
|
||||
});
|
||||
},
|
||||
3, 5, 6, 7 => return self.fail("TODO: genStrRegister for more abi_sizes", .{}),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
|
||||
switch (ptr) {
|
||||
.none => unreachable,
|
||||
@ -1960,8 +2021,28 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
|
||||
.embedded_in_code => {
|
||||
return self.fail("TODO implement storing to MCValue.embedded_in_code", .{});
|
||||
},
|
||||
.register => {
|
||||
return self.fail("TODO implement storing to MCValue.register", .{});
|
||||
.register => |addr_reg| {
|
||||
self.register_manager.freezeRegs(&.{addr_reg});
|
||||
defer self.register_manager.unfreezeRegs(&.{addr_reg});
|
||||
|
||||
const abi_size = value_ty.abiSize(self.target.*);
|
||||
switch (value) {
|
||||
.register => |value_reg| {
|
||||
try self.genStrRegister(value_reg, addr_reg, abi_size);
|
||||
},
|
||||
else => {
|
||||
if (abi_size <= 8) {
|
||||
const tmp_reg = try self.register_manager.allocReg(null);
|
||||
self.register_manager.freezeRegs(&.{tmp_reg});
|
||||
defer self.register_manager.unfreezeRegs(&.{tmp_reg});
|
||||
|
||||
try self.genSetReg(value_ty, tmp_reg, value);
|
||||
try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
|
||||
} else {
|
||||
return self.fail("TODO implement memcpy", .{});
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
.memory,
|
||||
.stack_offset,
|
||||
@ -2005,7 +2086,8 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
|
||||
fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue {
|
||||
return if (self.liveness.isUnused(inst)) .dead else result: {
|
||||
const mcv = try self.resolveInst(operand);
|
||||
const struct_ty = self.air.typeOf(operand).childType();
|
||||
const ptr_ty = self.air.typeOf(operand);
|
||||
const struct_ty = ptr_ty.childType();
|
||||
const struct_size = @intCast(u32, struct_ty.abiSize(self.target.*));
|
||||
const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));
|
||||
const struct_field_ty = struct_ty.structFieldType(index);
|
||||
@ -2014,7 +2096,28 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
|
||||
.ptr_stack_offset => |off| {
|
||||
break :result MCValue{ .ptr_stack_offset = off + struct_size - struct_field_offset - struct_field_size };
|
||||
},
|
||||
else => return self.fail("TODO implement codegen struct_field_ptr for {}", .{mcv}),
|
||||
else => {
|
||||
const offset_reg = try self.copyToTmpRegister(ptr_ty, .{
|
||||
.immediate = struct_field_offset,
|
||||
});
|
||||
self.register_manager.freezeRegs(&.{offset_reg});
|
||||
defer self.register_manager.unfreezeRegs(&.{offset_reg});
|
||||
|
||||
const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv);
|
||||
self.register_manager.freezeRegs(&.{addr_reg});
|
||||
defer self.register_manager.unfreezeRegs(&.{addr_reg});
|
||||
|
||||
const dest = try self.binOp(
|
||||
.add,
|
||||
null,
|
||||
.{ .register = addr_reg },
|
||||
.{ .register = offset_reg },
|
||||
Type.usize,
|
||||
Type.usize,
|
||||
);
|
||||
|
||||
break :result dest;
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -2983,8 +3086,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
const abi_size = ty.abiSize(self.target.*);
|
||||
switch (mcv) {
|
||||
.dead => unreachable,
|
||||
.ptr_stack_offset => unreachable,
|
||||
.ptr_embedded_in_code => unreachable,
|
||||
.unreach, .none => return, // Nothing to do.
|
||||
.undef => {
|
||||
if (!self.wantSafety())
|
||||
@ -3001,6 +3102,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
.compare_flags_unsigned,
|
||||
.compare_flags_signed,
|
||||
.immediate,
|
||||
.ptr_stack_offset,
|
||||
.ptr_embedded_in_code,
|
||||
=> {
|
||||
const reg = try self.copyToTmpRegister(ty, mcv);
|
||||
return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
|
||||
@ -3043,17 +3146,18 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
_ = sym_index;
|
||||
return self.fail("TODO implement set stack variable from {}", .{mcv});
|
||||
},
|
||||
.memory => |vaddr| {
|
||||
_ = vaddr;
|
||||
return self.fail("TODO implement set stack variable from memory vaddr", .{});
|
||||
},
|
||||
.stack_offset => |off| {
|
||||
if (stack_offset == off)
|
||||
return; // Copy stack variable to itself; nothing to do.
|
||||
.memory,
|
||||
.stack_offset,
|
||||
=> {
|
||||
switch (mcv) {
|
||||
.stack_offset => |off| {
|
||||
if (stack_offset == off)
|
||||
return; // Copy stack variable to itself; nothing to do.
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
const ptr_bits = self.target.cpu.arch.ptrBitWidth();
|
||||
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
|
||||
if (abi_size <= ptr_bytes) {
|
||||
if (abi_size <= 8) {
|
||||
const reg = try self.copyToTmpRegister(ty, mcv);
|
||||
return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
|
||||
} else {
|
||||
@ -3068,17 +3172,23 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
const count_reg = regs[3];
|
||||
const tmp_reg = regs[4];
|
||||
|
||||
// sub src_reg, fp, #off
|
||||
const adj_src_offset = off + abi_size;
|
||||
const src_offset = math.cast(u12, adj_src_offset) catch return self.fail("TODO load: larger stack offsets", .{});
|
||||
_ = try self.addInst(.{
|
||||
.tag = .sub_immediate,
|
||||
.data = .{ .rr_imm12_sh = .{
|
||||
.rd = src_reg,
|
||||
.rn = .x29,
|
||||
.imm12 = src_offset,
|
||||
} },
|
||||
});
|
||||
switch (mcv) {
|
||||
.stack_offset => |off| {
|
||||
// sub src_reg, fp, #off
|
||||
const adj_src_offset = off + abi_size;
|
||||
const src_offset = math.cast(u12, adj_src_offset) catch return self.fail("TODO load: larger stack offsets", .{});
|
||||
_ = try self.addInst(.{
|
||||
.tag = .sub_immediate,
|
||||
.data = .{ .rr_imm12_sh = .{
|
||||
.rd = src_reg,
|
||||
.rn = .x29,
|
||||
.imm12 = src_offset,
|
||||
} },
|
||||
});
|
||||
},
|
||||
.memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = addr }),
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
// sub dst_reg, fp, #stack_offset
|
||||
const adj_dst_off = stack_offset + abi_size;
|
||||
@ -3105,7 +3215,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
|
||||
switch (mcv) {
|
||||
.dead => unreachable,
|
||||
.ptr_stack_offset => unreachable,
|
||||
.ptr_embedded_in_code => unreachable,
|
||||
.unreach, .none => return, // Nothing to do.
|
||||
.undef => {
|
||||
@ -3118,6 +3227,24 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
|
||||
else => unreachable, // unexpected register size
|
||||
}
|
||||
},
|
||||
.ptr_stack_offset => |unadjusted_off| {
|
||||
// TODO: maybe addressing from sp instead of fp
|
||||
const elem_ty = ty.childType();
|
||||
const abi_size = elem_ty.abiSize(self.target.*);
|
||||
const adj_off = unadjusted_off + abi_size;
|
||||
|
||||
const imm12 = math.cast(u12, adj_off) catch
|
||||
return self.fail("TODO larger stack offsets", .{});
|
||||
|
||||
_ = try self.addInst(.{
|
||||
.tag = .sub_immediate,
|
||||
.data = .{ .rr_imm12_sh = .{
|
||||
.rd = reg,
|
||||
.rn = .x29,
|
||||
.imm12 = imm12,
|
||||
} },
|
||||
});
|
||||
},
|
||||
.compare_flags_unsigned,
|
||||
.compare_flags_signed,
|
||||
=> |op| {
|
||||
|
||||
@ -909,7 +909,7 @@ fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
var other_reg: Register = undefined;
|
||||
while (i > 0) : (i -= 1) {
|
||||
const reg = @intToEnum(Register, i - 1);
|
||||
if (reg_list & @as(u32, 1) << reg.id() != 0) {
|
||||
if (reg_list & @as(u32, 1) << @intCast(u5, reg.id()) != 0) {
|
||||
if (count % 2 == 0) {
|
||||
if (count == number_of_regs - 1) {
|
||||
try emit.writeInstruction(Instruction.ldr(
|
||||
@ -939,7 +939,7 @@ fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
var other_reg: Register = undefined;
|
||||
while (i < 32) : (i += 1) {
|
||||
const reg = @intToEnum(Register, i);
|
||||
if (reg_list & @as(u32, 1) << reg.id() != 0) {
|
||||
if (reg_list & @as(u32, 1) << @intCast(u5, reg.id()) != 0) {
|
||||
if (count % 2 == 0) {
|
||||
if (count == number_of_regs - 1) {
|
||||
try emit.writeInstruction(Instruction.str(
|
||||
|
||||
@ -7,7 +7,7 @@ const testing = std.testing;
|
||||
// zig fmt: off
|
||||
|
||||
/// General purpose registers in the AArch64 instruction set
|
||||
pub const Register = enum(u6) {
|
||||
pub const Register = enum(u7) {
|
||||
// 64-bit registers
|
||||
x0, x1, x2, x3, x4, x5, x6, x7,
|
||||
x8, x9, x10, x11, x12, x13, x14, x15,
|
||||
@ -20,10 +20,23 @@ pub const Register = enum(u6) {
|
||||
w16, w17, w18, w19, w20, w21, w22, w23,
|
||||
w24, w25, w26, w27, w28, w29, w30, wzr,
|
||||
|
||||
pub const sp = Register.xzr;
|
||||
// Stack pointer
|
||||
sp, wsp,
|
||||
|
||||
pub fn id(self: Register) u5 {
|
||||
return @truncate(u5, @enumToInt(self));
|
||||
pub fn id(self: Register) u6 {
|
||||
return switch (@enumToInt(self)) {
|
||||
0...63 => return @as(u6, @truncate(u5, @enumToInt(self))),
|
||||
64...65 => 32,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn enc(self: Register) u5 {
|
||||
return switch (@enumToInt(self)) {
|
||||
0...63 => return @truncate(u5, @enumToInt(self)),
|
||||
64...65 => 31,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the bit-width of the register.
|
||||
@ -31,17 +44,32 @@ pub const Register = enum(u6) {
|
||||
return switch (@enumToInt(self)) {
|
||||
0...31 => 64,
|
||||
32...63 => 32,
|
||||
64 => 64,
|
||||
65 => 32,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Convert from any register to its 64 bit alias.
|
||||
pub fn to64(self: Register) Register {
|
||||
return @intToEnum(Register, self.id());
|
||||
return switch (@enumToInt(self)) {
|
||||
0...31 => self,
|
||||
32...63 => @intToEnum(Register, @enumToInt(self) - 32),
|
||||
64 => .sp,
|
||||
65 => .sp,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Convert from any register to its 32 bit alias.
|
||||
pub fn to32(self: Register) Register {
|
||||
return @intToEnum(Register, @as(u6, self.id()) + 32);
|
||||
return switch (@enumToInt(self)) {
|
||||
0...31 => @intToEnum(Register, @enumToInt(self) + 32),
|
||||
32...63 => self,
|
||||
64 => .wsp,
|
||||
65 => .wsp,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the index into `callee_preserved_regs`.
|
||||
@ -53,7 +81,7 @@ pub const Register = enum(u6) {
|
||||
}
|
||||
|
||||
pub fn dwarfLocOp(self: Register) u8 {
|
||||
return @as(u8, self.id()) + DW.OP.reg0;
|
||||
return @as(u8, self.enc()) + DW.OP.reg0;
|
||||
}
|
||||
};
|
||||
|
||||
@ -76,15 +104,15 @@ pub const callee_preserved_regs = callee_preserved_regs_impl.callee_preserved_re
|
||||
pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
|
||||
pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
|
||||
|
||||
test "Register.id" {
|
||||
try testing.expectEqual(@as(u5, 0), Register.x0.id());
|
||||
try testing.expectEqual(@as(u5, 0), Register.w0.id());
|
||||
test "Register.enc" {
|
||||
try testing.expectEqual(@as(u5, 0), Register.x0.enc());
|
||||
try testing.expectEqual(@as(u5, 0), Register.w0.enc());
|
||||
|
||||
try testing.expectEqual(@as(u5, 31), Register.xzr.id());
|
||||
try testing.expectEqual(@as(u5, 31), Register.wzr.id());
|
||||
try testing.expectEqual(@as(u5, 31), Register.xzr.enc());
|
||||
try testing.expectEqual(@as(u5, 31), Register.wzr.enc());
|
||||
|
||||
try testing.expectEqual(@as(u5, 31), Register.sp.id());
|
||||
try testing.expectEqual(@as(u5, 31), Register.sp.id());
|
||||
try testing.expectEqual(@as(u5, 31), Register.sp.enc());
|
||||
try testing.expectEqual(@as(u5, 31), Register.sp.enc());
|
||||
}
|
||||
|
||||
test "Register.size" {
|
||||
@ -479,7 +507,7 @@ pub const Instruction = union(enum) {
|
||||
assert(shift % 16 == 0 and shift <= 16);
|
||||
return Instruction{
|
||||
.move_wide_immediate = .{
|
||||
.rd = rd.id(),
|
||||
.rd = rd.enc(),
|
||||
.imm16 = imm16,
|
||||
.hw = @intCast(u2, shift / 16),
|
||||
.opc = opc,
|
||||
@ -491,7 +519,7 @@ pub const Instruction = union(enum) {
|
||||
assert(shift % 16 == 0 and shift <= 48);
|
||||
return Instruction{
|
||||
.move_wide_immediate = .{
|
||||
.rd = rd.id(),
|
||||
.rd = rd.enc(),
|
||||
.imm16 = imm16,
|
||||
.hw = @intCast(u2, shift / 16),
|
||||
.opc = opc,
|
||||
@ -508,7 +536,7 @@ pub const Instruction = union(enum) {
|
||||
const imm21_u = @bitCast(u21, imm21);
|
||||
return Instruction{
|
||||
.pc_relative_address = .{
|
||||
.rd = rd.id(),
|
||||
.rd = rd.enc(),
|
||||
.immlo = @truncate(u2, imm21_u),
|
||||
.immhi = @truncate(u19, imm21_u >> 2),
|
||||
.op = op,
|
||||
@ -580,7 +608,7 @@ pub const Instruction = union(enum) {
|
||||
pub fn reg(rm: Register) LoadStoreOffset {
|
||||
return .{
|
||||
.register = .{
|
||||
.rm = rm.id(),
|
||||
.rm = rm.enc(),
|
||||
.shift = .{
|
||||
.lsl = 0,
|
||||
},
|
||||
@ -592,7 +620,7 @@ pub const Instruction = union(enum) {
|
||||
assert(rm.size() == 32 and (shift == 0 or shift == 2));
|
||||
return .{
|
||||
.register = .{
|
||||
.rm = rm.id(),
|
||||
.rm = rm.enc(),
|
||||
.shift = .{
|
||||
.uxtw = shift,
|
||||
},
|
||||
@ -604,7 +632,7 @@ pub const Instruction = union(enum) {
|
||||
assert(rm.size() == 64 and (shift == 0 or shift == 3));
|
||||
return .{
|
||||
.register = .{
|
||||
.rm = rm.id(),
|
||||
.rm = rm.enc(),
|
||||
.shift = .{
|
||||
.lsl = shift,
|
||||
},
|
||||
@ -616,7 +644,7 @@ pub const Instruction = union(enum) {
|
||||
assert(rm.size() == 32 and (shift == 0 or shift == 2));
|
||||
return .{
|
||||
.register = .{
|
||||
.rm = rm.id(),
|
||||
.rm = rm.enc(),
|
||||
.shift = .{
|
||||
.sxtw = shift,
|
||||
},
|
||||
@ -628,7 +656,7 @@ pub const Instruction = union(enum) {
|
||||
assert(rm.size() == 64 and (shift == 0 or shift == 3));
|
||||
return .{
|
||||
.register = .{
|
||||
.rm = rm.id(),
|
||||
.rm = rm.enc(),
|
||||
.shift = .{
|
||||
.sxtx = shift,
|
||||
},
|
||||
@ -676,8 +704,8 @@ pub const Instruction = union(enum) {
|
||||
};
|
||||
return Instruction{
|
||||
.load_store_register = .{
|
||||
.rt = rt.id(),
|
||||
.rn = rn.id(),
|
||||
.rt = rt.enc(),
|
||||
.rn = rn.enc(),
|
||||
.offset = off,
|
||||
.opc = opc,
|
||||
.op1 = op1,
|
||||
@ -711,9 +739,9 @@ pub const Instruction = union(enum) {
|
||||
const imm7 = @truncate(u7, @bitCast(u9, offset >> 2));
|
||||
return Instruction{
|
||||
.load_store_register_pair = .{
|
||||
.rt1 = rt1.id(),
|
||||
.rn = rn.id(),
|
||||
.rt2 = rt2.id(),
|
||||
.rt1 = rt1.enc(),
|
||||
.rn = rn.enc(),
|
||||
.rt2 = rt2.enc(),
|
||||
.imm7 = imm7,
|
||||
.load = @boolToInt(load),
|
||||
.encoding = encoding,
|
||||
@ -726,9 +754,9 @@ pub const Instruction = union(enum) {
|
||||
const imm7 = @truncate(u7, @bitCast(u9, offset >> 3));
|
||||
return Instruction{
|
||||
.load_store_register_pair = .{
|
||||
.rt1 = rt1.id(),
|
||||
.rn = rn.id(),
|
||||
.rt2 = rt2.id(),
|
||||
.rt1 = rt1.enc(),
|
||||
.rn = rn.enc(),
|
||||
.rt2 = rt2.enc(),
|
||||
.imm7 = imm7,
|
||||
.load = @boolToInt(load),
|
||||
.encoding = encoding,
|
||||
@ -743,7 +771,7 @@ pub const Instruction = union(enum) {
|
||||
fn loadLiteral(rt: Register, imm19: u19) Instruction {
|
||||
return Instruction{
|
||||
.load_literal = .{
|
||||
.rt = rt.id(),
|
||||
.rt = rt.enc(),
|
||||
.imm19 = imm19,
|
||||
.opc = switch (rt.size()) {
|
||||
32 => 0b00,
|
||||
@ -782,7 +810,7 @@ pub const Instruction = union(enum) {
|
||||
return Instruction{
|
||||
.unconditional_branch_register = .{
|
||||
.op4 = op4,
|
||||
.rn = rn.id(),
|
||||
.rn = rn.enc(),
|
||||
.op3 = op3,
|
||||
.op2 = op2,
|
||||
.opc = opc,
|
||||
@ -818,10 +846,10 @@ pub const Instruction = union(enum) {
|
||||
assert(amount < 32);
|
||||
return Instruction{
|
||||
.logical_shifted_register = .{
|
||||
.rd = rd.id(),
|
||||
.rn = rn.id(),
|
||||
.rd = rd.enc(),
|
||||
.rn = rn.enc(),
|
||||
.imm6 = amount,
|
||||
.rm = rm.id(),
|
||||
.rm = rm.enc(),
|
||||
.n = n,
|
||||
.shift = @enumToInt(shift),
|
||||
.opc = opc,
|
||||
@ -832,10 +860,10 @@ pub const Instruction = union(enum) {
|
||||
64 => {
|
||||
return Instruction{
|
||||
.logical_shifted_register = .{
|
||||
.rd = rd.id(),
|
||||
.rn = rn.id(),
|
||||
.rd = rd.enc(),
|
||||
.rn = rn.enc(),
|
||||
.imm6 = amount,
|
||||
.rm = rm.id(),
|
||||
.rm = rm.enc(),
|
||||
.n = n,
|
||||
.shift = @enumToInt(shift),
|
||||
.opc = opc,
|
||||
@ -857,8 +885,8 @@ pub const Instruction = union(enum) {
|
||||
) Instruction {
|
||||
return Instruction{
|
||||
.add_subtract_immediate = .{
|
||||
.rd = rd.id(),
|
||||
.rn = rn.id(),
|
||||
.rd = rd.enc(),
|
||||
.rn = rn.enc(),
|
||||
.imm12 = imm12,
|
||||
.sh = @boolToInt(shift),
|
||||
.s = s,
|
||||
@ -885,10 +913,10 @@ pub const Instruction = union(enum) {
|
||||
) Instruction {
|
||||
return Instruction{
|
||||
.add_subtract_shifted_register = .{
|
||||
.rd = rd.id(),
|
||||
.rn = rn.id(),
|
||||
.rd = rd.enc(),
|
||||
.rn = rn.enc(),
|
||||
.imm6 = imm6,
|
||||
.rm = rm.id(),
|
||||
.rm = rm.enc(),
|
||||
.shift = @enumToInt(shift),
|
||||
.s = s,
|
||||
.op = op,
|
||||
@ -926,7 +954,7 @@ pub const Instruction = union(enum) {
|
||||
assert(offset & 0b11 == 0b00);
|
||||
return Instruction{
|
||||
.compare_and_branch = .{
|
||||
.rt = rt.id(),
|
||||
.rt = rt.enc(),
|
||||
.imm19 = @bitCast(u19, @intCast(i19, offset >> 2)),
|
||||
.op = op,
|
||||
.sf = switch (rt.size()) {
|
||||
@ -949,11 +977,11 @@ pub const Instruction = union(enum) {
|
||||
) Instruction {
|
||||
return Instruction{
|
||||
.conditional_select = .{
|
||||
.rd = rd.id(),
|
||||
.rn = rn.id(),
|
||||
.rd = rd.enc(),
|
||||
.rn = rn.enc(),
|
||||
.op2 = op2,
|
||||
.cond = @enumToInt(cond),
|
||||
.rm = rm.id(),
|
||||
.rm = rm.enc(),
|
||||
.s = s,
|
||||
.op = op,
|
||||
.sf = switch (rd.size()) {
|
||||
@ -976,11 +1004,11 @@ pub const Instruction = union(enum) {
|
||||
) Instruction {
|
||||
return Instruction{
|
||||
.data_processing_3_source = .{
|
||||
.rd = rd.id(),
|
||||
.rn = rn.id(),
|
||||
.ra = ra.id(),
|
||||
.rd = rd.enc(),
|
||||
.rn = rn.enc(),
|
||||
.ra = ra.enc(),
|
||||
.o0 = o0,
|
||||
.rm = rm.id(),
|
||||
.rm = rm.enc(),
|
||||
.op31 = op31,
|
||||
.op54 = op54,
|
||||
.sf = switch (rd.size()) {
|
||||
|
||||
@ -27,7 +27,6 @@ test "default alignment allows unspecified in type syntax" {
|
||||
}
|
||||
|
||||
test "implicitly decreasing pointer alignment" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
const a: u32 align(4) = 3;
|
||||
const b: u32 align(8) = 4;
|
||||
try expect(addUnaligned(&a, &b) == 7);
|
||||
@ -38,7 +37,6 @@ fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 {
|
||||
}
|
||||
|
||||
test "@alignCast pointers" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
var x: u32 align(4) = 1;
|
||||
expectsOnly1(&x);
|
||||
try expect(x == 2);
|
||||
@ -313,7 +311,7 @@ fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) !void {
|
||||
}
|
||||
|
||||
test "alignment of function with c calling convention" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
||||
|
||||
var runtime_nothing = ¬hing;
|
||||
|
||||
@ -48,7 +48,7 @@ const g1: i32 = 1233 + 1;
|
||||
var g2: i32 = 0;
|
||||
|
||||
test "global variables" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest;
|
||||
try expect(g2 == 0);
|
||||
g2 = g1;
|
||||
try expect(g2 == 1234);
|
||||
@ -327,7 +327,6 @@ const FnPtrWrapper = struct {
|
||||
};
|
||||
|
||||
test "const ptr from var variable" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
|
||||
var x: u64 = undefined;
|
||||
@ -611,7 +610,7 @@ test "comptime cast fn to ptr" {
|
||||
}
|
||||
|
||||
test "equality compare fn ptrs" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
||||
|
||||
var a = &emptyFn;
|
||||
@ -619,7 +618,7 @@ test "equality compare fn ptrs" {
|
||||
}
|
||||
|
||||
test "self reference through fn ptr field" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
|
||||
|
||||
@ -8,7 +8,6 @@ test "@bitReverse large exotic integer" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
// Currently failing on stage1 for big-endian targets
|
||||
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ const ptr = &global;
|
||||
var global: usize = 123;
|
||||
|
||||
test "constant pointer to global variable causes runtime load" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest;
|
||||
global = 1234;
|
||||
try expect(&global == ptr);
|
||||
try expect(ptr.* == 1234);
|
||||
|
||||
@ -6,7 +6,7 @@ const S = struct {
|
||||
p: *S,
|
||||
};
|
||||
test "bug 2006" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
var a: S = undefined;
|
||||
a = S{ .p = undefined };
|
||||
|
||||
@ -211,7 +211,6 @@ test "implicit cast from *[N]T to [*c]T" {
|
||||
}
|
||||
|
||||
test "*usize to *void" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
var i = @as(usize, 0);
|
||||
var v = @ptrCast(*void, &i);
|
||||
v.* = {};
|
||||
@ -1014,7 +1013,7 @@ test "cast from array reference to fn: comptime fn ptr" {
|
||||
try expect(@ptrToInt(f) == @ptrToInt(&global_array));
|
||||
}
|
||||
test "cast from array reference to fn: runtime fn ptr" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
@ -36,7 +36,6 @@ test "optional pointer to size zero struct" {
|
||||
}
|
||||
|
||||
test "equality compare optional pointers" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
|
||||
@ -81,7 +81,7 @@ fn assertLenIsZero(msg: []const u8) !void {
|
||||
}
|
||||
|
||||
test "access len index of sentinel-terminated slice" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
|
||||
@ -43,7 +43,6 @@ const StructWithFields = struct {
|
||||
};
|
||||
|
||||
test "non-packed struct has fields padded out to the required alignment" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
|
||||
const foo = StructWithFields{ .a = 5, .b = 1, .c = 10, .d = 2 };
|
||||
@ -67,7 +66,7 @@ const SmallStruct = struct {
|
||||
};
|
||||
|
||||
test "lower unnamed constants" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest;
|
||||
var foo = SmallStruct{ .a = 1, .b = 255 };
|
||||
try expect(foo.first() == 1);
|
||||
try expect(foo.second() == 255);
|
||||
@ -186,7 +185,6 @@ test "store member function in variable" {
|
||||
}
|
||||
|
||||
test "member functions" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
const r = MemberFnRand{ .seed = 1234 };
|
||||
try expect(r.getSeed() == 1234);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user