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, 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 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 
135 impl 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)]
210 mod tests {
211     use crate::{ParseOptions, parse_test};
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 }
235