Skip to content

Commit

Permalink
Adds inline annotation to the compiler (FuelLabs#3185)
Browse files Browse the repository at this point in the history
`inline_function_calls`' `inline_heuristic` now checks inline metadata
and
if inline metadata is set to `Inline::Never` it returns false and the
function is not inlined.

When inline metadata is set to `Inline::Always` it will still follow the
default behavior, I left as TODO: check if inlining of function is
possible.

ref FuelLabs#2944

Co-authored-by: João Matos <[email protected]>
  • Loading branch information
esdrubal and tritao authored Oct 31, 2022
1 parent 18ed7f5 commit 2af781a
Show file tree
Hide file tree
Showing 11 changed files with 291 additions and 109 deletions.
8 changes: 7 additions & 1 deletion sway-core/src/ir_generation/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ fn compile_fn_with_args(
selector: Option<[u8; 4]>,
logged_types_map: &HashMap<TypeId, LogId>,
) -> Result<Function, CompileError> {
let inline_opt = ast_fn_decl.inline();
let ty::TyFunctionDeclaration {
name,
body,
Expand Down Expand Up @@ -255,7 +256,12 @@ fn compile_fn_with_args(

let span_md_idx = md_mgr.span_to_md(context, &span);
let storage_md_idx = md_mgr.purity_to_md(context, purity);
let metadata = md_combine(context, &span_md_idx, &storage_md_idx);
let mut metadata = md_combine(context, &span_md_idx, &storage_md_idx);

if let Some(inline) = inline_opt {
let inline_md_idx = md_mgr.inline_to_md(context, inline);
metadata = md_combine(context, &metadata, &inline_md_idx);
}

let func = Function::new(
context,
Expand Down
6 changes: 6 additions & 0 deletions sway-core/src/language/inline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/// The inline of a function suggests to the compiler whether or no a function should be inline.
#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
pub enum Inline {
Always,
Never,
}
2 changes: 2 additions & 0 deletions sway-core/src/language/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod asm;
mod call_path;
mod inline;
mod lazy_op;
mod literal;
mod module;
Expand All @@ -10,6 +11,7 @@ mod visibility;

pub use asm::*;
pub use call_path::*;
pub use inline::*;
pub use lazy_op::*;
pub use literal::*;
pub use module::*;
Expand Down
19 changes: 18 additions & 1 deletion sway-core/src/language/ty/declaration/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ use sway_types::{Ident, JsonABIFunction, JsonTypeApplication, JsonTypeDeclaratio
use crate::{
declaration_engine::*,
error::*,
language::{parsed, ty::*, Purity, Visibility},
language::{parsed, ty::*, Inline, Purity, Visibility},
transform,
type_system::*,
};

use sway_types::constants::{INLINE_ALWAYS_NAME, INLINE_NEVER_NAME};

#[derive(Clone, Debug, Eq)]
pub struct TyFunctionDeclaration {
pub name: Ident,
Expand Down Expand Up @@ -294,6 +296,21 @@ impl TyFunctionDeclaration {
.contains_key(&transform::AttributeKind::Test)
}

pub fn inline(&self) -> Option<Inline> {
match self
.attributes
.get(&transform::AttributeKind::Inline)?
.last()?
.args
.first()?
.as_str()
{
INLINE_NEVER_NAME => Some(Inline::Never),
INLINE_ALWAYS_NAME => Some(Inline::Always),
_ => None,
}
}

/// Whether or not this function describes a program entry point.
pub fn is_entry(&self) -> bool {
self.is_main_entry() || self.is_test()
Expand Down
31 changes: 23 additions & 8 deletions sway-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod transform;
pub mod type_system;

use crate::ir_generation::check_function_purity;
use crate::language::Inline;
use crate::{error::*, source_map::SourceMap};
pub use asm_generation::from_ir::compile_ir_to_asm;
use asm_generation::FinalizedAsm;
Expand Down Expand Up @@ -442,14 +443,14 @@ fn promote_to_registers(ir: &mut Context, functions: &[Function]) -> CompileResu
ok((), Vec::new(), Vec::new())
}

// Inline function calls based on two conditions:
// 1. The program we're compiling is a "predicate". Predicates cannot jump backwards which means
// that supporting function calls (i.e. without inlining) is not possible. This is a protocl
// restriction and not a heuristic.
// 2. If the program is not a "predicate" then, we rely on some heuristic which is described below
// in the `inline_heuristc` closure.
//
fn inline_function_calls(
/// Inline function calls based on two conditions:
/// 1. The program we're compiling is a "predicate". Predicates cannot jump backwards which means
/// that supporting function calls (i.e. without inlining) is not possible. This is a protocl
/// restriction and not a heuristic.
/// 2. If the program is not a "predicate" then, we rely on some heuristic which is described below
/// in the `inline_heuristc` closure.
///
pub fn inline_function_calls(
ir: &mut Context,
functions: &[Function],
tree_type: &parsed::TreeType,
Expand All @@ -472,6 +473,20 @@ fn inline_function_calls(
};

let inline_heuristic = |ctx: &Context, func: &Function, _call_site: &Value| {
let mut md_mgr = metadata::MetadataManager::default();
let attributed_inline = md_mgr.md_to_inline(ctx, func.get_metadata(ctx));

match attributed_inline {
Some(Inline::Always) => {
// TODO: check if inlining of function is possible
// return true;
}
Some(Inline::Never) => {
return false;
}
None => {}
}

// For now, pending improvements to ASMgen for calls, we must inline any function which has
// too many args.
if func.args_iter(ctx).count() as u8
Expand Down
61 changes: 60 additions & 1 deletion sway-core/src/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::language::Purity;
use crate::language::{Inline, Purity};

use sway_ir::{Context, MetadataIndex, Metadatum, Value};
use sway_types::Span;
Expand All @@ -19,11 +19,13 @@ pub(crate) struct MetadataManager {
md_file_loc_cache: HashMap<MetadataIndex, (Arc<PathBuf>, Arc<str>)>,
md_storage_op_cache: HashMap<MetadataIndex, StorageOperation>,
md_storage_key_cache: HashMap<MetadataIndex, u64>,
md_inline_cache: HashMap<MetadataIndex, Inline>,

span_md_cache: HashMap<Span, MetadataIndex>,
file_loc_md_cache: HashMap<*const PathBuf, MetadataIndex>,
storage_op_md_cache: HashMap<Purity, MetadataIndex>,
storage_key_md_cache: HashMap<u64, MetadataIndex>,
inline_md_cache: HashMap<Inline, MetadataIndex>,
}

#[derive(Clone, Copy)]
Expand Down Expand Up @@ -110,6 +112,34 @@ impl MetadataManager {
})
}

/// Gets Inline information from metadata index.
pub(crate) fn md_to_inline(
&mut self,
context: &Context,
md_idx: Option<MetadataIndex>,
) -> Option<Inline> {
Self::for_each_md_idx(context, md_idx, |md_idx| {
self.md_inline_cache.get(&md_idx).copied().or_else(|| {
// Create a new inline and save it in the cache.
md_idx
.get_content(context)
.unwrap_struct("inline", 1)
.and_then(|fields| fields[0].unwrap_string())
.and_then(|inline_str| {
let inline = match inline_str {
"always" => Some(Inline::Always),
"never" => Some(Inline::Never),
_otherwise => None,
}?;

self.md_inline_cache.insert(md_idx, inline);

Some(inline)
})
})
})
}

fn md_to_file_location(
&mut self,
context: &Context,
Expand Down Expand Up @@ -220,6 +250,35 @@ impl MetadataManager {
})
}

/// Inserts Inline information into metadata.
pub(crate) fn inline_to_md(
&mut self,
context: &mut Context,
inline: Inline,
) -> Option<MetadataIndex> {
Some(
self.inline_md_cache
.get(&inline)
.copied()
.unwrap_or_else(|| {
// Create new metadatum.
let field = match inline {
Inline::Always => "always",
Inline::Never => "never",
};
let md_idx = MetadataIndex::new_struct(
context,
"inline",
vec![Metadatum::String(field.to_owned())],
);

self.inline_md_cache.insert(inline, md_idx);

md_idx
}),
)
}

fn file_location_to_md(
&mut self,
context: &mut Context,
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/transform/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub struct Attribute {
pub enum AttributeKind {
Doc,
Storage,
Inline,
Test,
}

Expand Down
7 changes: 4 additions & 3 deletions sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ use sway_error::handler::{ErrorEmitted, Handler};
use sway_error::warning::{CompileWarning, Warning};
use sway_types::{
constants::{
DESTRUCTURE_PREFIX, DOC_ATTRIBUTE_NAME, MATCH_RETURN_VAR_NAME_PREFIX,
STORAGE_PURITY_ATTRIBUTE_NAME, STORAGE_PURITY_READ_NAME, STORAGE_PURITY_WRITE_NAME,
TEST_ATTRIBUTE_NAME, TUPLE_NAME_PREFIX, VALID_ATTRIBUTE_NAMES,
DESTRUCTURE_PREFIX, DOC_ATTRIBUTE_NAME, INLINE_ATTRIBUTE_NAME,
MATCH_RETURN_VAR_NAME_PREFIX, STORAGE_PURITY_ATTRIBUTE_NAME, STORAGE_PURITY_READ_NAME,
STORAGE_PURITY_WRITE_NAME, TEST_ATTRIBUTE_NAME, TUPLE_NAME_PREFIX, VALID_ATTRIBUTE_NAMES,
},
integer_bits::IntegerBits,
};
Expand Down Expand Up @@ -3261,6 +3261,7 @@ fn item_attrs_to_map(
if let Some(attr_kind) = match name {
DOC_ATTRIBUTE_NAME => Some(AttributeKind::Doc),
STORAGE_PURITY_ATTRIBUTE_NAME => Some(AttributeKind::Storage),
INLINE_ATTRIBUTE_NAME => Some(AttributeKind::Inline),
TEST_ATTRIBUTE_NAME => Some(AttributeKind::Test),
_ => None,
} {
Expand Down
5 changes: 5 additions & 0 deletions sway-types/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ pub const STORAGE_PURITY_ATTRIBUTE_NAME: &str = "storage";
pub const STORAGE_PURITY_READ_NAME: &str = "read";
pub const STORAGE_PURITY_WRITE_NAME: &str = "write";

/// The valid attribute strings related to inline.
pub const INLINE_ATTRIBUTE_NAME: &str = "inline";
pub const INLINE_NEVER_NAME: &str = "never";
pub const INLINE_ALWAYS_NAME: &str = "always";

/// The valid attribute strings related to documentation.
pub const DOC_ATTRIBUTE_NAME: &str = "doc";

Expand Down
Loading

0 comments on commit 2af781a

Please sign in to comment.