]>
Commit | Line | Data |
---|---|---|
29967ef6 XL |
1 | //! This module provides the [CommentWriter] which makes it possible |
2 | //! to add comments to the written cranelift ir. | |
3 | //! | |
4 | //! # Example | |
5 | //! | |
6 | //! ```clif | |
7 | //! test compile | |
8 | //! target x86_64 | |
9 | //! | |
10 | //! function u0:0(i64, i64, i64) system_v { | |
11 | //! ; symbol _ZN119_$LT$example..IsNotEmpty$u20$as$u20$mini_core..FnOnce$LT$$LP$$RF$$u27$a$u20$$RF$$u27$b$u20$$u5b$u16$u5d$$C$$RP$$GT$$GT$9call_once17he85059d5e6a760a0E | |
12 | //! ; instance Instance { def: Item(DefId(0/0:29 ~ example[8787]::{{impl}}[0]::call_once[0])), substs: [ReErased, ReErased] } | |
13 | //! ; sig ([IsNotEmpty, (&&[u16],)]; c_variadic: false)->(u8, u8) | |
14 | //! | |
15 | //! ; ssa {_2: NOT_SSA, _4: NOT_SSA, _0: NOT_SSA, _3: (empty), _1: NOT_SSA} | |
16 | //! ; msg loc.idx param pass mode ssa flags ty | |
17 | //! ; ret _0 = v0 ByRef NOT_SSA (u8, u8) | |
18 | //! ; arg _1 = v1 ByRef NOT_SSA IsNotEmpty | |
19 | //! ; arg _2.0 = v2 ByVal(types::I64) NOT_SSA &&[u16] | |
20 | //! | |
21 | //! ss0 = explicit_slot 0 ; _1: IsNotEmpty size=0 align=1,8 | |
22 | //! ss1 = explicit_slot 8 ; _2: (&&[u16],) size=8 align=8,8 | |
23 | //! ss2 = explicit_slot 8 ; _4: (&&[u16],) size=8 align=8,8 | |
24 | //! sig0 = (i64, i64, i64) system_v | |
25 | //! sig1 = (i64, i64, i64) system_v | |
26 | //! fn0 = colocated u0:6 sig1 ; Instance { def: Item(DefId(0/0:31 ~ example[8787]::{{impl}}[1]::call_mut[0])), substs: [ReErased, ReErased] } | |
27 | //! | |
28 | //! block0(v0: i64, v1: i64, v2: i64): | |
29 | //! v3 = stack_addr.i64 ss0 | |
30 | //! v4 = stack_addr.i64 ss1 | |
31 | //! store v2, v4 | |
32 | //! v5 = stack_addr.i64 ss2 | |
33 | //! jump block1 | |
34 | //! | |
35 | //! block1: | |
36 | //! nop | |
37 | //! ; _3 = &mut _1 | |
38 | //! ; _4 = _2 | |
39 | //! v6 = load.i64 v4 | |
40 | //! store v6, v5 | |
41 | //! ; | |
42 | //! ; _0 = const mini_core::FnMut::call_mut(move _3, move _4) | |
43 | //! v7 = load.i64 v5 | |
44 | //! call fn0(v0, v3, v7) | |
45 | //! jump block2 | |
46 | //! | |
47 | //! block2: | |
48 | //! nop | |
49 | //! ; | |
50 | //! ; return | |
51 | //! return | |
52 | //! } | |
53 | //! ``` | |
54 | ||
55 | use std::fmt; | |
5869c6ff | 56 | use std::io::Write; |
29967ef6 XL |
57 | |
58 | use cranelift_codegen::{ | |
59 | entity::SecondaryMap, | |
60 | ir::{entities::AnyEntity, function::DisplayFunctionAnnotations}, | |
61 | write::{FuncWriter, PlainWriter}, | |
62 | }; | |
63 | ||
5869c6ff | 64 | use rustc_middle::ty::layout::FnAbiExt; |
29967ef6 | 65 | use rustc_session::config::OutputType; |
5869c6ff | 66 | use rustc_target::abi::call::FnAbi; |
29967ef6 XL |
67 | |
68 | use crate::prelude::*; | |
69 | ||
70 | #[derive(Debug)] | |
71 | pub(crate) struct CommentWriter { | |
72 | global_comments: Vec<String>, | |
73 | entity_comments: FxHashMap<AnyEntity, String>, | |
74 | } | |
75 | ||
76 | impl CommentWriter { | |
77 | pub(crate) fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { | |
78 | let global_comments = if cfg!(debug_assertions) { | |
79 | vec![ | |
80 | format!("symbol {}", tcx.symbol_name(instance).name), | |
81 | format!("instance {:?}", instance), | |
82 | format!( | |
5869c6ff XL |
83 | "abi {:?}", |
84 | FnAbi::of_instance(&RevealAllLayoutCx(tcx), instance, &[]) | |
29967ef6 XL |
85 | ), |
86 | String::new(), | |
87 | ] | |
88 | } else { | |
89 | vec![] | |
90 | }; | |
91 | ||
92 | CommentWriter { | |
93 | global_comments, | |
94 | entity_comments: FxHashMap::default(), | |
95 | } | |
96 | } | |
97 | } | |
98 | ||
99 | #[cfg(debug_assertions)] | |
100 | impl CommentWriter { | |
101 | pub(crate) fn add_global_comment<S: Into<String>>(&mut self, comment: S) { | |
102 | self.global_comments.push(comment.into()); | |
103 | } | |
104 | ||
105 | pub(crate) fn add_comment<S: Into<String> + AsRef<str>, E: Into<AnyEntity>>( | |
106 | &mut self, | |
107 | entity: E, | |
108 | comment: S, | |
109 | ) { | |
110 | use std::collections::hash_map::Entry; | |
111 | match self.entity_comments.entry(entity.into()) { | |
112 | Entry::Occupied(mut occ) => { | |
113 | occ.get_mut().push('\n'); | |
114 | occ.get_mut().push_str(comment.as_ref()); | |
115 | } | |
116 | Entry::Vacant(vac) => { | |
117 | vac.insert(comment.into()); | |
118 | } | |
119 | } | |
120 | } | |
121 | } | |
122 | ||
123 | impl FuncWriter for &'_ CommentWriter { | |
124 | fn write_preamble( | |
125 | &mut self, | |
126 | w: &mut dyn fmt::Write, | |
127 | func: &Function, | |
128 | reg_info: Option<&isa::RegInfo>, | |
129 | ) -> Result<bool, fmt::Error> { | |
130 | for comment in &self.global_comments { | |
131 | if !comment.is_empty() { | |
132 | writeln!(w, "; {}", comment)?; | |
133 | } else { | |
134 | writeln!(w)?; | |
135 | } | |
136 | } | |
137 | if !self.global_comments.is_empty() { | |
138 | writeln!(w)?; | |
139 | } | |
140 | ||
141 | self.super_preamble(w, func, reg_info) | |
142 | } | |
143 | ||
144 | fn write_entity_definition( | |
145 | &mut self, | |
146 | w: &mut dyn fmt::Write, | |
147 | _func: &Function, | |
148 | entity: AnyEntity, | |
149 | value: &dyn fmt::Display, | |
150 | ) -> fmt::Result { | |
151 | write!(w, " {} = {}", entity, value)?; | |
152 | ||
153 | if let Some(comment) = self.entity_comments.get(&entity) { | |
154 | writeln!(w, " ; {}", comment.replace('\n', "\n; ")) | |
155 | } else { | |
156 | writeln!(w) | |
157 | } | |
158 | } | |
159 | ||
160 | fn write_block_header( | |
161 | &mut self, | |
162 | w: &mut dyn fmt::Write, | |
163 | func: &Function, | |
164 | isa: Option<&dyn isa::TargetIsa>, | |
165 | block: Block, | |
166 | indent: usize, | |
167 | ) -> fmt::Result { | |
168 | PlainWriter.write_block_header(w, func, isa, block, indent) | |
169 | } | |
170 | ||
171 | fn write_instruction( | |
172 | &mut self, | |
173 | w: &mut dyn fmt::Write, | |
174 | func: &Function, | |
175 | aliases: &SecondaryMap<Value, Vec<Value>>, | |
176 | isa: Option<&dyn isa::TargetIsa>, | |
177 | inst: Inst, | |
178 | indent: usize, | |
179 | ) -> fmt::Result { | |
180 | PlainWriter.write_instruction(w, func, aliases, isa, inst, indent)?; | |
181 | if let Some(comment) = self.entity_comments.get(&inst.into()) { | |
182 | writeln!(w, "; {}", comment.replace('\n', "\n; "))?; | |
183 | } | |
184 | Ok(()) | |
185 | } | |
186 | } | |
187 | ||
188 | #[cfg(debug_assertions)] | |
189 | impl<M: Module> FunctionCx<'_, '_, M> { | |
190 | pub(crate) fn add_global_comment<S: Into<String>>(&mut self, comment: S) { | |
191 | self.clif_comments.add_global_comment(comment); | |
192 | } | |
193 | ||
194 | pub(crate) fn add_comment<S: Into<String> + AsRef<str>, E: Into<AnyEntity>>( | |
195 | &mut self, | |
196 | entity: E, | |
197 | comment: S, | |
198 | ) { | |
199 | self.clif_comments.add_comment(entity, comment); | |
200 | } | |
201 | } | |
202 | ||
5869c6ff XL |
203 | pub(crate) fn should_write_ir(tcx: TyCtxt<'_>) -> bool { |
204 | cfg!(debug_assertions) | |
205 | || tcx | |
29967ef6 XL |
206 | .sess |
207 | .opts | |
208 | .output_types | |
209 | .contains_key(&OutputType::LlvmAssembly) | |
5869c6ff XL |
210 | } |
211 | ||
212 | pub(crate) fn write_ir_file<'tcx>( | |
213 | tcx: TyCtxt<'tcx>, | |
214 | name: &str, | |
215 | write: impl FnOnce(&mut dyn Write) -> std::io::Result<()>, | |
216 | ) { | |
217 | if !should_write_ir(tcx) { | |
29967ef6 XL |
218 | return; |
219 | } | |
220 | ||
29967ef6 XL |
221 | let clif_output_dir = tcx.output_filenames(LOCAL_CRATE).with_extension("clif"); |
222 | ||
223 | match std::fs::create_dir(&clif_output_dir) { | |
224 | Ok(()) => {} | |
225 | Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {} | |
226 | res @ Err(_) => res.unwrap(), | |
227 | } | |
228 | ||
5869c6ff | 229 | let clif_file_name = clif_output_dir.join(name); |
29967ef6 XL |
230 | |
231 | let res: std::io::Result<()> = try { | |
232 | let mut file = std::fs::File::create(clif_file_name)?; | |
5869c6ff | 233 | write(&mut file)?; |
29967ef6 XL |
234 | }; |
235 | if let Err(err) = res { | |
5869c6ff | 236 | tcx.sess.warn(&format!("error writing ir file: {}", err)); |
29967ef6 XL |
237 | } |
238 | } | |
239 | ||
5869c6ff XL |
240 | pub(crate) fn write_clif_file<'tcx>( |
241 | tcx: TyCtxt<'tcx>, | |
242 | postfix: &str, | |
243 | isa: Option<&dyn cranelift_codegen::isa::TargetIsa>, | |
244 | instance: Instance<'tcx>, | |
245 | context: &cranelift_codegen::Context, | |
246 | mut clif_comments: &CommentWriter, | |
247 | ) { | |
248 | write_ir_file( | |
249 | tcx, | |
250 | &format!("{}.{}.clif", tcx.symbol_name(instance).name, postfix), | |
251 | |file| { | |
252 | let value_ranges = isa.map(|isa| { | |
253 | context | |
254 | .build_value_labels_ranges(isa) | |
255 | .expect("value location ranges") | |
256 | }); | |
257 | ||
258 | let mut clif = String::new(); | |
259 | cranelift_codegen::write::decorate_function( | |
260 | &mut clif_comments, | |
261 | &mut clif, | |
262 | &context.func, | |
263 | &DisplayFunctionAnnotations { | |
264 | isa: Some(&*crate::build_isa(tcx.sess)), | |
265 | value_ranges: value_ranges.as_ref(), | |
266 | }, | |
267 | ) | |
268 | .unwrap(); | |
269 | ||
270 | writeln!(file, "test compile")?; | |
271 | writeln!(file, "set is_pic")?; | |
272 | writeln!(file, "set enable_simd")?; | |
273 | writeln!(file, "target {} haswell", crate::target_triple(tcx.sess))?; | |
274 | writeln!(file)?; | |
275 | file.write_all(clif.as_bytes())?; | |
276 | Ok(()) | |
277 | }, | |
278 | ); | |
279 | } | |
280 | ||
29967ef6 XL |
281 | impl<M: Module> fmt::Debug for FunctionCx<'_, '_, M> { |
282 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
283 | writeln!(f, "{:?}", self.instance.substs)?; | |
284 | writeln!(f, "{:?}", self.local_map)?; | |
285 | ||
286 | let mut clif = String::new(); | |
287 | ::cranelift_codegen::write::decorate_function( | |
288 | &mut &self.clif_comments, | |
289 | &mut clif, | |
290 | &self.bcx.func, | |
291 | &DisplayFunctionAnnotations::default(), | |
292 | ) | |
293 | .unwrap(); | |
294 | writeln!(f, "\n{}", clif) | |
295 | } | |
296 | } |