Skip to content

Commit 9b78e34

Browse files
authored
Merge pull request #138 from cobalt-language/llvm-override
Override `llvm-sys`'s linking
2 parents 95d591f + 7241e26 commit 9b78e34

File tree

8 files changed

+715
-323
lines changed

8 files changed

+715
-323
lines changed

cobalt-build/src/build.rs

Lines changed: 8 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,13 @@ fn build_file_2(
164162
inkwell::targets::CodeModel::Small,
165163
)
166164
.expect("failed to create target machine");
165+
ctx.module
166+
.run_passes(
167+
&opt::expand_pass_string(&opts.profile)?,
168+
&target_machine,
169+
PassBuilderOptions::create(),
170+
)
171+
.map_err(opt::PassError::from_llvm)?;
167172
if !out_path.parent().unwrap().exists() {
168173
out_path.parent().unwrap().create_dir_all_anyhow()?
169174
}

cobalt-build/src/opt.rs

Lines changed: 71 additions & 189 deletions
Original file line numberDiff line numberDiff line change
@@ -1,199 +1,81 @@
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 anyhow_std::*;
2+
use std::env::var_os;
3+
use std::path::PathBuf;
4+
use thiserror::Error;
155

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;
20-
}
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,
51-
}),
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,
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);
10+
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())
10620
}
107-
true
10821
}
10922

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-
}
149-
}
150-
}
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
29+
}),
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();
15146
}
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;
47+
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();
16753
}
16854
}
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;
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()))?,
17261
}
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-
}
62+
.to_string())
63+
}
64+
65+
fn non_ident(ch: char) -> bool {
66+
!matches!(ch, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' | '$')
67+
}
68+
69+
/// Expand a pass string, replacing `@name `with the contents of a pass.
70+
pub fn expand_pass_string(mut passes: &str) -> anyhow::Result<String> {
71+
let mut out = String::with_capacity(passes.len());
72+
while let Some(idx) = passes.find('@') {
73+
out += &passes[..idx];
74+
passes = &passes[(idx + 1)..];
75+
let idx = passes.find(non_ident).unwrap_or(passes.len());
76+
out += &expand_profile_name(&passes[..idx])?;
77+
passes = &passes[idx..];
19878
}
79+
out += passes;
80+
Ok(out)
19981
}

0 commit comments

Comments
 (0)