1use rucc_base::{Interner, Symbol};
40use rucc_ir::{AliasKind, Datum, GlobalId, Linkage, Module, SymbolRef};
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 addrs = Vec::new();
214 let mut written = 0;
215 for datum in &module[init] {
216 let piece = match *datum {
217 Datum::Zero(bytes) => Piece::Zero(bytes),
218 Datum::Bytes(range) => Piece::Bytes(module[range].to_vec()),
219 Datum::Scalar { ty, value } => {
220 if ty.lanes() != 1 {
221 let why = format!("a {ty} in an initializer");
222 return Err(Error::Image { name, why });
223 }
224 let bytes = usize::try_from(ty.bits().div_ceil(8)).expect("a scalar this wide");
225 let mut image = module[value].bits().to_le_bytes()[..bytes].to_vec();
226 if !module.datalayout.little_endian {
227 image.reverse();
228 }
229 Piece::Scalar(image)
230 }
231 Datum::Addr(idx) => {
232 let reloc = module[idx];
233 let bytes = match reloc.size {
237 4 | 8 => reloc.size as u8,
238 size => {
239 let why = format!("an address {size} bytes wide");
240 return Err(Error::Image { name, why });
241 }
242 };
243 let symbol = names.resolve(reloc.symbol).to_owned();
244 addrs.push(reloc.symbol);
245 Piece::Addr { symbol, addend: reloc.addend, bytes }
246 }
247 };
248 written += piece.size();
249 pieces.push(piece);
250 }
251 if written < global.size {
254 pieces.push(Piece::Zero(global.size - written));
255 }
256
257 let place = place(module, names, id, &pieces, &addrs);
258 let size = global.size.max(written);
259 let binding = binding(global.linkage);
260 Ok(Variable { name, size, align: u64::from(global.align), place, binding, pieces })
261}
262
263const fn binding(linkage: Linkage) -> Binding {
269 match linkage {
270 Linkage::Internal => Binding::Local,
271 Linkage::Weak | Linkage::LinkOnce => Binding::Weak,
272 Linkage::External | Linkage::Common => Binding::Global,
273 }
274}
275
276fn place(
288 module: &Module,
289 names: &Interner,
290 id: GlobalId,
291 pieces: &[Piece],
292 addrs: &[Symbol],
293) -> Place {
294 let global = &module[id];
295 if let Some(section) = global.section {
296 return Place::Named(names.resolve(section).to_owned());
297 }
298 if global.linkage == Linkage::Common {
299 return Place::Merged;
300 }
301 if pieces.iter().all(|piece| matches!(piece, Piece::Zero(_))) {
302 return Place::Zero;
303 }
304 if global.constant {
305 return match addrs {
306 [] => Place::ReadOnly,
307 _ => Place::RelocReadOnly {
308 local: addrs.iter().all(|&symbol| resolved_here(module, symbol)),
309 },
310 };
311 }
312 Place::Written
313}
314
315fn resolved_here(module: &Module, symbol: Symbol) -> bool {
321 match module.lookup(symbol) {
322 Some(SymbolRef::Func(id)) => {
323 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
324 }
325 Some(SymbolRef::Global(id)) => {
326 module[id].linkage == Linkage::Internal && !module[id].is_declaration()
327 }
328 Some(SymbolRef::Alias(id)) => module[id].linkage == Linkage::Internal,
329 None => false,
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336
337 use rucc_ir::{Alias as IrAlias, Global, Imm, Reloc as IrReloc, TlsModel, Type};
338 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
339
340 fn module(names: &mut Interner) -> Module {
342 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
343 Module::new(names.intern("t.c"), &target)
344 }
345
346 fn defined(module: &mut Module, names: &mut Interner, name: &str, data: &[Datum]) -> GlobalId {
348 let list = module.push_data(data);
349 let mut global = Global::new(names.intern(name), 4, 4);
350 global.init = Some(list);
351 module.add_global(global)
352 }
353
354 #[test]
355 fn a_declaration_is_not_a_variable_this_file_defines() {
356 let mut names = Interner::new();
357 let mut module = module(&mut names);
358 module.add_global(Global::new(names.intern("x"), 4, 4));
359 defined(&mut module, &mut names, "y", &[Datum::Zero(4)]);
360 let vars = globals(&module, &names).expect("a module of two globals").vars;
361 assert_eq!(vars.iter().map(|var| var.name.as_str()).collect::<Vec<_>>(), ["y"]);
362 }
363
364 #[test]
365 fn a_number_in_an_image_is_the_bytes_the_machine_reads_it_as() {
366 let mut names = Interner::new();
367 let mut module = module(&mut names);
368 let value = module.add_imm(Imm::int(258, Type::int(32)));
369 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(32), value }]);
370 let vars = globals(&module, &names).expect("a module of one global").vars;
371 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![2, 1, 0, 0])]);
372 assert_eq!(vars[0].pieces[0].size(), 4);
375 }
376
377 #[test]
378 fn what_a_variable_is_decides_which_section_it_goes_in() {
379 let mut names = Interner::new();
380 let mut module = module(&mut names);
381 let value = module.add_imm(Imm::int(1, Type::int(32)));
382 let scalar = Datum::Scalar { ty: Type::int(32), value };
383
384 let zeroed = defined(&mut module, &mut names, "zeroed", &[Datum::Zero(4)]);
385 let written = defined(&mut module, &mut names, "written", &[scalar]);
386 let read_only = defined(&mut module, &mut names, "read_only", &[scalar]);
387 module[read_only].constant = true;
388 let named = defined(&mut module, &mut names, "named", &[scalar]);
389 module[named].section = Some(names.intern(".init_array"));
390 let merged = defined(&mut module, &mut names, "merged", &[Datum::Zero(4)]);
391 module[merged].linkage = Linkage::Common;
392
393 let vars = globals(&module, &names).expect("a module of five globals").vars;
394 let places: Vec<&Place> = vars.iter().map(|var| &var.place).collect();
395 assert_eq!(
396 places,
397 [
398 &Place::Zero,
399 &Place::Written,
400 &Place::ReadOnly,
401 &Place::Named(".init_array".to_owned()),
402 &Place::Merged,
403 ]
404 );
405 let _ = (zeroed, written);
406 }
407
408 #[test]
416 fn a_constant_holding_an_address_goes_where_the_loader_may_write_it_once() {
417 let mut names = Interner::new();
418 let mut module = module(&mut names);
419 let value = module.add_imm(Imm::int(1, Type::int(32)));
420
421 let mine = defined(&mut module, &mut names, "mine", &[Datum::Zero(4)]);
422 module[mine].linkage = Linkage::Internal;
423 let theirs = module.add_global(Global::new(names.intern("theirs"), 4, 4));
424
425 let to_mine = module.add_reloc(IrReloc { symbol: module[mine].name, addend: 0, size: 8 });
426 let to_theirs =
427 module.add_reloc(IrReloc { symbol: module[theirs].name, addend: 0, size: 8 });
428
429 let plain = defined(
430 &mut module,
431 &mut names,
432 "plain",
433 &[Datum::Scalar { ty: Type::int(32), value }],
434 );
435 module[plain].constant = true;
436 let local = defined(&mut module, &mut names, "local", &[Datum::Addr(to_mine)]);
437 module[local].constant = true;
438 module[local].size = 8;
439 let far = defined(&mut module, &mut names, "far", &[Datum::Addr(to_theirs)]);
440 module[far].constant = true;
441 module[far].size = 8;
442 let both = defined(
443 &mut module,
444 &mut names,
445 "both",
446 &[Datum::Addr(to_mine), Datum::Addr(to_theirs)],
447 );
448 module[both].constant = true;
449 module[both].size = 16;
450
451 let vars = globals(&module, &names).expect("a module of five globals").vars;
452 let places: Vec<(&str, &Place)> =
453 vars.iter().map(|var| (var.name.as_str(), &var.place)).collect();
454 assert_eq!(
455 places,
456 [
457 ("mine", &Place::Zero),
458 ("plain", &Place::ReadOnly),
459 ("local", &Place::RelocReadOnly { local: true }),
460 ("far", &Place::RelocReadOnly { local: false }),
461 ("both", &Place::RelocReadOnly { local: false }),
462 ]
463 );
464 }
465
466 #[test]
467 fn the_rest_of_an_image_the_front_end_left_off_is_zeros() {
468 let mut names = Interner::new();
469 let mut module = module(&mut names);
470 let value = module.add_imm(Imm::int(7, Type::int(8)));
471 let id =
472 defined(&mut module, &mut names, "x", &[Datum::Scalar { ty: Type::int(8), value }]);
473 module[id].size = 4;
474 let vars = globals(&module, &names).expect("a module of one global").vars;
475 assert_eq!(vars[0].pieces, [Piece::Scalar(vec![7]), Piece::Zero(3)]);
476 assert_eq!(vars[0].size, 4);
477 }
478
479 #[test]
480 fn a_variable_holding_an_address_is_a_hole_and_a_name_for_the_linker() {
481 let mut names = Interner::new();
482 let mut module = module(&mut names);
483 let reloc = module.add_reloc(IrReloc { symbol: names.intern("y"), addend: 16, size: 8 });
484 let id = defined(&mut module, &mut names, "p", &[Datum::Addr(reloc)]);
485 module[id].size = 8;
486 let vars = globals(&module, &names).expect("a module of one global").vars;
487 assert_eq!(vars[0].pieces, [Piece::Addr { symbol: "y".to_owned(), addend: 16, bytes: 8 }]);
488
489 let data = Globals { vars }.image();
490 assert_eq!(data.objects[0].bytes, vec![0; 8]);
491 assert_eq!(
492 data.objects[0].relocs,
493 [Reloc {
494 at: 0,
495 symbol: "y".to_owned(),
496 kind: Reference::Address { bytes: 8 },
497 addend: 16,
498 }]
499 );
500 }
501
502 #[test]
503 fn a_variable_in_a_section_that_carries_no_image_carries_its_size_and_nothing_else() {
504 let mut names = Interner::new();
505 let mut module = module(&mut names);
506 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4096)]);
507 module[id].size = 4096;
508 let data = globals(&module, &names).expect("a module of one global").image();
509 assert_eq!(data.objects[0].place, Place::Zero);
510 assert_eq!(data.objects[0].size, 4096);
511 assert!(data.objects[0].bytes.is_empty());
513 }
514
515 #[test]
516 fn the_linkage_a_variable_had_decides_how_the_linker_sees_the_name() {
517 let mut names = Interner::new();
518 let mut module = module(&mut names);
519 for (index, (linkage, binding)) in [
520 (Linkage::External, Binding::Global),
521 (Linkage::Internal, Binding::Local),
522 (Linkage::Weak, Binding::Weak),
523 (Linkage::LinkOnce, Binding::Weak),
524 ]
525 .into_iter()
526 .enumerate()
527 {
528 let name = format!("x{index}");
529 let id = defined(&mut module, &mut names, &name, &[Datum::Zero(4)]);
530 module[id].linkage = linkage;
531 let vars = globals(&module, &names).expect("a module of globals").vars;
532 assert_eq!(vars[index].binding, binding, "{linkage:?}");
533 }
534 }
535
536 #[test]
537 fn the_linkage_an_alias_had_decides_how_the_linker_sees_the_second_name() {
538 let mut names = Interner::new();
539 let mut module = module(&mut names);
540 let target = names.intern("a");
541 for (index, (linkage, binding)) in [
542 (Linkage::External, Binding::Global),
543 (Linkage::Internal, Binding::Local),
544 (Linkage::Weak, Binding::Weak),
545 ]
546 .into_iter()
547 .enumerate()
548 {
549 let mut alias = IrAlias::new(names.intern(&format!("b{index}")), target);
550 alias.linkage = linkage;
551 module.add_alias(alias);
552 let written = aliases(&module, &names).expect("a module of aliases");
553 assert_eq!(written[index].binding, binding, "{linkage:?}");
554 assert_eq!(written[index].target, "a", "{linkage:?}");
555 }
556 }
557
558 #[test]
561 fn an_ifunc_is_refused_rather_than_written_as_an_ordinary_second_name() {
562 let mut names = Interner::new();
563 let mut module = module(&mut names);
564 let mut memcpy = IrAlias::new(names.intern("memcpy"), names.intern("pick_memcpy"));
565 memcpy.kind = AliasKind::IFunc;
566 module.add_alias(memcpy);
567 let error = aliases(&module, &names).expect_err("an ifunc");
568 assert_eq!(error, Error::IFunc { name: "memcpy".to_owned() });
569 }
570
571 #[test]
572 fn a_thread_local_variable_is_refused_rather_than_shared_between_every_thread() {
573 let mut names = Interner::new();
574 let mut module = module(&mut names);
575 let id = defined(&mut module, &mut names, "x", &[Datum::Zero(4)]);
576 module[id].tls = Some(TlsModel::GlobalDynamic);
577 let error = globals(&module, &names).expect_err("a thread-local variable");
578 assert_eq!(error, Error::Thread { name: "x".to_owned() });
579 }
580}