|
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; |
15 | 5 |
|
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()) |
106 | 20 | } |
107 | | - true |
108 | 21 | } |
109 | 22 |
|
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(); |
151 | 46 | } |
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(); |
167 | 53 | } |
168 | 54 | } |
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()))?, |
172 | 61 | } |
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..]; |
198 | 78 | } |
| 79 | + out += passes; |
| 80 | + Ok(out) |
199 | 81 | } |
0 commit comments