mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
Merge pull request #11462 from joachimschmidt557/stage2-aarch64
stage2 AArch64: truncation support
This commit is contained in:
commit
1a1b5ee264
7 changed files with 619 additions and 372 deletions
|
|
@ -376,7 +376,7 @@ fn gen(self: *Self) !void {
|
||||||
// mov fp, sp
|
// mov fp, sp
|
||||||
_ = try self.addInst(.{
|
_ = try self.addInst(.{
|
||||||
.tag = .mov_to_from_sp,
|
.tag = .mov_to_from_sp,
|
||||||
.data = .{ .rr = .{ .rd = .x29, .rn = .xzr } },
|
.data = .{ .rr = .{ .rd = .x29, .rn = .sp } },
|
||||||
});
|
});
|
||||||
|
|
||||||
// sub sp, sp, #reloc
|
// sub sp, sp, #reloc
|
||||||
|
|
@ -421,7 +421,7 @@ fn gen(self: *Self) !void {
|
||||||
if (math.cast(u12, stack_size)) |size| {
|
if (math.cast(u12, stack_size)) |size| {
|
||||||
self.mir_instructions.set(backpatch_reloc, .{
|
self.mir_instructions.set(backpatch_reloc, .{
|
||||||
.tag = .sub_immediate,
|
.tag = .sub_immediate,
|
||||||
.data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = size } },
|
.data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = size } },
|
||||||
});
|
});
|
||||||
} else |_| {
|
} else |_| {
|
||||||
return self.failSymbol("TODO AArch64: allow larger stacks", .{});
|
return self.failSymbol("TODO AArch64: allow larger stacks", .{});
|
||||||
|
|
@ -453,7 +453,7 @@ fn gen(self: *Self) !void {
|
||||||
// add sp, sp, #stack_size
|
// add sp, sp, #stack_size
|
||||||
_ = try self.addInst(.{
|
_ = try self.addInst(.{
|
||||||
.tag = .add_immediate,
|
.tag = .add_immediate,
|
||||||
.data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = @intCast(u12, stack_size) } },
|
.data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = @intCast(u12, stack_size) } },
|
||||||
});
|
});
|
||||||
|
|
||||||
// <load other registers>
|
// <load other registers>
|
||||||
|
|
@ -512,13 +512,13 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||||
switch (air_tags[inst]) {
|
switch (air_tags[inst]) {
|
||||||
// zig fmt: off
|
// zig fmt: off
|
||||||
.add, .ptr_add => try self.airBinOp(inst),
|
.add, .ptr_add => try self.airBinOp(inst),
|
||||||
.addwrap => try self.airAddWrap(inst),
|
.addwrap => try self.airBinOp(inst),
|
||||||
.add_sat => try self.airAddSat(inst),
|
.add_sat => try self.airAddSat(inst),
|
||||||
.sub, .ptr_sub => try self.airBinOp(inst),
|
.sub, .ptr_sub => try self.airBinOp(inst),
|
||||||
.subwrap => try self.airSubWrap(inst),
|
.subwrap => try self.airBinOp(inst),
|
||||||
.sub_sat => try self.airSubSat(inst),
|
.sub_sat => try self.airSubSat(inst),
|
||||||
.mul => try self.airBinOp(inst),
|
.mul => try self.airBinOp(inst),
|
||||||
.mulwrap => try self.airMulWrap(inst),
|
.mulwrap => try self.airBinOp(inst),
|
||||||
.mul_sat => try self.airMulSat(inst),
|
.mul_sat => try self.airMulSat(inst),
|
||||||
.rem => try self.airRem(inst),
|
.rem => try self.airRem(inst),
|
||||||
.mod => try self.airMod(inst),
|
.mod => try self.airMod(inst),
|
||||||
|
|
@ -882,7 +882,8 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
|
||||||
/// allocated. A second call to `copyToTmpRegister` may return the same register.
|
/// allocated. A second call to `copyToTmpRegister` may return the same register.
|
||||||
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
||||||
fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
|
fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
|
||||||
const reg = try self.register_manager.allocReg(null);
|
const raw_reg = try self.register_manager.allocReg(null);
|
||||||
|
const reg = registerAlias(raw_reg, ty.abiSize(self.target.*));
|
||||||
try self.genSetReg(ty, reg, mcv);
|
try self.genSetReg(ty, reg, mcv);
|
||||||
return reg;
|
return reg;
|
||||||
}
|
}
|
||||||
|
|
@ -891,7 +892,9 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
|
||||||
/// `reg_owner` is the instruction that gets associated with the register in the register table.
|
/// `reg_owner` is the instruction that gets associated with the register in the register table.
|
||||||
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
||||||
fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
|
fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
|
||||||
const reg = try self.register_manager.allocReg(reg_owner);
|
const raw_reg = try self.register_manager.allocReg(reg_owner);
|
||||||
|
const ty = self.air.typeOfIndex(reg_owner);
|
||||||
|
const reg = registerAlias(raw_reg, ty.abiSize(self.target.*));
|
||||||
try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
|
try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
|
||||||
return MCValue{ .register = reg };
|
return MCValue{ .register = reg };
|
||||||
}
|
}
|
||||||
|
|
@ -936,14 +939,99 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});
|
return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn truncRegister(
|
||||||
|
self: *Self,
|
||||||
|
operand_reg: Register,
|
||||||
|
dest_reg: Register,
|
||||||
|
int_signedness: std.builtin.Signedness,
|
||||||
|
int_bits: u16,
|
||||||
|
) !void {
|
||||||
|
switch (int_bits) {
|
||||||
|
1...31, 33...63 => {
|
||||||
|
_ = try self.addInst(.{
|
||||||
|
.tag = switch (int_signedness) {
|
||||||
|
.signed => .sbfx,
|
||||||
|
.unsigned => .ubfx,
|
||||||
|
},
|
||||||
|
.data = .{ .rr_lsb_width = .{
|
||||||
|
.rd = dest_reg,
|
||||||
|
.rn = operand_reg,
|
||||||
|
.lsb = 0,
|
||||||
|
.width = @intCast(u6, int_bits),
|
||||||
|
} },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
32, 64 => {
|
||||||
|
_ = try self.addInst(.{
|
||||||
|
.tag = .mov_register,
|
||||||
|
.data = .{ .rr = .{
|
||||||
|
.rd = dest_reg,
|
||||||
|
.rn = operand_reg,
|
||||||
|
} },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trunc(
|
||||||
|
self: *Self,
|
||||||
|
maybe_inst: ?Air.Inst.Index,
|
||||||
|
operand: MCValue,
|
||||||
|
operand_ty: Type,
|
||||||
|
dest_ty: Type,
|
||||||
|
) !MCValue {
|
||||||
|
const info_a = operand_ty.intInfo(self.target.*);
|
||||||
|
const info_b = dest_ty.intInfo(self.target.*);
|
||||||
|
|
||||||
|
if (info_b.bits <= 64) {
|
||||||
|
const operand_reg = switch (operand) {
|
||||||
|
.register => |r| r,
|
||||||
|
else => operand_reg: {
|
||||||
|
if (info_a.bits <= 64) {
|
||||||
|
const raw_reg = try self.copyToTmpRegister(operand_ty, operand);
|
||||||
|
break :operand_reg registerAlias(raw_reg, operand_ty.abiSize(self.target.*));
|
||||||
|
} else {
|
||||||
|
return self.fail("TODO load least significant word into register", .{});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.register_manager.freezeRegs(&.{operand_reg});
|
||||||
|
defer self.register_manager.unfreezeRegs(&.{operand_reg});
|
||||||
|
|
||||||
|
const dest_reg = if (maybe_inst) |inst| blk: {
|
||||||
|
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||||
|
|
||||||
|
if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
|
||||||
|
break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*));
|
||||||
|
} else {
|
||||||
|
const raw_reg = try self.register_manager.allocReg(inst);
|
||||||
|
break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*));
|
||||||
|
}
|
||||||
|
} else blk: {
|
||||||
|
const raw_reg = try self.register_manager.allocReg(null);
|
||||||
|
break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*));
|
||||||
|
};
|
||||||
|
|
||||||
|
try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits);
|
||||||
|
|
||||||
|
return MCValue{ .register = dest_reg };
|
||||||
|
} else {
|
||||||
|
return self.fail("TODO: truncate to ints > 32 bits", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
|
fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||||
if (self.liveness.isUnused(inst))
|
|
||||||
return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
|
|
||||||
|
|
||||||
const operand = try self.resolveInst(ty_op.operand);
|
const operand = try self.resolveInst(ty_op.operand);
|
||||||
_ = operand;
|
const operand_ty = self.air.typeOf(ty_op.operand);
|
||||||
return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch});
|
const dest_ty = self.air.typeOfIndex(inst);
|
||||||
|
|
||||||
|
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
|
||||||
|
break :blk try self.trunc(inst, operand, operand_ty, dest_ty);
|
||||||
|
};
|
||||||
|
|
||||||
|
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
|
fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
|
|
@ -1003,7 +1091,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
break :blk op_reg;
|
break :blk op_reg;
|
||||||
}
|
}
|
||||||
|
|
||||||
break :blk try self.register_manager.allocReg(null);
|
const raw_reg = try self.register_manager.allocReg(null);
|
||||||
|
break :blk raw_reg.to32();
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = try self.addInst(.{
|
_ = try self.addInst(.{
|
||||||
|
|
@ -1013,7 +1102,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
.rn = op_reg,
|
.rn = op_reg,
|
||||||
.imms = 0b000000,
|
.imms = 0b000000,
|
||||||
.immr = 0b000000,
|
.immr = 0b000000,
|
||||||
.n = 0b1,
|
.n = 0b0,
|
||||||
} },
|
} },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1035,7 +1124,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
break :blk op_reg;
|
break :blk op_reg;
|
||||||
}
|
}
|
||||||
|
|
||||||
break :blk try self.register_manager.allocReg(null);
|
const raw_reg = try self.register_manager.allocReg(null);
|
||||||
|
break :blk registerAlias(raw_reg, operand_ty.abiSize(self.target.*));
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = try self.addInst(.{
|
_ = try self.addInst(.{
|
||||||
|
|
@ -1048,6 +1138,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
} },
|
} },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
|
||||||
|
|
||||||
break :result MCValue{ .register = dest_reg };
|
break :result MCValue{ .register = dest_reg };
|
||||||
} else {
|
} else {
|
||||||
return self.fail("TODO AArch64 not on integers > u64/i64", .{});
|
return self.fail("TODO AArch64 not on integers > u64/i64", .{});
|
||||||
|
|
@ -1103,7 +1195,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
/// Asserts that generating an instruction of that form is possible.
|
/// Asserts that generating an instruction of that form is possible.
|
||||||
fn binOpRegister(
|
fn binOpRegister(
|
||||||
self: *Self,
|
self: *Self,
|
||||||
tag: Air.Inst.Tag,
|
mir_tag: Mir.Inst.Tag,
|
||||||
maybe_inst: ?Air.Inst.Index,
|
maybe_inst: ?Air.Inst.Index,
|
||||||
lhs: MCValue,
|
lhs: MCValue,
|
||||||
rhs: MCValue,
|
rhs: MCValue,
|
||||||
|
|
@ -1124,7 +1216,8 @@ fn binOpRegister(
|
||||||
break :inst Air.refToIndex(bin_op.lhs).?;
|
break :inst Air.refToIndex(bin_op.lhs).?;
|
||||||
} else null;
|
} else null;
|
||||||
|
|
||||||
const reg = try self.register_manager.allocReg(track_inst);
|
const raw_reg = try self.register_manager.allocReg(track_inst);
|
||||||
|
const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
|
||||||
self.register_manager.freezeRegs(&.{reg});
|
self.register_manager.freezeRegs(&.{reg});
|
||||||
|
|
||||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||||
|
|
@ -1139,7 +1232,8 @@ fn binOpRegister(
|
||||||
break :inst Air.refToIndex(bin_op.rhs).?;
|
break :inst Air.refToIndex(bin_op.rhs).?;
|
||||||
} else null;
|
} else null;
|
||||||
|
|
||||||
const reg = try self.register_manager.allocReg(track_inst);
|
const raw_reg = try self.register_manager.allocReg(track_inst);
|
||||||
|
const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*));
|
||||||
self.register_manager.freezeRegs(&.{reg});
|
self.register_manager.freezeRegs(&.{reg});
|
||||||
|
|
||||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||||
|
|
@ -1156,45 +1250,17 @@ fn binOpRegister(
|
||||||
} else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
|
} else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
|
||||||
break :blk rhs_reg;
|
break :blk rhs_reg;
|
||||||
} else {
|
} else {
|
||||||
break :blk try self.register_manager.allocReg(inst);
|
const raw_reg = try self.register_manager.allocReg(inst);
|
||||||
|
break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
|
||||||
}
|
}
|
||||||
} else try self.register_manager.allocReg(null);
|
} else try self.register_manager.allocReg(null);
|
||||||
|
|
||||||
if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
|
if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
|
||||||
if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
|
if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
|
||||||
|
|
||||||
const mir_tag: Mir.Inst.Tag = switch (tag) {
|
const mir_data: Mir.Inst.Data = switch (mir_tag) {
|
||||||
.add,
|
.add_shifted_register,
|
||||||
.ptr_add,
|
.sub_shifted_register,
|
||||||
=> .add_shifted_register,
|
|
||||||
.sub,
|
|
||||||
.ptr_sub,
|
|
||||||
=> .sub_shifted_register,
|
|
||||||
.cmp_eq => .cmp_shifted_register,
|
|
||||||
.mul => .mul,
|
|
||||||
.bit_and,
|
|
||||||
.bool_and,
|
|
||||||
=> .and_shifted_register,
|
|
||||||
.bit_or,
|
|
||||||
.bool_or,
|
|
||||||
=> .orr_shifted_register,
|
|
||||||
.shl,
|
|
||||||
.shl_exact,
|
|
||||||
=> .lsl_register,
|
|
||||||
.shr,
|
|
||||||
.shr_exact,
|
|
||||||
=> switch (lhs_ty.intInfo(self.target.*).signedness) {
|
|
||||||
.signed => Mir.Inst.Tag.asr_register,
|
|
||||||
.unsigned => Mir.Inst.Tag.lsr_register,
|
|
||||||
},
|
|
||||||
.xor => .eor_shifted_register,
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
const mir_data: Mir.Inst.Data = switch (tag) {
|
|
||||||
.add,
|
|
||||||
.sub,
|
|
||||||
.ptr_add,
|
|
||||||
.ptr_sub,
|
|
||||||
=> .{ .rrr_imm6_shift = .{
|
=> .{ .rrr_imm6_shift = .{
|
||||||
.rd = dest_reg,
|
.rd = dest_reg,
|
||||||
.rn = lhs_reg,
|
.rn = lhs_reg,
|
||||||
|
|
@ -1202,27 +1268,24 @@ fn binOpRegister(
|
||||||
.imm6 = 0,
|
.imm6 = 0,
|
||||||
.shift = .lsl,
|
.shift = .lsl,
|
||||||
} },
|
} },
|
||||||
.cmp_eq => .{ .rr_imm6_shift = .{
|
.cmp_shifted_register => .{ .rr_imm6_shift = .{
|
||||||
.rn = lhs_reg,
|
.rn = lhs_reg,
|
||||||
.rm = rhs_reg,
|
.rm = rhs_reg,
|
||||||
.imm6 = 0,
|
.imm6 = 0,
|
||||||
.shift = .lsl,
|
.shift = .lsl,
|
||||||
} },
|
} },
|
||||||
.mul,
|
.mul,
|
||||||
.shl,
|
.lsl_register,
|
||||||
.shl_exact,
|
.asr_register,
|
||||||
.shr,
|
.lsr_register,
|
||||||
.shr_exact,
|
|
||||||
=> .{ .rrr = .{
|
=> .{ .rrr = .{
|
||||||
.rd = dest_reg,
|
.rd = dest_reg,
|
||||||
.rn = lhs_reg,
|
.rn = lhs_reg,
|
||||||
.rm = rhs_reg,
|
.rm = rhs_reg,
|
||||||
} },
|
} },
|
||||||
.bit_and,
|
.and_shifted_register,
|
||||||
.bool_and,
|
.orr_shifted_register,
|
||||||
.bit_or,
|
.eor_shifted_register,
|
||||||
.bool_or,
|
|
||||||
.xor,
|
|
||||||
=> .{ .rrr_imm6_logical_shift = .{
|
=> .{ .rrr_imm6_logical_shift = .{
|
||||||
.rd = dest_reg,
|
.rd = dest_reg,
|
||||||
.rn = lhs_reg,
|
.rn = lhs_reg,
|
||||||
|
|
@ -1255,7 +1318,7 @@ fn binOpRegister(
|
||||||
/// Asserts that generating an instruction of that form is possible.
|
/// Asserts that generating an instruction of that form is possible.
|
||||||
fn binOpImmediate(
|
fn binOpImmediate(
|
||||||
self: *Self,
|
self: *Self,
|
||||||
tag: Air.Inst.Tag,
|
mir_tag: Mir.Inst.Tag,
|
||||||
maybe_inst: ?Air.Inst.Index,
|
maybe_inst: ?Air.Inst.Index,
|
||||||
lhs: MCValue,
|
lhs: MCValue,
|
||||||
rhs: MCValue,
|
rhs: MCValue,
|
||||||
|
|
@ -1276,7 +1339,8 @@ fn binOpImmediate(
|
||||||
).?;
|
).?;
|
||||||
} else null;
|
} else null;
|
||||||
|
|
||||||
const reg = try self.register_manager.allocReg(track_inst);
|
const raw_reg = try self.register_manager.allocReg(track_inst);
|
||||||
|
const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
|
||||||
self.register_manager.freezeRegs(&.{reg});
|
self.register_manager.freezeRegs(&.{reg});
|
||||||
|
|
||||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||||
|
|
@ -1285,8 +1349,8 @@ fn binOpImmediate(
|
||||||
};
|
};
|
||||||
defer self.register_manager.unfreezeRegs(&.{lhs_reg});
|
defer self.register_manager.unfreezeRegs(&.{lhs_reg});
|
||||||
|
|
||||||
const dest_reg = switch (tag) {
|
const dest_reg = switch (mir_tag) {
|
||||||
.cmp_eq => undefined, // cmp has no destination register
|
.cmp_immediate => undefined, // cmp has no destination register
|
||||||
else => if (maybe_inst) |inst| blk: {
|
else => if (maybe_inst) |inst| blk: {
|
||||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||||
|
|
||||||
|
|
@ -1298,46 +1362,31 @@ fn binOpImmediate(
|
||||||
)) {
|
)) {
|
||||||
break :blk lhs_reg;
|
break :blk lhs_reg;
|
||||||
} else {
|
} else {
|
||||||
break :blk try self.register_manager.allocReg(inst);
|
const raw_reg = try self.register_manager.allocReg(inst);
|
||||||
|
break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
|
||||||
}
|
}
|
||||||
} else try self.register_manager.allocReg(null),
|
} else try self.register_manager.allocReg(null),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
|
if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
|
||||||
|
|
||||||
const mir_tag: Mir.Inst.Tag = switch (tag) {
|
const mir_data: Mir.Inst.Data = switch (mir_tag) {
|
||||||
.add => .add_immediate,
|
.add_immediate,
|
||||||
.sub => .sub_immediate,
|
.sub_immediate,
|
||||||
.shl,
|
|
||||||
.shl_exact,
|
|
||||||
=> .lsl_immediate,
|
|
||||||
.shr,
|
|
||||||
.shr_exact,
|
|
||||||
=> switch (lhs_ty.intInfo(self.target.*).signedness) {
|
|
||||||
.signed => Mir.Inst.Tag.asr_immediate,
|
|
||||||
.unsigned => Mir.Inst.Tag.lsr_immediate,
|
|
||||||
},
|
|
||||||
.cmp_eq => .cmp_immediate,
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
const mir_data: Mir.Inst.Data = switch (tag) {
|
|
||||||
.add,
|
|
||||||
.sub,
|
|
||||||
=> .{ .rr_imm12_sh = .{
|
=> .{ .rr_imm12_sh = .{
|
||||||
.rd = dest_reg,
|
.rd = dest_reg,
|
||||||
.rn = lhs_reg,
|
.rn = lhs_reg,
|
||||||
.imm12 = @intCast(u12, rhs.immediate),
|
.imm12 = @intCast(u12, rhs.immediate),
|
||||||
} },
|
} },
|
||||||
.shl,
|
.lsl_immediate,
|
||||||
.shl_exact,
|
.asr_immediate,
|
||||||
.shr,
|
.lsr_immediate,
|
||||||
.shr_exact,
|
|
||||||
=> .{ .rr_shift = .{
|
=> .{ .rr_shift = .{
|
||||||
.rd = dest_reg,
|
.rd = dest_reg,
|
||||||
.rn = lhs_reg,
|
.rn = lhs_reg,
|
||||||
.shift = @intCast(u6, rhs.immediate),
|
.shift = @intCast(u6, rhs.immediate),
|
||||||
} },
|
} },
|
||||||
.cmp_eq => .{ .r_imm12_sh = .{
|
.cmp_immediate => .{ .r_imm12_sh = .{
|
||||||
.rn = lhs_reg,
|
.rn = lhs_reg,
|
||||||
.imm12 = @intCast(u12, rhs.immediate),
|
.imm12 = @intCast(u12, rhs.immediate),
|
||||||
} },
|
} },
|
||||||
|
|
@ -1375,7 +1424,6 @@ fn binOp(
|
||||||
) InnerError!MCValue {
|
) InnerError!MCValue {
|
||||||
const target = self.target.*;
|
const target = self.target.*;
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
// Arithmetic operations on integers and floats
|
|
||||||
.add,
|
.add,
|
||||||
.sub,
|
.sub,
|
||||||
.cmp_eq,
|
.cmp_eq,
|
||||||
|
|
@ -1403,13 +1451,26 @@ fn binOp(
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mir_tag_register: Mir.Inst.Tag = switch (tag) {
|
||||||
|
.add => .add_shifted_register,
|
||||||
|
.sub => .sub_shifted_register,
|
||||||
|
.cmp_eq => .cmp_shifted_register,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
const mir_tag_immediate: Mir.Inst.Tag = switch (tag) {
|
||||||
|
.add => .add_immediate,
|
||||||
|
.sub => .sub_immediate,
|
||||||
|
.cmp_eq => .cmp_immediate,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
if (rhs_immediate_ok) {
|
if (rhs_immediate_ok) {
|
||||||
return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
|
return try self.binOpImmediate(mir_tag_immediate, maybe_inst, lhs, rhs, lhs_ty, false);
|
||||||
} else if (lhs_immediate_ok) {
|
} else if (lhs_immediate_ok) {
|
||||||
// swap lhs and rhs
|
// swap lhs and rhs
|
||||||
return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true);
|
return try self.binOpImmediate(mir_tag_immediate, maybe_inst, rhs, lhs, rhs_ty, true);
|
||||||
} else {
|
} else {
|
||||||
return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return self.fail("TODO binary operations on int with bits > 64", .{});
|
return self.fail("TODO binary operations on int with bits > 64", .{});
|
||||||
|
|
@ -1428,7 +1489,7 @@ fn binOp(
|
||||||
// TODO add optimisations for multiplication
|
// TODO add optimisations for multiplication
|
||||||
// with immediates, for example a * 2 can be
|
// with immediates, for example a * 2 can be
|
||||||
// lowered to a << 1
|
// lowered to a << 1
|
||||||
return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
return try self.binOpRegister(.mul, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
||||||
} else {
|
} else {
|
||||||
return self.fail("TODO binary operations on int with bits > 64", .{});
|
return self.fail("TODO binary operations on int with bits > 64", .{});
|
||||||
}
|
}
|
||||||
|
|
@ -1436,7 +1497,36 @@ fn binOp(
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Bitwise operations on integers
|
.addwrap,
|
||||||
|
.subwrap,
|
||||||
|
.mulwrap,
|
||||||
|
=> {
|
||||||
|
const base_tag: Air.Inst.Tag = switch (tag) {
|
||||||
|
.addwrap => .add,
|
||||||
|
.subwrap => .sub,
|
||||||
|
.mulwrap => .mul,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate an add/sub/mul
|
||||||
|
const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
||||||
|
|
||||||
|
// Truncate if necessary
|
||||||
|
switch (lhs_ty.zigTypeTag()) {
|
||||||
|
.Vector => return self.fail("TODO binary operations on vectors", .{}),
|
||||||
|
.Int => {
|
||||||
|
const int_info = lhs_ty.intInfo(self.target.*);
|
||||||
|
if (int_info.bits <= 64) {
|
||||||
|
const result_reg = result.register;
|
||||||
|
try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return self.fail("TODO binary operations on integers > u64/i64", .{});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
},
|
||||||
.bit_and,
|
.bit_and,
|
||||||
.bit_or,
|
.bit_or,
|
||||||
.xor,
|
.xor,
|
||||||
|
|
@ -1448,7 +1538,53 @@ fn binOp(
|
||||||
const int_info = lhs_ty.intInfo(self.target.*);
|
const int_info = lhs_ty.intInfo(self.target.*);
|
||||||
if (int_info.bits <= 64) {
|
if (int_info.bits <= 64) {
|
||||||
// TODO implement bitwise operations with immediates
|
// TODO implement bitwise operations with immediates
|
||||||
return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
const mir_tag: Mir.Inst.Tag = switch (tag) {
|
||||||
|
.bit_and => .and_shifted_register,
|
||||||
|
.bit_or => .orr_shifted_register,
|
||||||
|
.xor => .eor_shifted_register,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
||||||
|
} else {
|
||||||
|
return self.fail("TODO binary operations on int with bits > 64", .{});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.shl_exact,
|
||||||
|
.shr_exact,
|
||||||
|
=> {
|
||||||
|
switch (lhs_ty.zigTypeTag()) {
|
||||||
|
.Vector => return self.fail("TODO binary operations on vectors", .{}),
|
||||||
|
.Int => {
|
||||||
|
const int_info = lhs_ty.intInfo(self.target.*);
|
||||||
|
if (int_info.bits <= 64) {
|
||||||
|
const rhs_immediate_ok = rhs == .immediate;
|
||||||
|
|
||||||
|
const mir_tag_register: Mir.Inst.Tag = switch (tag) {
|
||||||
|
.shl_exact => .lsl_register,
|
||||||
|
.shr_exact => switch (int_info.signedness) {
|
||||||
|
.signed => Mir.Inst.Tag.asr_register,
|
||||||
|
.unsigned => Mir.Inst.Tag.lsr_register,
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
const mir_tag_immediate: Mir.Inst.Tag = switch (tag) {
|
||||||
|
.shl_exact => .lsl_immediate,
|
||||||
|
.shr_exact => switch (int_info.signedness) {
|
||||||
|
.signed => Mir.Inst.Tag.asr_immediate,
|
||||||
|
.unsigned => Mir.Inst.Tag.lsr_immediate,
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (rhs_immediate_ok) {
|
||||||
|
return try self.binOpImmediate(mir_tag_immediate, maybe_inst, lhs, rhs, lhs_ty, false);
|
||||||
|
} else {
|
||||||
|
return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return self.fail("TODO binary operations on int with bits > 64", .{});
|
return self.fail("TODO binary operations on int with bits > 64", .{});
|
||||||
}
|
}
|
||||||
|
|
@ -1459,21 +1595,31 @@ fn binOp(
|
||||||
.shl,
|
.shl,
|
||||||
.shr,
|
.shr,
|
||||||
=> {
|
=> {
|
||||||
switch (lhs_ty.zigTypeTag()) {
|
const base_tag: Air.Inst.Tag = switch (tag) {
|
||||||
.Vector => return self.fail("TODO binary operations on vectors", .{}),
|
.shl => .shl_exact,
|
||||||
.Int => {
|
.shr => .shr_exact,
|
||||||
const int_info = lhs_ty.intInfo(self.target.*);
|
else => unreachable,
|
||||||
if (int_info.bits <= 64) {
|
};
|
||||||
const rhs_immediate_ok = rhs == .immediate;
|
|
||||||
|
|
||||||
if (rhs_immediate_ok) {
|
// Generate a shl_exact/shr_exact
|
||||||
return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false);
|
const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
||||||
|
|
||||||
|
// Truncate if necessary
|
||||||
|
switch (tag) {
|
||||||
|
.shr => return result,
|
||||||
|
.shl => switch (lhs_ty.zigTypeTag()) {
|
||||||
|
.Vector => return self.fail("TODO binary operations on vectors", .{}),
|
||||||
|
.Int => {
|
||||||
|
const int_info = lhs_ty.intInfo(self.target.*);
|
||||||
|
if (int_info.bits <= 64) {
|
||||||
|
const result_reg = result.register;
|
||||||
|
try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
|
||||||
|
return result;
|
||||||
} else {
|
} else {
|
||||||
return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
return self.fail("TODO binary operations on integers > u64/i64", .{});
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
return self.fail("TODO binary operations on int with bits > 64", .{});
|
else => unreachable,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
|
|
@ -1486,7 +1632,13 @@ fn binOp(
|
||||||
assert(lhs != .immediate); // should have been handled by Sema
|
assert(lhs != .immediate); // should have been handled by Sema
|
||||||
assert(rhs != .immediate); // should have been handled by Sema
|
assert(rhs != .immediate); // should have been handled by Sema
|
||||||
|
|
||||||
return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
const mir_tag_register: Mir.Inst.Tag = switch (tag) {
|
||||||
|
.bool_and => .and_shifted_register,
|
||||||
|
.bool_or => .orr_shifted_register,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
|
|
@ -1504,9 +1656,9 @@ fn binOp(
|
||||||
const elem_size = elem_ty.abiSize(self.target.*);
|
const elem_size = elem_ty.abiSize(self.target.*);
|
||||||
|
|
||||||
if (elem_size == 1) {
|
if (elem_size == 1) {
|
||||||
const base_tag: Air.Inst.Tag = switch (tag) {
|
const base_tag: Mir.Inst.Tag = switch (tag) {
|
||||||
.ptr_add => .add,
|
.ptr_add => .add_shifted_register,
|
||||||
.ptr_sub => .sub,
|
.ptr_sub => .sub_shifted_register,
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1538,36 +1690,18 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void {
|
|
||||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
|
||||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch});
|
|
||||||
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
|
fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
|
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
|
||||||
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
|
|
||||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
|
||||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch});
|
|
||||||
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
|
fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
|
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
|
||||||
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {
|
|
||||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
|
||||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch});
|
|
||||||
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
|
fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
|
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
|
||||||
|
|
@ -1961,11 +2095,12 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
|
||||||
.undef => unreachable,
|
.undef => unreachable,
|
||||||
.compare_flags_signed, .compare_flags_unsigned => unreachable,
|
.compare_flags_signed, .compare_flags_unsigned => unreachable,
|
||||||
.register => |dst_reg| {
|
.register => |dst_reg| {
|
||||||
try self.genLdrRegister(dst_reg, addr_reg, elem_size);
|
try self.genLdrRegister(dst_reg, addr_reg, elem_ty);
|
||||||
},
|
},
|
||||||
.stack_offset => |off| {
|
.stack_offset => |off| {
|
||||||
if (elem_size <= 8) {
|
if (elem_size <= 8) {
|
||||||
const tmp_reg = try self.register_manager.allocReg(null);
|
const raw_tmp_reg = try self.register_manager.allocReg(null);
|
||||||
|
const tmp_reg = registerAlias(raw_tmp_reg, elem_size);
|
||||||
self.register_manager.freezeRegs(&.{tmp_reg});
|
self.register_manager.freezeRegs(&.{tmp_reg});
|
||||||
defer self.register_manager.unfreezeRegs(&.{tmp_reg});
|
defer self.register_manager.unfreezeRegs(&.{tmp_reg});
|
||||||
|
|
||||||
|
|
@ -2001,12 +2136,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
|
||||||
.got_load,
|
.got_load,
|
||||||
.direct_load,
|
.direct_load,
|
||||||
=> {
|
=> {
|
||||||
const reg = try self.register_manager.allocReg(null);
|
const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr);
|
||||||
self.register_manager.freezeRegs(&.{reg});
|
try self.load(dst_mcv, .{ .register = addr_reg }, ptr_ty);
|
||||||
defer self.register_manager.unfreezeRegs(&.{reg});
|
|
||||||
|
|
||||||
try self.genSetReg(ptr_ty, reg, ptr);
|
|
||||||
try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2091,6 +2222,7 @@ fn genInlineMemcpy(
|
||||||
fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
|
fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||||
const elem_ty = self.air.typeOfIndex(inst);
|
const elem_ty = self.air.typeOfIndex(inst);
|
||||||
|
const elem_size = elem_ty.abiSize(self.target.*);
|
||||||
const result: MCValue = result: {
|
const result: MCValue = result: {
|
||||||
if (!elem_ty.hasRuntimeBits())
|
if (!elem_ty.hasRuntimeBits())
|
||||||
break :result MCValue.none;
|
break :result MCValue.none;
|
||||||
|
|
@ -2101,9 +2233,12 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
break :result MCValue.dead;
|
break :result MCValue.dead;
|
||||||
|
|
||||||
const dst_mcv: MCValue = blk: {
|
const dst_mcv: MCValue = blk: {
|
||||||
if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
|
if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
|
||||||
// The MCValue that holds the pointer can be re-used as the value.
|
// The MCValue that holds the pointer can be re-used as the value.
|
||||||
break :blk ptr;
|
break :blk switch (ptr) {
|
||||||
|
.register => |r| MCValue{ .register = registerAlias(r, elem_size) },
|
||||||
|
else => ptr,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
break :blk try self.allocRegOrMem(inst, true);
|
break :blk try self.allocRegOrMem(inst, true);
|
||||||
}
|
}
|
||||||
|
|
@ -2114,101 +2249,52 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
|
||||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, abi_size: u64) !void {
|
fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void {
|
||||||
switch (abi_size) {
|
const abi_size = ty.abiSize(self.target.*);
|
||||||
1 => {
|
|
||||||
_ = try self.addInst(.{
|
const tag: Mir.Inst.Tag = switch (abi_size) {
|
||||||
.tag = .ldrb_immediate,
|
1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_immediate else .ldrb_immediate,
|
||||||
.data = .{ .load_store_register_immediate = .{
|
2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_immediate else .ldrh_immediate,
|
||||||
.rt = value_reg.to32(),
|
4 => .ldr_immediate,
|
||||||
.rn = addr_reg,
|
8 => .ldr_immediate,
|
||||||
.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", .{}),
|
3, 5, 6, 7 => return self.fail("TODO: genLdrRegister for more abi_sizes", .{}),
|
||||||
else => unreachable,
|
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 {
|
fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void {
|
||||||
switch (abi_size) {
|
const abi_size = ty.abiSize(self.target.*);
|
||||||
1 => {
|
|
||||||
_ = try self.addInst(.{
|
const tag: Mir.Inst.Tag = switch (abi_size) {
|
||||||
.tag = .strb_immediate,
|
1 => .strb_immediate,
|
||||||
.data = .{ .load_store_register_immediate = .{
|
2 => .strh_immediate,
|
||||||
.rt = value_reg.to32(),
|
4, 8 => .str_immediate,
|
||||||
.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", .{}),
|
3, 5, 6, 7 => return self.fail("TODO: genStrRegister for more abi_sizes", .{}),
|
||||||
else => unreachable,
|
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 {
|
fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
|
||||||
|
const abi_size = value_ty.abiSize(self.target.*);
|
||||||
|
|
||||||
switch (ptr) {
|
switch (ptr) {
|
||||||
.none => unreachable,
|
.none => unreachable,
|
||||||
.undef => unreachable,
|
.undef => unreachable,
|
||||||
|
|
@ -2226,14 +2312,14 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
|
||||||
self.register_manager.freezeRegs(&.{addr_reg});
|
self.register_manager.freezeRegs(&.{addr_reg});
|
||||||
defer self.register_manager.unfreezeRegs(&.{addr_reg});
|
defer self.register_manager.unfreezeRegs(&.{addr_reg});
|
||||||
|
|
||||||
const abi_size = value_ty.abiSize(self.target.*);
|
|
||||||
switch (value) {
|
switch (value) {
|
||||||
.register => |value_reg| {
|
.register => |value_reg| {
|
||||||
try self.genStrRegister(value_reg, addr_reg, abi_size);
|
try self.genStrRegister(value_reg, addr_reg, value_ty);
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
if (abi_size <= 8) {
|
if (abi_size <= 8) {
|
||||||
const tmp_reg = try self.register_manager.allocReg(null);
|
const raw_tmp_reg = try self.register_manager.allocReg(null);
|
||||||
|
const tmp_reg = registerAlias(raw_tmp_reg, abi_size);
|
||||||
self.register_manager.freezeRegs(&.{tmp_reg});
|
self.register_manager.freezeRegs(&.{tmp_reg});
|
||||||
defer self.register_manager.unfreezeRegs(&.{tmp_reg});
|
defer self.register_manager.unfreezeRegs(&.{tmp_reg});
|
||||||
|
|
||||||
|
|
@ -3470,23 +3556,26 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
|
||||||
.data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } },
|
.data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (x > math.maxInt(u16)) {
|
if (x & 0x0000_0000_ffff_0000 != 0) {
|
||||||
_ = try self.addInst(.{
|
_ = try self.addInst(.{
|
||||||
.tag = .movk,
|
.tag = .movk,
|
||||||
.data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } },
|
.data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (x > math.maxInt(u32)) {
|
|
||||||
_ = try self.addInst(.{
|
if (reg.size() == 64) {
|
||||||
.tag = .movk,
|
if (x & 0x0000_ffff_0000_0000 != 0) {
|
||||||
.data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } },
|
_ = try self.addInst(.{
|
||||||
});
|
.tag = .movk,
|
||||||
}
|
.data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } },
|
||||||
if (x > math.maxInt(u48)) {
|
});
|
||||||
_ = try self.addInst(.{
|
}
|
||||||
.tag = .movk,
|
if (x & 0xffff_0000_0000_0000 != 0) {
|
||||||
.data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } },
|
_ = try self.addInst(.{
|
||||||
});
|
.tag = .movk,
|
||||||
|
.data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } },
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.register => |src_reg| {
|
.register => |src_reg| {
|
||||||
|
|
@ -3522,8 +3611,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
|
||||||
.memory => |addr| {
|
.memory => |addr| {
|
||||||
// The value is in memory at a hard-coded address.
|
// 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.
|
// If the type is a pointer, it means the pointer address is at this memory location.
|
||||||
try self.genSetReg(ty, reg, .{ .immediate = addr });
|
try self.genSetReg(ty, reg.to64(), .{ .immediate = addr });
|
||||||
try self.genLdrRegister(reg, reg, ty.abiSize(self.target.*));
|
try self.genLdrRegister(reg, reg.to64(), ty);
|
||||||
},
|
},
|
||||||
.stack_offset => |off| {
|
.stack_offset => |off| {
|
||||||
const abi_size = ty.abiSize(self.target.*);
|
const abi_size = ty.abiSize(self.target.*);
|
||||||
|
|
@ -3531,21 +3620,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
|
||||||
switch (abi_size) {
|
switch (abi_size) {
|
||||||
1, 2, 4, 8 => {
|
1, 2, 4, 8 => {
|
||||||
const tag: Mir.Inst.Tag = switch (abi_size) {
|
const tag: Mir.Inst.Tag = switch (abi_size) {
|
||||||
1 => .ldrb_stack,
|
1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_stack else .ldrb_stack,
|
||||||
2 => .ldrh_stack,
|
2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_stack else .ldrh_stack,
|
||||||
4, 8 => .ldr_stack,
|
4, 8 => .ldr_stack,
|
||||||
else => unreachable, // unexpected abi size
|
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(.{
|
_ = try self.addInst(.{
|
||||||
.tag = tag,
|
.tag = tag,
|
||||||
.data = .{ .load_store_stack = .{
|
.data = .{ .load_store_stack = .{
|
||||||
.rt = rt,
|
.rt = reg,
|
||||||
.offset = @intCast(u32, off),
|
.offset = @intCast(u32, off),
|
||||||
} },
|
} },
|
||||||
});
|
});
|
||||||
|
|
@ -3998,6 +4082,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
|
||||||
var nsaa: u32 = 0; // Next stacked argument address
|
var nsaa: u32 = 0; // Next stacked argument address
|
||||||
|
|
||||||
for (param_types) |ty, i| {
|
for (param_types) |ty, i| {
|
||||||
|
const param_size = @intCast(u32, ty.abiSize(self.target.*));
|
||||||
|
if (param_size == 0) {
|
||||||
|
result.args[i] = .{ .none = {} };
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// We round up NCRN only for non-Apple platforms which allow the 16-byte aligned
|
// We round up NCRN only for non-Apple platforms which allow the 16-byte aligned
|
||||||
// values to spread across odd-numbered registers.
|
// values to spread across odd-numbered registers.
|
||||||
if (ty.abiAlignment(self.target.*) == 16 and !self.target.isDarwin()) {
|
if (ty.abiAlignment(self.target.*) == 16 and !self.target.isDarwin()) {
|
||||||
|
|
@ -4005,10 +4095,9 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
|
||||||
ncrn += ncrn % 2;
|
ncrn += ncrn % 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
const param_size = @intCast(u32, ty.abiSize(self.target.*));
|
|
||||||
if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) {
|
if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) {
|
||||||
if (param_size <= 8) {
|
if (param_size <= 8) {
|
||||||
result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
|
result.args[i] = .{ .register = registerAlias(c_abi_int_param_regs[ncrn], param_size) };
|
||||||
ncrn += 1;
|
ncrn += 1;
|
||||||
} else {
|
} else {
|
||||||
return self.fail("TODO MCValues with multiple registers", .{});
|
return self.fail("TODO MCValues with multiple registers", .{});
|
||||||
|
|
@ -4045,7 +4134,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
|
||||||
.Unspecified, .C => {
|
.Unspecified, .C => {
|
||||||
const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
|
const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
|
||||||
if (ret_ty_size <= 8) {
|
if (ret_ty_size <= 8) {
|
||||||
result.return_value = .{ .register = c_abi_int_return_regs[0] };
|
result.return_value = .{ .register = registerAlias(c_abi_int_return_regs[0], ret_ty_size) };
|
||||||
} else {
|
} else {
|
||||||
return self.fail("TODO support more return types for ARM backend", .{});
|
return self.fail("TODO support more return types for ARM backend", .{});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,8 @@ pub fn emitMir(
|
||||||
.ldr_stack => try emit.mirLoadStoreStack(inst),
|
.ldr_stack => try emit.mirLoadStoreStack(inst),
|
||||||
.ldrb_stack => try emit.mirLoadStoreStack(inst),
|
.ldrb_stack => try emit.mirLoadStoreStack(inst),
|
||||||
.ldrh_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),
|
.str_stack => try emit.mirLoadStoreStack(inst),
|
||||||
.strb_stack => try emit.mirLoadStoreStack(inst),
|
.strb_stack => try emit.mirLoadStoreStack(inst),
|
||||||
.strh_stack => try emit.mirLoadStoreStack(inst),
|
.strh_stack => try emit.mirLoadStoreStack(inst),
|
||||||
|
|
@ -145,6 +147,9 @@ pub fn emitMir(
|
||||||
.ldr_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
|
.ldr_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
|
||||||
.ldrb_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
|
.ldrb_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
|
||||||
.ldrh_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),
|
.str_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
|
||||||
.strb_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
|
.strb_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
|
||||||
.strh_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
|
.strh_immediate => try emit.mirLoadStoreRegisterImmediate(inst),
|
||||||
|
|
@ -162,6 +167,17 @@ pub fn emitMir(
|
||||||
|
|
||||||
.push_regs => try emit.mirPushPopRegs(inst),
|
.push_regs => try emit.mirPushPopRegs(inst),
|
||||||
.pop_regs => try emit.mirPushPopRegs(inst),
|
.pop_regs => try emit.mirPushPopRegs(inst),
|
||||||
|
|
||||||
|
.sbfx,
|
||||||
|
.ubfx,
|
||||||
|
=> try emit.mirBitfieldExtract(inst),
|
||||||
|
|
||||||
|
.sxtb,
|
||||||
|
.sxth,
|
||||||
|
.sxtw,
|
||||||
|
.uxtb,
|
||||||
|
.uxth,
|
||||||
|
=> try emit.mirExtend(inst),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -457,8 +473,13 @@ fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
const rn = r_imm12_sh.rn;
|
const rn = r_imm12_sh.rn;
|
||||||
const imm12 = r_imm12_sh.imm12;
|
const imm12 = r_imm12_sh.imm12;
|
||||||
const sh = r_imm12_sh.sh == 1;
|
const sh = r_imm12_sh.sh == 1;
|
||||||
|
const zr: Register = switch (rn.size()) {
|
||||||
|
32 => .wzr,
|
||||||
|
64 => .xzr,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
try emit.writeInstruction(Instruction.subs(.xzr, rn, imm12, sh));
|
try emit.writeInstruction(Instruction.subs(zr, rn, imm12, sh));
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
|
|
@ -674,8 +695,13 @@ fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
const rm = rr_imm6_shift.rm;
|
const rm = rr_imm6_shift.rm;
|
||||||
const shift = rr_imm6_shift.shift;
|
const shift = rr_imm6_shift.shift;
|
||||||
const imm6 = rr_imm6_shift.imm6;
|
const imm6 = rr_imm6_shift.imm6;
|
||||||
|
const zr: Register = switch (rn.size()) {
|
||||||
|
32 => .wzr,
|
||||||
|
64 => .xzr,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
try emit.writeInstruction(Instruction.subsShiftedRegister(.xzr, rn, rm, shift, imm6));
|
try emit.writeInstruction(Instruction.subsShiftedRegister(zr, rn, rm, shift, imm6));
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
|
|
@ -686,7 +712,12 @@ fn mirConditionalSelect(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
.cset => {
|
.cset => {
|
||||||
const r_cond = emit.mir.instructions.items(.data)[inst].r_cond;
|
const r_cond = emit.mir.instructions.items(.data)[inst].r_cond;
|
||||||
try emit.writeInstruction(Instruction.csinc(r_cond.rd, .xzr, .xzr, r_cond.cond));
|
const zr: Register = switch (r_cond.rd.size()) {
|
||||||
|
32 => .wzr,
|
||||||
|
64 => .xzr,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
try emit.writeInstruction(Instruction.csinc(r_cond.rd, zr, zr, r_cond.cond));
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
|
|
@ -718,14 +749,14 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
// PC-relative displacement to the entry in memory.
|
// PC-relative displacement to the entry in memory.
|
||||||
// adrp
|
// adrp
|
||||||
const offset = @intCast(u32, emit.code.items.len);
|
const offset = @intCast(u32, emit.code.items.len);
|
||||||
try emit.writeInstruction(Instruction.adrp(reg, 0));
|
try emit.writeInstruction(Instruction.adrp(reg.to64(), 0));
|
||||||
|
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
.load_memory_got => {
|
.load_memory_got => {
|
||||||
// ldr reg, reg, offset
|
// ldr reg, reg, offset
|
||||||
try emit.writeInstruction(Instruction.ldr(
|
try emit.writeInstruction(Instruction.ldr(
|
||||||
reg,
|
reg,
|
||||||
reg,
|
reg.to64(),
|
||||||
Instruction.LoadStoreOffset.imm(0),
|
Instruction.LoadStoreOffset.imm(0),
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
|
|
@ -739,11 +770,11 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
// Note that this can potentially be optimised out by the codegen/linker if the
|
// Note that this can potentially be optimised out by the codegen/linker if the
|
||||||
// target address is appropriately aligned.
|
// target address is appropriately aligned.
|
||||||
// add reg, reg, offset
|
// add reg, reg, offset
|
||||||
try emit.writeInstruction(Instruction.add(reg, reg, 0, false));
|
try emit.writeInstruction(Instruction.add(reg.to64(), reg.to64(), 0, false));
|
||||||
// ldr reg, reg, offset
|
// ldr reg, reg, offset
|
||||||
try emit.writeInstruction(Instruction.ldr(
|
try emit.writeInstruction(Instruction.ldr(
|
||||||
reg,
|
reg,
|
||||||
reg,
|
reg.to64(),
|
||||||
Instruction.LoadStoreOffset.imm(0),
|
Instruction.LoadStoreOffset.imm(0),
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
|
|
@ -821,14 +852,14 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
|
|
||||||
const raw_offset = emit.stack_size - load_store_stack.offset;
|
const raw_offset = emit.stack_size - load_store_stack.offset;
|
||||||
const offset = switch (tag) {
|
const offset = switch (tag) {
|
||||||
.ldrb_stack, .strb_stack => blk: {
|
.ldrb_stack, .ldrsb_stack, .strb_stack => blk: {
|
||||||
if (math.cast(u12, raw_offset)) |imm| {
|
if (math.cast(u12, raw_offset)) |imm| {
|
||||||
break :blk Instruction.LoadStoreOffset.imm(imm);
|
break :blk Instruction.LoadStoreOffset.imm(imm);
|
||||||
} else |_| {
|
} else |_| {
|
||||||
return emit.fail("TODO load/store stack byte with larger offset", .{});
|
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
|
assert(std.mem.isAlignedGeneric(u32, raw_offset, 2)); // misaligned stack entry
|
||||||
if (math.cast(u12, @divExact(raw_offset, 2))) |imm| {
|
if (math.cast(u12, @divExact(raw_offset, 2))) |imm| {
|
||||||
break :blk Instruction.LoadStoreOffset.imm(imm);
|
break :blk Instruction.LoadStoreOffset.imm(imm);
|
||||||
|
|
@ -857,6 +888,8 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
.ldr_stack => try emit.writeInstruction(Instruction.ldr(rt, .sp, offset)),
|
.ldr_stack => try emit.writeInstruction(Instruction.ldr(rt, .sp, offset)),
|
||||||
.ldrb_stack => try emit.writeInstruction(Instruction.ldrb(rt, .sp, offset)),
|
.ldrb_stack => try emit.writeInstruction(Instruction.ldrb(rt, .sp, offset)),
|
||||||
.ldrh_stack => try emit.writeInstruction(Instruction.ldrh(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)),
|
.str_stack => try emit.writeInstruction(Instruction.str(rt, .sp, offset)),
|
||||||
.strb_stack => try emit.writeInstruction(Instruction.strb(rt, .sp, offset)),
|
.strb_stack => try emit.writeInstruction(Instruction.strb(rt, .sp, offset)),
|
||||||
.strh_stack => try emit.writeInstruction(Instruction.strh(rt, .sp, offset)),
|
.strh_stack => try emit.writeInstruction(Instruction.strh(rt, .sp, offset)),
|
||||||
|
|
@ -875,6 +908,9 @@ fn mirLoadStoreRegisterImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
.ldr_immediate => try emit.writeInstruction(Instruction.ldr(rt, rn, offset)),
|
.ldr_immediate => try emit.writeInstruction(Instruction.ldr(rt, rn, offset)),
|
||||||
.ldrb_immediate => try emit.writeInstruction(Instruction.ldrb(rt, rn, offset)),
|
.ldrb_immediate => try emit.writeInstruction(Instruction.ldrb(rt, rn, offset)),
|
||||||
.ldrh_immediate => try emit.writeInstruction(Instruction.ldrh(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)),
|
.str_immediate => try emit.writeInstruction(Instruction.str(rt, rn, offset)),
|
||||||
.strb_immediate => try emit.writeInstruction(Instruction.strb(rt, rn, offset)),
|
.strb_immediate => try emit.writeInstruction(Instruction.strb(rt, rn, offset)),
|
||||||
.strh_immediate => try emit.writeInstruction(Instruction.strh(rt, rn, offset)),
|
.strh_immediate => try emit.writeInstruction(Instruction.strh(rt, rn, offset)),
|
||||||
|
|
@ -905,7 +941,13 @@ fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
.mov_register => {
|
.mov_register => {
|
||||||
const rr = emit.mir.instructions.items(.data)[inst].rr;
|
const rr = emit.mir.instructions.items(.data)[inst].rr;
|
||||||
try emit.writeInstruction(Instruction.orrShiftedRegister(rr.rd, .xzr, rr.rn, .lsl, 0));
|
const zr: Register = switch (rr.rd.size()) {
|
||||||
|
32 => .wzr,
|
||||||
|
64 => .xzr,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
try emit.writeInstruction(Instruction.orrShiftedRegister(rr.rd, zr, rr.rn, .lsl, 0));
|
||||||
},
|
},
|
||||||
.mov_to_from_sp => {
|
.mov_to_from_sp => {
|
||||||
const rr = emit.mir.instructions.items(.data)[inst].rr;
|
const rr = emit.mir.instructions.items(.data)[inst].rr;
|
||||||
|
|
@ -917,8 +959,13 @@ fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
const rm = rr_imm6_logical_shift.rm;
|
const rm = rr_imm6_logical_shift.rm;
|
||||||
const shift = rr_imm6_logical_shift.shift;
|
const shift = rr_imm6_logical_shift.shift;
|
||||||
const imm6 = rr_imm6_logical_shift.imm6;
|
const imm6 = rr_imm6_logical_shift.imm6;
|
||||||
|
const zr: Register = switch (rd.size()) {
|
||||||
|
32 => .wzr,
|
||||||
|
64 => .xzr,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
try emit.writeInstruction(Instruction.ornShiftedRegister(rd, .xzr, rm, shift, imm6));
|
try emit.writeInstruction(Instruction.ornShiftedRegister(rd, zr, rm, shift, imm6));
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
|
|
@ -1024,3 +1071,32 @@ fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mirBitfieldExtract(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
|
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||||
|
const rr_lsb_width = emit.mir.instructions.items(.data)[inst].rr_lsb_width;
|
||||||
|
const rd = rr_lsb_width.rd;
|
||||||
|
const rn = rr_lsb_width.rn;
|
||||||
|
const lsb = rr_lsb_width.lsb;
|
||||||
|
const width = rr_lsb_width.width;
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
.sbfx => try emit.writeInstruction(Instruction.sbfx(rd, rn, lsb, width)),
|
||||||
|
.ubfx => try emit.writeInstruction(Instruction.ubfx(rd, rn, lsb, width)),
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mirExtend(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
|
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||||
|
const rr = emit.mir.instructions.items(.data)[inst].rr;
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
.sxtb => try emit.writeInstruction(Instruction.sxtb(rr.rd, rr.rn)),
|
||||||
|
.sxth => try emit.writeInstruction(Instruction.sxth(rr.rd, rr.rn)),
|
||||||
|
.sxtw => try emit.writeInstruction(Instruction.sxtw(rr.rd, rr.rn)),
|
||||||
|
.uxtb => try emit.writeInstruction(Instruction.uxtb(rr.rd, rr.rn)),
|
||||||
|
.uxth => try emit.writeInstruction(Instruction.uxth(rr.rd, rr.rn)),
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,16 @@ pub const Inst = struct {
|
||||||
ldrh_immediate,
|
ldrh_immediate,
|
||||||
/// Load Register Halfword (register)
|
/// Load Register Halfword (register)
|
||||||
ldrh_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)
|
/// Logical Shift Left (immediate)
|
||||||
lsl_immediate,
|
lsl_immediate,
|
||||||
/// Logical Shift Left (register)
|
/// Logical Shift Left (register)
|
||||||
|
|
@ -130,6 +140,14 @@ pub const Inst = struct {
|
||||||
push_regs,
|
push_regs,
|
||||||
/// Return from subroutine
|
/// Return from subroutine
|
||||||
ret,
|
ret,
|
||||||
|
/// Signed bitfield extract
|
||||||
|
sbfx,
|
||||||
|
/// Signed extend byte
|
||||||
|
sxtb,
|
||||||
|
/// Signed extend halfword
|
||||||
|
sxth,
|
||||||
|
/// Signed extend word
|
||||||
|
sxtw,
|
||||||
/// Store Pair of Registers
|
/// Store Pair of Registers
|
||||||
stp,
|
stp,
|
||||||
/// Pseudo-instruction: Store to stack
|
/// Pseudo-instruction: Store to stack
|
||||||
|
|
@ -156,6 +174,12 @@ pub const Inst = struct {
|
||||||
sub_shifted_register,
|
sub_shifted_register,
|
||||||
/// Supervisor Call
|
/// Supervisor Call
|
||||||
svc,
|
svc,
|
||||||
|
/// Unsigned bitfield extract
|
||||||
|
ubfx,
|
||||||
|
/// Unsigned extend byte
|
||||||
|
uxtb,
|
||||||
|
/// Unsigned extend halfword
|
||||||
|
uxth,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The position of an MIR instruction within the `Mir` instructions array.
|
/// The position of an MIR instruction within the `Mir` instructions array.
|
||||||
|
|
@ -225,13 +249,6 @@ pub const Inst = struct {
|
||||||
rt: Register,
|
rt: Register,
|
||||||
inst: Index,
|
inst: Index,
|
||||||
},
|
},
|
||||||
/// Two registers
|
|
||||||
///
|
|
||||||
/// Used by e.g. mov_register
|
|
||||||
rr: struct {
|
|
||||||
rd: Register,
|
|
||||||
rn: Register,
|
|
||||||
},
|
|
||||||
/// A register, an unsigned 12-bit immediate, and an optional shift
|
/// A register, an unsigned 12-bit immediate, and an optional shift
|
||||||
///
|
///
|
||||||
/// Used by e.g. cmp_immediate
|
/// Used by e.g. cmp_immediate
|
||||||
|
|
@ -240,6 +257,13 @@ pub const Inst = struct {
|
||||||
imm12: u12,
|
imm12: u12,
|
||||||
sh: u1 = 0,
|
sh: u1 = 0,
|
||||||
},
|
},
|
||||||
|
/// Two registers
|
||||||
|
///
|
||||||
|
/// Used by e.g. mov_register
|
||||||
|
rr: struct {
|
||||||
|
rd: Register,
|
||||||
|
rn: Register,
|
||||||
|
},
|
||||||
/// Two registers, an unsigned 12-bit immediate, and an optional shift
|
/// Two registers, an unsigned 12-bit immediate, and an optional shift
|
||||||
///
|
///
|
||||||
/// Used by e.g. sub_immediate
|
/// Used by e.g. sub_immediate
|
||||||
|
|
@ -268,6 +292,16 @@ pub const Inst = struct {
|
||||||
imm6: u6,
|
imm6: u6,
|
||||||
shift: bits.Instruction.LogicalShiftedRegisterShift,
|
shift: bits.Instruction.LogicalShiftedRegisterShift,
|
||||||
},
|
},
|
||||||
|
/// Two registers and a lsb (range 0-63) and a width (range
|
||||||
|
/// 1-64)
|
||||||
|
///
|
||||||
|
/// Used by e.g. ubfx
|
||||||
|
rr_lsb_width: struct {
|
||||||
|
rd: Register,
|
||||||
|
rn: Register,
|
||||||
|
lsb: u6,
|
||||||
|
width: u7,
|
||||||
|
},
|
||||||
/// Two registers and a bitmask immediate
|
/// Two registers and a bitmask immediate
|
||||||
///
|
///
|
||||||
/// Used by e.g. eor_immediate
|
/// Used by e.g. eor_immediate
|
||||||
|
|
|
||||||
|
|
@ -510,33 +510,23 @@ pub const Instruction = union(enum) {
|
||||||
imm16: u16,
|
imm16: u16,
|
||||||
shift: u6,
|
shift: u6,
|
||||||
) Instruction {
|
) Instruction {
|
||||||
switch (rd.size()) {
|
assert(shift % 16 == 0);
|
||||||
32 => {
|
assert(!(rd.size() == 32 and shift > 16));
|
||||||
assert(shift % 16 == 0 and shift <= 16);
|
assert(!(rd.size() == 64 and shift > 48));
|
||||||
return Instruction{
|
|
||||||
.move_wide_immediate = .{
|
return Instruction{
|
||||||
.rd = rd.enc(),
|
.move_wide_immediate = .{
|
||||||
.imm16 = imm16,
|
.rd = rd.enc(),
|
||||||
.hw = @intCast(u2, shift / 16),
|
.imm16 = imm16,
|
||||||
.opc = opc,
|
.hw = @intCast(u2, shift / 16),
|
||||||
.sf = 0,
|
.opc = opc,
|
||||||
},
|
.sf = switch (rd.size()) {
|
||||||
};
|
32 => 0,
|
||||||
|
64 => 1,
|
||||||
|
else => unreachable, // unexpected register size
|
||||||
|
},
|
||||||
},
|
},
|
||||||
64 => {
|
};
|
||||||
assert(shift % 16 == 0 and shift <= 48);
|
|
||||||
return Instruction{
|
|
||||||
.move_wide_immediate = .{
|
|
||||||
.rd = rd.enc(),
|
|
||||||
.imm16 = imm16,
|
|
||||||
.hw = @intCast(u2, shift / 16),
|
|
||||||
.opc = opc,
|
|
||||||
.sf = 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
else => unreachable, // unexpected register size
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction {
|
fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction {
|
||||||
|
|
@ -675,18 +665,24 @@ pub const Instruction = union(enum) {
|
||||||
|
|
||||||
/// Which kind of load/store to perform
|
/// Which kind of load/store to perform
|
||||||
const LoadStoreVariant = enum {
|
const LoadStoreVariant = enum {
|
||||||
/// 32-bit or 64-bit
|
/// 32 bits or 64 bits
|
||||||
str,
|
str,
|
||||||
/// 16-bit, zero-extended
|
/// 8 bits, zero-extended
|
||||||
strh,
|
|
||||||
/// 8-bit, zero-extended
|
|
||||||
strb,
|
strb,
|
||||||
/// 32-bit or 64-bit
|
/// 16 bits, zero-extended
|
||||||
|
strh,
|
||||||
|
/// 32 bits or 64 bits
|
||||||
ldr,
|
ldr,
|
||||||
/// 16-bit, zero-extended
|
/// 8 bits, zero-extended
|
||||||
ldrh,
|
|
||||||
/// 8-bit, zero-extended
|
|
||||||
ldrb,
|
ldrb,
|
||||||
|
/// 16 bits, zero-extended
|
||||||
|
ldrh,
|
||||||
|
/// 8 bits, sign extended
|
||||||
|
ldrsb,
|
||||||
|
/// 16 bits, sign extended
|
||||||
|
ldrsh,
|
||||||
|
/// 32 bits, sign extended
|
||||||
|
ldrsw,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn loadStoreRegister(
|
fn loadStoreRegister(
|
||||||
|
|
@ -695,7 +691,11 @@ pub const Instruction = union(enum) {
|
||||||
offset: LoadStoreOffset,
|
offset: LoadStoreOffset,
|
||||||
variant: LoadStoreVariant,
|
variant: LoadStoreVariant,
|
||||||
) Instruction {
|
) Instruction {
|
||||||
|
assert(rn.size() == 64);
|
||||||
|
assert(rn.id() != Register.xzr.id());
|
||||||
|
|
||||||
const off = offset.toU12();
|
const off = offset.toU12();
|
||||||
|
|
||||||
const op1: u2 = blk: {
|
const op1: u2 = blk: {
|
||||||
switch (offset) {
|
switch (offset) {
|
||||||
.immediate => |imm| switch (imm) {
|
.immediate => |imm| switch (imm) {
|
||||||
|
|
@ -706,10 +706,35 @@ pub const Instruction = union(enum) {
|
||||||
}
|
}
|
||||||
break :blk 0b00;
|
break :blk 0b00;
|
||||||
};
|
};
|
||||||
const opc: u2 = switch (variant) {
|
|
||||||
.ldr, .ldrh, .ldrb => 0b01,
|
const opc: u2 = blk: {
|
||||||
.str, .strh, .strb => 0b00,
|
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{
|
return Instruction{
|
||||||
.load_store_register = .{
|
.load_store_register = .{
|
||||||
.rt = rt.enc(),
|
.rt = rt.enc(),
|
||||||
|
|
@ -718,17 +743,7 @@ pub const Instruction = union(enum) {
|
||||||
.opc = opc,
|
.opc = opc,
|
||||||
.op1 = op1,
|
.op1 = op1,
|
||||||
.v = 0,
|
.v = 0,
|
||||||
.size = blk: {
|
.size = size,
|
||||||
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,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -741,6 +756,9 @@ pub const Instruction = union(enum) {
|
||||||
encoding: u2,
|
encoding: u2,
|
||||||
load: bool,
|
load: bool,
|
||||||
) Instruction {
|
) Instruction {
|
||||||
|
assert(rn.size() == 64);
|
||||||
|
assert(rn.id() != Register.xzr.id());
|
||||||
|
|
||||||
switch (rt1.size()) {
|
switch (rt1.size()) {
|
||||||
32 => {
|
32 => {
|
||||||
assert(-256 <= offset and offset <= 252);
|
assert(-256 <= offset and offset <= 252);
|
||||||
|
|
@ -849,38 +867,26 @@ pub const Instruction = union(enum) {
|
||||||
shift: LogicalShiftedRegisterShift,
|
shift: LogicalShiftedRegisterShift,
|
||||||
amount: u6,
|
amount: u6,
|
||||||
) Instruction {
|
) Instruction {
|
||||||
switch (rd.size()) {
|
assert(rd.size() == rn.size());
|
||||||
32 => {
|
assert(rd.size() == rm.size());
|
||||||
assert(amount < 32);
|
if (rd.size() == 32) assert(amount < 32);
|
||||||
return Instruction{
|
|
||||||
.logical_shifted_register = .{
|
return Instruction{
|
||||||
.rd = rd.enc(),
|
.logical_shifted_register = .{
|
||||||
.rn = rn.enc(),
|
.rd = rd.enc(),
|
||||||
.imm6 = amount,
|
.rn = rn.enc(),
|
||||||
.rm = rm.enc(),
|
.imm6 = amount,
|
||||||
.n = n,
|
.rm = rm.enc(),
|
||||||
.shift = @enumToInt(shift),
|
.n = n,
|
||||||
.opc = opc,
|
.shift = @enumToInt(shift),
|
||||||
.sf = 0b0,
|
.opc = opc,
|
||||||
},
|
.sf = switch (rd.size()) {
|
||||||
};
|
32 => 0b0,
|
||||||
|
64 => 0b1,
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
64 => {
|
};
|
||||||
return Instruction{
|
|
||||||
.logical_shifted_register = .{
|
|
||||||
.rd = rd.enc(),
|
|
||||||
.rn = rn.enc(),
|
|
||||||
.imm6 = amount,
|
|
||||||
.rm = rm.enc(),
|
|
||||||
.n = n,
|
|
||||||
.shift = @enumToInt(shift),
|
|
||||||
.opc = opc,
|
|
||||||
.sf = 0b1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
else => unreachable, // unexpected register size
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addSubtractImmediate(
|
fn addSubtractImmediate(
|
||||||
|
|
@ -891,6 +897,9 @@ pub const Instruction = union(enum) {
|
||||||
imm12: u12,
|
imm12: u12,
|
||||||
shift: bool,
|
shift: bool,
|
||||||
) Instruction {
|
) Instruction {
|
||||||
|
assert(rd.size() == rn.size());
|
||||||
|
assert(rn.id() != Register.xzr.id());
|
||||||
|
|
||||||
return Instruction{
|
return Instruction{
|
||||||
.add_subtract_immediate = .{
|
.add_subtract_immediate = .{
|
||||||
.rd = rd.enc(),
|
.rd = rd.enc(),
|
||||||
|
|
@ -916,6 +925,9 @@ pub const Instruction = union(enum) {
|
||||||
immr: u6,
|
immr: u6,
|
||||||
n: u1,
|
n: u1,
|
||||||
) Instruction {
|
) Instruction {
|
||||||
|
assert(rd.size() == rn.size());
|
||||||
|
assert(!(rd.size() == 32 and n != 0));
|
||||||
|
|
||||||
return Instruction{
|
return Instruction{
|
||||||
.logical_immediate = .{
|
.logical_immediate = .{
|
||||||
.rd = rd.enc(),
|
.rd = rd.enc(),
|
||||||
|
|
@ -941,6 +953,10 @@ pub const Instruction = union(enum) {
|
||||||
immr: u6,
|
immr: u6,
|
||||||
imms: u6,
|
imms: u6,
|
||||||
) Instruction {
|
) Instruction {
|
||||||
|
assert(rd.size() == rn.size());
|
||||||
|
assert(!(rd.size() == 64 and n != 1));
|
||||||
|
assert(!(rd.size() == 32 and (n != 0 or immr >> 5 != 0 or immr >> 5 != 0)));
|
||||||
|
|
||||||
return Instruction{
|
return Instruction{
|
||||||
.bitfield = .{
|
.bitfield = .{
|
||||||
.rd = rd.enc(),
|
.rd = rd.enc(),
|
||||||
|
|
@ -969,6 +985,9 @@ pub const Instruction = union(enum) {
|
||||||
rm: Register,
|
rm: Register,
|
||||||
imm6: u6,
|
imm6: u6,
|
||||||
) Instruction {
|
) Instruction {
|
||||||
|
assert(rd.size() == rn.size());
|
||||||
|
assert(rd.size() == rm.size());
|
||||||
|
|
||||||
return Instruction{
|
return Instruction{
|
||||||
.add_subtract_shifted_register = .{
|
.add_subtract_shifted_register = .{
|
||||||
.rd = rd.enc(),
|
.rd = rd.enc(),
|
||||||
|
|
@ -994,6 +1013,7 @@ pub const Instruction = union(enum) {
|
||||||
offset: i21,
|
offset: i21,
|
||||||
) Instruction {
|
) Instruction {
|
||||||
assert(offset & 0b11 == 0b00);
|
assert(offset & 0b11 == 0b00);
|
||||||
|
|
||||||
return Instruction{
|
return Instruction{
|
||||||
.conditional_branch = .{
|
.conditional_branch = .{
|
||||||
.cond = @enumToInt(cond),
|
.cond = @enumToInt(cond),
|
||||||
|
|
@ -1010,6 +1030,7 @@ pub const Instruction = union(enum) {
|
||||||
offset: i21,
|
offset: i21,
|
||||||
) Instruction {
|
) Instruction {
|
||||||
assert(offset & 0b11 == 0b00);
|
assert(offset & 0b11 == 0b00);
|
||||||
|
|
||||||
return Instruction{
|
return Instruction{
|
||||||
.compare_and_branch = .{
|
.compare_and_branch = .{
|
||||||
.rt = rt.enc(),
|
.rt = rt.enc(),
|
||||||
|
|
@ -1033,6 +1054,9 @@ pub const Instruction = union(enum) {
|
||||||
rm: Register,
|
rm: Register,
|
||||||
cond: Condition,
|
cond: Condition,
|
||||||
) Instruction {
|
) Instruction {
|
||||||
|
assert(rd.size() == rn.size());
|
||||||
|
assert(rd.size() == rm.size());
|
||||||
|
|
||||||
return Instruction{
|
return Instruction{
|
||||||
.conditional_select = .{
|
.conditional_select = .{
|
||||||
.rd = rd.enc(),
|
.rd = rd.enc(),
|
||||||
|
|
@ -1085,6 +1109,9 @@ pub const Instruction = union(enum) {
|
||||||
rn: Register,
|
rn: Register,
|
||||||
rm: Register,
|
rm: Register,
|
||||||
) Instruction {
|
) Instruction {
|
||||||
|
assert(rd.size() == rn.size());
|
||||||
|
assert(rd.size() == rm.size());
|
||||||
|
|
||||||
return Instruction{
|
return Instruction{
|
||||||
.data_processing_2_source = .{
|
.data_processing_2_source = .{
|
||||||
.rd = rd.enc(),
|
.rd = rd.enc(),
|
||||||
|
|
@ -1145,6 +1172,18 @@ pub const Instruction = union(enum) {
|
||||||
return loadStoreRegister(rt, rn, offset, .ldrb);
|
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 {
|
pub fn str(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction {
|
||||||
return loadStoreRegister(rt, rn, offset, .str);
|
return loadStoreRegister(rt, rn, offset, .str);
|
||||||
}
|
}
|
||||||
|
|
@ -1404,6 +1443,23 @@ pub const Instruction = union(enum) {
|
||||||
return sbfm(rd, rn, shift, imms);
|
return sbfm(rd, rn, shift, imms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sbfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction {
|
||||||
|
return sbfm(rd, rn, lsb, @intCast(u6, lsb + width - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sxtb(rd: Register, rn: Register) Instruction {
|
||||||
|
return sbfm(rd, rn, 0, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sxth(rd: Register, rn: Register) Instruction {
|
||||||
|
return sbfm(rd, rn, 0, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sxtw(rd: Register, rn: Register) Instruction {
|
||||||
|
assert(rd.size() == 64);
|
||||||
|
return sbfm(rd, rn, 0, 31);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction {
|
pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction {
|
||||||
const size = @intCast(u6, rd.size() - 1);
|
const size = @intCast(u6, rd.size() - 1);
|
||||||
return ubfm(rd, rn, size - shift + 1, size - shift);
|
return ubfm(rd, rn, size - shift + 1, size - shift);
|
||||||
|
|
@ -1414,6 +1470,18 @@ pub const Instruction = union(enum) {
|
||||||
return ubfm(rd, rn, shift, imms);
|
return ubfm(rd, rn, shift, imms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ubfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction {
|
||||||
|
return ubfm(rd, rn, lsb, @intCast(u6, lsb + width - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uxtb(rd: Register, rn: Register) Instruction {
|
||||||
|
return ubfm(rd, rn, 0, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uxth(rd: Register, rn: Register) Instruction {
|
||||||
|
return ubfm(rd, rn, 0, 15);
|
||||||
|
}
|
||||||
|
|
||||||
// Add/subtract (shifted register)
|
// Add/subtract (shifted register)
|
||||||
|
|
||||||
pub fn addShiftedRegister(
|
pub fn addShiftedRegister(
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ test "empty function with comments" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "truncate" {
|
test "truncate" {
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
||||||
|
|
||||||
try expect(testTruncate(0x10fd) == 0xfd);
|
try expect(testTruncate(0x10fd) == 0xfd);
|
||||||
comptime try expect(testTruncate(0x10fd) == 0xfd);
|
comptime try expect(testTruncate(0x10fd) == 0xfd);
|
||||||
}
|
}
|
||||||
|
|
@ -25,8 +23,6 @@ fn testTruncate(x: u32) u8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "truncate to non-power-of-two integers" {
|
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, 0b10101, 0b1);
|
||||||
try testTrunc(u32, u1, 0b10110, 0b0);
|
try testTrunc(u32, u1, 0b10110, 0b0);
|
||||||
try testTrunc(u32, u2, 0b10101, 0b01);
|
try testTrunc(u32, u2, 0b10101, 0b01);
|
||||||
|
|
|
||||||
|
|
@ -363,7 +363,6 @@ fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int
|
||||||
test "binary not" {
|
test "binary not" {
|
||||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
try expect(comptime x: {
|
try expect(comptime x: {
|
||||||
break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101;
|
break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101;
|
||||||
|
|
@ -499,8 +498,6 @@ fn mod(comptime T: type, a: T, b: T) T {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "unsigned wrapping" {
|
test "unsigned wrapping" {
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
try testUnsignedWrappingEval(maxInt(u32));
|
try testUnsignedWrappingEval(maxInt(u32));
|
||||||
comptime try testUnsignedWrappingEval(maxInt(u32));
|
comptime try testUnsignedWrappingEval(maxInt(u32));
|
||||||
}
|
}
|
||||||
|
|
@ -512,8 +509,6 @@ fn testUnsignedWrappingEval(x: u32) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "signed wrapping" {
|
test "signed wrapping" {
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
try testSignedWrappingEval(maxInt(i32));
|
try testSignedWrappingEval(maxInt(i32));
|
||||||
comptime try testSignedWrappingEval(maxInt(i32));
|
comptime try testSignedWrappingEval(maxInt(i32));
|
||||||
}
|
}
|
||||||
|
|
@ -525,8 +520,6 @@ fn testSignedWrappingEval(x: i32) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "signed negation wrapping" {
|
test "signed negation wrapping" {
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
try testSignedNegationWrappingEval(minInt(i16));
|
try testSignedNegationWrappingEval(minInt(i16));
|
||||||
comptime try testSignedNegationWrappingEval(minInt(i16));
|
comptime try testSignedNegationWrappingEval(minInt(i16));
|
||||||
}
|
}
|
||||||
|
|
@ -537,8 +530,6 @@ fn testSignedNegationWrappingEval(x: i16) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "unsigned negation wrapping" {
|
test "unsigned negation wrapping" {
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
try testUnsignedNegationWrappingEval(1);
|
try testUnsignedNegationWrappingEval(1);
|
||||||
comptime try testUnsignedNegationWrappingEval(1);
|
comptime try testUnsignedNegationWrappingEval(1);
|
||||||
}
|
}
|
||||||
|
|
@ -859,8 +850,6 @@ test "quad hex float literal parsing accurate" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "truncating shift left" {
|
test "truncating shift left" {
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
try testShlTrunc(maxInt(u16));
|
try testShlTrunc(maxInt(u16));
|
||||||
comptime try testShlTrunc(maxInt(u16));
|
comptime try testShlTrunc(maxInt(u16));
|
||||||
}
|
}
|
||||||
|
|
@ -871,7 +860,6 @@ fn testShlTrunc(x: u16) !void {
|
||||||
|
|
||||||
test "exact shift left" {
|
test "exact shift left" {
|
||||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
try testShlExact(0b00110101);
|
try testShlExact(0b00110101);
|
||||||
comptime try testShlExact(0b00110101);
|
comptime try testShlExact(0b00110101);
|
||||||
|
|
@ -883,7 +871,6 @@ fn testShlExact(x: u8) !void {
|
||||||
|
|
||||||
test "exact shift right" {
|
test "exact shift right" {
|
||||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
try testShrExact(0b10110100);
|
try testShrExact(0b10110100);
|
||||||
comptime try testShrExact(0b10110100);
|
comptime try testShrExact(0b10110100);
|
||||||
|
|
@ -895,7 +882,6 @@ fn testShrExact(x: u8) !void {
|
||||||
|
|
||||||
test "shift left/right on u0 operand" {
|
test "shift left/right on u0 operand" {
|
||||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
const S = struct {
|
const S = struct {
|
||||||
fn doTheTest() !void {
|
fn doTheTest() !void {
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,6 @@ test "truncate.i0.var" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "truncate on comptime integer" {
|
test "truncate on comptime integer" {
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
||||||
|
|
||||||
var x = @truncate(u16, 9999);
|
var x = @truncate(u16, 9999);
|
||||||
try expect(x == 9999);
|
try expect(x == 9999);
|
||||||
var y = @truncate(u16, -21555);
|
var y = @truncate(u16, -21555);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue