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