diff --git a/src/builder.rs b/src/builder.rs index 190a03bb8df..61e730d0fed 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -24,6 +24,16 @@ impl Builder { } } + /// Creates a `Builder` belonging to the global `Context`. + /// + /// # Example + /// + /// ```no_run + /// + /// use inkwell::builder::Builder; + /// + /// let builder = Builder::create(); + /// ``` pub fn create() -> Self { let builder = unsafe { LLVMCreateBuilder() diff --git a/src/lib.rs b/src/lib.rs index 886939b0e5b..08259ae62eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ pub mod context; pub mod data_layout; pub mod execution_engine; pub mod memory_buffer; +#[deny(missing_docs)] pub mod module; pub mod object_file; pub mod passes; @@ -33,18 +34,6 @@ use llvm_sys::{LLVMIntPredicate, LLVMRealPredicate, LLVMVisibility, LLVMThreadLo feature = "llvm5-0", feature = "llvm6-0")))] compile_error!("A LLVM feature flag must be provided. See the README for more details."); -// TODO: Probably move into error handling module -pub fn enable_llvm_pretty_stack_trace() { - #[cfg(any(feature = "llvm3-6", feature = "llvm3-7"))] - use llvm_sys::core::LLVMEnablePrettyStackTrace; - #[cfg(any(feature = "llvm3-8", feature = "llvm3-9", feature = "llvm4-0", feature = "llvm5-0", feature = "llvm6-0"))] - use llvm_sys::error_handling::LLVMEnablePrettyStackTrace; - - unsafe { - LLVMEnablePrettyStackTrace() - } -} - /// Defines the address space in which a global will be inserted. /// /// # Remarks @@ -73,17 +62,28 @@ impl From for AddressSpace { } // REVIEW: Maybe this belongs in some sort of prelude? +/// This enum defines how to compare a `left` and `right` `IntValue`. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum IntPredicate { + /// Equal EQ, + /// Not Equal NE, + /// Unsigned Greater Than UGT, + /// Unsigned Greater Than or Equal UGE, + /// Unsigned Less Than ULT, + /// Unsigned Less Than or Equal ULE, + /// Signed Greater Than SGT, + /// Signed Greater Than or Equal SGE, + /// Signed Less Than SLT, + /// Signed Less Than or Equal SLE, } @@ -105,7 +105,7 @@ impl IntPredicate { } // REVIEW: Maybe this belongs in some sort of prelude? -/// Defines how to compare a `left` and `right` float value. +/// Defines how to compare a `left` and `right` `FloatValue`. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum FloatPredicate { /// Returns true if `left` == `right` and neither are NaN @@ -280,9 +280,3 @@ impl DLLStorageClass { } } } - -// Misc Notes - -// Initializer (new) strategy: -// assert!(!val.is_null()); where null is not expected to ever occur, but Option -// when null is expected to be passed at some point diff --git a/src/module.rs b/src/module.rs index ef31334879d..3e4f29bda21 100644 --- a/src/module.rs +++ b/src/module.rs @@ -583,6 +583,25 @@ impl Module { GlobalValue::new(value) } + /// Writes a `Module` to a `Path`. + /// + /// # Example + /// + /// ```no_run + /// use inkwell::context::Context; + /// + /// use std::path::Path; + /// + /// let mut path = Path::new("module.bc"); + /// + /// let context = Context::create(); + /// let module = context.create_module("my_module"); + /// let void_type = context.void_type(); + /// let fn_type = void_type.fn_type(&[], false); + /// + /// module.add_function("my_fn", &fn_type, None); + /// module.write_bitcode_to_path(&path); + /// ``` pub fn write_bitcode_to_path(&self, path: &Path) -> bool { let path_str = path.to_str().expect("Did not find a valid Unicode path string"); let c_string = CString::new(path_str).expect("Conversion to CString failed unexpectedly"); @@ -593,24 +612,43 @@ impl Module { } // See GH issue #6 - #[cfg(unix)] + /// `write_bitcode_to_path` should be preferred over this method, as it does not work on all operating systems. pub fn write_bitcode_to_file(&self, file: &File, should_close: bool, unbuffered: bool) -> bool { - use std::os::unix::io::AsRawFd; - use llvm_sys::bit_writer::LLVMWriteBitcodeToFD; + #[cfg(unix)] + { + use std::os::unix::io::AsRawFd; + use llvm_sys::bit_writer::LLVMWriteBitcodeToFD; - // REVIEW: as_raw_fd docs suggest it only works in *nix - // Also, should_close should maybe be hardcoded to true? - unsafe { - LLVMWriteBitcodeToFD(self.module.get(), file.as_raw_fd(), should_close as i32, unbuffered as i32) == 0 + // REVIEW: as_raw_fd docs suggest it only works in *nix + // Also, should_close should maybe be hardcoded to true? + unsafe { + LLVMWriteBitcodeToFD(self.module.get(), file.as_raw_fd(), should_close as i32, unbuffered as i32) == 0 + } } + #[cfg(windows)] + return false; } - #[cfg(windows)] - #[allow(unused_variables)] - pub fn write_bitcode_to_file(&self, file: &File, should_close: bool, unbuffered: bool) -> bool { - false - } - + /// Writes this `Module` to a `MemoryBuffer`. + /// + /// # Example + /// + /// ```no_run + /// use inkwell::context::Context; + /// + /// let context = Context::create(); + /// let module = context.create_module("mod"); + /// let void_type = context.void_type(); + /// let fn_type = void_type.fn_type(&[], false); + /// let f = module.add_function("f", &fn_type, None); + /// let basic_block = f.append_basic_block("entry"); + /// let builder = context.create_builder(); + /// + /// builder.position_at_end(&basic_block); + /// builder.build_return(None); + /// + /// let buffer = module.write_bitcode_to_memory(); + /// ``` pub fn write_bitcode_to_memory(&self) -> MemoryBuffer { let memory_buffer = unsafe { LLVMWriteBitcodeToMemoryBuffer(self.module.get()) @@ -657,12 +695,54 @@ impl Module { DataLayout::new_borrowed(data_layout) } + /// Gets a smart pointer to the `DataLayout` belonging to a particular `Module`. + /// + /// # Example + /// + /// ```no_run + /// use inkwell::OptimizationLevel; + /// use inkwell::context::Context; + /// use inkwell::targets::{InitializationConfig, Target}; + /// + /// Target::initialize_native(&InitializationConfig::default()).expect("Failed to initialize native target"); + /// + /// let context = Context::create(); + /// let module = context.create_module("sum"); + /// let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None).unwrap(); + /// let target_data = execution_engine.get_target_data(); + /// let data_layout = target_data.get_data_layout(); + /// + /// module.set_data_layout(&data_layout); + /// + /// assert_eq!(*module.get_data_layout(), data_layout); + /// ``` pub fn get_data_layout(&self) -> Ref { Ref::map(self.data_layout.borrow(), |l| l.as_ref().expect("DataLayout should always exist until Drop")) } // REVIEW: Ensure the replaced string ptr still gets cleaned up by the module (I think it does) // valgrind might come in handy once non jemalloc allocators stabilize + /// Sets the `DataLayout` for a particular `Module`. + /// + /// # Example + /// + /// ```no_run + /// use inkwell::OptimizationLevel; + /// use inkwell::context::Context; + /// use inkwell::targets::{InitializationConfig, Target}; + /// + /// Target::initialize_native(&InitializationConfig::default()).expect("Failed to initialize native target"); + /// + /// let context = Context::create(); + /// let module = context.create_module("sum"); + /// let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None).unwrap(); + /// let target_data = execution_engine.get_target_data(); + /// let data_layout = target_data.get_data_layout(); + /// + /// module.set_data_layout(&data_layout); + /// + /// assert_eq!(*module.get_data_layout(), data_layout); + /// ``` pub fn set_data_layout(&self, data_layout: &DataLayout) { unsafe { LLVMSetDataLayout(self.module.get(), data_layout.as_ptr()); @@ -703,6 +783,9 @@ impl Module { Ok(()) } + /// Sets the inline assembly for the `Module`. + // REVIEW: Apparently LLVMSetModuleInlineAsm is deprecated at some point (recent?) in favor of + // LLVMSetModuleInlineAsm2 which takes a len pub fn set_inline_assembly(&self, asm: &str) { let c_string = CString::new(asm).expect("Conversion to CString failed unexpectedly"); @@ -715,6 +798,43 @@ impl Module { // REVIEW: Should we return a MetadataValue for the global since it's its own value? // it would be the last item in get_global_metadata I believe // TODOC: Appends your metadata to a global MetadataValue indexed by key + /// Appends a `MetaDataValue` to a global list indexed by a particular key. + /// + /// # Example + /// + /// ```no_run + /// use inkwell::context::Context; + /// use inkwell::values::MetadataValue; + /// + /// let context = Context::create(); + /// let module = context.create_module("my_module"); + /// let bool_type = context.bool_type(); + /// let f32_type = context.f32_type(); + /// let bool_val = bool_type.const_int(0, false); + /// let f32_val = f32_type.const_float(0.0); + /// + /// assert_eq!(module.get_global_metadata_size("my_md"), 0); + /// + /// let md_string = MetadataValue::create_string("lots of metadata here"); + /// let md_node = MetadataValue::create_node(&[&bool_val, &f32_val]); + /// + /// module.add_global_metadata("my_md", &md_string); + /// module.add_global_metadata("my_md", &md_node); + /// + /// assert_eq!(module.get_global_metadata_size("my_md"), 2); + /// + /// let global_md = module.get_global_metadata("my_md"); + /// + /// assert_eq!(global_md.len(), 2); + /// + /// let (md_0, md_1) = (global_md[0].get_node_values(), global_md[1].get_node_values()); + /// + /// assert_eq!(md_0.len(), 1); + /// assert_eq!(md_1.len(), 2); + /// assert_eq!(md_0[0].as_metadata_value().get_string_value(), md_string.get_string_value()); + /// assert_eq!(md_1[0].as_int_value(), &bool_val); + /// assert_eq!(md_1[1].as_float_value(), &f32_val); + /// ``` pub fn add_global_metadata(&self, key: &str, metadata: &MetadataValue) { let c_string = CString::new(key).expect("Conversion to CString failed unexpectedly"); @@ -722,8 +842,45 @@ impl Module { LLVMAddNamedMetadataOperand(self.module.get(), c_string.as_ptr(), metadata.as_value_ref()) } } - // REVIEW: Better name? - // TODOC: Gets the size of the metadata node indexed by key + + // REVIEW: Better name? get_global_metadata_len or _count? + /// Obtains the number of `MetaDataValue`s indexed by a particular key. + /// + /// # Example + /// + /// ```no_run + /// use inkwell::context::Context; + /// use inkwell::values::MetadataValue; + /// + /// let context = Context::create(); + /// let module = context.create_module("my_module"); + /// let bool_type = context.bool_type(); + /// let f32_type = context.f32_type(); + /// let bool_val = bool_type.const_int(0, false); + /// let f32_val = f32_type.const_float(0.0); + /// + /// assert_eq!(module.get_global_metadata_size("my_md"), 0); + /// + /// let md_string = MetadataValue::create_string("lots of metadata here"); + /// let md_node = MetadataValue::create_node(&[&bool_val, &f32_val]); + /// + /// module.add_global_metadata("my_md", &md_string); + /// module.add_global_metadata("my_md", &md_node); + /// + /// assert_eq!(module.get_global_metadata_size("my_md"), 2); + /// + /// let global_md = module.get_global_metadata("my_md"); + /// + /// assert_eq!(global_md.len(), 2); + /// + /// let (md_0, md_1) = (global_md[0].get_node_values(), global_md[1].get_node_values()); + /// + /// assert_eq!(md_0.len(), 1); + /// assert_eq!(md_1.len(), 2); + /// assert_eq!(md_0[0].as_metadata_value().get_string_value(), md_string.get_string_value()); + /// assert_eq!(md_1[0].as_int_value(), &bool_val); + /// assert_eq!(md_1[1].as_float_value(), &f32_val); + /// ``` pub fn get_global_metadata_size(&self, key: &str) -> u32 { let c_string = CString::new(key).expect("Conversion to CString failed unexpectedly"); @@ -732,8 +889,44 @@ impl Module { } } - // TODOC: Always returns a metadata node indexed by key, which may contain 1 string or multiple values as its get_node_values() // SubTypes: -> Vec> + /// Obtains the global `MetaDataValue` node indexed by key, which may contain 1 string or multiple values as its `get_node_values()` + /// + /// # Example + /// + /// ```no_run + /// use inkwell::context::Context; + /// use inkwell::values::MetadataValue; + /// + /// let context = Context::create(); + /// let module = context.create_module("my_module"); + /// let bool_type = context.bool_type(); + /// let f32_type = context.f32_type(); + /// let bool_val = bool_type.const_int(0, false); + /// let f32_val = f32_type.const_float(0.0); + /// + /// assert_eq!(module.get_global_metadata_size("my_md"), 0); + /// + /// let md_string = MetadataValue::create_string("lots of metadata here"); + /// let md_node = MetadataValue::create_node(&[&bool_val, &f32_val]); + /// + /// module.add_global_metadata("my_md", &md_string); + /// module.add_global_metadata("my_md", &md_node); + /// + /// assert_eq!(module.get_global_metadata_size("my_md"), 2); + /// + /// let global_md = module.get_global_metadata("my_md"); + /// + /// assert_eq!(global_md.len(), 2); + /// + /// let (md_0, md_1) = (global_md[0].get_node_values(), global_md[1].get_node_values()); + /// + /// assert_eq!(md_0.len(), 1); + /// assert_eq!(md_1.len(), 2); + /// assert_eq!(md_0[0].as_metadata_value().get_string_value(), md_string.get_string_value()); + /// assert_eq!(md_1[0].as_int_value(), &bool_val); + /// assert_eq!(md_1[1].as_float_value(), &f32_val); + /// ``` pub fn get_global_metadata(&self, key: &str) -> Vec { let c_string = CString::new(key).expect("Conversion to CString failed unexpectedly"); let count = self.get_global_metadata_size(key); diff --git a/src/support/mod.rs b/src/support/mod.rs index 8c50defc482..75a59504e05 100644 --- a/src/support/mod.rs +++ b/src/support/mod.rs @@ -141,3 +141,14 @@ pub fn is_multithreaded() -> bool { LLVMIsMultithreaded() == 1 } } + +pub fn enable_llvm_pretty_stack_trace() { + #[cfg(any(feature = "llvm3-6", feature = "llvm3-7"))] + use llvm_sys::core::LLVMEnablePrettyStackTrace; + #[cfg(any(feature = "llvm3-8", feature = "llvm3-9", feature = "llvm4-0", feature = "llvm5-0", feature = "llvm6-0"))] + use llvm_sys::error_handling::LLVMEnablePrettyStackTrace; + + unsafe { + LLVMEnablePrettyStackTrace() + } +} diff --git a/src/targets.rs b/src/targets.rs index 3b194fb9ec5..379017d2d11 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -800,7 +800,7 @@ impl TargetMachine { } } - pub fn get_target(&self)-> Target { + pub fn get_target(&self) -> Target { let target = unsafe { LLVMGetTargetMachineTarget(self.target_machine) }; @@ -816,6 +816,19 @@ impl TargetMachine { LLVMString::new(ptr) } + /// Gets the default triple for the current system. + /// + /// # Example + /// + /// ```no_run + /// use inkwell::targets::TargetMachine; + /// + /// use std::ffi::CString; + /// + /// let default_triple = TargetMachine::get_default_triple(); + /// + /// assert_eq!(*default_triple, *CString::new("x86_64-pc-linux-gnu").unwrap()); + /// ``` pub fn get_default_triple() -> LLVMString { let llvm_string = unsafe { LLVMGetDefaultTargetTriple() @@ -851,6 +864,32 @@ impl TargetMachine { } } + /// Writes a `TargetMachine` to a `MemoryBuffer`. + /// + /// # Example + /// + /// ```no_run + /// use inkwell::OptimizationLevel; + /// use inkwell::context::Context; + /// use inkwell::targets::{CodeModel, RelocMode, FileType, Target, TargetMachine, InitializationConfig}; + /// + /// Target::initialize_x86(&InitializationConfig::default()); + /// + /// let opt = OptimizationLevel::Default; + /// let reloc = RelocMode::Default; + /// let model = CodeModel::Default; + /// let target = Target::from_name("x86-64").unwrap(); + /// let target_machine = target.create_target_machine("x86_64-pc-linux-gnu", "x86-64", "+avx2", opt, reloc, model).unwrap(); + /// + /// let context = Context::create(); + /// let module = context.create_module("my_module"); + /// let void_type = context.void_type(); + /// let fn_type = void_type.fn_type(&[], false); + /// + /// module.add_function("my_fn", &fn_type, None); + /// + /// let buffer = target_machine.write_to_memory_buffer(&module, FileType::Assembly).unwrap(); + /// ``` pub fn write_to_memory_buffer(&self, module: &Module, file_type: FileType) -> Result { let mut memory_buffer = ptr::null_mut(); let mut err_string = unsafe { zeroed() }; @@ -868,6 +907,35 @@ impl TargetMachine { Ok(MemoryBuffer::new(memory_buffer)) } + /// Saves a `TargetMachine` to a file. + /// + /// # Example + /// + /// ```no_run + /// use inkwell::OptimizationLevel; + /// use inkwell::context::Context; + /// use inkwell::targets::{CodeModel, RelocMode, FileType, Target, TargetMachine, InitializationConfig}; + /// + /// use std::path::Path; + /// + /// Target::initialize_x86(&InitializationConfig::default()); + /// + /// let opt = OptimizationLevel::Default; + /// let reloc = RelocMode::Default; + /// let model = CodeModel::Default; + /// let path = Path::new("/tmp/some/path/main.asm"); + /// let target = Target::from_name("x86-64").unwrap(); + /// let target_machine = target.create_target_machine("x86_64-pc-linux-gnu", "x86-64", "+avx2", opt, reloc, model).unwrap(); + /// + /// let context = Context::create(); + /// let module = context.create_module("my_module"); + /// let void_type = context.void_type(); + /// let fn_type = void_type.fn_type(&[], false); + /// + /// module.add_function("my_fn", &fn_type, None); + /// + /// assert!(target_machine.write_to_file(&module, FileType::Object, &path).is_ok()); + /// ``` pub fn write_to_file(&self, module: &Module, file_type: FileType, path: &Path) -> Result<(), LLVMString> { let path = path.to_str().expect("Did not find a valid Unicode path string"); let path_c_string = CString::new(path).expect("Conversion to CString failed unexpectedly"); @@ -919,6 +987,8 @@ impl TargetData { /// Gets the `IntType` representing a bit width of a pointer. It will be assigned the global context. /// + /// # Example + /// /// ```no_run /// use inkwell::OptimizationLevel; /// use inkwell::context::Context; @@ -943,6 +1013,7 @@ impl TargetData { /// Gets the `IntType` representing a bit width of a pointer. It will be assigned the referenced context. /// + /// # Example /// /// ```no_run /// use inkwell::OptimizationLevel; diff --git a/tests/all/test_targets.rs b/tests/all/test_targets.rs index 2626260a05e..260b8b4453a 100644 --- a/tests/all/test_targets.rs +++ b/tests/all/test_targets.rs @@ -289,7 +289,7 @@ fn test_write_target_machine_to_file() { Target::initialize_x86(&InitializationConfig::default()); let target = Target::from_name("x86-64").unwrap(); - let target_machine = target.create_target_machine("x86_64-pc-linux-gnu", "x86-64", "+avx2", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default).unwrap(); + let target_machine = target.create_target_machine("x86_64-pc-linux-gnu", "x86-64", "+avx2", OptimizationLevel::Less, RelocMode::Static, CodeModel::Small).unwrap(); let mut path = temp_dir(); path.push("temp.asm"); @@ -325,7 +325,7 @@ fn test_write_target_machine_to_memory_buffer() { Target::initialize_x86(&InitializationConfig::default()); let target = Target::from_name("x86-64").unwrap(); - let target_machine = target.create_target_machine("x86_64-pc-linux-gnu", "x86-64", "+avx2", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default).unwrap(); + let target_machine = target.create_target_machine("x86_64-pc-linux-gnu", "x86-64", "+avx2", OptimizationLevel::Aggressive, RelocMode::PIC, CodeModel::Medium).unwrap(); let context = Context::create(); let module = context.create_module("my_module");