Skip to content

Commit 8de33da

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 8de33da

File tree

4 files changed

+26
-10
lines changed

4 files changed

+26
-10
lines changed

src/ion/data_structures.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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,

src/ion/liveranges.rs

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

2930
/// A spill weight computed for a certain Use.
@@ -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.
@@ -362,7 +367,7 @@ impl<'a, F: Function> Env<'a, F> {
362367
Ok(())
363368
}
364369

365-
pub fn build_liveranges(&mut self) {
370+
pub fn build_liveranges(&mut self) -> Result<(), RegAllocError> {
366371
// Create Uses and Defs referring to VRegs, and place the Uses
367372
// in LiveRanges.
368373
//
@@ -533,8 +538,8 @@ impl<'a, F: Function> Env<'a, F> {
533538
let pos = ProgPoint::before(inst);
534539
self.multi_fixed_reg_fixups.push(MultiFixedRegFixup {
535540
pos,
536-
from_slot: i as u8,
537-
to_slot: i as u8,
541+
from_slot: slot_idx(i)?,
542+
to_slot: slot_idx(i)?,
538543
to_preg: PRegIndex::new(preg.index()),
539544
vreg: VRegIndex::new(operand.vreg().vreg()),
540545
level: FixedRegFixupLevel::Initial,
@@ -638,7 +643,10 @@ impl<'a, F: Function> Env<'a, F> {
638643
live.set(operand.vreg().vreg(), true);
639644
}
640645
// Create the use in the LiveRange.
641-
self.insert_use_into_liverange(lr, Use::new(operand, pos, i as u8));
646+
self.insert_use_into_liverange(
647+
lr,
648+
Use::new(operand, pos, slot_idx(i)?),
649+
);
642650
// If def (not mod), this reg is now dead,
643651
// scanning backward; make it so.
644652
if operand.kind() == OperandKind::Def {
@@ -681,7 +689,10 @@ impl<'a, F: Function> Env<'a, F> {
681689

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

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

686697
// Add to live-set.
687698
live.set(operand.vreg().vreg(), true);
@@ -756,6 +767,8 @@ impl<'a, F: Function> Env<'a, F> {
756767
self.output.stats.blockparam_outs_count = self.blockparam_outs.len();
757768
self.ctx.scratch_vreg_ranges = vreg_ranges;
758769
self.ctx.scratch_operand_rewrites = operand_rewrites;
770+
771+
Ok(())
759772
}
760773

761774
pub fn fixup_multi_fixed_vregs(&mut self) {

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)