1use rucc_base::Interner;
40use rucc_ir::{AliasKind, Datum, GlobalId, Linkage, Module};
41use rucc_object::{Alias, Binding, Data, Object, Place, Reference, Reloc};
42
43use crate::Error;
44
45#[derive(Debug, Clone, Default, PartialEq, Eq)]
47pub struct Globals {
48 pub vars: Vec<Variable>,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct Variable {
57 pub name: String,
61 pub size: u64,
63 pub align: u64,
65 pub place: Place,
67 pub binding: Binding,
69 pub pieces: Vec<Piece>,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
75pub enum Piece {
76 Zero(u64),
79 Bytes(Vec<u8>),
81 Scalar(Vec<u8>),
83 Addr {
85 symbol: String,
87 addend: i64,
89 bytes: u8,
91 },
92}
93
94impl Piece {
95 #[must_use]
97 pub fn size(&self) -> u64 {
98 match self {
99 Piece::Zero(bytes) => *bytes,
100 Piece::Bytes(bytes) | Piece::Scalar(bytes) => bytes.len() as u64,
101 Piece::Addr { bytes, .. } => u64::from(*bytes),
102 }
103 }
104}
105
106impl Globals {
107 #[must_use]
112 pub fn image(&self) -> Data {
113 let mut data = Data::default();
114 for var in &self.vars {
115 let mut object = Object {
116 name: var.name.clone(),
117 bytes: Vec::new(),
118 size: var.size,
119 align: var.align,
120 place: var.place.clone(),
121 binding: var.binding,
122 relocs: Vec::new(),
123 };
124 if matches!(var.place, Place::Zero | Place::Merged) {
125 data.objects.push(object);
126 continue;
127 }
128 for piece in &var.pieces {
129 match piece {
130 Piece::Zero(bytes) => {
131 object.bytes.resize(object.bytes.len() + *bytes as usize, 0);
132 }
133 Piece::Bytes(bytes) | Piece::Scalar(bytes) => {
134 object.bytes.extend_from_slice(bytes);
135 }
136 Piece::Addr { symbol, addend, bytes } => {
137 object.relocs.push(Reloc {
140 at: object.bytes.len(),
141 symbol: symbol.clone(),
142 kind: Reference::Address { bytes: *bytes },
143 addend: *addend,
144 });
145 object.bytes.resize(object.bytes.len() + usize::from(*bytes), 0);
146 }
147 }
148 }
149 data.objects.push(object);
150 }
151 data
152 }
153}
154
155pub fn globals(module: &Module, names: &Interner) -> Result<Globals, Error> {
163 let mut out = Globals::default();
164 for id in module.globals() {
165 if module[id].is_declaration() {
166 continue;
167 }
168 out.vars.push(variable(module, names, id)?);
169 }
170 Ok(out)
171}
172
173pub fn aliases(module: &Module, names: &Interner) -> Result<Vec<Alias>, Error> {
184 let mut out = Vec::new();
185 for id in module.aliases() {
186 let alias = &module[id];
187 let name = names.resolve(alias.name).to_owned();
188 if alias.kind != AliasKind::Alias {
189 return Err(Error::IFunc { name });
190 }
191 out.push(Alias {
192 name,
193 target: names.resolve(alias.target).to_owned(),
194 binding: binding(alias.linkage),
195 });
196 }
197 Ok(out)
198}
199
200fn variable(module: &Module, names: &Interner, id: GlobalId) -> Result<Variable, Error> {
202 let global = &module[id];
203 let name = names.resolve(global.name).to_owned();
204 if global.tls.is_some() {
205 return Err(Error::Thread { name });
206 }
207 let init = global.init.expect("a definition has an image");
208
209 let mut pieces = Vec::new();
210 let mut written = 0;
211 for datum in &module[init] {
212 let piece = match *datum {
213 Datum::Zero(bytes) => Piece::Zero(bytes),
214 Datum::Bytes(range) => Piece::Bytes(module[range].to_vec()),
215 Datum::Scalar { ty, value } => {
216 if ty.lanes() != 1 {
217 let why = format!("a {ty} in an initializer");
218 return Err(Error::Image { name, why });
219 }
220 let bytes = usize::try_from(ty.bits().div_ceil(8)).expect("a scalar this wide");
221 let mut image = module[value].bits().to_le_bytes()[..bytes].to_vec();
222 if !module.datalayout.little_endian {
223 image.reverse();
224 }
225 Piece::Scalar(image)
226 }
227 Datum::Addr(idx) => {
228 let reloc = module[idx];
229 let bytes = match reloc.size {
233 4 | 8 => reloc.size as u8,
234 size => {
235 let why = format!("an address {size} bytes wide");
236 return Err(Error::Image { name, why });
237 }
238 };
239 let symbol = names.resolve(reloc.symbol).to_owned();
240 Piece::Addr { symbol, addend: reloc.addend, bytes }
241 }
242 };
243 written += piece.size();
244 pieces.push(piece);
245 }
246 if written < global.size {
249 pieces.push(Piece::Zero(global.size - written));
250 }
251
252 let place = place(module, names, id, &pieces);
253 let size = global.size.max(written);
254 let binding = binding(global.linkage);
255 Ok(Variable { name, size, align: u64::from(global.align), place, binding, pieces })
256}
257
258const fn binding(linkage: Linkage) -> Binding {
264 match linkage {
265 Linkage::Internal => Binding::Local,
266 Linkage::Weak | Linkage::LinkOnce => Binding::Weak,
267 Linkage::External | Linkage::Common => Binding::Global,
268 }
269}
270
271fn place(module: &Module, names: &Interner, id: GlobalId, pieces: &[Piece]) -> Place {
277 let global = &module[id];
278 if let Some(section) = global.section {
279 return Place::Named(names.resolve(section).to_owned());
280 }
281 if global.linkage == Linkage::Common {
282 return Place::Merged;
283 }
284 if pieces.iter().all(|piece| matches!(piece, Piece::Zero(_))) {
285 return Place::Zero;
286 }
287 if global.constant {
288 return Place::ReadOnly;
289 }
290 Place::Written
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296
297 use rucc_ir::{Alias as IrAlias, Global, Imm, Reloc as IrReloc, TlsModel, Type};
298 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
299
300 fn module(names: &mut Interner) -> Module {
302 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
303 Module::new(names.intern("t.c"), &target)
304 }
305
306 fn defined(module: &mut Module, names: &mut Interner, name: &str, data: &[Datum]) -> GlobalId {
308 let list = module.push_data(data);
309 let mut global = Global::new(names.intern(name), 4, 4);
310 global.init = Some(list);
311 module.add_global(global)
312 }
313
314 #[test]
315 fn a_declaration_is_not_a_variable_this_file_defines() {
316 let mut names = Interner::new();
317 let mut module = module(&mut names);
318 module.add_global(Global::new(names.intern("x"), 4, 4));
319 defined(&mut module, &mut names, "y", &[Datum::Zero(4)]);
320 let vars = globals(&module, &names).expect("a module of two globals").vars;
321 assert_eq!(vars.iter().map(|var| var.name.as_str()).collect::<Vec<_>>(), ["y"]);
322 }
323
324 #[test]
325 fn a_number_in_an_image_is_the_bytes_the_machine_reads_it_as() {
326 let mut names = Interner::new();
327 let mut module = module(&mut names);
328 let value = module.add_imm(Imm::int(258, Type::int(32)));
329 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
330 let vars = globals(&module, &names).expect("a module of one global").vars;
331 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![2, 1, 0, 0])]);
332 assert_eq!(vars[0].pieces[0].size(), 4);
335 }
336
337 #[test]
338 fn what_a_variable_is_decides_which_section_it_goes_in() {
339 let mut names = Interner::new();
340 let mut module = module(&mut names);
341 let value = module.add_imm(Imm::int(1, Type::int(32)));
342 let scalar = Datum::Scalar { ty: Type::int(32), value };
343
344 let zeroed = defined(&mut module, &mut names, "zeroed", &[Datum::Zero(4)]);
345 let written = defined(&mut module, &mut names, "written", &[scalar]);
346 let read_only = defined(&mut module, &mut names, "read_only", &[scalar]);
347 module[read_only].constant = true;
348 let named = defined(&mut module, &mut names, "named", &[scalar]);
349 module[named].section = Some(names.intern(".init_array"));
350 let merged = defined(&mut module, &mut names, "merged", &[Datum::Zero(4)]);
351 module[merged].linkage = Linkage::Common;
352
353 let vars = globals(&module, &names).expect("a module of five globals").vars;
354 let places: Vec<&Place> = vars.iter().map(|var| &var.place).collect();
355 assert_eq!(
356 places,
357 [
358 &Place::Zero,
359 &Place::Written,
360 &Place::ReadOnly,
361 &Place::Named(".init_array".to_owned()),
362 &Place::Merged,
363 ]
364 );
365 let _ = (zeroed, written);
366 }
367
368 #[test]
369 fn the_rest_of_an_image_the_front_end_left_off_is_zeros() {
370 let mut names = Interner::new();
371 let mut module = module(&mut names);
372 let value = module.add_imm(Imm::int(7, Type::int(8)));
373 let id =
374 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(8), value }]);
375 module[id].size = 4;
376 let vars = globals(&module, &names).expect("a module of one global").vars;
377 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![7]), Piece::Zero(3)]);
378 assert_eq!(vars[0].size, 4);
379 }
380
381 #[test]
382 fn a_variable_holding_an_address_is_a_hole_and_a_name_for_the_linker() {
383 let mut names = Interner::new();
384 let mut module = module(&mut names);
385 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 16, size: 8 });
386 let id = defined(&mut module, &mut names, "p", &[Datum::Addr(reloc)]);
387 module[id].size = 8;
388 let vars = globals(&module, &names).expect("a module of one global").vars;
389 assert_eq!(vars[0].pieces, [Piece::Addr { symbol: "y".to_owned(), addend: 16, bytes: 8 }]);
390
391 let data = Globals { vars }.image();
392 assert_eq!(data.objects[0].bytes, vec![0; 8]);
393 assert_eq!(
394 data.objects[0].relocs,
395 [Reloc {
396 at: 0,
397 symbol: "y".to_owned(),
398 kind: Reference::Address { bytes: 8 },
399 addend: 16,
400 }]
401 );
402 }
403
404 #[test]
405 fn a_variable_in_a_section_that_carries_no_image_carries_its_size_and_nothing_else() {
406 let mut names = Interner::new();
407 let mut module = module(&mut names);
408 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4096)]);
409 module[id].size = 4096;
410 let data = globals(&module, &names).expect("a module of one global").image();
411 assert_eq!(data.objects[0].place, Place::Zero);
412 assert_eq!(data.objects[0].size, 4096);
413 assert!(data.objects[0].bytes.is_empty());
415 }
416
417 #[test]
418 fn the_linkage_a_variable_had_decides_how_the_linker_sees_the_name() {
419 let mut names = Interner::new();
420 let mut module = module(&mut names);
421 for (index, (linkage, binding)) in [
422 (Linkage::External, Binding::Global),
423 (Linkage::Internal, Binding::Local),
424 (Linkage::Weak, Binding::Weak),
425 (Linkage::LinkOnce, Binding::Weak),
426 ]
427 .into_iter()
428 .enumerate()
429 {
430 let name = format!("x{index}");
431 let id = defined(&mut module, &mut names, &name, &[Datum::Zero(4)]);
432 module[id].linkage = linkage;
433 let vars = globals(&module, &names).expect("a module of globals").vars;
434 assert_eq!(vars[index].binding, binding, "{linkage:?}");
435 }
436 }
437
438 #[test]
439 fn the_linkage_an_alias_had_decides_how_the_linker_sees_the_second_name() {
440 let mut names = Interner::new();
441 let mut module = module(&mut names);
442 let target = names.intern("a");
443 for (index, (linkage, binding)) in [
444 (Linkage::External, Binding::Global),
445 (Linkage::Internal, Binding::Local),
446 (Linkage::Weak, Binding::Weak),
447 ]
448 .into_iter()
449 .enumerate()
450 {
451 let mut alias = IrAlias::new(names.intern(&format!("b{index}")), target);
452 alias.linkage = linkage;
453 module.add_alias(alias);
454 let written = aliases(&module, &names).expect("a module of aliases");
455 assert_eq!(written[index].binding, binding, "{linkage:?}");
456 assert_eq!(written[index].target, "a", "{linkage:?}");
457 }
458 }
459
460 #[test]
463 fn an_ifunc_is_refused_rather_than_written_as_an_ordinary_second_name() {
464 let mut names = Interner::new();
465 let mut module = module(&mut names);
466 let mut memcpy = IrAlias::new(names.intern("memcpy"), names.intern("pick_memcpy"));
467 memcpy.kind = AliasKind::IFunc;
468 module.add_alias(memcpy);
469 let error = aliases(&module, &names).expect_err("an ifunc");
470 assert_eq!(error, Error::IFunc { name: "memcpy".to_owned() });
471 }
472
473 #[test]
474 fn a_thread_local_variable_is_refused_rather_than_shared_between_every_thread() {
475 let mut names = Interner::new();
476 let mut module = module(&mut names);
477 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
478 module[id].tls = Some(TlsModel::GlobalDynamic);
479 let error = globals(&module, &names).expect_err("a thread-local variable");
480 assert_eq!(error, Error::Thread { name: "x".to_owned() });
481 }
482}