Skip to content

Commit aaaa2f6

Browse files
committed
Add support for inlining method calls
The compiler is now able to inline calls to static and instance methods. For each method we calculate a rough weight/cost, and calls are inlined into their callers until the maximum weight is reached. Inlining is done bottom-up using Tarjan's strongly connected components algorithm, reducing the amount of duplicate inlining work. Inlining is also done in a deterministic order as to ensure incremental compilation caches can be reused as much as possible. The current inlining threshold is on the conservative end as to not increase compile times and compile-time memory usage too much. Over time we may relax this based on user reports and any extra optimization passes we might add. If a method is annotated with the `inline` keyword, it's _always_ inlined into the caller regardless of it or the caller's weight. This is meant to be used when you want to guarantee a method is inlined, such as for the various operator methods of Int, Float, Bool, etc. Because of this guarantee one should use it sparingly, as to not increase the compile time and executable size too much. This fixes #343. Changelog: added
1 parent 9f68de6 commit aaaa2f6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+4136
-2358
lines changed

.github/workflows/tests.yml

+6-1
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,13 @@ jobs:
7575
key: amd64-linux-gnu-${{ hashFiles('Cargo.lock', 'rust-toolchain.toml') }}
7676
- name: Run compiler tests
7777
run: cargo test
78-
- name: Run stdlib tests
78+
# We run tests with and without optimizations, such that we can catch any
79+
# potential miscompilations introduced by optimizations. We only do this
80+
# for this particular target as our optimizations aren't target specific.
81+
- name: Run stdlib tests with optimizations
7982
run: 'cd std && cargo run -- test'
83+
- name: Run stdlib tests without optimizations
84+
run: 'cd std && cargo run -- test --opt=none'
8085

8186
amd64-linux-musl:
8287
runs-on: ubuntu-latest

Cargo.lock

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace]
2-
members = ["ast", "inko", "compiler", "rt"]
2+
members = ["ast", "inko", "compiler", "rt", "location"]
33
resolver = "2"
44

55
[workspace.package]

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ ${SOURCE_TAR}: ${TMP_DIR}
8585
std/src \
8686
rt \
8787
types \
88+
location \
8889
| gzip > "${@}"
8990

9091
release/source: ${SOURCE_TAR}

ast/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ doctest = false
1212
[dependencies]
1313
unicode-segmentation = "^1.8"
1414
getopts = "^0.2"
15+
location = { path = "../location" }
1516

1617
[dev-dependencies]
1718
similar-asserts = "^1.1"

ast/src/lexer.rs

+53-35
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! Lexical analysis of Inko source code.
2-
use crate::source_location::SourceLocation;
2+
use location::Location;
33
use unicode_segmentation::UnicodeSegmentation;
44

55
const NULL: u8 = 0;
@@ -166,6 +166,7 @@ pub enum TokenKind {
166166
While,
167167
Whitespace,
168168
Extern,
169+
Inline,
169170
}
170171

