]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_codegen_cranelift/src/pretty_clif.rs
Auto merge of #83271 - SparrowLii:simd_neg, r=Amanieu
[rust.git] / compiler / rustc_codegen_cranelift / src / pretty_clif.rs
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;
56 use std::io::Write;
57
58 use cranelift_codegen::{
59     entity::SecondaryMap,
60     ir::{entities::AnyEntity, function::DisplayFunctionAnnotations},
61     write::{FuncWriter, PlainWriter},
62 };
63
64 use rustc_middle::ty::layout::FnAbiExt;
65 use rustc_session::config::OutputType;
66 use rustc_target::abi::call::FnAbi;
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!("abi {:?}", FnAbi::of_instance(&RevealAllLayoutCx(tcx), instance, &[])),
83                 String::new(),
84             ]
85         } else {
86             vec![]
87         };
88
89         CommentWriter { global_comments, entity_comments: FxHashMap::default() }
90     }
91 }
92
93 #[cfg(debug_assertions)]
94 impl CommentWriter {
95     pub(crate) fn add_global_comment<S: Into<String>>(&mut self, comment: S) {
96         self.global_comments.push(comment.into());
97     }
98
99     pub(crate) fn add_comment<S: Into<String> + AsRef<str>, E: Into<AnyEntity>>(
100         &mut self,
101         entity: E,
102         comment: S,
103     ) {
104         use std::collections::hash_map::Entry;
105         match self.entity_comments.entry(entity.into()) {
106             Entry::Occupied(mut occ) => {
107                 occ.get_mut().push('\n');
108                 occ.get_mut().push_str(comment.as_ref());
109             }
110             Entry::Vacant(vac) => {
111                 vac.insert(comment.into());
112             }
113         }
114     }
115 }
116
117 impl FuncWriter for &'_ CommentWriter {
118     fn write_preamble(
119         &mut self,
120         w: &mut dyn fmt::Write,
121         func: &Function,
122         reg_info: Option<&isa::RegInfo>,
123     ) -> Result<bool, fmt::Error> {
124         for comment in &self.global_comments {
125             if !comment.is_empty() {
126                 writeln!(w, "; {}", comment)?;
127             } else {
128                 writeln!(w)?;
129             }
130         }
131         if !self.global_comments.is_empty() {
132             writeln!(w)?;
133         }
134
135         self.super_preamble(w, func, reg_info)
136     }
137
138     fn write_entity_definition(
139         &mut self,
140         w: &mut dyn fmt::Write,
141         _func: &Function,
142         entity: AnyEntity,
143         value: &dyn fmt::Display,
144     ) -> fmt::Result {
145         write!(w, "    {} = {}", entity, value)?;
146
147         if let Some(comment) = self.entity_comments.get(&entity) {
148             writeln!(w, " ; {}", comment.replace('\n', "\n; "))
149         } else {
150             writeln!(w)
151         }
152     }
153
154     fn write_block_header(
155         &mut self,
156         w: &mut dyn fmt::Write,
157         func: &Function,
158         isa: Option<&dyn isa::TargetIsa>,
159         block: Block,
160         indent: usize,
161     ) -> fmt::Result {
162         PlainWriter.write_block_header(w, func, isa, block, indent)
163     }
164
165     fn write_instruction(
166         &mut self,
167         w: &mut dyn fmt::Write,
168         func: &Function,
169         aliases: &SecondaryMap<Value, Vec<Value>>,
170         isa: Option<&dyn isa::TargetIsa>,
171         inst: Inst,
172         indent: usize,
173     ) -> fmt::Result {
174         PlainWriter.write_instruction(w, func, aliases, isa, inst, indent)?;
175         if let Some(comment) = self.entity_comments.get(&inst.into()) {
176             writeln!(w, "; {}", comment.replace('\n', "\n; "))?;
177         }
178         Ok(())
179     }
180 }
181
182 #[cfg(debug_assertions)]
183 impl FunctionCx<'_, '_, '_> {
184     pub(crate) fn add_global_comment<S: Into<String>>(&mut self, comment: S) {
185         self.clif_comments.add_global_comment(comment);
186     }
187
188     pub(crate) fn add_comment<S: Into<String> + AsRef<str>, E: Into<AnyEntity>>(
189         &mut self,
190         entity: E,
191         comment: S,
192     ) {
193         self.clif_comments.add_comment(entity, comment);
194     }
195 }
196
197 pub(crate) fn should_write_ir(tcx: TyCtxt<'_>) -> bool {
198     tcx.sess.opts.output_types.contains_key(&OutputType::LlvmAssembly)
199 }
200
201 pub(crate) fn write_ir_file<'tcx>(
202     tcx: TyCtxt<'tcx>,
203     name: &str,
204     write: impl FnOnce(&mut dyn Write) -> std::io::Result<()>,
205 ) {
206     if !should_write_ir(tcx) {
207         return;
208     }
209
210     let clif_output_dir = tcx.output_filenames(LOCAL_CRATE).with_extension("clif");
211
212     match std::fs::create_dir(&clif_output_dir) {
213         Ok(()) => {}
214         Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {}
215         res @ Err(_) => res.unwrap(),
216     }
217
218     let clif_file_name = clif_output_dir.join(name);
219
220     let res: std::io::Result<()> = try {
221         let mut file = std::fs::File::create(clif_file_name)?;
222         write(&mut file)?;
223     };
224     if let Err(err) = res {
225         tcx.sess.warn(&format!("error writing ir file: {}", err));
226     }
227 }
228
229 pub(crate) fn write_clif_file<'tcx>(
230     tcx: TyCtxt<'tcx>,
231     postfix: &str,
232     isa: Option<&dyn cranelift_codegen::isa::TargetIsa>,
233     instance: Instance<'tcx>,
234     context: &cranelift_codegen::Context,
235     mut clif_comments: &CommentWriter,
236 ) {
237     write_ir_file(tcx, &format!("{}.{}.clif", tcx.symbol_name(instance).name, postfix), |file| {
238         let value_ranges =
239             isa.map(|isa| context.build_value_labels_ranges(isa).expect("value location ranges"));
240
241         let mut clif = String::new();
242         cranelift_codegen::write::decorate_function(
243             &mut clif_comments,
244             &mut clif,
245             &context.func,
246             &DisplayFunctionAnnotations {
247                 isa: Some(&*crate::build_isa(tcx.sess)),
248                 value_ranges: value_ranges.as_ref(),
249             },
250         )
251         .unwrap();
252
253         writeln!(file, "test compile")?;
254         writeln!(file, "set is_pic")?;
255         writeln!(file, "set enable_simd")?;
256         writeln!(file, "target {} haswell", crate::target_triple(tcx.sess))?;
257         writeln!(file)?;
258         file.write_all(clif.as_bytes())?;
259         Ok(())
260     });
261 }
262
263 impl fmt::Debug for FunctionCx<'_, '_, '_> {
264     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265         writeln!(f, "{:?}", self.instance.substs)?;
266         writeln!(f, "{:?}", self.local_map)?;
267
268         let mut clif = String::new();
269         ::cranelift_codegen::write::decorate_function(
270             &mut &self.clif_comments,
271             &mut clif,
272             &self.bcx.func,
273             &DisplayFunctionAnnotations::default(),
274         )
275         .unwrap();
276         writeln!(f, "\n{}", clif)
277     }
278 }