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;
37use crate::inst::{Imm, Meta, MetaNode};
38use crate::ty::Type;
39
40pub type FuncId = Idx<Func>;
42
43pub type GlobalId = Idx<Global>;
45
46pub type AliasId = Idx<Alias>;
48
49pub type DataList = IdxRange<Datum>;
51
52#[derive(Debug)]
54pub struct Byte;
55
56pub type ByteRange = IdxRange<Byte>;
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
64pub enum Linkage {
65 #[default]
68 External,
69 Internal,
71 Weak,
75 LinkOnce,
79 Common,
82}
83
84impl Linkage {
85 #[must_use]
87 pub const fn name(self) -> &'static str {
88 match self {
89 Self::External => "external",
90 Self::Internal => "internal",
91 Self::Weak => "weak",
92 Self::LinkOnce => "linkonce",
93 Self::Common => "common",
94 }
95 }
96
97 #[must_use]
99 pub fn from_name(name: &str) -> Option<Self> {
100 Self::all().find(|linkage| linkage.name() == name)
101 }
102
103 pub fn all() -> impl Iterator<Item = Self> {
105 [Self::External, Self::Internal, Self::Weak, Self::LinkOnce, Self::Common].into_iter()
106 }
107
108 #[must_use]
111 pub const fn is_local(self) -> bool {
112 matches!(self, Self::Internal)
113 }
114
115 #[must_use]
120 pub const fn may_be_replaced(self) -> bool {
121 matches!(self, Self::Weak | Self::LinkOnce | Self::Common)
122 }
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
131pub enum Visibility {
132 #[default]
135 Default,
136 Hidden,
139 Protected,
142}
143
144impl Visibility {
145 #[must_use]
147 pub const fn name(self) -> &'static str {
148 match self {
149 Self::Default => "default",
150 Self::Hidden => "hidden",
151 Self::Protected => "protected",
152 }
153 }
154
155 #[must_use]
157 pub fn from_name(name: &str) -> Option<Self> {
158 Self::all().find(|visibility| visibility.name() == name)
159 }
160
161 pub fn all() -> impl Iterator<Item = Self> {
163 [Self::Default, Self::Hidden, Self::Protected].into_iter()
164 }
165}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
174pub enum TlsModel {
175 #[default]
177 GlobalDynamic,
178 LocalDynamic,
180 InitialExec,
183 LocalExec,
185}
186
187impl TlsModel {
188 #[must_use]
190 pub const fn name(self) -> &'static str {
191 match self {
192 Self::GlobalDynamic => "global_dynamic",
193 Self::LocalDynamic => "local_dynamic",
194 Self::InitialExec => "initial_exec",
195 Self::LocalExec => "local_exec",
196 }
197 }
198
199 #[must_use]
201 pub fn from_name(name: &str) -> Option<Self> {
202 Self::all().find(|model| model.name() == name)
203 }
204
205 pub fn all() -> impl Iterator<Item = Self> {
207 [Self::GlobalDynamic, Self::LocalDynamic, Self::InitialExec, Self::LocalExec].into_iter()
208 }
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
216pub enum Datum {
217 Zero(u64),
220 Bytes(ByteRange),
223 Scalar {
227 ty: Type,
229 value: Idx<Imm>,
231 },
232 Addr(Idx<Reloc>),
235}
236
237impl Datum {
238 #[must_use]
243 pub fn size(self, module: &Module) -> u64 {
244 match self {
245 Self::Zero(bytes) => bytes,
246 Self::Bytes(range) => range.len() as u64,
247 Self::Scalar { ty, .. } => u64::from(ty.bits().div_ceil(8)) * u64::from(ty.lanes()),
249 Self::Addr(reloc) => u64::from(module[reloc].size),
250 }
251 }
252}
253
254#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256pub struct Reloc {
257 pub symbol: Symbol,
259 pub addend: i64,
261 pub size: u32,
264}
265
266#[derive(Debug, Clone)]
272pub struct Global {
273 pub name: Symbol,
275 pub size: u64,
277 pub align: u32,
279 pub linkage: Linkage,
281 pub visibility: Visibility,
283 pub tls: Option<TlsModel>,
285 pub constant: bool,
288 pub section: Option<Symbol>,
291 pub init: Option<DataList>,
293}
294
295impl Global {
296 #[must_use]
298 pub fn new(name: Symbol, size: u64, align: u32) -> Self {
299 Self {
300 name,
301 size,
302 align,
303 linkage: Linkage::External,
304 visibility: Visibility::Default,
305 tls: None,
306 constant: false,
307 section: None,
308 init: None,
309 }
310 }
311
312 #[must_use]
314 pub fn is_declaration(&self) -> bool {
315 self.init.is_none()
316 }
317}
318
319#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
321pub enum AliasKind {
322 #[default]
325 Alias,
326 IFunc,
330}
331
332impl AliasKind {
333 #[must_use]
335 pub const fn name(self) -> &'static str {
336 match self {
337 Self::Alias => "alias",
338 Self::IFunc => "ifunc",
339 }
340 }
341
342 #[must_use]
344 pub fn from_name(name: &str) -> Option<Self> {
345 match name {
346 "alias" => Some(Self::Alias),
347 "ifunc" => Some(Self::IFunc),
348 _ => None,
349 }
350 }
351}
352
353#[derive(Debug, Clone, Copy, PartialEq, Eq)]
355pub struct Alias {
356 pub name: Symbol,
358 pub target: Symbol,
360 pub kind: AliasKind,
362 pub linkage: Linkage,
364 pub visibility: Visibility,
366}
367
368impl Alias {
369 #[must_use]
371 pub fn new(name: Symbol, target: Symbol) -> Self {
372 Self {
373 name,
374 target,
375 kind: AliasKind::Alias,
376 linkage: Linkage::External,
377 visibility: Visibility::Default,
378 }
379 }
380}
381
382#[derive(Debug, Clone, Copy, PartialEq, Eq)]
384pub enum SymbolRef {
385 Func(FuncId),
387 Global(GlobalId),
389 Alias(AliasId),
391}
392
393#[derive(Debug, Clone, Copy, PartialEq, Eq)]
402pub struct DataLayout {
403 pub little_endian: bool,
405 pub pointer_bits: u32,
407 pub pointer_align: u32,
409 pub i64_align: u32,
412 pub f80_align: Option<u32>,
415 pub stack_align: u32,
417}
418
419impl DataLayout {
420 #[must_use]
422 pub fn for_target(target: &TargetInfo) -> Self {
423 Self {
424 little_endian: target.little_endian,
425 pointer_bits: target.pointer_width,
426 pointer_align: target.pointer_width,
427 i64_align: 64,
431 f80_align: match target.long_double_format {
432 Format::X87Extended => Some(128),
433 _ => None,
434 },
435 stack_align: 128,
436 }
437 }
438
439 #[must_use]
446 pub fn parse(text: &str) -> Option<Self> {
447 let mut little_endian = None;
448 let mut pointer = None;
449 let mut i64_align = None;
450 let mut f80_align = None;
451 let mut stack_align = None;
452 for field in text.split('-') {
453 let seen = match field {
454 "e" => little_endian.replace(true).is_some(),
455 "E" => little_endian.replace(false).is_some(),
456 _ if field.starts_with("p:") => {
457 let (bits, align) = field[2..].split_once(':')?;
458 pointer.replace((number(bits)?, number(align)?)).is_some()
459 }
460 _ if field.starts_with("i64:") => i64_align.replace(number(&field[4..])?).is_some(),
461 _ if field.starts_with("f80:") => f80_align.replace(number(&field[4..])?).is_some(),
462 _ if field.starts_with('S') => stack_align.replace(number(&field[1..])?).is_some(),
463 _ => return None,
464 };
465 if seen {
466 return None;
467 }
468 }
469 let (pointer_bits, pointer_align) = pointer?;
470 Some(Self {
471 little_endian: little_endian?,
472 pointer_bits,
473 pointer_align,
474 i64_align: i64_align?,
475 f80_align,
476 stack_align: stack_align?,
477 })
478 }
479}
480
481impl fmt::Display for DataLayout {
482 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
483 write!(f, "{}", if self.little_endian { "e" } else { "E" })?;
484 write!(f, "-p:{}:{}", self.pointer_bits, self.pointer_align)?;
485 write!(f, "-i64:{}", self.i64_align)?;
486 if let Some(align) = self.f80_align {
487 write!(f, "-f80:{align}")?;
488 }
489 write!(f, "-S{}", self.stack_align)
490 }
491}
492
493fn number(text: &str) -> Option<u32> {
498 if text.is_empty() || (text.len() > 1 && text.starts_with('0')) {
499 return None;
500 }
501 if !text.bytes().all(|byte| byte.is_ascii_digit()) {
502 return None;
503 }
504 text.parse().ok()
505}
506
507#[derive(Debug)]
509pub struct Module {
510 pub name: Symbol,
513 pub triple: Triple,
515 pub datalayout: DataLayout,
517
518 funcs: Vec<Func>,
519 globals: Vec<Global>,
520 aliases: Vec<Alias>,
521 metadata: Vec<MetaNode>,
522
523 data: Vec<Datum>,
524 bytes: Vec<u8>,
525 imms: Vec<Imm>,
526 relocs: Vec<Reloc>,
527
528 symbols: HashMap<Symbol, SymbolRef>,
529}
530
531impl Module {
532 #[must_use]
534 pub fn new(name: Symbol, target: &TargetInfo) -> Self {
535 Self {
536 name,
537 triple: target.triple,
538 datalayout: DataLayout::for_target(target),
539 funcs: Vec::new(),
540 globals: Vec::new(),
541 aliases: Vec::new(),
542 metadata: Vec::new(),
543 data: Vec::new(),
544 bytes: Vec::new(),
545 imms: Vec::new(),
546 relocs: Vec::new(),
547 symbols: HashMap::new(),
548 }
549 }
550
551 pub fn add_func(&mut self, func: Func) -> FuncId {
561 let id = Idx::from_usize(self.funcs.len());
562 self.claim(func.name, SymbolRef::Func(id));
563 self.funcs.push(func);
564 id
565 }
566
567 pub fn add_global(&mut self, global: Global) -> GlobalId {
573 let id = Idx::from_usize(self.globals.len());
574 self.claim(global.name, SymbolRef::Global(id));
575 self.globals.push(global);
576 id
577 }
578
579 pub fn add_alias(&mut self, alias: Alias) -> AliasId {
588 let id = Idx::from_usize(self.aliases.len());
589 self.claim(alias.name, SymbolRef::Alias(id));
590 self.aliases.push(alias);
591 id
592 }
593
594 #[must_use]
596 pub fn lookup(&self, name: Symbol) -> Option<SymbolRef> {
597 self.symbols.get(&name).copied()
598 }
599
600 pub fn funcs(&self) -> impl Iterator<Item = FuncId> + use<> {
602 (0..self.funcs.len()).map(Idx::from_usize)
603 }
604
605 pub fn globals(&self) -> impl Iterator<Item = GlobalId> + use<> {
607 (0..self.globals.len()).map(Idx::from_usize)
608 }
609
610 pub fn aliases(&self) -> impl Iterator<Item = AliasId> + use<> {
612 (0..self.aliases.len()).map(Idx::from_usize)
613 }
614
615 fn claim(&mut self, name: Symbol, what: SymbolRef) {
616 assert!(
617 self.symbols.insert(name, what).is_none(),
618 "a module cannot have two symbols with the same name"
619 );
620 }
621
622 pub fn add_meta(&mut self, node: MetaNode) -> Meta {
630 self.metadata.push(node);
631 Idx::from_usize(self.metadata.len() - 1)
632 }
633
634 pub fn metadata(&self) -> impl Iterator<Item = Meta> + use<> {
636 (0..self.metadata.len()).map(Idx::from_usize)
637 }
638
639 pub fn push_data(&mut self, data: &[Datum]) -> DataList {
643 let start = self.data.len();
644 self.data.extend_from_slice(data);
645 DataList::new(Idx::from_usize(start), Idx::from_usize(self.data.len()))
646 }
647
648 pub fn push_bytes(&mut self, bytes: &[u8]) -> ByteRange {
650 let start = self.bytes.len();
651 self.bytes.extend_from_slice(bytes);
652 ByteRange::new(Idx::from_usize(start), Idx::from_usize(self.bytes.len()))
653 }
654
655 pub fn add_imm(&mut self, imm: Imm) -> Idx<Imm> {
657 self.imms.push(imm);
658 Idx::from_usize(self.imms.len() - 1)
659 }
660
661 pub fn add_reloc(&mut self, reloc: Reloc) -> Idx<Reloc> {
663 self.relocs.push(reloc);
664 Idx::from_usize(self.relocs.len() - 1)
665 }
666
667 #[must_use]
670 pub fn counts(&self) -> ModuleCounts {
671 ModuleCounts {
672 funcs: self.funcs.len(),
673 globals: self.globals.len(),
674 aliases: self.aliases.len(),
675 metadata: self.metadata.len(),
676 data_bytes: self.bytes.len(),
677 }
678 }
679}
680
681#[derive(Debug, Clone, Copy, PartialEq, Eq)]
683pub struct ModuleCounts {
684 pub funcs: usize,
686 pub globals: usize,
688 pub aliases: usize,
690 pub metadata: usize,
692 pub data_bytes: usize,
695}
696
697impl Index<FuncId> for Module {
698 type Output = Func;
699
700 fn index(&self, id: FuncId) -> &Func {
701 &self.funcs[id.index()]
702 }
703}
704
705impl IndexMut<FuncId> for Module {
706 fn index_mut(&mut self, id: FuncId) -> &mut Func {
707 &mut self.funcs[id.index()]
708 }
709}
710
711impl Index<GlobalId> for Module {
712 type Output = Global;
713
714 fn index(&self, id: GlobalId) -> &Global {
715 &self.globals[id.index()]
716 }
717}
718
719impl IndexMut<GlobalId> for Module {
720 fn index_mut(&mut self, id: GlobalId) -> &mut Global {
721 &mut self.globals[id.index()]
722 }
723}
724
725impl Index<AliasId> for Module {
726 type Output = Alias;
727
728 fn index(&self, id: AliasId) -> &Alias {
729 &self.aliases[id.index()]
730 }
731}
732
733impl Index<Meta> for Module {
734 type Output = MetaNode;
735
736 fn index(&self, meta: Meta) -> &MetaNode {
737 &self.metadata[meta.index()]
738 }
739}
740
741impl Index<Idx<Imm>> for Module {
742 type Output = Imm;
743
744 fn index(&self, imm: Idx<Imm>) -> &Imm {
745 &self.imms[imm.index()]
746 }
747}
748
749impl Index<Idx<Reloc>> for Module {
750 type Output = Reloc;
751
752 fn index(&self, reloc: Idx<Reloc>) -> &Reloc {
753 &self.relocs[reloc.index()]
754 }
755}
756
757impl Index<DataList> for Module {
758 type Output = [Datum];
759
760 fn index(&self, list: DataList) -> &[Datum] {
761 &self.data[list.as_usize_range()]
762 }
763}
764
765impl Index<ByteRange> for Module {
766 type Output = [u8];
767
768 fn index(&self, range: ByteRange) -> &[u8] {
769 &self.bytes[range.as_usize_range()]
770 }
771}
772
773#[cfg(test)]
774mod tests {
775 use rucc_base::Interner;
776 use rucc_target::{Arch, Env, Os};
777
778 use super::*;
779 use crate::inst::Signature;
780
781 fn target(arch: Arch, os: Os, env: Env) -> TargetInfo {
782 TargetInfo::new(Triple::new(arch, os, env))
783 }
784
785 fn linux() -> TargetInfo {
786 target(Arch::X86_64, Os::Linux, Env::Gnu)
787 }
788
789 #[test]
790 fn a_datum_is_sixteen_bytes() {
791 assert_eq!(size_of::<Datum>(), 16);
794 }
795
796 #[test]
797 fn the_layout_of_x86_64_linux_is_the_one_in_the_spec() {
798 let layout = DataLayout::for_target(&linux());
799 assert_eq!(layout.to_string(), "e-p:64:64-i64:64-f80:128-S128");
800 }
801
802 #[test]
803 fn only_x86_has_the_eighty_bit_format() {
804 assert_eq!(DataLayout::for_target(&linux()).f80_align, Some(128));
805 let arm = DataLayout::for_target(&target(Arch::Aarch64, Os::Linux, Env::Gnu));
806 assert_eq!(arm.f80_align, None);
807 assert_eq!(arm.to_string(), "e-p:64:64-i64:64-S128");
808 }
809
810 #[test]
811 fn a_layout_round_trips() {
812 for triple in [
813 Triple::new(Arch::X86_64, Os::Linux, Env::Gnu),
814 Triple::new(Arch::X86_64, Os::Darwin, Env::None),
815 Triple::new(Arch::Aarch64, Os::Darwin, Env::None),
816 Triple::new(Arch::Riscv64, Os::Linux, Env::Musl),
817 ] {
818 let layout = DataLayout::for_target(&TargetInfo::new(triple));
819 let text = layout.to_string();
820 assert_eq!(DataLayout::parse(&text), Some(layout), "{text}");
821 }
822 }
823
824 #[test]
825 fn a_layout_may_be_written_in_any_order() {
826 let text = "S128-i64:64-f80:128-p:64:64-e";
827 assert_eq!(DataLayout::parse(text), Some(DataLayout::for_target(&linux())));
828 }
829
830 #[test]
831 fn a_layout_needs_every_field_it_prints() {
832 for text in ["", "e", "e-p:64:64-S128", "e-i64:64-S128", "e-p:64:64-i64:64"] {
833 assert_eq!(DataLayout::parse(text), None, "{text}");
834 }
835 }
836
837 #[test]
838 fn a_layout_refuses_a_second_spelling() {
839 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"]
841 {
842 assert_eq!(DataLayout::parse(text), None, "{text}");
843 }
844 }
845
846 #[test]
847 fn a_module_finds_what_it_holds() {
848 let mut names = Interner::new();
849 let mut module = Module::new(names.intern("test.c"), &linux());
850
851 let counter = names.intern("counter");
852 let sum = names.intern("sum");
853 let total = names.intern("total");
854
855 let global = module.add_global(Global::new(counter, 4, 4));
856 let func = module.add_func(Func::new(sum, Signature::new()));
857 let alias = module.add_alias(Alias::new(total, counter));
858
859 assert_eq!(module.lookup(counter), Some(SymbolRef::Global(global)));
860 assert_eq!(module.lookup(sum), Some(SymbolRef::Func(func)));
861 assert_eq!(module.lookup(total), Some(SymbolRef::Alias(alias)));
862 assert_eq!(module.lookup(names.intern("nothing")), None);
863 assert_eq!(module[alias].target, counter);
864 assert!(module[global].is_declaration());
865 assert!(module[func].is_declaration());
866 }
867
868 #[test]
869 #[should_panic(expected = "two symbols with the same name")]
870 fn a_name_means_one_thing() {
871 let mut names = Interner::new();
872 let mut module = Module::new(names.intern("test.c"), &linux());
873 let name = names.intern("x");
874 module.add_global(Global::new(name, 4, 4));
875 module.add_func(Func::new(name, Signature::new()));
876 }
877
878 #[test]
879 fn an_initializer_adds_up_to_the_size() {
880 let mut names = Interner::new();
881 let mut module = Module::new(names.intern("test.c"), &linux());
882
883 let text = names.intern("hi.str");
885 let seven = module.add_imm(Imm::int(7, Type::int(32)));
886 let bytes = module.push_bytes(b"hi\0");
887 let addr = module.add_reloc(Reloc { symbol: text, addend: 0, size: 8 });
888 let init = module.push_data(&[
889 Datum::Scalar { ty: Type::int(32), value: seven },
890 Datum::Zero(4),
891 Datum::Addr(addr),
892 Datum::Zero(8),
895 ]);
896
897 let mut global = Global::new(names.intern("entry"), 24, 8);
898 global.init = Some(init);
899 global.constant = true;
900 let id = module.add_global(global);
901
902 assert!(!module[id].is_declaration());
903 let size: u64 = module[init].iter().map(|datum| datum.size(&module)).sum();
904 assert_eq!(size, module[id].size);
905 assert_eq!(&module[bytes], b"hi\0");
906 assert_eq!(module[seven].unsigned(), 7);
907 assert_eq!(module.counts().data_bytes, 3);
908 }
909
910 #[test]
911 fn a_scalar_datum_is_as_wide_as_its_type() {
912 let mut names = Interner::new();
913 let mut module = Module::new(names.intern("test.c"), &linux());
914 let value = module.add_imm(Imm::int(0, Type::int(32)));
915 assert_eq!(Datum::Scalar { ty: Type::int(32), value }.size(&module), 4);
916 assert_eq!(Datum::Scalar { ty: Type::I1, value }.size(&module), 1);
918 assert_eq!(Datum::Scalar { ty: Type::int(24), value }.size(&module), 3);
919 assert_eq!(Datum::Scalar { ty: Type::vector(Type::int(8), 16), value }.size(&module), 16);
920 }
921
922 #[test]
923 fn the_names_round_trip() {
924 for linkage in Linkage::all() {
925 assert_eq!(Linkage::from_name(linkage.name()), Some(linkage));
926 }
927 for visibility in Visibility::all() {
928 assert_eq!(Visibility::from_name(visibility.name()), Some(visibility));
929 }
930 for model in TlsModel::all() {
931 assert_eq!(TlsModel::from_name(model.name()), Some(model));
932 }
933 for kind in [AliasKind::Alias, AliasKind::IFunc] {
934 assert_eq!(AliasKind::from_name(kind.name()), Some(kind));
935 }
936 assert_eq!(Linkage::from_name("static"), None);
937 assert_eq!(Visibility::from_name("internal"), None);
938 }
939
940 #[test]
941 fn only_internal_linkage_is_local() {
942 for linkage in Linkage::all() {
943 assert_eq!(linkage.is_local(), linkage == Linkage::Internal);
944 assert_eq!(
945 linkage.may_be_replaced(),
946 !matches!(linkage, Linkage::External | Linkage::Internal)
947 );
948 }
949 }
950
951 #[test]
952 fn metadata_is_shared_by_the_whole_module() {
953 let mut names = Interner::new();
954 let mut module = Module::new(names.intern("test.c"), &linux());
955 let char_node = module.add_meta(MetaNode {
956 name: names.intern("omnipotent char"),
957 parent: None,
958 offset: 0,
959 });
960 let int_node = module.add_meta(MetaNode {
961 name: names.intern("int"),
962 parent: Some(char_node),
963 offset: 0,
964 });
965 assert_eq!(module[int_node].parent, Some(char_node));
966 assert_eq!(module.metadata().count(), 2);
967 }
968}