use itertools::Itertools;
use llvm_sys::core::*;
use llvm_sys::prelude::*;
use llvm_sys::target::*;
use llvm_sys::target_machine::*;
use llvm_sys::transforms::pass_manager_builder::*;
use llvm_sys::{LLVMBuilder, LLVMModule};
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::os::raw::{c_double, c_uint, c_ulonglong};
use std::ptr::null_mut;
use std::str;
use parser;
const LLVM_FALSE: LLVMBool = 0;
pub fn compile_to_module(
module_name: &str,
target_triple: Option<String>,
do_report_mem_usage: bool,
ast: &[parser::AstNode],
) -> Module {
let mut module = create_module(module_name, target_triple);
let main_bb = add_main_fn(&mut module);
unsafe {
for astnode in ast {
let (stmt_fn, stmt_bb) = add_stmt_fn(&mut module);
let jval = match astnode {
parser::AstNode::Print(expr) => {
let c_expr = compile_expr(expr, &mut module, stmt_bb);
match expr.as_ref() {
parser::AstNode::IsGlobal { ident: _, expr: _ } => (),
_ => {
let mut args = vec![c_expr.ptr, int1(1)];
add_function_call(&mut module, stmt_bb, "jprint", &mut args[..], "");
}
}
c_expr
}
_ => panic!("Not ready to compile top-level AST node: {:?}", astnode),
};
let mut args = vec![jval.ptr, int1(0)];
add_function_call(&mut module, stmt_bb, "jval_drop", &mut args[..], "");
let stmt_builder = Builder::new();
stmt_builder.position_at_end(stmt_bb);
LLVMBuildRetVoid(stmt_builder.builder);
let main_builder = Builder::new();
main_builder.position_at_end(main_bb);
LLVMBuildCall(
main_builder.builder,
stmt_fn,
Vec::new().as_mut_ptr(),
0,
module.new_string_ptr(""),
);
}
let mut args = vec![];
add_function_call(&mut module, main_bb, "jglobals_dropall", &mut args[..], "");
let mut args = vec![int1(do_report_mem_usage as u64)];
add_function_call(&mut module, main_bb, "jmemory_check", &mut args[..], "");
add_main_cleanup(main_bb);
module
}
}
#[derive(Clone)]
enum JValType {
Integer = 1,
DoublePrecisionFloat = 2,
Character = 3,
NDimensionalArray = 4,
}
#[derive(Clone)]
enum JValTypeParam {
Numeric = 1,
String = 2,
}
#[derive(Clone)]
enum JValLocation {
Stack = 1,
HeapGlobal = 3,
}
#[derive(Clone)]
struct JValPtr {
ptr: LLVMValueRef,
}
fn alloc_jval(
module: &mut Module,
bb: LLVMBasicBlockRef,
val: LLVMValueRef,
val_type: JValType,
val_type_param: JValTypeParam,
val_loc: JValLocation,
val_shape: Vec<u64>,
) -> JValPtr {
let builder = Builder::new();
builder.position_at_end(bb);
unsafe {
let jval_ptr = LLVMBuildAlloca(
builder.builder,
module.jval_struct_type,
module.new_string_ptr("jval"),
);
let mut type_offset = vec![int64(0), int32(0)];
let type_gep = LLVMBuildInBoundsGEP(
builder.builder,
jval_ptr,
type_offset.as_mut_ptr(),
type_offset.len() as u32,
module.new_string_ptr("jval_type_gep"),
);
LLVMBuildStore(builder.builder, int8(val_type.clone() as u64), type_gep);
let mut typaram_offset = vec![int64(0), int32(1)];
let type_param_gep = LLVMBuildInBoundsGEP(
builder.builder,
jval_ptr,
typaram_offset.as_mut_ptr(),
typaram_offset.len() as u32,
module.new_string_ptr("jval_typeparam_gep"),
);
LLVMBuildStore(builder.builder, int8(val_type_param as u64), type_param_gep);
let mut loc_offset = vec![int64(0), int32(2)];
let loc_gep = LLVMBuildInBoundsGEP(
builder.builder,
jval_ptr,
loc_offset.as_mut_ptr(),
loc_offset.len() as u32,
module.new_string_ptr("jval_loc_gep"),
);
LLVMBuildStore(builder.builder, int8(val_loc.clone() as u64), loc_gep);
let mut rank_offset = vec![int64(0), int32(3)];
let rank_gep = LLVMBuildInBoundsGEP(
builder.builder,
jval_ptr,
rank_offset.as_mut_ptr(),
rank_offset.len() as u32,
module.new_string_ptr("jval_rank_gep"),
);
LLVMBuildStore(builder.builder, int32(val_shape.len() as u64), rank_gep);
let shape_arr = LLVMBuildArrayAlloca(
builder.builder,
int32_type(),
int64(val_shape.len() as u64),
module.new_string_ptr("shape_arr"),
);
for (idx, dim) in val_shape.iter().cloned().enumerate() {
let mut indices = vec![int32(idx as u64)];
let dim_gep = LLVMBuildInBoundsGEP(
builder.builder,
shape_arr,
indices.as_mut_ptr(),
indices.len() as u32,
module.new_string_ptr("dim_gep"),
);
LLVMBuildStore(builder.builder, int32(dim), dim_gep);
}
let mut shape_offset = vec![int64(0), int32(4)];
let shape_gep = LLVMBuildInBoundsGEP(
builder.builder,
jval_ptr,
shape_offset.as_mut_ptr(),
shape_offset.len() as u32,
module.new_string_ptr("jval_shape_gep"),
);
LLVMBuildStore(builder.builder, shape_arr, shape_gep);
let mut ptr_offset = vec![int64(0), int32(5)];
let ptr_gep = LLVMBuildInBoundsGEP(
builder.builder,
jval_ptr,
ptr_offset.as_mut_ptr(),
ptr_offset.len() as u32,
module.new_string_ptr("jval_ptr_gep"),
);
let ptr_gep = match val_type {
JValType::Integer => LLVMBuildPointerCast(
builder.builder,
ptr_gep,
int32_ptr_ptr_type(),
module.new_string_ptr("jval_ptr_cast"),
),
JValType::DoublePrecisionFloat => LLVMBuildPointerCast(
builder.builder,
ptr_gep,
f64_ptr_ptr_type(),
module.new_string_ptr("jval_ptr_cast"),
),
JValType::Character => LLVMBuildPointerCast(
builder.builder,
ptr_gep,
int8_ptr_ptr_type(),
module.new_string_ptr("jval_ptr_cast"),
),
_ => ptr_gep,
};
LLVMBuildStore(builder.builder, val, ptr_gep);
JValPtr { ptr: jval_ptr }
}
}
fn compile_expr(expr: &parser::AstNode, module: &mut Module, bb: LLVMBasicBlockRef) -> JValPtr {
let builder = Builder::new();
builder.position_at_end(bb);
match *expr {
parser::AstNode::Integer(n) => {
unsafe {
let num = LLVMBuildAlloca(
builder.builder,
int32_type(),
module.new_string_ptr("int_alloc"),
);
LLVMBuildStore(builder.builder, int32(n as u64), num);
let ty = JValType::Integer;
alloc_jval(
module,
bb,
num,
ty.clone(),
JValTypeParam::Numeric,
JValLocation::Stack,
vec![],
)
}
}
parser::AstNode::DoublePrecisionFloat(n) => {
unsafe {
let num = LLVMBuildAlloca(
builder.builder,
f64_type(),
module.new_string_ptr("dblfp_alloc"),
);
LLVMBuildStore(builder.builder, f64(n), num);
alloc_jval(
module,
bb,
num,
JValType::DoublePrecisionFloat,
JValTypeParam::Numeric,
JValLocation::Stack,
vec![],
)
}
}
parser::AstNode::Str(ref str) => {
unsafe {
if str.len() == 1 {
let char_alloc = LLVMBuildAlloca(
builder.builder,
int8_type(),
module.new_string_ptr("char_alloc"),
);
LLVMBuildStore(builder.builder, int8(str.as_bytes()[0] as u64), char_alloc);
alloc_jval(
module,
bb,
char_alloc,
JValType::Character,
JValTypeParam::String,
JValLocation::Stack,
vec![],
)
} else {
let arr = LLVMBuildArrayAlloca(
builder.builder,
module.jval_ptr_type,
int64(str.as_bytes().len() as u64),
module.new_string_ptr("string_arr"),
);
let mut idx = 0;
for byte in str.as_bytes() {
let char_alloc = LLVMBuildAlloca(
builder.builder,
int8_type(),
module.new_string_ptr("char_alloc"),
);
LLVMBuildStore(builder.builder, int8(*byte as u64), char_alloc);
let char_jval = alloc_jval(
module,
bb,
char_alloc,
JValType::Character,
JValTypeParam::String,
JValLocation::Stack,
vec![],
);
let mut char_offset = vec![int32(idx)];
let char_gep = LLVMBuildInBoundsGEP(
builder.builder,
arr,
char_offset.as_mut_ptr(),
char_offset.len() as u32,
module.new_string_ptr("char_gep"),
);
LLVMBuildStore(builder.builder, char_jval.ptr, char_gep);
idx += 1;
}
alloc_jval(
module,
bb,
arr,
JValType::NDimensionalArray,
JValTypeParam::String,
JValLocation::Stack,
vec![str.len() as u64],
)
}
}
}
parser::AstNode::Terms(ref terms) => {
assert!(terms.len() >= 2);
let compiled_terms = terms
.iter()
.map(|t| compile_expr(t, module, bb))
.collect_vec();
unsafe {
let arr = LLVMBuildArrayAlloca(
builder.builder,
module.jval_ptr_type,
int64(compiled_terms.len() as u64),
module.new_string_ptr("terms_arr"),
);
for (idx, jval) in compiled_terms.iter().enumerate() {
let mut term_offset = vec![int32(idx as u64)];
let term_gep = LLVMBuildInBoundsGEP(
builder.builder,
arr,
term_offset.as_mut_ptr(),
term_offset.len() as u32,
module.new_string_ptr("term_gep"),
);
LLVMBuildStore(builder.builder, jval.ptr, term_gep);
}
alloc_jval(
module,
bb,
arr,
JValType::NDimensionalArray,
JValTypeParam::Numeric,
JValLocation::Stack,
vec![compiled_terms.len() as u64],
)
}
}
parser::AstNode::MonadicOp { ref verb, ref expr } => {
let expr = compile_expr(expr, module, bb);
unsafe {
let mut args = vec![int8(verb.clone() as u64), expr.ptr];
let monad_op_arr =
add_function_call(module, bb, "jmonad", &mut args[..], "monad_op_arr");
let mut args = vec![expr.ptr, int1(0)];
add_function_call(module, bb, "jval_drop", &mut args[..], "");
JValPtr { ptr: monad_op_arr }
}
}
parser::AstNode::DyadicOp {
ref verb,
ref lhs,
ref rhs,
} => {
let mut rhs = compile_expr(rhs, module, bb);
let mut lhs = compile_expr(lhs, module, bb);
unsafe {
let mut args = vec![int8(verb.clone() as u64), lhs.ptr, rhs.ptr];
let dyad_op_arr =
add_function_call(module, bb, "jdyad", &mut args[..], "dyad_op_arr");
let mut args = vec![lhs.ptr, int1(0)];
add_function_call(module, bb, "jval_drop", &mut args[..], "");
let mut args = vec![rhs.ptr, int1(0)];
add_function_call(module, bb, "jval_drop", &mut args[..], "");
JValPtr { ptr: dyad_op_arr }
}
}
parser::AstNode::Reduce { ref verb, ref expr } => {
let mut expr = compile_expr(expr, module, bb);
unsafe {
let mut args = vec![int8(verb.clone() as u64), expr.ptr];
let reduced_arr =
add_function_call(module, bb, "jreduce", &mut args[..], "reduced_arr");
let mut args = vec![expr.ptr, int1(0)];
add_function_call(module, bb, "jval_drop", &mut args[..], "");
JValPtr { ptr: reduced_arr }
}
}
parser::AstNode::IsGlobal {
ref ident,
ref expr,
} => {
let mut expr = compile_expr(expr, module, bb);
let mut args = vec![expr.ptr, int64(JValLocation::HeapGlobal as u64)];
let global_clone =
unsafe { add_function_call(module, bb, "jval_clone", &mut args[..], "") };
unsafe {
let mut args = vec![expr.ptr, int1(0)];
add_function_call(module, bb, "jval_drop", &mut args[..], "");
}
let global_id = module.get_or_assign_id_to_global_ident(&ident[..]);
unsafe {
let mut args = vec![int32(global_id as u64), global_clone];
add_function_call(module, bb, "jglobal_set_reference", &mut args[..], "");
}
let global = JValPtr { ptr: global_clone };
global
}
parser::AstNode::Ident(ref ident) => {
let global_id = module.get_or_assign_id_to_global_ident(ident);
unsafe {
let mut args = vec![int32(global_id as u64)];
let global_ref = add_function_call(
module,
bb,
"jglobal_get_reference",
&mut args[..],
"global_ref",
);
JValPtr { ptr: global_ref }
}
}
_ => unimplemented!("Not ready to compile expr: {:?}", expr),
}
}
pub struct Module {
module: *mut LLVMModule,
strings: Vec<CString>,
global_scope_idents: HashMap<String, u32>,
jval_struct_type: LLVMTypeRef,
jval_ptr_type: LLVMTypeRef,
}
impl Module {
fn new_string_ptr(&mut self, s: &str) -> *const i8 {
self.new_mut_string_ptr(s)
}
fn new_mut_string_ptr(&mut self, s: &str) -> *mut i8 {
let cstring = CString::new(s).unwrap();
let ptr = cstring.as_ptr() as *mut _;
self.strings.push(cstring);
ptr
}
pub fn to_cstring(&self) -> CString {
unsafe {
let llvm_ir_ptr = LLVMPrintModuleToString(self.module);
let llvm_ir = CStr::from_ptr(llvm_ir_ptr as *const _);
let module_string = CString::new(llvm_ir.to_bytes()).unwrap();
LLVMDisposeMessage(llvm_ir_ptr);
module_string
}
}
fn get_or_assign_id_to_global_ident(&mut self, ident: &str) -> u32 {
let current_len = self.global_scope_idents.len() as u32;
*self
.global_scope_idents
.entry(String::from(ident))
.or_insert(current_len)
}
}
impl Drop for Module {
fn drop(&mut self) {
unsafe {
LLVMDisposeModule(self.module);
}
}
}
struct Builder {
builder: *mut LLVMBuilder,
}
impl Builder {
fn new() -> Self {
unsafe {
Builder {
builder: LLVMCreateBuilder(),
}
}
}
fn position_at_end(&self, bb: LLVMBasicBlockRef) {
unsafe {
LLVMPositionBuilderAtEnd(self.builder, bb);
}
}
}
impl Drop for Builder {
fn drop(&mut self) {
unsafe {
LLVMDisposeBuilder(self.builder);
}
}
}
#[derive(Clone)]
struct CompileContext {
cells: LLVMValueRef,
cell_index_ptr: LLVMValueRef,
main_fn: LLVMValueRef,
}
unsafe fn int1(val: c_ulonglong) -> LLVMValueRef {
LLVMConstInt(LLVMInt1Type(), val, LLVM_FALSE)
}
unsafe fn int8(val: c_ulonglong) -> LLVMValueRef {
LLVMConstInt(LLVMInt8Type(), val, LLVM_FALSE)
}
unsafe fn f64(val: c_double) -> LLVMValueRef {
LLVMConstReal(LLVMDoubleType(), val)
}
fn int32(val: c_ulonglong) -> LLVMValueRef {
unsafe { LLVMConstInt(LLVMInt32Type(), val, LLVM_FALSE) }
}
fn int64(val: c_ulonglong) -> LLVMValueRef {
unsafe { LLVMConstInt(LLVMInt64Type(), val, LLVM_FALSE) }
}
fn int1_type() -> LLVMTypeRef {
unsafe { LLVMInt1Type() }
}
fn int8_type() -> LLVMTypeRef {
unsafe { LLVMInt8Type() }
}
fn int32_type() -> LLVMTypeRef {
unsafe { LLVMInt32Type() }
}
fn f64_type() -> LLVMTypeRef {
unsafe { LLVMDoubleType() }
}
fn f64_ptr_ptr_type() -> LLVMTypeRef {
unsafe { LLVMPointerType(LLVMPointerType(LLVMDoubleType(), 0), 0) }
}
fn int8_ptr_ptr_type() -> LLVMTypeRef {
unsafe { LLVMPointerType(LLVMPointerType(LLVMInt8Type(), 0), 0) }
}
fn int32_ptr_ptr_type() -> LLVMTypeRef {
unsafe { LLVMPointerType(LLVMPointerType(LLVMInt32Type(), 0), 0) }
}
fn void_ptr_type() -> LLVMTypeRef {
unsafe { LLVMPointerType(LLVMVoidType(), 0) }
}
fn add_function(
module: &mut Module,
fn_name: &str,
args: &mut [LLVMTypeRef],
ret_type: LLVMTypeRef,
) {
unsafe {
let fn_type = LLVMFunctionType(ret_type, args.as_mut_ptr(), args.len() as u32, LLVM_FALSE);
LLVMAddFunction(module.module, module.new_string_ptr(fn_name), fn_type);
}
}
fn add_c_declarations(module: &mut Module) {
let void;
unsafe {
void = LLVMVoidType();
}
let jval_ptr_type = module.jval_ptr_type.clone();
let jval_ptr_ptr_type = unsafe { LLVMPointerType(module.jval_ptr_type.clone(), 0) };
add_function(module, "jprint", &mut [jval_ptr_type, int1_type()], void);
add_function(
module,
"jmonad",
&mut [int8_type(), jval_ptr_type],
jval_ptr_type,
);
add_function(
module,
"jdyad",
&mut [int8_type(), jval_ptr_type, jval_ptr_type],
jval_ptr_type,
);
add_function(
module,
"jreduce",
&mut [int8_type(), jval_ptr_type],
jval_ptr_type,
);
add_function(module, "jval_drop", &mut [jval_ptr_type, int1_type()], void);
add_function(
module,
"jval_clone",
&mut [jval_ptr_type, int8_type()],
jval_ptr_type,
);
add_function(module, "jmemory_check", &mut [int1_type()], void);
add_function(
module,
"jglobal_set_reference",
&mut [int32_type(), jval_ptr_ptr_type],
void,
);
add_function(
module,
"jglobal_get_reference",
&mut [int32_type()],
jval_ptr_type,
);
add_function(module, "jglobals_dropall", &mut [], void);
}
unsafe fn add_function_call(
module: &mut Module,
bb: LLVMBasicBlockRef,
fn_name: &str,
args: &mut [LLVMValueRef],
name: &str,
) -> LLVMValueRef {
let builder = Builder::new();
builder.position_at_end(bb);
let function = LLVMGetNamedFunction(module.module, module.new_string_ptr(fn_name));
LLVMBuildCall(
builder.builder,
function,
args.as_mut_ptr(),
args.len() as c_uint,
module.new_string_ptr(name),
)
}
fn create_module(module_name: &str, target_triple: Option<String>) -> Module {
let mut strings = Vec::new();
let c_module_name = CString::new(module_name).unwrap();
let module_name_char_ptr = c_module_name.to_bytes_with_nul().as_ptr() as *const _;
strings.push(c_module_name);
let c_jval_struct_name = CString::new("jval_struct").unwrap();
let jval_struct_name_char_ptr = c_jval_struct_name.to_bytes_with_nul().as_ptr() as *const _;
strings.push(c_jval_struct_name);
let mut module = unsafe {
let llvm_module = LLVMModuleCreateWithName(module_name_char_ptr);
let global_ctx = LLVMGetGlobalContext();
let jval_struct_type = LLVMStructCreateNamed(global_ctx, jval_struct_name_char_ptr);
let jval_ptr_type = LLVMPointerType(jval_struct_type, 0);
let mut members = vec![
int8_type(),
int8_type(),
int8_type(),
int32_type(),
void_ptr_type(),
jval_ptr_type,
];
LLVMStructSetBody(
jval_struct_type,
members.as_mut_ptr(),
members.len() as u32,
0,
);
let heap_counter_names = vec![
"alive_heap_jval_counter",
"total_heap_jval_counter",
"alive_heap_int_counter",
"total_heap_int_counter",
"alive_heap_double_counter",
"total_heap_double_counter",
"alive_heap_char_counter",
"total_heap_char_counter",
"alive_heap_jvalptrarray_counter",
"total_heap_jvalptrarray_counter",
];
heap_counter_names.iter().for_each(|name| {
let cstr_name = CString::new(*name).unwrap();
let counter = LLVMAddGlobal(
llvm_module,
int32_type(),
cstr_name.to_bytes_with_nul().as_ptr() as *const _,
);
LLVMSetInitializer(counter, int32(0));
strings.push(cstr_name);
});
Module {
module: llvm_module,
strings,
global_scope_idents: HashMap::new(),
jval_struct_type,
jval_ptr_type: jval_ptr_type,
}
};
let target_triple_cstring = if let Some(target_triple) = target_triple {
CString::new(target_triple).unwrap()
} else {
get_default_target_triple()
};
unsafe {
LLVMSetTarget(module.module, target_triple_cstring.as_ptr() as *const _);
}
add_c_declarations(&mut module);
module
}
fn add_main_fn(module: &mut Module) -> LLVMBasicBlockRef {
let mut main_args = vec![];
unsafe {
let main_type = LLVMFunctionType(int32_type(), main_args.as_mut_ptr(), 0, LLVM_FALSE);
let main_fn = LLVMAddFunction(module.module, module.new_string_ptr("main"), main_type);
LLVMAppendBasicBlock(main_fn, module.new_string_ptr(""))
}
}
fn add_stmt_fn(module: &mut Module) -> (LLVMValueRef, LLVMBasicBlockRef) {
let mut args = vec![];
unsafe {
let stmt_type = LLVMFunctionType(LLVMVoidType(), args.as_mut_ptr(), 0, LLVM_FALSE);
let stmt_fn = LLVMAddFunction(module.module, module.new_string_ptr("stmt"), stmt_type);
let stmt_bb = LLVMAppendBasicBlock(stmt_fn, module.new_string_ptr(""));
(stmt_fn, stmt_bb)
}
}
unsafe fn add_main_cleanup(bb: LLVMBasicBlockRef) {
let builder = Builder::new();
builder.position_at_end(bb);
let zero = int32(0);
LLVMBuildRet(builder.builder, zero);
}
pub fn optimise_ir(module: &mut Module, llvm_opt: i64) {
unsafe {
let builder = LLVMPassManagerBuilderCreate();
LLVMPassManagerBuilderSetOptLevel(builder, llvm_opt as u32);
let pass_manager = LLVMCreatePassManager();
LLVMPassManagerBuilderPopulateModulePassManager(builder, pass_manager);
LLVMPassManagerBuilderDispose(builder);
LLVMRunPassManager(pass_manager, module.module);
LLVMRunPassManager(pass_manager, module.module);
LLVMDisposePassManager(pass_manager);
}
}
pub fn get_default_target_triple() -> CString {
let target_triple;
unsafe {
let target_triple_ptr = LLVMGetDefaultTargetTriple();
target_triple = CStr::from_ptr(target_triple_ptr as *const _).to_owned();
LLVMDisposeMessage(target_triple_ptr);
}
target_triple
}
struct TargetMachine {
tm: LLVMTargetMachineRef,
}
impl TargetMachine {
fn new(target_triple: *const i8) -> Result<Self, String> {
let mut target = null_mut();
let mut err_msg_ptr = null_mut();
unsafe {
LLVMGetTargetFromTriple(target_triple, &mut target, &mut err_msg_ptr);
if target.is_null() {
assert!(!err_msg_ptr.is_null());
let err_msg_cstr = CStr::from_ptr(err_msg_ptr as *const _);
let err_msg = str::from_utf8(err_msg_cstr.to_bytes()).unwrap();
return Err(err_msg.to_owned());
}
}
let cpu = CString::new("generic").unwrap();
let features = CString::new("").unwrap();
let target_machine;
unsafe {
target_machine = LLVMCreateTargetMachine(
target,
target_triple,
cpu.as_ptr() as *const _,
features.as_ptr() as *const _,
LLVMCodeGenOptLevel::LLVMCodeGenLevelAggressive,
LLVMRelocMode::LLVMRelocPIC,
LLVMCodeModel::LLVMCodeModelDefault,
);
}
Ok(TargetMachine { tm: target_machine })
}
}
impl Drop for TargetMachine {
fn drop(&mut self) {
unsafe {
LLVMDisposeTargetMachine(self.tm);
}
}
}
pub fn write_object_file(module: &mut Module, path: &str) -> Result<(), String> {
unsafe {
let target_triple = LLVMGetTarget(module.module);
LLVM_InitializeAllTargetInfos();
LLVM_InitializeAllTargets();
LLVM_InitializeAllTargetMCs();
LLVM_InitializeAllAsmParsers();
LLVM_InitializeAllAsmPrinters();
let target_machine = TargetMachine::new(target_triple).unwrap();
let mut obj_error = module.new_mut_string_ptr("Writing object file failed.");
let result = LLVMTargetMachineEmitToFile(
target_machine.tm,
module.module,
module.new_string_ptr(path) as *mut i8,
LLVMCodeGenFileType::LLVMObjectFile,
&mut obj_error,
);
if result != 0 {
panic!("obj_error: {:?}", CStr::from_ptr(obj_error as *const _));
}
}
Ok(())
}