171172
impl TokenKind {
@@ -268,6 +269,7 @@ impl TokenKind {
268269
TokenKind::Nil => "the 'nil' keyword",
269270
TokenKind::Replace => "a '=:'",
270271
TokenKind::Extern => "the 'extern' keyword",
272+
TokenKind::Inline => "the 'inline' keyword",
271273
}
272274
}
273275
}
@@ -276,23 +278,23 @@ impl TokenKind {
276278
pub struct Token {
277279
pub kind: TokenKind,
278280
pub value: String,
279-
pub location: SourceLocation,
281+
pub location: Location,
280282
}
281283

282284
impl Token {
283-
fn new(kind: TokenKind, value: String, location: SourceLocation) -> Self {
285+
fn new(kind: TokenKind, value: String, location: Location) -> Self {
284286
Self { kind, value, location }
285287
}
286288

287289
/// Returns a token signalling unexpected input. The token contains the
288290
/// invalid character.
289-
fn invalid(value: String, location: SourceLocation) -> Self {
291+
fn invalid(value: String, location: Location) -> Self {
290292
Self::new(TokenKind::Invalid, value, location)
291293
}
292294

293295
/// Returns a token that signals the end of the input stream. We use null
294296
/// tokens so we don't need to wrap/unwrap every token using an Option type.
295-
fn null(location: SourceLocation) -> Self {
297+
fn null(location: Location) -> Self {
296298
Self::new(TokenKind::Null, String::new(), location)
297299
}
298300

@@ -335,6 +337,7 @@ impl Token {
335337
| TokenKind::Case
336338
| TokenKind::Enum
337339
| TokenKind::Extern
340+
| TokenKind::Inline
338341
)
339342
}
340343

@@ -363,7 +366,7 @@ impl Token {
363366
}
364367

365368
pub fn same_line_as(&self, token: &Token) -> bool {
366-
self.location.lines.start() == token.location.lines.start()
369+
self.location.line_start == token.location.line_start
367370
}
368371
}
369372

@@ -422,10 +425,10 @@ pub struct Lexer {
422425
states: Vec<State>,
423426

424427
/// The current line number.
425-
line: usize,
428+
line: u32,
426429

427430
/// The current (starting) column number.
428-
column: usize,
431+
column: u32,
429432
}
430433

431434
impl Lexer {
@@ -443,8 +446,13 @@ impl Lexer {
443446
}
444447
}
445448

446-
pub fn start_location(&self) -> SourceLocation {
447-
SourceLocation::new(self.line..=self.line, self.column..=self.column)
449+
pub fn start_location(&self) -> Location {
450+
Location {
451+
line_start: self.line,
452+
line_end: self.line,
453+
column_start: self.column,
454+
column_end: self.column,
455+
}
448456
}
449457

450458
pub fn next_token(&mut self) -> Token {
@@ -457,18 +465,16 @@ impl Lexer {
457465
}
458466
}
459467

460-
fn source_location(
461-
&self,
462-
start_line: usize,
463-
start_column: usize,
464-
) -> SourceLocation {
465-
SourceLocation::new(
466-
start_line..=self.line,
468+
fn source_location(&self, start_line: u32, start_column: u32) -> Location {
469+
Location {
470+
line_start: start_line,
471+
line_end: self.line,
467472
// The end column points to whatever comes _after_ the last
468473
// processed character. This means the end column is one column
469474
// earlier.
470-
start_column..=(self.column - 1),
471-
)
475+
column_start: start_column,
476+
column_end: self.column - 1,
477+
}
472478
}
473479

474480
fn current_byte(&self) -> u8 {
@@ -500,7 +506,7 @@ impl Lexer {
500506
}
501507

502508
fn advance_column(&mut self, value: &str) {
503-
self.column += value.graphemes(true).count();
509+
self.column += value.graphemes(true).count() as u32;
504510
}
505511

506512
fn advance_char(&mut self) {
@@ -997,6 +1003,7 @@ impl Lexer {
9971003
"return" => TokenKind::Return,
9981004
"static" => TokenKind::Static,
9991005
"extern" => TokenKind::Extern,
1006+
"inline" => TokenKind::Inline,
10001007
_ => TokenKind::Identifier,
10011008
},
10021009
7 => match value.as_str() {
@@ -1087,14 +1094,14 @@ impl Lexer {
10871094
&mut self,
10881095
kind: TokenKind,
10891096
buffer: Vec<u8>,
1090-
line: usize,
1091-
column: usize,
1097+
line: u32,
1098+
column: u32,
10921099
new_line: bool,
10931100
) -> Token {
10941101
let value = String::from_utf8_lossy(&buffer).into_owned();
10951102

10961103
if !value.is_empty() {
1097-
self.column += value.graphemes(true).count();
1104+
self.column += value.graphemes(true).count() as u32;
10981105
}
10991106

11001107
let location = self.source_location(line, column);
@@ -1175,8 +1182,8 @@ impl Lexer {
11751182
&mut self,
11761183
kind: TokenKind,
11771184
start: usize,
1178-
line: usize,
1179-
column: usize,
1185+
line: u32,
1186+
column: u32,
11801187
) -> Token {
11811188
let value = self.slice_string(start, self.position);
11821189

@@ -1187,7 +1194,7 @@ impl Lexer {
11871194
Token::new(kind, value, location)
11881195
}
11891196

1190-
fn token(&mut self, kind: TokenKind, start: usize, line: usize) -> Token {
1197+
fn token(&mut self, kind: TokenKind, start: usize, line: u32) -> Token {
11911198
self.token_with_column(kind, start, line, self.column)
11921199
}
11931200

@@ -1223,13 +1230,22 @@ impl Lexer {
12231230
// When we encounter the end of the input, we want the location to point
12241231
// to the last column that came before it. This way any errors are
12251232
// reported within the bounds of the column range.
1226-
let lines = self.line..=self.line;
12271233
let location = if self.column == 1 {
1228-
SourceLocation::new(lines, 1..=1)
1234+
Location {
1235+
line_start: self.line,
1236+
line_end: self.line,
1237+
column_start: 1,
1238+
column_end: 1,
1239+
}
12291240
} else {
12301241
let column = self.column - 1;
12311242

1232-
SourceLocation::new(lines, column..=column)
1243+
Location {
1244+
line_start: self.line,
1245+
line_end: self.line,
1246+
column_start: column,
1247+
column_end: column,
1248+
}
12331249
};
12341250

12351251
Token::null(location)
@@ -1247,17 +1263,17 @@ mod tests {
12471263
}
12481264

12491265
fn location(
1250-
line_range: RangeInclusive<usize>,
1251-
column_range: RangeInclusive<usize>,
1252-
) -> SourceLocation {
1253-
SourceLocation::new(line_range, column_range)
1266+
line_range: RangeInclusive<u32>,
1267+
column_range: RangeInclusive<u32>,
1268+
) -> Location {
1269+
Location::new(&line_range, &column_range)
12541270
}
12551271

12561272
fn tok(
12571273
kind: TokenKind,
12581274
value: &str,
1259-
line_range: RangeInclusive<usize>,
1260-
column_range: RangeInclusive<usize>,
1275+
line_range: RangeInclusive<u32>,
1276+
column_range: RangeInclusive<u32>,
12611277
) -> Token {
12621278
Token::new(kind, value.to_string(), location(line_range, column_range))
12631279
}
@@ -1337,6 +1353,7 @@ mod tests {
13371353
assert!(tok(TokenKind::While, "", 1..=1, 1..=1).is_keyword());
13381354
assert!(tok(TokenKind::Recover, "", 1..=1, 1..=1).is_keyword());
13391355
assert!(tok(TokenKind::Nil, "", 1..=1, 1..=1).is_keyword());
1356+
assert!(tok(TokenKind::Inline, "", 1..=1, 1..=1).is_keyword());
13401357
}
13411358

13421359
#[test]
@@ -1978,6 +1995,7 @@ mod tests {
19781995
assert_token!("return", Return, "return", 1..=1, 1..=6);
19791996
assert_token!("static", Static, "static", 1..=1, 1..=6);
19801997
assert_token!("extern", Extern, "extern", 1..=1, 1..=6);
1998+
assert_token!("inline", Inline, "inline", 1..=1, 1..=6);
19811999

19822000
assert_token!("builtin", Builtin, "builtin", 1..=1, 1..=7);
19832001
assert_token!("recover", Recover, "recover", 1..=1, 1..=7);

ast/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@
22
pub mod lexer;
33
pub mod nodes;
44
pub mod parser;
5-
pub mod source_location;

0 commit comments

Comments
 (0)