Skip to content

Commit 3d5adf8

Browse files
committed
Update optimization to use new passes
1 parent fbe6f33 commit 3d5adf8

File tree

3 files changed

+138
-221
lines changed

3 files changed

+138
-221
lines changed

cobalt-build/src/build.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use cobalt_parser::parse_str;
66
use cobalt_utils::CellExt as Cell;
77
use either::Either;
88
use indexmap::IndexMap;
9+
use inkwell::passes::PassBuilderOptions;
910
use inkwell::targets::{FileType, Target as InkwellTarget, TargetTriple};
1011
use os_str_bytes::OsStrBytes;
1112
use path_calculate::*;
@@ -149,9 +150,6 @@ fn build_file_2(
149150
ctx.with_vars(|v| clear_mod(&mut v.symbols));
150151
return Ok(false);
151152
}
152-
let pm = inkwell::passes::PassManager::create(());
153-
opt::load_profile(&opts.profile, &pm);
154-
pm.run_on(&ctx.module);
155153
let trip = TargetTriple::create(&opts.triple);
156154
let target_machine = InkwellTarget::from_triple(&trip)
157155
.unwrap()
@@ -164,6 +162,11 @@ fn build_file_2(
164162
inkwell::targets::CodeModel::Small,
165163
)
166164
.expect("failed to create target machine");
165+
ctx.module.run_passes(
166+
&opt::expand_pass_string(&opts.profile)?,
167+
&target_machine,
168+
PassBuilderOptions::create(),
169+
).map_err(opt::PassError::from_llvm)?;
167170
if !out_path.parent().unwrap().exists() {
168171
out_path.parent().unwrap().create_dir_all_anyhow()?
169172
}

cobalt-build/src/opt.rs

Lines changed: 70 additions & 189 deletions
Original file line numberDiff line numberDiff line change
@@ -1,199 +1,80 @@
1-
use cobalt_errors::{error, warning};
2-
use cobalt_llvm::inkwell;
3-
use inkwell::module::Module;
4-
use inkwell::passes::*;
5-
use inkwell::OptimizationLevel::*;
6-
use std::env::var;
7-
use std::fs::read;
8-
use std::process::exit;
9-
pub enum AdditionalArg {
10-
Null,
11-
Bool(bool),
12-
Int(i32),
13-
}
14-
use AdditionalArg::*;
1+
use std::path::PathBuf;
2+
use anyhow_std::*;
3+
use std::env::var_os;
4+
use thiserror::Error;
5+
6+
/// A profile wasn't found in any of the searched paths
7+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Error)]
8+
#[error("couldn't find profile {0}")]
9+
pub struct MissingProfile(pub String);
1510

