Skip to content

Commit

Permalink
add test for inlinealways function attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
folkertdev committed Jun 26, 2023
1 parent ec963b9 commit 92023e3
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 17 deletions.
73 changes: 72 additions & 1 deletion src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,70 @@ use crate::types::AnyTypeEnum;
// REVIEW: Should Attributes have a 'ctx lifetime?
/// Functions, function parameters, and return types can have `Attribute`s to indicate
/// how they should be treated by optimizations and code generation.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy)]
pub struct Attribute {
pub(crate) attribute: LLVMAttributeRef,
}

impl std::fmt::Debug for Attribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_string() {
return f
.debug_struct("Attribute::String")
.field("ptr", &self.attribute)
.field("kind_id", &self.get_string_kind_id())
.field("value", &self.get_string_value())
.finish();
}

if self.is_enum() {
return f
.debug_struct("Attribute::Enum")
.field("ptr", &self.attribute)
.field("kind_id", &self.get_enum_kind_id())
.field("value", &self.get_enum_value())
.finish();
}

if self.is_type() {
return f
.debug_struct("Attribute::Type")
.field("ptr", &self.attribute)
.field("kind_id", &self.get_enum_kind_id())
.field("value", &self.get_type_value())
.finish();
}

unreachable!(
"attribute at {:?} is not a string, enum or type attribute",
self.attribute
);
}
}

impl Eq for Attribute {}

impl PartialEq<Self> for Attribute {
fn eq(&self, other: &Self) -> bool {
if self.is_enum() && other.is_enum() {
return self.get_enum_kind_id() == other.get_enum_kind_id()
&& self.get_enum_value() == other.get_enum_value();
}

if self.is_string() && other.is_string() {
return self.get_string_kind_id() == other.get_string_kind_id()
&& self.get_string_value() == other.get_string_value();
}

if self.is_type() && other.is_type() {
return self.get_enum_kind_id() == other.get_enum_kind_id()
&& self.get_type_value() == other.get_type_value();
}

self.attribute == other.attribute
}
}

