Skip to content

Commit

Permalink
Added initial support for attributes, did some cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
TheDan64 committed Sep 2, 2018
1 parent 28a1de3 commit 8ecc815
Show file tree
Hide file tree
Showing 16 changed files with 406 additions and 22 deletions.
191 changes: 191 additions & 0 deletions src/attributes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
//! An `Attribute` are optional modifiers to functions, function parameters, and return types.

use llvm_sys::prelude::LLVMAttributeRef;
use llvm_sys::core::{LLVMGetEnumAttributeKindForName, LLVMGetLastEnumAttributeKind, LLVMGetEnumAttributeKind, LLVMGetEnumAttributeValue, LLVMGetStringAttributeKind, LLVMGetStringAttributeValue, LLVMIsEnumAttribute, LLVMIsStringAttribute};

use std::ffi::CStr;

// SubTypes: Attribute<Enum>, Attribute<String>
/// Functions, function parameters, and return types can have `Attribute`s to indicate
/// how they should be treated by optimizations and code generation.
#[derive(Debug)]
pub struct Attribute {
attribute: LLVMAttributeRef,
}

impl Attribute {
pub(crate) fn new(attribute: LLVMAttributeRef) -> Self {
debug_assert!(!attribute.is_null());

Attribute {
attribute,
}
}

/// Determines whether or not an `Attribute` is an enum. This method will
/// likely be removed in the future in favor of `Attribute`s being generically
/// defined.
///
/// # Example
///
/// ```no_run
/// use inkwell::context::Context;
///
/// let context = Context::create();
/// let enum_attribute = context.create_enum_attribute(0, 10);
///
/// assert!(enum_attribute.is_enum());
/// ```
pub fn is_enum(&self) -> bool {
unsafe {
LLVMIsEnumAttribute(self.attribute) == 1
}
}

/// Determines whether or not an `Attribute` is a string. This method will
/// likely be removed in the future in favor of `Attribute`s being generically
/// defined.
///
/// # Example
///
/// ```no_run
/// use inkwell::context::Context;
///
/// let context = Context::create();
/// let string_attribute = context.create_string_attribute("my_key_123", "my_val");
///
/// assert!(string_attribute.is_string());
/// ```
pub fn is_string(&self) -> bool {
unsafe {
LLVMIsStringAttribute(self.attribute) == 1
}
}

/// Gets the enum kind id associated with a builtin name.
///
/// # Example
///
/// ```no_run
/// use inkwell::attributes::Attribute;
///
/// // This kind id doesn't exist:
/// assert_eq!(Attribute::get_named_enum_kind_id("foobar"), 0);
///
/// // These are real kind ids:
/// assert_eq!(Attribute::get_named_enum_kind_id("align"), 1);
/// assert_eq!(Attribute::get_named_enum_kind_id("builtin"), 5);
/// ```
pub fn get_named_enum_kind_id(name: &str) -> u32 {
unsafe {
LLVMGetEnumAttributeKindForName(name.as_ptr() as *const i8, name.len())
}
}

/// Gets the kind id associated with an enum `Attribute`.
///
/// # Example
///
/// ```no_run
/// use inkwell::context::Context;
///
/// let context = Context::create();
/// let enum_attribute = context.create_enum_attribute(0, 10);
///
/// assert_eq!(enum_attribute.get_enum_kind_id(), 0);
/// ```
pub fn get_enum_kind_id(&self) -> u32 {
assert!(self.is_enum()); // FIXME: SubTypes

unsafe {
LLVMGetEnumAttributeKind(self.attribute)
}
}

/// Gets the last enum kind id associated with builtin names.
///
/// # Example
///
/// ```no_run
/// use inkwell::attributes::Attribute;
///
/// assert_eq!(Attribute::get_last_enum_kind_id(), 56);
/// ```
pub fn get_last_enum_kind_id() -> u32 {
unsafe {
LLVMGetLastEnumAttributeKind()
}
}

/// Gets the value associated with an enum `Attribute`.
///
/// # Example
///
/// ```no_run
/// use inkwell::context::Context;
///
/// let context = Context::create();
/// let enum_attribute = context.create_enum_attribute(0, 10);
///
/// assert_eq!(enum_attribute.get_enum_value(), 10);
/// ```
pub fn get_enum_value(&self) -> u64 {
assert!(self.is_enum()); // FIXME: SubTypes

unsafe {
LLVMGetEnumAttributeValue(self.attribute)
}
}

/// Gets the string kind id associated with a string attribute.
///
/// # Example
///
/// ```no_run
/// use inkwell::context::Context;
/// use std::ffi::CString;
///
/// let context = Context::create();
/// let string_attribute = context.create_string_attribute("my_key", "my_val");
///
/// assert_eq!(*string_attribute.get_string_kind_id(), *CString::new("my_key").unwrap());
/// ```
pub fn get_string_kind_id(&self) -> &CStr {
assert!(self.is_string()); // FIXME: SubTypes

let mut length = 0;
let cstr_ptr = unsafe {
LLVMGetStringAttributeKind(self.attribute, &mut length)
};

unsafe {
CStr::from_ptr(cstr_ptr)
}
}

/// Gets the string value associated with a string attribute.
///
/// # Example
///
/// ```no_run
/// use inkwell::context::Context;
/// use std::ffi::CString;
///
/// let context = Context::create();
/// let string_attribute = context.create_string_attribute("my_key", "my_val");
///
/// assert_eq!(*string_attribute.get_string_value(), *CString::new("my_val").unwrap());
/// ```
pub fn get_string_value(&self) -> &CStr {
assert!(self.is_string()); // FIXME: SubTypes

let mut length = 0;
let cstr_ptr = unsafe {
LLVMGetStringAttributeValue(self.attribute, &mut length)
};

unsafe {
CStr::from_ptr(cstr_ptr)
}
}
}
27 changes: 23 additions & 4 deletions src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use either::Either;
use llvm_sys::core::{LLVMBuildAdd, LLVMBuildAlloca, LLVMBuildAnd, LLVMBuildArrayAlloca, LLVMBuildArrayMalloc, LLVMBuildBr, LLVMBuildCall, LLVMBuildCast, LLVMBuildCondBr, LLVMBuildExtractValue, LLVMBuildFAdd, LLVMBuildFCmp, LLVMBuildFDiv, LLVMBuildFence, LLVMBuildFMul, LLVMBuildFNeg, LLVMBuildFree, LLVMBuildFSub, LLVMBuildGEP, LLVMBuildICmp, LLVMBuildInsertValue, LLVMBuildIsNotNull, LLVMBuildIsNull, LLVMBuildLoad, LLVMBuildMalloc, LLVMBuildMul, LLVMBuildNeg, LLVMBuildNot, LLVMBuildOr, LLVMBuildPhi, LLVMBuildPointerCast, LLVMBuildRet, LLVMBuildRetVoid, LLVMBuildStore, LLVMBuildSub, LLVMBuildUDiv, LLVMBuildUnreachable, LLVMBuildXor, LLVMDisposeBuilder, LLVMGetElementType, LLVMGetInsertBlock, LLVMGetReturnType, LLVMGetTypeKind, LLVMInsertIntoBuilder, LLVMPositionBuilderAtEnd, LLVMTypeOf, LLVMSetTailCall, LLVMBuildExtractElement, LLVMBuildInsertElement, LLVMBuildIntToPtr, LLVMBuildPtrToInt, LLVMInsertIntoBuilderWithName, LLVMClearInsertionPosition, LLVMCreateBuilder, LLVMPositionBuilder, LLVMPositionBuilderBefore, LLVMBuildAggregateRet, LLVMBuildStructGEP, LLVMBuildInBoundsGEP, LLVMBuildPtrDiff, LLVMBuildNSWAdd, LLVMBuildNUWAdd, LLVMBuildNSWSub, LLVMBuildNUWSub, LLVMBuildNSWMul, LLVMBuildNUWMul, LLVMBuildSDiv, LLVMBuildSRem, LLVMBuildURem, LLVMBuildFRem, LLVMBuildNSWNeg, LLVMBuildNUWNeg, LLVMBuildFPToUI, LLVMBuildFPToSI, LLVMBuildSIToFP, LLVMBuildUIToFP, LLVMBuildFPTrunc, LLVMBuildFPExt, LLVMBuildIntCast, LLVMBuildFPCast, LLVMBuildSExtOrBitCast, LLVMBuildZExtOrBitCast, LLVMBuildTruncOrBitCast, LLVMBuildSwitch, LLVMAddCase, LLVMBuildShl, LLVMBuildAShr, LLVMBuildLShr, LLVMBuildGlobalString, LLVMBuildGlobalStringPtr, LLVMBuildExactSDiv, LLVMBuildTrunc, LLVMBuildSExt, LLVMBuildZExt};
use llvm_sys::core::{LLVMBuildAdd, LLVMBuildAlloca, LLVMBuildAnd, LLVMBuildArrayAlloca, LLVMBuildArrayMalloc, LLVMBuildBr, LLVMBuildCall, LLVMBuildCast, LLVMBuildCondBr, LLVMBuildExtractValue, LLVMBuildFAdd, LLVMBuildFCmp, LLVMBuildFDiv, LLVMBuildFence, LLVMBuildFMul, LLVMBuildFNeg, LLVMBuildFree, LLVMBuildFSub, LLVMBuildGEP, LLVMBuildICmp, LLVMBuildInsertValue, LLVMBuildIsNotNull, LLVMBuildIsNull, LLVMBuildLoad, LLVMBuildMalloc, LLVMBuildMul, LLVMBuildNeg, LLVMBuildNot, LLVMBuildOr, LLVMBuildPhi, LLVMBuildPointerCast, LLVMBuildRet, LLVMBuildRetVoid, LLVMBuildStore, LLVMBuildSub, LLVMBuildUDiv, LLVMBuildUnreachable, LLVMBuildXor, LLVMDisposeBuilder, LLVMGetElementType, LLVMGetInsertBlock, LLVMGetReturnType, LLVMGetTypeKind, LLVMInsertIntoBuilder, LLVMPositionBuilderAtEnd, LLVMTypeOf, LLVMSetTailCall, LLVMBuildExtractElement, LLVMBuildInsertElement, LLVMBuildIntToPtr, LLVMBuildPtrToInt, LLVMInsertIntoBuilderWithName, LLVMClearInsertionPosition, LLVMCreateBuilder, LLVMPositionBuilder, LLVMPositionBuilderBefore, LLVMBuildAggregateRet, LLVMBuildStructGEP, LLVMBuildInBoundsGEP, LLVMBuildPtrDiff, LLVMBuildNSWAdd, LLVMBuildNUWAdd, LLVMBuildNSWSub, LLVMBuildNUWSub, LLVMBuildNSWMul, LLVMBuildNUWMul, LLVMBuildSDiv, LLVMBuildSRem, LLVMBuildURem, LLVMBuildFRem, LLVMBuildNSWNeg, LLVMBuildNUWNeg, LLVMBuildFPToUI, LLVMBuildFPToSI, LLVMBuildSIToFP, LLVMBuildUIToFP, LLVMBuildFPTrunc, LLVMBuildFPExt, LLVMBuildIntCast, LLVMBuildFPCast, LLVMBuildSExtOrBitCast, LLVMBuildZExtOrBitCast, LLVMBuildTruncOrBitCast, LLVMBuildSwitch, LLVMAddCase, LLVMBuildShl, LLVMBuildAShr, LLVMBuildLShr, LLVMBuildGlobalString, LLVMBuildGlobalStringPtr, LLVMBuildExactSDiv, LLVMBuildTrunc, LLVMBuildSExt, LLVMBuildZExt, LLVMBuildSelect};
use llvm_sys::prelude::{LLVMBuilderRef, LLVMValueRef};
use llvm_sys::{LLVMTypeKind, LLVMAtomicOrdering};

