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 
9 use crate::error::{Location, ParseResult};
10 use crate::lexer::split_entity_name;
11 use cranelift_codegen::ir::entities::{AnyEntity, DynamicType};
12 use cranelift_codegen::ir::{
13     Block, Constant, DynamicStackSlot, FuncRef, GlobalValue, Heap, JumpTable, SigRef, StackSlot,
14     Table, Value,
15 };
16 use std::collections::HashMap;
17 
18 /// Mapping from entity names to source locations.
19 #[derive(Debug, Default)]
20 pub 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.
26 impl 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 heap entity.
53     pub fn contains_heap(&self, heap: Heap) -> bool {
54         self.locations.contains_key(&heap.into())
55     }
56 
57     /// Look up a table entity.
58     pub fn contains_table(&self, table: Table) -> bool {
59         self.locations.contains_key(&table.into())
60     }
61 
62     /// Look up a signature entity.
63     pub fn contains_sig(&self, sig: SigRef) -> bool {
64         self.locations.contains_key(&sig.into())
65     }
66 
67     /// Look up a function entity.
68     pub fn contains_fn(&self, fn_: FuncRef) -> bool {
69         self.locations.contains_key(&fn_.into())
70     }
71 
72     /// Look up a jump table entity.
73     pub fn contains_jt(&self, jt: JumpTable) -> bool {
74         self.locations.contains_key(&jt.into())
75     }
76 
77     /// Look up a constant entity.
78     pub fn contains_constant(&self, c: Constant) -> bool {
79         self.locations.contains_key(&c.into())
80     }
81 
82     /// Look up an entity by source name.
83     /// Returns the entity reference corresponding to `name`, if it exists.
84     pub fn lookup_str(&self, name: &str) -> Option<AnyEntity> {
85         split_entity_name(name).and_then(|(ent, num)| match ent {
86             "v" => Value::with_number(num).and_then(|v| {
87                 if !self.contains_value(v) {
88                     None
89                 } else {
90                     Some(v.into())
91                 }
92             }),
93             "block" => Block::with_number(num).and_then(|block| {
94                 if !self.contains_block(block) {
95                     None
96                 } else {
97                     Some(block.into())
98                 }
99             }),
100             "ss" => StackSlot::with_number(num).and_then(|ss| {
101                 if !self.contains_ss(ss) {
102                     None
103                 } else {
104                     Some(ss.into())
105                 }
106             }),
107             "gv" => GlobalValue::with_number(num).and_then(|gv| {
108                 if !self.contains_gv(gv) {
109                     None
110                 } else {
111                     Some(gv.into())
112                 }
113             }),
114             "heap" => Heap::with_number(num).and_then(|heap| {
115                 if !self.contains_heap(heap) {
116                     None
117                 } else {
118                     Some(heap.into())
119                 }
120             }),
121             "table" => Table::with_number(num).and_then(|table| {
122                 if !self.contains_table(table) {
123                     None
124                 } else {
125                     Some(table.into())
126                 }
127             }),
128             "sig" => SigRef::with_number(num).and_then(|sig| {
129                 if !self.contains_sig(sig) {
130                     None
131                 } else {
132                     Some(sig.into())
133                 }
134             }),
135             "fn" => FuncRef::with_number(num).and_then(|fn_| {
136                 if !self.contains_fn(fn_) {
137                     None
138                 } else {
139                     Some(fn_.into())
140                 }
141             }),
142             "jt" => JumpTable::with_number(num).and_then(|jt| {
143                 if !self.contains_jt(jt) {
144                     None
145                 } else {
146                     Some(jt.into())
147                 }
148             }),
149             _ => None,
150         })
151     }
152 
153     /// Get the source location where an entity was defined.
154     pub fn location(&self, entity: AnyEntity) -> Option<Location> {
155         self.locations.get(&entity).cloned()
156     }
157 }
158 
159 impl SourceMap {
160     /// Create a new empty `SourceMap`.
161     pub fn new() -> Self {
162         Self {
163             locations: HashMap::new(),
164         }
165     }
166 
167     /// Define the value `entity`.
168     pub fn def_value(&mut self, entity: Value, loc: Location) -> ParseResult<()> {
169         self.def_entity(entity.into(), loc)
170     }
171 
172     /// Define the block `entity`.
173     pub fn def_block(&mut self, entity: Block, loc: Location) -> ParseResult<()> {
174         self.def_entity(entity.into(), loc)
175     }
176 
177     /// Define the stack slot `entity`.
178     pub fn def_ss(&mut self, entity: StackSlot, loc: Location) -> ParseResult<()> {
179         self.def_entity(entity.into(), loc)
180     }
181 
182     /// Define the dynamic stack slot `entity`.
183     pub fn def_dss(&mut self, entity: DynamicStackSlot, loc: Location) -> ParseResult<()> {
184         self.def_entity(entity.into(), loc)
185     }
186 
187     /// Define the dynamic type `entity`.
188     pub fn def_dt(&mut self, entity: DynamicType, loc: Location) -> ParseResult<()> {
189         self.def_entity(entity.into(), loc)
190     }
191 
192     /// Define the global value `entity`.
193     pub fn def_gv(&mut self, entity: GlobalValue, loc: Location) -> ParseResult<()> {
194         self.def_entity(entity.into(), loc)
195     }
196 
197     /// Define the heap `entity`.
198     pub fn def_heap(&mut self, entity: Heap, loc: Location) -> ParseResult<()> {
199         self.def_entity(entity.into(), loc)
200     }
201 
202     /// Define the table `entity`.
203     pub fn def_table(&mut self, entity: Table, loc: Location) -> ParseResult<()> {
204         self.def_entity(entity.into(), loc)
205     }
206 
207     /// Define the signature `entity`.
208     pub fn def_sig(&mut self, entity: SigRef, loc: Location) -> ParseResult<()> {
209         self.def_entity(entity.into(), loc)
210     }
211 
212     /// Define the external function `entity`.
213     pub fn def_fn(&mut self, entity: FuncRef, loc: Location) -> ParseResult<()> {
214         self.def_entity(entity.into(), loc)
215     }
216 
217     /// Define the jump table `entity`.
218     pub fn def_jt(&mut self, entity: JumpTable, loc: Location) -> ParseResult<()> {
219         self.def_entity(entity.into(), loc)
220     }
221 
222     /// Define the jump table `entity`.
223     pub fn def_constant(&mut self, entity: Constant, loc: Location) -> ParseResult<()> {
224         self.def_entity(entity.into(), loc)
225     }
226 
227     /// Define an entity. This can be used for instructions whose numbers never
228     /// appear in source, or implicitly defined signatures.
229     pub fn def_entity(&mut self, entity: AnyEntity, loc: Location) -> ParseResult<()> {
230         if self.locations.insert(entity, loc).is_some() {
231             err!(loc, "duplicate entity: {}", entity)
232         } else {
233             Ok(())
234         }
235     }
236 }
237 
238 #[cfg(test)]
239 mod tests {
240     use crate::{parse_test, ParseOptions};
241 
242     #[test]
243     fn details() {
244         let tf = parse_test(
245             "function %detail() {
246                                ss10 = explicit_slot 13
247                                jt10 = jump_table [block0]
248                              block0(v4: i32, v7: i32):
249                                v10 = iadd v4, v7
250                              }",
251             ParseOptions::default(),
252         )
253         .unwrap();
254         let map = &tf.functions[0].1.map;
255 
256         assert_eq!(map.lookup_str("v0"), None);
257         assert_eq!(map.lookup_str("ss1"), None);
258         assert_eq!(map.lookup_str("ss10").unwrap().to_string(), "ss10");
259         assert_eq!(map.lookup_str("jt10").unwrap().to_string(), "jt10");
260         assert_eq!(map.lookup_str("block0").unwrap().to_string(), "block0");
261         assert_eq!(map.lookup_str("v4").unwrap().to_string(), "v4");
262         assert_eq!(map.lookup_str("v7").unwrap().to_string(), "v7");
263         assert_eq!(map.lookup_str("v10").unwrap().to_string(), "v10");
264     }
265 }
266