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