Skip to content

Commit 5f25684

Browse files
committed
Handle up to 2^16 - 1 operands per inst, and error properly on more.
This PR addresses an underlying limitation that became a problem for Cranelift recently, after folding return-value loads into call pseudoinsts (thus producing single instructions with many operands): we only supported 255 operands per instruction. It appears that `Use` was using a `u8` for the "slot index" for an operand as a result of earlier optimization/packing efforts. However, the current shape of the struct leaves a padding byte free, so we should be able to expand to a `u16` with no loss in performance. Furthermore, previously we weren't catching the overflow, but rather were silently wrapping around (eek). This properly returns a `RegAllocError::TooManyOperands` now if an instruction has too many operands (more than `u16::MAX`).
1 parent b2bb25d commit 5f25684

File tree

4 files changed

+39
-30
lines changed

4 files changed

+39
-30
lines changed

src/ion/data_structures.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313
//! Data structures for backtracking allocator.
1414
1515
use super::liveranges::SpillWeight;
16+
use crate::Vec2;
1617
use crate::cfg::{CFGInfo, CFGInfoCtx};
1718
use crate::index::ContainerComparator;
1819
use crate::indexset::IndexSet;
19-
use crate::Vec2;
2020
use crate::{
21-
define_index, Allocation, Block, Bump, Edit, Function, FxHashMap, FxHashSet, MachineEnv,
22-
Operand, Output, PReg, ProgPoint, RegClass, VReg,
21+
Allocation, Block, Bump, Edit, Function, FxHashMap, FxHashSet, MachineEnv, Operand, Output,
22+
PReg, ProgPoint, RegClass, VReg, define_index,
2323
};
2424
use alloc::collections::BTreeMap;
2525
use alloc::collections::VecDeque;
@@ -179,13 +179,13 @@ impl LiveRange {
179179
pub struct Use {
180180
pub operand: Operand,
181181
pub pos: ProgPoint,
182-
pub slot: u8,
182+
pub slot: u16,
183183
pub weight: u16,
184184
}
185185

186186
impl Use {
187187
#[inline(always)]
188-
pub fn new(operand: Operand, pos: ProgPoint, slot: u8) -> Self {
188+
pub fn new(operand: Operand, pos: ProgPoint, slot: u16) -> Self {
189189
Self {
190190
operand,
191191
pos,
@@ -315,8 +315,8 @@ pub struct PRegData {
315315
#[derive(Clone, Debug)]
316316
pub struct MultiFixedRegFixup {
317317
pub pos: ProgPoint,
318-
pub from_slot: u8,
319-
pub to_slot: u8,
318+
pub from_slot: u16,
319+
pub to_slot: u16,
320320
pub level: FixedRegFixupLevel,
321321
pub to_preg: PRegIndex,
322322
pub vreg: VRegIndex,
@@ -713,11 +713,7 @@ impl InsertedMoves {
713713
) {
714714
trace!(
715715
"insert_move: pos {:?} prio {:?} from_alloc {:?} to_alloc {:?} to_vreg {:?}",
716-
pos,
717-
prio,
718-
from_alloc,
719-
to_alloc,
720-
to_vreg
716+
pos, prio, from_alloc, to_alloc, to_vreg
721717
);
722718
if from_alloc == to_alloc {
723719
trace!(" -> skipping move with same source and dest");

src/ion/liveranges.rs

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ use crate::{
2424
Allocation, Block, Function, Inst, InstPosition, Operand, OperandConstraint, OperandKind,
2525
OperandPos, PReg, ProgPoint, RegAllocError, VReg, VecExt,
2626
};
27-
use smallvec::{smallvec, SmallVec};
27+
use core::convert::TryFrom;
28+
use smallvec::{SmallVec, smallvec};
2829

2930
/// A spill weight computed for a certain Use.
3031
#[derive(Clone, Copy, Debug)]
@@ -94,6 +95,10 @@ impl core::ops::Add<SpillWeight> for SpillWeight {
9495
}
9596
}
9697

98+
fn slot_idx(i: usize) -> Result<u16, RegAllocError> {
99+
u16::try_from(i).map_err(|_| RegAllocError::TooManyOperands)
100+
}
101+
97102
impl<'a, F: Function> Env<'a, F> {
98103
pub fn create_pregs_and_vregs(&mut self) {
99104
// Create PRegs from the env.
@@ -221,9 +226,7 @@ impl<'a, F: Function> Env<'a, F> {
221226

222227
trace!(
223228
"insert use {:?} into lr {:?} with weight {:?}",
224-
u,
225-
into,
226-
weight,
229+
u, into, weight,
227230
);
228231

229232
// N.B.: we do *not* update `requirement` on the range,
@@ -362,7 +365,7 @@ impl<'a, F: Function> Env<'a, F> {
362365
Ok(())
363366
}
364367

365-
pub fn build_liveranges(&mut self) {
368+
pub fn build_liveranges(&mut self) -> Result<(), RegAllocError> {
366369
// Create Uses and Defs referring to VRegs, and place the Uses
367370
// in LiveRanges.
368371
//
@@ -456,9 +459,11 @@ impl<'a, F: Function> Env<'a, F> {
456459
let mut reused_input = None;
457460
for op in self.func.inst_operands(inst) {
458461
if let OperandConstraint::Reuse(i) = op.constraint() {
459-
debug_assert!(self.func.inst_operands(inst)[i]
460-
.as_fixed_nonallocatable()
461-
.is_none());
462+
debug_assert!(
463+
self.func.inst_operands(inst)[i]
464+
.as_fixed_nonallocatable()
465+
.is_none()
466+
);
462467
reused_input = Some(self.func.inst_operands(inst)[i].vreg());
463468
break;
464469
}
@@ -527,14 +532,13 @@ impl<'a, F: Function> Env<'a, F> {
527532
"is downward live, and there is also a ",
528533
"def or clobber at this preg"
529534
),
530-
operand,
531-
preg
535+
operand, preg
532536
);
533537
let pos = ProgPoint::before(inst);
534538
self.multi_fixed_reg_fixups.push(MultiFixedRegFixup {
535539
pos,
536-
from_slot: i as u8,
537-
to_slot: i as u8,
540+
from_slot: slot_idx(i)?,
541+
to_slot: slot_idx(i)?,
538542
to_preg: PRegIndex::new(preg.index()),
539543
vreg: VRegIndex::new(operand.vreg().vreg()),
540544
level: FixedRegFixupLevel::Initial,
@@ -638,7 +642,10 @@ impl<'a, F: Function> Env<'a, F> {
638642
live.set(operand.vreg().vreg(), true);
639643
}
640644
// Create the use in the LiveRange.
641-
self.insert_use_into_liverange(lr, Use::new(operand, pos, i as u8));
645+
self.insert_use_into_liverange(
646+
lr,
647+
Use::new(operand, pos, slot_idx(i)?),
648+
);
642649
// If def (not mod), this reg is now dead,
643650
// scanning backward; make it so.
644651
if operand.kind() == OperandKind::Def {
@@ -681,7 +688,10 @@ impl<'a, F: Function> Env<'a, F> {
681688

682689
trace!("Use of {:?} at {:?} -> {:?}", operand, pos, lr,);
683690

684-
self.insert_use_into_liverange(lr, Use::new(operand, pos, i as u8));
691+
self.insert_use_into_liverange(
692+
lr,
693+
Use::new(operand, pos, slot_idx(i)?),
694+
);
685695

686696
// Add to live-set.
687697
live.set(operand.vreg().vreg(), true);
@@ -756,6 +766,8 @@ impl<'a, F: Function> Env<'a, F> {
756766
self.output.stats.blockparam_outs_count = self.blockparam_outs.len();
757767
self.ctx.scratch_vreg_ranges = vreg_ranges;
758768
self.ctx.scratch_operand_rewrites = operand_rewrites;
769+
770+
Ok(())
759771
}
760772

761773
pub fn fixup_multi_fixed_vregs(&mut self) {
@@ -837,9 +849,7 @@ impl<'a, F: Function> Env<'a, F> {
837849
let preg_idx = PRegIndex::new(preg.index());
838850
trace!(
839851
"at pos {:?}, vreg {:?} has fixed constraint to preg {:?}",
840-
u.pos,
841-
vreg_idx,
842-
preg_idx
852+
u.pos, vreg_idx, preg_idx
843853
);
844854

845855
// FixedStack is incompatible if there are any

src/ion/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ impl<'a, F: Function> Env<'a, F> {
8383
pub(crate) fn init(&mut self) -> Result<(), RegAllocError> {
8484
self.create_pregs_and_vregs();
8585
self.compute_liveness()?;
86-
self.build_liveranges();
86+
self.build_liveranges()?;
8787
self.fixup_multi_fixed_vregs();
8888
self.merge_vreg_bundles();
8989
self.queue_bundles();

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,6 +1577,9 @@ pub enum RegAllocError {
15771577
/// Too many pinned VRegs + Reg-constrained Operands are live at
15781578
/// once, making allocation impossible.
15791579
TooManyLiveRegs,
1580+
/// Too many operands on a single instruction (beyond limit of
1581+
/// 2^16 - 1).
1582+
TooManyOperands,
15801583
}
15811584

15821585
impl core::fmt::Display for RegAllocError {

0 commit comments

Comments
 (0)