Skip to content

Commit

Permalink
Extract attribute handling code into a module
Browse files Browse the repository at this point in the history
This commit causes no change in trans semantics, it just moves some functions around and
deduplicates them.
  • Loading branch information
nagisa committed Apr 3, 2015
1 parent d36c4db commit c71970e
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 131 deletions.
3 changes: 2 additions & 1 deletion src/librustc_llvm/lib.rs
Expand Up @@ -157,7 +157,7 @@ bitflags! {
#[derive(Copy, Clone)]
pub enum OtherAttribute {
// The following are not really exposed in
// the LLVM c api so instead to add these
// the LLVM C api so instead to add these
// we call a wrapper function in RustWrapper
// that uses the C++ api.
SanitizeAddressAttribute = 1 << 32,
Expand Down Expand Up @@ -958,6 +958,7 @@ extern {
pub fn LLVMAddFunctionAttrString(Fn: ValueRef, index: c_uint, Name: *const c_char);
pub fn LLVMRemoveFunctionAttrString(Fn: ValueRef, index: c_uint, Name: *const c_char);
pub fn LLVMGetFunctionAttr(Fn: ValueRef) -> c_ulonglong;
pub fn LLVMRemoveFunctionAttr(Fn: ValueRef, val: c_ulonglong);

/* Operations on parameters */
pub fn LLVMCountParams(Fn: ValueRef) -> c_uint;
Expand Down
108 changes: 108 additions & 0 deletions src/librustc_trans/trans/attributes.rs
@@ -0,0 +1,108 @@
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Set and unset common attributes on LLVM values.

use llvm::{self, ValueRef, AttrHelper};
use syntax::ast;
use syntax::attr::InlineAttr;
pub use syntax::attr::InlineAttr::*;
use trans::context::CrateContext;

use libc::{c_uint, c_ulonglong};

/// Mark LLVM function to use split stack.
#[inline]
pub fn split_stack(val: ValueRef, set: bool) {
unsafe {
let attr = "split-stack\0".as_ptr() as *const _;
if set {
llvm::LLVMAddFunctionAttrString(val, llvm::FunctionIndex as c_uint, attr);
} else {
llvm::LLVMRemoveFunctionAttrString(val, llvm::FunctionIndex as c_uint, attr);
}
}
}

/// Mark LLVM function to use provided inline heuristic.
#[inline]
pub fn inline(val: ValueRef, inline: InlineAttr) {
match inline {
InlineHint => llvm::SetFunctionAttribute(val, llvm::InlineHintAttribute),
InlineAlways => llvm::SetFunctionAttribute(val, llvm::AlwaysInlineAttribute),
InlineNever => llvm::SetFunctionAttribute(val, llvm::NoInlineAttribute),
InlineNone => {
let attr = llvm::InlineHintAttribute |
llvm::AlwaysInlineAttribute |
llvm::NoInlineAttribute;
unsafe {
llvm::LLVMRemoveFunctionAttr(val, attr.bits() as c_ulonglong)
}
},
};
}

/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.
#[inline]
pub fn emit_uwtable(val: ValueRef, emit: bool) {
if emit {
llvm::SetFunctionAttribute(val, llvm::UWTableAttribute);
} else {
unsafe {
llvm::LLVMRemoveFunctionAttr(val, llvm::UWTableAttribute.bits() as c_ulonglong);
}
}
}

/// Tell LLVM whether the function can or cannot unwind.
#[inline]
#[allow(dead_code)] // possibly useful function
pub fn unwind(val: ValueRef, can_unwind: bool) {
if can_unwind {
unsafe {
llvm::LLVMRemoveFunctionAttr(val, llvm::NoUnwindAttribute.bits() as c_ulonglong);
}
} else {
llvm::SetFunctionAttribute(val, llvm::NoUnwindAttribute);
}
}

/// Tell LLVM whether it should optimise function for size.
#[inline]
#[allow(dead_code)] // possibly useful function
pub fn set_optimize_for_size(val: ValueRef, optimize: bool) {
if optimize {
llvm::SetFunctionAttribute(val, llvm::OptimizeForSizeAttribute);
} else {
unsafe {
llvm::LLVMRemoveFunctionAttr(val, llvm::OptimizeForSizeAttribute.bits() as c_ulonglong);
}
}
}

/// Composite function which sets LLVM attributes for function depending on its AST (#[attribute])
/// attributes.
pub fn convert_fn_attrs_to_llvm(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRef) {
use syntax::attr::*;
inline(llfn, find_inline_attr(Some(ccx.sess().diagnostic()), attrs));

for attr in attrs {
if attr.check_name("no_stack_check") {
split_stack(llfn, false);
} else if attr.check_name("cold") {
unsafe {
llvm::LLVMAddFunctionAttribute(llfn,
llvm::FunctionIndex as c_uint,
llvm::ColdAttribute as u64)
}
} else if attr.check_name("allocator") {
llvm::NoAliasAttribute.apply_llfn(llvm::ReturnIndex as c_uint, llfn);
}
}
}
125 changes: 27 additions & 98 deletions src/librustc_trans/trans/base.rs
@@ -1,4 +1,4 @@
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
Expand All @@ -7,21 +7,20 @@
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// trans.rs: Translate the completed AST to the LLVM IR.
//
// Some functions here, such as trans_block and trans_expr, return a value --
// the result of the translation to LLVM -- while others, such as trans_fn,
// trans_impl, and trans_item, are called only for the side effect of adding a
// particular definition to the LLVM IR output we're producing.
//
// Hopefully useful general knowledge about trans:
//
// * There's no way to find out the Ty type of a ValueRef. Doing so
// would be "trying to get the eggs out of an omelette" (credit:
// pcwalton). You can, instead, find out its TypeRef by calling val_ty,
// but one TypeRef corresponds to many `Ty`s; for instance, tup(int, int,
// int) and rec(x=int, y=int, z=int) will have the same TypeRef.
//! Translate the completed AST to the LLVM IR.
//!
//! Some functions here, such as trans_block and trans_expr, return a value --
//! the result of the translation to LLVM -- while others, such as trans_fn,
//! trans_impl, and trans_item, are called only for the side effect of adding a
//! particular definition to the LLVM IR output we're producing.
//!
//! Hopefully useful general knowledge about trans:
//!
//! * There's no way to find out the Ty type of a ValueRef. Doing so
//! would be "trying to get the eggs out of an omelette" (credit:
//! pcwalton). You can, instead, find out its TypeRef by calling val_ty,
//! but one TypeRef corresponds to many `Ty`s; for instance, tup(int, int,
//! int) and rec(x=int, y=int, z=int) will have the same TypeRef.

#![allow(non_camel_case_types)]

Expand All @@ -33,7 +32,7 @@ use super::ModuleTranslation;
use back::link::mangle_exported_name;
use back::{link, abi};
use lint;
use llvm::{AttrHelper, BasicBlockRef, Linkage, ValueRef, Vector, get_param};
use llvm::{BasicBlockRef, Linkage, ValueRef, Vector, get_param};
use llvm;
use metadata::{csearch, encoder, loader};
use middle::astencode;
Expand All @@ -46,6 +45,7 @@ use session::config::{self, NoDebugInfo};
use session::Session;
use trans::_match;
use trans::adt;
use trans::attributes;
use trans::build::*;
use trans::builder::{Builder, noname};
use trans::callee;
Expand Down Expand Up @@ -204,7 +204,7 @@ pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv,
llvm::SetUnnamedAddr(llfn, true);

if ccx.is_split_stack_supported() && !ccx.sess().opts.cg.no_stack_check {
set_split_stack(llfn);
attributes::split_stack(llfn, true);
}

llfn
Expand Down Expand Up @@ -245,7 +245,7 @@ fn get_extern_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_ty: Ty<'tcx>,
let f = decl_rust_fn(ccx, fn_ty, name);

let attrs = csearch::get_item_attrs(&ccx.sess().cstore, did);
set_llvm_fn_attrs(ccx, &attrs[..], f);
attributes::convert_fn_attrs_to_llvm(ccx, &attrs[..], f);

ccx.externs().borrow_mut().insert(name.to_string(), f);
f
Expand Down Expand Up @@ -390,77 +390,6 @@ pub fn malloc_raw_dyn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
Result::new(r.bcx, PointerCast(r.bcx, r.val, llty_ptr))
}

#[allow(dead_code)] // useful
pub fn set_optimize_for_size(f: ValueRef) {
llvm::SetFunctionAttribute(f, llvm::OptimizeForSizeAttribute)
}

pub fn set_no_inline(f: ValueRef) {
llvm::SetFunctionAttribute(f, llvm::NoInlineAttribute)
}

#[allow(dead_code)] // useful
pub fn set_no_unwind(f: ValueRef) {
llvm::SetFunctionAttribute(f, llvm::NoUnwindAttribute)
}

// Tell LLVM to emit the information necessary to unwind the stack for the
// function f.
pub fn set_uwtable(f: ValueRef) {
llvm::SetFunctionAttribute(f, llvm::UWTableAttribute)
}

pub fn set_inline_hint(f: ValueRef) {
llvm::SetFunctionAttribute(f, llvm::InlineHintAttribute)
}

pub fn set_llvm_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRef) {
use syntax::attr::{find_inline_attr, InlineAttr};
// Set the inline hint if there is one
match find_inline_attr(Some(ccx.sess().diagnostic()), attrs) {
InlineAttr::Hint => set_inline_hint(llfn),
InlineAttr::Always => set_always_inline(llfn),
InlineAttr::Never => set_no_inline(llfn),
InlineAttr::None => { /* fallthrough */ }
}

for attr in attrs {
let mut used = true;
match &attr.name()[..] {
"no_stack_check" => unset_split_stack(llfn),
"cold" => unsafe {
llvm::LLVMAddFunctionAttribute(llfn,
llvm::FunctionIndex as c_uint,
llvm::ColdAttribute as uint64_t)
},
"allocator" => {
llvm::NoAliasAttribute.apply_llfn(llvm::ReturnIndex as c_uint, llfn);
}
_ => used = false,
}
if used {
attr::mark_used(attr);
}
}
}

pub fn set_always_inline(f: ValueRef) {
llvm::SetFunctionAttribute(f, llvm::AlwaysInlineAttribute)
}

pub fn set_split_stack(f: ValueRef) {
unsafe {
llvm::LLVMAddFunctionAttrString(f, llvm::FunctionIndex as c_uint,
"split-stack\0".as_ptr() as *const _);
}
}

pub fn unset_split_stack(f: ValueRef) {
unsafe {
llvm::LLVMRemoveFunctionAttrString(f, llvm::FunctionIndex as c_uint,
"split-stack\0".as_ptr() as *const _);
}
}

// Double-check that we never ask LLVM to declare the same symbol twice. It
// silently mangles such symbols, breaking our linkage model.
Expand Down Expand Up @@ -898,7 +827,7 @@ pub fn trans_external_path<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
_ => {
let llfn = foreign::register_foreign_item_fn(ccx, fn_ty.abi, t, &name[..]);
let attrs = csearch::get_item_attrs(&ccx.sess().cstore, did);
set_llvm_fn_attrs(ccx, &attrs, llfn);
attributes::convert_fn_attrs_to_llvm(ccx, &attrs, llfn);
llfn
}
}
Expand Down Expand Up @@ -1708,7 +1637,7 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1);

