riscv64: Don't reuse registers when loading constants (#5376)

Rework the constant loading functions in the riscv64 backend to generate fresh temporaries instead of reusing the destination register.
This commit is contained in:
Trevor Elliott
2022-12-05 16:51:52 -08:00
committed by GitHub
parent 28cfa57533
commit 7d28d586da
12 changed files with 164 additions and 136 deletions

View File

@@ -140,7 +140,7 @@ impl MachInstEmitState<Inst> for EmitState {
impl Inst {
/// construct a "imm - rs".
pub(crate) fn construct_imm_sub_rs(rd: Writable<Reg>, imm: u64, rs: Reg) -> SmallInstVec<Inst> {
let mut insts = Inst::load_constant_u64(rd, imm);
let mut insts = Inst::load_constant_u64(rd, imm, &mut |_| rd);
insts.push(Inst::AluRRR {
alu_op: AluOPRRR::Sub,
rd,
@@ -930,7 +930,7 @@ impl MachInstEmit for Inst {
.emit(&[], sink, emit_info, state);
} else {
let tmp = writable_spilltmp_reg();
let mut insts = Inst::load_constant_u64(tmp, amount as u64);
let mut insts = Inst::load_constant_u64(tmp, amount as u64, &mut |_| tmp);
insts.push(Inst::AluRRR {
alu_op: AluOPRRR::Add,
rd: writable_stack_reg(),
@@ -1111,9 +1111,11 @@ impl MachInstEmit for Inst {
} => {
let index = allocs.next(index);
// load
Inst::load_constant_u32(writable_spilltmp_reg(), targets_len as u64)
.iter()
.for_each(|i| i.emit(&[], sink, emit_info, state));
Inst::load_constant_u32(writable_spilltmp_reg(), targets_len as u64, &mut |_| {
writable_spilltmp_reg()
})
.iter()
.for_each(|i| i.emit(&[], sink, emit_info, state));
Inst::CondBr {
taken: BranchTarget::offset(Inst::INSTRUCTION_SIZE * 3),
not_taken: BranchTarget::zero(),
@@ -1254,7 +1256,7 @@ impl MachInstEmit for Inst {
if let Some(offset) = Imm12::maybe_from_u64(offset as u64) {
Inst::AluRRImm12 {
alu_op: AluOPRRI::Addi,
rd: rd,
rd,
rs: base,
imm12: offset,
}
@@ -1835,17 +1837,13 @@ impl MachInstEmit for Inst {
let f32_bounds = f32_cvt_to_int_bounds(is_signed, out_type.bits() as u8);
let f64_bounds = f64_cvt_to_int_bounds(is_signed, out_type.bits() as u8);
if in_type == F32 {
Inst::load_fp_constant32(
tmp,
f32_bits(f32_bounds.0),
writable_spilltmp_reg(),
)
Inst::load_fp_constant32(tmp, f32_bits(f32_bounds.0), |_| {
writable_spilltmp_reg()
})
} else {
Inst::load_fp_constant64(
tmp,
f64_bits(f64_bounds.0),
writable_spilltmp_reg(),
)
Inst::load_fp_constant64(tmp, f64_bits(f64_bounds.0), |_| {
writable_spilltmp_reg()
})
}
.iter()
.for_each(|i| i.emit(&[], sink, emit_info, state));
@@ -1859,17 +1857,13 @@ impl MachInstEmit for Inst {
}
.emit(&[], sink, emit_info, state);
if in_type == F32 {
Inst::load_fp_constant32(
tmp,
f32_bits(f32_bounds.1),
writable_spilltmp_reg(),
)
Inst::load_fp_constant32(tmp, f32_bits(f32_bounds.1), |_| {
writable_spilltmp_reg()
})
} else {
Inst::load_fp_constant64(
tmp,
f64_bits(f64_bounds.1),
writable_spilltmp_reg(),
)
Inst::load_fp_constant64(tmp, f64_bits(f64_bounds.1), |_| {
writable_spilltmp_reg()
})
}
.iter()
.for_each(|i| i.emit(&[], sink, emit_info, state));
@@ -2160,17 +2154,13 @@ impl MachInstEmit for Inst {
}
// load max value need to round.
if ty == F32 {
Inst::load_fp_constant32(
f_tmp,
max_value_need_round(ty) as u32,
writable_spilltmp_reg(),
)
Inst::load_fp_constant32(f_tmp, max_value_need_round(ty) as u32, &mut |_| {
writable_spilltmp_reg()
})
} else {
Inst::load_fp_constant64(
f_tmp,
max_value_need_round(ty),
writable_spilltmp_reg(),
)
Inst::load_fp_constant64(f_tmp, max_value_need_round(ty), &mut |_| {
writable_spilltmp_reg()
})
}
.into_iter()
.for_each(|i| i.emit(&[], sink, emit_info, state));
@@ -2843,10 +2833,14 @@ impl MachInstEmit for Inst {
tmp: guard_size_tmp,
} => {
let step = writable_spilltmp_reg();
Inst::load_constant_u64(step, (guard_size as u64) * (probe_count as u64))
.iter()
.for_each(|i| i.emit(&[], sink, emit_info, state));
Inst::load_constant_u64(guard_size_tmp, guard_size as u64)
Inst::load_constant_u64(
step,
(guard_size as u64) * (probe_count as u64),
&mut |_| step,
)
.iter()
.for_each(|i| i.emit(&[], sink, emit_info, state));
Inst::load_constant_u64(guard_size_tmp, guard_size as u64, &mut |_| guard_size_tmp)
.iter()
.for_each(|i| i.emit(&[], sink, emit_info, state));

View File

@@ -10,7 +10,6 @@ use crate::ir::types::{F32, F64, FFLAGS, I128, I16, I32, I64, I8, IFLAGS, R32, R
pub use crate::ir::{ExternalName, MemFlags, Opcode, SourceLoc, Type, ValueLabel};
use crate::isa::CallConv;
use crate::machinst::isle::WritableReg;
use crate::machinst::*;
use crate::{settings, CodegenError, CodegenResult};
@@ -194,43 +193,57 @@ impl Inst {
pub(crate) fn load_imm12(rd: Writable<Reg>, imm: Imm12) -> Inst {
Inst::AluRRImm12 {
alu_op: AluOPRRI::Addi,
rd: rd,
rd,
rs: zero_reg(),
imm12: imm,
}
}
/// Immediates can be loaded using lui and addi instructions.
fn load_const_imm(rd: Writable<Reg>, value: u64) -> Option<SmallInstVec<Inst>> {
fn load_const_imm<F: FnMut(Type) -> Writable<Reg>>(
rd: Writable<Reg>,
value: u64,
alloc_tmp: &mut F,
) -> Option<SmallInstVec<Inst>> {
Inst::generate_imm(value, |imm20, imm12| {
let mut insts = SmallVec::new();
imm20.map(|x| insts.push(Inst::Lui { rd, imm: x }));
imm12.map(|x| {
let imm20_is_none = imm20.is_none();
let rs = if imm20_is_none {
zero_reg()
} else {
rd.to_reg()
};
let rs = if let Some(imm) = imm20 {
let rd = if imm12.is_some() { alloc_tmp(I64) } else { rd };
insts.push(Inst::Lui { rd, imm });
rd.to_reg()
} else {
zero_reg()
};
if let Some(imm12) = imm12 {
insts.push(Inst::AluRRImm12 {
alu_op: AluOPRRI::Addi,
rd,
rs,
imm12: x,
imm12,
})
});
}
insts
})
}
pub(crate) fn load_constant_u32(rd: Writable<Reg>, value: u64) -> SmallInstVec<Inst> {
let insts = Inst::load_const_imm(rd, value);
pub(crate) fn load_constant_u32<F: FnMut(Type) -> Writable<Reg>>(
rd: Writable<Reg>,
value: u64,
alloc_tmp: &mut F,
) -> SmallInstVec<Inst> {
let insts = Inst::load_const_imm(rd, value, alloc_tmp);
insts.unwrap_or(LoadConstant::U32(value as u32).load_constant(rd))
}
pub fn load_constant_u64(rd: Writable<Reg>, value: u64) -> SmallInstVec<Inst> {
let insts = Inst::load_const_imm(rd, value);
pub fn load_constant_u64<F: FnMut(Type) -> Writable<Reg>>(
rd: Writable<Reg>,
value: u64,
alloc_tmp: &mut F,
) -> SmallInstVec<Inst> {
let insts = Inst::load_const_imm(rd, value, alloc_tmp);
insts.unwrap_or(LoadConstant::U64(value).load_constant(rd))
}
@@ -255,13 +268,18 @@ impl Inst {
}
/// Create instructions that load a 32-bit floating-point constant.
pub fn load_fp_constant32(
pub fn load_fp_constant32<F: FnMut(Type) -> Writable<Reg>>(
rd: Writable<Reg>,
const_data: u32,
tmp: Writable<Reg>,
mut alloc_tmp: F,
) -> SmallVec<[Inst; 4]> {
let mut insts = SmallVec::new();
insts.extend(Self::load_constant_u32(tmp, const_data as u64));
let tmp = alloc_tmp(I64);
insts.extend(Self::load_constant_u32(
tmp,
const_data as u64,
&mut alloc_tmp,
));
insts.push(Inst::FpuRR {
frm: None,
alu_op: FpuOPRR::move_x_to_f_op(F32),
@@ -272,13 +290,14 @@ impl Inst {
}
/// Create instructions that load a 64-bit floating-point constant.
pub fn load_fp_constant64(
pub fn load_fp_constant64<F: FnMut(Type) -> Writable<Reg>>(
rd: Writable<Reg>,
const_data: u64,
tmp: WritableReg,
mut alloc_tmp: F,
) -> SmallVec<[Inst; 4]> {
let mut insts = SmallInstVec::new();
insts.extend(Self::load_constant_u64(tmp, const_data));
let tmp = alloc_tmp(I64);
insts.extend(Self::load_constant_u64(tmp, const_data, &mut alloc_tmp));
insts.push(Inst::FpuRR {
frm: None,
alu_op: FpuOPRR::move_x_to_f_op(F64),
@@ -699,22 +718,31 @@ impl MachInst for Inst {
mut alloc_tmp: F,
) -> SmallVec<[Inst; 4]> {
if (ty.bits() <= 64 && ty.is_int()) || ty == R32 || ty == R64 {
return Inst::load_constant_u64(to_regs.only_reg().unwrap(), value as u64);
return Inst::load_constant_u64(
to_regs.only_reg().unwrap(),
value as u64,
&mut alloc_tmp,
);
};
match ty {
F32 => {
Inst::load_fp_constant32(to_regs.only_reg().unwrap(), value as u32, alloc_tmp(I64))
Inst::load_fp_constant32(to_regs.only_reg().unwrap(), value as u32, &mut alloc_tmp)
}
F64 => {
Inst::load_fp_constant64(to_regs.only_reg().unwrap(), value as u64, alloc_tmp(I64))
Inst::load_fp_constant64(to_regs.only_reg().unwrap(), value as u64, &mut alloc_tmp)
}
I128 => {
let mut insts = SmallInstVec::new();
insts.extend(Inst::load_constant_u64(
to_regs.regs()[0],
(value >> 64) as u64,
&mut alloc_tmp,
));
insts.extend(Inst::load_constant_u64(
to_regs.regs()[1],
value as u64,
&mut alloc_tmp,
));
insts.extend(Inst::load_constant_u64(to_regs.regs()[1], value as u64));
return insts;
}
_ => unreachable!("vector type not implemented now."),