use {
crate::{
backend::AllocatedVariable,
ir::{HardwareRegister, Instruction, TypedHardwareRegister},
},
std::collections::BTreeSet,
};
pub fn generate_standalone_asm(
label: &str,
instructions: &[Instruction<HardwareRegister>],
) -> String {
let label = format!("_{label}");
let formatted_instructions: String = instructions
.iter()
.map(|instruction| format!(" {instruction}"))
.collect::<Vec<_>>()
.join("\n");
format!(
r#"
.global {label}
.align 4
.text
{label}:
{formatted_instructions}
ret"#
)
}
pub fn format_instructions_rust_inline(instructions: &[Instruction<HardwareRegister>]) -> String {
instructions
.iter()
.map(|instruction| format!("\"{instruction}\""))
.collect::<Vec<_>>()
.join(",\n")
}
pub fn generate_rust_global_asm(
label: &str,
inputs_registers: &[AllocatedVariable],
outputs_registers: &[AllocatedVariable],
instructions: &[Instruction<HardwareRegister>],
) -> String {
let operands = generate_asm_operands(inputs_registers, outputs_registers, instructions);
let standalone = generate_standalone_asm(label, instructions);
let operands_with_comments: String = operands
.lines()
.map(|line| format!("//{line}"))
.collect::<Vec<_>>()
.join("\n");
format!(
r#"{operands_with_comments}
{standalone}"#
)
}
pub fn generate_rust_includable_asm(
inputs_registers: &[AllocatedVariable],
outputs_registers: &[AllocatedVariable],
instructions: &[Instruction<HardwareRegister>],
) -> String {
let operands = generate_asm_operands(inputs_registers, outputs_registers, instructions);
let formatted_instructions: String = instructions
.iter()
.map(|instruction| format!(" {instruction}"))
.collect::<Vec<_>>()
.join("\n");
let operands_with_comments: String = operands
.lines()
.map(|line| format!("// {line}"))
.collect::<Vec<_>>()
.join("\n");
format!(
r#"// GENERATED FILE, DO NOT EDIT!
{operands_with_comments}
{formatted_instructions}
"#
)
}
pub fn generate_rust_inline_asm(
inputs_registers: &[AllocatedVariable],
outputs_registers: &[AllocatedVariable],
instructions: &[Instruction<HardwareRegister>],
) -> String {
let inst = format_instructions_rust_inline(instructions);
let operands = generate_asm_operands(inputs_registers, outputs_registers, instructions);
format!(
r#"
unsafe {{ asm!(
{inst},
{operands}
)}};"#
)
}
pub fn generate_asm_operands(
inputs: &[AllocatedVariable],
outputs: &[AllocatedVariable],
instructions: &[Instruction<HardwareRegister>],
) -> String {
let input_operands = format_operands(inputs, "in");
let output_operands = format_operands(outputs, "lateout");
let clobber_registers = get_clobber_registers(outputs, instructions);
let clobbers = format_clobbers(&clobber_registers);
[
input_operands,
output_operands,
clobbers,
"lateout(\"lr\") _".to_string(),
]
.join(",\n")
}
fn get_clobber_registers(
outputs_registers: &[AllocatedVariable],
instructions: &[Instruction<HardwareRegister>],
) -> Vec<TypedHardwareRegister> {
let mut all_used_registers = BTreeSet::new();
for instruction in instructions {
all_used_registers.extend(
instruction
.extract_registers()
.map(|reg| reg.to_basic_register()),
);
}
let output_registers = outputs_registers
.iter()
.flat_map(|variable| variable.registers.clone())
.collect();
all_used_registers
.difference(&output_registers)
.cloned()
.collect()
}
fn format_clobbers(clobbered_registers: &[TypedHardwareRegister]) -> String {
clobbered_registers
.iter()
.map(|register| format!("lateout(\"{register}\") _"))
.collect::<Vec<_>>()
.join(", ")
}
fn format_operands(variables: &[AllocatedVariable], direction: &str) -> String {
variables
.iter()
.map(move |variable| {
if variable.registers.len() > 1 {
variable
.registers
.iter()
.enumerate()
.map(move |(variable_index, register)| {
format!(
"{direction}(\"{register}\") {}[{variable_index}]",
variable.label
)
})
.collect::<Vec<_>>()
.join(", ") } else {
format!(
"{direction}(\"{}\") {}",
variable.registers[0], variable.label
)
}
})
.collect::<Vec<_>>()
.join(",\n") }