use super::*;
use crate::native::ir::{LlGep, LlType, LlValue};
use crate::native::parse::{parse_phi, parse_switch, parse_typed_value, strip_comment, LlSwitch};
use std::collections::{HashMap, HashSet};
pub(in crate::native) fn ret_emit(terminator_text: &str) -> RetEmit {
let cleaned = strip_comment(terminator_text).trim();
let Some(rest) = cleaned.strip_prefix("ret ") else {
return RetEmit::FromText;
};
if rest.trim() == "void" {
return RetEmit::Void;
}
match parse_typed_value(rest) {
Ok(tv) => RetEmit::Value(tv),
Err(_) => RetEmit::FromText,
}
}
pub(in crate::native) fn switch_emit(terminator_text: &str) -> Option<LlSwitch> {
let cleaned = strip_comment(terminator_text).trim();
if !cleaned.starts_with("switch ") {
return None;
}
parse_switch(cleaned).ok()
}
pub(in crate::native) fn phi_incoming_of(line: &str) -> Option<(LlType, Vec<(LlValue, String)>)> {
let rhs = strip_comment(line).trim().split_once(" = ")?.1.trim();
let opcode = rhs.split_whitespace().next().unwrap_or("");
let rest = rhs[opcode.len()..].trim_start();
parse_phi(rest).ok()
}
pub(in crate::native) fn collect_forward_geps(blocks: &[TirBlock]) -> HashMap<String, LlGep> {
let mut geps = HashMap::new();
for block in blocks {
for inst in &block.insts {
if let (Some(name), Some(gep)) = (&inst.result, &inst.gep) {
geps.insert(name.clone(), gep.clone());
}
}
}
geps
}
pub(in crate::native) fn collect_pointer_phi_sets(
blocks: &[TirBlock],
) -> (HashSet<String>, HashSet<String>) {
let mut results = HashSet::new();
let mut incoming = HashSet::new();
for block in blocks {
for inst in &block.insts {
if inst.opcode != "phi" || !matches!(inst.result_ty, Some(LlType::Ptr(_))) {
continue;
}
let Some(name) = &inst.result else { continue };
results.insert(name.clone());
for operand in &inst.operands {
if let TirOperand::Value { name, .. } = operand {
incoming.insert(name.clone());
}
}
}
}
(results, incoming)
}
#[cfg(test)]
pub(in crate::native) fn build(
body: &[String],
entry_label: &str,
named_types: &HashMap<String, LlType>,
) -> Result<TirFunction, String> {
let mut blocks: Vec<TirBlock> = Vec::new();
let mut value_types: HashMap<String, LlType> = HashMap::new();
let mut pointer_pointees: HashMap<String, LlType> = HashMap::new();
let mut cur_label = entry_label.to_string();
let mut cur_insts: Vec<TirInst> = Vec::new();
let mut cur_term: Option<TirTerminator> = None;
let mut cur_term_text = String::new();
let flush = |label: &mut String,
insts: &mut Vec<TirInst>,
term: &mut Option<TirTerminator>,
term_text: &mut String,
blocks: &mut Vec<TirBlock>|
-> Result<(), String> {
if insts.is_empty() && term.is_none() && blocks.is_empty() {
return Ok(()); }
let terminator = term
.take()
.ok_or_else(|| format!("native tir: block {label} has no terminator"))?;
let terminator_text = std::mem::take(term_text);
blocks.push(TirBlock {
label: std::mem::take(label),
insts: std::mem::take(insts),
terminator,
ret: ret_emit(&terminator_text),
switch: switch_emit(&terminator_text),
});
Ok(())
};
for raw in body {
let line = raw.trim();
if line.is_empty() {
continue;
}
if let Some(label) = parse_block_label(line) {
if cur_term.is_some() || !cur_insts.is_empty() {
flush(
&mut cur_label,
&mut cur_insts,
&mut cur_term,
&mut cur_term_text,
&mut blocks,
)?;
}
cur_label = label;
continue;
}
if let Some(term) = parse_terminator(line) {
cur_term = Some(term);
cur_term_text = line.to_string();
continue;
}
push_inst_line(
line,
named_types,
&mut value_types,
&mut pointer_pointees,
&mut cur_insts,
);
}
flush(
&mut cur_label,
&mut cur_insts,
&mut cur_term,
&mut cur_term_text,
&mut blocks,
)?;
let (use_pointees, _, byte_view_pointers) = infer_use_pointees(&blocks);
let (pointer_phi_results, pointer_phi_incoming) = collect_pointer_phi_sets(&blocks);
let forward_geps = collect_forward_geps(&blocks);
Ok(TirFunction {
blocks,
value_types,
pointer_pointees,
use_pointees,
byte_view_pointers,
pointer_phi_results,
pointer_phi_incoming,
forward_geps,
})
}
pub(in crate::native) fn build_from_blocks(
blocks: &[crate::native::cfg::BodyBlock],
) -> Result<TirFunction, String> {
let mut tir_blocks: Vec<TirBlock> = Vec::new();
let mut value_types: HashMap<String, LlType> = HashMap::new();
let mut pointer_pointees: HashMap<String, LlType> = HashMap::new();
for bb in blocks {
let Some(carrier) = &bb.typed else {
return Err(format!(
"native tir: block {} role={:?} has no typed carrier (unpopulated synthesis site)",
bb.name, bb.role
));
};
for inst in &carrier.insts {
if let (Some(name), Some(ty)) = (&inst.result, &inst.result_ty) {
value_types.insert(name.clone(), ty.clone());
}
if let (Some(name), Some(pointee)) = (&inst.result, &inst.pointer_pointee) {
pointer_pointees.insert(name.clone(), pointee.clone());
}
}
tir_blocks.push(carrier.clone());
}
let (use_pointees, _, byte_view_pointers) = infer_use_pointees(&tir_blocks);
let (pointer_phi_results, pointer_phi_incoming) = collect_pointer_phi_sets(&tir_blocks);
let forward_geps = collect_forward_geps(&tir_blocks);
Ok(TirFunction {
blocks: tir_blocks,
value_types,
pointer_pointees,
use_pointees,
byte_view_pointers,
pointer_phi_results,
pointer_phi_incoming,
forward_geps,
})
}
pub(in crate::native) fn lower_block(
name: &str,
lines: &[String],
named_types: &HashMap<String, LlType>,
value_types: &mut HashMap<String, LlType>,
pointer_pointees: &mut HashMap<String, LlType>,
) -> Result<TirBlock, String> {
let mut cur_insts: Vec<TirInst> = Vec::new();
let mut cur_term: Option<TirTerminator> = None;
let mut cur_term_text = String::new();
for raw in lines {
let line = raw.trim();
if line.is_empty() {
continue;
}
if let Some(term) = parse_terminator(line) {
cur_term = Some(term);
cur_term_text = line.to_string();
continue;
}
push_inst_line(
line,
named_types,
value_types,
pointer_pointees,
&mut cur_insts,
);
}
let terminator =
cur_term.ok_or_else(|| format!("native tir: block {name} has no terminator"))?;
Ok(TirBlock {
label: name.to_string(),
insts: cur_insts,
terminator,
ret: ret_emit(&cur_term_text),
switch: switch_emit(&cur_term_text),
})
}
pub(in crate::native) fn lower_block_carrier(
name: &str,
lines: &[String],
named_types: &HashMap<String, LlType>,
) -> Option<TirBlock> {
let mut value_types = HashMap::new();
let mut pointer_pointees = HashMap::new();
lower_block(
name,
lines,
named_types,
&mut value_types,
&mut pointer_pointees,
)
.ok()
}
pub(in crate::native) fn lower_block_carrier_with_prefix(
name: &str,
prefix: &TirBlock,
tail_lines: &[String],
) -> Option<TirBlock> {
let tail = lower_block_carrier(name, tail_lines, &HashMap::new())?;
let mut insts = prefix.insts.clone();
insts.extend(tail.insts);
Some(TirBlock {
label: name.to_string(),
insts,
terminator: tail.terminator,
ret: tail.ret,
switch: tail.switch,
})
}
pub(in crate::native) fn lower_block_carrier_from_suffix(
name: &str,
source: &TirBlock,
skip: usize,
terminator_line: &str,
) -> Option<TirBlock> {
let insts = source.insts.get(skip..)?.to_vec();
let term = lower_block_carrier(
name,
std::slice::from_ref(&terminator_line.to_string()),
&HashMap::new(),
)?;
Some(TirBlock {
label: name.to_string(),
insts,
terminator: term.terminator,
ret: term.ret,
switch: term.switch,
})
}
pub(in crate::native) fn lower_block_carrier_prefix(
name: &str,
source: &TirBlock,
keep: usize,
terminator_line: &str,
) -> Option<TirBlock> {
let insts = source.insts.get(..keep)?.to_vec();
let term = lower_block_carrier(
name,
std::slice::from_ref(&terminator_line.to_string()),
&HashMap::new(),
)?;
Some(TirBlock {
label: name.to_string(),
insts,
terminator: term.terminator,
ret: term.ret,
switch: term.switch,
})
}
pub(in crate::native) fn push_inst_line(
line: &str,
named_types: &HashMap<String, LlType>,
value_types: &mut HashMap<String, LlType>,
pointer_pointees: &mut HashMap<String, LlType>,
cur_insts: &mut Vec<TirInst>,
) {
let mut pointer_pointee: Option<LlType> = None;
let (result, result_ty) = match resolve_result(line, named_types) {
Some((name, ty)) => {
if let Some(ty) = &ty {
value_types.insert(name.clone(), ty.clone());
}
if matches!(ty, Some(LlType::Ptr(_))) {
if let Some(pointee) = resolve_gep_pointee(rhs_of(line), named_types) {
pointer_pointees.insert(name.clone(), pointee.clone());
pointer_pointee = Some(pointee);
}
}
(Some(name), ty)
}
None => (result_name(line), None),
};
let uses = instruction_uses(line, result.as_deref());
let operands = resolve_operands(line);
let cmp_predicate = resolve_cmp_predicate(line);
let mem_align = resolve_mem_align(line);
let gep = resolve_gep(line);
let gep_source_ty = gep.as_ref().map(|g| g.source_ty.clone());
let call = resolve_call(line);
let opcode = rhs_of(line)
.split_whitespace()
.next()
.unwrap_or("")
.to_string();
let alloca_ty = if opcode == "alloca" {
resolve_alloca_ty(line)
} else {
None
};
let phi_incoming = if opcode == "phi" {
phi_incoming_of(line)
} else {
None
};
let aggregate_indices = resolve_aggregate_indices(line, &opcode);
let diag_line = if matches!(
opcode.as_str(),
"extractelement" | "insertelement" | "shufflevector"
) {
Some(crate::native::lex::strip_comment(line).trim().to_string())
} else {
None
};
let shuffle_mask = if opcode == "shufflevector" {
resolve_shuffle_mask(line)
} else {
None
};
let bitcast = resolve_bitcast(line, &opcode);
let icmp_rest = resolve_icmp_rest(line, &opcode);
let identity_ptr_bitcast = resolve_identity_ptr_bitcast(line);
let phi_incoming_values = resolve_phi_incoming_values(line, &opcode);
let select_arms = resolve_select_arms(line, &opcode);
let load = resolve_load_inst(line, &opcode);
let store = resolve_store(line, &opcode);
let alias_call = resolve_alias_call(line);
let emit_scan_call = resolve_emit_scan_call(line);
let void_call_line = if matches!(opcode.as_str(), "call" | "tail") && result.is_none() {
Some(crate::native::lex::strip_comment(line).trim().to_string())
} else {
None
};
cur_insts.push(TirInst {
result,
result_ty,
uses,
operands,
cmp_predicate,
mem_align,
gep_source_ty,
gep,
call,
opcode,
alloca_ty,
phi_incoming,
aggregate_indices,
diag_line,
shuffle_mask,
void_call_line,
bitcast,
icmp_rest,
pointer_pointee,
identity_ptr_bitcast,
phi_incoming_values,
select_arms,
load,
store,
alias_call,
emit_scan_call,
});
}