1use rucc_base::Interner;
30use rucc_ir::{Datum, GlobalId, Linkage, Module};
31use rucc_object::{Binding, Data, Object, Place, Reference, Reloc};
32
33use crate::Error;
34
35#[derive(Debug, Clone, Default, PartialEq, Eq)]
37pub struct Globals {
38 pub vars: Vec<Variable>,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct Variable {
47 pub name: String,
51 pub size: u64,
53 pub align: u64,
55 pub place: Place,
57 pub binding: Binding,
59 pub pieces: Vec<Piece>,
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
65pub enum Piece {
66 Zero(u64),
69 Bytes(Vec<u8>),
71 Scalar(Vec<u8>),
73 Addr {
75 symbol: String,
77 addend: i64,
79 bytes: u8,
81 },
82}
83
84impl Piece {
85 #[must_use]
87 pub fn size(&self) -> u64 {
88 match self {
89 Piece::Zero(bytes) => *bytes,
90 Piece::Bytes(bytes) | Piece::Scalar(bytes) => bytes.len() as u64,
91 Piece::Addr { bytes, .. } => u64::from(*bytes),
92 }
93 }
94}
95
96impl Globals {
97 #[must_use]
102 pub fn image(&self) -> Data {
103 let mut data = Data::default();
104 for var in &self.vars {
105 let mut object = Object {
106 name: var.name.clone(),
107 bytes: Vec::new(),
108 size: var.size,
109 align: var.align,
110 place: var.place.clone(),
111 binding: var.binding,
112 relocs: Vec::new(),
113 };
114 if matches!(var.place, Place::Zero | Place::Merged) {
115 data.objects.push(object);
116 continue;
117 }
118 for piece in &var.pieces {
119 match piece {
120 Piece::Zero(bytes) => {
121 object.bytes.resize(object.bytes.len() + *bytes as usize, 0);
122 }
123 Piece::Bytes(bytes) | Piece::Scalar(bytes) => {
124 object.bytes.extend_from_slice(bytes);
125 }
126 Piece::Addr { symbol, addend, bytes } => {
127 object.relocs.push(Reloc {
130 at: object.bytes.len(),
131 symbol: symbol.clone(),
132 kind: Reference::Address { bytes: *bytes },
133 addend: *addend,
134 });
135 object.bytes.resize(object.bytes.len() + usize::from(*bytes), 0);
136 }
137 }
138 }
139 data.objects.push(object);
140 }
141 data
142 }
143}
144
145pub fn globals(module: &Module, names: &Interner) -> Result<Globals, Error> {
153 let mut out = Globals::default();
154 for id in module.globals() {
155 if module[id].is_declaration() {
156 continue;
157 }
158 out.vars.push(variable(module, names, id)?);
159 }
160 Ok(out)
161}
162
163fn variable(module: &Module, names: &Interner, id: GlobalId) -> Result<Variable, Error> {
165 let global = &module[id];
166 let name = names.resolve(global.name).to_owned();
167 if global.tls.is_some() {
168 return Err(Error::Thread { name });
169 }
170 let init = global.init.expect("a definition has an image");
171
172 let mut pieces = Vec::new();
173 let mut written = 0;
174 for datum in &module[init] {
175 let piece = match *datum {
176 Datum::Zero(bytes) => Piece::Zero(bytes),
177 Datum::Bytes(range) => Piece::Bytes(module[range].to_vec()),
178 Datum::Scalar { ty, value } => {
179 if ty.lanes() != 1 {
180 let why = format!("a {ty} in an initializer");
181 return Err(Error::Image { name, why });
182 }
183 let bytes = usize::try_from(ty.bits().div_ceil(8)).expect("a scalar this wide");
184 let mut image = module[value].bits().to_le_bytes()[..bytes].to_vec();
185 if !module.datalayout.little_endian {
186 image.reverse();
187 }
188 Piece::Scalar(image)
189 }
190 Datum::Addr(idx) => {
191 let reloc = module[idx];
192 let bytes = match reloc.size {
196 4 | 8 => reloc.size as u8,
197 size => {
198 let why = format!("an address {size} bytes wide");
199 return Err(Error::Image { name, why });
200 }
201 };
202 let symbol = names.resolve(reloc.symbol).to_owned();
203 Piece::Addr { symbol, addend: reloc.addend, bytes }
204 }
205 };
206 written += piece.size();
207 pieces.push(piece);
208 }
209 if written < global.size {
212 pieces.push(Piece::Zero(global.size - written));
213 }
214
215 let place = place(module, names, id, &pieces);
216 let binding = match global.linkage {
217 Linkage::Internal => Binding::Local,
218 Linkage::Weak | Linkage::LinkOnce => Binding::Weak,
219 Linkage::External | Linkage::Common => Binding::Global,
220 };
221 let size = global.size.max(written);
222 Ok(Variable { name, size, align: u64::from(global.align), place, binding, pieces })
223}
224
225fn place(module: &Module, names: &Interner, id: GlobalId, pieces: &[Piece]) -> Place {
231 let global = &module[id];
232 if let Some(section) = global.section {
233 return Place::Named(names.resolve(section).to_owned());
234 }
235 if global.linkage == Linkage::Common {
236 return Place::Merged;
237 }
238 if pieces.iter().all(|piece| matches!(piece, Piece::Zero(_))) {
239 return Place::Zero;
240 }
241 if global.constant {
242 return Place::ReadOnly;
243 }
244 Place::Written
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 use rucc_ir::{Global, Imm, Reloc as IrReloc, TlsModel, Type};
252 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
253
254 fn module(names: &mut Interner) -> Module {
256 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
257 Module::new(names.intern("t.c"), &target)
258 }
259
260 fn defined(module: &mut Module, names: &mut Interner, name: &str, data: &[Datum]) -> GlobalId {
262 let list = module.push_data(data);
263 let mut global = Global::new(names.intern(name), 4, 4);
264 global.init = Some(list);
265 module.add_global(global)
266 }
267
268 #[test]
269 fn a_declaration_is_not_a_variable_this_file_defines() {
270 let mut names = Interner::new();
271 let mut module = module(&mut names);
272 module.add_global(Global::new(names.intern("x"), 4, 4));
273 defined(&mut module, &mut names, "y", &[Datum::Zero(4)]);
274 let vars = globals(&module, &names).expect("a module of two globals").vars;
275 assert_eq!(vars.iter().map(|var| var.name.as_str()).collect::<Vec<_>>(), ["y"]);
276 }
277
278 #[test]
279 fn a_number_in_an_image_is_the_bytes_the_machine_reads_it_as() {
280 let mut names = Interner::new();
281 let mut module = module(&mut names);
282 let value = module.add_imm(Imm::int(258, Type::int(32)));
283 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
284 let vars = globals(&module, &names).expect("a module of one global").vars;
285 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![2, 1, 0, 0])]);
286 assert_eq!(vars[0].pieces[0].size(), 4);
289 }
290
291 #[test]
292 fn what_a_variable_is_decides_which_section_it_goes_in() {
293 let mut names = Interner::new();
294 let mut module = module(&mut names);
295 let value = module.add_imm(Imm::int(1, Type::int(32)));
296 let scalar = Datum::Scalar { ty: Type::int(32), value };
297
298 let zeroed = defined(&mut module, &mut names, "zeroed", &[Datum::Zero(4)]);
299 let written = defined(&mut module, &mut names, "written", &[scalar]);
300 let read_only = defined(&mut module, &mut names, "read_only", &[scalar]);
301 module[read_only].constant = true;
302 let named = defined(&mut module, &mut names, "named", &[scalar]);
303 module[named].section = Some(names.intern(".init_array"));
304 let merged = defined(&mut module, &mut names, "merged", &[Datum::Zero(4)]);
305 module[merged].linkage = Linkage::Common;
306
307 let vars = globals(&module, &names).expect("a module of five globals").vars;
308 let places: Vec<&Place> = vars.iter().map(|var| &var.place).collect();
309 assert_eq!(
310 places,
311 [
312 &Place::Zero,
313 &Place::Written,
314 &Place::ReadOnly,
315 &Place::Named(".init_array".to_owned()),
316 &Place::Merged,
317 ]
318 );
319 let _ = (zeroed, written);
320 }
321
322 #[test]
323 fn the_rest_of_an_image_the_front_end_left_off_is_zeros() {
324 let mut names = Interner::new();
325 let mut module = module(&mut names);
326 let value = module.add_imm(Imm::int(7, Type::int(8)));
327 let id =
328 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(8), value }]);
329 module[id].size = 4;
330 let vars = globals(&module, &names).expect("a module of one global").vars;
331 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![7]), Piece::Zero(3)]);
332 assert_eq!(vars[0].size, 4);
333 }
334
335 #[test]
336 fn a_variable_holding_an_address_is_a_hole_and_a_name_for_the_linker() {
337 let mut names = Interner::new();
338 let mut module = module(&mut names);
339 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 16, size: 8 });
340 let id = defined(&mut module, &mut names, "p", &[Datum::Addr(reloc)]);
341 module[id].size = 8;
342 let vars = globals(&module, &names).expect("a module of one global").vars;
343 assert_eq!(vars[0].pieces, [Piece::Addr { symbol: "y".to_owned(), addend: 16, bytes: 8 }]);
344
345 let data = Globals { vars }.image();
346 assert_eq!(data.objects[0].bytes, vec![0; 8]);
347 assert_eq!(
348 data.objects[0].relocs,
349 [Reloc {
350 at: 0,
351 symbol: "y".to_owned(),
352 kind: Reference::Address { bytes: 8 },
353 addend: 16,
354 }]
355 );
356 }
357
358 #[test]
359 fn a_variable_in_a_section_that_carries_no_image_carries_its_size_and_nothing_else() {
360 let mut names = Interner::new();
361 let mut module = module(&mut names);
362 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4096)]);
363 module[id].size = 4096;
364 let data = globals(&module, &names).expect("a module of one global").image();
365 assert_eq!(data.objects[0].place, Place::Zero);
366 assert_eq!(data.objects[0].size, 4096);
367 assert!(data.objects[0].bytes.is_empty());
369 }
370
371 #[test]
372 fn the_linkage_a_variable_had_decides_how_the_linker_sees_the_name() {
373 let mut names = Interner::new();
374 let mut module = module(&mut names);
375 for (index, (linkage, binding)) in [
376 (Linkage::External, Binding::Global),
377 (Linkage::Internal, Binding::Local),
378 (Linkage::Weak, Binding::Weak),
379 (Linkage::LinkOnce, Binding::Weak),
380 ]
381 .into_iter()
382 .enumerate()
383 {
384 let name = format!("x{index}");
385 let id = defined(&mut module, &mut names, &name, &[Datum::Zero(4)]);
386 module[id].linkage = linkage;
387 let vars = globals(&module, &names).expect("a module of globals").vars;
388 assert_eq!(vars[index].binding, binding, "{linkage:?}");
389 }
390 }
391
392 #[test]
393 fn a_thread_local_variable_is_refused_rather_than_shared_between_every_thread() {
394 let mut names = Interner::new();
395 let mut module = module(&mut names);
396 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
397 module[id].tls = Some(TlsModel::GlobalDynamic);
398 let error = globals(&module, &names).expect_err("a thread-local variable");
399 assert_eq!(error, Error::Thread { name: "x".to_owned() });
400 }
401}