let _icx = push_ctxt("trans_closure");
set_uwtable(llfndecl);
attributes::emit_uwtable(llfndecl, true);

debug!("trans_closure(..., param_substs={})",
param_substs.repr(ccx.tcx()));
Expand Down Expand Up @@ -2312,7 +2241,7 @@ fn finish_register_fn(ccx: &CrateContext, sp: Span, sym: String, node_id: ast::N
// eh_personality functions need to be externally linkable.
let def = ast_util::local_def(node_id);
if ccx.tcx().lang_items.stack_exhausted() == Some(def) {
unset_split_stack(llfn);
attributes::split_stack(llfn, false);
llvm::SetLinkage(llfn, llvm::ExternalLinkage);
}
if ccx.tcx().lang_items.eh_personality() == Some(def) {
Expand Down Expand Up @@ -2733,7 +2662,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
sym,
i.id)
};
set_llvm_fn_attrs(ccx, &i.attrs, llfn);
attributes::convert_fn_attrs_to_llvm(ccx, &i.attrs, llfn);
llfn
}

Expand Down Expand Up @@ -2794,7 +2723,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
let ty = ty::node_id_to_type(ccx.tcx(), ni.id);
let name = foreign::link_name(&*ni);
let llfn = foreign::register_foreign_item_fn(ccx, abi, ty, &name);
set_llvm_fn_attrs(ccx, &ni.attrs, llfn);
attributes::convert_fn_attrs_to_llvm(ccx, &ni.attrs, llfn);
llfn
}
ast::ForeignItemStatic(..) => {
Expand Down Expand Up @@ -2826,7 +2755,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
}
_ => ccx.sess().bug("NodeVariant, shouldn't happen")
};
set_inline_hint(llfn);
attributes::inline(llfn, attributes::InlineHint);
llfn
}

