1 //! Defines `DataContext`.
2 
3 use cranelift_codegen::binemit::{Addend, CodeOffset};
4 use cranelift_codegen::entity::PrimaryMap;
5 use cranelift_codegen::ir;
6 use std::boxed::Box;
7 use std::vec::Vec;
8 
9 /// This specifies how data is to be initialized.
10 #[derive(PartialEq, Eq, Debug)]
11 pub enum Init {
12     /// This indicates that no initialization has been specified yet.
13     Uninitialized,
14     /// Initialize the data with all zeros.
15     Zeros {
16         /// The size of the data.
17         size: usize,
18     },
19     /// Initialize the data with the specified contents.
20     Bytes {
21         /// The contents, which also implies the size of the data.
22         contents: Box<[u8]>,
23     },
24 }
25 
26 impl Init {
27     /// Return the size of the data to be initialized.
28     pub fn size(&self) -> usize {
29         match *self {
30             Self::Uninitialized => panic!("data size not initialized yet"),
31             Self::Zeros { size } => size,
32             Self::Bytes { ref contents } => contents.len(),
33         }
34     }
35 }
36 
37 /// A description of a data object.
38 pub struct DataDescription {
39     /// How the data should be initialized.
40     pub init: Init,
41     /// External function declarations.
42     pub function_decls: PrimaryMap<ir::FuncRef, ir::ExternalName>,
43     /// External data object declarations.
44     pub data_decls: PrimaryMap<ir::GlobalValue, ir::ExternalName>,
45     /// Function addresses to write at specified offsets.
46     pub function_relocs: Vec<(CodeOffset, ir::FuncRef)>,
47     /// Data addresses to write at specified offsets.
48     pub data_relocs: Vec<(CodeOffset, ir::GlobalValue, Addend)>,
49 }
50 
51 /// This is to data objects what cranelift_codegen::Context is to functions.
52 pub struct DataContext {
53     description: DataDescription,
54 }
55 
56 impl DataContext {
57     /// Allocate a new context.
58     pub fn new() -> Self {
59         Self {
60             description: DataDescription {
61                 init: Init::Uninitialized,
62                 function_decls: PrimaryMap::new(),
63                 data_decls: PrimaryMap::new(),
64                 function_relocs: vec![],
65                 data_relocs: vec![],
66             },
67         }
68     }
69 
70     /// Clear all data structures in this context.
71     pub fn clear(&mut self) {
72         self.description.init = Init::Uninitialized;
73         self.description.function_decls.clear();
74         self.description.data_decls.clear();
75         self.description.function_relocs.clear();
76         self.description.data_relocs.clear();
77     }
78 
79     /// Define a zero-initialized object with the given size.
80     pub fn define_zeroinit(&mut self, size: usize) {
81         debug_assert_eq!(self.description.init, Init::Uninitialized);
82         self.description.init = Init::Zeros { size };
83     }
84 
85     /// Define an object initialized with the given contents.
86     ///
87     /// TODO: Can we avoid a Box here?
88     pub fn define(&mut self, contents: Box<[u8]>) {
89         debug_assert_eq!(self.description.init, Init::Uninitialized);
90         self.description.init = Init::Bytes { contents };
91     }
92 
93     /// Declare an external function import.
94     ///
95     /// Users of the `Module` API generally should call
96     /// `Module::declare_func_in_data` instead, as it takes care of generating
97     /// the appropriate `ExternalName`.
98     pub fn import_function(&mut self, name: ir::ExternalName) -> ir::FuncRef {
99         self.description.function_decls.push(name)
100     }
101 
102     /// Declares a global value import.
103     ///
104     /// TODO: Rename to import_data?
105     ///
106     /// Users of the `Module` API generally should call
107     /// `Module::declare_data_in_data` instead, as it takes care of generating
108     /// the appropriate `ExternalName`.
109     pub fn import_global_value(&mut self, name: ir::ExternalName) -> ir::GlobalValue {
110         self.description.data_decls.push(name)
111     }
112 
113     /// Write the address of `func` into the data at offset `offset`.
114     pub fn write_function_addr(&mut self, offset: CodeOffset, func: ir::FuncRef) {
115         self.description.function_relocs.push((offset, func))
116     }
117 
118     /// Write the address of `data` into the data at offset `offset`.
119     pub fn write_data_addr(&mut self, offset: CodeOffset, data: ir::GlobalValue, addend: Addend) {
120         self.description.data_relocs.push((offset, data, addend))
121     }
122 
123     /// Reference the initializer data.
124     pub fn description(&self) -> &DataDescription {
125         debug_assert!(
126             self.description.init != Init::Uninitialized,
127             "data must be initialized first"
128         );
129         &self.description
130     }
131 }
132 
133 #[cfg(test)]
134 mod tests {
135     use super::{DataContext, Init};
136     use cranelift_codegen::ir;
137 
138     #[test]
139     fn basic_data_context() {
140         let mut data_ctx = DataContext::new();
141         {
142             let description = &data_ctx.description;
143             assert_eq!(description.init, Init::Uninitialized);
144             assert!(description.function_decls.is_empty());
145             assert!(description.data_decls.is_empty());
146             assert!(description.function_relocs.is_empty());
147             assert!(description.data_relocs.is_empty());
148         }
149 
150         data_ctx.define_zeroinit(256);
151 
152         let _func_a = data_ctx.import_function(ir::ExternalName::user(0, 0));
153         let func_b = data_ctx.import_function(ir::ExternalName::user(0, 1));
154         let func_c = data_ctx.import_function(ir::ExternalName::user(1, 0));
155         let _data_a = data_ctx.import_global_value(ir::ExternalName::user(2, 2));
156         let data_b = data_ctx.import_global_value(ir::ExternalName::user(2, 3));
157 
158         data_ctx.write_function_addr(8, func_b);
159         data_ctx.write_function_addr(16, func_c);
160         data_ctx.write_data_addr(32, data_b, 27);
161 
162         {
163             let description = data_ctx.description();
164             assert_eq!(description.init, Init::Zeros { size: 256 });
165             assert_eq!(description.function_decls.len(), 3);
166             assert_eq!(description.data_decls.len(), 2);
167             assert_eq!(description.function_relocs.len(), 2);
168             assert_eq!(description.data_relocs.len(), 1);
169         }
170 
171         data_ctx.clear();
172         {
173             let description = &data_ctx.description;
174             assert_eq!(description.init, Init::Uninitialized);
175             assert!(description.function_decls.is_empty());
176             assert!(description.data_decls.is_empty());
177             assert!(description.function_relocs.is_empty());
178             assert!(description.data_relocs.is_empty());
179         }
180 
181         let contents = vec![33, 34, 35, 36];
182         let contents_clone = contents.clone();
183         data_ctx.define(contents.into_boxed_slice());
184         {
185             let description = data_ctx.description();
186             assert_eq!(
187                 description.init,
188                 Init::Bytes {
189                     contents: contents_clone.into_boxed_slice()
190                 }
191             );
192             assert_eq!(description.function_decls.len(), 0);
193             assert_eq!(description.data_decls.len(), 0);
194             assert_eq!(description.function_relocs.len(), 0);
195             assert_eq!(description.data_relocs.len(), 0);
196         }
197     }
198 }
199