use super::*;
use crate::structure::SsaValue;
impl<'a, 'b> StructuredBodyLowerer<'a, 'b> {
pub(super) fn loop_exit_state_preheader_init(
&self,
preheader: Option<BlockRef>,
value: &LoopValueMerge,
inside_exit_blocks: &BTreeSet<BlockRef>,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirExpr> {
if !loop_value_incoming_all_within_blocks(value, inside_exit_blocks) {
return None;
}
let preheader = preheader?;
let mut expr = expr_for_reg_at_block_exit(self.lowering, preheader, value.reg);
rewrite_expr_temps(&mut expr, &temp_expr_overrides(target_overrides));
Some(expr)
}
pub(super) fn loop_entry_expr(
&self,
preheader: Option<BlockRef>,
value: &LoopValueMerge,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirExpr> {
match preheader {
Some(preheader) => {
let incoming = value.outside_arm.incoming_for_pred(preheader)?;
self.loop_incoming_expr(
preheader,
value.reg,
incoming.defs.iter().copied(),
target_overrides,
)
}
None => self.multi_entry_loop_entry_expr(value, target_overrides),
}
}
pub(super) fn multi_entry_loop_entry_expr(
&self,
value: &LoopValueMerge,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirExpr> {
self.uniform_loop_incoming_expr(
value.reg,
value.outside_arm.incomings.iter(),
target_overrides,
)
}
pub(super) fn loop_exit_entry_expr_with_inside_blocks(
&self,
value: &LoopValueMerge,
inside_blocks: &BTreeSet<BlockRef>,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirExpr> {
let outside_incomings = value
.inside_arm
.incomings
.iter()
.chain(value.outside_arm.incomings.iter())
.filter(|incoming| {
incoming
.pred
.is_none_or(|pred| !inside_blocks.contains(&pred))
});
self.uniform_loop_incoming_expr(value.reg, outside_incomings, target_overrides)
}
fn uniform_loop_incoming_expr<'c>(
&self,
reg: Reg,
incomings: impl IntoIterator<Item = &'c crate::structure::LoopValueIncoming>,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirExpr> {
let raw_target_overrides = BTreeMap::new();
uniform_mapped_value(incomings, |incoming| match incoming.pred {
Some(pred) => self
.loop_incoming_expr_without_carried_override(
pred,
reg,
incoming.defs.iter().copied(),
&raw_target_overrides,
)
.or_else(|| {
self.loop_incoming_expr_without_carried_override(
pred,
reg,
incoming.defs.iter().copied(),
target_overrides,
)
})
.or_else(|| {
self.loop_incoming_expr(
pred,
reg,
incoming.defs.iter().copied(),
target_overrides,
)
}),
None => Some(self.loop_entry_initial_expr(reg)),
})
}
fn uniform_loop_incoming_lvalue<'c>(
&self,
reg: Reg,
incomings: impl IntoIterator<Item = &'c crate::structure::LoopValueIncoming>,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirLValue> {
let raw_target_overrides = BTreeMap::new();
uniform_mapped_value(incomings, |incoming| match incoming.pred {
Some(pred) => self
.loop_incoming_lvalue_without_carried_override(
pred,
reg,
incoming.defs.iter().copied(),
&raw_target_overrides,
)
.or_else(|| {
self.loop_incoming_lvalue_without_carried_override(
pred,
reg,
incoming.defs.iter().copied(),
target_overrides,
)
})
.or_else(|| {
self.loop_incoming_lvalue(
pred,
reg,
incoming.defs.iter().copied(),
target_overrides,
)
}),
None => expr_as_lvalue(&self.loop_entry_initial_expr(reg)),
})
}
pub(super) fn loop_entry_initial_expr(&self, reg: Reg) -> HirExpr {
if reg.index() < self.lowering.bindings.params.len() {
expr_for_entry_reg(self.lowering, reg)
} else if let Some(local) = self.lowering.bindings.entry_local_regs.get(®) {
HirExpr::LocalRef(*local)
} else {
HirExpr::Nil
}
}
fn loop_incoming_expr(
&self,
pred: BlockRef,
reg: Reg,
defs: impl IntoIterator<Item = crate::structure::DefId>,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirExpr> {
self.loop_incoming_expr_with_carried_override(pred, reg, defs, target_overrides, true)
}
fn loop_incoming_expr_without_carried_override(
&self,
pred: BlockRef,
reg: Reg,
defs: impl IntoIterator<Item = crate::structure::DefId>,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirExpr> {
self.loop_incoming_expr_with_carried_override(pred, reg, defs, target_overrides, false)
}
fn loop_incoming_expr_with_carried_override(
&self,
pred: BlockRef,
reg: Reg,
defs: impl IntoIterator<Item = crate::structure::DefId>,
target_overrides: &BTreeMap<TempId, HirLValue>,
allow_carried_override: bool,
) -> Option<HirExpr> {
let defs = defs.into_iter().collect::<Vec<_>>();
if allow_carried_override && let Some(expr) = self.overrides.carried_entry_expr(pred, reg) {
return Some(expr.clone());
}
if let Some(expr) = shared_expr_for_defs(
&self.lowering.bindings.fixed_temps,
defs.iter().copied(),
target_overrides,
) {
return Some(expr);
}
if let Some(expr) = single_fixed_def_expr(self.lowering, defs.iter().copied()) {
return Some(expr);
}
if defs.len() > 1 {
let mut expr = expr_for_reg_at_block_exit(self.lowering, pred, reg);
rewrite_expr_temps(&mut expr, &temp_expr_overrides(target_overrides));
return Some(expr);
}
if let Some(expr) = self.reaching_phi_override_expr(pred, reg, target_overrides) {
return Some(expr);
}
if defs.is_empty() && self.pred_has_no_reaching_value(pred, reg) {
return Some(HirExpr::Nil);
}
None
}
fn pred_has_no_reaching_value(&self, pred: BlockRef, reg: Reg) -> bool {
let range = self.lowering.cfg.blocks[pred.index()].instrs;
if range.is_empty() {
return false;
}
let values = self.lowering.dataflow.reaching_values_at(range.start);
let entry_empty = values.get(reg).is_none_or(|set| set.is_empty());
if !entry_empty {
return false;
}
!(range.start.index()..range.end()).any(|instr_index| {
let effect = &self.lowering.dataflow.instr_effects[instr_index];
effect.fixed_must_defs.contains(®) || effect.fixed_may_defs.contains(®)
})
}
fn reaching_phi_override_expr(
&self,
pred: BlockRef,
reg: Reg,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirExpr> {
let target = self.reaching_phi_target_override(pred, reg, target_overrides)?;
lvalue_as_expr(target)
}
pub(super) fn inherited_exit_target_for_value(
&self,
value: &LoopValueMerge,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirLValue> {
let fixed_temps = &self.lowering.bindings.fixed_temps;
let combined_defs = value
.inside_arm
.defs()
.chain(value.outside_arm.defs())
.collect::<Vec<_>>();
if combined_defs.is_empty() {
return None;
}
shared_lvalue_for_defs(fixed_temps, combined_defs, target_overrides)
}
pub(super) fn loop_state_target(
&self,
candidate: &LoopCandidate,
exit: BlockRef,
reg: Reg,
temp: TempId,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> HirLValue {
if let Some(target) = target_overrides
.get(&temp)
.filter(|target| lvalue_as_expr(target).is_some())
{
return target.clone();
}
if candidate.preheader.is_none()
&& let Some(target) =
self.multi_entry_loop_entry_lvalue(candidate, reg, target_overrides)
{
return target;
}
if let Some(target) =
self.uniform_loop_header_target_override(candidate, reg, target_overrides)
{
return target;
}
if let Some(target) =
self.uniform_loop_exit_target_override(candidate, exit, reg, target_overrides)
{
return target;
}
if let Some(preheader) = unique_loop_preheader(candidate)
&& let Some(target) =
self.reaching_phi_lvalue_override(preheader, reg, target_overrides)
{
return target;
}
HirLValue::Temp(temp)
}
fn multi_entry_loop_entry_lvalue(
&self,
candidate: &LoopCandidate,
reg: Reg,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirLValue> {
let value = Self::header_value_for_reg(candidate, reg)?;
self.uniform_loop_incoming_lvalue(reg, value.outside_arm.incomings.iter(), target_overrides)
}
fn loop_incoming_lvalue(
&self,
pred: BlockRef,
reg: Reg,
defs: impl IntoIterator<Item = crate::structure::DefId>,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirLValue> {
self.loop_incoming_lvalue_with_carried_override(pred, reg, defs, target_overrides, true)
}
fn loop_incoming_lvalue_without_carried_override(
&self,
pred: BlockRef,
reg: Reg,
defs: impl IntoIterator<Item = crate::structure::DefId>,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirLValue> {
self.loop_incoming_lvalue_with_carried_override(pred, reg, defs, target_overrides, false)
}
fn loop_incoming_lvalue_with_carried_override(
&self,
pred: BlockRef,
reg: Reg,
defs: impl IntoIterator<Item = crate::structure::DefId>,
target_overrides: &BTreeMap<TempId, HirLValue>,
allow_carried_override: bool,
) -> Option<HirLValue> {
let defs = defs.into_iter().collect::<Vec<_>>();
if allow_carried_override
&& let Some(target) = self
.overrides
.carried_entry_expr(pred, reg)
.and_then(expr_as_lvalue)
{
return Some(target);
}
if let Some(target) = shared_lvalue_for_defs(
&self.lowering.bindings.fixed_temps,
defs.iter().copied(),
target_overrides,
) {
return Some(target);
}
if let Some(target) = single_fixed_def_lvalue(self.lowering, defs.iter().copied()) {
return Some(target);
}
if let Some(target) = self.reaching_phi_lvalue_override(pred, reg, target_overrides) {
return Some(target);
}
None
}
fn reaching_phi_lvalue_override(
&self,
block: BlockRef,
reg: Reg,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirLValue> {
self.reaching_phi_target_override(block, reg, target_overrides)
.cloned()
}
fn reaching_phi_target_override<'c>(
&self,
block: BlockRef,
reg: Reg,
target_overrides: &'c BTreeMap<TempId, HirLValue>,
) -> Option<&'c HirLValue> {
let first_instr = self.lowering.cfg.blocks[block.index()].instrs.start;
let reaching = self.lowering.dataflow.reaching_values_at(first_instr);
let values = reaching.get(reg)?;
let mut phi_ids = values.iter().filter_map(|v| match v {
SsaValue::Phi(phi_id) => Some(phi_id),
SsaValue::Def(_) => None,
});
let phi_id = phi_ids.next()?;
if phi_ids.next().is_some() {
return None;
}
let temp = *self.lowering.bindings.phi_temps.get(phi_id.index())?;
let lvalue = target_overrides.get(&temp)?;
lvalue_as_expr(lvalue)?;
Some(lvalue)
}
pub(super) fn exit_value_is_owned_by_inherited_state(
&self,
value: &LoopValueMerge,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> bool {
let phi_temp = self.lowering.bindings.phi_temps[value.phi_id.index()];
if target_overrides.contains_key(&phi_temp) {
return true;
}
for def in value.inside_arm.defs() {
let def_temp = self.lowering.bindings.fixed_temps[def.index()];
if target_overrides.contains_key(&def_temp) {
return true;
}
}
false
}
fn uniform_loop_header_target_override(
&self,
candidate: &LoopCandidate,
reg: Reg,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirLValue> {
let value = Self::header_value_for_reg(candidate, reg)?;
self.shared_loop_inside_target(&value.inside_arm, target_overrides)
}
fn uniform_loop_exit_target_override(
&self,
candidate: &LoopCandidate,
exit: BlockRef,
reg: Reg,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirLValue> {
if let Some(value) = Self::exit_value_for_reg(candidate, exit, reg) {
if !loop_value_has_inside_and_outside_incoming(value) {
return None;
}
if let Some(target) =
self.shared_loop_inside_target(&value.inside_arm, target_overrides)
{
return Some(target);
}
}
None
}
fn shared_loop_inside_target(
&self,
arm: &LoopValueArm,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<HirLValue> {
shared_lvalue_for_defs(
&self.lowering.bindings.fixed_temps,
arm.defs(),
target_overrides,
)
}
}