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, Triple};
35
36use crate::func::Func;
37#[cfg(test)]
38use crate::inst::TbaaNode;
39use crate::inst::{Imm, Meta, MetaNode};
40use crate::ty::Type;
41
42pub type FuncId = Idx<Func>;
44
45pub type GlobalId = Idx<Global>;
47
48pub type AliasId = Idx<Alias>;
50
51pub type DataList = IdxRange<Datum>;
53
54#[derive(Debug)]
56pub struct Byte;
57
58pub type ByteRange = IdxRange<Byte>;
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
66pub enum Linkage {
67 #[default]
70 External,
71 Internal,
73 Weak,
77 LinkOnce,
81 Common,
84}
85
86impl Linkage {
87 #[must_use]
89 pub const fn name(self) -> &'static str {
90 match self {
91 Self::External => "external",
92 Self::Internal => "internal",
93 Self::Weak => "weak",
94 Self::LinkOnce => "linkonce",
95 Self::Common => "common",
96 }
97 }
98
99 #[must_use]
101 pub fn from_name(name: &str) -> Option<Self> {
102 Self::all().find(|linkage| linkage.name() == name)
103 }
104
105 pub fn all() -> impl Iterator<Item = Self> {
107 [Self::External, Self::Internal, Self::Weak, Self::LinkOnce, Self::Common].into_iter()
108 }
109
110 #[must_use]
113 pub const fn is_local(self) -> bool {
114 matches!(self, Self::Internal)
115 }
116
117 #[must_use]
122 pub const fn may_be_replaced(self) -> bool {
123 matches!(self, Self::Weak | Self::LinkOnce | Self::Common)
124 }
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
133pub enum Visibility {
134 #[default]
137 Default,
138 Hidden,
141 Protected,
144}
145
146impl Visibility {
147 #[must_use]
149 pub const fn name(self) -> &'static str {
150 match self {
151 Self::Default => "default",
152 Self::Hidden => "hidden",
153 Self::Protected => "protected",
154 }
155 }
156
157 #[must_use]
159 pub fn from_name(name: &str) -> Option<Self> {
160 Self::all().find(|visibility| visibility.name() == name)
161 }
162
163 pub fn all() -> impl Iterator<Item = Self> {
165 [Self::Default, Self::Hidden, Self::Protected].into_iter()
166 }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
176pub enum TlsModel {
177 #[default]
179 GlobalDynamic,
180 LocalDynamic,
182 InitialExec,
185 LocalExec,
187}
188
189impl TlsModel {
190 #[must_use]
192 pub const fn name(self) -> &'static str {
193 match self {
194 Self::GlobalDynamic => "global_dynamic",
195 Self::LocalDynamic => "local_dynamic",
196 Self::InitialExec => "initial_exec",
197 Self::LocalExec => "local_exec",
198 }
199 }
200
201 #[must_use]
203 pub fn from_name(name: &str) -> Option<Self> {
204 Self::all().find(|model| model.name() == name)
205 }
206
207 pub fn all() -> impl Iterator<Item = Self> {
209 [Self::GlobalDynamic, Self::LocalDynamic, Self::InitialExec, Self::LocalExec].into_iter()
210 }
211}
212
213#[derive(Debug, Clone, Copy, PartialEq, Eq)]
218pub enum Datum {
219 Zero(u64),
222 Bytes(ByteRange),
225 Scalar {
229 ty: Type,
231 value: Idx<Imm>,
233 },
234 Addr(Idx<Reloc>),
237}
238
239impl Datum {
240 #[must_use]
245 pub fn size(self, module: &Module) -> u64 {
246 match self {
247 Self::Zero(bytes) => bytes,
248 Self::Bytes(range) => range.len() as u64,
249 Self::Scalar { ty, .. } => u64::from(ty.bits().div_ceil(8)) * u64::from(ty.lanes()),
251 Self::Addr(reloc) => u64::from(module[reloc].size),
252 }
253 }
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
258pub struct Reloc {
259 pub symbol: Symbol,
261 pub addend: i64,
263 pub size: u32,
266}
267
268#[derive(Debug, Clone)]
274pub struct Global {
275 pub name: Symbol,
277 pub size: u64,
279 pub align: u32,
281 pub linkage: Linkage,
283 pub visibility: Visibility,
285 pub tls: Option<TlsModel>,
287 pub constant: bool,
290 pub section: Option<Symbol>,
293 pub init: Option<DataList>,
295}
296
297impl Global {
298 #[must_use]
300 pub fn new(name: Symbol, size: u64, align: u32) -> Self {
301 Self {
302 name,
303 size,
304 align,
305 linkage: Linkage::External,
306 visibility: Visibility::Default,
307 tls: None,
308 constant: false,
309 section: None,
310 init: None,
311 }
312 }
313
314 #[must_use]
316 pub fn is_declaration(&self) -> bool {
317 self.init.is_none()
318 }
319}
320
321#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
323pub enum AliasKind {
324 #[default]
327 Alias,
328 IFunc,
332}
333
334impl AliasKind {
335 #[must_use]
337 pub const fn name(self) -> &'static str {
338 match self {
339 Self::Alias => "alias",
340 Self::IFunc => "ifunc",
341 }
342 }
343
344 #[must_use]
346 pub fn from_name(name: &str) -> Option<Self> {
347 match name {
348 "alias" => Some(Self::Alias),
349 "ifunc" => Some(Self::IFunc),
350 _ => None,
351 }
352 }
353}
354
355#[derive(Debug, Clone, Copy, PartialEq, Eq)]
357pub struct Alias {
358 pub name: Symbol,
360 pub target: Symbol,
362 pub kind: AliasKind,
364 pub linkage: Linkage,
366 pub visibility: Visibility,
368}
369
370impl Alias {
371 #[must_use]
373 pub fn new(name: Symbol, target: Symbol) -> Self {
374 Self {
375 name,
376 target,
377 kind: AliasKind::Alias,
378 linkage: Linkage::External,
379 visibility: Visibility::Default,
380 }
381 }
382}
383
384#[derive(Debug, Clone, Copy, PartialEq, Eq)]
386pub enum SymbolRef {
387 Func(FuncId),
389 Global(GlobalId),
391 Alias(AliasId),
393}
394
395#[derive(Debug, Clone, Copy, PartialEq, Eq)]
404pub struct DataLayout {
405 pub little_endian: bool,
407 pub pointer_bits: u32,
409 pub pointer_align: u32,
411 pub i64_align: u32,
414 pub f80_align: Option<u32>,
417 pub stack_align: u32,
419}
420
421impl DataLayout {
422 #[must_use]
424 pub fn for_target(target: &TargetInfo) -> Self {
425 Self {
426 little_endian: target.little_endian,
427 pointer_bits: target.pointer_width,
428 pointer_align: target.pointer_width,
429 i64_align: 64,
433 f80_align: match target.long_double_format {
434 Format::X87Extended => Some(128),
435 _ => None,
436 },
437 stack_align: 128,
438 }
439 }
440
441 #[must_use]
448 pub fn parse(text: &str) -> Option<Self> {
449 let mut little_endian = None;
450 let mut pointer = None;
451 let mut i64_align = None;
452 let mut f80_align = None;
453 let mut stack_align = None;
454 for field in text.split('-') {
455 let seen = match field {
456 "e" => little_endian.replace(true).is_some(),
457 "E" => little_endian.replace(false).is_some(),
458 _ if field.starts_with("p:") => {
459 let (bits, align) = field[2..].split_once(':')?;
460 pointer.replace((number(bits)?, number(align)?)).is_some()
461 }
462 _ if field.starts_with("i64:") => i64_align.replace(number(&field[4..])?).is_some(),
463 _ if field.starts_with("f80:") => f80_align.replace(number(&field[4..])?).is_some(),
464 _ if field.starts_with('S') => stack_align.replace(number(&field[1..])?).is_some(),
465 _ => return None,
466 };
467 if seen {
468 return None;
469 }
470 }
471 let (pointer_bits, pointer_align) = pointer?;
472 Some(Self {
473 little_endian: little_endian?,
474 pointer_bits,
475 pointer_align,
476 i64_align: i64_align?,
477 f80_align,
478 stack_align: stack_align?,
479 })
480 }
481}
482
483impl fmt::Display for DataLayout {
484 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
485 write!(f, "{}", if self.little_endian { "e" } else { "E" })?;
486 write!(f, "-p:{}:{}", self.pointer_bits, self.pointer_align)?;
487 write!(f, "-i64:{}", self.i64_align)?;
488 if let Some(align) = self.f80_align {
489 write!(f, "-f80:{align}")?;
490 }
491 write!(f, "-S{}", self.stack_align)
492 }
493}
494
495fn number(text: &str) -> Option<u32> {
500 if text.is_empty() || (text.len() > 1 && text.starts_with('0')) {
501 return None;
502 }
503 if !text.bytes().all(|byte| byte.is_ascii_digit()) {
504 return None;
505 }
506 text.parse().ok()
507}
508
509#[derive(Debug)]
511pub struct Module {
512 pub name: Symbol,
515 pub triple: Triple,
517 pub datalayout: DataLayout,
519
520 funcs: Vec<Func>,
521 globals: Vec<Global>,
522 aliases: Vec<Alias>,
523 metadata: Vec<MetaNode>,
524
525 data: Vec<Datum>,
526 bytes: Vec<u8>,
527 imms: Vec<Imm>,
528 relocs: Vec<Reloc>,
529
530 symbols: HashMap<Symbol, SymbolRef>,
531}
532
533impl Module {
534 #[must_use]
536 pub fn new(name: Symbol, target: &TargetInfo) -> Self {
537 Self {
538 name,
539 triple: target.triple,
540 datalayout: DataLayout::for_target(target),
541 funcs: Vec::new(),
542 globals: Vec::new(),
543 aliases: Vec::new(),
544 metadata: Vec::new(),
545 data: Vec::new(),
546 bytes: Vec::new(),
547 imms: Vec::new(),
548 relocs: Vec::new(),
549 symbols: HashMap::new(),
550 }
551 }
552
553 pub fn add_func(&mut self, func: Func) -> FuncId {
563 let id = Idx::from_usize(self.funcs.len());
564 self.claim(func.name, SymbolRef::Func(id));
565 self.funcs.push(func);
566 id
567 }
568
569 pub fn add_global(&mut self, global: Global) -> GlobalId {
575 let id = Idx::from_usize(self.globals.len());
576 self.claim(global.name, SymbolRef::Global(id));
577 self.globals.push(global);
578 id
579 }
580
581 pub fn add_alias(&mut self, alias: Alias) -> AliasId {
590 let id = Idx::from_usize(self.aliases.len());
591 self.claim(alias.name, SymbolRef::Alias(id));
592 self.aliases.push(alias);
593 id
594 }
595
596 #[must_use]
598 pub fn lookup(&self, name: Symbol) -> Option<SymbolRef> {
599 self.symbols.get(&name).copied()
600 }
601
602 pub fn funcs(&self) -> impl Iterator<Item = FuncId> + use<> {
604 (0..self.funcs.len()).map(Idx::from_usize)
605 }
606
607 pub fn globals(&self) -> impl Iterator<Item = GlobalId> + use<> {
609 (0..self.globals.len()).map(Idx::from_usize)
610 }
611
612 pub fn aliases(&self) -> impl Iterator<Item = AliasId> + use<> {
614 (0..self.aliases.len()).map(Idx::from_usize)
615 }
616
617 fn claim(&mut self, name: Symbol, what: SymbolRef) {
618 assert!(
619 self.symbols.insert(name, what).is_none(),
620 "a module cannot have two symbols with the same name"
621 );
622 }
623
624 pub fn add_meta(&mut self, node: MetaNode) -> Meta {
632 self.metadata.push(node);
633 Idx::from_usize(self.metadata.len() - 1)
634 }
635
636 pub fn metadata(&self) -> impl Iterator<Item = Meta> + use<> {
638 (0..self.metadata.len()).map(Idx::from_usize)
639 }
640
641 pub fn push_data(&mut self, data: &[Datum]) -> DataList {
645 let start = self.data.len();
646 self.data.extend_from_slice(data);
647 DataList::new(Idx::from_usize(start), Idx::from_usize(self.data.len()))
648 }
649
650 pub fn push_bytes(&mut self, bytes: &[u8]) -> ByteRange {
652 let start = self.bytes.len();
653 self.bytes.extend_from_slice(bytes);
654 ByteRange::new(Idx::from_usize(start), Idx::from_usize(self.bytes.len()))
655 }
656
657 pub fn add_imm(&mut self, imm: Imm) -> Idx<Imm> {
659 self.imms.push(imm);
660 Idx::from_usize(self.imms.len() - 1)
661 }
662
663 pub fn add_reloc(&mut self, reloc: Reloc) -> Idx<Reloc> {
665 self.relocs.push(reloc);
666 Idx::from_usize(self.relocs.len() - 1)
667 }
668
669 #[must_use]
672 pub fn counts(&self) -> ModuleCounts {
673 ModuleCounts {
674 funcs: self.funcs.len(),
675 globals: self.globals.len(),
676 aliases: self.aliases.len(),
677 metadata: self.metadata.len(),
678 data_bytes: self.bytes.len(),
679 }
680 }
681}
682
683#[derive(Debug, Clone, Copy, PartialEq, Eq)]
685pub struct ModuleCounts {
686 pub funcs: usize,
688 pub globals: usize,
690 pub aliases: usize,
692 pub metadata: usize,
694 pub data_bytes: usize,
697}
698
699impl Index<FuncId> for Module {
700 type Output = Func;
701
702 fn index(&self, id: FuncId) -> &Func {
703 &self.funcs[id.index()]
704 }
705}
706
707impl IndexMut<FuncId> for Module {
708 fn index_mut(&mut self, id: FuncId) -> &mut Func {
709 &mut self.funcs[id.index()]
710 }
711}
712
713impl Index<GlobalId> for Module {
714 type Output = Global;
715
716 fn index(&self, id: GlobalId) -> &Global {
717 &self.globals[id.index()]
718 }
719}
720
721impl IndexMut<GlobalId> for Module {
722 fn index_mut(&mut self, id: GlobalId) -> &mut Global {
723 &mut self.globals[id.index()]
724 }
725}
726
727impl Index<AliasId> for Module {
728 type Output = Alias;
729
730 fn index(&self, id: AliasId) -> &Alias {
731 &self.aliases[id.index()]
732 }
733}
734
735impl Index<Meta> for Module {
736 type Output = MetaNode;
737
738 fn index(&self, meta: Meta) -> &MetaNode {
739 &self.metadata[meta.index()]
740 }
741}
742
743impl Index<Idx<Imm>> for Module {
744 type Output = Imm;
745
746 fn index(&self, imm: Idx<Imm>) -> &Imm {
747 &self.imms[imm.index()]
748 }
749}
750
751impl Index<Idx<Reloc>> for Module {
752 type Output = Reloc;
753
754 fn index(&self, reloc: Idx<Reloc>) -> &Reloc {
755 &self.relocs[reloc.index()]
756 }
757}
758
759impl Index<DataList> for Module {
760 type Output = [Datum];
761
762 fn index(&self, list: DataList) -> &[Datum] {
763 &self.data[list.as_usize_range()]
764 }
765}
766
767impl Index<ByteRange> for Module {
768 type Output = [u8];
769
770 fn index(&self, range: ByteRange) -> &[u8] {
771 &self.bytes[range.as_usize_range()]
772 }
773}
774
775#[cfg(test)]
776mod tests {
777 use rucc_base::Interner;
778 use rucc_target::{Arch, Env, Os};
779
780 use super::*;
781 use crate::inst::Signature;
782
783 fn target(arch: Arch, os: Os, env: Env) -> TargetInfo {
784 TargetInfo::new(Triple::new(arch, os, env))
785 }
786
787 fn linux() -> TargetInfo {
788 target(Arch::X86_64, Os::Linux, Env::Gnu)
789 }
790
791 #[test]
792 fn a_datum_is_sixteen_bytes() {
793 assert_eq!(size_of::<Datum>(), 16);
796 }
797
798 #[test]
799 fn the_layout_of_x86_64_linux_is_the_one_in_the_spec() {
800 let layout = DataLayout::for_target(&linux());
801 assert_eq!(layout.to_string(), "e-p:64:64-i64:64-f80:128-S128");
802 }
803
804 #[test]
805 fn only_x86_has_the_eighty_bit_format() {
806 assert_eq!(DataLayout::for_target(&linux()).f80_align, Some(128));
807 let arm = DataLayout::for_target(&target(Arch::Aarch64, Os::Linux, Env::Gnu));
808 assert_eq!(arm.f80_align, None);
809 assert_eq!(arm.to_string(), "e-p:64:64-i64:64-S128");
810 }
811
812 #[test]
813 fn a_layout_round_trips() {
814 for triple in [
815 Triple::new(Arch::X86_64, Os::Linux, Env::Gnu),
816 Triple::new(Arch::X86_64, Os::Darwin, Env::None),
817 Triple::new(Arch::Aarch64, Os::Darwin, Env::None),
818 Triple::new(Arch::Riscv64, Os::Linux, Env::Musl),
819 ] {
820 let layout = DataLayout::for_target(&TargetInfo::new(triple));
821 let text = layout.to_string();
822 assert_eq!(DataLayout::parse(&text), Some(layout), "{text}");
823 }
824 }
825
826 #[test]
827 fn a_layout_may_be_written_in_any_order() {
828 let text = "S128-i64:64-f80:128-p:64:64-e";
829 assert_eq!(DataLayout::parse(text), Some(DataLayout::for_target(&linux())));
830 }
831
832 #[test]
833 fn a_layout_needs_every_field_it_prints() {
834 for text in ["", "e", "e-p:64:64-S128", "e-i64:64-S128", "e-p:64:64-i64:64"] {
835 assert_eq!(DataLayout::parse(text), None, "{text}");
836 }
837 }
838
839 #[test]
840 fn a_layout_refuses_a_second_spelling() {
841 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"]
843 {
844 assert_eq!(DataLayout::parse(text), None, "{text}");
845 }
846 }
847
848 #[test]
849 fn a_module_finds_what_it_holds() {
850 let mut names = Interner::new();
851 let mut module = Module::new(names.intern("test.c"), &linux());
852
853 let counter = names.intern("counter");
854 let sum = names.intern("sum");
855 let total = names.intern("total");
856
857 let global = module.add_global(Global::new(counter, 4, 4));
858 let func = module.add_func(Func::new(sum, Signature::new()));
859 let alias = module.add_alias(Alias::new(total, counter));
860
861 assert_eq!(module.lookup(counter), Some(SymbolRef::Global(global)));
862 assert_eq!(module.lookup(sum), Some(SymbolRef::Func(func)));
863 assert_eq!(module.lookup(total), Some(SymbolRef::Alias(alias)));
864 assert_eq!(module.lookup(names.intern("nothing")), None);
865 assert_eq!(module[alias].target, counter);
866 assert!(module[global].is_declaration());
867 assert!(module[func].is_declaration());
868 }
869
870 #[test]
871 #[should_panic(expected = "two symbols with the same name")]
872 fn a_name_means_one_thing() {
873 let mut names = Interner::new();
874 let mut module = Module::new(names.intern("test.c"), &linux());
875 let name = names.intern("x");
876 module.add_global(Global::new(name, 4, 4));
877 module.add_func(Func::new(name, Signature::new()));
878 }
879
880 #[test]
881 fn an_initializer_adds_up_to_the_size() {
882 let mut names = Interner::new();
883 let mut module = Module::new(names.intern("test.c"), &linux());
884
885 let text = names.intern("hi.str");
887 let seven = module.add_imm(Imm::int(7, Type::int(32)));
888 let bytes = module.push_bytes(b"hi\0");
889 let addr = module.add_reloc(Reloc { symbol: text, addend: 0, size: 8 });
890 let init = module.push_data(&[
891 Datum::Scalar { ty: Type::int(32), value: seven },
892 Datum::Zero(4),
893 Datum::Addr(addr),
894 Datum::Zero(8),
897 ]);
898
899 let mut global = Global::new(names.intern("entry"), 24, 8);
900 global.init = Some(init);
901 global.constant = true;
902 let id = module.add_global(global);
903
904 assert!(!module[id].is_declaration());
905 let size: u64 = module[init].iter().map(|datum| datum.size(&module)).sum();
906 assert_eq!(size, module[id].size);
907 assert_eq!(&module[bytes], b"hi\0");
908 assert_eq!(module[seven].unsigned(), 7);
909 assert_eq!(module.counts().data_bytes, 3);
910 }
911
912 #[test]
913 fn a_scalar_datum_is_as_wide_as_its_type() {
914 let mut names = Interner::new();
915 let mut module = Module::new(names.intern("test.c"), &linux());
916 let value = module.add_imm(Imm::int(0, Type::int(32)));
917 assert_eq!(Datum::Scalar { ty: Type::int(32), value }.size(&module), 4);
918 assert_eq!(Datum::Scalar { ty: Type::I1, value }.size(&module), 1);
920 assert_eq!(Datum::Scalar { ty: Type::int(24), value }.size(&module), 3);
921 assert_eq!(Datum::Scalar { ty: Type::vector(Type::int(8), 16), value }.size(&module), 16);
922 }
923
924 #[test]
925 fn the_names_round_trip() {
926 for linkage in Linkage::all() {
927 assert_eq!(Linkage::from_name(linkage.name()), Some(linkage));
928 }
929 for visibility in Visibility::all() {
930 assert_eq!(Visibility::from_name(visibility.name()), Some(visibility));
931 }
932 for model in TlsModel::all() {
933 assert_eq!(TlsModel::from_name(model.name()), Some(model));
934 }
935 for kind in [AliasKind::Alias, AliasKind::IFunc] {
936 assert_eq!(AliasKind::from_name(kind.name()), Some(kind));
937 }
938 assert_eq!(Linkage::from_name("static"), None);
939 assert_eq!(Visibility::from_name("internal"), None);
940 }
941
942 #[test]
943 fn only_internal_linkage_is_local() {
944 for linkage in Linkage::all() {
945 assert_eq!(linkage.is_local(), linkage == Linkage::Internal);
946 assert_eq!(
947 linkage.may_be_replaced(),
948 !matches!(linkage, Linkage::External | Linkage::Internal)
949 );
950 }
951 }
952
953 #[test]
954 fn metadata_is_shared_by_the_whole_module() {
955 let mut names = Interner::new();
956 let mut module = Module::new(names.intern("test.c"), &linux());
957 let char_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
958 name: names.intern("omnipotent char"),
959 parent: None,
960 offset: 0,
961 }));
962 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
963 name: names.intern("int"),
964 parent: Some(char_node),
965 offset: 0,
966 }));
967 assert_eq!(module[int_node].parent(), Some(char_node));
968 assert_eq!(module.metadata().count(), 2);
969 }
970}