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