1use std::collections::HashMap;
29use std::fmt;
30use std::ops::{Index, IndexMut};
31
32use rucc_base::float::Format;
33use rucc_base::{Idx, IdxRange, Symbol};
34use rucc_target::TargetInfo;
35use rucc_tuple::TargetTuple;
36
37use crate::func::Func;
38#[cfg(test)]
39use crate::inst::TbaaNode;
40use crate::inst::{Imm, Meta, MetaNode};
41use crate::ty::Type;
42
43pub type FuncId = Idx<Func>;
45
46pub type GlobalId = Idx<Global>;
48
49pub type AliasId = Idx<Alias>;
51
52pub type DataList = IdxRange<Datum>;
54
55#[derive(Debug)]
57pub struct Byte;
58
59pub type ByteRange = IdxRange<Byte>;
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
67pub enum Linkage {
68 #[default]
71 External,
72 Internal,
74 Weak,
78 LinkOnce,
82 Common,
85}
86
87impl Linkage {
88 #[must_use]
90 pub const fn name(self) -> &'static str {
91 match self {
92 Self::External => "external",
93 Self::Internal => "internal",
94 Self::Weak => "weak",
95 Self::LinkOnce => "linkonce",
96 Self::Common => "common",
97 }
98 }
99
100 #[must_use]
102 pub fn from_name(name: &str) -> Option<Self> {
103 Self::all().find(|linkage| linkage.name() == name)
104 }
105
106 pub fn all() -> impl Iterator<Item = Self> {
108 [Self::External, Self::Internal, Self::Weak, Self::LinkOnce, Self::Common].into_iter()
109 }
110
111 #[must_use]
114 pub const fn is_local(self) -> bool {
115 matches!(self, Self::Internal)
116 }
117
118 #[must_use]
123 pub const fn may_be_replaced(self) -> bool {
124 matches!(self, Self::Weak | Self::LinkOnce | Self::Common)
125 }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
134pub enum Visibility {
135 #[default]
138 Default,
139 Hidden,
142 Protected,
145}
146
147impl Visibility {
148 #[must_use]
150 pub const fn name(self) -> &'static str {
151 match self {
152 Self::Default => "default",
153 Self::Hidden => "hidden",
154 Self::Protected => "protected",
155 }
156 }
157
158 #[must_use]
160 pub fn from_name(name: &str) -> Option<Self> {
161 Self::all().find(|visibility| visibility.name() == name)
162 }
163
164 pub fn all() -> impl Iterator<Item = Self> {
166 [Self::Default, Self::Hidden, Self::Protected].into_iter()
167 }
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
177pub enum TlsModel {
178 #[default]
180 GlobalDynamic,
181 LocalDynamic,
183 InitialExec,
186 LocalExec,
188}
189
190impl TlsModel {
191 #[must_use]
193 pub const fn name(self) -> &'static str {
194 match self {
195 Self::GlobalDynamic => "global_dynamic",
196 Self::LocalDynamic => "local_dynamic",
197 Self::InitialExec => "initial_exec",
198 Self::LocalExec => "local_exec",
199 }
200 }
201
202 #[must_use]
204 pub fn from_name(name: &str) -> Option<Self> {
205 Self::all().find(|model| model.name() == name)
206 }
207
208 pub fn all() -> impl Iterator<Item = Self> {
210 [Self::GlobalDynamic, Self::LocalDynamic, Self::InitialExec, Self::LocalExec].into_iter()
211 }
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq)]
219pub enum Datum {
220 Zero(u64),
223 Bytes(ByteRange),
226 Scalar {
230 ty: Type,
232 value: Idx<Imm>,
234 },
235 Addr(Idx<Reloc>),
238}
239
240impl Datum {
241 #[must_use]
246 pub fn size(self, module: &Module) -> u64 {
247 match self {
248 Self::Zero(bytes) => bytes,
249 Self::Bytes(range) => range.len() as u64,
250 Self::Scalar { ty, .. } => u64::from(ty.bits().div_ceil(8)) * u64::from(ty.lanes()),
252 Self::Addr(reloc) => u64::from(module[reloc].size),
253 }
254 }
255}
256
257#[derive(Debug, Clone, Copy, PartialEq, Eq)]
259pub struct Reloc {
260 pub symbol: Symbol,
262 pub addend: i64,
264 pub size: u32,
267}
268
269#[derive(Debug, Clone)]
275pub struct Global {
276 pub name: Symbol,
278 pub size: u64,
280 pub align: u32,
282 pub linkage: Linkage,
284 pub visibility: Visibility,
286 pub tls: Option<TlsModel>,
288 pub constant: bool,
291 pub section: Option<Symbol>,
294 pub init: Option<DataList>,
296}
297
298impl Global {
299 #[must_use]
301 pub fn new(name: Symbol, size: u64, align: u32) -> Self {
302 Self {
303 name,
304 size,
305 align,
306 linkage: Linkage::External,
307 visibility: Visibility::Default,
308 tls: None,
309 constant: false,
310 section: None,
311 init: None,
312 }
313 }
314
315 #[must_use]
317 pub fn is_declaration(&self) -> bool {
318 self.init.is_none()
319 }
320}
321
322#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
324pub enum AliasKind {
325 #[default]
328 Alias,
329 IFunc,
333}
334
335impl AliasKind {
336 #[must_use]
338 pub const fn name(self) -> &'static str {
339 match self {
340 Self::Alias => "alias",
341 Self::IFunc => "ifunc",
342 }
343 }
344
345 #[must_use]
347 pub fn from_name(name: &str) -> Option<Self> {
348 match name {
349 "alias" => Some(Self::Alias),
350 "ifunc" => Some(Self::IFunc),
351 _ => None,
352 }
353 }
354}
355
356#[derive(Debug, Clone, Copy, PartialEq, Eq)]
358pub struct Alias {
359 pub name: Symbol,
361 pub target: Symbol,
363 pub kind: AliasKind,
365 pub linkage: Linkage,
367 pub visibility: Visibility,
369}
370
371impl Alias {
372 #[must_use]
374 pub fn new(name: Symbol, target: Symbol) -> Self {
375 Self {
376 name,
377 target,
378 kind: AliasKind::Alias,
379 linkage: Linkage::External,
380 visibility: Visibility::Default,
381 }
382 }
383}
384
385#[derive(Debug, Clone, Copy, PartialEq, Eq)]
387pub enum SymbolRef {
388 Func(FuncId),
390 Global(GlobalId),
392 Alias(AliasId),
394}
395
396#[derive(Debug, Clone, Copy, PartialEq, Eq)]
405pub struct DataLayout {
406 pub little_endian: bool,
408 pub pointer_bits: u32,
410 pub pointer_align: u32,
412 pub i64_align: u32,
415 pub f80_align: Option<u32>,
418 pub stack_align: u32,
420}
421
422impl DataLayout {
423 #[must_use]
431 pub fn for_target(target: &TargetInfo) -> Self {
432 Self {
433 little_endian: target.little_endian,
434 pointer_bits: target.pointer_width,
435 pointer_align: target.pointer_width,
436 i64_align: u32::try_from(target.scalars.long_long_align * 8)
440 .expect("no integer alignment is four billion bits"),
441 f80_align: match target.long_double_format {
442 Format::X87Extended => Some(128),
443 _ => None,
444 },
445 stack_align: 128,
446 }
447 }
448
449 #[must_use]
456 pub fn parse(text: &str) -> Option<Self> {
457 let mut little_endian = None;
458 let mut pointer = None;
459 let mut i64_align = None;
460 let mut f80_align = None;
461 let mut stack_align = None;
462 for field in text.split('-') {
463 let seen = match field {
464 "e" => little_endian.replace(true).is_some(),
465 "E" => little_endian.replace(false).is_some(),
466 _ if field.starts_with("p:") => {
467 let (bits, align) = field[2..].split_once(':')?;
468 pointer.replace((number(bits)?, number(align)?)).is_some()
469 }
470 _ if field.starts_with("i64:") => i64_align.replace(number(&field[4..])?).is_some(),
471 _ if field.starts_with("f80:") => f80_align.replace(number(&field[4..])?).is_some(),
472 _ if field.starts_with('S') => stack_align.replace(number(&field[1..])?).is_some(),
473 _ => return None,
474 };
475 if seen {
476 return None;
477 }
478 }
479 let (pointer_bits, pointer_align) = pointer?;
480 Some(Self {
481 little_endian: little_endian?,
482 pointer_bits,
483 pointer_align,
484 i64_align: i64_align?,
485 f80_align,
486 stack_align: stack_align?,
487 })
488 }
489}
490
491impl fmt::Display for DataLayout {
492 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493 write!(f, "{}", if self.little_endian { "e" } else { "E" })?;
494 write!(f, "-p:{}:{}", self.pointer_bits, self.pointer_align)?;
495 write!(f, "-i64:{}", self.i64_align)?;
496 if let Some(align) = self.f80_align {
497 write!(f, "-f80:{align}")?;
498 }
499 write!(f, "-S{}", self.stack_align)
500 }
501}
502
503fn number(text: &str) -> Option<u32> {
508 if text.is_empty() || (text.len() > 1 && text.starts_with('0')) {
509 return None;
510 }
511 if !text.bytes().all(|byte| byte.is_ascii_digit()) {
512 return None;
513 }
514 text.parse().ok()
515}
516
517#[derive(Debug)]
519pub struct Module {
520 pub name: Symbol,
523 pub tuple: TargetTuple,
525 pub datalayout: DataLayout,
527
528 funcs: Vec<Func>,
529 globals: Vec<Global>,
530 aliases: Vec<Alias>,
531 metadata: Vec<MetaNode>,
532
533 data: Vec<Datum>,
534 bytes: Vec<u8>,
535 imms: Vec<Imm>,
536 relocs: Vec<Reloc>,
537
538 symbols: HashMap<Symbol, SymbolRef>,
539}
540
541impl Module {
542 #[must_use]
544 pub fn new(name: Symbol, target: &TargetInfo) -> Self {
545 Self {
546 name,
547 tuple: target.tuple,
548 datalayout: DataLayout::for_target(target),
549 funcs: Vec::new(),
550 globals: Vec::new(),
551 aliases: Vec::new(),
552 metadata: Vec::new(),
553 data: Vec::new(),
554 bytes: Vec::new(),
555 imms: Vec::new(),
556 relocs: Vec::new(),
557 symbols: HashMap::new(),
558 }
559 }
560
561 pub fn add_func(&mut self, func: Func) -> FuncId {
571 let id = Idx::from_usize(self.funcs.len());
572 self.claim(func.name, SymbolRef::Func(id));
573 self.funcs.push(func);
574 id
575 }
576
577 pub fn add_global(&mut self, global: Global) -> GlobalId {
583 let id = Idx::from_usize(self.globals.len());
584 self.claim(global.name, SymbolRef::Global(id));
585 self.globals.push(global);
586 id
587 }
588
589 pub fn add_alias(&mut self, alias: Alias) -> AliasId {
598 let id = Idx::from_usize(self.aliases.len());
599 self.claim(alias.name, SymbolRef::Alias(id));
600 self.aliases.push(alias);
601 id
602 }
603
604 #[must_use]
606 pub fn lookup(&self, name: Symbol) -> Option<SymbolRef> {
607 self.symbols.get(&name).copied()
608 }
609
610 pub fn funcs(&self) -> impl Iterator<Item = FuncId> + use<> {
612 (0..self.funcs.len()).map(Idx::from_usize)
613 }
614
615 pub fn globals(&self) -> impl Iterator<Item = GlobalId> + use<> {
617 (0..self.globals.len()).map(Idx::from_usize)
618 }
619
620 pub fn aliases(&self) -> impl Iterator<Item = AliasId> + use<> {
622 (0..self.aliases.len()).map(Idx::from_usize)
623 }
624
625 fn claim(&mut self, name: Symbol, what: SymbolRef) {
626 assert!(
627 self.symbols.insert(name, what).is_none(),
628 "a module cannot have two symbols with the same name"
629 );
630 }
631
632 pub fn add_meta(&mut self, node: MetaNode) -> Meta {
640 self.metadata.push(node);
641 Idx::from_usize(self.metadata.len() - 1)
642 }
643
644 pub fn metadata(&self) -> impl Iterator<Item = Meta> + use<> {
646 (0..self.metadata.len()).map(Idx::from_usize)
647 }
648
649 pub fn push_data(&mut self, data: &[Datum]) -> DataList {
653 let start = self.data.len();
654 self.data.extend_from_slice(data);
655 DataList::new(Idx::from_usize(start), Idx::from_usize(self.data.len()))
656 }
657
658 pub fn push_bytes(&mut self, bytes: &[u8]) -> ByteRange {
660 let start = self.bytes.len();
661 self.bytes.extend_from_slice(bytes);
662 ByteRange::new(Idx::from_usize(start), Idx::from_usize(self.bytes.len()))
663 }
664
665 pub fn add_imm(&mut self, imm: Imm) -> Idx<Imm> {
667 self.imms.push(imm);
668 Idx::from_usize(self.imms.len() - 1)
669 }
670
671 pub fn add_reloc(&mut self, reloc: Reloc) -> Idx<Reloc> {
673 self.relocs.push(reloc);
674 Idx::from_usize(self.relocs.len() - 1)
675 }
676
677 #[must_use]
680 pub fn counts(&self) -> ModuleCounts {
681 ModuleCounts {
682 funcs: self.funcs.len(),
683 globals: self.globals.len(),
684 aliases: self.aliases.len(),
685 metadata: self.metadata.len(),
686 data_bytes: self.bytes.len(),
687 }
688 }
689}
690
691#[derive(Debug, Clone, Copy, PartialEq, Eq)]
693pub struct ModuleCounts {
694 pub funcs: usize,
696 pub globals: usize,
698 pub aliases: usize,
700 pub metadata: usize,
702 pub data_bytes: usize,
705}
706
707impl Index<FuncId> for Module {
708 type Output = Func;
709
710 fn index(&self, id: FuncId) -> &Func {
711 &self.funcs[id.index()]
712 }
713}
714
715impl IndexMut<FuncId> for Module {
716 fn index_mut(&mut self, id: FuncId) -> &mut Func {
717 &mut self.funcs[id.index()]
718 }
719}
720
721impl Index<GlobalId> for Module {
722 type Output = Global;
723
724 fn index(&self, id: GlobalId) -> &Global {
725 &self.globals[id.index()]
726 }
727}
728
729impl IndexMut<GlobalId> for Module {
730 fn index_mut(&mut self, id: GlobalId) -> &mut Global {
731 &mut self.globals[id.index()]
732 }
733}
734
735impl Index<AliasId> for Module {
736 type Output = Alias;
737
738 fn index(&self, id: AliasId) -> &Alias {
739 &self.aliases[id.index()]
740 }
741}
742
743impl Index<Meta> for Module {
744 type Output = MetaNode;
745
746 fn index(&self, meta: Meta) -> &MetaNode {
747 &self.metadata[meta.index()]
748 }
749}
750
751impl Index<Idx<Imm>> for Module {
752 type Output = Imm;
753
754 fn index(&self, imm: Idx<Imm>) -> &Imm {
755 &self.imms[imm.index()]
756 }
757}
758
759impl Index<Idx<Reloc>> for Module {
760 type Output = Reloc;
761
762 fn index(&self, reloc: Idx<Reloc>) -> &Reloc {
763 &self.relocs[reloc.index()]
764 }
765}
766
767impl Index<DataList> for Module {
768 type Output = [Datum];
769
770 fn index(&self, list: DataList) -> &[Datum] {
771 &self.data[list.as_usize_range()]
772 }
773}
774
775impl Index<ByteRange> for Module {
776 type Output = [u8];
777
778 fn index(&self, range: ByteRange) -> &[u8] {
779 &self.bytes[range.as_usize_range()]
780 }
781}
782
783#[cfg(test)]
784mod tests {
785 use rucc_base::Interner;
786 use rucc_target::{Arch, Env, Os, Triple};
787
788 use super::*;
789 use crate::inst::Signature;
790
791 fn target(arch: Arch, os: Os, env: Env) -> TargetInfo {
792 TargetInfo::new(Triple::new(arch, os, env))
793 }
794
795 fn linux() -> TargetInfo {
796 target(Arch::X86_64, Os::Linux, Env::Gnu)
797 }
798
799 #[test]
800 fn a_datum_is_sixteen_bytes() {
801 assert_eq!(size_of::<Datum>(), 16);
804 }
805
806 #[test]
807 fn the_layout_of_x86_64_linux_is_the_one_in_the_spec() {
808 let layout = DataLayout::for_target(&linux());
809 assert_eq!(layout.to_string(), "e-p:64:64-i64:64-f80:128-S128");
810 }
811
812 #[test]
813 fn only_x86_has_the_eighty_bit_format() {
814 assert_eq!(DataLayout::for_target(&linux()).f80_align, Some(128));
815 let arm = DataLayout::for_target(&target(Arch::Aarch64, Os::Linux, Env::Gnu));
816 assert_eq!(arm.f80_align, None);
817 assert_eq!(arm.to_string(), "e-p:64:64-i64:64-S128");
818 }
819
820 #[test]
821 fn a_layout_round_trips() {
822 for triple in [
823 Triple::new(Arch::X86_64, Os::Linux, Env::Gnu),
824 Triple::new(Arch::X86_64, Os::Darwin, Env::None),
825 Triple::new(Arch::Aarch64, Os::Darwin, Env::None),
826 Triple::new(Arch::Riscv64, Os::Linux, Env::Musl),
827 ] {
828 let layout = DataLayout::for_target(&TargetInfo::new(triple));
829 let text = layout.to_string();
830 assert_eq!(DataLayout::parse(&text), Some(layout), "{text}");
831 }
832 }
833
834 #[test]
835 fn a_layout_may_be_written_in_any_order() {
836 let text = "S128-i64:64-f80:128-p:64:64-e";
837 assert_eq!(DataLayout::parse(text), Some(DataLayout::for_target(&linux())));
838 }
839
840 #[test]
841 fn a_layout_needs_every_field_it_prints() {
842 for text in ["", "e", "e-p:64:64-S128", "e-i64:64-S128", "e-p:64:64-i64:64"] {
843 assert_eq!(DataLayout::parse(text), None, "{text}");
844 }
845 }
846
847 #[test]
848 fn a_layout_refuses_a_second_spelling() {
849 for text in ["e-p:64:064-i64:64-S128", "e-e-p:64:64-i64:64-S128", "e-p:64:64-i64:64-S128-x"]
851 {
852 assert_eq!(DataLayout::parse(text), None, "{text}");
853 }
854 }
855
856 #[test]
857 fn a_module_finds_what_it_holds() {
858 let mut names = Interner::new();
859 let mut module = Module::new(names.intern("test.c"), &linux());
860
861 let counter = names.intern("counter");
862 let sum = names.intern("sum");
863 let total = names.intern("total");
864
865 let global = module.add_global(Global::new(counter, 4, 4));
866 let func = module.add_func(Func::new(sum, Signature::new()));
867 let alias = module.add_alias(Alias::new(total, counter));
868
869 assert_eq!(module.lookup(counter), Some(SymbolRef::Global(global)));
870 assert_eq!(module.lookup(sum), Some(SymbolRef::Func(func)));
871 assert_eq!(module.lookup(total), Some(SymbolRef::Alias(alias)));
872 assert_eq!(module.lookup(names.intern("nothing")), None);
873 assert_eq!(module[alias].target, counter);
874 assert!(module[global].is_declaration());
875 assert!(module[func].is_declaration());
876 }
877
878 #[test]
879 #[should_panic(expected = "two symbols with the same name")]
880 fn a_name_means_one_thing() {
881 let mut names = Interner::new();
882 let mut module = Module::new(names.intern("test.c"), &linux());
883 let name = names.intern("x");
884 module.add_global(Global::new(name, 4, 4));
885 module.add_func(Func::new(name, Signature::new()));
886 }
887
888 #[test]
889 fn an_initializer_adds_up_to_the_size() {
890 let mut names = Interner::new();
891 let mut module = Module::new(names.intern("test.c"), &linux());
892
893 let text = names.intern("hi.str");
895 let seven = module.add_imm(Imm::int(7, Type::int(32)));
896 let bytes = module.push_bytes(b"hi\0");
897 let addr = module.add_reloc(Reloc { symbol: text, addend: 0, size: 8 });
898 let init = module.push_data(&[
899 Datum::Scalar { ty: Type::int(32), value: seven },
900 Datum::Zero(4),
901 Datum::Addr(addr),
902 Datum::Zero(8),
905 ]);
906
907 let mut global = Global::new(names.intern("entry"), 24, 8);
908 global.init = Some(init);
909 global.constant = true;
910 let id = module.add_global(global);
911
912 assert!(!module[id].is_declaration());
913 let size: u64 = module[init].iter().map(|datum| datum.size(&module)).sum();
914 assert_eq!(size, module[id].size);
915 assert_eq!(&module[bytes], b"hi\0");
916 assert_eq!(module[seven].unsigned(), 7);
917 assert_eq!(module.counts().data_bytes, 3);
918 }
919
920 #[test]
921 fn a_scalar_datum_is_as_wide_as_its_type() {
922 let mut names = Interner::new();
923 let mut module = Module::new(names.intern("test.c"), &linux());
924 let value = module.add_imm(Imm::int(0, Type::int(32)));
925 assert_eq!(Datum::Scalar { ty: Type::int(32), value }.size(&module), 4);
926 assert_eq!(Datum::Scalar { ty: Type::I1, value }.size(&module), 1);
928 assert_eq!(Datum::Scalar { ty: Type::int(24), value }.size(&module), 3);
929 assert_eq!(Datum::Scalar { ty: Type::vector(Type::int(8), 16), value }.size(&module), 16);
930 }
931
932 #[test]
933 fn the_names_round_trip() {
934 for linkage in Linkage::all() {
935 assert_eq!(Linkage::from_name(linkage.name()), Some(linkage));
936 }
937 for visibility in Visibility::all() {
938 assert_eq!(Visibility::from_name(visibility.name()), Some(visibility));
939 }
940 for model in TlsModel::all() {
941 assert_eq!(TlsModel::from_name(model.name()), Some(model));
942 }
943 for kind in [AliasKind::Alias, AliasKind::IFunc] {
944 assert_eq!(AliasKind::from_name(kind.name()), Some(kind));
945 }
946 assert_eq!(Linkage::from_name("static"), None);
947 assert_eq!(Visibility::from_name("internal"), None);
948 }
949
950 #[test]
951 fn only_internal_linkage_is_local() {
952 for linkage in Linkage::all() {
953 assert_eq!(linkage.is_local(), linkage == Linkage::Internal);
954 assert_eq!(
955 linkage.may_be_replaced(),
956 !matches!(linkage, Linkage::External | Linkage::Internal)
957 );
958 }
959 }
960
961 #[test]
962 fn metadata_is_shared_by_the_whole_module() {
963 let mut names = Interner::new();
964 let mut module = Module::new(names.intern("test.c"), &linux());
965 let char_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
966 name: names.intern("omnipotent char"),
967 parent: None,
968 offset: 0,
969 }));
970 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
971 name: names.intern("int"),
972 parent: Some(char_node),
973 offset: 0,
974 }));
975 assert_eq!(module[int_node].parent(), Some(char_node));
976 assert_eq!(module.metadata().count(), 2);
977 }
978}