1 //! Utility routines for pretty-printing error messages.
2 
3 use crate::entity::SecondaryMap;
4 use crate::ir;
5 use crate::ir::entities::{AnyEntity, Block, Inst, Value};
6 use crate::ir::function::Function;
7 use crate::isa::TargetIsa;
8 use crate::result::CodegenError;
9 use crate::verifier::{VerifierError, VerifierErrors};
10 use crate::write::{decorate_function, FuncWriter, PlainWriter};
11 use alloc::boxed::Box;
12 use alloc::string::{String, ToString};
13 use alloc::vec::Vec;
14 use core::fmt;
15 use core::fmt::Write;
16 
17 /// Pretty-print a verifier error.
pretty_verifier_error<'a>( func: &ir::Function, isa: Option<&dyn TargetIsa>, func_w: Option<Box<dyn FuncWriter + 'a>>, errors: VerifierErrors, ) -> String18 pub fn pretty_verifier_error<'a>(
19     func: &ir::Function,
20     isa: Option<&dyn TargetIsa>,
21     func_w: Option<Box<dyn FuncWriter + 'a>>,
22     errors: VerifierErrors,
23 ) -> String {
24     let mut errors = errors.0;
25     let mut w = String::new();
26     let num_errors = errors.len();
27 
28     decorate_function(
29         &mut PrettyVerifierError(func_w.unwrap_or_else(|| Box::new(PlainWriter)), &mut errors),
30         &mut w,
31         func,
32         &isa.into(),
33     )
34     .unwrap();
35 
36     writeln!(
37         w,
38         "\n; {} verifier error{} detected (see above). Compilation aborted.",
39         num_errors,
40         if num_errors == 1 { "" } else { "s" }
41     )
42     .unwrap();
43 
44     w
45 }
46 
47 struct PrettyVerifierError<'a>(Box<dyn FuncWriter + 'a>, &'a mut Vec<VerifierError>);
48 
49 impl<'a> FuncWriter for PrettyVerifierError<'a> {
write_block_header( &mut self, w: &mut dyn Write, func: &Function, isa: Option<&dyn TargetIsa>, block: Block, indent: usize, ) -> fmt::Result50     fn write_block_header(
51         &mut self,
52         w: &mut dyn Write,
53         func: &Function,
54         isa: Option<&dyn TargetIsa>,
55         block: Block,
56         indent: usize,
57     ) -> fmt::Result {
58         pretty_block_header_error(w, func, isa, block, indent, &mut *self.0, self.1)
59     }
60 
write_instruction( &mut self, w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, isa: Option<&dyn TargetIsa>, inst: Inst, indent: usize, ) -> fmt::Result61     fn write_instruction(
62         &mut self,
63         w: &mut dyn Write,
64         func: &Function,
65         aliases: &SecondaryMap<Value, Vec<Value>>,
66         isa: Option<&dyn TargetIsa>,
67         inst: Inst,
68         indent: usize,
69     ) -> fmt::Result {
70         pretty_instruction_error(w, func, aliases, isa, inst, indent, &mut *self.0, self.1)
71     }
72 
write_entity_definition( &mut self, w: &mut dyn Write, func: &Function, entity: AnyEntity, value: &dyn fmt::Display, ) -> fmt::Result73     fn write_entity_definition(
74         &mut self,
75         w: &mut dyn Write,
76         func: &Function,
77         entity: AnyEntity,
78         value: &dyn fmt::Display,
79     ) -> fmt::Result {
80         pretty_preamble_error(w, func, entity, value, &mut *self.0, self.1)
81     }
82 }
83 
84 /// Pretty-print a function verifier error for a given block.
pretty_block_header_error( w: &mut dyn Write, func: &Function, isa: Option<&dyn TargetIsa>, cur_block: Block, indent: usize, func_w: &mut dyn FuncWriter, errors: &mut Vec<VerifierError>, ) -> fmt::Result85 fn pretty_block_header_error(
86     w: &mut dyn Write,
87     func: &Function,
88     isa: Option<&dyn TargetIsa>,
89     cur_block: Block,
90     indent: usize,
91     func_w: &mut dyn FuncWriter,
92     errors: &mut Vec<VerifierError>,
93 ) -> fmt::Result {
94     let mut s = String::new();
95     func_w.write_block_header(&mut s, func, isa, cur_block, indent)?;
96     write!(w, "{}", s)?;
97 
98     // TODO: Use drain_filter here when it gets stabilized
99     let mut i = 0;
100     let mut printed_error = false;
101     while i != errors.len() {
102         match errors[i].location {
103             ir::entities::AnyEntity::Block(block) if block == cur_block => {
104                 if !printed_error {
105                     print_arrow(w, &s)?;
106                     printed_error = true;
107                 }
108                 let err = errors.remove(i);
109                 print_error(w, err)?;
110             }
111             _ => i += 1,
112         }
113     }
114 
115     if printed_error {
116         w.write_char('\n')?;
117     }
118 
119     Ok(())
120 }
121 
122 /// Pretty-print a function verifier error for a given instruction.
pretty_instruction_error( w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, isa: Option<&dyn TargetIsa>, cur_inst: Inst, indent: usize, func_w: &mut dyn FuncWriter, errors: &mut Vec<VerifierError>, ) -> fmt::Result123 fn pretty_instruction_error(
124     w: &mut dyn Write,
125     func: &Function,
126     aliases: &SecondaryMap<Value, Vec<Value>>,
127     isa: Option<&dyn TargetIsa>,
128     cur_inst: Inst,
129     indent: usize,
130     func_w: &mut dyn FuncWriter,
131     errors: &mut Vec<VerifierError>,
132 ) -> fmt::Result {
133     let mut s = String::new();
134     func_w.write_instruction(&mut s, func, aliases, isa, cur_inst, indent)?;
135     write!(w, "{}", s)?;
136 
137     // TODO: Use drain_filter here when it gets stabilized
138     let mut i = 0;
139     let mut printed_error = false;
140     while i != errors.len() {
141         match errors[i].location {
142             ir::entities::AnyEntity::Inst(inst) if inst == cur_inst => {
143                 if !printed_error {
144                     print_arrow(w, &s)?;
145                     printed_error = true;
146                 }
147                 let err = errors.remove(i);
148                 print_error(w, err)?;
149             }
150             _ => i += 1,
151         }
152     }
153 
154     if printed_error {
155         w.write_char('\n')?;
156     }
157 
158     Ok(())
159 }
160 
pretty_preamble_error( w: &mut dyn Write, func: &Function, entity: AnyEntity, value: &dyn fmt::Display, func_w: &mut dyn FuncWriter, errors: &mut Vec<VerifierError>, ) -> fmt::Result161 fn pretty_preamble_error(
162     w: &mut dyn Write,
163     func: &Function,
164     entity: AnyEntity,
165     value: &dyn fmt::Display,
166     func_w: &mut dyn FuncWriter,
167     errors: &mut Vec<VerifierError>,
168 ) -> fmt::Result {
169     let mut s = String::new();
170     func_w.write_entity_definition(&mut s, func, entity, value)?;
171     write!(w, "{}", s)?;
172 
173     // TODO: Use drain_filter here when it gets stabilized
174     let mut i = 0;
175     let mut printed_error = false;
176     while i != errors.len() {
177         if entity == errors[i].location {
178             if !printed_error {
179                 print_arrow(w, &s)?;
180                 printed_error = true;
181             }
182             let err = errors.remove(i);
183             print_error(w, err)?;
184         } else {
185             i += 1
186         }
187     }
188 
189     if printed_error {
190         w.write_char('\n')?;
191     }
192 
193     Ok(())
194 }
195 
196 /// Prints:
197 ///    ;   ^~~~~~
print_arrow(w: &mut dyn Write, entity: &str) -> fmt::Result198 fn print_arrow(w: &mut dyn Write, entity: &str) -> fmt::Result {
199     write!(w, ";")?;
200 
201     let indent = entity.len() - entity.trim_start().len();
202     if indent != 0 {
203         write!(w, "{1:0$}^", indent - 1, "")?;
204     }
205 
206     for _ in 0..entity.trim().len() - 1 {
207         write!(w, "~")?;
208     }
209 
210     writeln!(w)
211 }
212 
213 /// Prints:
214 ///    ; error: [ERROR BODY]
print_error(w: &mut dyn Write, err: VerifierError) -> fmt::Result215 fn print_error(w: &mut dyn Write, err: VerifierError) -> fmt::Result {
216     writeln!(w, "; error: {}", err.to_string())?;
217     Ok(())
218 }
219 
220 /// Pretty-print a Cranelift error.
pretty_error(func: &ir::Function, isa: Option<&dyn TargetIsa>, err: CodegenError) -> String221 pub fn pretty_error(func: &ir::Function, isa: Option<&dyn TargetIsa>, err: CodegenError) -> String {
222     if let CodegenError::Verifier(e) = err {
223         pretty_verifier_error(func, isa, None, e)
224     } else {
225         err.to_string()
226     }
227 }
228