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