-
Notifications
You must be signed in to change notification settings - Fork 224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add test for inlinealways function attribute #426
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 { | ||
|
@@ -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 | ||
} | ||
Comment on lines
+157
to
+161
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this feels a bit cheaty, but it's the simplest way I saw to keep code duplication to a minimum. Suggestions are welcome. |
||
|
||
/// Gets the enum kind id associated with a builtin name. | ||
/// | ||
/// # Example | ||
|
@@ -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. | ||
|
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() { | ||
|
@@ -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); | ||
Comment on lines
-87
to
-88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
to apply the |
||
|
||
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![]); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used the Debug and PartialEq instance during debugging. I can remove them again, but then when something fails you only see an address, which is not helpful at all.