Expand All @@ -2848,7 +2777,7 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
&struct_item.attrs);
let llfn = register_fn(ccx, struct_item.span,
sym, ctor_id, ty);
set_inline_hint(llfn);
attributes::inline(llfn, attributes::InlineHint);
llfn
}

Expand Down Expand Up @@ -2883,7 +2812,7 @@ fn register_method(ccx: &CrateContext, id: ast::NodeId,
} else {
foreign::register_rust_fn_with_foreign_abi(ccx, span, sym, id)
};
set_llvm_fn_attrs(ccx, &attrs, llfn);
attributes::convert_fn_attrs_to_llvm(ccx, &attrs, llfn);
return llfn;
} else {
ccx.sess().span_bug(span, "expected bare rust function");
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_trans/trans/closure.rs
Expand Up @@ -13,6 +13,7 @@ use back::link::{self, mangle_internal_name_by_path_and_seq};
use llvm::{ValueRef, get_param};
use middle::mem_categorization::Typer;
use trans::adt;
use trans::attributes;
use trans::base::*;
use trans::build::*;
use trans::callee::{self, ArgVals, Callee, TraitItem, MethodData};
Expand Down Expand Up @@ -164,7 +165,7 @@ pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tc
let llfn = decl_internal_rust_fn(ccx, function_type, &symbol[..]);

// set an inline hint for all closures
set_inline_hint(llfn);
attributes::inline(llfn, attributes::InlineHint);

debug!("get_or_create_declaration_if_closure(): inserting new \
closure {:?} (type {})",
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_trans/trans/foreign.rs
Expand Up @@ -13,6 +13,7 @@ use back::link;
use llvm::{ValueRef, CallConv, get_param};
use llvm;
use middle::weak_lang_items;
use trans::attributes;
use trans::base::{llvm_linkage_by_name, push_ctxt};
use trans::base;
use trans::build::*;
Expand Down Expand Up @@ -612,7 +613,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
id, t.repr(tcx));

let llfn = base::decl_internal_rust_fn(ccx, t, &ps[..]);
base::set_llvm_fn_attrs(ccx, attrs, llfn);
attributes::convert_fn_attrs_to_llvm(ccx, attrs, llfn);
base::trans_fn(ccx, decl, body, llfn, param_substs, id, &[]);
llfn
}
Expand Down

0 comments on commit c71970e

Please sign in to comment.