mod access;
mod branch;
mod defs;
mod packs;
mod regs;
use crate::cfg::BlockRef;
use crate::cfg::{DefId, SsaValue};
use crate::hir::common::{
HirBinaryExpr, HirBinaryOpKind, HirCallExpr, HirCapture, HirClosureExpr, HirExpr, HirGlobalRef,
HirLValue, HirTableAccess, HirUnaryExpr, HirUnaryOpKind, UpvalueId,
};
use crate::parser::RawLiteralConst;
use crate::transformer::{
AccessBase, AccessKey, BinaryOpKind, BranchCond, BranchOperands, BranchPredicate, CallKind,
CondOperand, ConstRef, InstrRef, LowInstr, LoweredProto, MethodNameHint, Reg, ResultPack,
UnaryOpKind, ValueOperand,
};
pub(super) use self::access::{
expr_for_const, expr_for_value_operand, lower_table_access_expr, lower_table_access_target,
};
use self::access::{
expr_for_value_operand_inline, lower_table_access_expr_inline,
lower_table_access_expr_single_eval,
};
pub(super) use self::branch::{
lower_binary_op, lower_branch_cond, lower_branch_subject, lower_branch_subject_inline,
lower_branch_subject_single_eval, lower_unary_op,
};
pub(super) use self::defs::{
expr_for_dup_safe_fixed_def, expr_for_fixed_def, expr_for_fixed_def_single_eval,
is_multiret_results,
};
use self::packs::lower_value_pack_single_eval;
pub(super) use self::packs::{lower_value_pack, lower_value_pack_components};
pub(super) use self::regs::{
expr_for_closure_capture, expr_for_reg_at_block_entry, expr_for_reg_at_block_exit,
expr_for_reg_use,
};
use self::regs::{expr_for_reg_use_inline, expr_for_reg_use_single_eval};
use super::ProtoLowering;
use super::helpers::{concat_expr, decode_raw_string, unresolved_expr};
fn resolve_open_pack_tail(
lowering: &ProtoLowering<'_>,
instr_ref: InstrRef,
start_reg: Reg,
) -> Option<(Reg, HirExpr)> {
let defs = lowering.dataflow.open_use_defs_at(instr_ref);
if defs.len() == 1 {
let def = defs
.iter()
.next()
.expect("len checked above, exactly one reaching open def exists");
let open_def = lowering.dataflow.open_defs.get(def.index())?;
if open_def.start_reg.index() < start_reg.index() {
return None;
}
return Some((
open_def.start_reg,
HirExpr::TempRef(lowering.bindings.open_temps[def.index()]),
));
}
if defs.is_empty()
&& lowering.proto.signature.is_vararg
&& start_reg.index() == usize::from(lowering.proto.signature.num_params)
{
return Some((start_reg, HirExpr::VarArg));
}
None
}
fn resolve_open_pack_tail_single_eval(
lowering: &ProtoLowering<'_>,
instr_ref: InstrRef,
start_reg: Reg,
) -> Option<(Reg, HirExpr)> {
let defs = lowering.dataflow.open_use_defs_at(instr_ref);
if defs.len() == 1 {
let def = defs
.iter()
.next()
.expect("len checked above, exactly one reaching open def exists");
let open_def = lowering.dataflow.open_defs.get(def.index())?;
if open_def.start_reg.index() < start_reg.index() {
return None;
}
let expr = expr_for_open_def_single_eval(lowering, *def)
.unwrap_or_else(|| HirExpr::TempRef(lowering.bindings.open_temps[def.index()]));
return Some((open_def.start_reg, expr));
}
if defs.is_empty()
&& lowering.proto.signature.is_vararg
&& start_reg.index() == usize::from(lowering.proto.signature.num_params)
{
return Some((start_reg, HirExpr::VarArg));
}
None
}
fn expr_for_open_def_single_eval(
lowering: &ProtoLowering<'_>,
open_def_id: crate::cfg::OpenDefId,
) -> Option<HirExpr> {
let open_def = lowering.dataflow.open_defs.get(open_def_id.index())?;
let instr = lowering.proto.instrs.get(open_def.instr.index())?;
match instr {
LowInstr::Call(call) if matches!(call.results, ResultPack::Open(_)) => {
Some(HirExpr::Call(Box::new(HirCallExpr {
callee: expr_for_reg_use_single_eval(
lowering,
open_def.block,
open_def.instr,
call.callee,
),
args: lower_value_pack_single_eval(
lowering,
open_def.block,
open_def.instr,
call.args,
),
multiret: true,
method: matches!(call.kind, CallKind::Method),
method_name: lower_method_name(lowering.proto, call.method_name),
})))
}
LowInstr::VarArg(vararg) if matches!(vararg.results, ResultPack::Open(_)) => {
Some(HirExpr::VarArg)
}
_ => None,
}
}
fn entry_reg_expr(lowering: &ProtoLowering<'_>, reg: Reg) -> HirExpr {
if reg.index() < lowering.bindings.params.len() {
HirExpr::ParamRef(lowering.bindings.params[reg.index()])
} else if let Some(local) = lowering.bindings.entry_local_regs.get(®) {
HirExpr::LocalRef(*local)
} else {
unresolved_expr(format!("entry-reg r{}", reg.index()))
}
}
fn loop_local_for_reg(
lowering: &ProtoLowering<'_>,
block: BlockRef,
reg: Reg,
) -> Option<crate::hir::common::LocalId> {
lowering
.bindings
.block_local_regs
.get(&block)
.and_then(|locals| locals.get(®))
.copied()
}
fn fixed_def_for_reg(lowering: &ProtoLowering<'_>, instr_ref: InstrRef, reg: Reg) -> Option<DefId> {
lowering.dataflow.instr_def_for_reg(instr_ref, reg)
}
fn reg_in_range(range: crate::transformer::RegRange, reg: Reg) -> bool {
reg.index() >= range.start.index() && reg.index() < range.start.index() + range.len
}
pub(super) fn lower_method_name(
proto: &LoweredProto,
method_name: Option<MethodNameHint>,
) -> Option<String> {
let const_ref = method_name?.const_ref;
match proto.constants.common.literals.get(const_ref.index()) {
Some(RawLiteralConst::String(value)) => Some(decode_raw_string(value)),
_ => None,
}
}