use crate::global_isel::gisel_machine_ir::{
GInstruction, GMachineBasicBlock, GMachineFunction, GOpcode, MOperand, VReg,
};
use std::collections::HashMap;
struct TranslationState {
value_map: HashMap<String, VReg>,
block_map: HashMap<String, usize>,
next_vreg: VReg,
}
impl TranslationState {
fn new() -> Self {
TranslationState {
value_map: HashMap::new(),
block_map: HashMap::new(),
next_vreg: 0,
}
}
fn alloc_vreg(&mut self) -> VReg {
let v = self.next_vreg;
self.next_vreg += 1;
v
}
fn map_value(&mut self, name: &str, vreg: VReg) {
self.value_map.insert(name.to_string(), vreg);
}
fn lookup_value(&self, name: &str) -> Option<VReg> {
self.value_map.get(name).copied()
}
fn map_block(&mut self, name: &str, bb_idx: usize) {
self.block_map.insert(name.to_string(), bb_idx);
}
fn lookup_block(&self, name: &str) -> Option<usize> {
self.block_map.get(name).copied()
}
}
pub struct GISelIRTranslator {
pub mf: Option<GMachineFunction>,
pub instructions_translated: usize,
pub blocks_translated: usize,
}
impl GISelIRTranslator {
pub fn new() -> Self {
GISelIRTranslator {
mf: None,
instructions_translated: 0,
blocks_translated: 0,
}
}
fn reset(&mut self) {
self.mf = None;
self.instructions_translated = 0;
self.blocks_translated = 0;
}
}
impl Default for GISelIRTranslator {
fn default() -> Self {
Self::new()
}
}
impl GISelIRTranslator {
pub fn translate_instruction(
&mut self,
state: &mut TranslationState,
line: &str,
) -> Option<(GInstruction, Option<VReg>)> {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with(';') || trimmed.starts_with("label") {
return None;
}
let parts: Vec<&str> = trimmed.split_whitespace().collect();
if parts.is_empty() {
return None;
}
if parts.len() >= 2 && parts[0].starts_with('%') && parts.len() > 2 && parts[1] == "=" {
let result_name = parts[0].trim_start_matches('%').to_string();
let opcode_str = parts[2];
let remaining = &parts[3..];
return self.translate_assignment(state, &result_name, opcode_str, remaining);
}
if parts[0] == "ret" {
return self.translate_return(state, &parts[1..]);
}
if parts[0] == "br" {
return self.translate_branch(state, &parts[1..]);
}
if parts[0] == "switch" {
return self.translate_switch(state, &parts[1..]);
}
if parts[0] == "unreachable" {
return self.translate_unreachable(state);
}
if parts[0] == "store" {
return self.translate_store(state, &parts[1..]);
}
None
}
fn translate_assignment(
&mut self,
state: &mut TranslationState,
result_name: &str,
opcode_str: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
match opcode_str {
"add" => self.translate_binary_op(state, result_name, GOpcode::G_ADD, remaining),
"sub" => self.translate_binary_op(state, result_name, GOpcode::G_SUB, remaining),
"mul" => self.translate_binary_op(state, result_name, GOpcode::G_MUL, remaining),
"sdiv" => self.translate_binary_op(state, result_name, GOpcode::G_SDIV, remaining),
"udiv" => self.translate_binary_op(state, result_name, GOpcode::G_UDIV, remaining),
"srem" => self.translate_binary_op(state, result_name, GOpcode::G_SREM, remaining),
"urem" => self.translate_binary_op(state, result_name, GOpcode::G_UREM, remaining),
"and" => self.translate_binary_op(state, result_name, GOpcode::G_AND, remaining),
"or" => self.translate_binary_op(state, result_name, GOpcode::G_OR, remaining),
"xor" => self.translate_binary_op(state, result_name, GOpcode::G_XOR, remaining),
"shl" => self.translate_binary_op(state, result_name, GOpcode::G_SHL, remaining),
"lshr" => self.translate_binary_op(state, result_name, GOpcode::G_LSHR, remaining),
"ashr" => self.translate_binary_op(state, result_name, GOpcode::G_ASHR, remaining),
"fadd" => self.translate_binary_op(state, result_name, GOpcode::G_FADD, remaining),
"fsub" => self.translate_binary_op(state, result_name, GOpcode::G_FSUB, remaining),
"fmul" => self.translate_binary_op(state, result_name, GOpcode::G_FMUL, remaining),
"fdiv" => self.translate_binary_op(state, result_name, GOpcode::G_FDIV, remaining),
"frem" => self.translate_binary_op(state, result_name, GOpcode::G_FREM, remaining),
"trunc" => self.translate_cast(state, result_name, GOpcode::G_TRUNC, remaining),
"zext" => self.translate_cast(state, result_name, GOpcode::G_ZEXT, remaining),
"sext" => self.translate_cast(state, result_name, GOpcode::G_SEXT, remaining),
"fptrunc" => self.translate_cast(state, result_name, GOpcode::G_FPTRUNC, remaining),
"fpext" => self.translate_cast(state, result_name, GOpcode::G_FPEXT, remaining),
"fptoui" => self.translate_cast(state, result_name, GOpcode::G_FPTOUI, remaining),
"fptosi" => self.translate_cast(state, result_name, GOpcode::G_FPTOSI, remaining),
"uitofp" => self.translate_cast(state, result_name, GOpcode::G_UITOFP, remaining),
"sitofp" => self.translate_cast(state, result_name, GOpcode::G_SITOFP, remaining),
"icmp" => self.translate_icmp(state, result_name, remaining),
"fcmp" => self.translate_fcmp(state, result_name, remaining),
"select" => self.translate_select(state, result_name, remaining),
"phi" => self.translate_phi(state, result_name, remaining),
"alloca" => self.translate_alloca(state, result_name, remaining),
"load" => self.translate_load(state, result_name, remaining),
"getelementptr" => self.translate_gep(state, result_name, remaining),
"call" => self.translate_call(state, result_name, remaining),
"bitcast" => self.translate_bitcast(state, result_name, remaining),
"inttoptr" => self.translate_inttoptr(state, result_name, remaining),
"ptrtoint" => self.translate_ptrtoint(state, result_name, remaining),
"extractvalue" => self.translate_extractvalue(state, result_name, remaining),
"insertvalue" => self.translate_insertvalue(state, result_name, remaining),
"extractelement" => self.translate_extractelement(state, result_name, remaining),
"insertelement" => self.translate_insertelement(state, result_name, remaining),
"shufflevector" => self.translate_shufflevector(state, result_name, remaining),
_ => None,
}
}
}
impl GISelIRTranslator {
fn translate_binary_op(
&mut self,
state: &mut TranslationState,
result_name: &str,
opcode: GOpcode,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 2 {
return None;
}
let lhs_str = remaining[1].trim_end_matches(',');
let rhs_str = if remaining.len() > 2 {
remaining[2].trim_end_matches(',')
} else {
return None;
};
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let lhs_vreg = self.resolve_operand(state, lhs_str);
let rhs_vreg = self.resolve_operand(state, rhs_str);
let inst = GInstruction::with_operands(
opcode,
vec![
MOperand::vreg(result_vreg),
MOperand::vreg(lhs_vreg),
MOperand::vreg(rhs_vreg),
],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn translate_cast(
&mut self,
state: &mut TranslationState,
result_name: &str,
opcode: GOpcode,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.is_empty() {
return None;
}
let src_str = remaining[1];
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let src_vreg = self.resolve_operand(state, src_str);
let inst = GInstruction::with_operands(
opcode,
vec![MOperand::vreg(result_vreg), MOperand::vreg(src_vreg)],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn resolve_operand(&mut self, state: &mut TranslationState, s: &str) -> VReg {
let s = s.trim();
if s.starts_with('%') {
let name = s.trim_start_matches('%').trim_end_matches(',');
state.lookup_value(name).unwrap_or_else(|| {
let v = state.alloc_vreg();
state.map_value(name, v);
v
})
} else if let Ok(val) = s.trim_end_matches(',').parse::<i64>() {
let v = state.alloc_vreg();
v
} else {
let v = state.alloc_vreg();
v
}
}
}
impl GISelIRTranslator {
fn translate_alloca(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let fi = state.alloc_vreg();
let inst = GInstruction::with_operands(
GOpcode::G_FRAME_INDEX,
vec![MOperand::vreg(result_vreg), MOperand::imm(fi as i64)],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn translate_load(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 3 {
return None;
}
let ptr_str = remaining[2].trim_end_matches(',');
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let ptr_vreg = self.resolve_operand(state, ptr_str);
let inst = GInstruction::with_operands(
GOpcode::G_LOAD,
vec![MOperand::vreg(result_vreg), MOperand::vreg(ptr_vreg)],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn translate_store(
&mut self,
state: &mut TranslationState,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 3 {
return None;
}
let val_str = remaining[1].trim_end_matches(',');
let ptr_str = remaining[2].trim_end_matches(',');
let val_vreg = self.resolve_operand(state, val_str);
let ptr_vreg = self.resolve_operand(state, ptr_str);
let inst = GInstruction::with_operands(
GOpcode::G_STORE,
vec![MOperand::vreg(val_vreg), MOperand::vreg(ptr_vreg)],
);
self.instructions_translated += 1;
Some((inst, None))
}
fn translate_gep(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 3 {
return None;
}
let mut idx = 2; if remaining.get(0) == Some(&"inbounds") {
idx = 3;
}
let base_str = if idx < remaining.len() {
remaining[idx].trim_end_matches(',')
} else {
return None;
};
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let base_vreg = self.resolve_operand(state, base_str);
let offset_vreg = if remaining.len() > idx + 1 {
let offset_str = remaining[idx + 1].trim_end_matches(',');
self.resolve_operand(state, offset_str)
} else {
state.alloc_vreg()
};
let inst = GInstruction::with_operands(
GOpcode::G_PTR_ADD,
vec![
MOperand::vreg(result_vreg),
MOperand::vreg(base_vreg),
MOperand::vreg(offset_vreg),
],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
}
impl GISelIRTranslator {
fn translate_icmp(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 3 {
return None;
}
let pred = Self::icmp_predicate_to_code(remaining[0])?;
let lhs_str = remaining[2].trim_end_matches(',');
let rhs_str = remaining.get(3).map(|s| s.trim_end_matches(',')).unwrap_or("");
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let lhs_vreg = self.resolve_operand(state, lhs_str);
let rhs_vreg = self.resolve_operand(state, rhs_str);
let inst = GInstruction::with_operands(
GOpcode::G_ICMP,
vec![
MOperand::vreg(result_vreg),
MOperand::imm(pred),
MOperand::vreg(lhs_vreg),
MOperand::vreg(rhs_vreg),
],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn icmp_predicate_to_code(pred: &str) -> Option<i64> {
match pred {
"eq" => Some(0),
"ne" => Some(1),
"sgt" => Some(2),
"sge" => Some(3),
"slt" => Some(4),
"sle" => Some(5),
"ugt" => Some(6),
"uge" => Some(7),
"ult" => Some(8),
"ule" => Some(9),
_ => None,
}
}
fn translate_fcmp(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 3 {
return None;
}
let pred = Self::fcmp_predicate_to_code(remaining[0])?;
let lhs_str = remaining[2].trim_end_matches(',');
let rhs_str = remaining.get(3).map(|s| s.trim_end_matches(',')).unwrap_or("");
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let lhs_vreg = self.resolve_operand(state, lhs_str);
let rhs_vreg = self.resolve_operand(state, rhs_str);
let inst = GInstruction::with_operands(
GOpcode::G_FCMP,
vec![
MOperand::vreg(result_vreg),
MOperand::imm(pred),
MOperand::vreg(lhs_vreg),
MOperand::vreg(rhs_vreg),
],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn fcmp_predicate_to_code(pred: &str) -> Option<i64> {
match pred {
"false" => Some(0),
"oeq" => Some(1),
"ogt" => Some(2),
"oge" => Some(3),
"olt" => Some(4),
"ole" => Some(5),
"one" => Some(6),
"ord" => Some(7),
"ueq" => Some(8),
"ugt" => Some(9),
"uge" => Some(10),
"ult" => Some(11),
"ule" => Some(12),
"une" => Some(13),
"uno" => Some(14),
"true" => Some(15),
_ => None,
}
}
}
impl GISelIRTranslator {
fn translate_select(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 3 {
return None;
}
let cond_str = remaining[1].trim_end_matches(',');
let true_str = remaining[2].trim_end_matches(',');
let false_str = if remaining.len() > 3 {
remaining[3].trim_end_matches(',')
} else {
return None;
};
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let cond_vreg = self.resolve_operand(state, cond_str);
let true_vreg = self.resolve_operand(state, true_str);
let false_vreg = self.resolve_operand(state, false_str);
let inst = GInstruction::with_operands(
GOpcode::G_SELECT,
vec![
MOperand::vreg(result_vreg),
MOperand::vreg(cond_vreg),
MOperand::vreg(true_vreg),
MOperand::vreg(false_vreg),
],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn translate_phi(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 2 {
return None;
}
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let mut operands = vec![MOperand::vreg(result_vreg)];
let text = remaining.join(" ");
for segment in text.split('[').skip(1) {
let inner = segment.split(']').next().unwrap_or("");
let parts: Vec<&str> = inner.split(',').collect();
if parts.len() >= 2 {
let val_str = parts[0].trim().trim_start_matches('%');
let _label_str = parts[1].trim().trim_start_matches('%');
let val_vreg = state.lookup_value(val_str).unwrap_or_else(|| {
let v = state.alloc_vreg();
state.map_value(val_str, v);
v
});
operands.push(MOperand::vreg(val_vreg));
}
}
if operands.len() < 3 {
operands.push(MOperand::vreg(state.alloc_vreg()));
}
let inst = GInstruction::with_operands(GOpcode::G_PHI, operands);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
}
impl GISelIRTranslator {
fn translate_return(
&mut self,
state: &mut TranslationState,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.is_empty() {
return None;
}
if remaining[0] == "void" {
let inst = GInstruction::with_operands(GOpcode::G_RET, vec![]);
self.instructions_translated += 1;
return Some((inst, None));
}
if remaining.len() >= 2 {
let val_str = remaining[1].trim_end_matches(',');
let val_vreg = self.resolve_operand(state, val_str);
let inst = GInstruction::with_operands(
GOpcode::G_RET,
vec![MOperand::vreg(val_vreg)],
);
self.instructions_translated += 1;
return Some((inst, None));
}
None
}
fn translate_branch(
&mut self,
state: &mut TranslationState,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.is_empty() {
return None;
}
if remaining[0] == "label" {
if remaining.len() >= 2 {
let dest = remaining[1].trim_start_matches('%').trim_end_matches(',');
let inst = GInstruction::with_operands(
GOpcode::G_BR,
vec![MOperand::label(dest)],
);
self.instructions_translated += 1;
return Some((inst, None));
}
} else {
if remaining.len() >= 6 {
let cond_str = remaining[1].trim_end_matches(',');
let true_dest = remaining[3].trim_start_matches('%').trim_end_matches(',');
let false_dest = remaining[5].trim_start_matches('%').trim_end_matches(',');
let cond_vreg = self.resolve_operand(state, cond_str);
let inst = GInstruction::with_operands(
GOpcode::G_BRCOND,
vec![
MOperand::vreg(cond_vreg),
MOperand::label(true_dest),
MOperand::label(false_dest),
],
);
self.instructions_translated += 1;
return Some((inst, None));
}
}
None
}
fn translate_switch(
&mut self,
_state: &mut TranslationState,
_remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
None
}
fn translate_unreachable(
&mut self,
_state: &mut TranslationState,
) -> Option<(GInstruction, Option<VReg>)> {
let inst = GInstruction::with_operands(GOpcode::G_BR, vec![]);
self.instructions_translated += 1;
Some((inst, None))
}
}
impl GISelIRTranslator {
fn translate_call(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 2 {
return None;
}
let callee = remaining[1].trim_start_matches('@').trim_end_matches(',');
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let mut operands = vec![MOperand::vreg(result_vreg), MOperand::label(callee)];
let remaining_text = remaining.join(" ");
if let Some(args_start) = remaining_text.find('(') {
let args_part = &remaining_text[args_start + 1..];
if let Some(args_end) = args_part.find(')') {
let args_text = &args_part[..args_end];
for arg in args_text.split(',') {
let arg_trimmed = arg.trim();
if !arg_trimmed.is_empty() {
let parts: Vec<&str> = arg_trimmed.split_whitespace().collect();
if parts.len() >= 2 {
let arg_val = parts[1].trim_start_matches('%');
let arg_vreg = self.resolve_operand(state, &format!("%{}", arg_val));
operands.push(MOperand::vreg(arg_vreg));
}
}
}
}
}
let inst = GInstruction::with_operands(GOpcode::G_BR, operands);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
}
impl GISelIRTranslator {
fn translate_bitcast(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 2 {
return None;
}
let src_str = remaining[1];
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let src_vreg = self.resolve_operand(state, src_str);
let inst = GInstruction::with_operands(
GOpcode::G_BITCAST,
vec![MOperand::vreg(result_vreg), MOperand::vreg(src_vreg)],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn translate_inttoptr(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 2 {
return None;
}
let src_str = remaining[1];
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let src_vreg = self.resolve_operand(state, src_str);
let inst = GInstruction::with_operands(
GOpcode::G_INTTOPTR,
vec![MOperand::vreg(result_vreg), MOperand::vreg(src_vreg)],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn translate_ptrtoint(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 2 {
return None;
}
let src_str = remaining[1];
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let src_vreg = self.resolve_operand(state, src_str);
let inst = GInstruction::with_operands(
GOpcode::G_PTRTOINT,
vec![MOperand::vreg(result_vreg), MOperand::vreg(src_vreg)],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn translate_extractvalue(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 2 {
return None;
}
let agg_str = remaining[1].trim_end_matches(',');
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let agg_vreg = self.resolve_operand(state, agg_str);
let inst = GInstruction::with_operands(
GOpcode::G_BITCAST,
vec![MOperand::vreg(result_vreg), MOperand::vreg(agg_vreg)],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn translate_insertvalue(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 4 {
return None;
}
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let agg_str = remaining[1].trim_end_matches(',');
let val_str = remaining[2].trim_end_matches(',');
let agg_vreg = self.resolve_operand(state, agg_str);
let val_vreg = self.resolve_operand(state, val_str);
let inst = GInstruction::with_operands(
GOpcode::G_BITCAST,
vec![MOperand::vreg(result_vreg), MOperand::vreg(agg_vreg), MOperand::vreg(val_vreg)],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn translate_extractelement(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 3 {
return None;
}
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let vec_str = remaining[1].trim_end_matches(',');
let idx_str = remaining[2].trim_end_matches(',');
let vec_vreg = self.resolve_operand(state, vec_str);
let idx_vreg = self.resolve_operand(state, idx_str);
let inst = GInstruction::with_operands(
GOpcode::G_BITCAST,
vec![MOperand::vreg(result_vreg), MOperand::vreg(vec_vreg), MOperand::vreg(idx_vreg)],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn translate_insertelement(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 4 {
return None;
}
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let vec_str = remaining[1].trim_end_matches(',');
let elt_str = remaining[2].trim_end_matches(',');
let idx_str = remaining[3].trim_end_matches(',');
let vec_vreg = self.resolve_operand(state, vec_str);
let elt_vreg = self.resolve_operand(state, elt_str);
let idx_vreg = self.resolve_operand(state, idx_str);
let inst = GInstruction::with_operands(
GOpcode::G_BITCAST,
vec![MOperand::vreg(result_vreg), MOperand::vreg(vec_vreg), MOperand::vreg(elt_vreg), MOperand::vreg(idx_vreg)],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
fn translate_shufflevector(
&mut self,
state: &mut TranslationState,
result_name: &str,
remaining: &[&str],
) -> Option<(GInstruction, Option<VReg>)> {
if remaining.len() < 3 {
return None;
}
let result_vreg = state.alloc_vreg();
state.map_value(result_name, result_vreg);
let v1_str = remaining[1].trim_end_matches(',');
let v2_str = remaining[2].trim_end_matches(',');
let v1_vreg = self.resolve_operand(state, v1_str);
let v2_vreg = self.resolve_operand(state, v2_str);
let inst = GInstruction::with_operands(
GOpcode::G_BITCAST,
vec![MOperand::vreg(result_vreg), MOperand::vreg(v1_vreg), MOperand::vreg(v2_vreg)],
);
self.instructions_translated += 1;
Some((inst, Some(result_vreg)))
}
}
impl GISelIRTranslator {
pub fn translate_function(&mut self, ir_lines: &[&str], func_name: &str) -> GMachineFunction {
self.reset();
let mut mf = GMachineFunction::new(func_name.to_string());
let entry = mf.push_block("entry".to_string());
let mut state = TranslationState::new();
state.map_block("entry", entry);
let mut current_block = entry;
for line in ir_lines {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with(';') {
continue;
}
if trimmed.ends_with(':') && !trimmed.starts_with('%') && !trimmed.contains('=') {
let label = trimmed.trim_end_matches(':');
let bb_idx = mf.push_block(label.to_string());
state.map_block(label, bb_idx);
current_block = bb_idx;
self.blocks_translated += 1;
continue;
}
if let Some((inst, _)) = self.translate_instruction(&mut state, line) {
mf.blocks[current_block].push_instruction(inst);
}
}
mf.next_vreg = state.next_vreg;
self.mf = Some(mf.clone());
mf
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_translator_new() {
let translator = GISelIRTranslator::new();
assert_eq!(translator.instructions_translated, 0);
assert_eq!(translator.blocks_translated, 0);
assert!(translator.mf.is_none());
}
#[test]
fn test_translator_default() {
let translator = GISelIRTranslator::default();
assert_eq!(translator.instructions_translated, 0);
}
#[test]
fn test_translate_binary_add() {
let mut translator = GISelIRTranslator::new();
let mut state = TranslationState::new();
let line = "%0 = add i32 %1, %2";
let result = translator.translate_instruction(&mut state, line);
assert!(result.is_some());
let (inst, _) = result.unwrap();
assert_eq!(inst.opcode, GOpcode::G_ADD);
}
#[test]
fn test_translate_sub() {
let mut translator = GISelIRTranslator::new();
let mut state = TranslationState::new();
let line = "%0 = sub i32 %1, %2";
let result = translator.translate_instruction(&mut state, line);
assert!(result.is_some());
let (inst, _) = result.unwrap();
assert_eq!(inst.opcode, GOpcode::G_SUB);
}
#[test]
fn test_translate_icmp() {
let mut translator = GISelIRTranslator::new();
let mut state = TranslationState::new();
let line = "%0 = icmp eq i32 %1, %2";
let result = translator.translate_instruction(&mut state, line);
assert!(result.is_some());
let (inst, _) = result.unwrap();
assert_eq!(inst.opcode, GOpcode::G_ICMP);
}
#[test]
fn test_translate_select() {
let mut translator = GISelIRTranslator::new();
let mut state = TranslationState::new();
let line = "%0 = select i1 %1, i32 %2, i32 %3";
let result = translator.translate_instruction(&mut state, line);
assert!(result.is_some());
let (inst, _) = result.unwrap();
assert_eq!(inst.opcode, GOpcode::G_SELECT);
}
#[test]
fn test_translate_return_void() {
let mut translator = GISelIRTranslator::new();
let mut state = TranslationState::new();
let line = "ret void";
let result = translator.translate_instruction(&mut state, line);
assert!(result.is_some());
let (inst, _) = result.unwrap();
assert_eq!(inst.opcode, GOpcode::G_RET);
}
#[test]
fn test_translate_return_value() {
let mut translator = GISelIRTranslator::new();
let mut state = TranslationState::new();
let line = "ret i32 %0";
let result = translator.translate_instruction(&mut state, line);
assert!(result.is_some());
let (inst, _) = result.unwrap();
assert_eq!(inst.opcode, GOpcode::G_RET);
}
#[test]
fn test_translate_br_unconditional() {
let mut translator = GISelIRTranslator::new();
let mut state = TranslationState::new();
let line = "br label %exit";
let result = translator.translate_instruction(&mut state, line);
assert!(result.is_some());
let (inst, _) = result.unwrap();
assert_eq!(inst.opcode, GOpcode::G_BR);
}
#[test]
fn test_translate_br_conditional() {
let mut translator = GISelIRTranslator::new();
let mut state = TranslationState::new();
let line = "br i1 %cond, label %then, label %else";
let result = translator.translate_instruction(&mut state, line);
assert!(result.is_some());
let (inst, _) = result.unwrap();
assert_eq!(inst.opcode, GOpcode::G_BRCOND);
}
#[test]
fn test_translate_load() {
let mut translator = GISelIRTranslator::new();
let mut state = TranslationState::new();
let line = "%0 = load i32, ptr %1";
let result = translator.translate_instruction(&mut state, line);
assert!(result.is_some());
let (inst, _) = result.unwrap();
assert_eq!(inst.opcode, GOpcode::G_LOAD);
}
#[test]
fn test_translate_store() {
let mut translator = GISelIRTranslator::new();
let mut state = TranslationState::new();
let line = "store i32 %val, ptr %ptr";
let result = translator.translate_instruction(&mut state, line);
assert!(result.is_some());
let (inst, _) = result.unwrap();
assert_eq!(inst.opcode, GOpcode::G_STORE);
}
#[test]
fn test_translate_alloca() {
let mut translator = GISelIRTranslator::new();
let mut state = TranslationState::new();
let line = "%0 = alloca i32";
let result = translator.translate_instruction(&mut state, line);
assert!(result.is_some());
let (inst, _) = result.unwrap();
assert_eq!(inst.opcode, GOpcode::G_FRAME_INDEX);
}
#[test]
fn test_translate_function() {
let mut translator = GISelIRTranslator::new();
let ir = vec![
"%1 = alloca i32",
"%2 = load i32, ptr %1",
"%3 = add i32 %2, %2",
"ret i32 %3",
];
let mf = translator.translate_function(&ir, "test");
assert_eq!(mf.block_count(), 1);
assert!(mf.blocks[0].instruction_count() >= 4);
assert!(translator.instructions_translated >= 4);
}
#[test]
fn test_icmp_predicate_code() {
assert_eq!(GISelIRTranslator::icmp_predicate_to_code("eq"), Some(0));
assert_eq!(GISelIRTranslator::icmp_predicate_to_code("sgt"), Some(2));
assert_eq!(GISelIRTranslator::icmp_predicate_to_code("ult"), Some(8));
assert_eq!(GISelIRTranslator::icmp_predicate_to_code("invalid"), None);
}
#[test]
fn test_fcmp_predicate_code() {
assert_eq!(GISelIRTranslator::fcmp_predicate_to_code("oeq"), Some(1));
assert_eq!(GISelIRTranslator::fcmp_predicate_to_code("false"), Some(0));
assert_eq!(GISelIRTranslator::fcmp_predicate_to_code("true"), Some(15));
}
#[test]
fn test_reset() {
let mut translator = GISelIRTranslator::new();
let mut state = TranslationState::new();
let _ = translator.translate_instruction(&mut state, "%0 = add i32 %1, %2");
assert!(translator.instructions_translated > 0);
translator.reset();
assert_eq!(translator.instructions_translated, 0);
assert!(translator.mf.is_none());
}
}