16-
pub fn add_pass(pm: &PassManager<Module>, name: &str, arg: AdditionalArg) -> bool {
17-
if let Some(profile) = name.strip_prefix('@') {
18-
load_profile(profile.trim(), pm);
19-
return true;
11+
/// Something went wrong while running passes
12+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Error)]
13+
#[error("encountered an error while running passes:{}{0}", if .0.contains('\n') {'\n'} else {' '})]
14+
pub struct PassError(pub String);
15+
impl PassError {
16+
/// Convenience method for use with `map_err`
17+
#[inline(always)]
18+
pub fn from_llvm(s: cobalt_llvm::inkwell::support::LLVMString) -> Self {
19+
Self(s.to_string())
2020
}
21-
// I wish there was a better way to do this
22-
let name = name
23-
.replace("attribute", "attr")
24-
.replace("function", "func")
25-
.replace("fn", "func")
26-
.replace("argument", "arg")
27-
.replace("constant", "const")
28-
.replace("optimizer", "opt")
29-
.replace("optimization", "opt")
30-
.replace("alignment", "align")
31-
.replace("variable", "var")
32-
.replace("instruction", "inst")
33-
.replace(['_', '-'], " ")
34-
.to_lowercase();
35-
match name.as_str() {
36-
"const merge" => pm.add_constant_merge_pass(),
37-
"merge func" | "merge funcs" => pm.add_merge_functions_pass(),
38-
"dead arg" | "dead args" | "dead arg elimination" | "dead args elimination" => {
39-
pm.add_dead_arg_elimination_pass()
40-
}
41-
"func attr" | "func attrs" => pm.add_function_attrs_pass(),
42-
"func inline" | "func inlining" => pm.add_function_inlining_pass(),
43-
"always inline" | "always inliner" => pm.add_always_inliner_pass(),
44-
"global dce" => pm.add_global_dce_pass(),
45-
"global opt" => pm.add_global_optimizer_pass(),
46-
"ipsccp" => pm.add_ipsccp_pass(),
47-
"internalize" => pm.add_internalize_pass(match arg {
48-
Null => false,
49-
Bool(val) => val,
50-
Int(val) => val != 0,
21+
}
22+
23+
lazy_static::lazy_static! {
24+
static ref SEARCH_DIRS: Vec<PathBuf> = [
25+
var_os("COBALT_DIR").map(|p| {
26+
let mut p = PathBuf::from(p);
27+
p.push("profiles");
28+
p
5129
}),
52-
"strip dead prototype"
53-
| "strip dead prototypes"
54-
| "strip prototype"
55-
| "strip prototypes" => pm.add_strip_dead_prototypes_pass(),
56-
"strip symbol" | "strip symbols" => pm.add_strip_symbol_pass(),
57-
"loop vectorize" => pm.add_loop_vectorize_pass(),
58-
"slp vectorize" => pm.add_slp_vectorize_pass(),
59-
"aggresive dce" | "adce" => pm.add_aggressive_dce_pass(),
60-
"bit tracking dce" | "bit dce" | "tracking dce" => pm.add_bit_tracking_dce_pass(),
61-
"align from assumptions" => pm.add_alignment_from_assumptions_pass(),
62-
"cfg simplification" => pm.add_cfg_simplification_pass(),
63-
"dead store" | "dead store elimination" => pm.add_dead_store_elimination_pass(),
64-
"scalarizer" => pm.add_scalarizer_pass(),
65-
"mlsm" | "merged load store motion" => pm.add_merged_load_store_motion_pass(),
66-
"gvn" | "global value numbering" => pm.add_gvn_pass(),
67-
"new gvn" | "new global value numbering" => pm.add_new_gvn_pass(),
68-
"ind var simplify" | "induction var simplify" => pm.add_ind_var_simplify_pass(),
69-
"inst combine" | "inst combining" => pm.add_instruction_combining_pass(),
70-
"jump thread" | "jump threading" => pm.add_jump_threading_pass(),
71-
"licm" => pm.add_licm_pass(),
72-
"dld" | "loop deletion" | "dead loop deletion" => pm.add_loop_deletion_pass(),
73-
"loop idiom" => pm.add_loop_idiom_pass(),
74-
"loop rotate" => pm.add_loop_rotate_pass(),
75-
"loop reroll" => pm.add_loop_reroll_pass(),
76-
"loop unroll" => pm.add_loop_unroll_pass(),
77-
"memcpy" | "memcpy opt" => pm.add_memcpy_optimize_pass(),
78-
"partially inline lib calls" => pm.add_partially_inline_lib_calls_pass(),
79-
"lower switch" => pm.add_lower_switch_pass(),
80-
"promote memory to register" => pm.add_promote_memory_to_register_pass(),
81-
"reassociate" => pm.add_reassociate_pass(),
82-
"sccp" => pm.add_sccp_pass(),
83-
"scalar repl aggregates" => match arg {
84-
Null | Bool(false) => pm.add_scalar_repl_aggregates_pass(),
85-
Bool(true) => pm.add_scalar_repl_aggregates_pass_ssa(),
86-
Int(val) => pm.add_scalar_repl_aggregates_pass_with_threshold(val),
87-
},
88-
"scalar repl aggregates ssa" => pm.add_scalar_repl_aggregates_pass_ssa(),
89-
"simplify lib calls" => pm.add_simplify_lib_calls_pass(),
90-
"tail call elimination" => pm.add_tail_call_elimination_pass(),
91-
"inst simplify" => pm.add_instruction_simplify_pass(),
92-
"demote memory to register" => pm.add_demote_memory_to_register_pass(),
93-
"verify" | "verifier" => pm.add_verifier_pass(),
94-
"correlated value propagation" => pm.add_correlated_value_propagation_pass(),
95-
"early cse" => match arg {
96-
Null | Bool(false) | Int(0) => pm.add_early_cse_pass(),
97-
Bool(true) | Int(_) => pm.add_early_cse_mem_ssa_pass(),
98-
},
99-
"early cse mem ssa" => pm.add_early_cse_mem_ssa_pass(),
100-
"lower expect intrinsic" => pm.add_lower_expect_intrinsic_pass(),
101-
"type based alias analysis" => pm.add_type_based_alias_analysis_pass(),
102-
"scoped no alias aa" => pm.add_scoped_no_alias_aa_pass(),
103-
"basic alias analysis" => pm.add_basic_alias_analysis_pass(),
104-
"loop unroll and jam" | "loop unroll jam" => pm.add_loop_unroll_and_jam_pass(),
105-
_ => return false,
106-
}
107-
true
30+
var_os("HOME").map(|p| {
31+
let mut p = PathBuf::from(p);
32+
p.push(".cobalt");
33+
p.push("profiles");
34+
p
35+
}),
36+
var_os("HOME").map(|p| {
37+
let mut p = PathBuf::from(p);
38+
p.push(".config");
39+
p.push("cobalt");
40+
p.push("profiles");
41+
p
42+
}),
43+
Some("/usr/local/share/cobalt/profiles".into()),
44+
Some("/usr/share/cobalt/profiles".into()),
45+
].into_iter().filter_map(|p| p.and_then(|p| p.is_dir().then_some(p))).collect();
10846
}
10947

110-
pub fn from_file(data: &str, pm: &PassManager<Module>) {
111-
for (n, mut line) in data.split('\n').enumerate() {
112-
let n = n + 1;
113-
if let Some(idx) = line.find('#') {
114-
line = &line[..idx];
115-
}
116-
if line.trim().is_empty() {
117-
continue;
118-
}
119-
if let Some(idx) = line.find('=') {
120-
let pass = line[..idx].trim();
121-
let val = line[(idx + 1)..].trim().to_lowercase();
122-
if val.is_empty() {
123-
if !add_pass(pm, pass, Null) {
124-
warning!("{n}: unknown pass '{pass}'");
125-
}
126-
} else if val == "true" {
127-
if !add_pass(pm, pass, Bool(true)) {
128-
warning!("{n}: unknown pass '{pass}'");
129-
}
130-
} else if val == "false" {
131-
if !add_pass(pm, pass, Bool(false)) {
132-
warning!("{n}: unknown pass '{pass}'");
133-
}
134-
} else if let Ok(val) = val.parse() {
135-
if !add_pass(pm, pass, Int(val)) {
136-
warning!("{n}: unknown pass '{pass}'");
137-
}
138-
} else {
139-
warning!("{n}: unrecognized value '{val}'");
140-
if !add_pass(pm, pass, Null) {
141-
warning!("{n}: unknown pass '{pass}'");
142-
}
143-
}
144-
} else {
145-
let pass = line.trim();
146-
if !add_pass(pm, pass, Null) {
147-
warning!("{n}: unknown pass '{pass}'");
148-
}
48+
fn expand_profile_name(name: &str) -> anyhow::Result<String> {
49+
for p in SEARCH_DIRS.iter() {
50+
let p = p.join(name);
51+
if p.is_file() {
52+
return p.read_to_string_anyhow();
14953
}
15054
}
55+
Ok(match name {
56+
"none" | "0" => "default<O0>",
57+
"less" | "1" => "default<O1>",
58+
"some" | "2" | "default" => "default<O2>",
59+
"aggr" | "3" => "default<O3>",
60+
_ => Err(MissingProfile(name.to_string()))?
61+
}.to_string())
15162
}
152-
pub fn load_profile(name: &str, pm: &PassManager<Module>) {
153-
if let Ok(cobalt_dir) = var("COBALT_DIR") {
154-
if let Ok(data) = read(format!("{cobalt_dir}/profiles/{name}")) {
155-
from_file(&String::from_utf8_lossy(data.as_slice()), pm);
156-
return;
157-
}
158-
}
159-
if let Ok(home_dir) = var("HOME") {
160-
if let Ok(data) = read(format!("{home_dir}/.cobalt/profiles/{name}")) {
161-
from_file(&String::from_utf8_lossy(data.as_slice()), pm);
162-
return;
163-
}
164-
if let Ok(data) = read(format!("{home_dir}/.config/cobalt/profiles/{name}")) {
165-
from_file(&String::from_utf8_lossy(data.as_slice()), pm);
166-
return;
167-
}
168-
}
169-
if let Ok(data) = read(format!("/usr/local/share/cobalt/profiles/{name}")) {
170-
from_file(&String::from_utf8_lossy(data.as_slice()), pm);
171-
return;
172-
}
173-
if let Ok(data) = read(format!("/usr/share/cobalt/profiles/{name}")) {
174-
from_file(&String::from_utf8_lossy(data.as_slice()), pm);
175-
return;
176-
}
177-
match name {
178-
"none" | "0" => {}
179-
"less" | "1" => {
180-
let pmb = PassManagerBuilder::create();
181-
pmb.set_optimization_level(Less);
182-
pmb.populate_module_pass_manager(pm);
183-
}
184-
"some" | "2" | "default" => {
185-
let pmb = PassManagerBuilder::create();
186-
pmb.set_optimization_level(Default);
187-
pmb.populate_module_pass_manager(pm);
188-
}
189-
"aggressive" | "3" => {
190-
let pmb = PassManagerBuilder::create();
191-
pmb.set_optimization_level(Aggressive);
192-
pmb.populate_module_pass_manager(pm);
193-
}
194-
_ => {
195-
error!("couldn't find profile {name}");
196-
exit(103);
197-
}
63+
64+
fn non_ident(ch: char) -> bool {
65+
!matches!(ch, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' | '$')
66+
}
67+
68+
/// Expand a pass string, replacing `@name `with the contents of a pass.
69+
pub fn expand_pass_string(mut passes: &str) -> anyhow::Result<String> {
70+
let mut out = String::with_capacity(passes.len());
71+
while let Some(idx) = passes.find('@') {
72+
out += &passes[..idx];
73+
passes = &passes[(idx + 1)..];
74+
let idx = passes.find(non_ident).unwrap_or(passes.len());
75+
out += &expand_profile_name(&passes[..idx])?;
76+
passes = &passes[idx..];
19877
}
78+
out += passes;
79+
Ok(out)
19980
}

0 commit comments

Comments
 (0)