cranelift_reader/
sourcemap.rs

1//! Source map associating entities with their source locations.
2//!
3//! When the parser reads in a source file, it records the locations of the
4//! definitions of entities like instructions, blocks, and values.
5//!
6//! The `SourceMap` struct defined in this module makes this mapping available
7//! to parser clients.
8
9use crate::error::{Location, ParseResult};
10use crate::lexer::split_entity_name;
11use cranelift_codegen::ir::entities::{AnyEntity, DynamicType};
12use cranelift_codegen::ir::{
13    Block, Constant, DynamicStackSlot, FuncRef, GlobalValue, JumpTable, MemoryType, SigRef,
14    StackSlot, Value,
15};
16use std::collections::HashMap;
17
18/// Mapping from entity names to source locations.
19#[derive(Debug, Default)]
20pub struct SourceMap {
21    // Store locations for entities, including instructions.
22    locations: HashMap<AnyEntity, Location>,
23}
24
25/// Read-only interface which is exposed outside the parser crate.
26impl SourceMap {
27    /// Look up a value entity.
28    pub fn contains_value(&self, v: Value) -> bool {
29        self.locations.contains_key(&v.into())
30    }
31
32    /// Look up a block entity.
33    pub fn contains_block(&self, block: Block) -> bool {
34        self.locations.contains_key(&block.into())
35    }
36
37    /// Look up a stack slot entity.
38    pub fn contains_ss(&self, ss: StackSlot) -> bool {
39        self.locations.contains_key(&ss.into())
40    }
41
42    /// Look up a dynamic stack slot entity.
43    pub fn contains_dss(&self, dss: DynamicStackSlot) -> bool {
44        self.locations.contains_key(&dss.into())
45    }
46
47    /// Look up a global value entity.
48    pub fn contains_gv(&self, gv: GlobalValue) -> bool {
49        self.locations.contains_key(&gv.into())
50    }
51
52    /// Look up a signature entity.
53    pub fn contains_sig(&self, sig: SigRef) -> bool {
54        self.locations.contains_key(&sig.into())
55    }
56
57    /// Look up a function entity.
58    pub fn contains_fn(&self, fn_: FuncRef) -> bool {
59        self.locations.contains_key(&fn_.into())
60    }
61
62    /// Look up a jump table entity.
63    pub fn contains_jt(&self, jt: JumpTable) -> bool {
64        self.locations.contains_key(&jt.into())
65    }
66
67    /// Look up a constant entity.
68    pub fn contains_constant(&self, c: Constant) -> bool {
69        self.locations.contains_key(&c.into())
70    }
71
72    /// Look up an entity by source name.
73    /// Returns the entity reference corresponding to `name`, if it exists.
74    pub fn lookup_str(&self, name: &str) -> Option<AnyEntity> {
75        split_entity_name(name).and_then(|(ent, num)| match ent {
76            "v" => Value::with_number(num).and_then(|v| {
77                if !self.contains_value(v) {
78                    None
79                } else {
80                    Some(v.into())
81                }
82            }),
83            "block" => Block::with_number(num).and_then(|block| {
84                if !self.contains_block(block) {
85                    None
86                } else {
87                    Some(block.into())
88                }
89            }),
90            "ss" => StackSlot::with_number(num).and_then(|ss| {
91                if !self.contains_ss(ss) {
92                    None
93                } else {
94                    Some(ss.into())
95                }
96            }),
97            "gv" => GlobalValue::with_number(num).and_then(|gv| {
98                if !self.contains_gv(gv) {
99                    None
100                } else {
101                    Some(gv.into())
102                }
103            }),
104            "sig" => SigRef::with_number(num).and_then(|sig| {
105                if !self.contains_sig(sig) {
106                    None
107                } else {
108                    Some(sig.into())
109                }
110            }),
111            "fn" => FuncRef::with_number(num).and_then(|fn_| {
112                if !self.contains_fn(fn_) {
113                    None
114                } else {
115                    Some(fn_.into())
116                }
117            }),
118            "jt" => JumpTable::with_number(num).and_then(|jt| {
119                if !self.contains_jt(jt) {
120                    None
121                } else {
122                    Some(jt.into())
123                }
124            }),
125            _ => None,
126        })
127    }
128
129    /// Get the source location where an entity was defined.
130    pub fn location(&self, entity: AnyEntity) -> Option<Location> {
131        self.locations.get(&entity).cloned()
132    }
133}
134
135impl SourceMap {
136    /// Create a new empty `SourceMap`.
137    pub fn new() -> Self {
138        Self {
139            locations: HashMap::new(),
140        }
141    }
142
143    /// Define the value `entity`.
144    pub fn def_value(&mut self, entity: Value, loc: Location) -> ParseResult<()> {
145        self.def_entity(entity.into(), loc)
146    }
147
148    /// Define the block `entity`.
149    pub fn def_block(&mut self, entity: Block, loc: Location) -> ParseResult<()> {
150        self.def_entity(entity.into(), loc)
151    }
152
153    /// Define the stack slot `entity`.
154    pub fn def_ss(&mut self, entity: StackSlot, loc: Location) -> ParseResult<()> {
155        self.def_entity(entity.into(), loc)
156    }
157
158    /// Define the dynamic stack slot `entity`.
159    pub fn def_dss(&mut self, entity: DynamicStackSlot, loc: Location) -> ParseResult<()> {
160        self.def_entity(entity.into(), loc)
161    }
162
163    /// Define the dynamic type `entity`.
164    pub fn def_dt(&mut self, entity: DynamicType, loc: Location) -> ParseResult<()> {
165        self.def_entity(entity.into(), loc)
166    }
167
168    /// Define the global value `entity`.
169    pub fn def_gv(&mut self, entity: GlobalValue, loc: Location) -> ParseResult<()> {
170        self.def_entity(entity.into(), loc)
171    }
172
173    /// Define the memory type `entity`.
174    pub fn def_mt(&mut self, entity: MemoryType, loc: Location) -> ParseResult<()> {
175        self.def_entity(entity.into(), loc)
176    }
177
178    /// Define the signature `entity`.
179    pub fn def_sig(&mut self, entity: SigRef, loc: Location) -> ParseResult<()> {
180        self.def_entity(entity.into(), loc)
181    }
182
183    /// Define the external function `entity`.
184    pub fn def_fn(&mut self, entity: FuncRef, loc: Location) -> ParseResult<()> {
185        self.def_entity(entity.into(), loc)
186    }
187
188    /// Define the jump table `entity`.
189    pub fn def_jt(&mut self, entity: JumpTable, loc: Location) -> ParseResult<()> {
190        self.def_entity(entity.into(), loc)
191    }
192
193    /// Define the jump table `entity`.
194    pub fn def_constant(&mut self, entity: Constant, loc: Location) -> ParseResult<()> {
195        self.def_entity(entity.into(), loc)
196    }
197
198    /// Define an entity. This can be used for instructions whose numbers never
199    /// appear in source, or implicitly defined signatures.
200    pub fn def_entity(&mut self, entity: AnyEntity, loc: Location) -> ParseResult<()> {
201        if self.locations.insert(entity, loc).is_some() {
202            err!(loc, "duplicate entity: {}", entity)
203        } else {
204            Ok(())
205        }
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use crate::{parse_test, ParseOptions};
212
213    #[test]
214    fn details() {
215        let tf = parse_test(
216            "function %detail() {
217                               ss10 = explicit_slot 13
218                             block0(v4: i32, v7: i32):
219                               v10 = iadd v4, v7
220                             }",
221            ParseOptions::default(),
222        )
223        .unwrap();
224        let map = &tf.functions[0].1.map;
225
226        assert_eq!(map.lookup_str("v0"), None);
227        assert_eq!(map.lookup_str("ss1"), None);
228        assert_eq!(map.lookup_str("ss10").unwrap().to_string(), "ss10");
229        assert_eq!(map.lookup_str("block0").unwrap().to_string(), "block0");
230        assert_eq!(map.lookup_str("v4").unwrap().to_string(), "v4");
231        assert_eq!(map.lookup_str("v7").unwrap().to_string(), "v7");
232        assert_eq!(map.lookup_str("v10").unwrap().to_string(), "v10");
233    }
234}