stage2 AArch64: Add ldrsb, ldrsh instructions

This commit is contained in:
joachimschmidt557 2022-04-16 23:03:11 +02:00
parent f95a8ddafa
commit d9d9fea6ae
No known key found for this signature in database
GPG Key ID: E0B575BE2884ACC5
6 changed files with 119 additions and 125 deletions

View File

@ -2056,7 +2056,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
.undef => unreachable,
.compare_flags_signed, .compare_flags_unsigned => unreachable,
.register => |dst_reg| {
try self.genLdrRegister(dst_reg, addr_reg, elem_size);
try self.genLdrRegister(dst_reg, addr_reg, elem_ty);
},
.stack_offset => |off| {
if (elem_size <= 8) {
@ -2210,98 +2210,47 @@ 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,
} },
});
},
fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void {
const abi_size = ty.abiSize(self.target.*);
const tag: Mir.Inst.Tag = switch (abi_size) {
1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_immediate else .ldrb_immediate,
2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_immediate else .ldrh_immediate,
4 => .ldr_immediate,
8 => .ldr_immediate,
3, 5, 6, 7 => return self.fail("TODO: genLdrRegister for more abi_sizes", .{}),
else => unreachable,
}
};
_ = try self.addInst(.{
.tag = tag,
.data = .{ .load_store_register_immediate = .{
.rt = value_reg,
.rn = addr_reg,
.offset = Instruction.LoadStoreOffset.none.immediate,
} },
});
}
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,
} },
});
},
fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void {
const abi_size = ty.abiSize(self.target.*);
const tag: Mir.Inst.Tag = switch (abi_size) {
1 => .strb_immediate,
2 => .strh_immediate,
4, 8 => .str_immediate,
3, 5, 6, 7 => return self.fail("TODO: genStrRegister for more abi_sizes", .{}),
else => unreachable,
}
};
_ = try self.addInst(.{
.tag = tag,
.data = .{ .load_store_register_immediate = .{
.rt = value_reg,
.rn = addr_reg,
.offset = Instruction.LoadStoreOffset.none.immediate,
} },
});
}
fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
@ -2326,7 +2275,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
switch (value) {
.register => |value_reg| {
try self.genStrRegister(value_reg, addr_reg, abi_size);
try self.genStrRegister(value_reg, addr_reg, value_ty);
},
else => {
if (abi_size <= 8) {
@ -3624,7 +3573,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
// The value is in memory at a hard-coded address.
// If the type is a pointer, it means the pointer address is at this memory location.
try self.genSetReg(ty, reg.to64(), .{ .immediate = addr });
try self.genLdrRegister(reg, reg.to64(), ty.abiSize(self.target.*));
try self.genLdrRegister(reg, reg.to64(), ty);
},
.stack_offset => |off| {
const abi_size = ty.abiSize(self.target.*);
@ -3632,21 +3581,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
switch (abi_size) {
1, 2, 4, 8 => {
const tag: Mir.Inst.Tag = switch (abi_size) {
1 => .ldrb_stack,
2 => .ldrh_stack,
1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_stack else .ldrb_stack,
2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_stack else .ldrh_stack,
4, 8 => .ldr_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
};
_ = try self.addInst(.{
.tag = tag,
.data = .{ .load_store_stack = .{
.rt = rt,
.rt = reg,
.offset = @intCast(u32, off),
} },
});

View File

@ -131,6 +131,8 @@ pub fn emitMir(
.ldr_stack => try emit.mirLoadStoreStack(inst),
.ldrb_stack => try emit.mirLoadStoreStack(inst),
.ldrh_stack => try emit.mirLoadStoreStack(inst),
.ldrsb_stack => try emit.mirLoadStoreStack(inst),
.ldrsh_stack => try emit.mirLoadStoreStack(inst),
.str_stack => try emit.mirLoadStoreStack(inst),
.strb_stack => try emit.mirLoadStoreStack(inst),
.strh_stack => try emit.mirLoadStoreStack(inst),
@ -145,6 +147,9 @@ pub fn emitMir(
.ldr_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
.ldrb_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
.ldrh_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
.ldrsb_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
.ldrsh_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
.ldrsw_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
.str_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
.strb_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
.strh_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
@ -847,14 +852,14 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void {
const raw_offset = emit.stack_size - load_store_stack.offset;
const offset = switch (tag) {
.ldrb_stack, .strb_stack => blk: {
.ldrb_stack, .ldrsb_stack, .strb_stack => blk: {
if (math.cast(u12, raw_offset)) |imm| {
break :blk Instruction.LoadStoreOffset.imm(imm);
} else |_| {
return emit.fail("TODO load/store stack byte with larger offset", .{});
}
},
.ldrh_stack, .strh_stack => blk: {
.ldrh_stack, .ldrsh_stack, .strh_stack => blk: {
assert(std.mem.isAlignedGeneric(u32, raw_offset, 2)); // misaligned stack entry
if (math.cast(u12, @divExact(raw_offset, 2))) |imm| {
break :blk Instruction.LoadStoreOffset.imm(imm);
@ -883,6 +888,8 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void {
.ldr_stack => try emit.writeInstruction(Instruction.ldr(rt, .sp, offset)),
.ldrb_stack => try emit.writeInstruction(Instruction.ldrb(rt, .sp, offset)),
.ldrh_stack => try emit.writeInstruction(Instruction.ldrh(rt, .sp, offset)),
.ldrsb_stack => try emit.writeInstruction(Instruction.ldrsb(rt, .sp, offset)),
.ldrsh_stack => try emit.writeInstruction(Instruction.ldrsh(rt, .sp, offset)),
.str_stack => try emit.writeInstruction(Instruction.str(rt, .sp, offset)),
.strb_stack => try emit.writeInstruction(Instruction.strb(rt, .sp, offset)),
.strh_stack => try emit.writeInstruction(Instruction.strh(rt, .sp, offset)),
@ -901,6 +908,9 @@ fn mirLoadStoreRegisterImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
.ldr_immediate => try emit.writeInstruction(Instruction.ldr(rt, rn, offset)),
.ldrb_immediate => try emit.writeInstruction(Instruction.ldrb(rt, rn, offset)),
.ldrh_immediate => try emit.writeInstruction(Instruction.ldrh(rt, rn, offset)),
.ldrsb_immediate => try emit.writeInstruction(Instruction.ldrsb(rt, rn, offset)),
.ldrsh_immediate => try emit.writeInstruction(Instruction.ldrsh(rt, rn, offset)),
.ldrsw_immediate => try emit.writeInstruction(Instruction.ldrsw(rt, rn, offset)),
.str_immediate => try emit.writeInstruction(Instruction.str(rt, rn, offset)),
.strb_immediate => try emit.writeInstruction(Instruction.strb(rt, rn, offset)),
.strh_immediate => try emit.writeInstruction(Instruction.strh(rt, rn, offset)),

View File

@ -100,6 +100,16 @@ pub const Inst = struct {
ldrh_immediate,
/// Load Register Halfword (register)
ldrh_register,
/// Load Register Signed Byte (immediate)
ldrsb_immediate,
/// Pseudo-instruction: Load signed byte from stack
ldrsb_stack,
/// Load Register Signed Halfword (immediate)
ldrsh_immediate,
/// Pseudo-instruction: Load signed halfword from stack
ldrsh_stack,
/// Load Register Signed Word (immediate)
ldrsw_immediate,
/// Logical Shift Left (immediate)
lsl_immediate,
/// Logical Shift Left (register)

View File

@ -665,18 +665,24 @@ pub const Instruction = union(enum) {
/// Which kind of load/store to perform
const LoadStoreVariant = enum {
/// 32-bit or 64-bit
/// 32 bits or 64 bits
str,
/// 16-bit, zero-extended
strh,
/// 8-bit, zero-extended
/// 8 bits, zero-extended
strb,
/// 32-bit or 64-bit
/// 16 bits, zero-extended
strh,
/// 32 bits or 64 bits
ldr,
/// 16-bit, zero-extended
ldrh,
/// 8-bit, zero-extended
/// 8 bits, zero-extended
ldrb,
/// 16 bits, zero-extended
ldrh,
/// 8 bits, sign extended
ldrsb,
/// 16 bits, sign extended
ldrsh,
/// 32 bits, sign extended
ldrsw,
};
fn loadStoreRegister(
@ -689,6 +695,7 @@ pub const Instruction = union(enum) {
assert(rn.id() != Register.xzr.id());
const off = offset.toU12();
const op1: u2 = blk: {
switch (offset) {
.immediate => |imm| switch (imm) {
@ -699,10 +706,35 @@ pub const Instruction = union(enum) {
}
break :blk 0b00;
};
const opc: u2 = switch (variant) {
.ldr, .ldrh, .ldrb => 0b01,
.str, .strh, .strb => 0b00,
const opc: u2 = blk: {
switch (variant) {
.ldr, .ldrh, .ldrb => break :blk 0b01,
.str, .strh, .strb => break :blk 0b00,
.ldrsb,
.ldrsh,
=> switch (rt.size()) {
32 => break :blk 0b11,
64 => break :blk 0b10,
else => unreachable, // unexpected register size
},
.ldrsw => break :blk 0b10,
}
};
const size: u2 = blk: {
switch (variant) {
.ldr, .str => switch (rt.size()) {
32 => break :blk 0b10,
64 => break :blk 0b11,
else => unreachable, // unexpected register size
},
.ldrsw => break :blk 0b10,
.ldrh, .ldrsh, .strh => break :blk 0b01,
.ldrb, .ldrsb, .strb => break :blk 0b00,
}
};
return Instruction{
.load_store_register = .{
.rt = rt.enc(),
@ -711,17 +743,7 @@ pub const Instruction = union(enum) {
.opc = opc,
.op1 = op1,
.v = 0,
.size = blk: {
switch (variant) {
.ldr, .str => switch (rt.size()) {
32 => break :blk 0b10,
64 => break :blk 0b11,
else => unreachable, // unexpected register size
},
.ldrh, .strh => break :blk 0b01,
.ldrb, .strb => break :blk 0b00,
}
},
.size = size,
},
};
}
@ -1150,6 +1172,18 @@ pub const Instruction = union(enum) {
return loadStoreRegister(rt, rn, offset, .ldrb);
}
pub fn ldrsb(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
return loadStoreRegister(rt, rn, offset, .ldrsb);
}
pub fn ldrsh(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
return loadStoreRegister(rt, rn, offset, .ldrsh);
}
pub fn ldrsw(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
return loadStoreRegister(rt, rn, offset, .ldrsw);
}
pub fn str(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
return loadStoreRegister(rt, rn, offset, .str);
}

View File

@ -23,8 +23,6 @@ fn testTruncate(x: u32) u8 {
}
test "truncate to non-power-of-two integers" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
try testTrunc(u32, u1, 0b10101, 0b1);
try testTrunc(u32, u1, 0b10110, 0b0);
try testTrunc(u32, u2, 0b10101, 0b01);

View File

@ -49,8 +49,6 @@ test "truncate.i0.var" {
}
test "truncate on comptime integer" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
var x = @truncate(u16, 9999);
try expect(x == 9999);
var y = @truncate(u16, -21555);