Expand Down Expand Up @@ -117,6 +117,7 @@ impl Builder {
}

// REVIEW: Doesn't GEP work on array too?
// REVIEW: This could be merge in with build_gep via a in_bounds: bool param
/// GEP is very likely to segfault if indexes are used incorrectly, and is therefore an unsafe function. Maybe we can change this in the future.
pub unsafe fn build_in_bounds_gep(&self, ptr: PointerValue, ordered_indexes: &[IntValue], name: &str) -> PointerValue {
let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");
Expand All @@ -130,6 +131,8 @@ impl Builder {
}

// REVIEW: Shouldn't this take a StructValue? Or does it still need to be PointerValue<StructValue>?
// I think it's the latter. This might not be as unsafe as regular GEP. Should check to see if it lets us
// go OOB. Maybe we could use the PointerValue<StructValue>'s struct info to do bounds checking...
/// GEP is very likely to segfault if indexes are used incorrectly, and is therefore an unsafe function. Maybe we can change this in the future.
pub unsafe fn build_struct_gep(&self, ptr: PointerValue, index: u32, name: &str) -> PointerValue {
let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");
Expand All @@ -149,6 +152,11 @@ impl Builder {
IntValue::new(value)
}

// SubTypes: Maybe this should return PhiValue<T>? That way we could force incoming values to be of T::Value?
// That is, assuming LLVM complains about different phi types.. which I imagine it would. But this would get
// tricky with VoidType since it has no instance value?
// TODOC: Phi Instruction(s) must be first instruction(s) in a BasicBlock.
// REVIEW: Not sure if we can enforce the above somehow via types.
pub fn build_phi<T: BasicType>(&self, type_: T, name: &str) -> PhiValue {
let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");

Expand Down Expand Up @@ -967,11 +975,12 @@ impl Builder {

// REVIEW: Not sure if this should return InstructionValue or an actual value
// TODO: Better name for num?
pub fn build_fence(&self, atmoic_ordering: LLVMAtomicOrdering, num: i32, name: &str) -> InstructionValue {
// FIXME: Shouldn't use llvm_sys enum
pub fn build_fence(&self, atomic_ordering: LLVMAtomicOrdering, num: i32, name: &str) -> InstructionValue {
let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");

let val = unsafe {
LLVMBuildFence(self.builder, atmoic_ordering, num, c_string.as_ptr())
LLVMBuildFence(self.builder, atomic_ordering, num, c_string.as_ptr())
};

InstructionValue::new(val)
Expand Down Expand Up @@ -1030,7 +1039,7 @@ impl Builder {
// REVIEW: Returning InstructionValue is the safe move here; but if the value means something
// (IE the result of the switch) it should probably return BasicValueEnum?
// SubTypes: I think value and case values must be the same subtype (maybe). Case value might need to be constants
pub fn build_switch(&self, value: &IntValue, else_block: &BasicBlock, cases: &[(&IntValue, &BasicBlock)]) -> InstructionValue {
pub fn build_switch(&self, value: IntValue, else_block: &BasicBlock, cases: &[(IntValue, &BasicBlock)]) -> InstructionValue {
let switch_value = unsafe {
LLVMBuildSwitch(self.builder, value.as_value_ref(), else_block.basic_block, cases.len() as u32)
};
Expand All @@ -1044,6 +1053,16 @@ impl Builder {
InstructionValue::new(switch_value)
}

// SubTypes: condition can only be IntValue<bool> or VectorValue<IntValue<Bool>>
pub fn build_select<BV: BasicValue, IMV: IntMathValue>(&self, condition: IMV, then: BV, else_: BV, name: &str) -> BasicValueEnum {
let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");
let value = unsafe {
LLVMBuildSelect(self.builder, condition.as_value_ref(), then.as_value_ref(), else_.as_value_ref(), c_string.as_ptr())
};

BasicValueEnum::new(value)
}

// The unsafety of this function should be fixable with subtypes. See GH #32
pub unsafe fn build_global_string(&self, value: &str, name: &str) -> GlobalValue {
let c_string_value = CString::new(value).expect("Conversion to CString failed unexpectedly");
Expand Down
26 changes: 26 additions & 0 deletions src/context.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
//! A `Context` is an opaque owner and manager of core global data.

use llvm_sys::core::{LLVMAppendBasicBlockInContext, LLVMContextCreate, LLVMContextDispose, LLVMCreateBuilderInContext, LLVMDoubleTypeInContext, LLVMFloatTypeInContext, LLVMFP128TypeInContext, LLVMInsertBasicBlockInContext, LLVMInt16TypeInContext, LLVMInt1TypeInContext, LLVMInt32TypeInContext, LLVMInt64TypeInContext, LLVMInt8TypeInContext, LLVMIntTypeInContext, LLVMModuleCreateWithNameInContext, LLVMStructCreateNamed, LLVMStructTypeInContext, LLVMVoidTypeInContext, LLVMHalfTypeInContext, LLVMGetGlobalContext, LLVMPPCFP128TypeInContext, LLVMConstStructInContext, LLVMMDNodeInContext, LLVMMDStringInContext, LLVMGetMDKindIDInContext, LLVMX86FP80TypeInContext};
#[cfg(not(any(feature = "llvm3-6", feature = "llvm3-7", feature = "llvm3-8", feature = "llvm3-9")))]
use llvm_sys::core::{LLVMCreateEnumAttribute, LLVMCreateStringAttribute};
use llvm_sys::prelude::{LLVMContextRef, LLVMTypeRef, LLVMValueRef};
use llvm_sys::ir_reader::LLVMParseIRInContext;

#[cfg(not(any(feature = "llvm3-6", feature = "llvm3-7", feature = "llvm3-8", feature = "llvm3-9")))]
use attributes::Attribute;
use basic_block::BasicBlock;
use builder::Builder;
use memory_buffer::MemoryBuffer;
Expand Down Expand Up @@ -715,6 +719,28 @@ impl Context {

// DiagnosticHandler::new(handler)
// }

/// Creates an enum `Attribute`.
// TODO: Better docs
#[cfg(not(any(feature = "llvm3-6", feature = "llvm3-7", feature = "llvm3-8", feature = "llvm3-9")))]
pub fn create_enum_attribute(&self, kind_id: u32, val: u64) -> Attribute {
let attribute = unsafe {
LLVMCreateEnumAttribute(*self.context, kind_id, val)
};

Attribute::new(attribute)
}

/// Creates a string `Attribute`
// TODO: Better docs
#[cfg(not(any(feature = "llvm3-6", feature = "llvm3-7", feature = "llvm3-8", feature = "llvm3-9")))]
pub fn create_string_attribute(&self, key: &str, val: &str) -> Attribute {
let attribute = unsafe {
LLVMCreateStringAttribute(*self.context, key.as_ptr() as *const _, key.len() as u32, val.as_ptr() as *const _, val.len() as u32)
};

Attribute::new(attribute)
}
}

impl Drop for Context {
Expand Down
10 changes: 7 additions & 3 deletions src/execution_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use values::{AnyValue, AsValueRef, FunctionValue, GenericValue};
use std::error::Error;
use std::rc::Rc;
use std::ops::Deref;
use std::ffi::{CStr, CString};
use std::ffi::CString;
use std::fmt::{self, Debug, Display, Formatter};
use std::mem::{forget, uninitialized, zeroed, transmute_copy, size_of};
use std::mem::{forget, zeroed, transmute_copy, size_of};

#[derive(Debug, PartialEq, Eq)]
pub enum FunctionLookupError {
Expand Down Expand Up @@ -110,12 +110,14 @@ impl ExecutionEngine {
}
}

// REVIEW: Should we support this function?
pub fn link_in_mc_jit() {
unsafe {
LLVMLinkInMCJIT()
}
}

// REVIEW: Should we support this function?
pub fn link_in_interpreter() {
unsafe {
LLVMLinkInInterpreter();
Expand Down Expand Up @@ -206,7 +208,7 @@ impl ExecutionEngine {
_ => ()
}

let mut new_module = unsafe { uninitialized() };
let mut new_module = unsafe { zeroed() };
let mut err_string = unsafe { zeroed() };

let code = unsafe {
Expand Down Expand Up @@ -371,12 +373,14 @@ impl ExecutionEngine {
}
}

// REVIEW: Is this actually safe?
pub fn run_static_constructors(&self) {
unsafe {
LLVMRunStaticConstructors(*self.execution_engine)
}
}

// REVIEW: Is this actually safe? Can you double destruct/free?
pub fn run_static_destructors(&self) {
unsafe {
LLVMRunStaticDestructors(*self.execution_engine)
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ extern crate enum_methods;
extern crate libc;
extern crate llvm_sys;

#[deny(missing_docs)]
#[cfg(not(any(feature = "llvm3-6", feature = "llvm3-7", feature = "llvm3-8", feature = "llvm3-9")))]
pub mod attributes;
#[deny(missing_docs)]
pub mod basic_block;
pub mod builder;
Expand Down
2 changes: 1 addition & 1 deletion src/memory_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use llvm_sys::object::LLVMCreateObjectFile;
use object_file::ObjectFile;
use support::LLVMString;

use std::ffi::{CString, CStr};
use std::ffi::CString;
use std::mem::{forget, zeroed};
use std::path::Path;
use std::ptr;
Expand Down
Loading

0 comments on commit 8ecc815

Please sign in to comment.