use super::*;
pub(crate) fn expr_for_reg_use(
lowering: &ProtoLowering<'_>,
block: BlockRef,
instr_ref: InstrRef,
reg: Reg,
) -> HirExpr {
if let Some(local) = loop_local_for_reg(lowering, block, reg) {
return HirExpr::LocalRef(local);
}
let Some(values) = lowering.dataflow.use_values_at(instr_ref).get(reg) else {
return entry_reg_expr(lowering, reg);
};
if values.is_empty() {
return entry_reg_expr(lowering, reg);
}
if values.len() == 1 {
let value = values
.iter()
.next()
.expect("len checked above, exactly one SSA-like value exists");
return match value {
SsaValue::Def(def) => HirExpr::TempRef(lowering.bindings.fixed_temps[def.index()]),
SsaValue::Phi(phi) => HirExpr::TempRef(lowering.bindings.phi_temps[phi.index()]),
};
}
unresolved_expr(format!(
"multi-value use r{} @{}",
reg.index(),
instr_ref.index()
))
}
pub(crate) fn expr_for_closure_capture(
lowering: &ProtoLowering<'_>,
block: BlockRef,
instr_ref: InstrRef,
dst: Reg,
source: crate::transformer::CaptureSource,
) -> HirExpr {
match source {
crate::transformer::CaptureSource::Reg(reg) if reg == dst => {
let self_temp = lowering.bindings.instr_fixed_defs[instr_ref.index()]
.first()
.copied()
.expect("closure writes exactly one fixed target");
HirExpr::TempRef(self_temp)
}
crate::transformer::CaptureSource::Reg(reg) => {
let expr = expr_for_reg_use(lowering, block, instr_ref, reg);
let should_forward = match &expr {
HirExpr::Unresolved(_) => true,
HirExpr::TempRef(_) => is_loadnil_def(lowering, instr_ref, reg),
_ => false,
};
if should_forward
&& let Some(forward_expr) = forward_def_in_block(lowering, block, instr_ref, reg)
{
return forward_expr;
}
expr
}
crate::transformer::CaptureSource::Upvalue(upvalue) => {
HirExpr::UpvalueRef(UpvalueId(upvalue.index()))
}
}
}
fn is_loadnil_def(lowering: &ProtoLowering<'_>, instr_ref: InstrRef, reg: Reg) -> bool {
let Some(values) = lowering.dataflow.use_values_at(instr_ref).get(reg) else {
return false;
};
if values.len() != 1 {
return false;
}
let value = values.iter().next().unwrap();
let SsaValue::Def(def) = value else {
return false;
};
let def_instr = lowering.dataflow.def_instr(def);
matches!(
&lowering.proto.instrs[def_instr.index()],
crate::transformer::LowInstr::LoadNil(_)
)
}
fn forward_def_in_block(
lowering: &ProtoLowering<'_>,
block: BlockRef,
instr_ref: InstrRef,
reg: Reg,
) -> Option<HirExpr> {
let block_range = lowering.cfg.blocks[block.index()].instrs;
let mut last_def_temp = None;
for idx in (instr_ref.index() + 1)..block_range.end() {
for def in &lowering.dataflow.instr_defs[idx] {
if lowering.dataflow.def_reg(*def) == reg {
last_def_temp = Some(lowering.bindings.fixed_temps[def.index()]);
}
}
}
last_def_temp.map(HirExpr::TempRef)
}
pub(crate) fn expr_for_reg_at_block_entry(
lowering: &ProtoLowering<'_>,
block: BlockRef,
reg: Reg,
) -> HirExpr {
if let Some(local) = loop_local_for_reg(lowering, block, reg) {
return HirExpr::LocalRef(local);
}
let range = lowering.cfg.blocks[block.index()].instrs;
if range.is_empty() {
return entry_reg_expr(lowering, reg);
}
let Some(values) = lowering.dataflow.reaching_values_at(range.start).get(reg) else {
return entry_reg_expr(lowering, reg);
};
if values.is_empty() {
return entry_reg_expr(lowering, reg);
}
if values.len() == 1 {
let value = values
.iter()
.next()
.expect("len checked above, exactly one SSA-like value exists");
return match value {
SsaValue::Def(def) => HirExpr::TempRef(lowering.bindings.fixed_temps[def.index()]),
SsaValue::Phi(phi) => HirExpr::TempRef(lowering.bindings.phi_temps[phi.index()]),
};
}
unresolved_expr(format!(
"multi-value entry r{} block#{}",
reg.index(),
block.index()
))
}
pub(crate) fn expr_for_reg_at_block_exit(
lowering: &ProtoLowering<'_>,
block: BlockRef,
reg: Reg,
) -> HirExpr {
if let Some(local) = loop_local_for_reg(lowering, block, reg) {
return HirExpr::LocalRef(local);
}
let range = lowering.cfg.blocks[block.index()].instrs;
let Some(last_instr_ref) = range.last() else {
return entry_reg_expr(lowering, reg);
};
let effect = &lowering.dataflow.instr_effects[last_instr_ref.index()];
if effect.fixed_must_defs.contains(®) {
let Some(def) = fixed_def_for_reg(lowering, last_instr_ref, reg) else {
return unresolved_expr(format!(
"missing block-exit def r{} block#{}",
reg.index(),
block.index()
));
};
return HirExpr::TempRef(lowering.bindings.fixed_temps[def.index()]);
}
let mut values = lowering
.dataflow
.reaching_values_at(last_instr_ref)
.get(reg)
.map(|values| values.to_compact_set())
.unwrap_or_default();
if effect.fixed_may_defs.contains(®) {
let Some(def) = fixed_def_for_reg(lowering, last_instr_ref, reg) else {
return unresolved_expr(format!(
"missing block-exit may-def r{} block#{}",
reg.index(),
block.index()
));
};
values.insert(SsaValue::Def(def));
}
if values.is_empty() {
return entry_reg_expr(lowering, reg);
}
if values.len() == 1 {
let value = values
.iter()
.next()
.expect("len checked above, exactly one SSA-like value exists");
return match value {
SsaValue::Def(def) => HirExpr::TempRef(lowering.bindings.fixed_temps[def.index()]),
SsaValue::Phi(phi) => HirExpr::TempRef(lowering.bindings.phi_temps[phi.index()]),
};
}
unresolved_expr(format!(
"multi-value exit r{} block#{}",
reg.index(),
block.index()
))
}
pub(crate) fn expr_for_reg_use_inline(
lowering: &ProtoLowering<'_>,
block: BlockRef,
instr_ref: InstrRef,
reg: Reg,
) -> HirExpr {
if let Some(local) = loop_local_for_reg(lowering, block, reg) {
return HirExpr::LocalRef(local);
}
let Some(values) = lowering.dataflow.use_values_at(instr_ref).get(reg) else {
return entry_reg_expr(lowering, reg);
};
if values.is_empty() {
return entry_reg_expr(lowering, reg);
}
if values.len() == 1 {
let value = values
.iter()
.next()
.expect("len checked above, exactly one SSA-like value exists");
return match value {
SsaValue::Def(def) => expr_for_dup_safe_fixed_def(lowering, def)
.unwrap_or_else(|| HirExpr::TempRef(lowering.bindings.fixed_temps[def.index()])),
SsaValue::Phi(phi) => HirExpr::TempRef(lowering.bindings.phi_temps[phi.index()]),
};
}
unresolved_expr(format!(
"multi-value use r{} @{}",
reg.index(),
instr_ref.index()
))
}
pub(crate) fn expr_for_reg_use_single_eval(
lowering: &ProtoLowering<'_>,
block: BlockRef,
instr_ref: InstrRef,
reg: Reg,
) -> HirExpr {
if let Some(local) = loop_local_for_reg(lowering, block, reg) {
return HirExpr::LocalRef(local);
}
let Some(values) = lowering.dataflow.use_values_at(instr_ref).get(reg) else {
return entry_reg_expr(lowering, reg);
};
if values.is_empty() {
return entry_reg_expr(lowering, reg);
}
if values.len() == 1 {
let value = values
.iter()
.next()
.expect("len checked above, exactly one SSA-like value exists");
return match value {
SsaValue::Def(def) => expr_for_fixed_def_single_eval(lowering, def)
.unwrap_or_else(|| HirExpr::TempRef(lowering.bindings.fixed_temps[def.index()])),
SsaValue::Phi(phi) => HirExpr::TempRef(lowering.bindings.phi_temps[phi.index()]),
};
}
unresolved_expr(format!(
"multi-value use r{} @{}",
reg.index(),
instr_ref.index()
))
}