revmc_cranelift/
pretty_clif.rs

1//! This module provides the [`CommentWriter`] which makes it possible to add comments to the
2//! written cranelift IR.
3//!
4//! Modified from [`rustc_codegen_cranelift`](https://github.com/rust-lang/rustc_codegen_cranelift/blob/07633821ed63360d4d7464998c29f4f588930a03/src/pretty_clif.rs).
5
6#![allow(dead_code)]
7
8use cranelift::codegen::{
9    entity::SecondaryMap,
10    ir::{entities::AnyEntity, Block, Fact, Function, Inst, Value},
11    isa::TargetIsa,
12    write::{FuncWriter, PlainWriter},
13};
14use std::{
15    collections::{hash_map::Entry, HashMap},
16    fmt,
17    io::Write,
18    path::Path,
19};
20
21#[derive(Clone, Debug)]
22pub(crate) struct CommentWriter {
23    enabled: bool,
24    global_comments: Vec<String>,
25    entity_comments: HashMap<AnyEntity, String>,
26}
27
28impl Default for CommentWriter {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl CommentWriter {
35    pub(crate) fn new() -> Self {
36        Self {
37            enabled: should_write_ir(),
38            global_comments: Vec::new(),
39            entity_comments: HashMap::new(),
40        }
41    }
42
43    pub(crate) fn enabled(&self) -> bool {
44        self.enabled
45    }
46
47    pub(crate) fn clear(&mut self) {
48        self.global_comments.clear();
49        self.entity_comments.clear();
50    }
51
52    pub(crate) fn add_global_comment<S: Into<String>>(&mut self, comment: S) {
53        debug_assert!(self.enabled);
54        self.global_comments.push(comment.into());
55    }
56
57    pub(crate) fn add_comment<S: Into<String> + AsRef<str>, E: Into<AnyEntity>>(
58        &mut self,
59        entity: E,
60        comment: S,
61    ) {
62        debug_assert!(self.enabled);
63
64        match self.entity_comments.entry(entity.into()) {
65            Entry::Occupied(mut occ) => {
66                occ.get_mut().push('\n');
67                occ.get_mut().push_str(comment.as_ref());
68            }
69            Entry::Vacant(vac) => {
70                vac.insert(comment.into());
71            }
72        }
73    }
74}
75
76impl FuncWriter for &'_ CommentWriter {
77    fn write_preamble(
78        &mut self,
79        w: &mut dyn fmt::Write,
80        func: &Function,
81    ) -> Result<bool, fmt::Error> {
82        for comment in &self.global_comments {
83            if !comment.is_empty() {
84                writeln!(w, "; {comment}")?;
85            } else {
86                writeln!(w)?;
87            }
88        }
89        if !self.global_comments.is_empty() {
90            writeln!(w)?;
91        }
92
93        self.super_preamble(w, func)
94    }
95
96    fn write_entity_definition(
97        &mut self,
98        w: &mut dyn fmt::Write,
99        _func: &Function,
100        entity: AnyEntity,
101        value: &dyn fmt::Display,
102        maybe_fact: Option<&Fact>,
103    ) -> fmt::Result {
104        if let Some(fact) = maybe_fact {
105            write!(w, "    {entity} ! {fact} = {value}")?;
106        } else {
107            write!(w, "    {entity} = {value}")?;
108        }
109
110        if let Some(comment) = self.entity_comments.get(&entity) {
111            writeln!(w, " ; {}", comment.replace('\n', "\n; "))
112        } else {
113            writeln!(w)
114        }
115    }
116
117    fn write_block_header(
118        &mut self,
119        w: &mut dyn fmt::Write,
120        func: &Function,
121        block: Block,
122        indent: usize,
123    ) -> fmt::Result {
124        PlainWriter.write_block_header(w, func, block, indent)
125    }
126
127    fn write_instruction(
128        &mut self,
129        w: &mut dyn fmt::Write,
130        func: &Function,
131        aliases: &SecondaryMap<Value, Vec<Value>>,
132        inst: Inst,
133        indent: usize,
134    ) -> fmt::Result {
135        PlainWriter.write_instruction(w, func, aliases, inst, indent)?;
136        if let Some(comment) = self.entity_comments.get(&inst.into()) {
137            writeln!(w, "; {}", comment.replace('\n', "\n; "))?;
138        }
139        Ok(())
140    }
141}
142
143pub(crate) fn should_write_ir() -> bool {
144    cfg!(debug_assertions)
145}
146
147pub(crate) fn write_ir_file(
148    path: &Path,
149    write: impl FnOnce(&mut std::fs::File) -> std::io::Result<()>,
150) {
151    let res = std::fs::File::create(path).and_then(|mut file| write(&mut file));
152    if let Err(err) = res {
153        panic!("{err}")
154    }
155}
156
157pub(crate) fn write_clif_file(
158    path: &Path,
159    isa: &dyn TargetIsa,
160    func: &Function,
161    mut clif_comments: &CommentWriter,
162) {
163    write_ir_file(path, |file| {
164        let mut clif = String::new();
165        cranelift::codegen::write::decorate_function(&mut clif_comments, &mut clif, func).unwrap();
166
167        for flag in isa.flags().iter() {
168            writeln!(file, "set {flag}")?;
169        }
170        write!(file, "target {}", isa.triple().architecture)?;
171        for isa_flag in isa.isa_flags().iter() {
172            write!(file, " {isa_flag}")?;
173        }
174        writeln!(file, "\n")?;
175        writeln!(file)?;
176        file.write_all(clif.as_bytes())?;
177        Ok(())
178    });
179}