use crate::basic_block;
use crate::constants;
use crate::context::LLVMContext;
use crate::function;
use crate::instruction::{self, FCmpPred, ICmpPred};
use crate::ir_builder::IRBuilder;
use crate::module::Module;
use crate::types::{Type, TypeId, TypeKind};
use crate::value::{valref, SubclassKind, Value, ValueRef};
use super::ast::*;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::collections::HashSet;
use crate::instruction::Opcode;
pub struct IRGenerator<'a> {
pub context: &'a LLVMContext,
pub module: &'a mut Module,
pub builder: IRBuilder,
pub current_function: Option<ValueRef>,
pub named_values: HashMap<String, ValueRef>,
pub global_values: HashMap<String, ValueRef>,
pub functions: HashMap<String, ValueRef>,
pub struct_types: HashMap<String, Type>,
pub struct_field_names: HashMap<TypeId, Vec<String>>,
pub enum_types: HashMap<String, Type>,
pub typedef_types: HashMap<String, Type>,
pub type_cache: HashMap<String, Type>,
pub errors: Vec<String>,
pub warnings: Vec<String>,
pub string_pool: HashMap<String, ValueRef>,
break_targets: Vec<ValueRef>,
continue_targets: Vec<ValueRef>,
switch_cases: Vec<(i64, ValueRef)>,
default_case_block: Option<ValueRef>,
switch_merge_block: Option<ValueRef>,
pub labels: HashMap<String, ValueRef>,
forward_gotos: Vec<(String, ValueRef)>,
pub debug_compile_unit: Option<ValueRef>,
pub current_debug_loc: Option<(u32, u32, ValueRef)>,
pub debug_subprograms: HashMap<String, ValueRef>,
pub debug_local_vars: HashMap<String, ValueRef>,
pub source_file: String,
pub target_triple: String,
pub data_layout: String,
pub generate_debug_info: bool,
pub optimization_level: u8,
tmp_counter: u64,
_phantom: std::marker::PhantomData<&'a ()>,
}
impl<'a> IRGenerator<'a> {
pub fn new(context: &'a LLVMContext, module: &'a mut Module, builder: IRBuilder) -> Self {
IRGenerator {
context,
module,
builder,
current_function: None,
named_values: HashMap::new(),
global_values: HashMap::new(),
functions: HashMap::new(),
struct_types: HashMap::new(),
struct_field_names: HashMap::new(),
enum_types: HashMap::new(),
typedef_types: HashMap::new(),
type_cache: HashMap::new(),
errors: Vec::new(),
warnings: Vec::new(),
string_pool: HashMap::new(),
break_targets: Vec::new(),
continue_targets: Vec::new(),
switch_cases: Vec::new(),
default_case_block: None,
switch_merge_block: None,
labels: HashMap::new(),
forward_gotos: Vec::new(),
debug_compile_unit: None,
current_debug_loc: None,
debug_subprograms: HashMap::new(),
debug_local_vars: HashMap::new(),
source_file: String::from("unknown.c"),
target_triple: String::from("x86_64-unknown-linux-gnu"),
data_layout: String::from(
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128",
),
generate_debug_info: false,
optimization_level: 0,
tmp_counter: 0,
_phantom: std::marker::PhantomData,
}
}
pub fn new_empty(context: &'a LLVMContext, module: &'a mut Module) -> Self {
let builder = IRBuilder::new(context);
Self::new(context, module, builder)
}
pub fn compile_translation_unit(&mut self, tu: &TranslationUnit) -> usize {
self.module.set_source_filename(&tu.filename);
self.module.set_target_triple(&self.target_triple);
self.module.set_data_layout(&self.data_layout);
for decl in &tu.decls {
if let Err(e) = self.forward_declare(decl) {
self.errors.push(e);
}
}
for decl in &tu.decls {
self.decl_pass(decl);
}
for decl in &tu.decls {
if let Err(e) = self.compile_decl(decl) {
self.errors.push(e);
}
}
self.resolve_forward_gotos();
if self.generate_debug_info {
self.finalize_debug_info();
}
self.errors.len()
}
fn forward_declare(&mut self, decl: &Decl) -> Result<(), String> {
match decl {
Decl::Function => {
Ok(())
}
Decl::Struct => {
Ok(())
}
Decl::Enum => {
Ok(())
}
Decl::Variable => {
Ok(())
}
Decl::Typedef => Ok(()),
Decl::EnumVariant => Ok(()),
}
}
fn decl_pass(&mut self, _decl: &Decl) {
}
pub fn compile_decl(&mut self, decl: &Decl) -> Result<(), String> {
match decl {
Decl::Function => {
Ok(())
}
Decl::Variable => {
Ok(())
}
_ => Ok(()),
}
}
fn resolve_forward_gotos(&mut self) {
self.forward_gotos.clear();
}
fn finalize_debug_info(&mut self) {
}
pub fn convert_type(&mut self, tn: &TypeNode) -> Type {
let cache_key = format!("{:?}", tn);
if let Some(cached) = self.type_cache.get(&cache_key) {
return cached.clone();
}
let llvm_type = match tn {
TypeNode::Void => Type::void(),
TypeNode::Char => Type::i8(),
TypeNode::SChar => Type::i8(),
TypeNode::UChar => Type::i8(),
TypeNode::Short => Type::i16(),
TypeNode::UShort => Type::i16(),
TypeNode::Int => Type::i32(),
TypeNode::UInt => Type::i32(),
TypeNode::Long => {
if self.target_triple.contains("64") {
Type::i64()
} else {
Type::i32()
}
}
TypeNode::ULong => {
if self.target_triple.contains("64") {
Type::i64()
} else {
Type::i32()
}
}
TypeNode::LongLong => Type::i64(),
TypeNode::ULongLong => Type::i64(),
TypeNode::Float => Type::float(),
TypeNode::Double => Type::double(),
TypeNode::LongDouble => {
Type::x86_fp80()
}
TypeNode::Bool => Type::i1(),
TypeNode::Complex => {
let elem_ty = Type::float(); Type::struct_literal_with(&[elem_ty.id, elem_ty.id], false)
}
TypeNode::Auto => Type::i32(), TypeNode::Record => Type::i32(), TypeNode::Pointer(pointee) => {
let elem_ty = self.convert_type_node_to_ast(pointee);
let llvm_elem = self.convert_type_node(&elem_ty);
Type::pointer(llvm_elem.id)
}
TypeNode::Array(elem, size) => {
let elem_ty = self.convert_type_node_to_ast(elem);
let llvm_elem = self.convert_type_node(&elem_ty);
Type::array_with(llvm_elem.id, *size as u64)
}
TypeNode::Function(ret, params, is_vararg) => {
let ret_ty = self.convert_type_node_to_ast(ret);
let llvm_ret = self.convert_type_node(&ret_ty);
let param_types: Vec<TypeId> = params
.iter()
.map(|p| {
let pt = self.convert_type_node_to_ast(p);
self.convert_type_node(&pt).id
})
.collect();
Type::function_type_with(llvm_ret.id, ¶m_types, *is_vararg)
}
TypeNode::Struct(name, fields, is_union) => {
if let Some(cached) = self.struct_types.get(name) {
return cached.clone();
}
let field_types: Vec<TypeId> = fields
.iter()
.map(|f| {
let ft = self.convert_type_node_to_ast(&f.ty);
self.convert_type_node(&ft).id
})
.collect();
let struct_ty = Type::struct_named_with(name, &field_types, *is_union);
self.struct_field_names.insert(
struct_ty.id,
fields.iter().map(|f| f.name.clone()).collect(),
);
self.struct_types.insert(name.clone(), struct_ty.clone());
struct_ty
}
TypeNode::Enum(name, _variants) => {
let enum_ty = Type::i32();
self.enum_types.insert(name.clone(), enum_ty.clone());
enum_ty
}
TypeNode::Typedef(name, underlying) => {
let base = self.convert_type_node_to_ast(underlying);
let ty = self.convert_type_node(&base);
self.typedef_types.insert(name.clone(), ty.clone());
ty
}
};
self.type_cache.insert(cache_key, llvm_type.clone());
llvm_type
}
fn convert_type_node_to_ast(&self, tn: &TypeNode) -> TypeNode {
tn.clone()
}
pub fn convert_qualtype(&mut self, qt: &QualType) -> Type {
let mut tn = qt.base.clone();
self.convert_type(&tn)
}
pub fn convert_struct_type(&mut self, sd: &StructDecl) -> Type {
let field_types: Vec<TypeId> = sd
.fields
.iter()
.map(|f| {
let ft = self.convert_type_node_to_ast(&f.ty);
self.convert_type_node(&ft).id
})
.collect();
let struct_ty = Type::struct_named_with(&sd.name, &field_types, sd.is_union);
self.struct_field_names.insert(
struct_ty.id,
sd.fields.iter().map(|f| f.name.clone()).collect(),
);
self.struct_types.insert(sd.name.clone(), struct_ty.clone());
struct_ty
}
pub fn create_function_prototype(&mut self, fd: &FunctionDecl) -> Result<ValueRef, String> {
let ret_ty = self.convert_type_node_to_ast(&fd.ret_ty);
let llvm_ret_ty = self.convert_type_node(&ret_ty);
let param_types: Vec<Type> = fd
.params
.iter()
.map(|p| {
let pt = self.convert_type_node_to_ast(&p.ty);
self.convert_type_node(&pt)
})
.collect();
let param_ids: Vec<TypeId> = param_types.iter().map(|t| t.id).collect();
let func_ty = Type::function_type_with(llvm_ret_ty.id, ¶m_ids, fd.is_vararg);
let func_val = Value::named(&fd.name);
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
self.module.add_function(func_val.clone());
self.functions.insert(fd.name.clone(), func_val.clone());
self.global_values.insert(fd.name.clone(), func_val.clone());
Ok(func_val)
}
pub fn compile_function(&mut self, fd: &FunctionDecl) -> Result<ValueRef, String> {
let func_val = self.create_function_prototype(fd)?;
if let Some(ref body) = fd.body {
self.current_function = Some(func_val.clone());
let entry_bb = self.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
self.builder.set_insert_point(&entry_bb);
self.named_values.clear();
for (i, param) in fd.params.iter().enumerate() {
let param_ty = self.convert_type_node_to_ast(¶m.ty);
let llvm_param_ty = self.convert_type_node(¶m_ty);
let param_val = Value::named(¶m.name);
param_val.borrow_mut().ty = llvm_param_ty.id;
param_val.borrow_mut().subclass = SubclassKind::Argument;
self.named_values.insert(param.name.clone(), param_val);
}
self.compile_compound_stmt(body);
let current_block = self.builder.get_insert_block();
if let Some(bb) = current_block {
let bb_ref = bb.borrow();
let has_terminator = bb_ref.instructions.iter().any(|inst| {
let i = inst.borrow();
instruction::is_terminator(i.opcode)
});
if !has_terminator {
if llvm_ret_ty_for_func(&self.convert_type_node_to_ast(&fd.ret_ty)).is_void() {
self.builder.create_ret_void();
} else {
let undef_val = Value::new(
llvm_ret_ty_for_func(&self.convert_type_node_to_ast(&fd.ret_ty)).id,
);
self.builder.create_ret(undef_val);
}
}
}
self.current_function = None;
}
Ok(func_val)
}
pub fn compile_global(&mut self, vd: &VarDecl) -> Result<ValueRef, String> {
let var_ty = self.convert_type_node_to_ast(&vd.ty);
let llvm_ty = self.convert_type_node(&var_ty);
let gv = Value::named(&vd.name);
gv.borrow_mut().ty = llvm_ty.id;
gv.borrow_mut().subclass = SubclassKind::GlobalVariable;
if let Some(ref init_expr) = vd.init {
let init_val = self.compile_expr(init_expr)?;
gv.borrow_mut().initializer = Some(init_val);
}
self.module.add_global_variable(gv.clone());
self.global_values.insert(vd.name.clone(), gv.clone());
Ok(gv)
}
pub fn compile_static_local(&mut self, vd: &VarDecl) -> Result<ValueRef, String> {
let var_ty = self.convert_type_node_to_ast(&vd.ty);
let llvm_ty = self.convert_type_node(&var_ty);
let gv = Value::named(&vd.name);
gv.borrow_mut().ty = llvm_ty.id;
gv.borrow_mut().subclass = SubclassKind::GlobalVariable;
if let Some(ref init_expr) = vd.init {
let init_val = self.compile_expr(init_expr)?;
gv.borrow_mut().initializer = Some(init_val);
}
self.module.add_global_variable(gv.clone());
self.global_values.insert(vd.name.clone(), gv.clone());
Ok(gv)
}
pub fn compile_stmt(&mut self, stmt: &Stmt) -> Result<Option<ValueRef>, String> {
match stmt {
Stmt::Compound(cs) => {
self.compile_compound_stmt(cs);
Ok(None)
}
Stmt::Return(expr) => {
self.compile_return(expr.as_ref())?;
Ok(None)
}
Stmt::If(cond, then, els) => {
self.compile_if(cond, then, els.as_ref())?;
Ok(None)
}
Stmt::While(cond, body) => {
self.compile_while(cond, body)?;
Ok(None)
}
Stmt::DoWhile(body, cond) => {
self.compile_do_while(body, cond)?;
Ok(None)
}
Stmt::For(init, cond, incr, body) => {
self.compile_for(init.as_ref(), cond.as_ref(), incr.as_ref(), body)?;
Ok(None)
}
Stmt::Switch(expr, body) => {
self.compile_switch(expr, body)?;
Ok(None)
}
Stmt::Case(value, stmt) => {
self.compile_case(*value, stmt)?;
Ok(None)
}
Stmt::Default(stmt) => {
self.compile_default(stmt)?;
Ok(None)
}
Stmt::Break => {
self.compile_break()?;
Ok(None)
}
Stmt::Continue => {
self.compile_continue()?;
Ok(None)
}
Stmt::Goto(label) => {
self.compile_goto(label)?;
Ok(None)
}
Stmt::Label(name, stmt) => {
self.compile_label(name, stmt)?;
Ok(None)
}
Stmt::Expr(expr) => {
let val = self.compile_expr(expr)?;
Ok(Some(val))
}
Stmt::Decl(decl) => {
self.compile_decl_stmt(decl)?;
Ok(None)
}
Stmt::Null => Ok(None),
}
}
fn compile_decl_stmt(&mut self, decl: &Decl) -> Result<(), String> {
match decl {
Decl::Variable => {
Ok(())
}
_ => Ok(()),
}
}
pub fn compile_compound_stmt(&mut self, cs: &CompoundStmt) {
for stmt in &cs.stmts {
let _ = self.compile_stmt(stmt);
}
}
pub fn compile_if(
&mut self,
cond: &Expr,
then: &Stmt,
els: Option<&Stmt>,
) -> Result<(), String> {
let cond_val = self.compile_expr(cond)?;
let cond_bool = self.int_to_bool(cond_val);
let then_bb = self.builder.create_basic_block("if.then");
let else_bb = if els.is_some() {
Some(self.builder.create_basic_block("if.else"))
} else {
None
};
let merge_bb = self.builder.create_basic_block("if.merge");
if let Some(ref else_b) = else_bb {
self.builder.create_cond_br(cond_bool, &then_bb, else_b);
} else {
self.builder.create_cond_br(cond_bool, &then_bb, &merge_bb);
}
self.builder.set_insert_point(&then_bb);
self.compile_stmt(then)?;
let then_terminated = self.block_has_terminator();
if !then_terminated {
self.builder.create_br(&merge_bb);
}
if let (Some(els_stmt), Some(ref else_b)) = (els, else_bb) {
self.builder.set_insert_point(else_b);
self.compile_stmt(els_stmt)?;
let else_terminated = self.block_has_terminator();
if !else_terminated {
self.builder.create_br(&merge_bb);
}
}
self.builder.set_insert_point(&merge_bb);
Ok(())
}
pub fn compile_while(&mut self, cond: &Expr, body: &Stmt) -> Result<(), String> {
let cond_bb = self.builder.create_basic_block("while.cond");
let body_bb = self.builder.create_basic_block("while.body");
let merge_bb = self.builder.create_basic_block("while.merge");
self.break_targets.push(merge_bb.clone());
self.continue_targets.push(cond_bb.clone());
self.builder.create_br(&cond_bb);
self.builder.set_insert_point(&cond_bb);
let cond_val = self.compile_expr(cond)?;
let cond_bool = self.int_to_bool(cond_val);
self.builder.create_cond_br(cond_bool, &body_bb, &merge_bb);
self.builder.set_insert_point(&body_bb);
self.compile_stmt(body)?;
let body_terminated = self.block_has_terminator();
if !body_terminated {
self.builder.create_br(&cond_bb);
}
self.break_targets.pop();
self.continue_targets.pop();
self.builder.set_insert_point(&merge_bb);
Ok(())
}
pub fn compile_do_while(&mut self, body: &Stmt, cond: &Expr) -> Result<(), String> {
let body_bb = self.builder.create_basic_block("do.body");
let cond_bb = self.builder.create_basic_block("do.cond");
let merge_bb = self.builder.create_basic_block("do.merge");
self.break_targets.push(merge_bb.clone());
self.continue_targets.push(cond_bb.clone());
self.builder.create_br(&body_bb);
self.builder.set_insert_point(&body_bb);
self.compile_stmt(body)?;
let body_terminated = self.block_has_terminator();
if !body_terminated {
self.builder.create_br(&cond_bb);
}
self.builder.set_insert_point(&cond_bb);
let cond_val = self.compile_expr(cond)?;
let cond_bool = self.int_to_bool(cond_val);
self.builder.create_cond_br(cond_bool, &body_bb, &merge_bb);
self.break_targets.pop();
self.continue_targets.pop();
self.builder.set_insert_point(&merge_bb);
Ok(())
}
pub fn compile_for(
&mut self,
init: Option<&Stmt>,
cond: Option<&Expr>,
incr: Option<&Expr>,
body: &Stmt,
) -> Result<(), String> {
if let Some(init_stmt) = init {
self.compile_stmt(init_stmt)?;
}
let cond_bb = self.builder.create_basic_block("for.cond");
let body_bb = self.builder.create_basic_block("for.body");
let incr_bb = self.builder.create_basic_block("for.incr");
let merge_bb = self.builder.create_basic_block("for.merge");
self.break_targets.push(merge_bb.clone());
self.continue_targets.push(incr_bb.clone());
self.builder.create_br(&cond_bb);
self.builder.set_insert_point(&cond_bb);
let cond_bool = if let Some(cond_expr) = cond {
let cond_val = self.compile_expr(cond_expr)?;
self.int_to_bool(cond_val)
} else {
self.builder.get_bool(true) };
self.builder.create_cond_br(cond_bool, &body_bb, &merge_bb);
self.builder.set_insert_point(&body_bb);
self.compile_stmt(body)?;
let body_terminated = self.block_has_terminator();
if !body_terminated {
self.builder.create_br(&incr_bb);
}
self.builder.set_insert_point(&incr_bb);
if let Some(incr_expr) = incr {
self.compile_expr(incr_expr)?;
}
self.builder.create_br(&cond_bb);
self.break_targets.pop();
self.continue_targets.pop();
self.builder.set_insert_point(&merge_bb);
Ok(())
}
pub fn compile_switch(&mut self, expr: &Expr, body: &Stmt) -> Result<(), String> {
let switch_val = self.compile_expr(expr)?;
let switch_ty = Type::from_id(switch_val.borrow().ty);
let merge_bb = self.builder.create_basic_block("switch.merge");
let default_bb = self.builder.create_basic_block("switch.default");
let old_cases = std::mem::take(&mut self.switch_cases);
let old_default = self.default_case_block.take();
let old_merge = self.switch_merge_block.take();
self.switch_merge_block = Some(merge_bb.clone());
self.default_case_block = Some(default_bb.clone());
self.break_targets.push(merge_bb.clone());
self.compile_stmt(body)?;
let cases: Vec<(i64, ValueRef)> = std::mem::take(&mut self.switch_cases);
let default_target = self.default_case_block.take().unwrap_or(default_bb);
if cases.is_empty() {
self.builder.create_br(&merge_bb);
} else {
let switch_inst = instruction::switch(
switch_val,
&default_target,
&cases
.iter()
.map(|(v, bb)| (*v, bb.clone()))
.collect::<Vec<_>>(),
);
if let Some(current_bb) = self.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(switch_inst);
}
}
self.switch_cases = old_cases;
self.default_case_block = old_default;
self.switch_merge_block = old_merge;
self.break_targets.pop();
self.builder.set_insert_point(&merge_bb);
Ok(())
}
fn compile_case(&mut self, value: i64, stmt: &Stmt) -> Result<(), String> {
let case_bb = self
.builder
.create_basic_block(&format!("switch.case.{}", value));
self.builder.create_br(&case_bb);
self.builder.set_insert_point(&case_bb);
self.switch_cases.push((value, case_bb));
self.compile_stmt(stmt)?;
Ok(())
}
fn compile_default(&mut self, stmt: &Stmt) -> Result<(), String> {
let default_bb = self.builder.create_basic_block("switch.default.body");
self.builder.create_br(&default_bb);
self.builder.set_insert_point(&default_bb);
self.default_case_block = Some(default_bb);
self.compile_stmt(stmt)?;
Ok(())
}
pub fn compile_return(&mut self, expr: Option<&Expr>) -> Result<(), String> {
if let Some(expr) = expr {
let val = self.compile_expr(expr)?;
self.builder.create_ret(val);
} else {
self.builder.create_ret_void();
}
Ok(())
}
fn compile_break(&mut self) -> Result<(), String> {
if let Some(target) = self.break_targets.last() {
self.builder.create_br(target);
Ok(())
} else {
Err("break statement not within loop or switch".to_string())
}
}
fn compile_continue(&mut self) -> Result<(), String> {
if let Some(target) = self.continue_targets.last() {
self.builder.create_br(target);
Ok(())
} else {
Err("continue statement not within loop".to_string())
}
}
fn compile_goto(&mut self, label: &str) -> Result<(), String> {
if let Some(target_bb) = self.labels.get(label) {
self.builder.create_br(target_bb);
Ok(())
} else {
let placeholder = self
.builder
.create_basic_block(&format!("goto.forward.{}", label));
self.builder.create_br(&placeholder);
self.forward_gotos.push((label.to_string(), placeholder));
Ok(())
}
}
fn compile_label(&mut self, name: &str, stmt: &Stmt) -> Result<(), String> {
let label_bb = self.builder.create_basic_block(&format!("label.{}", name));
self.builder.create_br(&label_bb);
self.builder.set_insert_point(&label_bb);
self.labels.insert(name.to_string(), label_bb);
self.compile_stmt(stmt)?;
Ok(())
}
pub fn compile_expr(&mut self, expr: &Expr) -> Result<ValueRef, String> {
match expr {
Expr::IntLiteral(v) => self.compile_int_literal(*v),
Expr::UIntLiteral(v, _) => self.compile_int_literal(*v as i64),
Expr::FloatLiteral(v) => self.compile_float_literal(*v),
Expr::DoubleLiteral(v) => self.compile_double_literal(*v),
Expr::CharLiteral(c) => self.compile_char_literal(*c),
Expr::StringLiteral(s) => self.compile_string_literal(s),
Expr::Ident(name) => self.compile_ident(name),
Expr::Unary(op, operand) => self.compile_unary(*op, operand),
Expr::SizeOf(operand) => self.compile_sizeof_expr(operand),
Expr::SizeOfType(tn) => self.compile_sizeof_type(tn),
Expr::AlignOf(operand) => self.compile_alignof_expr(operand),
Expr::AlignOfType(tn) => self.compile_alignof_type(tn),
Expr::Cast(operand, target_tn) => self.compile_cast(operand, target_tn),
Expr::Binary(op, lhs, rhs) => self.compile_binary(*op, lhs, rhs),
Expr::Assign(lhs, rhs) => self.compile_assign(*lhs, rhs),
Expr::Conditional(cond, then, els) => self.compile_conditional(cond, then, els),
Expr::Call(callee, args) => self.compile_call(callee, args),
Expr::Subscript(base, index) => self.compile_subscript(base, index),
Expr::Member(base, field, is_arrow) => self.compile_member(base, field, *is_arrow),
Expr::PostInc(operand) => self.compile_post_inc(operand),
Expr::PostDec(operand) => self.compile_post_dec(operand),
Expr::PreInc(operand) => self.compile_pre_inc(operand),
Expr::PreDec(operand) => self.compile_pre_dec(operand),
Expr::CompoundLiteral(tn, init) => self.compile_compound_literal(tn, init),
Expr::AggregateLiteral(vals) => {
if let Some(first) = vals.first() {
self.compile_expr(first)
} else {
let ty = Type::void();
Value::new(ty.id).with_subclass(crate::value::SubclassKind::Undef);
Value::new(ty.id)
}
}
}
}
pub fn compile_int_literal(&self, v: i64) -> Result<ValueRef, String> {
let int_ty = Type::i32();
let val = Value::new(int_ty.id);
val.borrow_mut().subclass = SubclassKind::ConstantInt;
val.borrow_mut().subclass_data = Some(v as u64);
val.borrow_mut().is_constant = true;
Ok(val)
}
pub fn compile_float_literal(&self, v: f64) -> Result<ValueRef, String> {
let float_ty = Type::float();
let val = Value::new(float_ty.id);
val.borrow_mut().subclass = SubclassKind::ConstantFP;
val.borrow_mut().subclass_data = Some(v.to_bits());
val.borrow_mut().is_constant = true;
Ok(val)
}
pub fn compile_double_literal(&self, v: f64) -> Result<ValueRef, String> {
let double_ty = Type::double();
let val = Value::new(double_ty.id);
val.borrow_mut().subclass = SubclassKind::ConstantFP;
val.borrow_mut().subclass_data = Some(v.to_bits());
val.borrow_mut().is_constant = true;
Ok(val)
}
pub fn compile_char_literal(&self, c: u8) -> Result<ValueRef, String> {
let char_ty = Type::i8();
let val = Value::new(char_ty.id);
val.borrow_mut().subclass = SubclassKind::ConstantInt;
val.borrow_mut().subclass_data = Some(c as u64);
val.borrow_mut().is_constant = true;
Ok(val)
}
pub fn compile_string_literal(&mut self, s: &str) -> Result<ValueRef, String> {
if let Some(existing) = self.string_pool.get(s) {
return Ok(existing.clone());
}
let bytes: Vec<u8> = s
.as_bytes()
.iter()
.chain(std::iter::once(&0u8))
.copied()
.collect();
let char_ty = Type::i8();
let array_ty = Type::array_with(char_ty.id, bytes.len() as u64);
let str_global = Value::named(&format!(".str.{}", self.string_pool.len()));
str_global.borrow_mut().ty = array_ty.id;
str_global.borrow_mut().subclass = SubclassKind::GlobalVariable;
self.module.add_global_variable(str_global.clone());
let ptr_ty = Type::pointer(char_ty.id);
let gep_val = Value::new(ptr_ty.id);
gep_val.borrow_mut().subclass = SubclassKind::GEPOperator;
self.string_pool.insert(s.to_string(), gep_val.clone());
Ok(gep_val)
}
pub fn compile_ident(&mut self, name: &str) -> Result<ValueRef, String> {
if let Some(val) = self.named_values.get(name) {
return self.compile_rvalue(val.clone());
}
if let Some(val) = self.global_values.get(name) {
return Ok(val.clone());
}
if let Some(val) = self.functions.get(name) {
return Ok(val.clone());
}
Err(format!("undefined identifier: {}", name))
}
pub fn compile_binary(
&mut self,
op: BinaryOp,
lhs: &Expr,
rhs: &Expr,
) -> Result<ValueRef, String> {
let lhs_val = self.compile_expr(lhs)?;
let rhs_val = self.compile_expr(rhs)?;
let lhs_ty = Type::from_id(lhs_val.borrow().ty);
let rhs_ty = Type::from_id(rhs_val.borrow().ty);
let (lhs_val, rhs_val, common_ty) = self.usual_arithmetic_conversions(lhs_val, rhs_val)?;
let result = match op {
BinaryOp::Add => self.builder.create_add(lhs_val, rhs_val),
BinaryOp::Sub => self.builder.create_sub(lhs_val, rhs_val),
BinaryOp::Mul => self.builder.create_mul(lhs_val, rhs_val),
BinaryOp::Div => {
if common_ty.is_floating_point() {
self.builder.create_fdiv(lhs_val, rhs_val)
} else if type_is_unsigned(&common_ty) {
self.builder.create_udiv(lhs_val, rhs_val)
} else {
self.builder.create_sdiv(lhs_val, rhs_val)
}
}
BinaryOp::Mod => {
if type_is_unsigned(&common_ty) {
self.builder.create_urem(lhs_val, rhs_val)
} else {
self.builder.create_srem(lhs_val, rhs_val)
}
}
BinaryOp::And => self.builder.create_and(lhs_val, rhs_val),
BinaryOp::Or => self.builder.create_or(lhs_val, rhs_val),
BinaryOp::Xor => self.builder.create_xor(lhs_val, rhs_val),
BinaryOp::Shl => self.builder.create_shl(lhs_val, rhs_val),
BinaryOp::Shr => {
if type_is_unsigned(&common_ty) {
self.builder.create_lshr(lhs_val, rhs_val)
} else {
self.builder.create_ashr(lhs_val, rhs_val)
}
}
BinaryOp::Eq => {
if common_ty.is_floating_point() {
self.builder.create_fcmp(FCmpPred::OEQ, lhs_val, rhs_val)
} else {
self.builder.create_icmp(ICmpPred::EQ, lhs_val, rhs_val)
}
}
BinaryOp::Ne => {
if common_ty.is_floating_point() {
self.builder.create_fcmp(FCmpPred::ONE, lhs_val, rhs_val)
} else {
self.builder.create_icmp(ICmpPred::NE, lhs_val, rhs_val)
}
}
BinaryOp::Lt => {
if common_ty.is_floating_point() {
self.builder.create_fcmp(FCmpPred::OLT, lhs_val, rhs_val)
} else if type_is_unsigned(&common_ty) {
self.builder.create_icmp(ICmpPred::ULT, lhs_val, rhs_val)
} else {
self.builder.create_icmp(ICmpPred::SLT, lhs_val, rhs_val)
}
}
BinaryOp::Gt => {
if common_ty.is_floating_point() {
self.builder.create_fcmp(FCmpPred::OGT, lhs_val, rhs_val)
} else if type_is_unsigned(&common_ty) {
self.builder.create_icmp(ICmpPred::UGT, lhs_val, rhs_val)
} else {
self.builder.create_icmp(ICmpPred::SGT, lhs_val, rhs_val)
}
}
BinaryOp::Le => {
if common_ty.is_floating_point() {
self.builder.create_fcmp(FCmpPred::OLE, lhs_val, rhs_val)
} else if type_is_unsigned(&common_ty) {
self.builder.create_icmp(ICmpPred::ULE, lhs_val, rhs_val)
} else {
self.builder.create_icmp(ICmpPred::SLE, lhs_val, rhs_val)
}
}
BinaryOp::Ge => {
if common_ty.is_floating_point() {
self.builder.create_fcmp(FCmpPred::OGE, lhs_val, rhs_val)
} else if type_is_unsigned(&common_ty) {
self.builder.create_icmp(ICmpPred::UGE, lhs_val, rhs_val)
} else {
self.builder.create_icmp(ICmpPred::SGE, lhs_val, rhs_val)
}
}
BinaryOp::LogicAnd => self.compile_logical_and(lhs_val, rhs_val)?,
BinaryOp::LogicOr => self.compile_logical_or(lhs_val, rhs_val)?,
BinaryOp::Comma => rhs_val.clone(),
BinaryOp::Assign
| BinaryOp::AddAssign
| BinaryOp::SubAssign
| BinaryOp::MulAssign
| BinaryOp::DivAssign
| BinaryOp::ModAssign
| BinaryOp::AndAssign
| BinaryOp::OrAssign
| BinaryOp::XorAssign
| BinaryOp::ShlAssign
| BinaryOp::ShrAssign => {
return Ok(result);
}
};
Ok(result)
}
pub fn compile_unary(&mut self, op: UnaryOp, operand: &Expr) -> Result<ValueRef, String> {
match op {
UnaryOp::Plus => {
let val = self.compile_expr(operand)?;
self.integer_promotion(val)
}
UnaryOp::Minus => {
let val = self.compile_expr(operand)?;
let ty = Type::from_id(val.borrow().ty);
if ty.is_floating_point() {
let zero = self.compile_float_literal(0.0)?;
Ok(self.builder.create_fsub(zero, val))
} else {
let zero = self.compile_int_literal(0)?;
let zero_casted = self.create_int_cast(zero, val.borrow().ty)?;
Ok(self.builder.create_sub(zero_casted, val))
}
}
UnaryOp::Not => {
let val = self.compile_expr(operand)?;
let bool_val = self.int_to_bool(val);
let one = self.compile_int_literal(1)?;
let one_bool = Value::new(Type::i1().id);
one_bool.borrow_mut().subclass = SubclassKind::ConstantInt;
one_bool.borrow_mut().subclass_data = Some(1);
one_bool.borrow_mut().is_constant = true;
Ok(self.builder.create_xor(bool_val, one_bool))
}
UnaryOp::BitNot => {
let val = self.compile_expr(operand)?;
let ty = Type::from_id(val.borrow().ty);
let all_ones = self.compile_int_literal(-1)?;
let all_ones_casted = self.create_int_cast(all_ones, val.borrow().ty)?;
Ok(self.builder.create_xor(val, all_ones_casted))
}
UnaryOp::AddrOf => self.compile_lvalue(operand),
UnaryOp::Deref => {
let val = self.compile_expr(operand)?;
let ptr_ty = Type::from_id(val.borrow().ty);
let pointee_ty_id = ptr_ty.element_type_id().unwrap_or(Type::i32().id);
Ok(self.builder.create_load(val, Type::from_id(pointee_ty_id)))
}
}
}
pub fn compile_call(&mut self, callee: &str, args: &[Expr]) -> Result<ValueRef, String> {
if callee.starts_with("__builtin_") {
return self.compile_builtin_call(callee, args);
}
let func_val = self
.functions
.get(callee)
.cloned()
.ok_or_else(|| format!("undefined function: {}", callee))?;
let func_ty = Type::from_id(func_val.borrow().ty);
let return_ty_id = func_ty.function_return_type_id().unwrap_or(Type::void().id);
let mut arg_values: Vec<ValueRef> = Vec::new();
for arg in args {
let arg_val = self.compile_expr(arg)?;
arg_values.push(arg_val);
}
let call_inst =
instruction::call(func_val.clone(), &arg_values, Type::from_id(return_ty_id));
let result_val = Value::new(return_ty_id);
result_val.borrow_mut().subclass = SubclassKind::CallInst;
result_val.borrow_mut().result = Some(call_inst);
if let Some(current_bb) = self.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(call_inst);
}
Ok(result_val)
}
pub fn compile_assign(
&mut self,
op: BinaryOp,
lhs: &Expr,
rhs: &Expr,
) -> Result<ValueRef, String> {
let lvalue = self.compile_lvalue(lhs)?;
let rhs_val = self.compile_expr(rhs)?;
let result_val = match op {
BinaryOp::Assign => rhs_val.clone(),
BinaryOp::AddAssign => {
let loaded = self.compile_rvalue(lvalue.clone())?;
self.builder.create_add(loaded, rhs_val)
}
BinaryOp::SubAssign => {
let loaded = self.compile_rvalue(lvalue.clone())?;
self.builder.create_sub(loaded, rhs_val)
}
BinaryOp::MulAssign => {
let loaded = self.compile_rvalue(lvalue.clone())?;
self.builder.create_mul(loaded, rhs_val)
}
BinaryOp::DivAssign => {
let loaded = self.compile_rvalue(lvalue.clone())?;
self.builder.create_sdiv(loaded, rhs_val)
}
BinaryOp::ModAssign => {
let loaded = self.compile_rvalue(lvalue.clone())?;
self.builder.create_srem(loaded, rhs_val)
}
BinaryOp::AndAssign => {
let loaded = self.compile_rvalue(lvalue.clone())?;
self.builder.create_and(loaded, rhs_val)
}
BinaryOp::OrAssign => {
let loaded = self.compile_rvalue(lvalue.clone())?;
self.builder.create_or(loaded, rhs_val)
}
BinaryOp::XorAssign => {
let loaded = self.compile_rvalue(lvalue.clone())?;
self.builder.create_xor(loaded, rhs_val)
}
BinaryOp::ShlAssign => {
let loaded = self.compile_rvalue(lvalue.clone())?;
self.builder.create_shl(loaded, rhs_val)
}
BinaryOp::ShrAssign => {
let loaded = self.compile_rvalue(lvalue.clone())?;
self.builder.create_ashr(loaded, rhs_val)
}
_ => rhs_val.clone(),
};
self.builder.create_store(result_val.clone(), lvalue);
Ok(result_val)
}
pub fn compile_cast(
&mut self,
operand: &Expr,
target_tn: &TypeNode,
) -> Result<ValueRef, String> {
let val = self.compile_expr(operand)?;
let target_ty = self.convert_type(target_tn);
let source_ty = Type::from_id(val.borrow().ty);
self.convert_scalar(val, source_ty, target_ty)
}
pub fn compile_conditional(
&mut self,
cond: &Expr,
then: &Expr,
els: &Expr,
) -> Result<ValueRef, String> {
let cond_val = self.compile_expr(cond)?;
let cond_bool = self.int_to_bool(cond_val);
let then_bb = self.builder.create_basic_block("cond.true");
let else_bb = self.builder.create_basic_block("cond.false");
let merge_bb = self.builder.create_basic_block("cond.merge");
self.builder.create_cond_br(cond_bool, &then_bb, &else_bb);
self.builder.set_insert_point(&then_bb);
let then_val = self.compile_expr(then)?;
self.builder.create_br(&merge_bb);
let then_end_bb = self.builder.get_insert_block().unwrap();
self.builder.set_insert_point(&else_bb);
let else_val = self.compile_expr(els)?;
self.builder.create_br(&merge_bb);
let else_end_bb = self.builder.get_insert_block().unwrap();
self.builder.set_insert_point(&merge_bb);
let phi = instruction::phi(
Type::from_id(then_val.borrow().ty),
&[(then_val, then_end_bb), (else_val, else_end_bb)],
);
let result = Value::new(then_val.borrow().ty);
result.borrow_mut().subclass = SubclassKind::PHINode;
if let Some(current_bb) = self.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(phi);
}
Ok(result)
}
pub fn compile_lvalue(&mut self, expr: &Expr) -> Result<ValueRef, String> {
match expr {
Expr::Ident(name) => {
if let Some(val) = self.named_values.get(name) {
return Ok(val.clone());
}
if let Some(val) = self.global_values.get(name) {
return Ok(val.clone());
}
Err(format!("cannot take address of {}", name))
}
Expr::Subscript(base, index) => self.compile_subscript_lvalue(base, index),
Expr::Member(base, field, is_arrow) => {
self.compile_member_lvalue(base, field, *is_arrow)
}
Expr::Unary(UnaryOp::Deref, operand) => self.compile_expr(operand),
_ => {
let val = self.compile_expr(expr)?;
let alloca = self.builder.create_alloca(Type::from_id(val.borrow().ty));
self.builder.create_store(val, alloca.clone());
Ok(alloca)
}
}
}
pub fn compile_rvalue(&mut self, lvalue: ValueRef) -> Result<ValueRef, String> {
let ty = Type::from_id(lvalue.borrow().ty);
if ty.is_pointer() {
if let Some(pointee_ty_id) = ty.element_type_id() {
let pointee_ty = Type::from_id(pointee_ty_id);
if !pointee_ty.is_pointer() {
return Ok(self.builder.create_load(lvalue, pointee_ty));
}
}
}
if ty.is_pointer() {
let pointee_ty_id = ty.element_type_id().unwrap_or(Type::i32().id);
Ok(self
.builder
.create_load(lvalue, Type::from_id(pointee_ty_id)))
} else {
Ok(lvalue)
}
}
fn compile_subscript(&mut self, base: &Expr, index: &Expr) -> Result<ValueRef, String> {
let ptr = self.compile_subscript_lvalue(base, index)?;
self.compile_rvalue(ptr)
}
fn compile_subscript_lvalue(&mut self, base: &Expr, index: &Expr) -> Result<ValueRef, String> {
let base_val = self.compile_expr(base)?;
let index_val = self.compile_expr(index)?;
let ptr_val = self.array_to_pointer_decay(base_val)?;
let ptr_ty = Type::from_id(ptr_val.borrow().ty);
let elem_ty_id = ptr_ty.element_type_id().unwrap_or(Type::i32().id);
let gep = instruction::getelementptr(
ptr_val,
Type::from_id(elem_ty_id),
&[index_val],
Type::from_id(elem_ty_id),
);
let result = Value::new(Type::pointer(elem_ty_id).id);
result.borrow_mut().subclass = SubclassKind::GEPOperator;
if let Some(current_bb) = self.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(gep);
}
Ok(result)
}
fn compile_member(
&mut self,
base: &Expr,
field: &str,
is_arrow: bool,
) -> Result<ValueRef, String> {
let ptr = self.compile_member_lvalue(base, field, is_arrow)?;
self.compile_rvalue(ptr)
}
fn compile_member_lvalue(
&mut self,
base: &Expr,
field: &str,
is_arrow: bool,
) -> Result<ValueRef, String> {
let base_val = self.compile_expr(base)?;
let ptr_val = if is_arrow {
base_val
} else {
let base_ty = Type::from_id(base_val.borrow().ty);
let alloca = self.builder.create_alloca(base_ty);
self.builder.create_store(base_val, alloca.clone());
alloca
};
let ptr_ty = Type::from_id(ptr_val.borrow().ty);
let struct_ty_id = if ptr_ty.is_pointer() {
ptr_ty.element_type_id().unwrap_or(Type::i32().id)
} else {
ptr_val.borrow().ty
};
let field_index = self.compute_field_index(struct_ty_id, field)?;
let field_idx_val = self.compile_int_literal(field_index as i64)?;
let gep = instruction::getelementptr(
ptr_val,
Type::from_id(struct_ty_id),
&[
self.compile_int_literal(0)?, field_idx_val, ],
Type::from_id(struct_ty_id),
);
let result = Value::new(Type::pointer(struct_ty_id).id);
result.borrow_mut().subclass = SubclassKind::GEPOperator;
if let Some(current_bb) = self.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(gep);
}
Ok(result)
}
fn compute_field_index(&self, struct_ty_id: TypeId, field_name: &str) -> Result<usize, String> {
if let Some(field_names) = self.struct_field_names.get(&struct_ty_id) {
for (i, name) in field_names.iter().enumerate() {
if name == field_name {
return Ok(i);
}
}
Err(format!(
"field '{}' not found in struct type (id={})",
field_name, struct_ty_id
))
} else {
Err(format!(
"struct type (id={}) has no field names registered",
struct_ty_id
))
}
}
fn compile_post_inc(&mut self, operand: &Expr) -> Result<ValueRef, String> {
let lvalue = self.compile_lvalue(operand)?;
let old_val = self.compile_rvalue(lvalue.clone())?;
let one = self.compile_int_literal(1)?;
let one_casted = self.create_int_cast(one, old_val.borrow().ty)?;
let new_val = self.builder.create_add(old_val.clone(), one_casted);
self.builder.create_store(new_val, lvalue);
Ok(old_val)
}
fn compile_post_dec(&mut self, operand: &Expr) -> Result<ValueRef, String> {
let lvalue = self.compile_lvalue(operand)?;
let old_val = self.compile_rvalue(lvalue.clone())?;
let one = self.compile_int_literal(1)?;
let one_casted = self.create_int_cast(one, old_val.borrow().ty)?;
let new_val = self.builder.create_sub(old_val.clone(), one_casted);
self.builder.create_store(new_val, lvalue);
Ok(old_val)
}
fn compile_pre_inc(&mut self, operand: &Expr) -> Result<ValueRef, String> {
let lvalue = self.compile_lvalue(operand)?;
let old_val = self.compile_rvalue(lvalue.clone())?;
let one = self.compile_int_literal(1)?;
let one_casted = self.create_int_cast(one, old_val.borrow().ty)?;
let new_val = self.builder.create_add(old_val, one_casted);
self.builder.create_store(new_val.clone(), lvalue);
Ok(new_val)
}
fn compile_pre_dec(&mut self, operand: &Expr) -> Result<ValueRef, String> {
let lvalue = self.compile_lvalue(operand)?;
let old_val = self.compile_rvalue(lvalue.clone())?;
let one = self.compile_int_literal(1)?;
let one_casted = self.create_int_cast(one, old_val.borrow().ty)?;
let new_val = self.builder.create_sub(old_val, one_casted);
self.builder.create_store(new_val.clone(), lvalue);
Ok(new_val)
}
fn compile_compound_literal(&mut self, tn: &TypeNode, init: &Expr) -> Result<ValueRef, String> {
let ty = self.convert_type(tn);
let init_val = self.compile_expr(init)?;
let alloca = self.builder.create_alloca(ty);
self.builder.create_store(init_val, alloca.clone());
Ok(alloca)
}
fn compile_sizeof_expr(&mut self, operand: &Expr) -> Result<ValueRef, String> {
let val = self.compile_expr(operand)?;
let ty = Type::from_id(val.borrow().ty);
let size = ty.size_in_bytes().unwrap_or(4);
self.compile_int_literal(size as i64)
}
fn compile_sizeof_type(&mut self, tn: &TypeNode) -> Result<ValueRef, String> {
let ty = self.convert_type(tn);
let size = ty.size_in_bytes().unwrap_or(4);
self.compile_int_literal(size as i64)
}
fn compile_alignof_expr(&mut self, operand: &Expr) -> Result<ValueRef, String> {
let val = self.compile_expr(operand)?;
let ty = Type::from_id(val.borrow().ty);
let align = ty.alignment().unwrap_or(4);
self.compile_int_literal(align as i64)
}
fn compile_alignof_type(&mut self, tn: &TypeNode) -> Result<ValueRef, String> {
let ty = self.convert_type(tn);
let align = ty.alignment().unwrap_or(4);
self.compile_int_literal(align as i64)
}
fn compile_logical_and(&mut self, lhs: ValueRef, rhs: ValueRef) -> Result<ValueRef, String> {
let lhs_bool = self.int_to_bool(lhs);
let rhs_bb = self.builder.create_basic_block("land.rhs");
let merge_bb = self.builder.create_basic_block("land.merge");
self.builder
.create_cond_br(lhs_bool.clone(), &rhs_bb, &merge_bb);
let false_val = self.builder.get_bool(false);
let true_val = self.builder.get_bool(true);
self.builder.set_insert_point(&rhs_bb);
let rhs_bool = self.int_to_bool(rhs);
let rhs_end_bb = self.builder.get_insert_block().unwrap();
self.builder.create_br(&merge_bb);
self.builder.set_insert_point(&merge_bb);
let phi = instruction::phi(
Type::i1(),
&[(false_val, rhs_end_bb.clone()), (false_val, rhs_end_bb)],
);
let result = Value::new(Type::i1().id);
if let Some(current_bb) = self.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(phi);
}
Ok(result)
}
fn compile_logical_or(&mut self, lhs: ValueRef, rhs: ValueRef) -> Result<ValueRef, String> {
let lhs_bool = self.int_to_bool(lhs);
let rhs_bb = self.builder.create_basic_block("lor.rhs");
let merge_bb = self.builder.create_basic_block("lor.merge");
self.builder
.create_cond_br(lhs_bool.clone(), &merge_bb, &rhs_bb);
self.builder.set_insert_point(&rhs_bb);
let rhs_bool = self.int_to_bool(rhs);
let rhs_end_bb = self.builder.get_insert_block().unwrap();
self.builder.create_br(&merge_bb);
self.builder.set_insert_point(&merge_bb);
let phi = instruction::phi(
Type::i1(),
&[
(self.builder.get_bool(true), rhs_end_bb.clone()),
(rhs_bool, rhs_end_bb),
],
);
let result = Value::new(Type::i1().id);
if let Some(current_bb) = self.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(phi);
}
Ok(result)
}
pub fn usual_arithmetic_conversions(
&mut self,
lhs: ValueRef,
rhs: ValueRef,
) -> Result<(ValueRef, ValueRef, Type), String> {
let lhs_ty = Type::from_id(lhs.borrow().ty);
let rhs_ty = Type::from_id(rhs.borrow().ty);
if lhs_ty.id == rhs_ty.id {
return Ok((lhs, rhs, lhs_ty));
}
let lhs_promoted = self.integer_promotion(lhs)?;
let rhs_promoted = self.integer_promotion(rhs)?;
let lhs_ty = Type::from_id(lhs_promoted.borrow().ty);
let rhs_ty = Type::from_id(rhs_promoted.borrow().ty);
if lhs_ty.is_floating_point() && !rhs_ty.is_floating_point() {
let rhs_casted = self.convert_scalar(rhs_promoted, rhs_ty, lhs_ty.clone())?;
return Ok((lhs_promoted, rhs_casted, lhs_ty));
}
if rhs_ty.is_floating_point() && !lhs_ty.is_floating_point() {
let lhs_casted = self.convert_scalar(lhs_promoted, lhs_ty, rhs_ty.clone())?;
return Ok((lhs_casted, rhs_promoted, rhs_ty));
}
let lhs_size = lhs_ty.size_in_bits().unwrap_or(32);
let rhs_size = rhs_ty.size_in_bits().unwrap_or(32);
if lhs_size > rhs_size {
let rhs_casted = self.create_int_cast(rhs_promoted, lhs_ty.id)?;
Ok((lhs_promoted, rhs_casted, lhs_ty))
} else {
let lhs_casted = self.create_int_cast(lhs_promoted, rhs_ty.id)?;
Ok((lhs_casted, rhs_promoted, rhs_ty))
}
}
pub fn integer_promotion(&mut self, val: ValueRef) -> Result<ValueRef, String> {
let ty = Type::from_id(val.borrow().ty);
if !ty.is_integer() {
return Ok(val);
}
let bit_width = ty.integer_bit_width().unwrap_or(32);
if bit_width < 32 {
let int_ty = Type::i32();
self.convert_scalar(val, ty, int_ty)
} else {
Ok(val)
}
}
pub fn int_to_bool(&mut self, val: ValueRef) -> ValueRef {
let ty = Type::from_id(val.borrow().ty);
if ty.id == Type::i1().id {
return val;
}
let zero = Value::new(ty.id);
zero.borrow_mut().subclass = SubclassKind::ConstantInt;
zero.borrow_mut().subclass_data = Some(0);
zero.borrow_mut().is_constant = true;
self.builder.create_icmp(ICmpPred::NE, val, zero)
}
pub fn convert_scalar(
&mut self,
val: ValueRef,
from_ty: Type,
to_ty: Type,
) -> Result<ValueRef, String> {
if from_ty.id == to_ty.id {
return Ok(val);
}
match (&from_ty.kind, &to_ty.kind) {
(TypeKind::Integer { .. }, TypeKind::Integer { .. }) => {
let from_bits = from_ty.size_in_bits().unwrap_or(32);
let to_bits = to_ty.size_in_bits().unwrap_or(32);
if to_bits == 1 && from_bits > 1 {
Ok(self.int_to_bool(val))
} else if from_bits > to_bits {
Ok(self.builder.create_trunc(val, to_ty))
} else if from_bits < to_bits {
Ok(self.builder.create_zext(val, to_ty))
} else {
Ok(val)
}
}
(TypeKind::Integer { .. }, _) if to_ty.is_floating_point() => {
if type_is_unsigned(&from_ty) {
Ok(self.builder.create_uitofp(val, to_ty))
} else {
Ok(self.builder.create_sitofp(val, to_ty))
}
}
(_, TypeKind::Integer { .. }) if from_ty.is_floating_point() => {
if type_is_unsigned(&to_ty) {
Ok(self.builder.create_fptoui(val, to_ty))
} else {
Ok(self.builder.create_fptosi(val, to_ty))
}
}
_ if from_ty.is_floating_point() && to_ty.is_floating_point() => {
let from_bits = from_ty.size_in_bits().unwrap_or(32);
let to_bits = to_ty.size_in_bits().unwrap_or(64);
if from_bits > to_bits {
Ok(self.builder.create_fptrunc(val, to_ty))
} else {
Ok(self.builder.create_fpext(val, to_ty))
}
}
(TypeKind::Pointer { .. }, TypeKind::Integer { .. }) => {
Ok(self.builder.create_ptrtoint(val, to_ty))
}
(TypeKind::Integer { .. }, TypeKind::Pointer { .. }) => {
Ok(self.builder.create_inttoptr(val, to_ty))
}
(TypeKind::Pointer { .. }, TypeKind::Pointer { .. }) => {
Ok(self.builder.create_bitcast(val, to_ty))
}
_ => Ok(self.builder.create_bitcast(val, to_ty)),
}
}
fn create_int_cast(&mut self, val: ValueRef, target_ty_id: TypeId) -> Result<ValueRef, String> {
let from_ty = Type::from_id(val.borrow().ty);
let to_ty = Type::from_id(target_ty_id);
if !from_ty.is_integer() || !to_ty.is_integer() {
return Ok(self.builder.create_bitcast(val, to_ty));
}
let from_bits = from_ty.size_in_bits().unwrap_or(32);
let to_bits = to_ty.size_in_bits().unwrap_or(32);
if from_bits > to_bits {
Ok(self.builder.create_trunc(val, to_ty))
} else if from_bits < to_bits {
Ok(self.builder.create_zext(val, to_ty))
} else {
Ok(val)
}
}
pub fn array_to_pointer_decay(&mut self, val: ValueRef) -> Result<ValueRef, String> {
let ty = Type::from_id(val.borrow().ty);
if !ty.is_array() {
return Ok(val);
}
let elem_ty_id = ty.element_type_id().unwrap_or(Type::i32().id);
let idx_val = self.compile_int_literal(0)?;
let gep = instruction::getelementptr(
val,
Type::from_id(elem_ty_id),
&[idx_val],
Type::from_id(elem_ty_id),
);
let ptr_ty = Type::pointer(elem_ty_id);
let result = Value::new(ptr_ty.id);
result.borrow_mut().subclass = SubclassKind::GEPOperator;
if let Some(current_bb) = self.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(gep);
}
Ok(result)
}
pub fn compute_member_gep(
&mut self,
struct_ptr: ValueRef,
struct_ty: Type,
field_name: &str,
) -> Result<ValueRef, String> {
let field_index = self.compute_field_index(struct_ty.id, field_name)?;
let idx0 = self.compile_int_literal(0)?;
let idx1 = self.compile_int_literal(field_index as i64)?;
let gep =
instruction::getelementptr(struct_ptr, struct_ty.clone(), &[idx0, idx1], struct_ty);
let result = Value::new(Type::pointer(struct_ty.id).id);
result.borrow_mut().subclass = SubclassKind::GEPOperator;
if let Some(current_bb) = self.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(gep);
}
Ok(result)
}
pub fn lower_struct_type(&self, struct_ty: &Type) -> Type {
let size = struct_ty.size_in_bits().unwrap_or(64);
if size <= 64 {
Type::int(size as u32)
} else {
struct_ty.clone()
}
}
pub fn lower_array_type(&self, array_ty: &Type) -> Type {
if let Some(elem_ty_id) = array_ty.element_type_id() {
Type::pointer(elem_ty_id)
} else {
array_ty.clone()
}
}
fn evaluate_const_expr(&self, expr: &Expr) -> Option<i64> {
match expr {
Expr::IntLiteral(v) => Some(*v),
Expr::CharLiteral(c) => Some(*c as i64),
Expr::Unary(UnaryOp::Minus, operand) => self.evaluate_const_expr(operand).map(|v| -v),
Expr::Unary(UnaryOp::Plus, operand) => self.evaluate_const_expr(operand),
Expr::Unary(UnaryOp::Not, operand) => {
self.evaluate_const_expr(operand)
.map(|v| if v == 0 { 1 } else { 0 })
}
Expr::Unary(UnaryOp::BitNot, operand) => self.evaluate_const_expr(operand).map(|v| !v),
Expr::Binary(op, lhs, rhs) => {
let l = self.evaluate_const_expr(lhs)?;
let r = self.evaluate_const_expr(rhs)?;
match op {
BinaryOp::Add => Some(l.wrapping_add(r)),
BinaryOp::Sub => Some(l.wrapping_sub(r)),
BinaryOp::Mul => Some(l.wrapping_mul(r)),
BinaryOp::Div => {
if r != 0 {
Some(l / r)
} else {
None
}
}
BinaryOp::Mod => {
if r != 0 {
Some(l % r)
} else {
None
}
}
BinaryOp::And => Some(l & r),
BinaryOp::Or => Some(l | r),
BinaryOp::Xor => Some(l ^ r),
BinaryOp::Shl => Some(l << r),
BinaryOp::Shr => Some(l >> r),
BinaryOp::Eq => Some(if l == r { 1 } else { 0 }),
BinaryOp::Ne => Some(if l != r { 1 } else { 0 }),
BinaryOp::Lt => Some(if l < r { 1 } else { 0 }),
BinaryOp::Gt => Some(if l > r { 1 } else { 0 }),
BinaryOp::Le => Some(if l <= r { 1 } else { 0 }),
BinaryOp::Ge => Some(if l >= r { 1 } else { 0 }),
_ => None,
}
}
Expr::Cast(operand, _) => self.evaluate_const_expr(operand),
Expr::Conditional(cond, then, els) => {
let c = self.evaluate_const_expr(cond)?;
if c != 0 {
self.evaluate_const_expr(then)
} else {
self.evaluate_const_expr(els)
}
}
_ => None,
}
}
pub fn get_builtin_kind(name: &str) -> BuiltinKind {
match name {
"__builtin_alloca" => BuiltinKind::Alloca,
"__builtin_expect" => BuiltinKind::Expect,
"__builtin_prefetch" => BuiltinKind::Prefetch,
"__builtin_assume" => BuiltinKind::Assume,
"__builtin_unreachable" => BuiltinKind::Unreachable,
"__builtin_trap" => BuiltinKind::Trap,
"__builtin_debugtrap" => BuiltinKind::Debugtrap,
"__builtin_memcpy" => BuiltinKind::Memcpy,
"__builtin_memmove" => BuiltinKind::Memmove,
"__builtin_memset" => BuiltinKind::Memset,
"__builtin_bswap16" => BuiltinKind::Bswap16,
"__builtin_bswap32" => BuiltinKind::Bswap32,
"__builtin_bswap64" => BuiltinKind::Bswap64,
"__builtin_clz" => BuiltinKind::Clz,
"__builtin_clzll" => BuiltinKind::Clzll,
"__builtin_ctz" => BuiltinKind::Ctz,
"__builtin_ctzll" => BuiltinKind::Ctzll,
"__builtin_clrsb" => BuiltinKind::Clrsb,
"__builtin_clrsbll" => BuiltinKind::Clrsbll,
"__builtin_popcount" => BuiltinKind::Popcount,
"__builtin_popcountll" => BuiltinKind::Popcountll,
"__builtin_parity" => BuiltinKind::Parity,
"__builtin_parityll" => BuiltinKind::Parityll,
"__builtin_ffs" => BuiltinKind::Ffs,
"__builtin_ffsll" => BuiltinKind::Ffsll,
"__builtin_sqrt" => BuiltinKind::Sqrt,
"__builtin_sqrtf" => BuiltinKind::Sqrtf,
"__builtin_sqrtl" => BuiltinKind::Sqrtl,
"__builtin_fma" => BuiltinKind::Fma,
"__builtin_fmaf" => BuiltinKind::Fmaf,
"__builtin_fmal" => BuiltinKind::Fmal,
"__builtin_expect_with_probability" => BuiltinKind::ExpectWithProbability,
"__builtin_constant_p" => BuiltinKind::ConstantP,
"__builtin_frame_address" => BuiltinKind::FrameAddress,
"__builtin_return_address" => BuiltinKind::ReturnAddress,
"__builtin_object_size" => BuiltinKind::ObjectSize,
_ => BuiltinKind::Unknown,
}
}
pub fn compile_builtin_call(&mut self, name: &str, args: &[Expr]) -> Result<ValueRef, String> {
let kind = Self::get_builtin_kind(name);
match kind {
BuiltinKind::Alloca => {
if args.is_empty() {
return Err("__builtin_alloca requires a size argument".to_string());
}
let size_val = self.compile_expr(&args[0])?;
let alloca_ty = Type::i8();
let alloca = self.builder.create_alloca(alloca_ty);
Ok(alloca)
}
BuiltinKind::Expect => {
if args.len() < 2 {
return Err("__builtin_expect requires two arguments".to_string());
}
let val = self.compile_expr(&args[0])?;
Ok(val)
}
BuiltinKind::Prefetch => {
if args.is_empty() {
return Err("__builtin_prefetch requires an address argument".to_string());
}
let addr_val = self.compile_expr(&args[0])?;
Ok(addr_val)
}
BuiltinKind::Assume => {
if args.is_empty() {
return Err("__builtin_assume requires a condition".to_string());
}
let cond_val = self.compile_expr(&args[0])?;
Ok(cond_val)
}
BuiltinKind::Unreachable => {
self.builder.create_unreachable();
let undef_val = Value::new(Type::void().id);
Ok(undef_val)
}
BuiltinKind::Trap => {
self.builder.create_unreachable();
let undef_val = Value::new(Type::void().id);
Ok(undef_val)
}
BuiltinKind::Debugtrap => {
self.builder.create_unreachable();
let undef_val = Value::new(Type::void().id);
Ok(undef_val)
}
BuiltinKind::Memcpy => {
if args.len() < 3 {
return Err("__builtin_memcpy requires dest, src, size".to_string());
}
let dest = self.compile_expr(&args[0])?;
let src = self.compile_expr(&args[1])?;
let size = self.compile_expr(&args[2])?;
self.emit_memcpy(dest, src, size)
}
BuiltinKind::Memmove => {
if args.len() < 3 {
return Err("__builtin_memmove requires dest, src, size".to_string());
}
let dest = self.compile_expr(&args[0])?;
let src = self.compile_expr(&args[1])?;
let size = self.compile_expr(&args[2])?;
self.emit_memmove(dest, src, size)
}
BuiltinKind::Memset => {
if args.len() < 3 {
return Err("__builtin_memset requires dest, value, size".to_string());
}
let dest = self.compile_expr(&args[0])?;
let value = self.compile_expr(&args[1])?;
let size = self.compile_expr(&args[2])?;
self.emit_memset(dest, value, size)
}
BuiltinKind::Bswap16 | BuiltinKind::Bswap32 | BuiltinKind::Bswap64 => {
if args.is_empty() {
return Err(format!("{} requires an argument", name));
}
let val = self.compile_expr(&args[0])?;
self.emit_bswap(val, &kind)
}
BuiltinKind::Clz | BuiltinKind::Clzll => {
if args.is_empty() {
return Err(format!("{} requires an argument", name));
}
let val = self.compile_expr(&args[0])?;
self.emit_ctlz(val)
}
BuiltinKind::Ctz | BuiltinKind::Ctzll => {
if args.is_empty() {
return Err(format!("{} requires an argument", name));
}
let val = self.compile_expr(&args[0])?;
self.emit_cttz(val)
}
BuiltinKind::Popcount | BuiltinKind::Popcountll => {
if args.is_empty() {
return Err(format!("{} requires an argument", name));
}
let val = self.compile_expr(&args[0])?;
self.emit_ctpop(val)
}
BuiltinKind::Sqrt | BuiltinKind::Sqrtf | BuiltinKind::Sqrtl => {
if args.is_empty() {
return Err(format!("{} requires an argument", name));
}
let val = self.compile_expr(&args[0])?;
self.emit_sqrt(val)
}
BuiltinKind::Fma | BuiltinKind::Fmaf | BuiltinKind::Fmal => {
if args.len() < 3 {
return Err(format!("{} requires three arguments", name));
}
let a = self.compile_expr(&args[0])?;
let b = self.compile_expr(&args[1])?;
let c = self.compile_expr(&args[2])?;
self.emit_fma(a, b, c)
}
BuiltinKind::ConstantP => {
if args.is_empty() {
return self.compile_int_literal(0);
}
let is_const = self.evaluate_const_expr(&args[0]).is_some();
self.compile_int_literal(if is_const { 1 } else { 0 })
}
BuiltinKind::FrameAddress => {
self.compile_int_literal(0) }
BuiltinKind::ReturnAddress => {
self.compile_int_literal(0) }
BuiltinKind::ObjectSize => {
if args.len() < 2 {
return Err("__builtin_object_size requires two arguments".to_string());
}
self.compile_int_literal(-1)
}
BuiltinKind::ExpectWithProbability
| BuiltinKind::Parity
| BuiltinKind::Parityll
| BuiltinKind::Ffs
| BuiltinKind::Ffsll
| BuiltinKind::Clrsb
| BuiltinKind::Clrsbll => {
if args.is_empty() {
return Err(format!("{} requires an argument", name));
}
let val = self.compile_expr(&args[0])?;
Ok(val) }
BuiltinKind::Unknown => Err(format!("unknown builtin: {}", name)),
}
}
fn emit_memcpy(
&mut self,
dest: ValueRef,
src: ValueRef,
size: ValueRef,
) -> Result<ValueRef, String> {
let void_ty = Type::void();
let result = Value::new(void_ty.id);
Ok(result)
}
fn emit_memmove(
&mut self,
dest: ValueRef,
src: ValueRef,
size: ValueRef,
) -> Result<ValueRef, String> {
let void_ty = Type::void();
let result = Value::new(void_ty.id);
Ok(result)
}
fn emit_memset(
&mut self,
dest: ValueRef,
value: ValueRef,
size: ValueRef,
) -> Result<ValueRef, String> {
let void_ty = Type::void();
let result = Value::new(void_ty.id);
Ok(result)
}
fn emit_bswap(&mut self, val: ValueRef, kind: &BuiltinKind) -> Result<ValueRef, String> {
Ok(val)
}
fn emit_ctlz(&mut self, val: ValueRef) -> Result<ValueRef, String> {
Ok(val) }
fn emit_cttz(&mut self, val: ValueRef) -> Result<ValueRef, String> {
Ok(val) }
fn emit_ctpop(&mut self, val: ValueRef) -> Result<ValueRef, String> {
Ok(val) }
fn emit_sqrt(&mut self, val: ValueRef) -> Result<ValueRef, String> {
Ok(val) }
fn emit_fma(&mut self, a: ValueRef, b: ValueRef, c: ValueRef) -> Result<ValueRef, String> {
Ok(a) }
fn block_has_terminator(&self) -> bool {
if let Some(bb) = self.builder.get_insert_block() {
let bb_ref = bb.borrow();
bb_ref
.instructions
.iter()
.any(|inst| instruction::is_terminator(inst.borrow().opcode))
} else {
false
}
}
pub fn get_variable_ptr(&self, name: &str) -> Option<ValueRef> {
self.named_values
.get(name)
.cloned()
.or_else(|| self.global_values.get(name).cloned())
}
pub fn create_basic_block(&mut self, name: &str) -> ValueRef {
self.builder.create_basic_block(name)
}
pub fn set_insert_point(&mut self, bb: &ValueRef) {
self.builder.set_insert_point(bb);
}
pub fn branch_to(&mut self, bb: &ValueRef) {
self.builder.create_br(bb);
}
pub fn conditional_branch(&mut self, cond: ValueRef, then_bb: &ValueRef, else_bb: &ValueRef) {
self.builder.create_cond_br(cond, then_bb, else_bb);
}
pub fn create_alloca(&mut self, ty: Type) -> ValueRef {
self.builder.create_alloca(ty)
}
pub fn create_load(&mut self, ptr: ValueRef, ty: Type) -> ValueRef {
self.builder.create_load(ptr, ty)
}
pub fn create_store(&mut self, val: ValueRef, ptr: ValueRef) {
self.builder.create_store(val, ptr);
}
pub fn create_gep(
&mut self,
ptr: ValueRef,
ty: Type,
indices: &[ValueRef],
result_ty: Type,
) -> ValueRef {
self.builder.create_gep(ptr, ty, indices, result_ty)
}
}
pub struct ExprCodeGen<'a> {
pub gen: &'a mut IRGenerator<'a>,
}
impl<'a> ExprCodeGen<'a> {
pub fn new(gen: &'a mut IRGenerator<'a>) -> Self {
ExprCodeGen { gen }
}
pub fn codegen_integer_literal(&self, value: i64, bits: u32) -> ValueRef {
let int_ty = Type::int(bits);
let val = Value::new(int_ty.id);
val.borrow_mut().subclass = SubclassKind::ConstantInt;
val.borrow_mut().subclass_data = Some(value as u64);
val.borrow_mut().is_constant = true;
val
}
pub fn codegen_unsigned_literal(&self, value: u64, bits: u32) -> ValueRef {
let int_ty = Type::int(bits);
let val = Value::new(int_ty.id);
val.borrow_mut().subclass = SubclassKind::ConstantInt;
val.borrow_mut().subclass_data = Some(value);
val.borrow_mut().is_constant = true;
val
}
pub fn codegen_float_literal(&self, value: f32) -> ValueRef {
let float_ty = Type::float();
let val = Value::new(float_ty.id);
val.borrow_mut().subclass = SubclassKind::ConstantFP;
val.borrow_mut().subclass_data = Some((value as f64).to_bits());
val.borrow_mut().is_constant = true;
val
}
pub fn codegen_double_literal(&self, value: f64) -> ValueRef {
let double_ty = Type::double();
let val = Value::new(double_ty.id);
val.borrow_mut().subclass = SubclassKind::ConstantFP;
val.borrow_mut().subclass_data = Some(value.to_bits());
val.borrow_mut().is_constant = true;
val
}
pub fn codegen_char_literal(&self, c: char) -> ValueRef {
let char_ty = Type::i8();
let val = Value::new(char_ty.id);
val.borrow_mut().subclass = SubclassKind::ConstantInt;
val.borrow_mut().subclass_data = Some(c as u64);
val.borrow_mut().is_constant = true;
val
}
pub fn codegen_arithmetic_binary(
&mut self,
op: BinaryOp,
lhs: ValueRef,
rhs: ValueRef,
result_ty: &Type,
) -> ValueRef {
match op {
BinaryOp::Add => self.gen.builder.create_add(lhs, rhs),
BinaryOp::Sub => self.gen.builder.create_sub(lhs, rhs),
BinaryOp::Mul => self.gen.builder.create_mul(lhs, rhs),
BinaryOp::Div => {
if result_ty.is_floating_point() {
self.gen.builder.create_fdiv(lhs, rhs)
} else if type_is_unsigned(result_ty) {
self.gen.builder.create_udiv(lhs, rhs)
} else {
self.gen.builder.create_sdiv(lhs, rhs)
}
}
BinaryOp::Mod => {
if type_is_unsigned(result_ty) {
self.gen.builder.create_urem(lhs, rhs)
} else {
self.gen.builder.create_srem(lhs, rhs)
}
}
_ => lhs, }
}
pub fn codegen_bitwise_binary(
&mut self,
op: BinaryOp,
lhs: ValueRef,
rhs: ValueRef,
) -> ValueRef {
match op {
BinaryOp::And => self.gen.builder.create_and(lhs, rhs),
BinaryOp::Or => self.gen.builder.create_or(lhs, rhs),
BinaryOp::Xor => self.gen.builder.create_xor(lhs, rhs),
BinaryOp::Shl => self.gen.builder.create_shl(lhs, rhs),
BinaryOp::Shr => self.gen.builder.create_lshr(lhs, rhs), _ => lhs,
}
}
pub fn codegen_comparison(
&mut self,
op: BinaryOp,
lhs: ValueRef,
rhs: ValueRef,
is_float: bool,
is_unsigned: bool,
) -> ValueRef {
if is_float {
let pred = match op {
BinaryOp::Eq => FCmpPred::OEQ,
BinaryOp::Ne => FCmpPred::ONE,
BinaryOp::Lt => FCmpPred::OLT,
BinaryOp::Gt => FCmpPred::OGT,
BinaryOp::Le => FCmpPred::OLE,
BinaryOp::Ge => FCmpPred::OGE,
_ => FCmpPred::OEQ,
};
self.gen.builder.create_fcmp(pred, lhs, rhs)
} else {
let pred = match op {
BinaryOp::Eq => ICmpPred::EQ,
BinaryOp::Ne => ICmpPred::NE,
BinaryOp::Lt => {
if is_unsigned {
ICmpPred::ULT
} else {
ICmpPred::SLT
}
}
BinaryOp::Gt => {
if is_unsigned {
ICmpPred::UGT
} else {
ICmpPred::SGT
}
}
BinaryOp::Le => {
if is_unsigned {
ICmpPred::ULE
} else {
ICmpPred::SLE
}
}
BinaryOp::Ge => {
if is_unsigned {
ICmpPred::UGE
} else {
ICmpPred::SGE
}
}
_ => ICmpPred::EQ,
};
self.gen.builder.create_icmp(pred, lhs, rhs)
}
}
pub fn codegen_logical_and(&mut self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
let lhs_bool = self.gen.int_to_bool(lhs);
let rhs_bool = self.gen.int_to_bool(rhs);
self.gen.builder.create_and(lhs_bool, rhs_bool)
}
pub fn codegen_logical_or(&mut self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
let lhs_bool = self.gen.int_to_bool(lhs);
let rhs_bool = self.gen.int_to_bool(rhs);
self.gen.builder.create_or(lhs_bool, rhs_bool)
}
pub fn codegen_comma(&mut self, _lhs: ValueRef, rhs: ValueRef) -> ValueRef {
rhs
}
pub fn codegen_unary_plus(&mut self, val: ValueRef) -> Result<ValueRef, String> {
self.gen.integer_promotion(val)
}
pub fn codegen_unary_minus(&mut self, val: ValueRef) -> ValueRef {
let ty = Type::from_id(val.borrow().ty);
if ty.is_floating_point() {
let zero = self.codegen_float_literal(0.0);
self.gen.builder.create_fsub(zero, val)
} else {
let zero = self.codegen_integer_literal(0, ty.size_in_bits().unwrap_or(32) as u32);
self.gen.builder.create_sub(zero, val)
}
}
pub fn codegen_logical_not(&mut self, val: ValueRef) -> ValueRef {
let bool_val = self.gen.int_to_bool(val);
let one = Value::new(Type::i1().id);
one.borrow_mut().subclass = SubclassKind::ConstantInt;
one.borrow_mut().subclass_data = Some(1);
one.borrow_mut().is_constant = true;
self.gen.builder.create_xor(bool_val, one)
}
pub fn codegen_bitwise_not(&mut self, val: ValueRef) -> ValueRef {
let ty = Type::from_id(val.borrow().ty);
let all_ones = self.codegen_integer_literal(-1, ty.size_in_bits().unwrap_or(32) as u32);
self.gen.builder.create_xor(val, all_ones)
}
pub fn codegen_address_of(&mut self, expr: &Expr) -> Result<ValueRef, String> {
self.gen.compile_lvalue(expr)
}
pub fn codegen_dereference(&mut self, ptr: ValueRef) -> ValueRef {
let ptr_ty = Type::from_id(ptr.borrow().ty);
let pointee_ty_id = ptr_ty.element_type_id().unwrap_or(Type::i32().id);
self.gen
.builder
.create_load(ptr, Type::from_id(pointee_ty_id))
}
pub fn codegen_trunc(&mut self, val: ValueRef, to_ty: Type) -> ValueRef {
self.gen.builder.create_trunc(val, to_ty)
}
pub fn codegen_zext(&mut self, val: ValueRef, to_ty: Type) -> ValueRef {
self.gen.builder.create_zext(val, to_ty)
}
pub fn codegen_sext(&mut self, val: ValueRef, to_ty: Type) -> ValueRef {
self.gen.builder.create_sext(val, to_ty)
}
pub fn codegen_fptrunc(&mut self, val: ValueRef, to_ty: Type) -> ValueRef {
self.gen.builder.create_fptrunc(val, to_ty)
}
pub fn codegen_fpext(&mut self, val: ValueRef, to_ty: Type) -> ValueRef {
self.gen.builder.create_fpext(val, to_ty)
}
pub fn codegen_sitofp(&mut self, val: ValueRef, to_ty: Type) -> ValueRef {
self.gen.builder.create_sitofp(val, to_ty)
}
pub fn codegen_uitofp(&mut self, val: ValueRef, to_ty: Type) -> ValueRef {
self.gen.builder.create_uitofp(val, to_ty)
}
pub fn codegen_fptosi(&mut self, val: ValueRef, to_ty: Type) -> ValueRef {
self.gen.builder.create_fptosi(val, to_ty)
}
pub fn codegen_fptoui(&mut self, val: ValueRef, to_ty: Type) -> ValueRef {
self.gen.builder.create_fptoui(val, to_ty)
}
pub fn codegen_ptrtoint(&mut self, val: ValueRef, to_ty: Type) -> ValueRef {
self.gen.builder.create_ptrtoint(val, to_ty)
}
pub fn codegen_inttoptr(&mut self, val: ValueRef, to_ty: Type) -> ValueRef {
self.gen.builder.create_inttoptr(val, to_ty)
}
pub fn codegen_bitcast(&mut self, val: ValueRef, to_ty: Type) -> ValueRef {
self.gen.builder.create_bitcast(val, to_ty)
}
pub fn codegen_sizeof_type(&self, tn: &TypeNode) -> ValueRef {
let ty = self.gen.convert_type(tn);
let size = ty.size_in_bytes().unwrap_or(4);
self.codegen_integer_literal(size as i64, 64)
}
pub fn codegen_sizeof_expr(&mut self, expr: &Expr) -> Result<ValueRef, String> {
let val = self.gen.compile_expr(expr)?;
let ty = Type::from_id(val.borrow().ty);
let size = ty.size_in_bytes().unwrap_or(4);
Ok(self.codegen_integer_literal(size as i64, 64))
}
pub fn codegen_alignof_type(&self, tn: &TypeNode) -> ValueRef {
let ty = self.gen.convert_type(tn);
let align = ty.alignment().unwrap_or(4);
self.codegen_integer_literal(align as i64, 64)
}
pub fn codegen_alignof_expr(&mut self, expr: &Expr) -> Result<ValueRef, String> {
let val = self.gen.compile_expr(expr)?;
let ty = Type::from_id(val.borrow().ty);
let align = ty.alignment().unwrap_or(4);
Ok(self.codegen_integer_literal(align as i64, 64))
}
pub fn codegen_conditional(
&mut self,
cond: ValueRef,
then_val: ValueRef,
else_val: ValueRef,
) -> ValueRef {
let cond_bool = self.gen.int_to_bool(cond);
self.gen
.builder
.create_select(cond_bool, then_val, else_val)
}
pub fn codegen_assign(&mut self, lvalue: ValueRef, rvalue: ValueRef) -> ValueRef {
self.gen.builder.create_store(rvalue.clone(), lvalue);
rvalue
}
pub fn codegen_compound_assign(
&mut self,
op: BinaryOp,
lvalue: ValueRef,
rvalue: ValueRef,
) -> ValueRef {
let loaded = self.codegen_dereference(lvalue.clone());
let result = match op {
BinaryOp::AddAssign => self.gen.builder.create_add(loaded, rvalue),
BinaryOp::SubAssign => self.gen.builder.create_sub(loaded, rvalue),
BinaryOp::MulAssign => self.gen.builder.create_mul(loaded, rvalue),
BinaryOp::DivAssign => self.gen.builder.create_sdiv(loaded, rvalue),
BinaryOp::ModAssign => self.gen.builder.create_srem(loaded, rvalue),
BinaryOp::AndAssign => self.gen.builder.create_and(loaded, rvalue),
BinaryOp::OrAssign => self.gen.builder.create_or(loaded, rvalue),
BinaryOp::XorAssign => self.gen.builder.create_xor(loaded, rvalue),
BinaryOp::ShlAssign => self.gen.builder.create_shl(loaded, rvalue),
BinaryOp::ShrAssign => self.gen.builder.create_ashr(loaded, rvalue),
_ => rvalue,
};
self.gen.builder.create_store(result.clone(), lvalue);
result
}
pub fn codegen_call(
&mut self,
callee_name: &str,
args: &[ValueRef],
return_ty: Type,
) -> Result<ValueRef, String> {
let func_val = self
.gen
.functions
.get(callee_name)
.cloned()
.ok_or_else(|| format!("undefined function: {}", callee_name))?;
let call_inst = instruction::call(func_val, args, return_ty.clone());
let result = Value::new(return_ty.id);
result.borrow_mut().subclass = SubclassKind::CallInst;
result.borrow_mut().result = Some(call_inst.clone());
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(call_inst);
}
Ok(result)
}
pub fn codegen_subscript(
&mut self,
base: ValueRef,
index: ValueRef,
elem_ty: Type,
) -> ValueRef {
let ptr = base; let gep = instruction::getelementptr(ptr, elem_ty.clone(), &[index], elem_ty.clone());
let ptr_ty = Type::pointer(elem_ty.id);
let result = Value::new(ptr_ty.id);
result.borrow_mut().subclass = SubclassKind::GEPOperator;
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(gep);
}
self.codegen_dereference(result)
}
pub fn codegen_member_access(
&mut self,
base: ValueRef,
field_name: &str,
is_arrow: bool,
struct_ty: &Type,
) -> Result<ValueRef, String> {
let ptr = if is_arrow {
base
} else {
let alloca = self.gen.builder.create_alloca(struct_ty.clone());
self.gen.builder.create_store(base, alloca.clone());
alloca
};
let field_index = self.gen.compute_field_index(struct_ty.id, field_name)?;
let idx0 = self.gen.builder.get_int32(0);
let idx1 = Value::new(Type::i32().id);
idx1.borrow_mut().subclass = SubclassKind::ConstantInt;
idx1.borrow_mut().subclass_data = Some(field_index as u64);
idx1.borrow_mut().is_constant = true;
let gep =
instruction::getelementptr(ptr, struct_ty.clone(), &[idx0, idx1], struct_ty.clone());
let result = Value::new(Type::pointer(struct_ty.id).id);
result.borrow_mut().subclass = SubclassKind::GEPOperator;
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(gep);
}
Ok(self.codegen_dereference(result))
}
pub fn codegen_post_inc(&mut self, lvalue: ValueRef) -> ValueRef {
let old_val = self.codegen_dereference(lvalue.clone());
let one = self.codegen_integer_literal(
1,
Type::from_id(old_val.borrow().ty)
.size_in_bits()
.unwrap_or(32) as u32,
);
let new_val = self.gen.builder.create_add(old_val.clone(), one);
self.gen.builder.create_store(new_val, lvalue);
old_val
}
pub fn codegen_post_dec(&mut self, lvalue: ValueRef) -> ValueRef {
let old_val = self.codegen_dereference(lvalue.clone());
let one = self.codegen_integer_literal(
1,
Type::from_id(old_val.borrow().ty)
.size_in_bits()
.unwrap_or(32) as u32,
);
let new_val = self.gen.builder.create_sub(old_val.clone(), one);
self.gen.builder.create_store(new_val, lvalue);
old_val
}
pub fn codegen_pre_inc(&mut self, lvalue: ValueRef) -> ValueRef {
let old_val = self.codegen_dereference(lvalue.clone());
let one = self.codegen_integer_literal(
1,
Type::from_id(old_val.borrow().ty)
.size_in_bits()
.unwrap_or(32) as u32,
);
let new_val = self.gen.builder.create_add(old_val, one);
self.gen.builder.create_store(new_val.clone(), lvalue);
new_val
}
pub fn codegen_pre_dec(&mut self, lvalue: ValueRef) -> ValueRef {
let old_val = self.codegen_dereference(lvalue.clone());
let one = self.codegen_integer_literal(
1,
Type::from_id(old_val.borrow().ty)
.size_in_bits()
.unwrap_or(32) as u32,
);
let new_val = self.gen.builder.create_sub(old_val, one);
self.gen.builder.create_store(new_val.clone(), lvalue);
new_val
}
}
pub struct StmtCodeGen<'a> {
pub gen: &'a mut IRGenerator<'a>,
}
impl<'a> StmtCodeGen<'a> {
pub fn new(gen: &'a mut IRGenerator<'a>) -> Self {
StmtCodeGen { gen }
}
pub fn codegen_compound(&mut self, stmts: &[Stmt]) -> Result<(), String> {
for stmt in stmts {
self.codegen_stmt(stmt)?;
}
Ok(())
}
pub fn codegen_stmt(&mut self, stmt: &Stmt) -> Result<(), String> {
match stmt {
Stmt::Compound(cs) => self.codegen_compound(&cs.stmts),
Stmt::Expr(expr) => {
self.gen.compile_expr(expr)?;
Ok(())
}
Stmt::Return(expr) => {
self.codegen_return(expr.as_ref())?;
Ok(())
}
Stmt::If(cond, then, els) => self.codegen_if(cond, then, els.as_ref()),
Stmt::While(cond, body) => self.codegen_while(cond, body),
Stmt::DoWhile(body, cond) => self.codegen_do_while(body, cond),
Stmt::For(init, cond, incr, body) => {
self.codegen_for(init.as_ref(), cond.as_ref(), incr.as_ref(), body)
}
Stmt::Switch(expr, body) => self.codegen_switch(expr, body),
Stmt::Break => self.codegen_break(),
Stmt::Continue => self.codegen_continue(),
Stmt::Goto(label) => self.codegen_goto(label),
Stmt::Label(name, stmt) => self.codegen_label(name, stmt),
Stmt::Case(_, _) | Stmt::Default(_) | Stmt::Decl(_) | Stmt::Null => Ok(()),
}
}
pub fn codegen_return(&mut self, expr: Option<&Expr>) -> Result<(), String> {
if let Some(expr) = expr {
let val = self.gen.compile_expr(expr)?;
self.gen.builder.create_ret(val);
} else {
self.gen.builder.create_ret_void();
}
Ok(())
}
pub fn codegen_if(
&mut self,
cond: &Expr,
then: &Stmt,
els: Option<&Stmt>,
) -> Result<(), String> {
self.gen.compile_if(cond, then, els)
}
pub fn codegen_while(&mut self, cond: &Expr, body: &Stmt) -> Result<(), String> {
self.gen.compile_while(cond, body)
}
pub fn codegen_do_while(&mut self, body: &Stmt, cond: &Expr) -> Result<(), String> {
self.gen.compile_do_while(body, cond)
}
pub fn codegen_for(
&mut self,
init: Option<&Stmt>,
cond: Option<&Expr>,
incr: Option<&Expr>,
body: &Stmt,
) -> Result<(), String> {
self.gen.compile_for(init, cond, incr, body)
}
pub fn codegen_switch(&mut self, expr: &Expr, body: &Stmt) -> Result<(), String> {
self.gen.compile_switch(expr, body)
}
pub fn codegen_break(&mut self) -> Result<(), String> {
self.gen.compile_break()
}
pub fn codegen_continue(&mut self) -> Result<(), String> {
self.gen.compile_continue()
}
pub fn codegen_goto(&mut self, label: &str) -> Result<(), String> {
self.gen.compile_goto(label)
}
pub fn codegen_label(&mut self, name: &str, stmt: &Stmt) -> Result<(), String> {
self.gen.compile_label(name, stmt)
}
pub fn begin_loop(&mut self, break_target: ValueRef, continue_target: ValueRef) {
self.gen.break_targets.push(break_target);
self.gen.continue_targets.push(continue_target);
}
pub fn end_loop(&mut self) {
self.gen.break_targets.pop();
self.gen.continue_targets.pop();
}
pub fn get_break_target(&self) -> Option<ValueRef> {
self.gen.break_targets.last().cloned()
}
pub fn get_continue_target(&self) -> Option<ValueRef> {
self.gen.continue_targets.last().cloned()
}
pub fn begin_switch(&mut self, merge_bb: ValueRef) {
self.gen.switch_merge_block = Some(merge_bb);
self.gen.switch_cases.clear();
self.gen.default_case_block = None;
}
pub fn end_switch(&mut self) {
self.gen.switch_merge_block = None;
self.gen.switch_cases.clear();
self.gen.default_case_block = None;
}
}
pub struct DeclCodeGen<'a> {
pub gen: &'a mut IRGenerator<'a>,
}
impl<'a> DeclCodeGen<'a> {
pub fn new(gen: &'a mut IRGenerator<'a>) -> Self {
DeclCodeGen { gen }
}
pub fn codegen_local_variable(
&mut self,
name: &str,
ty: &Type,
init: Option<&Expr>,
) -> Result<ValueRef, String> {
let alloca = self.gen.builder.create_alloca(ty.clone());
alloca.borrow_mut().name = Some(name.to_string());
self.gen
.named_values
.insert(name.to_string(), alloca.clone());
if let Some(init_expr) = init {
let init_val = self.gen.compile_expr(init_expr)?;
let init_val = self.gen.convert_scalar(
init_val,
Type::from_id(init_val.borrow().ty),
ty.clone(),
)?;
self.gen.builder.create_store(init_val, alloca.clone());
}
Ok(alloca)
}
pub fn codegen_global_variable(
&mut self,
name: &str,
ty: &Type,
init: Option<&Expr>,
is_extern: bool,
) -> Result<ValueRef, String> {
let gv = Value::named(name);
gv.borrow_mut().ty = ty.id;
gv.borrow_mut().subclass = SubclassKind::GlobalVariable;
if !is_extern {
if let Some(init_expr) = init {
let init_val = self.gen.compile_expr(init_expr)?;
gv.borrow_mut().initializer = Some(init_val);
} else {
let zero = Value::new(ty.id);
zero.borrow_mut().subclass = SubclassKind::ConstantInt;
zero.borrow_mut().subclass_data = Some(0);
zero.borrow_mut().is_constant = true;
gv.borrow_mut().initializer = Some(zero);
}
}
self.gen.module.add_global_variable(gv.clone());
self.gen.global_values.insert(name.to_string(), gv.clone());
Ok(gv)
}
pub fn codegen_static_local(
&mut self,
name: &str,
ty: &Type,
init: Option<&Expr>,
) -> Result<ValueRef, String> {
let guard_name = format!("__static_guard_{}", name);
let guard_ty = Type::i8();
let guard_gv = Value::named(&guard_name);
guard_gv.borrow_mut().ty = guard_ty.id;
guard_gv.borrow_mut().subclass = SubclassKind::GlobalVariable;
let guard_zero = Value::new(guard_ty.id);
guard_zero.borrow_mut().subclass = SubclassKind::ConstantInt;
guard_zero.borrow_mut().subclass_data = Some(0);
guard_zero.borrow_mut().is_constant = true;
guard_gv.borrow_mut().initializer = Some(guard_zero);
self.gen.module.add_global_variable(guard_gv.clone());
let var_name = format!("__static_{}", name);
let gv = Value::named(&var_name);
gv.borrow_mut().ty = ty.id;
gv.borrow_mut().subclass = SubclassKind::GlobalVariable;
if let Some(init_expr) = init {
let init_val = self.gen.compile_expr(init_expr)?;
gv.borrow_mut().initializer = Some(init_val);
}
self.gen.module.add_global_variable(gv.clone());
self.gen.global_values.insert(var_name.clone(), gv.clone());
self.gen.named_values.insert(name.to_string(), gv.clone());
Ok(gv)
}
pub fn codegen_extern_variable(&mut self, name: &str, ty: &Type) -> Result<ValueRef, String> {
let gv = Value::named(name);
gv.borrow_mut().ty = ty.id;
gv.borrow_mut().subclass = SubclassKind::GlobalVariable;
self.gen.module.add_global_variable(gv.clone());
self.gen.global_values.insert(name.to_string(), gv.clone());
Ok(gv)
}
pub fn codegen_function_prototype(
&mut self,
name: &str,
return_ty: &Type,
param_types: &[Type],
param_names: &[String],
is_vararg: bool,
) -> Result<ValueRef, String> {
let param_type_ids: Vec<TypeId> = param_types.iter().map(|t| t.id).collect();
let func_ty = Type::function_type_with(return_ty.id, ¶m_type_ids, is_vararg);
let func_val = Value::named(name);
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
func_val.borrow_mut().is_vararg = is_vararg;
self.gen.module.add_function(func_val.clone());
self.gen
.functions
.insert(name.to_string(), func_val.clone());
self.gen
.global_values
.insert(name.to_string(), func_val.clone());
Ok(func_val)
}
pub fn codegen_function_definition(
&mut self,
name: &str,
return_ty: &Type,
param_types: &[Type],
param_names: &[String],
body: &CompoundStmt,
is_vararg: bool,
) -> Result<ValueRef, String> {
let func_val =
self.codegen_function_prototype(name, return_ty, param_types, param_names, is_vararg)?;
self.gen.current_function = Some(func_val.clone());
let entry_bb = self.gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
self.gen.builder.set_insert_point(&entry_bb);
self.gen.named_values.clear();
for (i, (name, ty)) in param_names.iter().zip(param_types.iter()).enumerate() {
let alloca = self.gen.builder.create_alloca(ty.clone());
alloca.borrow_mut().name = Some(name.clone());
self.gen.named_values.insert(name.clone(), alloca);
}
self.gen.compile_compound_stmt(body);
let current_block = self.gen.builder.get_insert_block();
if let Some(bb) = current_block {
let bb_ref = bb.borrow();
let has_terminator = bb_ref
.instructions
.iter()
.any(|inst| instruction::is_terminator(inst.borrow().opcode));
if !has_terminator {
if return_ty.is_void() {
self.gen.builder.create_ret_void();
} else {
let undef_val = Value::new(return_ty.id);
self.gen.builder.create_ret(undef_val);
}
}
}
self.gen.current_function = None;
Ok(func_val)
}
pub fn codegen_struct_declaration(
&mut self,
name: &str,
field_types: &[Type],
field_names: &[String],
is_union: bool,
is_packed: bool,
) -> Type {
let field_type_ids: Vec<TypeId> = field_types.iter().map(|t| t.id).collect();
let struct_ty = Type::struct_named_with(name, &field_type_ids, is_union);
self.gen
.struct_types
.insert(name.to_string(), struct_ty.clone());
struct_ty
}
pub fn codegen_enum_declaration(&mut self, name: &str, variants: &[(String, i64)]) -> Type {
let enum_ty = Type::i32();
self.gen
.enum_types
.insert(name.to_string(), enum_ty.clone());
enum_ty
}
pub fn codegen_typedef(&mut self, name: &str, underlying: &Type) -> Type {
self.gen
.typedef_types
.insert(name.to_string(), underlying.clone());
underlying.clone()
}
pub fn codegen_aggregate_initializer(&mut self, ty: &Type, elements: &[ValueRef]) -> ValueRef {
let agg_val = Value::new(ty.id);
agg_val.borrow_mut().subclass = SubclassKind::ConstantAggregate;
for elem in elements {
agg_val.borrow_mut().operands.push(elem.clone());
}
agg_val.borrow_mut().is_constant = true;
agg_val
}
pub fn codegen_scalar_initializer(&mut self, ty: &Type, value: ValueRef) -> ValueRef {
let converted = self
.gen
.convert_scalar(value, Type::from_id(value.borrow().ty), ty.clone())
.unwrap_or(value);
converted
}
pub fn codegen_zero_initializer(&self, ty: &Type) -> ValueRef {
let zero = Value::new(ty.id);
zero.borrow_mut().subclass = SubclassKind::ConstantInt;
zero.borrow_mut().subclass_data = Some(0);
zero.borrow_mut().is_constant = true;
zero
}
}
pub struct TypeConversion;
impl TypeConversion {
pub fn is_unsigned_type(tn: &TypeNode) -> bool {
matches!(
tn,
TypeNode::UChar
| TypeNode::UShort
| TypeNode::UInt
| TypeNode::ULong
| TypeNode::ULongLong
)
}
pub fn integer_rank(tn: &TypeNode) -> u32 {
match tn {
TypeNode::Bool => 1,
TypeNode::Char | TypeNode::SChar | TypeNode::UChar => 2,
TypeNode::Short | TypeNode::UShort => 3,
TypeNode::Int | TypeNode::UInt => 4,
TypeNode::Long | TypeNode::ULong => 5,
TypeNode::LongLong | TypeNode::ULongLong => 6,
_ => 0,
}
}
pub fn float_rank(tn: &TypeNode) -> u32 {
match tn {
TypeNode::Float => 1,
TypeNode::Double => 2,
TypeNode::LongDouble => 3,
_ => 0,
}
}
pub fn integer_promotion_type(tn: &TypeNode) -> TypeNode {
match tn {
TypeNode::Bool
| TypeNode::Char
| TypeNode::SChar
| TypeNode::UChar
| TypeNode::Short
| TypeNode::UShort => TypeNode::Int,
other => other.clone(),
}
}
pub fn usual_arithmetic_conversion_type(lhs: &TypeNode, rhs: &TypeNode) -> TypeNode {
if matches!(lhs, TypeNode::LongDouble) || matches!(rhs, TypeNode::LongDouble) {
return TypeNode::LongDouble;
}
if matches!(lhs, TypeNode::Double) || matches!(rhs, TypeNode::Double) {
return TypeNode::Double;
}
if matches!(lhs, TypeNode::Float) || matches!(rhs, TypeNode::Float) {
return TypeNode::Float;
}
let lhs_promoted = Self::integer_promotion_type(lhs);
let rhs_promoted = Self::integer_promotion_type(rhs);
if lhs_promoted == rhs_promoted {
return lhs_promoted;
}
let lhs_unsigned = Self::is_unsigned_type(&lhs_promoted);
let rhs_unsigned = Self::is_unsigned_type(&rhs_promoted);
if lhs_unsigned == rhs_unsigned {
let lhs_rank = Self::integer_rank(&lhs_promoted);
let rhs_rank = Self::integer_rank(&rhs_promoted);
return if lhs_rank >= rhs_rank {
lhs_promoted
} else {
rhs_promoted
};
}
let lhs_rank = Self::integer_rank(&lhs_promoted);
let rhs_rank = Self::integer_rank(&rhs_promoted);
if lhs_unsigned && lhs_rank >= rhs_rank {
return lhs_promoted;
}
if rhs_unsigned && rhs_rank >= lhs_rank {
return rhs_promoted;
}
if !lhs_unsigned {
match &lhs_promoted {
TypeNode::Int => return TypeNode::UInt,
TypeNode::Long => return TypeNode::ULong,
TypeNode::LongLong => return TypeNode::ULongLong,
_ => {}
}
}
if !rhs_unsigned {
match &rhs_promoted {
TypeNode::Int => return TypeNode::UInt,
TypeNode::Long => return TypeNode::ULong,
TypeNode::LongLong => return TypeNode::ULongLong,
_ => {}
}
}
lhs_promoted
}
pub fn is_truncation(from_bits: u32, to_bits: u32) -> bool {
to_bits < from_bits
}
pub fn is_extension(from_bits: u32, to_bits: u32) -> bool {
to_bits > from_bits
}
pub fn integer_cast_kind(from_signed: bool, to_bits: u32, from_bits: u32) -> ConversionKind {
if to_bits > from_bits {
if from_signed {
ConversionKind::Sext
} else {
ConversionKind::Zext
}
} else if to_bits < from_bits {
ConversionKind::Trunc
} else {
ConversionKind::None
}
}
pub fn needs_lvalue_to_rvalue(val: &ValueRef) -> bool {
let ty = Type::from_id(val.borrow().ty);
ty.is_pointer() && !val.borrow().is_constant
}
pub fn rvalue_type(lvalue_ty: &Type) -> Option<Type> {
if lvalue_ty.is_pointer() {
lvalue_ty.element_type_id().map(Type::from_id)
} else {
Some(lvalue_ty.clone())
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConversionKind {
None,
Sext,
Zext,
Trunc,
FPTrunc,
FPExt,
SIToFP,
UIToFP,
FPToSI,
FPToUI,
PtrToInt,
IntToPtr,
BitCast,
}
pub struct AggregateCodeGen<'a> {
pub gen: &'a mut IRGenerator<'a>,
}
impl<'a> AggregateCodeGen<'a> {
pub fn new(gen: &'a mut IRGenerator<'a>) -> Self {
AggregateCodeGen { gen }
}
pub fn codegen_struct_gep(
&mut self,
struct_ptr: ValueRef,
struct_ty: &Type,
field_index: u32,
) -> ValueRef {
let idx0 = self.gen.builder.get_int32(0);
let idx1 = Value::new(Type::i32().id);
idx1.borrow_mut().subclass = SubclassKind::ConstantInt;
idx1.borrow_mut().subclass_data = Some(field_index as u64);
idx1.borrow_mut().is_constant = true;
let gep = instruction::getelementptr(
struct_ptr,
struct_ty.clone(),
&[idx0, idx1],
struct_ty.clone(),
);
let ptr_ty = Type::pointer(struct_ty.id);
let result = Value::new(ptr_ty.id);
result.borrow_mut().subclass = SubclassKind::GEPOperator;
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(gep);
}
result
}
pub fn codegen_struct_field_load(
&mut self,
struct_ptr: ValueRef,
struct_ty: &Type,
field_index: u32,
field_ty: &Type,
) -> ValueRef {
let field_ptr = self.codegen_struct_gep(struct_ptr, struct_ty, field_index);
self.gen.builder.create_load(field_ptr, field_ty.clone())
}
pub fn codegen_struct_field_store(
&mut self,
struct_ptr: ValueRef,
struct_ty: &Type,
field_index: u32,
value: ValueRef,
) {
let field_ptr = self.codegen_struct_gep(struct_ptr, struct_ty, field_index);
self.gen.builder.create_store(value, field_ptr);
}
pub fn codegen_struct_init(
&mut self,
struct_ty: &Type,
field_values: &[(u32, ValueRef)],
) -> ValueRef {
let alloca = self.gen.builder.create_alloca(struct_ty.clone());
for (field_index, value) in field_values {
self.codegen_struct_field_store(alloca.clone(), struct_ty, *field_index, value.clone());
}
alloca
}
pub fn codegen_struct_return(&mut self, struct_val: ValueRef, struct_ty: &Type) -> ValueRef {
let size_bits = struct_ty.size_in_bits().unwrap_or(64);
if size_bits <= 64 {
let int_ty = Type::int(size_bits as u32);
self.gen.builder.create_bitcast(struct_val, int_ty)
} else {
struct_val
}
}
pub fn codegen_array_gep(
&mut self,
array_ptr: ValueRef,
elem_ty: &Type,
index: ValueRef,
) -> ValueRef {
let gep = instruction::getelementptr(array_ptr, elem_ty.clone(), &[index], elem_ty.clone());
let ptr_ty = Type::pointer(elem_ty.id);
let result = Value::new(ptr_ty.id);
result.borrow_mut().subclass = SubclassKind::GEPOperator;
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(gep);
}
result
}
pub fn codegen_array_load(
&mut self,
array_ptr: ValueRef,
elem_ty: &Type,
index: ValueRef,
) -> ValueRef {
let elem_ptr = self.codegen_array_gep(array_ptr, elem_ty, index);
self.gen.builder.create_load(elem_ptr, elem_ty.clone())
}
pub fn codegen_array_store(
&mut self,
array_ptr: ValueRef,
elem_ty: &Type,
index: ValueRef,
value: ValueRef,
) {
let elem_ptr = self.codegen_array_gep(array_ptr, elem_ty, index);
self.gen.builder.create_store(value, elem_ptr);
}
pub fn codegen_array_init(
&mut self,
array_ty: &Type,
elem_ty: &Type,
element_values: &[ValueRef],
) -> ValueRef {
let alloca = self.gen.builder.create_alloca(array_ty.clone());
for (i, value) in element_values.iter().enumerate() {
let index_val = Value::new(Type::i32().id);
index_val.borrow_mut().subclass = SubclassKind::ConstantInt;
index_val.borrow_mut().subclass_data = Some(i as u64);
index_val.borrow_mut().is_constant = true;
self.codegen_array_store(alloca.clone(), elem_ty, index_val, value.clone());
}
alloca
}
pub fn codegen_array_decay(&mut self, array_val: ValueRef, array_ty: &Type) -> ValueRef {
let elem_ty_id = array_ty.element_type_id().unwrap_or(Type::i32().id);
let elem_ty = Type::from_id(elem_ty_id);
let index_zero = self.gen.builder.get_int32(0);
let gep = instruction::getelementptr(array_val, elem_ty.clone(), &[index_zero], elem_ty);
let ptr_ty = Type::pointer(elem_ty_id);
let result = Value::new(ptr_ty.id);
result.borrow_mut().subclass = SubclassKind::GEPOperator;
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(gep);
}
result
}
pub fn codegen_union_access(&mut self, union_ptr: ValueRef, field_ty: &Type) -> ValueRef {
self.gen
.builder
.create_bitcast(union_ptr, Type::pointer(field_ty.id))
}
pub fn codegen_union_init(&mut self, union_ty: &Type, value: ValueRef) -> ValueRef {
let alloca = self.gen.builder.create_alloca(union_ty.clone());
let cast_ptr = self
.gen
.builder
.create_bitcast(alloca.clone(), Type::pointer(value.borrow().ty));
self.gen.builder.create_store(value, cast_ptr);
alloca
}
pub fn codegen_compound_literal(&mut self, ty: &Type, init_val: ValueRef) -> ValueRef {
let alloca = self.gen.builder.create_alloca(ty.clone());
self.gen.builder.create_store(init_val, alloca.clone());
alloca
}
pub fn codegen_extract_value(&mut self, agg_val: ValueRef, indices: &[u32]) -> ValueRef {
let extract_inst = instruction::create_extractvalue(agg_val, indices);
let result = Value::new(Type::i32().id); if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(extract_inst);
}
result
}
pub fn codegen_insert_value(
&mut self,
agg_val: ValueRef,
field_val: ValueRef,
indices: &[u32],
) -> ValueRef {
let insert_inst = instruction::create_insertvalue(agg_val, field_val, indices);
let result = Value::new(Type::i32().id); if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(insert_inst);
}
result
}
}
pub struct BuiltinCodeGen<'a> {
pub gen: &'a mut IRGenerator<'a>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BuiltinKind {
Alloca,
Expect,
Prefetch,
Assume,
Unreachable,
Trap,
Debugtrap,
Memcpy,
Memmove,
Memset,
Bswap16,
Bswap32,
Bswap64,
Clz,
Clzll,
Ctz,
Ctzll,
Clrsb,
Clrsbll,
Popcount,
Popcountll,
Parity,
Parityll,
Ffs,
Ffsll,
Sqrt,
Sqrtf,
Sqrtl,
Fma,
Fmaf,
Fmal,
ExpectWithProbability,
ConstantP,
FrameAddress,
ReturnAddress,
ObjectSize,
Unknown,
}
impl<'a> BuiltinCodeGen<'a> {
pub fn new(gen: &'a mut IRGenerator<'a>) -> Self {
BuiltinCodeGen { gen }
}
pub fn recognize(name: &str) -> BuiltinKind {
IRGenerator::<'_>::get_builtin_kind(name)
}
pub fn llvm_intrinsic_name(kind: &BuiltinKind) -> Option<&'static str> {
match kind {
BuiltinKind::Memcpy => Some("llvm.memcpy.p0i8.p0i8.i64"),
BuiltinKind::Memmove => Some("llvm.memmove.p0i8.p0i8.i64"),
BuiltinKind::Memset => Some("llvm.memset.p0i8.i64"),
BuiltinKind::Sqrt | BuiltinKind::Sqrtf | BuiltinKind::Sqrtl => Some("llvm.sqrt.f64"),
BuiltinKind::Fma | BuiltinKind::Fmaf | BuiltinKind::Fmal => Some("llvm.fma.f64"),
BuiltinKind::Trap => Some("llvm.trap"),
BuiltinKind::Debugtrap => Some("llvm.debugtrap"),
BuiltinKind::Expect => Some("llvm.expect.i64"),
BuiltinKind::Assume => Some("llvm.assume"),
BuiltinKind::Clz | BuiltinKind::Clzll => Some("llvm.ctlz.i64"),
BuiltinKind::Ctz | BuiltinKind::Ctzll => Some("llvm.cttz.i64"),
BuiltinKind::Popcount | BuiltinKind::Popcountll => Some("llvm.ctpop.i64"),
BuiltinKind::Bswap16 | BuiltinKind::Bswap32 | BuiltinKind::Bswap64 => {
Some("llvm.bswap.i64")
}
_ => None,
}
}
pub fn compile(&mut self, name: &str, args: &[ValueRef]) -> Result<ValueRef, String> {
let kind = Self::recognize(name);
match kind {
BuiltinKind::Alloca => self.emit_alloca(args),
BuiltinKind::Expect => self.emit_expect(args),
BuiltinKind::Prefetch => self.emit_prefetch(args),
BuiltinKind::Assume => self.emit_assume(args),
BuiltinKind::Unreachable => self.emit_unreachable(),
BuiltinKind::Trap => self.emit_trap(),
BuiltinKind::Debugtrap => self.emit_debugtrap(),
BuiltinKind::Memcpy => self.emit_memcpy(args),
BuiltinKind::Memmove => self.emit_memmove(args),
BuiltinKind::Memset => self.emit_memset(args),
BuiltinKind::Bswap16 | BuiltinKind::Bswap32 | BuiltinKind::Bswap64 => {
self.emit_bswap(args, kind)
}
BuiltinKind::Clz | BuiltinKind::Clzll => self.emit_ctlz(args),
BuiltinKind::Ctz | BuiltinKind::Ctzll => self.emit_cttz(args),
BuiltinKind::Clrsb | BuiltinKind::Clrsbll => self.emit_clrsb(args),
BuiltinKind::Popcount | BuiltinKind::Popcountll => self.emit_popcount(args),
BuiltinKind::Parity | BuiltinKind::Parityll => self.emit_parity(args),
BuiltinKind::Ffs | BuiltinKind::Ffsll => self.emit_ffs(args),
BuiltinKind::Sqrt | BuiltinKind::Sqrtf | BuiltinKind::Sqrtl => self.emit_sqrt(args),
BuiltinKind::Fma | BuiltinKind::Fmaf | BuiltinKind::Fmal => self.emit_fma(args),
BuiltinKind::ExpectWithProbability => self.emit_expect_with_probability(args),
BuiltinKind::ConstantP => self.emit_constant_p(args),
BuiltinKind::FrameAddress => self.emit_frame_address(args),
BuiltinKind::ReturnAddress => self.emit_return_address(args),
BuiltinKind::ObjectSize => self.emit_object_size(args),
BuiltinKind::Unknown => Err(format!("unknown builtin: {}", name)),
}
}
fn emit_alloca(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.is_empty() {
return Err("__builtin_alloca requires size argument".to_string());
}
let alloca_ty = Type::i8();
Ok(self.gen.builder.create_alloca(alloca_ty))
}
fn emit_expect(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.len() < 2 {
return Err("__builtin_expect requires two arguments".to_string());
}
Ok(args[0].clone())
}
fn emit_prefetch(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.is_empty() {
return Err("__builtin_prefetch requires an address".to_string());
}
Ok(args[0].clone())
}
fn emit_assume(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.is_empty() {
return Err("__builtin_assume requires a condition".to_string());
}
Ok(args[0].clone())
}
fn emit_unreachable(&mut self) -> Result<ValueRef, String> {
self.gen.builder.create_unreachable();
Ok(Value::new(Type::void().id))
}
fn emit_trap(&mut self) -> Result<ValueRef, String> {
self.gen.builder.create_unreachable();
Ok(Value::new(Type::void().id))
}
fn emit_debugtrap(&mut self) -> Result<ValueRef, String> {
self.gen.builder.create_unreachable();
Ok(Value::new(Type::void().id))
}
fn emit_memcpy(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.len() < 3 {
return Err("__builtin_memcpy requires dest, src, size".to_string());
}
let dest = args[0].clone();
let src = args[1].clone();
let size = args[2].clone();
let memcpy_inst = instruction::call(
dest.clone(), &[dest, src, size],
Type::void(),
);
let result = Value::new(Type::void().id);
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(memcpy_inst);
}
Ok(result)
}
fn emit_memmove(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.len() < 3 {
return Err("__builtin_memmove requires dest, src, size".to_string());
}
let result = Value::new(Type::void().id);
Ok(result)
}
fn emit_memset(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.len() < 3 {
return Err("__builtin_memset requires dest, value, size".to_string());
}
let result = Value::new(Type::void().id);
Ok(result)
}
fn emit_bswap(&mut self, args: &[ValueRef], _kind: BuiltinKind) -> Result<ValueRef, String> {
if args.is_empty() {
return Err("__builtin_bswap requires an argument".to_string());
}
Ok(args[0].clone())
}
fn emit_ctlz(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.is_empty() {
return Err("__builtin_clz requires an argument".to_string());
}
Ok(args[0].clone())
}
fn emit_cttz(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.is_empty() {
return Err("__builtin_ctz requires an argument".to_string());
}
Ok(args[0].clone())
}
fn emit_clrsb(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.is_empty() {
return Err("__builtin_clrsb requires an argument".to_string());
}
Ok(args[0].clone())
}
fn emit_popcount(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.is_empty() {
return Err("__builtin_popcount requires an argument".to_string());
}
Ok(args[0].clone())
}
fn emit_parity(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.is_empty() {
return Err("__builtin_parity requires an argument".to_string());
}
Ok(args[0].clone())
}
fn emit_ffs(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.is_empty() {
return Err("__builtin_ffs requires an argument".to_string());
}
Ok(args[0].clone())
}
fn emit_sqrt(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.is_empty() {
return Err("__builtin_sqrt requires an argument".to_string());
}
Ok(args[0].clone())
}
fn emit_fma(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.len() < 3 {
return Err("__builtin_fma requires three arguments".to_string());
}
Ok(args[0].clone())
}
fn emit_expect_with_probability(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
if args.len() < 2 {
return Err(
"__builtin_expect_with_probability requires at least two arguments".to_string(),
);
}
Ok(args[0].clone())
}
fn emit_constant_p(&mut self, args: &[ValueRef]) -> Result<ValueRef, String> {
let result = if args.is_empty() {
self.gen.compile_int_literal(0)?
} else {
let is_const = args[0].borrow().is_constant;
self.gen.compile_int_literal(if is_const { 1 } else { 0 })?
};
Ok(result)
}
fn emit_frame_address(&mut self, _args: &[ValueRef]) -> Result<ValueRef, String> {
let ptr_ty = Type::pointer(Type::i8().id);
let null_val = Value::new(ptr_ty.id);
null_val.borrow_mut().subclass = SubclassKind::ConstantInt;
null_val.borrow_mut().subclass_data = Some(0);
null_val.borrow_mut().is_constant = true;
Ok(null_val)
}
fn emit_return_address(&mut self, _args: &[ValueRef]) -> Result<ValueRef, String> {
let ptr_ty = Type::pointer(Type::i8().id);
let null_val = Value::new(ptr_ty.id);
null_val.borrow_mut().subclass = SubclassKind::ConstantInt;
null_val.borrow_mut().subclass_data = Some(0);
null_val.borrow_mut().is_constant = true;
Ok(null_val)
}
fn emit_object_size(&mut self, _args: &[ValueRef]) -> Result<ValueRef, String> {
self.gen.compile_int_literal(-1)
}
pub fn is_builtin(name: &str) -> bool {
name.starts_with("__builtin_")
}
pub fn builtin_return_type(kind: &BuiltinKind) -> Type {
match kind {
BuiltinKind::Alloca => Type::pointer(Type::i8().id),
BuiltinKind::Expect | BuiltinKind::ExpectWithProbability => Type::i64(),
BuiltinKind::ConstantP => Type::i32(),
BuiltinKind::ObjectSize => Type::i64(),
BuiltinKind::FrameAddress | BuiltinKind::ReturnAddress => Type::pointer(Type::i8().id),
BuiltinKind::Unreachable | BuiltinKind::Trap | BuiltinKind::Debugtrap => Type::void(),
BuiltinKind::Memcpy | BuiltinKind::Memmove | BuiltinKind::Memset => Type::void(),
BuiltinKind::Sqrt | BuiltinKind::Sqrtf | BuiltinKind::Sqrtl => Type::double(),
BuiltinKind::Fma | BuiltinKind::Fmaf | BuiltinKind::Fmal => Type::double(),
_ => Type::i32(),
}
}
}
pub struct DebugInfo;
impl DebugInfo {
pub fn create_compile_unit(
language: u32, file_name: &str,
directory: &str,
producer: &str,
is_optimized: bool,
flags: &str,
runtime_version: u32,
) -> ValueRef {
let md_val = Value::new(Type::metadata().id);
md_val.borrow_mut().name = Some(format!("llvm.dbg.cu"));
md_val.borrow_mut().subclass = SubclassKind::MetadataAsValue;
let desc = format!(
"!DICompileUnit(language: {}, file: !DIFile({}, {}), producer: {}, optimized: {}, flags: {}, runtimeVersion: {})",
language, file_name, directory, producer, is_optimized, flags, runtime_version
);
md_val.borrow_mut().subclass_data = Some(desc.len() as u64);
md_val
}
pub fn create_file(filename: &str, directory: &str) -> ValueRef {
let md_val = Value::new(Type::metadata().id);
md_val.borrow_mut().name = Some(format!("!DIFile({}, {})", filename, directory));
md_val.borrow_mut().subclass = SubclassKind::MetadataAsValue;
md_val
}
pub fn create_subprogram(
name: &str,
linkage_name: &str,
file: &ValueRef,
line: u32,
ty: &ValueRef,
is_local: bool,
is_definition: bool,
scope_line: u32,
flags: u32,
is_optimized: bool,
) -> ValueRef {
let md_val = Value::new(Type::metadata().id);
md_val.borrow_mut().name = Some(format!("!DISubprogram(name: {}, line: {})", name, line));
md_val.borrow_mut().subclass = SubclassKind::MetadataAsValue;
md_val
}
pub fn create_lexical_block(
scope: &ValueRef,
file: &ValueRef,
line: u32,
column: u32,
) -> ValueRef {
let md_val = Value::new(Type::metadata().id);
md_val.borrow_mut().name = Some(format!("!DILexicalBlock(line: {})", line));
md_val.borrow_mut().subclass = SubclassKind::MetadataAsValue;
md_val
}
pub fn create_local_variable(
name: &str,
file: &ValueRef,
line: u32,
ty: &ValueRef,
arg: u32,
flags: u32,
) -> ValueRef {
let md_val = Value::new(Type::metadata().id);
md_val.borrow_mut().name =
Some(format!("!DILocalVariable(name: {}, line: {})", name, line));
md_val.borrow_mut().subclass = SubclassKind::MetadataAsValue;
md_val
}
pub fn create_location(
line: u32,
column: u32,
scope: &ValueRef,
inlined_at: Option<&ValueRef>,
) -> ValueRef {
let md_val = Value::new(Type::metadata().id);
md_val.borrow_mut().name = Some(format!("!DILocation(line: {}, column: {})", line, column));
md_val.borrow_mut().subclass = SubclassKind::MetadataAsValue;
md_val
}
pub fn create_basic_type(
name: &str,
size_in_bits: u64,
encoding: u32, ) -> ValueRef {
let md_val = Value::new(Type::metadata().id);
md_val.borrow_mut().name = Some(format!(
"!DIBasicType(name: {}, size: {})",
name, size_in_bits
));
md_val.borrow_mut().subclass = SubclassKind::MetadataAsValue;
md_val
}
pub fn create_derived_type(
tag: u32, name: &str,
base_type: &ValueRef,
size_in_bits: u64,
align_in_bits: u64,
offset_in_bits: u64,
) -> ValueRef {
let md_val = Value::new(Type::metadata().id);
md_val.borrow_mut().name = Some(format!("!DIDerivedType(tag: {}, name: {})", tag, name));
md_val.borrow_mut().subclass = SubclassKind::MetadataAsValue;
md_val
}
pub fn create_composite_type(
tag: u32, name: &str,
file: &ValueRef,
line: u32,
size_in_bits: u64,
align_in_bits: u64,
elements: &[ValueRef],
) -> ValueRef {
let md_val = Value::new(Type::metadata().id);
md_val.borrow_mut().name = Some(format!("!DICompositeType(tag: {}, name: {})", tag, name));
md_val.borrow_mut().subclass = SubclassKind::MetadataAsValue;
md_val
}
pub fn create_subrange(lower_bound: i64, count: i64) -> ValueRef {
let md_val = Value::new(Type::metadata().id);
md_val.borrow_mut().name = Some(format!(
"!DISubrange(lower: {}, count: {})",
lower_bound, count
));
md_val.borrow_mut().subclass = SubclassKind::MetadataAsValue;
md_val
}
pub fn create_enumerator(name: &str, value: i64) -> ValueRef {
let md_val = Value::new(Type::metadata().id);
md_val.borrow_mut().name = Some(format!("!DIEnumerator(name: {}, value: {})", name, value));
md_val.borrow_mut().subclass = SubclassKind::MetadataAsValue;
md_val
}
pub fn create_global_variable(
name: &str,
linkage_name: &str,
file: &ValueRef,
line: u32,
ty: &ValueRef,
is_local: bool,
is_definition: bool,
) -> ValueRef {
let md_val = Value::new(Type::metadata().id);
md_val.borrow_mut().name = Some(format!("!DIGlobalVariable(name: {})", name));
md_val.borrow_mut().subclass = SubclassKind::MetadataAsValue;
md_val
}
pub fn create_module(name: &str, file: &ValueRef, line: u32) -> ValueRef {
let md_val = Value::new(Type::metadata().id);
md_val.borrow_mut().name = Some(format!("!DIModule(name: {})", name));
md_val.borrow_mut().subclass = SubclassKind::MetadataAsValue;
md_val
}
pub const DW_LANG_C89: u32 = 1;
pub const DW_LANG_C: u32 = 2;
pub const DW_LANG_C99: u32 = 3;
pub const DW_LANG_C11: u32 = 4;
pub const DW_LANG_C17: u32 = 5;
pub const DW_LANG_C23: u32 = 6;
pub const DW_LANG_CPlusPlus: u32 = 9;
pub const DW_LANG_CPlusPlus11: u32 = 13;
pub const DW_LANG_CPlusPlus14: u32 = 14;
pub const DW_LANG_CPlusPlus17: u32 = 15;
pub const DW_LANG_CPlusPlus20: u32 = 16;
pub const DW_TAG_array_type: u32 = 1;
pub const DW_TAG_pointer_type: u32 = 15;
pub const DW_TAG_structure_type: u32 = 19;
pub const DW_TAG_union_type: u32 = 23;
pub const DW_TAG_enumeration_type: u32 = 4;
pub const DW_TAG_typedef: u32 = 22;
pub const DW_TAG_subprogram: u32 = 46;
pub const DW_TAG_variable: u32 = 52;
pub const DW_ATE_address: u32 = 1;
pub const DW_ATE_boolean: u32 = 2;
pub const DW_ATE_float: u32 = 4;
pub const DW_ATE_signed: u32 = 5;
pub const DW_ATE_signed_char: u32 = 6;
pub const DW_ATE_unsigned: u32 = 7;
pub const DW_ATE_unsigned_char: u32 = 8;
pub fn attach_debug_location(inst: &ValueRef, line: u32, column: u32, scope: &ValueRef) {
let loc = Self::create_location(line, column, scope, None);
inst.borrow_mut().add_metadata(loc);
}
pub fn set_debug_location(gen: &mut IRGenerator, line: u32, column: u32, scope: &ValueRef) {
gen.current_debug_loc = Some((line, column, scope.clone()));
let loc = Self::create_location(line, column, scope, None);
gen.builder.set_current_debug_location(Some(loc));
}
pub fn finalize(
gen: &mut IRGenerator,
cu: &ValueRef,
subprograms: &[ValueRef],
globals: &[ValueRef],
) {
gen.debug_compile_unit = Some(cu.clone());
let named_md_name = "llvm.dbg.cu";
gen.module.add_named_metadata(named_md_name, cu.clone());
}
}
fn type_is_unsigned(ty: &Type) -> bool {
false
}
fn llvm_ret_ty_for_func(tn: &TypeNode) -> Type {
match tn {
TypeNode::Void => Type::void(),
TypeNode::Int | TypeNode::UInt => Type::i32(),
TypeNode::Long | TypeNode::ULong => Type::i64(),
TypeNode::Float => Type::float(),
TypeNode::Double => Type::double(),
TypeNode::Char | TypeNode::SChar | TypeNode::UChar => Type::i8(),
TypeNode::Short | TypeNode::UShort => Type::i16(),
TypeNode::LongLong | TypeNode::ULongLong => Type::i64(),
TypeNode::Bool => Type::i1(),
TypeNode::LongDouble => Type::x86_fp80(),
_ => Type::i32(),
}
}
pub struct VectorCodeGen<'a> {
pub gen: &'a mut IRGenerator<'a>,
}
impl<'a> VectorCodeGen<'a> {
pub fn new(gen: &'a mut IRGenerator<'a>) -> Self {
VectorCodeGen { gen }
}
pub fn codegen_vector_splat(&mut self, scalar: ValueRef, num_elements: u32) -> ValueRef {
let elem_ty = Type::from_id(scalar.borrow().ty);
let vec_ty = Type::fixed_vector_with(elem_ty.id, num_elements as u64);
let undef_vec = Value::new(vec_ty.id);
undef_vec.borrow_mut().subclass = SubclassKind::Constant;
let zero_idx = Value::new(Type::i32().id);
zero_idx.borrow_mut().subclass = SubclassKind::ConstantInt;
zero_idx.borrow_mut().subclass_data = Some(0);
zero_idx.borrow_mut().is_constant = true;
let insert0 = instruction::create_insertelement(undef_vec, scalar, zero_idx);
let mut result = Value::new(vec_ty.id);
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(insert0);
}
for i in 1..num_elements {
let idx = Value::new(Type::i32().id);
idx.borrow_mut().subclass = SubclassKind::ConstantInt;
idx.borrow_mut().subclass_data = Some(i as u64);
idx.borrow_mut().is_constant = true;
let shuffle_mask: Vec<u32> = (0..num_elements).map(|_| 0u32).collect();
let shuffle =
instruction::create_shufflevector(result.clone(), result.clone(), &shuffle_mask);
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(shuffle);
}
}
result
}
pub fn codegen_extract_element(&mut self, vector: ValueRef, index: u32) -> ValueRef {
let idx = Value::new(Type::i32().id);
idx.borrow_mut().subclass = SubclassKind::ConstantInt;
idx.borrow_mut().subclass_data = Some(index as u64);
idx.borrow_mut().is_constant = true;
let vec_ty = Type::from_id(vector.borrow().ty);
let elem_ty_id = vec_ty.element_type_id().unwrap_or(Type::i32().id);
let extract = instruction::create_extractelement(vector, idx);
let result = Value::new(elem_ty_id);
result.borrow_mut().subclass = SubclassKind::Instruction;
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(extract);
}
result
}
pub fn codegen_insert_element(
&mut self,
vector: ValueRef,
element: ValueRef,
index: u32,
) -> ValueRef {
let idx = Value::new(Type::i32().id);
idx.borrow_mut().subclass = SubclassKind::ConstantInt;
idx.borrow_mut().subclass_data = Some(index as u64);
idx.borrow_mut().is_constant = true;
let insert = instruction::create_insertelement(vector, element, idx);
let vec_ty = Type::from_id(vector.borrow().ty);
let result = Value::new(vec_ty.id);
result.borrow_mut().subclass = SubclassKind::Instruction;
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(insert);
}
result
}
pub fn codegen_shuffle_vector(&mut self, v1: ValueRef, v2: ValueRef, mask: &[u32]) -> ValueRef {
let v1_ty = Type::from_id(v1.borrow().ty);
let num_elements = v1_ty.vector_num_elements().unwrap_or(4) as u32;
let result_elem_ty_id = v1_ty.element_type_id().unwrap_or(Type::i32().id);
let result_ty = Type::fixed_vector_with(result_elem_ty_id, mask.len() as u64);
let shuffle = instruction::create_shufflevector(v1, v2, mask);
let result = Value::new(result_ty.id);
result.borrow_mut().subclass = SubclassKind::Instruction;
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(shuffle);
}
result
}
pub fn codegen_vector_binary(&mut self, op: BinaryOp, v1: ValueRef, v2: ValueRef) -> ValueRef {
match op {
BinaryOp::Add => self.gen.builder.create_add(v1, v2),
BinaryOp::Sub => self.gen.builder.create_sub(v1, v2),
BinaryOp::Mul => self.gen.builder.create_mul(v1, v2),
BinaryOp::Div => self.gen.builder.create_sdiv(v1, v2),
BinaryOp::And => self.gen.builder.create_and(v1, v2),
BinaryOp::Or => self.gen.builder.create_or(v1, v2),
BinaryOp::Xor => self.gen.builder.create_xor(v1, v2),
BinaryOp::Shl => self.gen.builder.create_shl(v1, v2),
BinaryOp::Shr => self.gen.builder.create_lshr(v1, v2),
_ => v1,
}
}
pub fn codegen_vector_compare(&mut self, op: BinaryOp, v1: ValueRef, v2: ValueRef) -> ValueRef {
let pred = match op {
BinaryOp::Eq => ICmpPred::EQ,
BinaryOp::Ne => ICmpPred::NE,
BinaryOp::Lt => ICmpPred::SLT,
BinaryOp::Gt => ICmpPred::SGT,
BinaryOp::Le => ICmpPred::SLE,
BinaryOp::Ge => ICmpPred::SGE,
_ => ICmpPred::EQ,
};
self.gen.builder.create_icmp(pred, v1, v2)
}
pub fn codegen_vector_reduce_add(&mut self, vector: ValueRef) -> ValueRef {
let vec_ty = Type::from_id(vector.borrow().ty);
let num_elements = vec_ty.vector_num_elements().unwrap_or(4) as u32;
if num_elements == 0 {
return vector;
}
let mut accum = self.codegen_extract_element(vector.clone(), 0);
for i in 1..num_elements {
let elem = self.codegen_extract_element(vector.clone(), i);
accum = self.gen.builder.create_add(accum, elem);
}
accum
}
pub fn codegen_vector_cast(&mut self, vector: ValueRef, to_elem_ty: &Type) -> ValueRef {
let vec_ty = Type::from_id(vector.borrow().ty);
let num_elements = vec_ty.vector_num_elements().unwrap_or(4);
let to_vec_ty = Type::fixed_vector_with(to_elem_ty.id, num_elements);
self.gen.builder.create_bitcast(vector, to_vec_ty)
}
pub fn codegen_vector_reverse(&mut self, vector: ValueRef) -> ValueRef {
let vec_ty = Type::from_id(vector.borrow().ty);
let n = vec_ty.vector_num_elements().unwrap_or(4) as u32;
let mut mask: Vec<u32> = (0..n).rev().collect();
self.codegen_shuffle_vector(vector.clone(), vector, &mask)
}
}
pub struct AtomicCodeGen<'a> {
pub gen: &'a mut IRGenerator<'a>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemOrdering {
NotAtomic,
Unordered,
Monotonic,
Acquire,
Release,
AcquireRelease,
SequentiallyConsistent,
}
impl MemOrdering {
pub fn as_str(&self) -> &'static str {
match self {
MemOrdering::NotAtomic => "not_atomic",
MemOrdering::Unordered => "unordered",
MemOrdering::Monotonic => "monotonic",
MemOrdering::Acquire => "acquire",
MemOrdering::Release => "release",
MemOrdering::AcquireRelease => "acq_rel",
MemOrdering::SequentiallyConsistent => "seq_cst",
}
}
pub fn from_int(v: u32) -> MemOrdering {
match v {
0 => MemOrdering::NotAtomic,
1 => MemOrdering::Unordered,
2 => MemOrdering::Monotonic,
3 => MemOrdering::Acquire,
4 => MemOrdering::Release,
5 => MemOrdering::AcquireRelease,
6 => MemOrdering::SequentiallyConsistent,
_ => MemOrdering::SequentiallyConsistent,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AtomicRMWOp {
Xchg,
Add,
Sub,
And,
Nand,
Or,
Xor,
Max,
Min,
UMax,
UMin,
FAdd,
FSub,
FMax,
FMin,
}
impl AtomicRMWOp {
pub fn as_str(&self) -> &'static str {
match self {
AtomicRMWOp::Xchg => "xchg",
AtomicRMWOp::Add => "add",
AtomicRMWOp::Sub => "sub",
AtomicRMWOp::And => "and",
AtomicRMWOp::Nand => "nand",
AtomicRMWOp::Or => "or",
AtomicRMWOp::Xor => "xor",
AtomicRMWOp::Max => "max",
AtomicRMWOp::Min => "min",
AtomicRMWOp::UMax => "umax",
AtomicRMWOp::UMin => "umin",
AtomicRMWOp::FAdd => "fadd",
AtomicRMWOp::FSub => "fsub",
AtomicRMWOp::FMax => "fmax",
AtomicRMWOp::FMin => "fmin",
}
}
}
impl<'a> AtomicCodeGen<'a> {
pub fn new(gen: &'a mut IRGenerator<'a>) -> Self {
AtomicCodeGen { gen }
}
pub fn codegen_atomic_load(&mut self, ptr: ValueRef, ordering: MemOrdering) -> ValueRef {
let ptr_ty = Type::from_id(ptr.borrow().ty);
let elem_ty_id = ptr_ty.element_type_id().unwrap_or(Type::i32().id);
let elem_ty = Type::from_id(elem_ty_id);
let load_val = self.gen.builder.create_load(ptr, elem_ty);
load_val.borrow_mut().subclass_data = Some(ordering as u64);
load_val
}
pub fn codegen_atomic_store(&mut self, ptr: ValueRef, value: ValueRef, ordering: MemOrdering) {
self.gen.builder.create_store(value, ptr);
}
pub fn codegen_atomic_exchange(
&mut self,
ptr: ValueRef,
value: ValueRef,
ordering: MemOrdering,
) -> ValueRef {
let ptr_ty = Type::from_id(ptr.borrow().ty);
let elem_ty_id = ptr_ty.element_type_id().unwrap_or(Type::i32().id);
let elem_ty = Type::from_id(elem_ty_id);
let atomicrmw =
instruction::create_atomicrmw(AtomicRMWOp::Xchg as u32, ptr, value, ordering as u32);
let result = Value::new(elem_ty_id);
result.borrow_mut().subclass = SubclassKind::Instruction;
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(atomicrmw);
}
result
}
pub fn codegen_atomic_cmp_xchg(
&mut self,
ptr: ValueRef,
cmp: ValueRef,
new: ValueRef,
success_ordering: MemOrdering,
failure_ordering: MemOrdering,
) -> ValueRef {
let ptr_ty = Type::from_id(ptr.borrow().ty);
let elem_ty_id = ptr_ty.element_type_id().unwrap_or(Type::i32().id);
let cmpxchg = instruction::create_cmpxchg(
ptr,
cmp,
new,
Type::from_id(elem_ty_id),
success_ordering as u32,
failure_ordering as u32,
);
let struct_ty = Type::struct_literal_with(&[elem_ty_id, Type::i1().id], false);
let result = Value::new(struct_ty.id);
result.borrow_mut().subclass = SubclassKind::Instruction;
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(cmpxchg);
}
result
}
pub fn codegen_atomic_rmw(
&mut self,
op: AtomicRMWOp,
ptr: ValueRef,
value: ValueRef,
ordering: MemOrdering,
) -> ValueRef {
let ptr_ty = Type::from_id(ptr.borrow().ty);
let elem_ty_id = ptr_ty.element_type_id().unwrap_or(Type::i32().id);
let atomicrmw = instruction::create_atomicrmw(op as u32, ptr, value, ordering as u32);
let result = Value::new(elem_ty_id);
result.borrow_mut().subclass = SubclassKind::Instruction;
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(atomicrmw);
}
result
}
pub fn codegen_fence(&mut self, ordering: MemOrdering) -> ValueRef {
let fence = instruction::create_fence(ordering as u32);
let result = Value::new(Type::void().id);
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(fence);
}
result
}
pub fn get_cmpxchg_failure_ordering(success: MemOrdering) -> MemOrdering {
match success {
MemOrdering::Release | MemOrdering::Monotonic => MemOrdering::Monotonic,
MemOrdering::Acquire | MemOrdering::AcquireRelease => MemOrdering::Acquire,
MemOrdering::SequentiallyConsistent => MemOrdering::SequentiallyConsistent,
_ => MemOrdering::Monotonic,
}
}
pub fn is_valid_load_ordering(ordering: MemOrdering) -> bool {
matches!(
ordering,
MemOrdering::NotAtomic
| MemOrdering::Unordered
| MemOrdering::Monotonic
| MemOrdering::Acquire
| MemOrdering::SequentiallyConsistent
)
}
pub fn is_valid_store_ordering(ordering: MemOrdering) -> bool {
matches!(
ordering,
MemOrdering::NotAtomic
| MemOrdering::Unordered
| MemOrdering::Monotonic
| MemOrdering::Release
| MemOrdering::SequentiallyConsistent
)
}
}
pub struct ABIInfo;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86_64Class {
NoClass,
Integer,
SSE,
SSEUp,
X87,
X87Up,
ComplexX87,
Memory,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ARMClass {
Core,
VFP,
Memory,
}
impl ABIInfo {
pub fn classify_x86_64(ty: &Type) -> [X86_64Class; 2] {
let size_bytes = ty.size_in_bytes().unwrap_or(8);
if size_bytes > 16 {
return [X86_64Class::Memory, X86_64Class::Memory];
}
if ty.is_void() {
return [X86_64Class::NoClass, X86_64Class::NoClass];
}
if ty.is_integer() {
let bits = ty.size_in_bits().unwrap_or(32);
return if bits <= 64 {
[X86_64Class::Integer, X86_64Class::NoClass]
} else {
[X86_64Class::Integer, X86_64Class::Integer]
};
}
if ty.is_floating_point() {
return match ty.size_in_bits().unwrap_or(64) {
32 => [X86_64Class::SSE, X86_64Class::NoClass],
64 => [X86_64Class::SSE, X86_64Class::NoClass],
80 => [X86_64Class::X87, X86_64Class::NoClass],
128 => [X86_64Class::SSE, X86_64Class::SSEUp],
_ => [X86_64Class::Memory, X86_64Class::Memory],
};
}
if ty.is_pointer() {
return [X86_64Class::Integer, X86_64Class::NoClass];
}
if ty.is_struct() {
return Self::classify_struct_x86_64(ty);
}
if ty.is_array() {
let elem_ty_id = ty.element_type_id().unwrap_or(Type::i32().id);
let elem_ty = Type::from_id(elem_ty_id);
let elem_class = Self::classify_x86_64(&elem_ty);
let num_elems = ty.array_num_elements().unwrap_or(1) as usize;
let elem_size = elem_ty.size_in_bytes().unwrap_or(4) as usize;
let total = num_elems * elem_size;
if total > 16 {
return [X86_64Class::Memory, X86_64Class::Memory];
}
return elem_class;
}
[X86_64Class::Memory, X86_64Class::Memory]
}
fn classify_struct_x86_64(ty: &Type) -> [X86_64Class; 2] {
let field_type_ids = ty.struct_element_type_ids().unwrap_or_default();
if field_type_ids.is_empty() {
return [X86_64Class::NoClass, X86_64Class::NoClass];
}
let mut lo = X86_64Class::NoClass;
let mut hi = X86_64Class::NoClass;
let mut offset_bytes: u64 = 0;
for field_ty_id in &field_type_ids {
let field_ty = Type::from_id(*field_ty_id);
let field_size = field_ty.size_in_bytes().unwrap_or(4);
let field_align = field_ty.alignment().unwrap_or(4) as u64;
if offset_bytes % field_align != 0 {
offset_bytes = ((offset_bytes / field_align) + 1) * field_align;
}
let field_class = Self::classify_x86_64(&field_ty);
if offset_bytes < 8 {
lo = Self::merge_classes(lo, field_class[0]);
if field_class[1] != X86_64Class::NoClass && offset_bytes + 8 > 8 {
hi = Self::merge_classes(hi, field_class[1]);
}
} else {
hi = Self::merge_classes(hi, field_class[0]);
}
offset_bytes += field_size;
}
if lo == X86_64Class::Memory || hi == X86_64Class::Memory {
return [X86_64Class::Memory, X86_64Class::Memory];
}
[lo, hi]
}
fn merge_classes(c1: X86_64Class, c2: X86_64Class) -> X86_64Class {
if c1 == c2 {
return c1;
}
if c1 == X86_64Class::NoClass {
return c2;
}
if c2 == X86_64Class::NoClass {
return c1;
}
if c1 == X86_64Class::Memory || c2 == X86_64Class::Memory {
return X86_64Class::Memory;
}
if c1 == X86_64Class::Integer || c2 == X86_64Class::Integer {
return X86_64Class::Integer;
}
if c1 == X86_64Class::X87 || c1 == X86_64Class::X87Up || c1 == X86_64Class::ComplexX87 {
return X86_64Class::Memory;
}
if c2 == X86_64Class::X87 || c2 == X86_64Class::X87Up || c2 == X86_64Class::ComplexX87 {
return X86_64Class::Memory;
}
X86_64Class::SSE
}
pub fn classify_aarch64(ty: &Type) -> ARMClass {
let size_bytes = ty.size_in_bytes().unwrap_or(8);
if size_bytes > 16 {
return ARMClass::Memory;
}
if ty.is_floating_point() || ty.is_vector() {
return ARMClass::VFP;
}
if ty.is_integer() || ty.is_pointer() {
return ARMClass::Core;
}
if ty.is_struct() {
if let Ok((hfa_ty, count)) = Self::is_hfa(ty) {
if count <= 4 {
return ARMClass::VFP;
}
}
}
ARMClass::Core
}
pub fn is_hfa(ty: &Type) -> Result<(Type, u32), ()> {
let field_type_ids = ty.struct_element_type_ids().unwrap_or_default();
if field_type_ids.is_empty() {
return Err(());
}
let first_field_ty = Type::from_id(field_type_ids[0]);
if !first_field_ty.is_floating_point() && !first_field_ty.is_vector() {
return Err(());
}
let hfa_ty = first_field_ty;
let mut count = 0u32;
for field_ty_id in &field_type_ids {
let field_ty = Type::from_id(*field_ty_id);
if field_ty.id != hfa_ty.id {
return Err(());
}
count += 1;
}
Ok((hfa_ty, count))
}
pub fn num_gprs(classes: &[X86_64Class; 2]) -> u32 {
let mut count = 0;
if classes[0] == X86_64Class::Integer {
count += 1;
}
if classes[1] == X86_64Class::Integer {
count += 1;
}
count
}
pub fn num_sse_regs(classes: &[X86_64Class; 2]) -> u32 {
let mut count = 0;
if classes[0] == X86_64Class::SSE {
count += 1;
}
if classes[1] == X86_64Class::SSE {
count += 1;
}
count
}
pub fn is_memory_class(classes: &[X86_64Class; 2]) -> bool {
classes[0] == X86_64Class::Memory || classes[1] == X86_64Class::Memory
}
pub fn get_coerced_type(ty: &Type) -> Type {
let classes = Self::classify_x86_64(ty);
if Self::is_memory_class(&classes) {
return ty.clone();
}
match (classes[0], classes[1]) {
(X86_64Class::Integer, X86_64Class::NoClass) => {
let bits = ty.size_in_bits().unwrap_or(64).min(64);
Type::int(bits as u32)
}
(X86_64Class::Integer, X86_64Class::Integer) => Type::int(128),
(X86_64Class::SSE, X86_64Class::NoClass) => {
let bits = ty.size_in_bits().unwrap_or(64);
match bits {
32 => Type::float(),
64 => Type::double(),
_ => Type::double(),
}
}
(X86_64Class::SSE, X86_64Class::SSE) => Type::fixed_vector_with(Type::double().id, 2),
_ => ty.clone(),
}
}
}
pub struct StructLayout;
#[derive(Debug, Clone)]
pub struct FieldLayout {
pub name: String,
pub offset_bytes: u64,
pub size_bytes: u64,
pub alignment_bytes: u64,
pub field_type_id: TypeId,
}
#[derive(Debug, Clone)]
pub struct FullStructLayout {
pub name: String,
pub total_size_bytes: u64,
pub total_alignment_bytes: u64,
pub fields: Vec<FieldLayout>,
pub is_packed: bool,
pub is_union: bool,
pub has_tail_padding: bool,
}
impl StructLayout {
pub fn compute_layout(
struct_ty: &Type,
field_names: &[String],
is_packed: bool,
is_union: bool,
) -> FullStructLayout {
let field_type_ids = struct_ty.struct_element_type_ids().unwrap_or_default();
let name = struct_ty
.struct_name()
.unwrap_or_else(|| "<anon>".to_string());
let mut fields = Vec::new();
let mut current_offset: u64 = 0;
let mut max_alignment: u64 = 1;
let mut max_union_size: u64 = 0;
for (i, &field_ty_id) in field_type_ids.iter().enumerate() {
let field_ty = Type::from_id(field_ty_id);
let field_size = field_ty.size_in_bytes().unwrap_or(4);
let field_align = if is_packed {
1
} else {
field_ty.alignment().unwrap_or(4) as u64
};
if is_union {
max_union_size = max_union_size.max(field_size);
max_alignment = max_alignment.max(field_align);
fields.push(FieldLayout {
name: field_names
.get(i)
.cloned()
.unwrap_or_else(|| format!("field_{}", i)),
offset_bytes: 0,
size_bytes: field_size,
alignment_bytes: field_align,
field_type_id,
});
} else {
if !is_packed && current_offset % field_align != 0 {
current_offset = ((current_offset / field_align) + 1) * field_align;
}
max_alignment = max_alignment.max(field_align);
fields.push(FieldLayout {
name: field_names
.get(i)
.cloned()
.unwrap_or_else(|| format!("field_{}", i)),
offset_bytes: current_offset,
size_bytes: field_size,
alignment_bytes: field_align,
field_type_id,
});
current_offset += field_size;
}
}
let total_size = if is_union {
max_union_size
} else {
current_offset
};
let aligned_size = if is_packed {
total_size
} else if total_size % max_alignment != 0 {
((total_size / max_alignment) + 1) * max_alignment
} else {
total_size
};
let has_tail_padding = aligned_size > total_size;
FullStructLayout {
name,
total_size_bytes: aligned_size,
total_alignment_bytes: max_alignment,
fields,
is_packed,
is_union,
has_tail_padding,
}
}
pub fn get_field_offset(layout: &FullStructLayout, field_name: &str) -> Option<u64> {
layout
.fields
.iter()
.find(|f| f.name == field_name)
.map(|f| f.offset_bytes)
}
pub fn get_field_size(layout: &FullStructLayout, field_name: &str) -> Option<u64> {
layout
.fields
.iter()
.find(|f| f.name == field_name)
.map(|f| f.size_bytes)
}
pub fn has_bitfields(_layout: &FullStructLayout) -> bool {
false
}
pub fn inter_field_padding(layout: &FullStructLayout, field_index: usize) -> Option<u64> {
if field_index == 0 {
return Some(0);
}
if field_index >= layout.fields.len() {
return None;
}
let prev = &layout.fields[field_index - 1];
let curr = &layout.fields[field_index];
let prev_end = prev.offset_bytes + prev.size_bytes;
if curr.offset_bytes > prev_end {
Some(curr.offset_bytes - prev_end)
} else {
Some(0)
}
}
pub fn alignment(layout: &FullStructLayout) -> u64 {
layout.total_alignment_bytes
}
}
pub struct ExceptionCodeGen<'a> {
pub gen: &'a mut IRGenerator<'a>,
}
impl<'a> ExceptionCodeGen<'a> {
pub fn new(gen: &'a mut IRGenerator<'a>) -> Self {
ExceptionCodeGen { gen }
}
pub fn codegen_landing_pad(
&mut self,
result_ty: &Type,
num_clauses: u32,
personality_fn: Option<ValueRef>,
is_cleanup: bool,
) -> ValueRef {
let lp = instruction::create_landingpad(
result_ty.clone(),
num_clauses,
personality_fn,
is_cleanup,
);
let result = Value::new(result_ty.id);
result.borrow_mut().subclass = SubclassKind::Instruction;
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(lp);
}
result
}
pub fn codegen_resume(&mut self, exception_val: ValueRef) {
let resume_inst = instruction::create_resume(exception_val);
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(resume_inst);
}
}
pub fn codegen_invoke(
&mut self,
callee: ValueRef,
args: &[ValueRef],
return_ty: &Type,
normal_dest: &ValueRef,
unwind_dest: &ValueRef,
) -> ValueRef {
let invoke = instruction::create_invoke(
callee,
args,
return_ty.clone(),
normal_dest.clone(),
unwind_dest.clone(),
);
let result = Value::new(return_ty.id);
result.borrow_mut().subclass = SubclassKind::CallInst;
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(invoke);
}
result
}
pub fn default_personality_function() -> &'static str {
"__gxx_personality_v0"
}
pub fn c_personality_function() -> &'static str {
"__gcc_personality_v0"
}
pub fn seh_personality_function() -> &'static str {
"__C_specific_handler"
}
pub fn personality_for_language(is_cpp: bool, is_windows: bool) -> &'static str {
if is_windows {
Self::seh_personality_function()
} else if is_cpp {
Self::default_personality_function()
} else {
Self::c_personality_function()
}
}
}
pub struct LinkageCodeGen;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LLVMLinkage {
External,
AvailableExternally,
LinkOnceAny,
LinkOnceODR,
WeakAny,
WeakODR,
Appending,
Internal,
Private,
ExternalWeak,
Common,
}
impl LLVMLinkage {
pub fn as_str(&self) -> &'static str {
match self {
LLVMLinkage::External => "external",
LLVMLinkage::AvailableExternally => "available_externally",
LLVMLinkage::LinkOnceAny => "linkonce",
LLVMLinkage::LinkOnceODR => "linkonce_odr",
LLVMLinkage::WeakAny => "weak",
LLVMLinkage::WeakODR => "weak_odr",
LLVMLinkage::Appending => "appending",
LLVMLinkage::Internal => "internal",
LLVMLinkage::Private => "private",
LLVMLinkage::ExternalWeak => "extern_weak",
LLVMLinkage::Common => "common",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LLVMVisibility {
Default,
Hidden,
Protected,
}
impl LLVMVisibility {
pub fn as_str(&self) -> &'static str {
match self {
LLVMVisibility::Default => "default",
LLVMVisibility::Hidden => "hidden",
LLVMVisibility::Protected => "protected",
}
}
}
impl LinkageCodeGen {
pub fn map_linkage(linkage: &Linkage, is_definition: bool) -> LLVMLinkage {
match linkage {
Linkage::External if is_definition => LLVMLinkage::External,
Linkage::External => LLVMLinkage::External,
Linkage::Internal => LLVMLinkage::Internal,
Linkage::None => {
if is_definition {
LLVMLinkage::Internal
} else {
LLVMLinkage::External
}
}
}
}
pub fn map_visibility(is_hidden: bool, is_protected: bool) -> LLVMVisibility {
if is_hidden {
LLVMVisibility::Hidden
} else if is_protected {
LLVMVisibility::Protected
} else {
LLVMVisibility::Default
}
}
pub fn should_use_comdat(linkage: &LLVMLinkage) -> bool {
matches!(
linkage,
LLVMLinkage::LinkOnceAny
| LLVMLinkage::LinkOnceODR
| LLVMLinkage::WeakAny
| LLVMLinkage::WeakODR
)
}
pub fn section_prefix(attr: &str) -> &'static str {
match attr {
"init" | ".init" => ".init_array",
"fini" | ".fini" => ".fini_array",
"ctors" | ".ctors" => ".ctors",
"dtors" | ".dtors" => ".dtors",
"text" | ".text" => ".text",
"data" | ".data" => ".data",
"rodata" | ".rodata" => ".rodata",
"bss" | ".bss" => ".bss",
_ => "",
}
}
pub fn canonicalize_section(name: &str) -> String {
if name.starts_with('.') {
name.to_string()
} else {
format!(".{}", name)
}
}
pub fn default_section_for_linkage(linkage: &LLVMLinkage) -> &'static str {
match linkage {
LLVMLinkage::Internal | LLVMLinkage::Private => ".text",
LLVMLinkage::Common => ".bss",
_ => "",
}
}
pub fn is_dll_export(_attrs: &[&str]) -> bool {
false
}
pub fn is_dll_import(_attrs: &[&str]) -> bool {
false
}
}
pub struct InlineASMCodeGen<'a> {
pub gen: &'a mut IRGenerator<'a>,
}
#[derive(Debug, Clone)]
pub struct InlineAsmDesc {
pub asm_string: String,
pub constraints: String,
pub has_side_effects: bool,
pub is_align_stack: bool,
pub is_intel_dialect: bool,
pub can_throw: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AsmConstraintKind {
Register,
Memory,
Immediate,
General,
Any,
Offsetable,
NonOffsetable,
Matching(u32),
ReadWrite,
WriteOnly,
EarlyClobber,
Commutative,
Ignore,
FloatRegister,
VectorRegister,
Unknown,
}
impl AsmConstraintKind {
pub fn parse(s: &str) -> Vec<AsmConstraintKind> {
let mut constraints = Vec::new();
let mut chars = s.chars().peekable();
while let Some(&ch) = chars.peek() {
match ch {
'r' => constraints.push(AsmConstraintKind::Register),
'm' => constraints.push(AsmConstraintKind::Memory),
'i' => constraints.push(AsmConstraintKind::Immediate),
'g' => constraints.push(AsmConstraintKind::General),
'X' => constraints.push(AsmConstraintKind::Any),
'o' => constraints.push(AsmConstraintKind::Offsetable),
'V' => constraints.push(AsmConstraintKind::NonOffsetable),
'=' => constraints.push(AsmConstraintKind::WriteOnly),
'+' => constraints.push(AsmConstraintKind::ReadWrite),
'&' => constraints.push(AsmConstraintKind::EarlyClobber),
'%' => constraints.push(AsmConstraintKind::Commutative),
'0'..='9' => {
let num: u32 = ch.to_digit(10).unwrap();
constraints.push(AsmConstraintKind::Matching(num));
}
'#' | ',' | '*' | '?' | '!' | '~' => {
constraints.push(AsmConstraintKind::Ignore);
}
'f' => constraints.push(AsmConstraintKind::FloatRegister),
'w' | 'v' => constraints.push(AsmConstraintKind::VectorRegister),
_ => constraints.push(AsmConstraintKind::Unknown),
}
chars.next();
}
constraints
}
pub fn is_output(&self) -> bool {
matches!(
self,
AsmConstraintKind::WriteOnly | AsmConstraintKind::ReadWrite
)
}
pub fn is_input(&self) -> bool {
!self.is_output() && !matches!(self, AsmConstraintKind::Ignore | AsmConstraintKind::Unknown)
}
}
impl<'a> InlineASMCodeGen<'a> {
pub fn new(gen: &'a mut IRGenerator<'a>) -> Self {
InlineASMCodeGen { gen }
}
pub fn codegen_inline_asm(
&mut self,
desc: &InlineAsmDesc,
inputs: &[ValueRef],
outputs: &[ValueRef],
clobbers: &[String],
) -> ValueRef {
let asm_val = Value::new(Type::void().id);
asm_val.borrow_mut().subclass = SubclassKind::InlineAsm;
asm_val.borrow_mut().name = Some(desc.asm_string.clone());
let full_constraint = format!("{},{}", desc.constraints, clobbers.join(","));
asm_val.borrow_mut().subclass_data = Some(full_constraint.len() as u64);
for output in outputs {
asm_val.borrow_mut().push_operand(output.clone());
}
for input in inputs {
asm_val.borrow_mut().push_operand(input.clone());
}
if let Some(current_bb) = self.gen.builder.get_insert_block() {
current_bb.borrow_mut().instructions.push(asm_val.clone());
}
asm_val
}
pub fn parse_clobbers(clobber_str: &str) -> Vec<String> {
clobber_str
.split(',')
.map(|s| s.trim().trim_matches('"').to_string())
.filter(|s| !s.is_empty())
.collect()
}
pub fn is_memory_clobber(clobbers: &[String]) -> bool {
clobbers.iter().any(|c| c == "memory")
}
pub fn is_cc_clobber(clobbers: &[String]) -> bool {
clobbers.iter().any(|c| c == "cc")
}
pub fn x86_clobber_registers() -> Vec<&'static str> {
vec![
"ax", "bx", "cx", "dx", "si", "di", "bp", "sp", "r8", "r9", "r10", "r11", "r12", "r13",
"r14", "r15", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8",
"xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", "st", "st(0)", "st(1)",
"st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)", "mm0", "mm1", "mm2", "mm3",
"mm4", "mm5", "mm6", "mm7", "flags", "fpsr", "dirflag",
]
}
pub fn is_valid_clobber(reg: &str) -> bool {
Self::x86_clobber_registers().contains(®) || reg == "memory" || reg == "cc"
}
pub fn encode_constraints(
output_constraints: &[String],
input_constraints: &[String],
clobbers: &[String],
) -> String {
let mut parts: Vec<String> = Vec::new();
for c in output_constraints {
parts.push(format!("={}", c));
}
for c in input_constraints {
parts.push(c.clone());
}
if !clobbers.is_empty() {
parts.push(format!("~{{{}}}", clobbers.join(",")));
}
parts.join(",")
}
}
pub struct IRVerifier;
#[derive(Debug, Clone)]
pub struct VerificationResult {
pub valid: bool,
pub errors: Vec<String>,
pub warnings: Vec<String>,
}
impl VerificationResult {
pub fn new() -> Self {
VerificationResult {
valid: true,
errors: Vec::new(),
warnings: Vec::new(),
}
}
pub fn add_error(&mut self, msg: String) {
self.valid = false;
self.errors.push(msg);
}
pub fn add_warning(&mut self, msg: String) {
self.warnings.push(msg);
}
pub fn is_valid(&self) -> bool {
self.valid && self.errors.is_empty()
}
}
impl Default for VerificationResult {
fn default() -> Self {
Self::new()
}
}
impl IRVerifier {
pub fn verify_module(module: &Module) -> VerificationResult {
let mut result = VerificationResult::new();
if module.name.is_empty() {
result.add_warning("module has no name".to_string());
}
for func in &module.functions {
let func_result = Self::verify_function(func);
if !func_result.is_valid() {
for err in &func_result.errors {
result.add_error(err.clone());
}
}
for warn in &func_result.warnings {
result.add_warning(warn.clone());
}
}
result
}
pub fn verify_function(func: &ValueRef) -> VerificationResult {
let mut result = VerificationResult::new();
let func_ref = func.borrow();
if func_ref.blocks.is_empty() {
if !func_ref.is_vararg {
}
}
for (_i, bb) in func_ref.blocks.iter().enumerate() {
let bb_result = Self::verify_basic_block(bb);
if !bb_result.is_valid() {
for err in &bb_result.errors {
result.add_error(err.clone());
}
}
}
result
}
pub fn verify_basic_block(bb: &ValueRef) -> VerificationResult {
let mut result = VerificationResult::new();
let bb_ref = bb.borrow();
let instructions = &bb_ref.instructions;
if instructions.is_empty() {
result.add_warning(format!("basic block {:?} has no instructions", bb_ref.name));
return result;
}
let last_inst = instructions.last().unwrap();
let last_opcode = last_inst.borrow().opcode;
if !instruction::is_terminator(last_opcode) {
result.add_error(format!(
"basic block {:?} does not end with a terminator",
bb_ref.name
));
}
let terminator_count = instructions
.iter()
.filter(|inst| instruction::is_terminator(inst.borrow().opcode))
.count();
if terminator_count > 1 {
result.add_error(format!(
"basic block {:?} has {} terminators (expected 1)",
bb_ref.name, terminator_count
));
}
let mut seen_non_phi = false;
for inst in instructions {
let op = inst.borrow().opcode;
let is_phi = matches!(op, Opcode::Phi);
if is_phi && seen_non_phi {
result.add_error(format!(
"phi node after non-phi instruction in block {:?}",
bb_ref.name
));
}
if !is_phi {
seen_non_phi = true;
}
}
result
}
pub fn verify_ssa_dominance(func: &ValueRef) -> VerificationResult {
let mut result = VerificationResult::new();
let func_ref = func.borrow();
for (_block_idx, bb) in func_ref.blocks.iter().enumerate() {
let bb_ref = bb.borrow();
for inst in &bb_ref.instructions {
let inst_ref = inst.borrow();
for operand in &inst_ref.operands {
if operand.borrow().isa(SubclassKind::Instruction) {
if operand.borrow().vid == 0 && !operand.borrow().is_constant {
result.add_warning(format!(
"instruction in block {:?} uses undefined value",
bb_ref.name
));
}
}
}
}
}
result
}
pub fn verify_block_references(func: &ValueRef) -> VerificationResult {
let mut result = VerificationResult::new();
let func_ref = func.borrow();
let block_ids: HashSet<usize> = func_ref.blocks.iter().map(|bb| bb.borrow().vid).collect();
for bb in &func_ref.blocks {
let bb_ref = bb.borrow();
if let Some(last) = bb_ref.instructions.last() {
let last_ref = last.borrow();
for succ in &last_ref.successors {
let succ_id = succ.borrow().vid;
if !block_ids.contains(&succ_id) {
result.add_error(format!(
"branch target in block {:?} references non-existent block",
bb_ref.name
));
}
}
}
}
result
}
pub fn quick_check(module: &Module) -> bool {
if module.functions.is_empty() && module.globals.is_empty() {
return true; }
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::LLVMContext;
use crate::ir_builder::IRBuilder;
use crate::module::Module;
use crate::types::Type;
use crate::value::Value;
fn make_gen<'a>(ctx: &'a LLVMContext, module: &'a mut Module) -> IRGenerator<'a> {
let builder = IRBuilder::new(ctx);
IRGenerator::new(ctx, module, builder)
}
fn make_function_decl(name: &str, return_type: TypeNode) -> FunctionDecl {
FunctionDecl {
name: name.to_string(),
ret_ty: return_type,
params: vec![],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
}
}
fn make_var_decl(name: &str, ty: TypeNode) -> VarDecl {
VarDecl {
name: name.to_string(),
ty,
init: None,
linkage: Linkage::None,
is_global: false,
is_extern: false,
is_static: false,
}
}
fn make_int_literal(v: i64) -> Expr {
Expr::IntLiteral(v)
}
fn make_binary(op: BinaryOp, lhs: Expr, rhs: Expr) -> Expr {
Expr::Binary(op, Box::new(lhs), Box::new(rhs))
}
fn make_ident(name: &str) -> Expr {
Expr::Ident(name.to_string())
}
#[test]
fn test_ir_generator_new() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let gen = make_gen(&ctx, &mut module);
assert!(gen.errors.is_empty());
assert!(gen.named_values.is_empty());
assert_eq!(gen.source_file, "unknown.c");
}
#[test]
fn test_convert_void_type() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let void_ty = gen.convert_type(&TypeNode::Void);
assert!(void_ty.is_void());
}
#[test]
fn test_convert_int_type() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let int_ty = gen.convert_type(&TypeNode::Int);
assert!(int_ty.is_integer());
assert_eq!(int_ty.size_in_bits().unwrap(), 32);
}
#[test]
fn test_convert_float_type() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let float_ty = gen.convert_type(&TypeNode::Float);
assert!(float_ty.is_float());
}
#[test]
fn test_convert_double_type() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let double_ty = gen.convert_type(&TypeNode::Double);
assert!(double_ty.is_double());
}
#[test]
fn test_convert_bool_type() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let bool_ty = gen.convert_type(&TypeNode::Bool);
assert_eq!(bool_ty.size_in_bits().unwrap(), 1);
}
#[test]
fn test_convert_pointer_type() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let ptr_ty = gen.convert_type(&TypeNode::Pointer(Box::new(TypeNode::Int)));
assert!(ptr_ty.is_pointer());
}
#[test]
fn test_convert_array_type() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let arr_ty = gen.convert_type(&TypeNode::Array(Box::new(TypeNode::Int), 10));
assert!(arr_ty.is_array());
}
#[test]
fn test_compile_int_literal() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let gen = make_gen(&ctx, &mut module);
let val = gen.compile_int_literal(42).unwrap();
assert!(val.borrow().is_constant);
}
#[test]
fn test_compile_float_literal() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let gen = make_gen(&ctx, &mut module);
let val = gen.compile_float_literal(3.14).unwrap();
assert!(val.borrow().is_constant);
}
#[test]
fn test_compile_double_literal() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let gen = make_gen(&ctx, &mut module);
let val = gen.compile_double_literal(2.718).unwrap();
assert!(val.borrow().is_constant);
}
#[test]
fn test_compile_char_literal() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let gen = make_gen(&ctx, &mut module);
let val = gen.compile_char_literal(b'A').unwrap();
assert!(val.borrow().is_constant);
}
#[test]
fn test_compile_binary_add() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let ret_ty = TypeNode::Int;
let llvm_ret = gen.convert_type(&ret_ty);
let func_ty = Type::function_type_with(llvm_ret.id, &[], false);
let func_val = Value::named("test_add");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let lhs = Expr::IntLiteral(10);
let rhs = Expr::IntLiteral(20);
let bin_expr = make_binary(BinaryOp::Add, lhs, rhs);
let result = gen.compile_expr(&bin_expr);
assert!(result.is_ok());
gen.current_function = None;
}
#[test]
fn test_compile_binary_sub() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let ret_ty = TypeNode::Int;
let llvm_ret = gen.convert_type(&ret_ty);
let func_ty = Type::function_type_with(llvm_ret.id, &[], false);
let func_val = Value::named("test_sub");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let lhs = Expr::IntLiteral(30);
let rhs = Expr::IntLiteral(10);
let bin_expr = make_binary(BinaryOp::Sub, lhs, rhs);
let result = gen.compile_expr(&bin_expr);
assert!(result.is_ok());
gen.current_function = None;
}
#[test]
fn test_compile_unary_minus() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let ret_ty = TypeNode::Int;
let llvm_ret = gen.convert_type(&ret_ty);
let func_ty = Type::function_type_with(llvm_ret.id, &[], false);
let func_val = Value::named("test_unary");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let unary_expr = Expr::Unary(UnaryOp::Minus, Box::new(Expr::IntLiteral(5)));
let result = gen.compile_expr(&unary_expr);
assert!(result.is_ok());
gen.current_function = None;
}
#[test]
fn test_create_function_prototype_void() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let fd = FunctionDecl {
name: "void_func".to_string(),
ret_ty: TypeNode::Void,
params: vec![],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
let result = gen.create_function_prototype(&fd);
assert!(result.is_ok());
assert!(module.has_function("void_func"));
}
#[test]
fn test_create_function_prototype_with_params() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let fd = FunctionDecl {
name: "param_func".to_string(),
ret_ty: TypeNode::Int,
params: vec![
VarDecl {
name: "a".to_string(),
ty: TypeNode::Int,
init: None,
linkage: Linkage::None,
is_global: false,
is_extern: false,
is_static: false,
},
VarDecl {
name: "b".to_string(),
ty: TypeNode::Float,
init: None,
linkage: Linkage::None,
is_global: false,
is_extern: false,
is_static: false,
},
],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
let result = gen.create_function_prototype(&fd);
assert!(result.is_ok());
assert!(module.has_function("param_func"));
}
#[test]
fn test_recognize_builtin_alloca() {
let kind = IRGenerator::<'_>::get_builtin_kind("__builtin_alloca");
assert_eq!(kind, BuiltinKind::Alloca);
}
#[test]
fn test_recognize_builtin_memcpy() {
let kind = IRGenerator::<'_>::get_builtin_kind("__builtin_memcpy");
assert_eq!(kind, BuiltinKind::Memcpy);
}
#[test]
fn test_recognize_builtin_trap() {
let kind = IRGenerator::<'_>::get_builtin_kind("__builtin_trap");
assert_eq!(kind, BuiltinKind::Trap);
}
#[test]
fn test_recognize_builtin_expect() {
let kind = IRGenerator::<'_>::get_builtin_kind("__builtin_expect");
assert_eq!(kind, BuiltinKind::Expect);
}
#[test]
fn test_recognize_builtin_unknown() {
let kind = IRGenerator::<'_>::get_builtin_kind("__builtin_nonexistent");
assert_eq!(kind, BuiltinKind::Unknown);
}
#[test]
fn test_is_builtin() {
assert!(BuiltinCodeGen::is_builtin("__builtin_alloca"));
assert!(BuiltinCodeGen::is_builtin("__builtin_expect"));
assert!(!BuiltinCodeGen::is_builtin("malloc"));
}
#[test]
fn test_is_unsigned_type() {
assert!(TypeConversion::is_unsigned_type(&TypeNode::UInt));
assert!(TypeConversion::is_unsigned_type(&TypeNode::ULong));
assert!(!TypeConversion::is_unsigned_type(&TypeNode::Int));
}
#[test]
fn test_integer_promotion_type() {
let promoted = TypeConversion::integer_promotion_type(&TypeNode::Char);
assert_eq!(promoted, TypeNode::Int);
let promoted = TypeConversion::integer_promotion_type(&TypeNode::Int);
assert_eq!(promoted, TypeNode::Int);
}
#[test]
fn test_integer_rank() {
assert!(
TypeConversion::integer_rank(&TypeNode::Int)
> TypeConversion::integer_rank(&TypeNode::Char)
);
assert!(
TypeConversion::integer_rank(&TypeNode::LongLong)
> TypeConversion::integer_rank(&TypeNode::Int)
);
}
#[test]
fn test_is_truncation() {
assert!(TypeConversion::is_truncation(64, 32));
assert!(!TypeConversion::is_truncation(32, 64));
}
#[test]
fn test_is_extension() {
assert!(TypeConversion::is_extension(32, 64));
assert!(!TypeConversion::is_extension(64, 32));
}
#[test]
fn test_codegen_struct_gep() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let struct_ty = Type::struct_named_with("Point", &[Type::i32().id, Type::i32().id], false);
let ptr_ty = Type::pointer(struct_ty.id);
let ptr_val = Value::new(ptr_ty.id);
let mut agg = AggregateCodeGen::new(&mut gen);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("test_gep");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let result = agg.codegen_struct_gep(ptr_val, &struct_ty, 1);
gen.current_function = None;
let result_ty = Type::from_id(result.borrow().ty);
assert!(result_ty.is_pointer());
}
#[test]
fn test_codegen_array_gep() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let elem_ty = Type::i32();
let ptr_ty = Type::pointer(elem_ty.id);
let ptr_val = Value::new(ptr_ty.id);
let mut agg = AggregateCodeGen::new(&mut gen);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("test_arr_gep");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let index_val = gen.compile_int_literal(5).unwrap();
let result = agg.codegen_array_gep(ptr_val, &elem_ty, index_val);
gen.current_function = None;
let result_ty = Type::from_id(result.borrow().ty);
assert!(result_ty.is_pointer());
}
#[test]
fn test_create_compile_unit() {
let cu = DebugInfo::create_compile_unit(
DebugInfo::DW_LANG_C11,
"test.c",
"/home/test",
"clang 18.0.0",
false,
"",
0,
);
assert!(cu.borrow().subclass == SubclassKind::MetadataAsValue);
}
#[test]
fn test_create_file() {
let file = DebugInfo::create_file("main.c", "/src");
assert!(file.borrow().subclass == SubclassKind::MetadataAsValue);
}
#[test]
fn test_create_subprogram() {
let file = DebugInfo::create_file("main.c", "/src");
let void_ty = DebugInfo::create_basic_type("void", 0, 0);
let sp = DebugInfo::create_subprogram(
"main", "main", &file, 10, &void_ty, false, true, 10, 0, false,
);
assert!(sp.borrow().subclass == SubclassKind::MetadataAsValue);
}
#[test]
fn test_create_location() {
let file = DebugInfo::create_file("main.c", "/src");
let void_ty = DebugInfo::create_basic_type("void", 0, 0);
let scope = DebugInfo::create_subprogram(
"main", "main", &file, 10, &void_ty, false, true, 10, 0, false,
);
let loc = DebugInfo::create_location(42, 5, &scope, None);
assert!(loc.borrow().subclass == SubclassKind::MetadataAsValue);
}
#[test]
fn test_dwarf_language_constants() {
assert_eq!(DebugInfo::DW_LANG_C, 2);
assert_eq!(DebugInfo::DW_LANG_C11, 4);
assert_eq!(DebugInfo::DW_LANG_CPlusPlus, 9);
assert_eq!(DebugInfo::DW_LANG_CPlusPlus20, 16);
}
#[test]
fn test_expr_codegen_literals() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let expr_gen = ExprCodeGen::new(&mut gen);
let int_val = expr_gen.codegen_integer_literal(100, 32);
assert!(int_val.borrow().is_constant);
let float_val = expr_gen.codegen_float_literal(1.5);
assert!(float_val.borrow().is_constant);
let char_val = expr_gen.codegen_char_literal('Z');
assert!(char_val.borrow().is_constant);
}
#[test]
fn test_compile_translation_unit_empty() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let tu = TranslationUnit::new("empty.c");
let errors = gen.compile_translation_unit(&tu);
assert_eq!(errors, 0);
}
#[test]
fn test_compile_module_has_target() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let tu = TranslationUnit::new("target.c");
gen.compile_translation_unit(&tu);
assert_eq!(module.get_target_triple(), "x86_64-unknown-linux-gnu");
}
#[test]
fn test_builtin_emit_trap() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("trap_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let mut bc = BuiltinCodeGen::new(&mut gen);
let result = bc.emit_trap();
assert!(result.is_ok());
gen.current_function = None;
}
#[test]
fn test_builtin_emit_constant_p() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut bc = BuiltinCodeGen::new(&mut gen);
let const_val = gen.compile_int_literal(42).unwrap();
let result = bc.emit_constant_p(&[const_val]);
assert!(result.is_ok());
let result = bc.emit_constant_p(&[]);
assert!(result.is_ok());
}
#[test]
fn test_builtin_llvm_intrinsic_names() {
assert_eq!(
BuiltinCodeGen::llvm_intrinsic_name(&BuiltinKind::Memcpy),
Some("llvm.memcpy.p0i8.p0i8.i64")
);
assert_eq!(
BuiltinCodeGen::llvm_intrinsic_name(&BuiltinKind::Trap),
Some("llvm.trap")
);
assert_eq!(
BuiltinCodeGen::llvm_intrinsic_name(&BuiltinKind::Sqrt),
Some("llvm.sqrt.f64")
);
}
#[test]
fn test_decl_codegen_local_variable() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("decl_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let mut dc = DeclCodeGen::new(&mut gen);
let int_ty = Type::i32();
let init_expr = make_int_literal(42);
let result = dc.codegen_local_variable("x", &int_ty, Some(&init_expr));
assert!(result.is_ok());
assert!(gen.named_values.contains_key("x"));
gen.current_function = None;
}
#[test]
fn test_decl_codegen_global_variable() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut dc = DeclCodeGen::new(&mut gen);
let int_ty = Type::i32();
let init_expr = make_int_literal(100);
let result = dc.codegen_global_variable("g_var", &int_ty, Some(&init_expr), false);
assert!(result.is_ok());
assert!(module.get_global_variable("g_var").is_some());
}
#[test]
fn test_decl_codegen_extern_variable() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut dc = DeclCodeGen::new(&mut gen);
let int_ty = Type::i32();
let result = dc.codegen_extern_variable("ext_var", &int_ty);
assert!(result.is_ok());
assert!(module.get_global_variable("ext_var").is_some());
}
#[test]
fn test_stmt_codegen_return() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("stmt_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let mut sc = StmtCodeGen::new(&mut gen);
let ret_stmt = Stmt::Return(None);
let result = sc.codegen_stmt(&ret_stmt);
assert!(result.is_ok());
gen.current_function = None;
}
#[test]
fn test_evaluate_const_expr_int() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let gen = make_gen(&ctx, &mut module);
let expr = make_int_literal(42);
let result = gen.evaluate_const_expr(&expr);
assert_eq!(result, Some(42));
}
#[test]
fn test_evaluate_const_expr_binary() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let gen = make_gen(&ctx, &mut module);
let expr = make_binary(BinaryOp::Add, make_int_literal(10), make_int_literal(32));
let result = gen.evaluate_const_expr(&expr);
assert_eq!(result, Some(42));
}
#[test]
fn test_evaluate_const_expr_unary_minus() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let gen = make_gen(&ctx, &mut module);
let expr = Expr::Unary(UnaryOp::Minus, Box::new(make_int_literal(42)));
let result = gen.evaluate_const_expr(&expr);
assert_eq!(result, Some(-42));
}
#[test]
fn test_usual_arithmetic_same_type() {
let result =
TypeConversion::usual_arithmetic_conversion_type(&TypeNode::Int, &TypeNode::Int);
assert_eq!(result, TypeNode::Int);
}
#[test]
fn test_usual_arithmetic_float_double() {
let result =
TypeConversion::usual_arithmetic_conversion_type(&TypeNode::Float, &TypeNode::Double);
assert_eq!(result, TypeNode::Double);
}
#[test]
fn test_conversion_kind_integer_cast() {
let kind = TypeConversion::integer_cast_kind(true, 64, 32);
assert_eq!(kind, ConversionKind::Sext);
let kind = TypeConversion::integer_cast_kind(false, 64, 32);
assert_eq!(kind, ConversionKind::Zext);
let kind = TypeConversion::integer_cast_kind(true, 32, 64);
assert_eq!(kind, ConversionKind::Trunc);
}
#[test]
fn test_compile_string_literal() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let result = gen.compile_string_literal("hello");
assert!(result.is_ok());
let result2 = gen.compile_string_literal("hello");
assert!(result2.is_ok());
}
#[test]
fn test_expr_codegen_conditional() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::i32().id, &[], false);
let func_val = Value::named("cond_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let cond = make_int_literal(1);
let then_val = make_int_literal(10);
let else_val = make_int_literal(20);
let ternary = Expr::Conditional(Box::new(cond), Box::new(then_val), Box::new(else_val));
let result = gen.compile_expr(&ternary);
assert!(result.is_ok());
gen.current_function = None;
}
#[test]
fn test_expr_codegen_subscript() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::i32().id, &[], false);
let func_val = Value::named("sub_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let arr_ty = Type::array_with(Type::i32().id, 10);
let alloca = gen.builder.create_alloca(arr_ty);
gen.named_values.insert("arr".to_string(), alloca);
let sub_expr = Expr::Subscript(Box::new(make_ident("arr")), Box::new(make_int_literal(3)));
let result = gen.compile_expr(&sub_expr);
let _ = result;
gen.current_function = None;
}
#[test]
fn test_builtin_return_types() {
let void_ty = BuiltinCodeGen::builtin_return_type(&BuiltinKind::Unreachable);
assert!(void_ty.is_void());
let ptr_ty = BuiltinCodeGen::builtin_return_type(&BuiltinKind::Alloca);
assert!(ptr_ty.is_pointer());
let int_ty = BuiltinCodeGen::builtin_return_type(&BuiltinKind::ConstantP);
assert!(int_ty.is_integer());
}
#[test]
fn test_dwarf_tag_constants() {
assert_eq!(DebugInfo::DW_TAG_array_type, 1);
assert_eq!(DebugInfo::DW_TAG_pointer_type, 15);
assert_eq!(DebugInfo::DW_TAG_structure_type, 19);
assert_eq!(DebugInfo::DW_TAG_subprogram, 46);
}
#[test]
fn test_dwarf_ate_constants() {
assert_eq!(DebugInfo::DW_ATE_float, 4);
assert_eq!(DebugInfo::DW_ATE_signed, 5);
assert_eq!(DebugInfo::DW_ATE_unsigned, 7);
}
#[test]
fn test_compile_lvalue_ident() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("lval_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let int_ty = Type::i32();
let alloca = gen.builder.create_alloca(int_ty);
gen.named_values.insert("x".to_string(), alloca);
let ident_expr = make_ident("x");
let result = gen.compile_lvalue(&ident_expr);
assert!(result.is_ok());
gen.current_function = None;
}
#[test]
fn test_expr_codegen_pre_inc() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("inc_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let int_ty = Type::i32();
let alloca = gen.builder.create_alloca(int_ty.clone());
let zero = gen.builder.get_int32(0);
gen.builder.create_store(zero, alloca.clone());
let mut ecg = ExprCodeGen::new(&mut gen);
let result = ecg.codegen_pre_inc(alloca);
gen.current_function = None;
}
#[test]
fn test_expr_codegen_cast_operations() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("cast_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let ecg = ExprCodeGen::new(&mut gen);
let int_val = ecg.codegen_integer_literal(42, 32);
let i64_ty = Type::i64();
let extended = ecg.codegen_zext(int_val.clone(), i64_ty.clone());
let truncated = ecg.codegen_trunc(extended, Type::i32());
gen.current_function = None;
}
#[test]
fn test_compile_simple_function_body() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let ret_stmt = Stmt::Return(Some(make_int_literal(42)));
let body = CompoundStmt {
stmts: vec![ret_stmt],
};
let fd = FunctionDecl {
name: "simple_func".to_string(),
ret_ty: TypeNode::Int,
params: vec![],
body: Some(body),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
let result = gen.compile_function(&fd);
assert!(result.is_ok());
assert!(module.has_function("simple_func"));
}
#[test]
fn test_aggregate_struct_field_load() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let struct_ty = Type::struct_named_with(
"Vec3",
&[Type::f32().id, Type::f32().id, Type::f32().id],
false,
);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("field_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let alloca = gen.builder.create_alloca(struct_ty.clone());
let mut agg = AggregateCodeGen::new(&mut gen);
let field_ty = Type::f32();
let result = agg.codegen_struct_field_load(alloca, &struct_ty, 1, &field_ty);
gen.current_function = None;
}
#[test]
fn test_convert_scalar_int_to_float() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("conv_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let int_val = gen.compile_int_literal(42).unwrap();
let int_ty = Type::from_id(int_val.borrow().ty);
let float_ty = Type::float();
let result = gen.convert_scalar(int_val, int_ty, float_ty);
assert!(result.is_ok());
gen.current_function = None;
}
#[test]
fn test_integer_promotion() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("promo_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let char_val = gen.compile_char_literal(b'A').unwrap();
let promoted = gen.integer_promotion(char_val);
assert!(promoted.is_ok());
let promoted_ty = Type::from_id(promoted.unwrap().borrow().ty);
assert_eq!(promoted_ty.size_in_bits().unwrap(), 32);
gen.current_function = None;
}
#[test]
fn test_debug_location_attachment() {
let file = DebugInfo::create_file("test.c", "/tmp");
let void_ty = DebugInfo::create_basic_type("void", 0, 0);
let scope = DebugInfo::create_subprogram(
"test_func",
"test_func",
&file,
1,
&void_ty,
false,
true,
1,
0,
false,
);
let inst = Value::new(Type::i32().id);
DebugInfo::attach_debug_location(&inst, 10, 5, &scope);
assert!(inst.borrow().has_metadata());
}
#[test]
fn test_aggregate_compound_literal() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("compound_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let int_ty = Type::i32();
let init_val = gen.compile_int_literal(99).unwrap();
let mut agg = AggregateCodeGen::new(&mut gen);
let result = agg.codegen_compound_literal(&int_ty, init_val);
gen.current_function = None;
let result_ty = Type::from_id(result.borrow().ty);
assert!(result_ty.is_pointer());
}
#[test]
fn test_vector_splat() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("vec_splat_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let scalar = gen.compile_int_literal(42).unwrap();
let mut vcg = VectorCodeGen::new(&mut gen);
let result = vcg.codegen_vector_splat(scalar, 4);
gen.current_function = None;
let ty = Type::from_id(result.borrow().ty);
assert!(ty.is_vector());
}
#[test]
fn test_vector_extract_element() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("vec_extract_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let vec_ty = Type::fixed_vector_with(Type::i32().id, 4);
let vector = Value::new(vec_ty.id);
let mut vcg = VectorCodeGen::new(&mut gen);
let result = vcg.codegen_extract_element(vector, 2);
gen.current_function = None;
let ty = Type::from_id(result.borrow().ty);
assert!(ty.is_integer());
}
#[test]
fn test_vector_insert_element() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("vec_insert_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let vec_ty = Type::fixed_vector_with(Type::i32().id, 4);
let vector = Value::new(vec_ty.id);
let element = gen.compile_int_literal(7).unwrap();
let mut vcg = VectorCodeGen::new(&mut gen);
let result = vcg.codegen_insert_element(vector, element, 1);
gen.current_function = None;
let ty = Type::from_id(result.borrow().ty);
assert!(ty.is_vector());
}
#[test]
fn test_vector_shuffle() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("vec_shuffle_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let vec_ty = Type::fixed_vector_with(Type::i32().id, 4);
let v1 = Value::new(vec_ty.id);
let v2 = Value::new(vec_ty.id);
let mut vcg = VectorCodeGen::new(&mut gen);
let mask = vec![0, 1, 2, 3];
let result = vcg.codegen_shuffle_vector(v1, v2, &mask);
gen.current_function = None;
let ty = Type::from_id(result.borrow().ty);
assert!(ty.is_vector());
}
#[test]
fn test_vector_reverse() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("vec_reverse_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let vec_ty = Type::fixed_vector_with(Type::i32().id, 4);
let vector = Value::new(vec_ty.id);
let mut vcg = VectorCodeGen::new(&mut gen);
let result = vcg.codegen_vector_reverse(vector);
gen.current_function = None;
let ty = Type::from_id(result.borrow().ty);
assert!(ty.is_vector());
}
#[test]
fn test_atomic_load() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("atomic_load_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let int_ty = Type::i32();
let alloca = gen.builder.create_alloca(int_ty);
let mut acg = AtomicCodeGen::new(&mut gen);
let result = acg.codegen_atomic_load(alloca, MemOrdering::SequentiallyConsistent);
gen.current_function = None;
}
#[test]
fn test_atomic_exchange() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("atomic_xchg_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let int_ty = Type::i32();
let alloca = gen.builder.create_alloca(int_ty.clone());
let value = gen.compile_int_literal(42).unwrap();
let mut acg = AtomicCodeGen::new(&mut gen);
let result = acg.codegen_atomic_exchange(alloca, value, MemOrdering::AcquireRelease);
gen.current_function = None;
}
#[test]
fn test_atomic_cmp_xchg() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("cmpxchg_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let int_ty = Type::i32();
let alloca = gen.builder.create_alloca(int_ty.clone());
let cmp = gen.compile_int_literal(10).unwrap();
let new = gen.compile_int_literal(20).unwrap();
let mut acg = AtomicCodeGen::new(&mut gen);
let result = acg.codegen_atomic_cmp_xchg(
alloca,
cmp,
new,
MemOrdering::SequentiallyConsistent,
MemOrdering::Acquire,
);
gen.current_function = None;
}
#[test]
fn test_atomic_fence() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("fence_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let mut acg = AtomicCodeGen::new(&mut gen);
let result = acg.codegen_fence(MemOrdering::SequentiallyConsistent);
gen.current_function = None;
}
#[test]
fn test_atomic_rmw_operations() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("rmw_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let int_ty = Type::i32();
let alloca = gen.builder.create_alloca(int_ty);
let value = gen.compile_int_literal(1).unwrap();
let mut acg = AtomicCodeGen::new(&mut gen);
let _add = acg.codegen_atomic_rmw(
AtomicRMWOp::Add,
alloca.clone(),
value.clone(),
MemOrdering::SequentiallyConsistent,
);
let _sub = acg.codegen_atomic_rmw(
AtomicRMWOp::Sub,
alloca.clone(),
value.clone(),
MemOrdering::AcquireRelease,
);
let _and = acg.codegen_atomic_rmw(
AtomicRMWOp::And,
alloca.clone(),
value.clone(),
MemOrdering::Monotonic,
);
let _or = acg.codegen_atomic_rmw(
AtomicRMWOp::Or,
alloca.clone(),
value.clone(),
MemOrdering::Release,
);
let _xor = acg.codegen_atomic_rmw(AtomicRMWOp::Xor, alloca, value, MemOrdering::Acquire);
gen.current_function = None;
}
#[test]
fn test_mem_ordering_from_int() {
assert_eq!(MemOrdering::from_int(0), MemOrdering::NotAtomic);
assert_eq!(MemOrdering::from_int(3), MemOrdering::Acquire);
assert_eq!(MemOrdering::from_int(4), MemOrdering::Release);
assert_eq!(
MemOrdering::from_int(6),
MemOrdering::SequentiallyConsistent
);
}
#[test]
fn test_atomic_rmw_op_as_str() {
assert_eq!(AtomicRMWOp::Add.as_str(), "add");
assert_eq!(AtomicRMWOp::Xchg.as_str(), "xchg");
assert_eq!(AtomicRMWOp::FAdd.as_str(), "fadd");
}
#[test]
fn test_valid_atomic_orderings() {
assert!(AtomicCodeGen::is_valid_load_ordering(MemOrdering::Acquire));
assert!(!AtomicCodeGen::is_valid_load_ordering(MemOrdering::Release));
assert!(AtomicCodeGen::is_valid_store_ordering(MemOrdering::Release));
assert!(!AtomicCodeGen::is_valid_store_ordering(
MemOrdering::Acquire
));
}
#[test]
fn test_abi_classify_int_x86_64() {
let int_ty = Type::i32();
let classes = ABIInfo::classify_x86_64(&int_ty);
assert_eq!(classes[0], X86_64Class::Integer);
assert_eq!(classes[1], X86_64Class::NoClass);
}
#[test]
fn test_abi_classify_double_x86_64() {
let double_ty = Type::double();
let classes = ABIInfo::classify_x86_64(&double_ty);
assert_eq!(classes[0], X86_64Class::SSE);
assert_eq!(classes[1], X86_64Class::NoClass);
}
#[test]
fn test_abi_classify_pointer_x86_64() {
let ptr_ty = Type::pointer(Type::i32().id);
let classes = ABIInfo::classify_x86_64(&ptr_ty);
assert_eq!(classes[0], X86_64Class::Integer);
}
#[test]
fn test_abi_classify_void_x86_64() {
let void_ty = Type::void();
let classes = ABIInfo::classify_x86_64(&void_ty);
assert_eq!(classes[0], X86_64Class::NoClass);
}
#[test]
fn test_abi_classify_large_struct() {
let fields = vec![Type::i64().id; 4];
let struct_ty = Type::struct_named_with("BigStruct", &fields, false);
let classes = ABIInfo::classify_x86_64(&struct_ty);
assert_eq!(classes[0], X86_64Class::Memory);
}
#[test]
fn test_abi_merge_classes() {
assert_eq!(
ABIInfo::merge_classes(X86_64Class::Integer, X86_64Class::NoClass),
X86_64Class::Integer
);
assert_eq!(
ABIInfo::merge_classes(X86_64Class::SSE, X86_64Class::SSEUp),
X86_64Class::SSE
);
assert_eq!(
ABIInfo::merge_classes(X86_64Class::Integer, X86_64Class::SSE),
X86_64Class::Integer
);
assert_eq!(
ABIInfo::merge_classes(X86_64Class::Memory, X86_64Class::Integer),
X86_64Class::Memory
);
}
#[test]
fn test_abi_num_gprs() {
let classes = [X86_64Class::Integer, X86_64Class::Integer];
assert_eq!(ABIInfo::num_gprs(&classes), 2);
let classes = [X86_64Class::Integer, X86_64Class::NoClass];
assert_eq!(ABIInfo::num_gprs(&classes), 1);
let classes = [X86_64Class::SSE, X86_64Class::SSE];
assert_eq!(ABIInfo::num_gprs(&classes), 0);
}
#[test]
fn test_abi_num_sse_regs() {
let classes = [X86_64Class::SSE, X86_64Class::SSE];
assert_eq!(ABIInfo::num_sse_regs(&classes), 2);
}
#[test]
fn test_abi_classify_aarch64_int() {
let int_ty = Type::i32();
let class = ABIInfo::classify_aarch64(&int_ty);
assert_eq!(class, ARMClass::Core);
}
#[test]
fn test_abi_classify_aarch64_float() {
let float_ty = Type::float();
let class = ABIInfo::classify_aarch64(&float_ty);
assert_eq!(class, ARMClass::VFP);
}
#[test]
fn test_abi_hfa_detection() {
let fields = vec![Type::float().id, Type::float().id, Type::float().id];
let struct_ty = Type::struct_named_with("Vec3", &fields, false);
let result = ABIInfo::is_hfa(&struct_ty);
assert!(result.is_ok());
let (hfa_ty, count) = result.unwrap();
assert!(hfa_ty.is_float());
assert_eq!(count, 3);
}
#[test]
fn test_abi_non_hfa() {
let fields = vec![Type::i32().id, Type::float().id];
let struct_ty = Type::struct_named_with("Mixed", &fields, false);
let result = ABIInfo::is_hfa(&struct_ty);
assert!(result.is_err());
}
#[test]
fn test_struct_layout_simple() {
let fields = vec![Type::i32().id, Type::i32().id];
let struct_ty = Type::struct_named_with("Point", &fields, false);
let layout = StructLayout::compute_layout(
&struct_ty,
&["x".to_string(), "y".to_string()],
false,
false,
);
assert_eq!(layout.fields.len(), 2);
assert_eq!(layout.fields[0].offset_bytes, 0);
assert_eq!(layout.fields[1].offset_bytes, 4);
assert!(!layout.is_packed);
assert!(!layout.is_union);
}
#[test]
fn test_struct_layout_packed() {
let fields = vec![Type::i64().id, Type::i8().id, Type::i32().id];
let struct_ty = Type::struct_named_with("Packed", &fields, false);
let layout = StructLayout::compute_layout(
&struct_ty,
&["a".to_string(), "b".to_string(), "c".to_string()],
true,
false,
);
assert!(layout.is_packed);
assert_eq!(layout.fields[2].offset_bytes, 9);
}
#[test]
fn test_struct_layout_union() {
let fields = vec![Type::i64().id, Type::f64().id];
let struct_ty = Type::struct_named_with("Union", &fields, true);
let layout = StructLayout::compute_layout(
&struct_ty,
&["i".to_string(), "f".to_string()],
false,
true,
);
assert!(layout.is_union);
assert_eq!(layout.fields[0].offset_bytes, 0);
assert_eq!(layout.fields[1].offset_bytes, 0);
}
#[test]
fn test_struct_field_offset_lookup() {
let fields = vec![Type::i32().id, Type::i64().id, Type::i8().id];
let struct_ty = Type::struct_named_with("Lookup", &fields, false);
let layout = StructLayout::compute_layout(
&struct_ty,
&["a".to_string(), "b".to_string(), "c".to_string()],
false,
false,
);
assert_eq!(StructLayout::get_field_offset(&layout, "a"), Some(0));
assert_eq!(StructLayout::get_field_offset(&layout, "b"), Some(8));
assert_eq!(StructLayout::get_field_offset(&layout, "nonexistent"), None);
}
#[test]
fn test_struct_inter_field_padding() {
let fields = vec![Type::i8().id, Type::i32().id];
let struct_ty = Type::struct_named_with("Padded", &fields, false);
let layout = StructLayout::compute_layout(
&struct_ty,
&["a".to_string(), "b".to_string()],
false,
false,
);
let padding = StructLayout::inter_field_padding(&layout, 1);
assert_eq!(padding, Some(3)); }
#[test]
fn test_struct_alignment() {
let fields = vec![Type::i64().id, Type::i8().id];
let struct_ty = Type::struct_named_with("Aligned", &fields, false);
let layout = StructLayout::compute_layout(
&struct_ty,
&["a".to_string(), "b".to_string()],
false,
false,
);
assert_eq!(layout.total_size_bytes, 16);
assert!(layout.has_tail_padding);
}
#[test]
fn test_exception_personality_functions() {
assert_eq!(
ExceptionCodeGen::personality_for_language(true, false),
"__gxx_personality_v0"
);
assert_eq!(
ExceptionCodeGen::personality_for_language(false, false),
"__gcc_personality_v0"
);
assert_eq!(
ExceptionCodeGen::personality_for_language(true, true),
"__C_specific_handler"
);
}
#[test]
fn test_exception_landing_pad() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("eh_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let mut ecg = ExceptionCodeGen::new(&mut gen);
let result = ecg.codegen_landing_pad(&Type::i32(), 1, None, true);
gen.current_function = None;
}
#[test]
fn test_exception_resume() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("resume_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let exc_val = Value::new(Type::i32().id);
let mut ecg = ExceptionCodeGen::new(&mut gen);
ecg.codegen_resume(exc_val);
gen.current_function = None;
}
#[test]
fn test_linkage_map_external_definition() {
let linkage = LinkageCodeGen::map_linkage(&Linkage::External, true);
assert_eq!(linkage, LLVMLinkage::External);
}
#[test]
fn test_linkage_map_internal() {
let linkage = LinkageCodeGen::map_linkage(&Linkage::Internal, true);
assert_eq!(linkage, LLVMLinkage::Internal);
}
#[test]
fn test_linkage_should_use_comdat() {
assert!(LinkageCodeGen::should_use_comdat(&LLVMLinkage::LinkOnceAny));
assert!(LinkageCodeGen::should_use_comdat(&LLVMLinkage::WeakAny));
assert!(!LinkageCodeGen::should_use_comdat(&LLVMLinkage::External));
assert!(!LinkageCodeGen::should_use_comdat(&LLVMLinkage::Internal));
}
#[test]
fn test_linkage_section_prefixes() {
assert_eq!(LinkageCodeGen::section_prefix("init"), ".init_array");
assert_eq!(LinkageCodeGen::section_prefix(".text"), ".text");
assert_eq!(LinkageCodeGen::section_prefix(".rodata"), ".rodata");
assert_eq!(LinkageCodeGen::section_prefix("unknown"), "");
}
#[test]
fn test_llvm_linkage_as_str() {
assert_eq!(LLVMLinkage::External.as_str(), "external");
assert_eq!(LLVMLinkage::Internal.as_str(), "internal");
assert_eq!(LLVMLinkage::Private.as_str(), "private");
assert_eq!(LLVMLinkage::Common.as_str(), "common");
}
#[test]
fn test_visibility_as_str() {
assert_eq!(LLVMVisibility::Default.as_str(), "default");
assert_eq!(LLVMVisibility::Hidden.as_str(), "hidden");
assert_eq!(LLVMVisibility::Protected.as_str(), "protected");
}
#[test]
fn test_type_conversion_float_rank() {
assert!(
TypeConversion::float_rank(&TypeNode::Double)
> TypeConversion::float_rank(&TypeNode::Float)
);
assert!(
TypeConversion::float_rank(&TypeNode::LongDouble)
> TypeConversion::float_rank(&TypeNode::Double)
);
assert_eq!(TypeConversion::float_rank(&TypeNode::Int), 0);
}
#[test]
fn test_type_conversion_usual_arithmetic_int_long() {
let result =
TypeConversion::usual_arithmetic_conversion_type(&TypeNode::Int, &TypeNode::Long);
assert_eq!(result, TypeNode::Long);
}
#[test]
fn test_type_conversion_usual_arithmetic_unsigned_int() {
let result =
TypeConversion::usual_arithmetic_conversion_type(&TypeNode::Int, &TypeNode::UInt);
assert_eq!(result, TypeNode::UInt);
}
#[test]
fn test_conversion_kind_display() {
let kinds = [
ConversionKind::None,
ConversionKind::Sext,
ConversionKind::Zext,
ConversionKind::Trunc,
ConversionKind::FPTrunc,
ConversionKind::FPExt,
ConversionKind::SIToFP,
ConversionKind::UIToFP,
ConversionKind::FPToSI,
ConversionKind::FPToUI,
ConversionKind::PtrToInt,
ConversionKind::IntToPtr,
ConversionKind::BitCast,
];
assert_eq!(kinds.len(), 13);
}
#[test]
fn test_compile_ident_function_value() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("my_func");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.functions.insert("my_func".to_string(), func_val);
let result = gen.compile_ident("my_func");
assert!(result.is_ok());
}
#[test]
fn test_compile_ident_undefined() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let result = gen.compile_ident("nonexistent_var");
assert!(result.is_err());
}
#[test]
fn test_array_to_pointer_decay() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("decay_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let arr_ty = Type::array_with(Type::i32().id, 5);
let arr_val = Value::new(arr_ty.id);
let result = gen.array_to_pointer_decay(arr_val);
assert!(result.is_ok());
let ptr_ty = Type::from_id(result.unwrap().borrow().ty);
assert!(ptr_ty.is_pointer());
gen.current_function = None;
}
#[test]
fn test_compile_pre_inc() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("preinc_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let int_ty = Type::i32();
let alloca = gen.builder.create_alloca(int_ty.clone());
gen.builder
.create_store(gen.builder.get_int32(0), alloca.clone());
gen.named_values.insert("x".to_string(), alloca.clone());
let result = gen.compile_expr(&Expr::PreInc(Box::new(make_ident("x"))));
assert!(result.is_ok());
gen.current_function = None;
}
#[test]
fn test_compile_post_inc() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("postinc_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let int_ty = Type::i32();
let alloca = gen.builder.create_alloca(int_ty.clone());
gen.builder
.create_store(gen.builder.get_int32(0), alloca.clone());
gen.named_values.insert("y".to_string(), alloca.clone());
let result = gen.compile_expr(&Expr::PostDec(Box::new(make_ident("y"))));
assert!(result.is_ok());
gen.current_function = None;
}
#[test]
fn test_expr_codegen_logical_and() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("land_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let mut ecg = ExprCodeGen::new(&mut gen);
let lhs = gen.compile_int_literal(1).unwrap();
let rhs = gen.compile_int_literal(0).unwrap();
let result = ecg.codegen_logical_and(lhs, rhs);
gen.current_function = None;
}
#[test]
fn test_expr_codegen_logical_or() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("lor_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let mut ecg = ExprCodeGen::new(&mut gen);
let lhs = gen.compile_int_literal(0).unwrap();
let rhs = gen.compile_int_literal(1).unwrap();
let result = ecg.codegen_logical_or(lhs, rhs);
gen.current_function = None;
}
#[test]
fn test_compile_assign_simple() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("assign_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let int_ty = Type::i32();
let alloca = gen.builder.create_alloca(int_ty);
gen.named_values.insert("z".to_string(), alloca);
let result = gen.compile_assign(BinaryOp::Assign, &make_ident("z"), &make_int_literal(99));
assert!(result.is_ok());
gen.current_function = None;
}
#[test]
fn test_compile_compound_assign_add() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("add_assign_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let int_ty = Type::i32();
let alloca = gen.builder.create_alloca(int_ty);
gen.builder
.create_store(gen.builder.get_int32(5), alloca.clone());
gen.named_values.insert("w".to_string(), alloca);
let mut ecg = ExprCodeGen::new(&mut gen);
let rhs = gen.compile_int_literal(3).unwrap();
let result = ecg.codegen_compound_assign(BinaryOp::AddAssign, alloca.clone(), rhs);
gen.current_function = None;
}
#[test]
fn test_debug_create_basic_type() {
let basic = DebugInfo::create_basic_type("int", 32, DebugInfo::DW_ATE_signed);
assert_eq!(basic.borrow().subclass, SubclassKind::MetadataAsValue);
}
#[test]
fn test_debug_create_derived_type() {
let base = DebugInfo::create_basic_type("int", 32, 5);
let ptr = DebugInfo::create_derived_type(
DebugInfo::DW_TAG_pointer_type,
"int*",
&base,
64,
64,
0,
);
assert_eq!(ptr.borrow().subclass, SubclassKind::MetadataAsValue);
}
#[test]
fn test_debug_create_composite_type() {
let file = DebugInfo::create_file("test.c", "/tmp");
let field = DebugInfo::create_basic_type("int", 32, 5);
let struct_di = DebugInfo::create_composite_type(
DebugInfo::DW_TAG_structure_type,
"MyStruct",
&file,
10,
64,
64,
&[field],
);
assert_eq!(struct_di.borrow().subclass, SubclassKind::MetadataAsValue);
}
#[test]
fn test_debug_create_subrange() {
let subrange = DebugInfo::create_subrange(0, 10);
assert_eq!(subrange.borrow().subclass, SubclassKind::MetadataAsValue);
}
#[test]
fn test_debug_create_enumerator() {
let enumerator = DebugInfo::create_enumerator("RED", 0);
assert_eq!(enumerator.borrow().subclass, SubclassKind::MetadataAsValue);
}
#[test]
fn test_debug_create_global_variable() {
let file = DebugInfo::create_file("test.c", "/tmp");
let ty = DebugInfo::create_basic_type("int", 32, 5);
let gv = DebugInfo::create_global_variable("g_x", "g_x", &file, 42, &ty, false, true);
assert_eq!(gv.borrow().subclass, SubclassKind::MetadataAsValue);
}
#[test]
fn test_debug_create_module() {
let file = DebugInfo::create_file("mod.c", "/src");
let module_di = DebugInfo::create_module("MyModule", &file, 1);
assert_eq!(module_di.borrow().subclass, SubclassKind::MetadataAsValue);
}
#[test]
fn test_debug_set_location() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let file = DebugInfo::create_file("main.c", "/src");
let void_ty = DebugInfo::create_basic_type("void", 0, 0);
let scope = DebugInfo::create_subprogram(
"main", "main", &file, 1, &void_ty, false, true, 1, 0, false,
);
DebugInfo::set_debug_location(&mut gen, 15, 3, &scope);
assert!(gen.current_debug_loc.is_some());
let (line, col, _) = gen.current_debug_loc.unwrap();
assert_eq!(line, 15);
assert_eq!(col, 3);
}
#[test]
fn test_debug_finalize() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let cu = DebugInfo::create_compile_unit(
DebugInfo::DW_LANG_C11,
"test.c",
"/src",
"clang",
false,
"",
0,
);
let file = DebugInfo::create_file("test.c", "/src");
let void_ty = DebugInfo::create_basic_type("void", 0, 0);
let sp =
DebugInfo::create_subprogram("f", "f", &file, 1, &void_ty, false, true, 1, 0, false);
DebugInfo::finalize(&mut gen, &cu, &[sp], &[]);
assert!(gen.debug_compile_unit.is_some());
}
#[test]
fn test_aggregate_extract_value() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("extract_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let struct_ty = Type::struct_literal_with(&[Type::i32().id, Type::i32().id], false);
let agg_val = Value::new(struct_ty.id);
let mut agg = AggregateCodeGen::new(&mut gen);
let result = agg.codegen_extract_value(agg_val, &[0]);
gen.current_function = None;
}
#[test]
fn test_aggregate_insert_value() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("insert_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let struct_ty = Type::struct_literal_with(&[Type::i32().id, Type::i32().id], false);
let agg_val = Value::new(struct_ty.id);
let field_val = gen.compile_int_literal(42).unwrap();
let mut agg = AggregateCodeGen::new(&mut gen);
let result = agg.codegen_insert_value(agg_val, field_val, &[1]);
gen.current_function = None;
}
#[test]
fn test_ir_generator_errors_initially_empty() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let gen = make_gen(&ctx, &mut module);
assert!(gen.errors.is_empty());
assert!(gen.warnings.is_empty());
}
#[test]
fn test_ir_generator_named_values_initially_empty() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let gen = make_gen(&ctx, &mut module);
assert!(gen.named_values.is_empty());
assert!(!gen.generate_debug_info);
}
#[test]
fn test_ir_generator_string_pool_initially_empty() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let gen = make_gen(&ctx, &mut module);
assert!(gen.string_pool.is_empty());
}
#[test]
fn test_ir_generator_optimization_level_default() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let gen = make_gen(&ctx, &mut module);
assert_eq!(gen.optimization_level, 0);
}
#[test]
fn test_expr_codegen_sizeof_type() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let result = gen.compile_sizeof_type(&TypeNode::Int);
assert!(result.is_ok());
let val = result.unwrap();
assert!(val.borrow().is_constant);
}
#[test]
fn test_expr_codegen_alignof_type() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let result = gen.compile_alignof_type(&TypeNode::Double);
assert!(result.is_ok());
let val = result.unwrap();
assert!(val.borrow().is_constant);
}
#[test]
fn test_decl_codegen_function_definition_with_body() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let return_ty = Type::i32();
let body = CompoundStmt {
stmts: vec![Stmt::Return(Some(make_int_literal(0)))],
};
let mut dc = DeclCodeGen::new(&mut gen);
let result =
dc.codegen_function_definition("body_func", &return_ty, &[], &[], &body, false);
assert!(result.is_ok());
assert!(module.has_function("body_func"));
}
#[test]
fn test_decl_codegen_static_local() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("static_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let int_ty = Type::i32();
let mut dc = DeclCodeGen::new(&mut gen);
let init_expr = make_int_literal(0);
let result = dc.codegen_static_local("counter", &int_ty, Some(&init_expr));
assert!(result.is_ok());
gen.current_function = None;
}
#[test]
fn test_stmt_codegen_loop_break_continue_targets() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let break_bb = gen.builder.create_basic_block("break_target");
let continue_bb = gen.builder.create_basic_block("continue_target");
let mut sc = StmtCodeGen::new(&mut gen);
sc.begin_loop(break_bb.clone(), continue_bb.clone());
let bt = sc.get_break_target();
let ct = sc.get_continue_target();
assert!(bt.is_some());
assert!(ct.is_some());
sc.end_loop();
assert!(sc.get_break_target().is_none());
assert!(sc.get_continue_target().is_none());
}
#[test]
fn test_stmt_codegen_switch_begin_end() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let merge_bb = gen.builder.create_basic_block("switch_merge");
let mut sc = StmtCodeGen::new(&mut gen);
sc.begin_switch(merge_bb);
sc.end_switch();
assert!(gen.switch_merge_block.is_none());
assert!(gen.switch_cases.is_empty());
assert!(gen.default_case_block.is_none());
}
#[test]
fn test_parse_asm_constraint_register() {
let constraints = AsmConstraintKind::parse("r");
assert_eq!(constraints.len(), 1);
assert_eq!(constraints[0], AsmConstraintKind::Register);
}
#[test]
fn test_parse_asm_constraint_memory() {
let constraints = AsmConstraintKind::parse("m");
assert_eq!(constraints[0], AsmConstraintKind::Memory);
}
#[test]
fn test_parse_asm_constraint_multiple() {
let constraints = AsmConstraintKind::parse("=&r");
assert_eq!(constraints.len(), 3);
assert_eq!(constraints[0], AsmConstraintKind::WriteOnly);
assert_eq!(constraints[1], AsmConstraintKind::EarlyClobber);
assert_eq!(constraints[2], AsmConstraintKind::Register);
}
#[test]
fn test_asm_constraint_is_output() {
assert!(AsmConstraintKind::WriteOnly.is_output());
assert!(AsmConstraintKind::ReadWrite.is_output());
assert!(!AsmConstraintKind::Register.is_output());
}
#[test]
fn test_asm_constraint_is_input() {
assert!(!AsmConstraintKind::WriteOnly.is_input());
assert!(AsmConstraintKind::Register.is_input());
}
#[test]
fn test_asm_parse_clobbers() {
let clobbers = InlineASMCodeGen::parse_clobbers("rax, rbx, memory, cc");
assert_eq!(clobbers.len(), 4);
assert_eq!(clobbers[0], "rax");
assert_eq!(clobbers[2], "memory");
}
#[test]
fn test_asm_is_memory_clobber() {
let clobbers = vec!["rax".to_string(), "memory".to_string()];
assert!(InlineASMCodeGen::is_memory_clobber(&clobbers));
let clobbers2 = vec!["rax".to_string()];
assert!(!InlineASMCodeGen::is_memory_clobber(&clobbers2));
}
#[test]
fn test_asm_is_cc_clobber() {
let clobbers = vec!["rax".to_string(), "cc".to_string()];
assert!(InlineASMCodeGen::is_cc_clobber(&clobbers));
}
#[test]
fn test_asm_valid_clobber() {
assert!(InlineASMCodeGen::is_valid_clobber("rax"));
assert!(InlineASMCodeGen::is_valid_clobber("memory"));
assert!(InlineASMCodeGen::is_valid_clobber("xmm0"));
assert!(!InlineASMCodeGen::is_valid_clobber("nonexistent"));
}
#[test]
fn test_asm_encode_constraints() {
let out = vec!["r".to_string()];
let inp = vec!["r".to_string()];
let clobbers = vec!["memory".to_string()];
let encoded = InlineASMCodeGen::encode_constraints(&out, &inp, &clobbers);
assert!(encoded.contains("=r"));
assert!(encoded.contains("~{memory}"));
}
#[test]
fn test_asm_x86_clobber_count() {
let regs = InlineASMCodeGen::x86_clobber_registers();
assert!(regs.len() > 30);
}
#[test]
fn test_inline_asm_codegen() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("asm_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let desc = InlineAsmDesc {
asm_string: "nop".to_string(),
constraints: "".to_string(),
has_side_effects: false,
is_align_stack: false,
is_intel_dialect: false,
can_throw: false,
};
let mut acg = InlineASMCodeGen::new(&mut gen);
let result = acg.codegen_inline_asm(&desc, &[], &[], &[]);
gen.current_function = None;
}
#[test]
fn test_verifier_new() {
let result = VerificationResult::new();
assert!(result.is_valid());
assert!(result.errors.is_empty());
}
#[test]
fn test_verifier_add_error() {
let mut result = VerificationResult::new();
result.add_error("test error".to_string());
assert!(!result.is_valid());
assert_eq!(result.errors.len(), 1);
}
#[test]
fn test_verifier_add_warning() {
let mut result = VerificationResult::new();
result.add_warning("test warning".to_string());
assert!(result.is_valid());
assert_eq!(result.warnings.len(), 1);
}
#[test]
fn test_verifier_default() {
let result = VerificationResult::default();
assert!(result.is_valid());
}
#[test]
fn test_verifier_quick_check_empty() {
let ctx = LLVMContext::new();
let module = ctx.create_module("test");
assert!(IRVerifier::quick_check(&module));
}
#[test]
fn test_verifier_basic_block_no_terminator() {
let bb = Value::new(Type::label().id);
let result = IRVerifier::verify_basic_block(&bb);
assert!(result.is_valid()); assert!(!result.warnings.is_empty());
}
#[test]
fn test_verifier_module() {
let ctx = LLVMContext::new();
let module = ctx.create_module("test");
let result = IRVerifier::verify_module(&module);
assert!(result.is_valid());
}
#[test]
fn test_verifier_module_with_warning() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module(""); let result = IRVerifier::verify_module(&module);
assert!(!result.warnings.is_empty() || result.is_valid());
}
#[test]
fn test_linkage_canonicalize_section() {
assert_eq!(LinkageCodeGen::canonicalize_section("text"), ".text");
assert_eq!(LinkageCodeGen::canonicalize_section(".data"), ".data");
}
#[test]
fn test_linkage_default_section_internal() {
assert_eq!(
LinkageCodeGen::default_section_for_linkage(&LLVMLinkage::Internal),
".text"
);
}
#[test]
fn test_linkage_default_section_common() {
assert_eq!(
LinkageCodeGen::default_section_for_linkage(&LLVMLinkage::Common),
".bss"
);
}
#[test]
fn test_linkage_default_section_external() {
assert_eq!(
LinkageCodeGen::default_section_for_linkage(&LLVMLinkage::External),
""
);
}
#[test]
fn test_linkage_map_visibility() {
assert_eq!(
LinkageCodeGen::map_visibility(true, false),
LLVMVisibility::Hidden
);
assert_eq!(
LinkageCodeGen::map_visibility(false, true),
LLVMVisibility::Protected
);
assert_eq!(
LinkageCodeGen::map_visibility(false, false),
LLVMVisibility::Default
);
}
#[test]
fn test_builtin_object_size() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut bc = BuiltinCodeGen::new(&mut gen);
let ptr = gen.compile_int_literal(0x1000).unwrap();
let type_val = gen.compile_int_literal(0).unwrap();
let result = bc.emit_object_size(&[ptr, type_val]);
assert!(result.is_ok());
}
#[test]
fn test_builtin_frame_address() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut bc = BuiltinCodeGen::new(&mut gen);
let level = gen.compile_int_literal(0).unwrap();
let result = bc.emit_frame_address(&[level]);
assert!(result.is_ok());
}
#[test]
fn test_builtin_all_recognized() {
let builtins = [
"__builtin_alloca",
"__builtin_expect",
"__builtin_prefetch",
"__builtin_assume",
"__builtin_unreachable",
"__builtin_trap",
"__builtin_debugtrap",
"__builtin_memcpy",
"__builtin_memmove",
"__builtin_memset",
"__builtin_bswap16",
"__builtin_bswap32",
"__builtin_bswap64",
"__builtin_clz",
"__builtin_clzll",
"__builtin_ctz",
"__builtin_ctzll",
"__builtin_popcount",
"__builtin_popcountll",
"__builtin_sqrt",
"__builtin_sqrtf",
"__builtin_sqrtl",
"__builtin_fma",
"__builtin_fmaf",
"__builtin_fmal",
"__builtin_constant_p",
"__builtin_frame_address",
"__builtin_return_address",
"__builtin_object_size",
];
for &name in &builtins {
let kind = IRGenerator::<'_>::get_builtin_kind(name);
assert_ne!(kind, BuiltinKind::Unknown, "{} should be recognized", name);
}
}
#[test]
fn test_builtin_return_type_void() {
let ty = BuiltinCodeGen::builtin_return_type(&BuiltinKind::Memcpy);
assert!(ty.is_void());
}
#[test]
fn test_builtin_return_type_ptr() {
let ty = BuiltinCodeGen::builtin_return_type(&BuiltinKind::FrameAddress);
assert!(ty.is_pointer());
}
#[test]
fn test_aggregate_union_access() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("union_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let union_ty = Type::struct_named_with("MyUnion", &[Type::i64().id, Type::f64().id], true);
let ptr = gen.builder.create_alloca(union_ty.clone());
let mut agg = AggregateCodeGen::new(&mut gen);
let field_ptr = agg.codegen_union_access(ptr, &Type::f64());
gen.current_function = None;
assert!(Type::from_id(field_ptr.borrow().ty).is_pointer());
}
#[test]
fn test_aggregate_union_init() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("union_init_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let union_ty = Type::struct_named_with("U", &[Type::i32().id], true);
let init_val = gen.compile_int_literal(42).unwrap();
let mut agg = AggregateCodeGen::new(&mut gen);
let result = agg.codegen_union_init(&union_ty, init_val);
gen.current_function = None;
}
#[test]
fn test_aggregate_array_init() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("arr_init_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let elem_ty = Type::i32();
let arr_ty = Type::array_with(elem_ty.id, 3);
let elem1 = gen.compile_int_literal(1).unwrap();
let elem2 = gen.compile_int_literal(2).unwrap();
let elem3 = gen.compile_int_literal(3).unwrap();
let mut agg = AggregateCodeGen::new(&mut gen);
let result = agg.codegen_array_init(&arr_ty, &elem_ty, &[elem1, elem2, elem3]);
gen.current_function = None;
assert!(Type::from_id(result.borrow().ty).is_pointer());
}
#[test]
fn test_aggregate_struct_return() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("sret_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let struct_ty = Type::struct_literal_with(&[Type::i32().id, Type::i32().id], false);
let struct_val = Value::new(struct_ty.id);
let mut agg = AggregateCodeGen::new(&mut gen);
let result = agg.codegen_struct_return(struct_val, &struct_ty);
gen.current_function = None;
}
#[test]
fn test_expr_codegen_comparison_float() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("fcmp_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let mut ecg = ExprCodeGen::new(&mut gen);
let lhs = ecg.codegen_float_literal(1.0);
let rhs = ecg.codegen_float_literal(2.0);
let result = ecg.codegen_comparison(BinaryOp::Lt, lhs, rhs, true, false);
gen.current_function = None;
assert_eq!(Type::from_id(result.borrow().ty).size_in_bits().unwrap(), 1);
}
#[test]
fn test_expr_codegen_comparison_unsigned() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("ucmp_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let mut ecg = ExprCodeGen::new(&mut gen);
let lhs = ecg.codegen_integer_literal(100, 32);
let rhs = ecg.codegen_integer_literal(200, 32);
let result = ecg.codegen_comparison(BinaryOp::Gt, lhs, rhs, false, true);
gen.current_function = None;
}
#[test]
fn test_expr_codegen_arithmetic_binary() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("arith_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let mut ecg = ExprCodeGen::new(&mut gen);
let lhs = ecg.codegen_integer_literal(10, 32);
let rhs = ecg.codegen_integer_literal(5, 32);
let ty = Type::i32();
let _add = ecg.codegen_arithmetic_binary(BinaryOp::Add, lhs.clone(), rhs.clone(), &ty);
let _sub = ecg.codegen_arithmetic_binary(BinaryOp::Sub, lhs.clone(), rhs.clone(), &ty);
let _mul = ecg.codegen_arithmetic_binary(BinaryOp::Mul, lhs.clone(), rhs.clone(), &ty);
let _div = ecg.codegen_arithmetic_binary(BinaryOp::Div, lhs, rhs, &ty);
gen.current_function = None;
}
#[test]
fn test_expr_codegen_bitwise_binary() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("bitwise_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let mut ecg = ExprCodeGen::new(&mut gen);
let lhs = ecg.codegen_integer_literal(0xFF, 32);
let rhs = ecg.codegen_integer_literal(0x0F, 32);
let _and = ecg.codegen_bitwise_binary(BinaryOp::And, lhs.clone(), rhs.clone());
let _or = ecg.codegen_bitwise_binary(BinaryOp::Or, lhs.clone(), rhs.clone());
let _xor = ecg.codegen_bitwise_binary(BinaryOp::Xor, lhs.clone(), rhs.clone());
let _shl = ecg.codegen_bitwise_binary(BinaryOp::Shl, lhs, rhs);
gen.current_function = None;
}
#[test]
fn test_expr_codegen_unary_ops() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("unary_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let mut ecg = ExprCodeGen::new(&mut gen);
let val = ecg.codegen_integer_literal(42, 32);
let _neg = ecg.codegen_unary_minus(val.clone());
let _not = ecg.codegen_logical_not(val.clone());
let _bitnot = ecg.codegen_bitwise_not(val);
gen.current_function = None;
}
#[test]
fn test_compile_break_outside_loop() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let result = gen.compile_break();
assert!(result.is_err());
}
#[test]
fn test_compile_continue_outside_loop() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let result = gen.compile_continue();
assert!(result.is_err());
}
#[test]
fn test_compile_string_literal_caching() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let result1 = gen.compile_string_literal("hello");
let result2 = gen.compile_string_literal("hello");
assert!(result1.is_ok());
assert!(result2.is_ok());
assert_eq!(result1.unwrap().borrow().vid, result2.unwrap().borrow().vid);
}
#[test]
fn test_decl_codegen_typedef() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut dc = DeclCodeGen::new(&mut gen);
let int_ty = Type::i32();
let result = dc.codegen_typedef("my_int", &int_ty);
assert!(result.is_integer());
}
#[test]
fn test_decl_codegen_enum() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut dc = DeclCodeGen::new(&mut gen);
let variants = vec![
("A".to_string(), 0i64),
("B".to_string(), 1i64),
("C".to_string(), 2i64),
];
let result = dc.codegen_enum_declaration("Color", &variants);
assert!(result.is_integer());
}
#[test]
fn test_decl_codegen_struct() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut dc = DeclCodeGen::new(&mut gen);
let types = vec![Type::i32(), Type::i32()];
let names = vec!["x".to_string(), "y".to_string()];
let result = dc.codegen_struct_declaration("Point", &types, &names, false, false);
assert!(result.is_struct());
}
#[test]
fn test_convert_struct_type_cached() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let sd = StructDecl {
name: "Cached".to_string(),
fields: vec![FieldDecl::new("a", TypeNode::Int)],
is_union: false,
};
let ty1 = gen.convert_struct_type(&sd);
let ty2 = gen.convert_struct_type(&sd);
assert_eq!(ty1.id, ty2.id);
}
#[test]
fn test_decl_codegen_zero_initializer() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut dc = DeclCodeGen::new(&mut gen);
let int_ty = Type::i32();
let zero = dc.codegen_zero_initializer(&int_ty);
assert!(zero.borrow().is_constant);
assert_eq!(zero.borrow().subclass_data, Some(0));
}
#[test]
fn test_compile_translation_unit_with_decls() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut tu = TranslationUnit::new("multi.c");
let errors = gen.compile_translation_unit(&tu);
assert_eq!(errors, 0);
}
#[test]
fn test_module_has_correct_triple() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
gen.target_triple = "aarch64-unknown-linux-gnu".to_string();
let tu = TranslationUnit::new("arm.c");
gen.compile_translation_unit(&tu);
assert_eq!(module.get_target_triple(), "aarch64-unknown-linux-gnu");
}
#[test]
fn test_compile_global_extern() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let vd = VarDecl {
name: "ext_global".to_string(),
ty: TypeNode::Int,
init: None,
linkage: Linkage::External,
is_global: true,
is_extern: true,
is_static: false,
};
let result = gen.compile_global(&vd);
assert!(result.is_ok());
assert!(module.get_global_variable("ext_global").is_some());
}
#[test]
fn test_compile_global_with_init() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let vd = VarDecl {
name: "init_global".to_string(),
ty: TypeNode::Int,
init: Some(make_int_literal(42)),
linkage: Linkage::External,
is_global: true,
is_extern: false,
is_static: false,
};
let result = gen.compile_global(&vd);
assert!(result.is_ok());
}
#[test]
fn test_compile_static_local_init() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let vd = VarDecl {
name: "count".to_string(),
ty: TypeNode::Int,
init: Some(make_int_literal(0)),
linkage: Linkage::Internal,
is_global: false,
is_extern: false,
is_static: true,
};
let result = gen.compile_static_local(&vd);
assert!(result.is_ok());
}
#[test]
fn test_convert_type_long_on_64bit() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
gen.target_triple = "x86_64-unknown-linux-gnu".to_string();
let long_ty = gen.convert_type(&TypeNode::Long);
assert_eq!(long_ty.size_in_bits().unwrap(), 64);
}
#[test]
fn test_convert_type_long_on_32bit() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
gen.target_triple = "i386-unknown-linux-gnu".to_string();
let long_ty = gen.convert_type(&TypeNode::Long);
assert_eq!(long_ty.size_in_bits().unwrap(), 32);
}
#[test]
fn test_convert_enum_type_cached() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let enum_ty = gen.convert_type(&TypeNode::Enum("Color".to_string(), vec![]));
assert!(enum_ty.is_integer());
}
#[test]
fn test_convert_typedef_type_cached() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let typedef_ty = gen.convert_type(&TypeNode::Typedef(
"size_t".to_string(),
Box::new(TypeNode::ULong),
));
assert!(typedef_ty.is_integer());
}
#[test]
fn test_convert_function_type_vararg() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = gen.convert_type(&TypeNode::Function(
Box::new(TypeNode::Void),
vec![TypeNode::Pointer(Box::new(TypeNode::Char))],
true,
));
assert!(func_ty.is_function());
}
#[test]
fn test_convert_complex_type() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let complex_ty = gen.convert_type(&TypeNode::Complex);
assert!(complex_ty.is_struct());
}
#[test]
fn test_type_cache_hit() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let tn = TypeNode::Int;
let ty1 = gen.convert_type(&tn);
let ty2 = gen.convert_type(&tn);
assert_eq!(ty1.id, ty2.id);
}
#[test]
fn test_abi_coerced_type_int() {
let int_ty = Type::i32();
let coerced = ABIInfo::get_coerced_type(&int_ty);
assert!(coerced.is_integer());
}
#[test]
fn test_abi_coerced_type_double() {
let double_ty = Type::double();
let coerced = ABIInfo::get_coerced_type(&double_ty);
assert!(coerced.is_floating_point());
}
#[test]
fn test_abi_is_memory_class() {
assert!(ABIInfo::is_memory_class(&[
X86_64Class::Memory,
X86_64Class::NoClass
]));
assert!(!ABIInfo::is_memory_class(&[
X86_64Class::Integer,
X86_64Class::SSE
]));
}
#[test]
fn test_block_has_terminator_empty() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("term_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
assert!(!gen.block_has_terminator());
gen.builder.create_ret_void();
assert!(gen.block_has_terminator());
gen.current_function = None;
}
#[test]
fn test_create_basic_block_named() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("bb_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let bb = gen.create_basic_block("my_block");
let bb_ref = bb.borrow();
assert!(bb_ref
.name
.as_ref()
.map_or(false, |n| n.contains("my_block")));
gen.current_function = None;
}
#[test]
fn test_get_variable_ptr_found() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let int_ty = Type::i32();
let alloca = gen.builder.create_alloca(int_ty);
gen.named_values.insert("var".to_string(), alloca.clone());
let ptr = gen.get_variable_ptr("var");
assert!(ptr.is_some());
}
#[test]
fn test_get_variable_ptr_not_found() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let gen = make_gen(&ctx, &mut module);
let ptr = gen.get_variable_ptr("nonexistent");
assert!(ptr.is_none());
}
#[test]
fn test_builtin_alloca_no_args_error() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut bc = BuiltinCodeGen::new(&mut gen);
let result = bc.emit_alloca(&[]);
assert!(result.is_err());
}
#[test]
fn test_builtin_expect_no_args_error() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut bc = BuiltinCodeGen::new(&mut gen);
let result = bc.emit_expect(&[]);
assert!(result.is_err());
}
#[test]
fn test_builtin_unknown_returns_error() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut bc = BuiltinCodeGen::new(&mut gen);
let result = bc.compile("__builtin_nonexistent", &[]);
assert!(result.is_err());
}
#[test]
fn test_full_module_pipeline() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("pipeline_test");
let mut gen = make_gen(&ctx, &mut module);
gen.target_triple = "x86_64-unknown-linux-gnu".to_string();
gen.data_layout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string();
gen.source_file = "pipeline.c".to_string();
let tu = TranslationUnit::new("pipeline.c");
let errors = gen.compile_translation_unit(&tu);
assert_eq!(errors, 0);
}
#[test]
fn test_compile_function_void_return_no_body() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let fd = FunctionDecl {
name: "decl_only".to_string(),
ret_ty: TypeNode::Void,
params: vec![],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
let result = gen.compile_function(&fd);
assert!(result.is_ok());
assert!(module.has_function("decl_only"));
}
#[test]
fn test_compile_function_with_block_terminator() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let body = CompoundStmt {
stmts: vec![Stmt::Return(None)],
};
let fd = FunctionDecl {
name: "void_with_return".to_string(),
ret_ty: TypeNode::Void,
params: vec![],
body: Some(body),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
let result = gen.compile_function(&fd);
assert!(result.is_ok());
}
#[test]
fn test_decl_pipeline_function_and_global() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut dc = DeclCodeGen::new(&mut gen);
let i32_ty = Type::i32();
dc.codegen_global_variable("g", &i32_ty, None, false)
.unwrap();
dc.codegen_function_prototype(
"add",
&i32_ty,
&[i32_ty.clone(), i32_ty.clone()],
&["a".to_string(), "b".to_string()],
false,
)
.unwrap();
assert!(module.get_global_variable("g").is_some());
assert!(module.has_function("add"));
}
#[test]
fn test_decl_scalar_initializer() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut dc = DeclCodeGen::new(&mut gen);
let i64_ty = Type::i64();
let val = gen.compile_int_literal(42).unwrap();
let result = dc.codegen_scalar_initializer(&i64_ty, val);
assert!(result.borrow().is_constant);
}
#[test]
fn test_decl_aggregate_initializer() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut dc = DeclCodeGen::new(&mut gen);
let struct_ty = Type::struct_literal_with(&[Type::i32().id, Type::i32().id], false);
let elem1 = gen.compile_int_literal(10).unwrap();
let elem2 = gen.compile_int_literal(20).unwrap();
let result = dc.codegen_aggregate_initializer(&struct_ty, &[elem1, elem2]);
assert!(result.borrow().is_constant);
}
#[test]
fn test_decl_function_vararg() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let mut dc = DeclCodeGen::new(&mut gen);
let i32_ty = Type::i32();
let result = dc.codegen_function_prototype(
"printf",
&i32_ty,
&[Type::pointer(Type::i8().id)],
&["fmt".to_string()],
true,
);
assert!(result.is_ok());
let func_val = result.unwrap();
assert!(func_val.borrow().is_vararg);
}
#[test]
fn test_mem_ordering_all_values() {
let orderings = [
MemOrdering::NotAtomic,
MemOrdering::Unordered,
MemOrdering::Monotonic,
MemOrdering::Acquire,
MemOrdering::Release,
MemOrdering::AcquireRelease,
MemOrdering::SequentiallyConsistent,
];
for ord in &orderings {
let s = ord.as_str();
assert!(!s.is_empty());
let roundtrip = MemOrdering::from_int(*ord as u32);
assert_eq!(*ord, roundtrip);
}
}
#[test]
fn test_atomic_rmw_op_all_values() {
let ops = [
AtomicRMWOp::Xchg,
AtomicRMWOp::Add,
AtomicRMWOp::Sub,
AtomicRMWOp::And,
AtomicRMWOp::Nand,
AtomicRMWOp::Or,
AtomicRMWOp::Xor,
AtomicRMWOp::Max,
AtomicRMWOp::Min,
AtomicRMWOp::UMax,
AtomicRMWOp::UMin,
AtomicRMWOp::FAdd,
AtomicRMWOp::FSub,
AtomicRMWOp::FMax,
AtomicRMWOp::FMin,
];
for op in &ops {
let s = op.as_str();
assert!(!s.is_empty());
}
}
#[test]
fn test_cmpxchg_failure_ordering() {
assert_eq!(
AtomicCodeGen::get_cmpxchg_failure_ordering(MemOrdering::SequentiallyConsistent),
MemOrdering::SequentiallyConsistent
);
assert_eq!(
AtomicCodeGen::get_cmpxchg_failure_ordering(MemOrdering::AcquireRelease),
MemOrdering::Acquire
);
assert_eq!(
AtomicCodeGen::get_cmpxchg_failure_ordering(MemOrdering::Release),
MemOrdering::Monotonic
);
}
#[test]
fn test_struct_layout_has_bitfields() {
let layout = FullStructLayout {
name: "Test".to_string(),
total_size_bytes: 8,
total_alignment_bytes: 8,
fields: vec![],
is_packed: false,
is_union: false,
has_tail_padding: false,
};
assert!(!StructLayout::has_bitfields(&layout));
}
#[test]
fn test_struct_get_field_size() {
let layout = FullStructLayout {
name: "Test".to_string(),
total_size_bytes: 8,
total_alignment_bytes: 4,
fields: vec![
FieldLayout {
name: "x".to_string(),
offset_bytes: 0,
size_bytes: 4,
alignment_bytes: 4,
field_type_id: Type::i32().id,
},
FieldLayout {
name: "y".to_string(),
offset_bytes: 4,
size_bytes: 4,
alignment_bytes: 4,
field_type_id: Type::i32().id,
},
],
is_packed: false,
is_union: false,
has_tail_padding: false,
};
assert_eq!(StructLayout::get_field_size(&layout, "x"), Some(4));
assert_eq!(StructLayout::get_field_size(&layout, "y"), Some(4));
assert_eq!(StructLayout::get_field_size(&layout, "z"), None);
}
#[test]
fn test_struct_inter_field_padding_first() {
let layout = FullStructLayout {
name: "T".to_string(),
total_size_bytes: 12,
total_alignment_bytes: 4,
fields: vec![FieldLayout {
name: "a".to_string(),
offset_bytes: 0,
size_bytes: 4,
alignment_bytes: 4,
field_type_id: Type::i32().id,
}],
is_packed: false,
is_union: false,
has_tail_padding: false,
};
assert_eq!(StructLayout::inter_field_padding(&layout, 0), Some(0));
assert_eq!(StructLayout::inter_field_padding(&layout, 10), None);
}
#[test]
fn test_struct_alignment_accessor() {
let layout = FullStructLayout {
name: "Aligned".to_string(),
total_size_bytes: 16,
total_alignment_bytes: 8,
fields: vec![],
is_packed: false,
is_union: false,
has_tail_padding: true,
};
assert_eq!(StructLayout::alignment(&layout), 8);
}
#[test]
fn test_asm_constraint_match() {
let constraints = AsmConstraintKind::parse("0");
assert_eq!(constraints.len(), 1);
assert_eq!(constraints[0], AsmConstraintKind::Matching(0));
}
#[test]
fn test_asm_constraint_float_register() {
let constraints = AsmConstraintKind::parse("f");
assert_eq!(constraints[0], AsmConstraintKind::FloatRegister);
}
#[test]
fn test_asm_constraint_vector_register() {
let constraints = AsmConstraintKind::parse("w");
assert_eq!(constraints[0], AsmConstraintKind::VectorRegister);
}
#[test]
fn test_asm_empty_clobbers() {
let clobbers: Vec<String> = vec![];
assert!(!InlineASMCodeGen::is_memory_clobber(&clobbers));
assert!(!InlineASMCodeGen::is_cc_clobber(&clobbers));
}
#[test]
fn test_asm_parse_clobbers_empty() {
let clobbers = InlineASMCodeGen::parse_clobbers("");
assert!(clobbers.is_empty());
}
#[test]
fn test_inline_asm_desc_defaults() {
let desc = InlineAsmDesc {
asm_string: "nop".to_string(),
constraints: "=r,r".to_string(),
has_side_effects: true,
is_align_stack: false,
is_intel_dialect: false,
can_throw: false,
};
assert!(desc.has_side_effects);
assert!(!desc.is_align_stack);
}
#[test]
fn test_compile_full_c_program() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("integration_test");
let mut gen = make_gen(&ctx, &mut module);
gen.optimization_level = 2;
gen.generate_debug_info = true;
let cu = DebugInfo::create_compile_unit(
DebugInfo::DW_LANG_C11,
"main.c",
"/home/user",
"clang-deep",
true,
"-O2",
0,
);
gen.debug_compile_unit = Some(cu);
let body = CompoundStmt {
stmts: vec![Stmt::Return(Some(make_int_literal(0)))],
};
let fd = FunctionDecl {
name: "main".to_string(),
ret_ty: TypeNode::Int,
params: vec![],
body: Some(body),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
let result = gen.compile_function(&fd);
assert!(result.is_ok());
assert!(module.has_function("main"));
assert!(gen.errors.is_empty());
}
#[test]
fn test_multiple_functions_in_module() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("multi");
let mut gen = make_gen(&ctx, &mut module);
let body1 = CompoundStmt {
stmts: vec![Stmt::Return(Some(make_int_literal(1)))],
};
let fd1 = FunctionDecl {
name: "f1".to_string(),
ret_ty: TypeNode::Int,
params: vec![],
body: Some(body1),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
gen.compile_function(&fd1).unwrap();
let body2 = CompoundStmt {
stmts: vec![Stmt::Return(Some(make_int_literal(2)))],
};
let fd2 = FunctionDecl {
name: "f2".to_string(),
ret_ty: TypeNode::Int,
params: vec![],
body: Some(body2),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
gen.compile_function(&fd2).unwrap();
assert!(module.has_function("f1"));
assert!(module.has_function("f2"));
assert_eq!(module.get_function_count(), 2);
}
#[test]
fn test_debug_info_full_pipeline() {
let file = DebugInfo::create_file("test.c", "/src");
let void_ty = DebugInfo::create_basic_type("void", 0, 0);
let sp = DebugInfo::create_subprogram(
"test_func",
"test_func",
&file,
1,
&void_ty,
false,
true,
1,
0,
false,
);
let loc = DebugInfo::create_location(1, 1, &sp, None);
let cu = DebugInfo::create_compile_unit(
DebugInfo::DW_LANG_C17,
"test.c",
"/src",
"clang-deep",
false,
"",
0,
);
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let gv_ty = DebugInfo::create_basic_type("int", 32, 5);
let gv = DebugInfo::create_global_variable("g_x", "g_x", &file, 42, &gv_ty, false, true);
DebugInfo::finalize(&mut gen, &cu, &[sp], &[gv]);
assert!(gen.debug_compile_unit.is_some());
}
#[test]
fn test_convert_struct_type_cached_multiple() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let sd = StructDecl {
name: "Vec".to_string(),
fields: vec![
FieldDecl::new("x", TypeNode::Float),
FieldDecl::new("y", TypeNode::Float),
],
is_union: false,
};
let ty1 = gen.convert_struct_type(&sd);
let ty2 = gen.convert_struct_type(&sd);
assert_eq!(ty1.id, ty2.id);
assert!(ty1.is_struct());
}
#[test]
fn test_convert_pointer_to_pointer() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let ptr_to_int = gen.convert_type(&TypeNode::Pointer(Box::new(TypeNode::Int)));
assert!(ptr_to_int.is_pointer());
}
#[test]
fn test_convert_array_of_struct() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let struct_tn = TypeNode::Struct(
"Point".to_string(),
vec![
FieldDecl::new("x", TypeNode::Int),
FieldDecl::new("y", TypeNode::Int),
],
false,
);
let arr_of_struct = gen.convert_type(&TypeNode::Array(Box::new(struct_tn), 5));
assert!(arr_of_struct.is_array());
}
#[test]
fn test_expr_codegen_unary_plus() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("uplus_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let mut ecg = ExprCodeGen::new(&mut gen);
let val = ecg.codegen_integer_literal(42, 32);
let result = ecg.codegen_unary_plus(val);
assert!(result.is_ok());
gen.current_function = None;
}
#[test]
fn test_vector_binary_ops() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("vec_binop_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let vec_ty = Type::fixed_vector_with(Type::i32().id, 4);
let v1 = Value::new(vec_ty.id);
let v2 = Value::new(vec_ty.id);
let mut vcg = VectorCodeGen::new(&mut gen);
let _add = vcg.codegen_vector_binary(BinaryOp::Add, v1.clone(), v2.clone());
let _sub = vcg.codegen_vector_binary(BinaryOp::Sub, v1.clone(), v2.clone());
let _mul = vcg.codegen_vector_binary(BinaryOp::Mul, v1.clone(), v2.clone());
let _and = vcg.codegen_vector_binary(BinaryOp::And, v1, v2);
gen.current_function = None;
}
#[test]
fn test_vector_compare_ops() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("vec_cmp_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let vec_ty = Type::fixed_vector_with(Type::i32().id, 4);
let v1 = Value::new(vec_ty.id);
let v2 = Value::new(vec_ty.id);
let mut vcg = VectorCodeGen::new(&mut gen);
let _eq = vcg.codegen_vector_compare(BinaryOp::Eq, v1.clone(), v2.clone());
let _ne = vcg.codegen_vector_compare(BinaryOp::Ne, v1.clone(), v2.clone());
let _lt = vcg.codegen_vector_compare(BinaryOp::Lt, v1, v2);
gen.current_function = None;
}
#[test]
fn test_vector_reduce_add() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("vec_reduce_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let vec_ty = Type::fixed_vector_with(Type::i32().id, 4);
let vector = Value::new(vec_ty.id);
let mut vcg = VectorCodeGen::new(&mut gen);
let result = vcg.codegen_vector_reduce_add(vector);
gen.current_function = None;
}
#[test]
fn test_vector_cast() {
let ctx = LLVMContext::new();
let mut module = ctx.create_module("test");
let mut gen = make_gen(&ctx, &mut module);
let func_ty = Type::function_type_with(Type::void().id, &[], false);
let func_val = Value::named("vec_cast_test");
func_val.borrow_mut().ty = func_ty.id;
func_val.borrow_mut().subclass = SubclassKind::Function;
module.add_function(func_val.clone());
gen.current_function = Some(func_val.clone());
let entry_bb = gen.builder.create_basic_block("entry");
func_val.borrow_mut().blocks = vec![entry_bb.clone()];
gen.builder.set_insert_point(&entry_bb);
let vec_ty = Type::fixed_vector_with(Type::i32().id, 4);
let vector = Value::new(vec_ty.id);
let mut vcg = VectorCodeGen::new(&mut gen);
let result = vcg.codegen_vector_cast(vector, &Type::f32());
gen.current_function = None;
let ty = Type::from_id(result.borrow().ty);
assert!(ty.is_vector());
}
}