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