Simplify pinned-vreg API: don't require slice of all pinned vregs. (#28)
Simplify pinned-vreg API: don't require slice of all pinned vregs. Previously, we kept a bool flag `is_pinned` in the `VRegData`, and we required a `&[VReg]` of all pinned vregs to be provided by `Function::pinned_vregs()`. This was (I think) done for convenience, but it turns out not to really be necessary, as we can just query `is_pinned_vreg` where needed (and in the likely implementation, e.g. in Cranelift, this will be a `< NUM_PINNED_VREGS` check that can be inlined). This adds convenience for the embedder (the main benefit), and also reduces complexity, removes some state, and avoids some work initializing the regalloc state for a run.
This commit is contained in:
@@ -259,7 +259,6 @@ pub struct VRegData {
|
|||||||
pub ranges: LiveRangeList,
|
pub ranges: LiveRangeList,
|
||||||
pub blockparam: Block,
|
pub blockparam: Block,
|
||||||
pub is_ref: bool,
|
pub is_ref: bool,
|
||||||
pub is_pinned: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|||||||
@@ -119,16 +119,12 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
ranges: smallvec![],
|
ranges: smallvec![],
|
||||||
blockparam: Block::invalid(),
|
blockparam: Block::invalid(),
|
||||||
is_ref: false,
|
is_ref: false,
|
||||||
is_pinned: false,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for v in self.func.reftype_vregs() {
|
for v in self.func.reftype_vregs() {
|
||||||
self.vregs[v.vreg()].is_ref = true;
|
self.vregs[v.vreg()].is_ref = true;
|
||||||
}
|
}
|
||||||
for v in self.func.pinned_vregs() {
|
|
||||||
self.vregs[v.vreg()].is_pinned = true;
|
|
||||||
}
|
|
||||||
// Create allocations too.
|
// Create allocations too.
|
||||||
for inst in 0..self.func.num_insts() {
|
for inst in 0..self.func.num_insts() {
|
||||||
let start = self.allocs.len() as u32;
|
let start = self.allocs.len() as u32;
|
||||||
@@ -507,389 +503,388 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
debug_assert_eq!(dst.kind(), OperandKind::Def);
|
debug_assert_eq!(dst.kind(), OperandKind::Def);
|
||||||
debug_assert_eq!(dst.pos(), OperandPos::Late);
|
debug_assert_eq!(dst.pos(), OperandPos::Late);
|
||||||
|
|
||||||
// If both src and dest are pinned, emit the
|
let src_pinned = self.func.is_pinned_vreg(src.vreg());
|
||||||
// move right here, right now.
|
let dst_pinned = self.func.is_pinned_vreg(dst.vreg());
|
||||||
if self.vregs[src.vreg().vreg()].is_pinned
|
|
||||||
&& self.vregs[dst.vreg().vreg()].is_pinned
|
|
||||||
{
|
|
||||||
// Update LRs.
|
|
||||||
if !live.get(src.vreg().vreg()) {
|
|
||||||
let lr = self.add_liverange_to_vreg(
|
|
||||||
VRegIndex::new(src.vreg().vreg()),
|
|
||||||
CodeRange {
|
|
||||||
from: self.cfginfo.block_entry[block.index()],
|
|
||||||
to: ProgPoint::after(inst),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
live.set(src.vreg().vreg(), true);
|
|
||||||
vreg_ranges[src.vreg().vreg()] = lr;
|
|
||||||
}
|
|
||||||
if live.get(dst.vreg().vreg()) {
|
|
||||||
let lr = vreg_ranges[dst.vreg().vreg()];
|
|
||||||
self.ranges[lr.index()].range.from = ProgPoint::after(inst);
|
|
||||||
live.set(dst.vreg().vreg(), false);
|
|
||||||
} else {
|
|
||||||
self.add_liverange_to_vreg(
|
|
||||||
VRegIndex::new(dst.vreg().vreg()),
|
|
||||||
CodeRange {
|
|
||||||
from: ProgPoint::after(inst),
|
|
||||||
to: ProgPoint::before(inst.next()),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let src_preg = match src.constraint() {
|
match (src_pinned, dst_pinned) {
|
||||||
OperandConstraint::FixedReg(r) => r,
|
// If both src and dest are pinned, emit
|
||||||
_ => unreachable!(),
|
// the move right here, right now.
|
||||||
};
|
(Some(src_preg), Some(dst_preg)) => {
|
||||||
let dst_preg = match dst.constraint() {
|
// Update LRs.
|
||||||
OperandConstraint::FixedReg(r) => r,
|
if !live.get(src.vreg().vreg()) {
|
||||||
_ => unreachable!(),
|
let lr = self.add_liverange_to_vreg(
|
||||||
};
|
VRegIndex::new(src.vreg().vreg()),
|
||||||
self.insert_move(
|
|
||||||
ProgPoint::before(inst),
|
|
||||||
InsertMovePrio::MultiFixedReg,
|
|
||||||
Allocation::reg(src_preg),
|
|
||||||
Allocation::reg(dst_preg),
|
|
||||||
Some(dst.vreg()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// If exactly one of source and dest (but not
|
|
||||||
// both) is a pinned-vreg, convert this into a
|
|
||||||
// ghost use on the other vreg with a FixedReg
|
|
||||||
// constraint.
|
|
||||||
else if self.vregs[src.vreg().vreg()].is_pinned
|
|
||||||
|| self.vregs[dst.vreg().vreg()].is_pinned
|
|
||||||
{
|
|
||||||
trace!(" -> exactly one of src/dst is pinned; converting to ghost use");
|
|
||||||
let (preg, vreg, pinned_vreg, kind, pos, progpoint) =
|
|
||||||
if self.vregs[src.vreg().vreg()].is_pinned {
|
|
||||||
// Source is pinned: this is a def on the dst with a pinned preg.
|
|
||||||
(
|
|
||||||
self.func.is_pinned_vreg(src.vreg()).unwrap(),
|
|
||||||
dst.vreg(),
|
|
||||||
src.vreg(),
|
|
||||||
OperandKind::Def,
|
|
||||||
OperandPos::Late,
|
|
||||||
ProgPoint::after(inst),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Dest is pinned: this is a use on the src with a pinned preg.
|
|
||||||
(
|
|
||||||
self.func.is_pinned_vreg(dst.vreg()).unwrap(),
|
|
||||||
src.vreg(),
|
|
||||||
dst.vreg(),
|
|
||||||
OperandKind::Use,
|
|
||||||
OperandPos::Early,
|
|
||||||
ProgPoint::after(inst),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let constraint = OperandConstraint::FixedReg(preg);
|
|
||||||
let operand = Operand::new(vreg, constraint, kind, pos);
|
|
||||||
|
|
||||||
trace!(
|
|
||||||
concat!(
|
|
||||||
" -> preg {:?} vreg {:?} kind {:?} ",
|
|
||||||
"pos {:?} progpoint {:?} constraint {:?} operand {:?}"
|
|
||||||
),
|
|
||||||
preg,
|
|
||||||
vreg,
|
|
||||||
kind,
|
|
||||||
pos,
|
|
||||||
progpoint,
|
|
||||||
constraint,
|
|
||||||
operand
|
|
||||||
);
|
|
||||||
|
|
||||||
// Get the LR for the vreg; if none, create one.
|
|
||||||
let mut lr = vreg_ranges[vreg.vreg()];
|
|
||||||
if !live.get(vreg.vreg()) {
|
|
||||||
let from = match kind {
|
|
||||||
OperandKind::Use => self.cfginfo.block_entry[block.index()],
|
|
||||||
OperandKind::Def => progpoint,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let to = progpoint.next();
|
|
||||||
lr = self.add_liverange_to_vreg(
|
|
||||||
VRegIndex::new(vreg.vreg()),
|
|
||||||
CodeRange { from, to },
|
|
||||||
);
|
|
||||||
trace!(" -> dead; created LR");
|
|
||||||
}
|
|
||||||
trace!(" -> LR {:?}", lr);
|
|
||||||
|
|
||||||
self.insert_use_into_liverange(
|
|
||||||
lr,
|
|
||||||
Use::new(operand, progpoint, SLOT_NONE),
|
|
||||||
);
|
|
||||||
|
|
||||||
if kind == OperandKind::Def {
|
|
||||||
live.set(vreg.vreg(), false);
|
|
||||||
if self.ranges[lr.index()].range.from
|
|
||||||
== self.cfginfo.block_entry[block.index()]
|
|
||||||
{
|
|
||||||
self.ranges[lr.index()].range.from = progpoint;
|
|
||||||
}
|
|
||||||
self.ranges[lr.index()].set_flag(LiveRangeFlag::StartsAtDef);
|
|
||||||
} else {
|
|
||||||
live.set(vreg.vreg(), true);
|
|
||||||
vreg_ranges[vreg.vreg()] = lr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle liveness of the other vreg. Note
|
|
||||||
// that this is somewhat special. For the
|
|
||||||
// destination case, we want the pinned
|
|
||||||
// vreg's LR to start just *after* the
|
|
||||||
// operand we inserted above, because
|
|
||||||
// otherwise it would overlap, and
|
|
||||||
// interfere, and prevent allocation. For
|
|
||||||
// the source case, we want to "poke a
|
|
||||||
// hole" in the LR: if it's live going
|
|
||||||
// downward, end it just after the operand
|
|
||||||
// and restart it before; if it isn't
|
|
||||||
// (this is the last use), start it
|
|
||||||
// before.
|
|
||||||
if kind == OperandKind::Def {
|
|
||||||
trace!(" -> src on pinned vreg {:?}", pinned_vreg);
|
|
||||||
// The *other* vreg is a def, so the pinned-vreg
|
|
||||||
// mention is a use. If already live,
|
|
||||||
// end the existing LR just *after*
|
|
||||||
// the `progpoint` defined above and
|
|
||||||
// start a new one just *before* the
|
|
||||||
// `progpoint` defined above,
|
|
||||||
// preserving the start. If not, start
|
|
||||||
// a new one live back to the top of
|
|
||||||
// the block, starting just before
|
|
||||||
// `progpoint`.
|
|
||||||
if live.get(pinned_vreg.vreg()) {
|
|
||||||
let pinned_lr = vreg_ranges[pinned_vreg.vreg()];
|
|
||||||
let orig_start = self.ranges[pinned_lr.index()].range.from;
|
|
||||||
trace!(
|
|
||||||
" -> live with LR {:?}; truncating to start at {:?}",
|
|
||||||
pinned_lr,
|
|
||||||
progpoint.next()
|
|
||||||
);
|
|
||||||
self.ranges[pinned_lr.index()].range.from = progpoint.next();
|
|
||||||
let new_lr = self.add_liverange_to_vreg(
|
|
||||||
VRegIndex::new(pinned_vreg.vreg()),
|
|
||||||
CodeRange {
|
CodeRange {
|
||||||
from: orig_start,
|
from: self.cfginfo.block_entry[block.index()],
|
||||||
to: progpoint.prev(),
|
to: ProgPoint::after(inst),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
vreg_ranges[pinned_vreg.vreg()] = new_lr;
|
live.set(src.vreg().vreg(), true);
|
||||||
trace!(" -> created LR {:?} with remaining range from {:?} to {:?}", new_lr, orig_start, progpoint);
|
vreg_ranges[src.vreg().vreg()] = lr;
|
||||||
|
}
|
||||||
// Add an edit right now to indicate that at
|
if live.get(dst.vreg().vreg()) {
|
||||||
// this program point, the given
|
let lr = vreg_ranges[dst.vreg().vreg()];
|
||||||
// preg is now known as that vreg,
|
self.ranges[lr.index()].range.from = ProgPoint::after(inst);
|
||||||
// not the preg, but immediately
|
live.set(dst.vreg().vreg(), false);
|
||||||
// after, it is known as the preg
|
|
||||||
// again. This is used by the
|
|
||||||
// checker.
|
|
||||||
self.insert_move(
|
|
||||||
ProgPoint::after(inst),
|
|
||||||
InsertMovePrio::Regular,
|
|
||||||
Allocation::reg(preg),
|
|
||||||
Allocation::reg(preg),
|
|
||||||
Some(dst.vreg()),
|
|
||||||
);
|
|
||||||
self.insert_move(
|
|
||||||
ProgPoint::before(inst.next()),
|
|
||||||
InsertMovePrio::MultiFixedReg,
|
|
||||||
Allocation::reg(preg),
|
|
||||||
Allocation::reg(preg),
|
|
||||||
Some(src.vreg()),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
if inst > self.cfginfo.block_entry[block.index()].inst() {
|
self.add_liverange_to_vreg(
|
||||||
|
VRegIndex::new(dst.vreg().vreg()),
|
||||||
|
CodeRange {
|
||||||
|
from: ProgPoint::after(inst),
|
||||||
|
to: ProgPoint::before(inst.next()),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.insert_move(
|
||||||
|
ProgPoint::before(inst),
|
||||||
|
InsertMovePrio::MultiFixedReg,
|
||||||
|
Allocation::reg(src_preg),
|
||||||
|
Allocation::reg(dst_preg),
|
||||||
|
Some(dst.vreg()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If exactly one of source and dest (but
|
||||||
|
// not both) is a pinned-vreg, convert
|
||||||
|
// this into a ghost use on the other vreg
|
||||||
|
// with a FixedReg constraint.
|
||||||
|
(Some(preg), None) | (None, Some(preg)) => {
|
||||||
|
trace!(
|
||||||
|
" -> exactly one of src/dst is pinned; converting to ghost use"
|
||||||
|
);
|
||||||
|
let (vreg, pinned_vreg, kind, pos, progpoint) =
|
||||||
|
if src_pinned.is_some() {
|
||||||
|
// Source is pinned: this is a def on the dst with a pinned preg.
|
||||||
|
(
|
||||||
|
dst.vreg(),
|
||||||
|
src.vreg(),
|
||||||
|
OperandKind::Def,
|
||||||
|
OperandPos::Late,
|
||||||
|
ProgPoint::after(inst),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Dest is pinned: this is a use on the src with a pinned preg.
|
||||||
|
(
|
||||||
|
src.vreg(),
|
||||||
|
dst.vreg(),
|
||||||
|
OperandKind::Use,
|
||||||
|
OperandPos::Early,
|
||||||
|
ProgPoint::after(inst),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let constraint = OperandConstraint::FixedReg(preg);
|
||||||
|
let operand = Operand::new(vreg, constraint, kind, pos);
|
||||||
|
|
||||||
|
trace!(
|
||||||
|
concat!(
|
||||||
|
" -> preg {:?} vreg {:?} kind {:?} ",
|
||||||
|
"pos {:?} progpoint {:?} constraint {:?} operand {:?}"
|
||||||
|
),
|
||||||
|
preg,
|
||||||
|
vreg,
|
||||||
|
kind,
|
||||||
|
pos,
|
||||||
|
progpoint,
|
||||||
|
constraint,
|
||||||
|
operand
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the LR for the vreg; if none, create one.
|
||||||
|
let mut lr = vreg_ranges[vreg.vreg()];
|
||||||
|
if !live.get(vreg.vreg()) {
|
||||||
|
let from = match kind {
|
||||||
|
OperandKind::Use => self.cfginfo.block_entry[block.index()],
|
||||||
|
OperandKind::Def => progpoint,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let to = progpoint.next();
|
||||||
|
lr = self.add_liverange_to_vreg(
|
||||||
|
VRegIndex::new(vreg.vreg()),
|
||||||
|
CodeRange { from, to },
|
||||||
|
);
|
||||||
|
trace!(" -> dead; created LR");
|
||||||
|
}
|
||||||
|
trace!(" -> LR {:?}", lr);
|
||||||
|
|
||||||
|
self.insert_use_into_liverange(
|
||||||
|
lr,
|
||||||
|
Use::new(operand, progpoint, SLOT_NONE),
|
||||||
|
);
|
||||||
|
|
||||||
|
if kind == OperandKind::Def {
|
||||||
|
live.set(vreg.vreg(), false);
|
||||||
|
if self.ranges[lr.index()].range.from
|
||||||
|
== self.cfginfo.block_entry[block.index()]
|
||||||
|
{
|
||||||
|
self.ranges[lr.index()].range.from = progpoint;
|
||||||
|
}
|
||||||
|
self.ranges[lr.index()].set_flag(LiveRangeFlag::StartsAtDef);
|
||||||
|
} else {
|
||||||
|
live.set(vreg.vreg(), true);
|
||||||
|
vreg_ranges[vreg.vreg()] = lr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle liveness of the other vreg. Note
|
||||||
|
// that this is somewhat special. For the
|
||||||
|
// destination case, we want the pinned
|
||||||
|
// vreg's LR to start just *after* the
|
||||||
|
// operand we inserted above, because
|
||||||
|
// otherwise it would overlap, and
|
||||||
|
// interfere, and prevent allocation. For
|
||||||
|
// the source case, we want to "poke a
|
||||||
|
// hole" in the LR: if it's live going
|
||||||
|
// downward, end it just after the operand
|
||||||
|
// and restart it before; if it isn't
|
||||||
|
// (this is the last use), start it
|
||||||
|
// before.
|
||||||
|
if kind == OperandKind::Def {
|
||||||
|
trace!(" -> src on pinned vreg {:?}", pinned_vreg);
|
||||||
|
// The *other* vreg is a def, so the pinned-vreg
|
||||||
|
// mention is a use. If already live,
|
||||||
|
// end the existing LR just *after*
|
||||||
|
// the `progpoint` defined above and
|
||||||
|
// start a new one just *before* the
|
||||||
|
// `progpoint` defined above,
|
||||||
|
// preserving the start. If not, start
|
||||||
|
// a new one live back to the top of
|
||||||
|
// the block, starting just before
|
||||||
|
// `progpoint`.
|
||||||
|
if live.get(pinned_vreg.vreg()) {
|
||||||
|
let pinned_lr = vreg_ranges[pinned_vreg.vreg()];
|
||||||
|
let orig_start = self.ranges[pinned_lr.index()].range.from;
|
||||||
|
trace!(
|
||||||
|
" -> live with LR {:?}; truncating to start at {:?}",
|
||||||
|
pinned_lr,
|
||||||
|
progpoint.next()
|
||||||
|
);
|
||||||
|
self.ranges[pinned_lr.index()].range.from =
|
||||||
|
progpoint.next();
|
||||||
let new_lr = self.add_liverange_to_vreg(
|
let new_lr = self.add_liverange_to_vreg(
|
||||||
VRegIndex::new(pinned_vreg.vreg()),
|
VRegIndex::new(pinned_vreg.vreg()),
|
||||||
CodeRange {
|
CodeRange {
|
||||||
from: self.cfginfo.block_entry[block.index()],
|
from: orig_start,
|
||||||
to: ProgPoint::before(inst),
|
to: progpoint.prev(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
vreg_ranges[pinned_vreg.vreg()] = new_lr;
|
vreg_ranges[pinned_vreg.vreg()] = new_lr;
|
||||||
live.set(pinned_vreg.vreg(), true);
|
trace!(" -> created LR {:?} with remaining range from {:?} to {:?}", new_lr, orig_start, progpoint);
|
||||||
trace!(" -> was not live; created new LR {:?}", new_lr);
|
|
||||||
|
// Add an edit right now to indicate that at
|
||||||
|
// this program point, the given
|
||||||
|
// preg is now known as that vreg,
|
||||||
|
// not the preg, but immediately
|
||||||
|
// after, it is known as the preg
|
||||||
|
// again. This is used by the
|
||||||
|
// checker.
|
||||||
|
self.insert_move(
|
||||||
|
ProgPoint::after(inst),
|
||||||
|
InsertMovePrio::Regular,
|
||||||
|
Allocation::reg(preg),
|
||||||
|
Allocation::reg(preg),
|
||||||
|
Some(dst.vreg()),
|
||||||
|
);
|
||||||
|
self.insert_move(
|
||||||
|
ProgPoint::before(inst.next()),
|
||||||
|
InsertMovePrio::MultiFixedReg,
|
||||||
|
Allocation::reg(preg),
|
||||||
|
Allocation::reg(preg),
|
||||||
|
Some(src.vreg()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if inst > self.cfginfo.block_entry[block.index()].inst() {
|
||||||
|
let new_lr = self.add_liverange_to_vreg(
|
||||||
|
VRegIndex::new(pinned_vreg.vreg()),
|
||||||
|
CodeRange {
|
||||||
|
from: self.cfginfo.block_entry[block.index()],
|
||||||
|
to: ProgPoint::before(inst),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
vreg_ranges[pinned_vreg.vreg()] = new_lr;
|
||||||
|
live.set(pinned_vreg.vreg(), true);
|
||||||
|
trace!(" -> was not live; created new LR {:?}", new_lr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an edit right now to indicate that at
|
||||||
|
// this program point, the given
|
||||||
|
// preg is now known as that vreg,
|
||||||
|
// not the preg. This is used by
|
||||||
|
// the checker.
|
||||||
|
self.insert_move(
|
||||||
|
ProgPoint::after(inst),
|
||||||
|
InsertMovePrio::BlockParam,
|
||||||
|
Allocation::reg(preg),
|
||||||
|
Allocation::reg(preg),
|
||||||
|
Some(dst.vreg()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
trace!(" -> dst on pinned vreg {:?}", pinned_vreg);
|
||||||
|
// The *other* vreg is a use, so the pinned-vreg
|
||||||
|
// mention is a def. Truncate its LR
|
||||||
|
// just *after* the `progpoint`
|
||||||
|
// defined above.
|
||||||
|
if live.get(pinned_vreg.vreg()) {
|
||||||
|
let pinned_lr = vreg_ranges[pinned_vreg.vreg()];
|
||||||
|
self.ranges[pinned_lr.index()].range.from =
|
||||||
|
progpoint.next();
|
||||||
|
trace!(
|
||||||
|
" -> was live with LR {:?}; truncated start to {:?}",
|
||||||
|
pinned_lr,
|
||||||
|
progpoint.next()
|
||||||
|
);
|
||||||
|
live.set(pinned_vreg.vreg(), false);
|
||||||
|
|
||||||
// Add an edit right now to indicate that at
|
// Add a no-op edit right now to indicate that
|
||||||
// this program point, the given
|
// at this program point, the
|
||||||
// preg is now known as that vreg,
|
// given preg is now known as that
|
||||||
// not the preg. This is used by
|
// preg, not the vreg. This is
|
||||||
// the checker.
|
// used by the checker.
|
||||||
self.insert_move(
|
self.insert_move(
|
||||||
ProgPoint::after(inst),
|
ProgPoint::before(inst.next()),
|
||||||
InsertMovePrio::BlockParam,
|
InsertMovePrio::PostRegular,
|
||||||
Allocation::reg(preg),
|
Allocation::reg(preg),
|
||||||
Allocation::reg(preg),
|
Allocation::reg(preg),
|
||||||
Some(dst.vreg()),
|
Some(dst.vreg()),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
// Otherwise, if dead, no need to create
|
||||||
|
// a dummy LR -- there is no
|
||||||
|
// reservation to make (the other vreg
|
||||||
|
// will land in the reg with the
|
||||||
|
// fixed-reg operand constraint, but
|
||||||
|
// it's a dead move anyway).
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
trace!(" -> dst on pinned vreg {:?}", pinned_vreg);
|
|
||||||
// The *other* vreg is a use, so the pinned-vreg
|
|
||||||
// mention is a def. Truncate its LR
|
|
||||||
// just *after* the `progpoint`
|
|
||||||
// defined above.
|
|
||||||
if live.get(pinned_vreg.vreg()) {
|
|
||||||
let pinned_lr = vreg_ranges[pinned_vreg.vreg()];
|
|
||||||
self.ranges[pinned_lr.index()].range.from = progpoint.next();
|
|
||||||
trace!(
|
|
||||||
" -> was live with LR {:?}; truncated start to {:?}",
|
|
||||||
pinned_lr,
|
|
||||||
progpoint.next()
|
|
||||||
);
|
|
||||||
live.set(pinned_vreg.vreg(), false);
|
|
||||||
|
|
||||||
// Add a no-op edit right now to indicate that
|
|
||||||
// at this program point, the
|
|
||||||
// given preg is now known as that
|
|
||||||
// preg, not the vreg. This is
|
|
||||||
// used by the checker.
|
|
||||||
self.insert_move(
|
|
||||||
ProgPoint::before(inst.next()),
|
|
||||||
InsertMovePrio::PostRegular,
|
|
||||||
Allocation::reg(preg),
|
|
||||||
Allocation::reg(preg),
|
|
||||||
Some(dst.vreg()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Otherwise, if dead, no need to create
|
|
||||||
// a dummy LR -- there is no
|
|
||||||
// reservation to make (the other vreg
|
|
||||||
// will land in the reg with the
|
|
||||||
// fixed-reg operand constraint, but
|
|
||||||
// it's a dead move anyway).
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Redefine src and dst operands to have
|
|
||||||
// positions of After and Before respectively
|
|
||||||
// (see note below), and to have Any
|
|
||||||
// constraints if they were originally Reg.
|
|
||||||
let src_constraint = match src.constraint() {
|
|
||||||
OperandConstraint::Reg => OperandConstraint::Any,
|
|
||||||
x => x,
|
|
||||||
};
|
|
||||||
let dst_constraint = match dst.constraint() {
|
|
||||||
OperandConstraint::Reg => OperandConstraint::Any,
|
|
||||||
x => x,
|
|
||||||
};
|
|
||||||
let src = Operand::new(
|
|
||||||
src.vreg(),
|
|
||||||
src_constraint,
|
|
||||||
OperandKind::Use,
|
|
||||||
OperandPos::Late,
|
|
||||||
);
|
|
||||||
let dst = Operand::new(
|
|
||||||
dst.vreg(),
|
|
||||||
dst_constraint,
|
|
||||||
OperandKind::Def,
|
|
||||||
OperandPos::Early,
|
|
||||||
);
|
|
||||||
|
|
||||||
if self.annotations_enabled {
|
|
||||||
self.annotate(
|
|
||||||
ProgPoint::after(inst),
|
|
||||||
format!(
|
|
||||||
" prog-move v{} ({:?}) -> v{} ({:?})",
|
|
||||||
src.vreg().vreg(),
|
|
||||||
src_constraint,
|
|
||||||
dst.vreg().vreg(),
|
|
||||||
dst_constraint,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// N.B.: in order to integrate with the move
|
// Ordinary move between two non-pinned vregs.
|
||||||
// resolution that joins LRs in general, we
|
(None, None) => {
|
||||||
// conceptually treat the move as happening
|
// Redefine src and dst operands to have
|
||||||
// between the move inst's After and the next
|
// positions of After and Before respectively
|
||||||
// inst's Before. Thus the src LR goes up to
|
// (see note below), and to have Any
|
||||||
// (exclusive) next-inst-pre, and the dst LR
|
// constraints if they were originally Reg.
|
||||||
// starts at next-inst-pre. We have to take
|
let src_constraint = match src.constraint() {
|
||||||
// care in our move insertion to handle this
|
OperandConstraint::Reg => OperandConstraint::Any,
|
||||||
// like other inter-inst moves, i.e., at
|
x => x,
|
||||||
// `Regular` priority, so it properly happens
|
|
||||||
// in parallel with other inter-LR moves.
|
|
||||||
//
|
|
||||||
// Why the progpoint between move and next
|
|
||||||
// inst, and not the progpoint between prev
|
|
||||||
// inst and move? Because a move can be the
|
|
||||||
// first inst in a block, but cannot be the
|
|
||||||
// last; so the following progpoint is always
|
|
||||||
// within the same block, while the previous
|
|
||||||
// one may be an inter-block point (and the
|
|
||||||
// After of the prev inst in a different
|
|
||||||
// block).
|
|
||||||
|
|
||||||
// Handle the def w.r.t. liveranges: trim the
|
|
||||||
// start of the range and mark it dead at this
|
|
||||||
// point in our backward scan.
|
|
||||||
let pos = ProgPoint::before(inst.next());
|
|
||||||
let mut dst_lr = vreg_ranges[dst.vreg().vreg()];
|
|
||||||
if !live.get(dst.vreg().vreg()) {
|
|
||||||
let from = pos;
|
|
||||||
let to = pos.next();
|
|
||||||
dst_lr = self.add_liverange_to_vreg(
|
|
||||||
VRegIndex::new(dst.vreg().vreg()),
|
|
||||||
CodeRange { from, to },
|
|
||||||
);
|
|
||||||
trace!(" -> invalid LR for def; created {:?}", dst_lr);
|
|
||||||
}
|
|
||||||
trace!(" -> has existing LR {:?}", dst_lr);
|
|
||||||
// Trim the LR to start here.
|
|
||||||
if self.ranges[dst_lr.index()].range.from
|
|
||||||
== self.cfginfo.block_entry[block.index()]
|
|
||||||
{
|
|
||||||
trace!(" -> started at block start; trimming to {:?}", pos);
|
|
||||||
self.ranges[dst_lr.index()].range.from = pos;
|
|
||||||
}
|
|
||||||
self.ranges[dst_lr.index()].set_flag(LiveRangeFlag::StartsAtDef);
|
|
||||||
live.set(dst.vreg().vreg(), false);
|
|
||||||
vreg_ranges[dst.vreg().vreg()] = LiveRangeIndex::invalid();
|
|
||||||
self.vreg_regs[dst.vreg().vreg()] = dst.vreg();
|
|
||||||
|
|
||||||
// Handle the use w.r.t. liveranges: make it live
|
|
||||||
// and create an initial LR back to the start of
|
|
||||||
// the block.
|
|
||||||
let pos = ProgPoint::after(inst);
|
|
||||||
let src_lr = if !live.get(src.vreg().vreg()) {
|
|
||||||
let range = CodeRange {
|
|
||||||
from: self.cfginfo.block_entry[block.index()],
|
|
||||||
to: pos.next(),
|
|
||||||
};
|
};
|
||||||
let src_lr = self.add_liverange_to_vreg(
|
let dst_constraint = match dst.constraint() {
|
||||||
VRegIndex::new(src.vreg().vreg()),
|
OperandConstraint::Reg => OperandConstraint::Any,
|
||||||
range,
|
x => x,
|
||||||
|
};
|
||||||
|
let src = Operand::new(
|
||||||
|
src.vreg(),
|
||||||
|
src_constraint,
|
||||||
|
OperandKind::Use,
|
||||||
|
OperandPos::Late,
|
||||||
|
);
|
||||||
|
let dst = Operand::new(
|
||||||
|
dst.vreg(),
|
||||||
|
dst_constraint,
|
||||||
|
OperandKind::Def,
|
||||||
|
OperandPos::Early,
|
||||||
);
|
);
|
||||||
vreg_ranges[src.vreg().vreg()] = src_lr;
|
|
||||||
src_lr
|
|
||||||
} else {
|
|
||||||
vreg_ranges[src.vreg().vreg()]
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!(" -> src LR {:?}", src_lr);
|
if self.annotations_enabled {
|
||||||
|
self.annotate(
|
||||||
|
ProgPoint::after(inst),
|
||||||
|
format!(
|
||||||
|
" prog-move v{} ({:?}) -> v{} ({:?})",
|
||||||
|
src.vreg().vreg(),
|
||||||
|
src_constraint,
|
||||||
|
dst.vreg().vreg(),
|
||||||
|
dst_constraint,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Add to live-set.
|
// N.B.: in order to integrate with the move
|
||||||
let src_is_dead_after_move = !live.get(src.vreg().vreg());
|
// resolution that joins LRs in general, we
|
||||||
live.set(src.vreg().vreg(), true);
|
// conceptually treat the move as happening
|
||||||
|
// between the move inst's After and the next
|
||||||
|
// inst's Before. Thus the src LR goes up to
|
||||||
|
// (exclusive) next-inst-pre, and the dst LR
|
||||||
|
// starts at next-inst-pre. We have to take
|
||||||
|
// care in our move insertion to handle this
|
||||||
|
// like other inter-inst moves, i.e., at
|
||||||
|
// `Regular` priority, so it properly happens
|
||||||
|
// in parallel with other inter-LR moves.
|
||||||
|
//
|
||||||
|
// Why the progpoint between move and next
|
||||||
|
// inst, and not the progpoint between prev
|
||||||
|
// inst and move? Because a move can be the
|
||||||
|
// first inst in a block, but cannot be the
|
||||||
|
// last; so the following progpoint is always
|
||||||
|
// within the same block, while the previous
|
||||||
|
// one may be an inter-block point (and the
|
||||||
|
// After of the prev inst in a different
|
||||||
|
// block).
|
||||||
|
|
||||||
// Add to program-moves lists.
|
// Handle the def w.r.t. liveranges: trim the
|
||||||
self.prog_move_srcs.push((
|
// start of the range and mark it dead at this
|
||||||
(VRegIndex::new(src.vreg().vreg()), inst),
|
// point in our backward scan.
|
||||||
Allocation::none(),
|
let pos = ProgPoint::before(inst.next());
|
||||||
));
|
let mut dst_lr = vreg_ranges[dst.vreg().vreg()];
|
||||||
self.prog_move_dsts.push((
|
if !live.get(dst.vreg().vreg()) {
|
||||||
(VRegIndex::new(dst.vreg().vreg()), inst.next()),
|
let from = pos;
|
||||||
Allocation::none(),
|
let to = pos.next();
|
||||||
));
|
dst_lr = self.add_liverange_to_vreg(
|
||||||
self.stats.prog_moves += 1;
|
VRegIndex::new(dst.vreg().vreg()),
|
||||||
if src_is_dead_after_move {
|
CodeRange { from, to },
|
||||||
self.stats.prog_moves_dead_src += 1;
|
);
|
||||||
self.prog_move_merges.push((src_lr, dst_lr));
|
trace!(" -> invalid LR for def; created {:?}", dst_lr);
|
||||||
|
}
|
||||||
|
trace!(" -> has existing LR {:?}", dst_lr);
|
||||||
|
// Trim the LR to start here.
|
||||||
|
if self.ranges[dst_lr.index()].range.from
|
||||||
|
== self.cfginfo.block_entry[block.index()]
|
||||||
|
{
|
||||||
|
trace!(" -> started at block start; trimming to {:?}", pos);
|
||||||
|
self.ranges[dst_lr.index()].range.from = pos;
|
||||||
|
}
|
||||||
|
self.ranges[dst_lr.index()].set_flag(LiveRangeFlag::StartsAtDef);
|
||||||
|
live.set(dst.vreg().vreg(), false);
|
||||||
|
vreg_ranges[dst.vreg().vreg()] = LiveRangeIndex::invalid();
|
||||||
|
self.vreg_regs[dst.vreg().vreg()] = dst.vreg();
|
||||||
|
|
||||||
|
// Handle the use w.r.t. liveranges: make it live
|
||||||
|
// and create an initial LR back to the start of
|
||||||
|
// the block.
|
||||||
|
let pos = ProgPoint::after(inst);
|
||||||
|
let src_lr = if !live.get(src.vreg().vreg()) {
|
||||||
|
let range = CodeRange {
|
||||||
|
from: self.cfginfo.block_entry[block.index()],
|
||||||
|
to: pos.next(),
|
||||||
|
};
|
||||||
|
let src_lr = self.add_liverange_to_vreg(
|
||||||
|
VRegIndex::new(src.vreg().vreg()),
|
||||||
|
range,
|
||||||
|
);
|
||||||
|
vreg_ranges[src.vreg().vreg()] = src_lr;
|
||||||
|
src_lr
|
||||||
|
} else {
|
||||||
|
vreg_ranges[src.vreg().vreg()]
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!(" -> src LR {:?}", src_lr);
|
||||||
|
|
||||||
|
// Add to live-set.
|
||||||
|
let src_is_dead_after_move = !live.get(src.vreg().vreg());
|
||||||
|
live.set(src.vreg().vreg(), true);
|
||||||
|
|
||||||
|
// Add to program-moves lists.
|
||||||
|
self.prog_move_srcs.push((
|
||||||
|
(VRegIndex::new(src.vreg().vreg()), inst),
|
||||||
|
Allocation::none(),
|
||||||
|
));
|
||||||
|
self.prog_move_dsts.push((
|
||||||
|
(VRegIndex::new(dst.vreg().vreg()), inst.next()),
|
||||||
|
Allocation::none(),
|
||||||
|
));
|
||||||
|
self.stats.prog_moves += 1;
|
||||||
|
if src_is_dead_after_move {
|
||||||
|
self.stats.prog_moves_dead_src += 1;
|
||||||
|
self.prog_move_merges.push((src_lr, dst_lr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1089,8 +1084,8 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insert safepoint virtual stack uses, if needed.
|
// Insert safepoint virtual stack uses, if needed.
|
||||||
for vreg in self.func.reftype_vregs() {
|
for &vreg in self.func.reftype_vregs() {
|
||||||
if self.vregs[vreg.vreg()].is_pinned {
|
if self.func.is_pinned_vreg(vreg).is_some() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let vreg = VRegIndex::new(vreg.vreg());
|
let vreg = VRegIndex::new(vreg.vreg());
|
||||||
|
|||||||
@@ -234,12 +234,8 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
|
|
||||||
// If this is a pinned vreg, go ahead and add it to the
|
// If this is a pinned vreg, go ahead and add it to the
|
||||||
// commitment map, and avoid creating a bundle entirely.
|
// commitment map, and avoid creating a bundle entirely.
|
||||||
if self.vregs[vreg.index()].is_pinned {
|
if let Some(preg) = self.func.is_pinned_vreg(self.vreg_regs[vreg.index()]) {
|
||||||
for entry in &self.vregs[vreg.index()].ranges {
|
for entry in &self.vregs[vreg.index()].ranges {
|
||||||
let preg = self
|
|
||||||
.func
|
|
||||||
.is_pinned_vreg(self.vreg_regs[vreg.index()])
|
|
||||||
.unwrap();
|
|
||||||
let key = LiveRangeKey::from_range(&entry.range);
|
let key = LiveRangeKey::from_range(&entry.range);
|
||||||
self.pregs[preg.index()]
|
self.pregs[preg.index()]
|
||||||
.allocations
|
.allocations
|
||||||
@@ -308,8 +304,8 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
if let OperandConstraint::Reuse(reuse_idx) = op.constraint() {
|
if let OperandConstraint::Reuse(reuse_idx) = op.constraint() {
|
||||||
let src_vreg = op.vreg();
|
let src_vreg = op.vreg();
|
||||||
let dst_vreg = self.func.inst_operands(inst)[reuse_idx].vreg();
|
let dst_vreg = self.func.inst_operands(inst)[reuse_idx].vreg();
|
||||||
if self.vregs[src_vreg.vreg()].is_pinned
|
if self.func.is_pinned_vreg(src_vreg).is_some()
|
||||||
|| self.vregs[dst_vreg.vreg()].is_pinned
|
|| self.func.is_pinned_vreg(dst_vreg).is_some()
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -367,21 +363,21 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
|
|
||||||
let dst_vreg = self.vreg_regs[self.ranges[dst.index()].vreg.index()];
|
let dst_vreg = self.vreg_regs[self.ranges[dst.index()].vreg.index()];
|
||||||
let src_vreg = self.vreg_regs[self.ranges[src.index()].vreg.index()];
|
let src_vreg = self.vreg_regs[self.ranges[src.index()].vreg.index()];
|
||||||
if self.vregs[src_vreg.vreg()].is_pinned && self.vregs[dst_vreg.vreg()].is_pinned {
|
if self.func.is_pinned_vreg(src_vreg).is_some()
|
||||||
|
&& self.func.is_pinned_vreg(dst_vreg).is_some()
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if self.vregs[src_vreg.vreg()].is_pinned {
|
if let Some(preg) = self.func.is_pinned_vreg(src_vreg) {
|
||||||
let dest_bundle = self.ranges[dst.index()].bundle;
|
let dest_bundle = self.ranges[dst.index()].bundle;
|
||||||
let spillset = self.bundles[dest_bundle.index()].spillset;
|
let spillset = self.bundles[dest_bundle.index()].spillset;
|
||||||
self.spillsets[spillset.index()].reg_hint =
|
self.spillsets[spillset.index()].reg_hint = preg;
|
||||||
self.func.is_pinned_vreg(src_vreg).unwrap();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if self.vregs[dst_vreg.vreg()].is_pinned {
|
if let Some(preg) = self.func.is_pinned_vreg(dst_vreg) {
|
||||||
let src_bundle = self.ranges[src.index()].bundle;
|
let src_bundle = self.ranges[src.index()].bundle;
|
||||||
let spillset = self.bundles[src_bundle.index()].spillset;
|
let spillset = self.bundles[src_bundle.index()].spillset;
|
||||||
self.spillsets[spillset.index()].reg_hint =
|
self.spillsets[spillset.index()].reg_hint = preg;
|
||||||
self.func.is_pinned_vreg(dst_vreg).unwrap();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -209,11 +209,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
for vreg in 0..self.vregs.len() {
|
for vreg in 0..self.vregs.len() {
|
||||||
let vreg = VRegIndex::new(vreg);
|
let vreg = VRegIndex::new(vreg);
|
||||||
|
|
||||||
let pinned_alloc = if self.vregs[vreg.index()].is_pinned {
|
let pinned_alloc = self.func.is_pinned_vreg(self.vreg_regs[vreg.index()]);
|
||||||
self.func.is_pinned_vreg(self.vreg_regs[vreg.index()])
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// For each range in each vreg, insert moves or
|
// For each range in each vreg, insert moves or
|
||||||
// half-moves. We also scan over `blockparam_ins` and
|
// half-moves. We also scan over `blockparam_ins` and
|
||||||
|
|||||||
@@ -981,11 +981,6 @@ pub trait Function {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a list of all pinned vregs.
|
|
||||||
fn pinned_vregs(&self) -> &[VReg] {
|
|
||||||
&[]
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------
|
// --------------
|
||||||
// Spills/reloads
|
// Spills/reloads
|
||||||
// --------------
|
// --------------
|
||||||
|
|||||||
Reference in New Issue
Block a user