impl Attribute {
/// Creates a new `Attribute` from a raw pointer.
pub unsafe fn new(attribute: LLVMAttributeRef) -> Self {
Expand Down Expand Up @@ -95,6 +154,12 @@ impl Attribute {
unsafe { LLVMIsTypeAttribute(self.attribute) == 1 }
}

// private function to make code elsewhere easier
#[llvm_versions(4.0..12.0)]
fn is_type(self) -> bool {
false
}

/// Gets the enum kind id associated with a builtin name.
///
/// # Example
Expand Down Expand Up @@ -280,6 +345,12 @@ impl Attribute {

unsafe { AnyTypeEnum::new(LLVMGetTypeAttributeValue(self.attribute)) }
}

// private function to make code elsewhere easier
#[llvm_versions(4.0..12.0)]
fn get_type_value(&self) {
unreachable!("not implemented in this version")
}
}

/// An `AttributeLoc` determines where on a function an attribute is assigned to.
Expand Down
56 changes: 40 additions & 16 deletions tests/all/test_attributes.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use inkwell::attributes::{Attribute, AttributeLoc};
use inkwell::context::Context;
use inkwell::values::BasicValueEnum;
use inkwell::AddressSpace;

#[test]
fn test_enum_attribute_kinds() {
Expand Down Expand Up @@ -78,56 +80,78 @@ fn test_attributes_on_function_values() {
let context = Context::create();
let builder = context.create_builder();
let module = context.create_module("my_mod");
let void_type = context.void_type();
let i32_type = context.i32_type();
let fn_type = void_type.fn_type(&[i32_type.into()], false);
let i32_ptr_type = context.i32_type().ptr_type(AddressSpace::default());
let fn_type = i32_ptr_type.fn_type(&[i32_ptr_type.into()], false);
let fn_value = module.add_function("my_fn", fn_type, None);
let entry_bb = context.append_basic_block(fn_value, "entry");
let string_attribute = context.create_string_attribute("my_key", "my_val");
let alignstack_attribute = Attribute::get_named_enum_kind_id("alignstack");
let enum_attribute = context.create_enum_attribute(alignstack_attribute, 1);

let alwaysinline_attribute = Attribute::get_named_enum_kind_id("alwaysinline");
let function_enum_attribute = context.create_enum_attribute(alwaysinline_attribute, 0);

let inreg_attribute = Attribute::get_named_enum_kind_id("noalias");
let return_enum_attribute = context.create_enum_attribute(inreg_attribute, 0);

builder.position_at_end(entry_bb);
builder.build_return(None);
let null: BasicValueEnum = i32_ptr_type.const_null().into();
builder.build_return(Some(&null));

assert_eq!(fn_value.count_attributes(AttributeLoc::Return), 0);
assert_eq!(fn_value.count_attributes(AttributeLoc::Param(0)), 0);
assert_eq!(fn_value.attributes(AttributeLoc::Return), vec![]);
assert_eq!(fn_value.attributes(AttributeLoc::Param(0)), vec![]);

fn_value.remove_string_attribute(AttributeLoc::Return, "my_key"); // Noop
fn_value.remove_enum_attribute(AttributeLoc::Return, alignstack_attribute); // Noop

// define align 1 "my_key"="my_val" void @my_fn()
fn_value.remove_enum_attribute(AttributeLoc::Return, inreg_attribute); // Noop
fn_value.remove_enum_attribute(AttributeLoc::Function, alwaysinline_attribute); // Noop

// ; Function Attrs: alwaysinline
// define inreg "my_key"="my_val" void @my_fn(i32 "my_key"="my_val" %0) #0 {
// entry:
// ret void
// }
//
// attributes #0 = { alwaysinline }
fn_value.add_attribute(AttributeLoc::Return, string_attribute);
fn_value.add_attribute(AttributeLoc::Param(0), string_attribute); // Applied to 1st param
fn_value.add_attribute(AttributeLoc::Return, enum_attribute);
fn_value.add_attribute(AttributeLoc::Return, return_enum_attribute);
fn_value.add_attribute(AttributeLoc::Function, function_enum_attribute);

module.print_to_stderr();

module.verify().unwrap();

assert_eq!(fn_value.count_attributes(AttributeLoc::Return), 2);
assert_eq!(
fn_value.get_enum_attribute(AttributeLoc::Return, alignstack_attribute),
Some(enum_attribute)
fn_value.get_enum_attribute(AttributeLoc::Return, inreg_attribute),
Some(return_enum_attribute)
);
assert_eq!(
fn_value.get_string_attribute(AttributeLoc::Return, "my_key"),
Some(string_attribute)
);
assert_eq!(
fn_value.attributes(AttributeLoc::Return),
vec![enum_attribute, string_attribute]
vec![return_enum_attribute, string_attribute]
);

fn_value.remove_string_attribute(AttributeLoc::Return, "my_key");

assert_eq!(fn_value.count_attributes(AttributeLoc::Return), 1);
assert_eq!(fn_value.attributes(AttributeLoc::Return), vec![enum_attribute]);
assert_eq!(fn_value.attributes(AttributeLoc::Return), vec![return_enum_attribute]);
fn_value.remove_enum_attribute(AttributeLoc::Return, inreg_attribute);

fn_value.remove_enum_attribute(AttributeLoc::Return, alignstack_attribute);
assert_eq!(fn_value.count_attributes(AttributeLoc::Function), 1);
assert_eq!(
fn_value.attributes(AttributeLoc::Function),
vec![function_enum_attribute]
);
fn_value.remove_enum_attribute(AttributeLoc::Function, alwaysinline_attribute);

assert_eq!(fn_value.count_attributes(AttributeLoc::Function), 0);
assert_eq!(fn_value.count_attributes(AttributeLoc::Return), 0);
assert!(fn_value
.get_enum_attribute(AttributeLoc::Return, alignstack_attribute)
.get_enum_attribute(AttributeLoc::Return, inreg_attribute)
.is_none());
assert!(fn_value.get_string_attribute(AttributeLoc::Return, "my_key").is_none());
assert_eq!(fn_value.attributes(AttributeLoc::Function), vec![]);
Expand Down

0 comments on commit 92023e3

Please sign in to comment.