1use std::collections::HashSet;
75
76use rucc_base::Symbol;
77use rucc_ir::{
78 AttrSet, Attrs, DataLayout, Def, Extra, Flags, Func, Imm, Inst, MemInfo, Meta, Module, Opcode,
79 Restrict, SymbolRef, Type, Value,
80};
81
82const CHASE_LIMIT: u32 = 64;
89
90const TREE_LIMIT: u32 = 32;
96
97#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
101pub enum Reason {
102 Distinct,
104 Escape,
106 Offset,
108 Tbaa,
110 Restrict,
112 Attribute,
114}
115
116impl Reason {
117 pub const ALL: [Self; 6] =
119 [Self::Distinct, Self::Escape, Self::Offset, Self::Tbaa, Self::Restrict, Self::Attribute];
120
121 pub const COUNT: usize = Self::ALL.len();
123
124 #[must_use]
126 pub const fn index(self) -> usize {
127 match self {
128 Self::Distinct => 0,
129 Self::Escape => 1,
130 Self::Offset => 2,
131 Self::Tbaa => 3,
132 Self::Restrict => 4,
133 Self::Attribute => 5,
134 }
135 }
136
137 #[must_use]
139 pub const fn name(self) -> &'static str {
140 match self {
141 Self::Distinct => "distinct",
142 Self::Escape => "escape",
143 Self::Offset => "offset",
144 Self::Tbaa => "tbaa",
145 Self::Restrict => "restrict",
146 Self::Attribute => "attribute",
147 }
148 }
149
150 #[must_use]
152 pub const fn describe(self) -> &'static str {
153 match self {
154 Self::Distinct => "they are two different objects",
155 Self::Escape => "the address of that local never leaves this function",
156 Self::Offset => "they are parts of one object that do not overlap",
157 Self::Tbaa => "no object has both of those types",
158 Self::Restrict => "restrict says those two pointers do not reach the same object",
159 Self::Attribute => "the callee is declared not to touch memory that way",
160 }
161 }
162}
163
164#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
166pub enum Answer {
167 May,
169 No(Reason),
171}
172
173impl Answer {
174 #[must_use]
176 pub const fn is_no(self) -> bool {
177 matches!(self, Self::No(_))
178 }
179
180 #[must_use]
182 pub const fn reason(self) -> Option<Reason> {
183 match self {
184 Self::No(reason) => Some(reason),
185 Self::May => None,
186 }
187 }
188}
189
190#[derive(Clone, Copy, Debug, PartialEq, Eq)]
196pub struct Options {
197 pub strict_aliasing: bool,
200}
201
202impl Default for Options {
203 fn default() -> Self {
204 Self { strict_aliasing: true }
205 }
206}
207
208#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
216pub enum Origin {
217 Local(Inst),
220 Global(Symbol),
222 Unknown(Value),
225}
226
227impl Origin {
228 #[must_use]
230 pub const fn is_object(self) -> bool {
231 matches!(self, Self::Local(_) | Self::Global(_))
232 }
233}
234
235#[must_use]
241pub fn origin(func: &Func, mut value: Value) -> (Origin, Option<i64>) {
242 let mut offset = Some(0i64);
243 for _ in 0..CHASE_LIMIT {
244 let Def::Result { inst, .. } = func[value].def else {
245 return (Origin::Unknown(value), offset);
247 };
248 let data = func[inst];
249 match data.opcode {
250 Opcode::Alloca => return (Origin::Local(inst), offset),
251 Opcode::GlobalAddr => {
252 let Extra::Symbol(name) = data.extra else {
253 return (Origin::Unknown(value), offset);
254 };
255 return (Origin::Global(name), offset);
256 }
257 Opcode::PtrAdd => {
258 let args = &func[data.args];
259 let (base, by) = (args[0], args[1]);
260 offset = offset
261 .and_then(|so_far| Some((so_far, constant(func, by)?)))
262 .and_then(|(so_far, by)| so_far.checked_add(by));
263 value = base;
264 }
265 Opcode::Bitcast => value = func[data.args][0],
268 _ => return (Origin::Unknown(value), offset),
269 }
270 }
271 (Origin::Unknown(value), None)
272}
273
274#[derive(Clone, Copy, Debug, PartialEq, Eq)]
280pub struct Access {
281 pub origin: Origin,
283 pub offset: Option<i64>,
285 pub size: Option<u64>,
287 pub tbaa: Option<Meta>,
289 pub restrict: Restrict,
291 pub volatile: bool,
293}
294
295impl Access {
296 #[must_use]
302 pub fn through(func: &Func, pointer: Value) -> Self {
303 let (origin, offset) = origin(func, pointer);
304 Self { origin, offset, size: None, tbaa: None, restrict: Restrict::NONE, volatile: false }
305 }
306
307 #[must_use]
309 pub fn range(&self) -> Option<(i128, i128)> {
310 let (offset, size) = (self.offset?, self.size?);
311 let start = i128::from(offset);
312 Some((start, start + i128::from(size)))
313 }
314}
315
316#[derive(Clone, Debug, Default)]
328pub struct Escapes {
329 escaped: HashSet<Inst>,
330}
331
332impl Escapes {
333 #[must_use]
335 pub fn of(func: &Func) -> Self {
336 let mut escaped = HashSet::new();
337 for block in func.blocks() {
338 for inst in func.insts(block) {
339 let data = func[inst];
340 for (index, &arg) in func[data.args].iter().enumerate() {
341 if keeps_address(data.opcode, index) {
342 continue;
343 }
344 if let (Origin::Local(local), _) = origin(func, arg) {
345 escaped.insert(local);
346 }
347 }
348 for call in func.successors(inst) {
351 for &arg in &func[call.args] {
352 if let (Origin::Local(local), _) = origin(func, arg) {
353 escaped.insert(local);
354 }
355 }
356 }
357 }
358 }
359 Self { escaped }
360 }
361
362 #[must_use]
364 pub fn escaped(&self, local: Inst) -> bool {
365 self.escaped.contains(&local)
366 }
367
368 #[must_use]
370 pub fn count(&self) -> usize {
371 self.escaped.len()
372 }
373}
374
375#[must_use]
379pub const fn keeps_address(opcode: Opcode, index: usize) -> bool {
380 match (opcode, index) {
381 (Opcode::Load | Opcode::AtomicLoad, 0)
383 | (Opcode::Store | Opcode::AtomicStore, 1)
384 | (Opcode::AtomicRmw | Opcode::Cmpxchg, 0)
385 | (Opcode::Memcpy | Opcode::Memmove, 0 | 1)
386 | (Opcode::Memset | Opcode::Prefetch, 0) => true,
387 (Opcode::PtrAdd | Opcode::Bitcast, 0) => true,
390 (Opcode::ICmp, 0 | 1) => true,
393 _ => false,
394 }
395}
396
397#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
403pub struct Counts {
404 queries: u64,
405 answered: [u64; Reason::COUNT],
406}
407
408impl Counts {
409 #[must_use]
411 pub const fn queries(&self) -> u64 {
412 self.queries
413 }
414
415 #[must_use]
417 pub const fn answered(&self, reason: Reason) -> u64 {
418 self.answered[reason.index()]
419 }
420
421 #[must_use]
423 pub fn total(&self) -> u64 {
424 self.answered.iter().sum()
425 }
426}
427
428#[derive(Debug)]
435pub struct Alias<'a> {
436 func: &'a Func,
437 module: &'a Module,
438 options: Options,
439 escapes: Escapes,
440 counts: Counts,
441}
442
443impl<'a> Alias<'a> {
444 #[must_use]
446 pub fn new(func: &'a Func, module: &'a Module) -> Self {
447 Self::with(func, module, Options::default())
448 }
449
450 #[must_use]
452 pub fn with(func: &'a Func, module: &'a Module, options: Options) -> Self {
453 Self { func, module, options, escapes: Escapes::of(func), counts: Counts::default() }
454 }
455
456 #[must_use]
458 pub const fn escapes(&self) -> &Escapes {
459 &self.escapes
460 }
461
462 #[must_use]
464 pub const fn counts(&self) -> &Counts {
465 &self.counts
466 }
467
468 #[must_use]
470 pub fn reads(&self, inst: Inst) -> Option<Access> {
471 let data = self.func[inst];
472 let args = &self.func[data.args];
473 let info = self.mem(inst);
474 let (pointer, size) = match data.opcode {
475 Opcode::Load | Opcode::AtomicLoad => (args[0], self.width(self.result_type(inst)?)),
476 Opcode::Memcpy | Opcode::Memmove => (args[1], Some(info?.size)),
478 Opcode::AtomicRmw => (args[0], self.width(self.func[args[1]].ty)),
481 Opcode::Cmpxchg => (args[0], self.width(self.func[args[1]].ty)),
482 Opcode::VaObject => (args[0], Some(info?.size)),
483 _ => return None,
484 };
485 Some(self.access(pointer, size, info, data.flags))
486 }
487
488 #[must_use]
490 pub fn writes(&self, inst: Inst) -> Option<Access> {
491 let data = self.func[inst];
492 let args = &self.func[data.args];
493 let info = self.mem(inst);
494 let (pointer, size) = match data.opcode {
495 Opcode::Store | Opcode::AtomicStore => (args[1], self.width(self.func[args[0]].ty)),
496 Opcode::Memcpy | Opcode::Memmove | Opcode::Memset => (args[0], Some(info?.size)),
497 Opcode::AtomicRmw | Opcode::Cmpxchg => (args[0], self.width(self.func[args[1]].ty)),
498 _ => return None,
499 };
500 Some(self.access(pointer, size, info, data.flags))
501 }
502
503 pub fn query(&mut self, a: &Access, b: &Access) -> Answer {
505 self.counts.queries += 1;
506 let answer = self.decide(a, b);
507 if let Answer::No(reason) = answer {
508 self.counts.answered[reason.index()] += 1;
509 }
510 answer
511 }
512
513 pub fn clobbered_by(&mut self, reference: &Access, call: Inst) -> Answer {
521 self.touched_by(reference, call, true)
522 }
523
524 pub fn read_by(&mut self, reference: &Access, call: Inst) -> Answer {
528 self.touched_by(reference, call, false)
529 }
530
531 fn decide(&self, a: &Access, b: &Access) -> Answer {
534 if a.volatile && b.volatile {
538 return Answer::May;
539 }
540
541 if a.origin.is_object() && b.origin.is_object() {
549 if self.distinct(a.origin, b.origin) {
550 return Answer::No(Reason::Distinct);
551 }
552 if a.origin == b.origin {
553 return by_offset(a, b);
554 }
555 return Answer::May;
556 }
557
558 if let Some(local) = self.private(a).or_else(|| self.private(b)) {
561 let _ = local;
562 return Answer::No(Reason::Escape);
563 }
564
565 if a.restrict.disjoint(b.restrict) {
566 return Answer::No(Reason::Restrict);
567 }
568
569 if self.options.strict_aliasing {
570 if let (Some(one), Some(other)) = (a.tbaa, b.tbaa) {
571 if !self.types_conflict(one, other) {
572 return Answer::No(Reason::Tbaa);
573 }
574 }
575 }
576
577 if a.origin == b.origin {
579 return by_offset(a, b);
580 }
581
582 Answer::May
583 }
584
585 fn private(&self, reference: &Access) -> Option<Inst> {
588 match reference.origin {
589 Origin::Local(local) if !self.escapes.escaped(local) => Some(local),
590 _ => None,
591 }
592 }
593
594 fn distinct(&self, a: Origin, b: Origin) -> bool {
596 match (a, b) {
597 (Origin::Local(one), Origin::Local(other)) => one != other,
598 (Origin::Local(_), Origin::Global(_)) | (Origin::Global(_), Origin::Local(_)) => true,
600 (Origin::Global(one), Origin::Global(other)) => {
601 one != other && self.one_object(one) && self.one_object(other)
602 }
603 _ => false,
604 }
605 }
606
607 fn one_object(&self, name: Symbol) -> bool {
614 matches!(self.module.lookup(name), Some(SymbolRef::Func(_) | SymbolRef::Global(_)))
615 }
616
617 fn types_conflict(&self, one: Meta, other: Meta) -> bool {
623 self.at_or_below(one, other) || self.at_or_below(other, one)
624 }
625
626 fn at_or_below(&self, mut node: Meta, ancestor: Meta) -> bool {
628 for _ in 0..TREE_LIMIT {
629 if node == ancestor {
630 return true;
631 }
632 match self.module[node].parent() {
633 Some(up) => node = up,
634 None => return false,
635 }
636 }
637 true
640 }
641
642 fn touched_by(&mut self, reference: &Access, call: Inst, writing: bool) -> Answer {
643 self.counts.queries += 1;
644 let answer = self.decide_call(reference, call, writing);
645 if let Answer::No(reason) = answer {
646 self.counts.answered[reason.index()] += 1;
647 }
648 answer
649 }
650
651 fn decide_call(&self, reference: &Access, call: Inst, writing: bool) -> Answer {
652 if self.private(reference).is_some() {
656 return Answer::No(Reason::Escape);
657 }
658
659 let Some(attrs) = self.callee(call) else {
660 return Answer::May;
661 };
662 if attrs.set.contains(AttrSet::READNONE)
664 || (writing && attrs.set.contains(AttrSet::READONLY))
665 {
666 return Answer::No(Reason::Attribute);
667 }
668
669 if attrs.set.contains(AttrSet::ARGMEM_ONLY) {
676 let args = &self.func[self.func[call].args];
677 let mut all = true;
678 for &arg in args {
679 if !self.func[arg].ty.is_ptr() {
680 continue;
681 }
682 let through = Access::through(self.func, arg);
683 all &= self.decide(reference, &through).is_no();
684 }
685 if all {
686 return Answer::No(Reason::Attribute);
687 }
688 }
689
690 Answer::May
691 }
692
693 fn callee(&self, call: Inst) -> Option<Attrs> {
698 let Extra::Call(info) = self.func[call].extra else {
699 return None;
700 };
701 let name = self.func[info].callee?;
702 match self.module.lookup(name)? {
703 SymbolRef::Func(id) => Some(self.module[id].attrs),
704 _ => None,
705 }
706 }
707
708 fn mem(&self, inst: Inst) -> Option<MemInfo> {
709 match self.func[inst].extra {
710 Extra::Mem(info) | Extra::Rmw(_, info) => Some(self.func[info]),
711 Extra::VaObject(object) => Some(self.func[self.func[object].mem]),
712 _ => None,
713 }
714 }
715
716 fn result_type(&self, inst: Inst) -> Option<Type> {
717 self.func[inst].results().next().map(|value| self.func[value].ty)
718 }
719
720 fn access(
721 &self,
722 pointer: Value,
723 size: Option<u64>,
724 info: Option<MemInfo>,
725 flags: Flags,
726 ) -> Access {
727 let (origin, offset) = origin(self.func, pointer);
728 Access {
729 origin,
730 offset,
731 size,
732 tbaa: info.and_then(|info| info.tbaa),
733 restrict: info.map_or(Restrict::NONE, |info| info.restrict),
734 volatile: flags.contains(Flags::VOLATILE),
735 }
736 }
737
738 fn width(&self, ty: Type) -> Option<u64> {
741 let layout: &DataLayout = &self.module.datalayout;
742 if ty.is_ptr() {
743 return Some(u64::from(layout.pointer_bits).div_ceil(8));
744 }
745 let bits = u64::from(ty.bits()) * u64::from(ty.lanes());
746 (bits > 0).then(|| bits.div_ceil(8))
747 }
748}
749
750fn by_offset(a: &Access, b: &Access) -> Answer {
752 let (Some((a_start, a_end)), Some((b_start, b_end))) = (a.range(), b.range()) else {
753 return Answer::May;
754 };
755 if a_end <= b_start || b_end <= a_start {
756 return Answer::No(Reason::Offset);
757 }
758 Answer::May
759}
760
761fn constant(func: &Func, value: Value) -> Option<i64> {
763 let Def::Result { inst, .. } = func[value].def else {
764 return None;
765 };
766 let data = func[inst];
767 if data.opcode != Opcode::IConst {
768 return None;
769 }
770 let Extra::Imm(imm) = data.extra else {
771 return None;
772 };
773 i64::try_from(Imm::signed(func[imm], func[value].ty)).ok()
774}
775
776#[cfg(test)]
777mod tests {
778 use rucc_base::{Interner, Symbol};
779 use rucc_ir::{
780 AttrSet, Attrs, Builder, CallInfo, Extra, Flags, Func, Global, InstData, IntPred, MemInfo,
781 MemOrder, MetaNode, Module, Opcode, Restrict, Signature, TbaaNode, Type, Value,
782 };
783 use rucc_target::{TargetInfo, Triple};
784
785 use super::*;
786
787 fn module(names: &mut Interner) -> Module {
789 let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
790 Module::new(names.intern("t.c"), &target)
791 }
792
793 fn func(names: &mut Interner, params: &[Type]) -> Func {
795 let mut func = Func::new(names.intern("f"), Signature::new().with_params(params));
796 let entry = func.create_block();
797 for &ty in params {
798 func.append_param(entry, ty);
799 }
800 func
801 }
802
803 fn builder(func: &mut Func) -> Builder<'_> {
805 let entry = func.entry().expect("the function has an entry block");
806 Builder::new(func, entry)
807 }
808
809 fn param(func: &Func, index: usize) -> Value {
810 let entry = func.entry().expect("the function has an entry block");
811 func[entry].params[index]
812 }
813
814 fn plain(align: u32) -> MemInfo {
815 MemInfo { size: 0, align, order: MemOrder::NotAtomic, tbaa: None, restrict: Restrict::NONE }
816 }
817
818 fn sized(size: u64, align: u32) -> MemInfo {
819 MemInfo { size, ..plain(align) }
820 }
821
822 fn local(build: &mut Builder<'_>, size: u64) -> Value {
824 let mem = build.func().add_mem(sized(size, 8));
825 build.value(InstData { extra: Extra::Mem(mem), ..InstData::new(Opcode::Alloca) }, Type::PTR)
826 }
827
828 fn at(build: &mut Builder<'_>, base: Value, offset: i64) -> Value {
830 let by = build.iconst(Type::int(64), i128::from(offset));
831 build.binary(Opcode::PtrAdd, base, by, Flags::NONE)
832 }
833
834 fn global(build: &mut Builder<'_>, module: &mut Module, name: Symbol) -> Value {
836 module.add_global(Global::new(name, 16, 8));
837 build.value(
838 InstData { extra: Extra::Symbol(name), ..InstData::new(Opcode::GlobalAddr) },
839 Type::PTR,
840 )
841 }
842
843 #[test]
844 fn two_different_locals_are_two_objects() {
845 let mut names = Interner::new();
846 let module = module(&mut names);
847 let mut f = func(&mut names, &[]);
848 let mut build = builder(&mut f);
849 let one = local(&mut build, 16);
850 let other = local(&mut build, 16);
851 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
852 build.store(read, other, plain(4), Flags::NONE);
853 build.ret(&[]);
854
855 let mut alias = Alias::new(&f, &module);
856 let (a, b) = two(&alias, &f);
857 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
858 assert_eq!(alias.counts().answered(Reason::Distinct), 1);
859 assert_eq!(alias.counts().queries(), 1);
860 }
861
862 fn two(alias: &Alias<'_>, func: &Func) -> (Access, Access) {
864 let mut read = None;
865 let mut written = None;
866 for block in func.blocks() {
867 for inst in func.insts(block) {
868 if read.is_none() {
869 read = alias.reads(inst);
870 }
871 if written.is_none() {
872 written = alias.writes(inst);
873 }
874 }
875 }
876 (read.expect("a read"), written.expect("a write"))
877 }
878
879 #[test]
880 fn a_local_and_a_global_are_two_objects() {
881 let mut names = Interner::new();
882 let mut module = module(&mut names);
883 let x = names.intern("x");
884 let mut f = func(&mut names, &[]);
885 let mut build = builder(&mut f);
886 let one = local(&mut build, 16);
887 let other = global(&mut build, &mut module, x);
888 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
889 build.store(read, other, plain(4), Flags::NONE);
890 build.ret(&[]);
891
892 let mut alias = Alias::new(&f, &module);
893 let (a, b) = two(&alias, &f);
894 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
895 }
896
897 #[test]
898 fn two_different_globals_are_two_objects() {
899 let mut names = Interner::new();
900 let mut module = module(&mut names);
901 let (x, y) = (names.intern("x"), names.intern("y"));
902 let mut f = func(&mut names, &[]);
903 let mut build = builder(&mut f);
904 let one = global(&mut build, &mut module, x);
905 let other = global(&mut build, &mut module, y);
906 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
907 build.store(read, other, plain(4), Flags::NONE);
908 build.ret(&[]);
909
910 let mut alias = Alias::new(&f, &module);
911 let (a, b) = two(&alias, &f);
912 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
913 }
914
915 #[test]
916 fn a_global_the_module_does_not_have_is_not_argued_about() {
917 let mut names = Interner::new();
920 let mut module = module(&mut names);
921 let (x, y) = (names.intern("x"), names.intern("y"));
922 let mut f = func(&mut names, &[]);
923 let mut build = builder(&mut f);
924 let one = global(&mut build, &mut module, x);
925 let other = build.value(
926 InstData { extra: Extra::Symbol(y), ..InstData::new(Opcode::GlobalAddr) },
927 Type::PTR,
928 );
929 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
930 build.store(read, other, plain(4), Flags::NONE);
931 build.ret(&[]);
932
933 let mut alias = Alias::new(&f, &module);
934 let (a, b) = two(&alias, &f);
935 assert_eq!(alias.query(&a, &b), Answer::May);
936 }
937
938 #[test]
939 fn two_parts_of_one_object_that_do_not_overlap_are_disjoint() {
940 let mut names = Interner::new();
941 let module = module(&mut names);
942 let mut f = func(&mut names, &[]);
943 let mut build = builder(&mut f);
944 let object = local(&mut build, 16);
945 let first = at(&mut build, object, 0);
946 let second = at(&mut build, object, 4);
947 let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
948 build.store(read, second, plain(4), Flags::NONE);
949 build.ret(&[]);
950
951 let mut alias = Alias::new(&f, &module);
952 let (a, b) = two(&alias, &f);
953 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Offset));
954 }
955
956 #[test]
957 fn two_parts_of_one_object_that_do_overlap_are_not() {
958 let mut names = Interner::new();
959 let module = module(&mut names);
960 let mut f = func(&mut names, &[]);
961 let mut build = builder(&mut f);
962 let object = local(&mut build, 16);
963 let first = at(&mut build, object, 0);
964 let second = at(&mut build, object, 2);
965 let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
966 build.store(read, second, plain(4), Flags::NONE);
967 build.ret(&[]);
968
969 let mut alias = Alias::new(&f, &module);
970 let (a, b) = two(&alias, &f);
971 assert_eq!(alias.query(&a, &b), Answer::May);
972 }
973
974 #[test]
975 fn an_offset_nobody_knows_gives_up_the_offset_and_keeps_the_object() {
976 let mut names = Interner::new();
977 let module = module(&mut names);
978 let mut f = func(&mut names, &[Type::int(64)]);
979 let n = param(&f, 0);
980 let mut build = builder(&mut f);
981 let object = local(&mut build, 16);
982 let somewhere = build.binary(Opcode::PtrAdd, object, n, Flags::NONE);
983 let read = build.load(Type::int(32), somewhere, plain(4), Flags::NONE);
984 build.store(read, object, plain(4), Flags::NONE);
985 build.ret(&[]);
986
987 let mut alias = Alias::new(&f, &module);
988 let (a, b) = two(&alias, &f);
989 assert_eq!(a.origin, b.origin, "both are still that one object");
990 assert_eq!(a.offset, None);
991 assert_eq!(alias.query(&a, &b), Answer::May);
992 }
993
994 #[test]
995 fn a_local_whose_address_stays_here_is_not_what_a_parameter_points_at() {
996 let mut names = Interner::new();
997 let module = module(&mut names);
998 let mut f = func(&mut names, &[Type::PTR]);
999 let outside = param(&f, 0);
1000 let mut build = builder(&mut f);
1001 let object = local(&mut build, 16);
1002 let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1003 build.store(read, outside, plain(4), Flags::NONE);
1004 build.ret(&[]);
1005
1006 let mut alias = Alias::new(&f, &module);
1007 assert_eq!(alias.escapes().count(), 0);
1008 let (a, b) = two(&alias, &f);
1009 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Escape));
1010 }
1011
1012 #[test]
1013 fn a_local_whose_address_was_stored_somewhere_is() {
1014 let mut names = Interner::new();
1015 let module = module(&mut names);
1016 let mut f = func(&mut names, &[Type::PTR]);
1017 let outside = param(&f, 0);
1018 let mut build = builder(&mut f);
1019 let object = local(&mut build, 16);
1020 build.store(object, outside, plain(8), Flags::NONE);
1023 let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1024 build.store(read, outside, plain(4), Flags::NONE);
1025 build.ret(&[]);
1026
1027 let mut alias = Alias::new(&f, &module);
1028 assert_eq!(alias.escapes().count(), 1);
1029 let read = first(&f, Opcode::Load);
1030 let write = last(&f, Opcode::Store);
1031 let a = alias.reads(read).unwrap();
1032 let b = alias.writes(write).unwrap();
1033 assert_eq!(alias.query(&a, &b), Answer::May);
1034 }
1035
1036 fn first(func: &Func, opcode: Opcode) -> Inst {
1037 func.blocks()
1038 .flat_map(|block| func.insts(block))
1039 .find(|&inst| func[inst].opcode == opcode)
1040 .expect("an instruction with that opcode")
1041 }
1042
1043 fn last(func: &Func, opcode: Opcode) -> Inst {
1044 func.blocks()
1045 .flat_map(|block| func.insts(block))
1046 .filter(|&inst| func[inst].opcode == opcode)
1047 .last()
1048 .expect("an instruction with that opcode")
1049 }
1050
1051 #[test]
1052 fn an_address_carried_through_a_block_parameter_has_left_the_function() {
1053 let mut names = Interner::new();
1054 let module = module(&mut names);
1055 let mut f = func(&mut names, &[]);
1056 let start = f.entry().expect("an entry block");
1057 let next = f.create_block();
1058 f.append_param(next, Type::PTR);
1059
1060 let mut build = Builder::new(&mut f, start);
1061 let object = local(&mut build, 16);
1062 build.jump(next, &[object]);
1063 let mut build = Builder::new(&mut f, next);
1064 build.ret(&[]);
1065
1066 let alias = Alias::new(&f, &module);
1067 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1068 }
1069
1070 #[test]
1071 fn comparing_two_addresses_does_not_let_either_of_them_out() {
1072 let mut names = Interner::new();
1073 let module = module(&mut names);
1074 let mut f = func(&mut names, &[Type::PTR]);
1075 let outside = param(&f, 0);
1076 let mut build = builder(&mut f);
1077 let object = local(&mut build, 16);
1078 build.icmp(IntPred::Eq, object, outside);
1079 build.ret(&[]);
1080
1081 let alias = Alias::new(&f, &module);
1082 assert_eq!(alias.escapes().count(), 0);
1083 }
1084
1085 #[test]
1086 fn an_address_turned_into_a_number_has_left_the_function() {
1087 let mut names = Interner::new();
1090 let module = module(&mut names);
1091 let mut f = func(&mut names, &[]);
1092 let mut build = builder(&mut f);
1093 let object = local(&mut build, 16);
1094 build.unary(Opcode::PtrToInt, object, Type::int(64));
1095 build.ret(&[]);
1096
1097 let alias = Alias::new(&f, &module);
1098 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1099 }
1100
1101 #[test]
1102 fn two_restrict_pointers_in_one_scope_do_not_reach_the_same_object() {
1103 let mut names = Interner::new();
1104 let module = module(&mut names);
1105 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1106 let (one, other) = (param(&f, 0), param(&f, 1));
1107 let mut build = builder(&mut f);
1108 let mut info = plain(4);
1109 info.restrict = Restrict { clique: 1, base: 1 };
1110 let read = build.load(Type::int(32), one, info, Flags::NONE);
1111 info.restrict = Restrict { clique: 1, base: 2 };
1112 build.store(read, other, info, Flags::NONE);
1113 build.ret(&[]);
1114
1115 let mut alias = Alias::new(&f, &module);
1116 let (a, b) = two(&alias, &f);
1117 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Restrict));
1118 }
1119
1120 #[test]
1121 fn two_restrict_pointers_in_different_scopes_say_nothing_about_each_other() {
1122 let mut names = Interner::new();
1123 let module = module(&mut names);
1124 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1125 let (one, other) = (param(&f, 0), param(&f, 1));
1126 let mut build = builder(&mut f);
1127 let mut info = plain(4);
1128 info.restrict = Restrict { clique: 1, base: 1 };
1129 let read = build.load(Type::int(32), one, info, Flags::NONE);
1130 info.restrict = Restrict { clique: 2, base: 1 };
1131 build.store(read, other, info, Flags::NONE);
1132 build.ret(&[]);
1133
1134 let mut alias = Alias::new(&f, &module);
1135 let (a, b) = two(&alias, &f);
1136 assert_eq!(alias.query(&a, &b), Answer::May);
1137 }
1138
1139 fn types(module: &mut Module, names: &mut Interner) -> (Meta, Meta, Meta) {
1141 let root = module.add_meta(MetaNode::Tbaa(TbaaNode {
1142 name: names.intern("char"),
1143 parent: None,
1144 offset: 0,
1145 }));
1146 let int = module.add_meta(MetaNode::Tbaa(TbaaNode {
1147 name: names.intern("int"),
1148 parent: Some(root),
1149 offset: 0,
1150 }));
1151 let float = module.add_meta(MetaNode::Tbaa(TbaaNode {
1152 name: names.intern("float"),
1153 parent: Some(root),
1154 offset: 0,
1155 }));
1156 (root, int, float)
1157 }
1158
1159 #[test]
1160 fn two_unrelated_types_describe_no_object_in_common() {
1161 let mut names = Interner::new();
1162 let mut module = module(&mut names);
1163 let (_, int, float) = types(&mut module, &mut names);
1164 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1165 let (one, other) = (param(&f, 0), param(&f, 1));
1166 let mut build = builder(&mut f);
1167 let mut info = plain(4);
1168 info.tbaa = Some(int);
1169 let read = build.load(Type::int(32), one, info, Flags::NONE);
1170 info.tbaa = Some(float);
1171 build.store(read, other, info, Flags::NONE);
1172 build.ret(&[]);
1173
1174 let mut alias = Alias::new(&f, &module);
1175 let (a, b) = two(&alias, &f);
1176 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Tbaa));
1177 }
1178
1179 #[test]
1180 fn an_access_through_char_conflicts_with_everything() {
1181 let mut names = Interner::new();
1182 let mut module = module(&mut names);
1183 let (root, int, _) = types(&mut module, &mut names);
1184 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1185 let (one, other) = (param(&f, 0), param(&f, 1));
1186 let mut build = builder(&mut f);
1187 let mut info = plain(4);
1188 info.tbaa = Some(int);
1189 let read = build.load(Type::int(32), one, info, Flags::NONE);
1190 info.tbaa = Some(root);
1191 build.store(read, other, info, Flags::NONE);
1192 build.ret(&[]);
1193
1194 let mut alias = Alias::new(&f, &module);
1195 let (a, b) = two(&alias, &f);
1196 assert_eq!(alias.query(&a, &b), Answer::May);
1197 }
1198
1199 #[test]
1200 fn turning_strict_aliasing_off_turns_off_that_layer_and_no_other() {
1201 let mut names = Interner::new();
1202 let mut module = module(&mut names);
1203 let (_, int, float) = types(&mut module, &mut names);
1204 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1205 let (one, other) = (param(&f, 0), param(&f, 1));
1206 let mut build = builder(&mut f);
1207 let mut info = plain(4);
1208 info.tbaa = Some(int);
1209 info.restrict = Restrict { clique: 1, base: 1 };
1210 let read = build.load(Type::int(32), one, info, Flags::NONE);
1211 info.tbaa = Some(float);
1212 info.restrict = Restrict { clique: 1, base: 2 };
1213 build.store(read, other, info, Flags::NONE);
1214 build.ret(&[]);
1215
1216 let options = Options { strict_aliasing: false };
1217 let mut alias = Alias::with(&f, &module, options);
1218 let (a, b) = two(&alias, &f);
1219 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Restrict));
1222
1223 let mut without = Alias::with(&f, &module, options);
1224 let plainer = Access { restrict: Restrict::NONE, ..a };
1225 let other = Access { restrict: Restrict::NONE, ..b };
1226 assert_eq!(without.query(&plainer, &other), Answer::May);
1227
1228 let mut with = Alias::new(&f, &module);
1229 assert_eq!(with.query(&plainer, &other), Answer::No(Reason::Tbaa));
1230 }
1231
1232 #[test]
1233 fn writing_one_member_of_a_union_and_reading_another_is_one_object() {
1234 let mut names = Interner::new();
1239 let mut module = module(&mut names);
1240 let (_, int, float) = types(&mut module, &mut names);
1241 let mut f = func(&mut names, &[]);
1242 let mut build = builder(&mut f);
1243 let object = local(&mut build, 4);
1244 let mut info = plain(4);
1245 info.tbaa = Some(float);
1246 let read = build.load(Type::int(32), object, info, Flags::NONE);
1247 info.tbaa = Some(int);
1248 build.store(read, object, info, Flags::NONE);
1249 build.ret(&[]);
1250
1251 let mut alias = Alias::new(&f, &module);
1252 let (a, b) = two(&alias, &f);
1253 assert_eq!(alias.query(&a, &b), Answer::May);
1254 }
1255
1256 #[test]
1257 fn two_volatile_accesses_conflict_whatever_else_is_true_of_them() {
1258 let mut names = Interner::new();
1259 let module = module(&mut names);
1260 let mut f = func(&mut names, &[]);
1261 let mut build = builder(&mut f);
1262 let one = local(&mut build, 16);
1263 let other = local(&mut build, 16);
1264 let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1265 build.store(read, other, plain(4), Flags::VOLATILE);
1266 build.ret(&[]);
1267
1268 let mut alias = Alias::new(&f, &module);
1269 let (a, b) = two(&alias, &f);
1270 assert_eq!(alias.query(&a, &b), Answer::May);
1273 }
1274
1275 #[test]
1276 fn one_volatile_access_and_one_ordinary_one_are_argued_about_as_usual() {
1277 let mut names = Interner::new();
1278 let module = module(&mut names);
1279 let mut f = func(&mut names, &[]);
1280 let mut build = builder(&mut f);
1281 let one = local(&mut build, 16);
1282 let other = local(&mut build, 16);
1283 let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1284 build.store(read, other, plain(4), Flags::NONE);
1285 build.ret(&[]);
1286
1287 let mut alias = Alias::new(&f, &module);
1288 let (a, b) = two(&alias, &f);
1289 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1290 }
1291
1292 #[test]
1293 fn a_copy_reads_its_source_and_writes_its_destination() {
1294 let mut names = Interner::new();
1295 let module = module(&mut names);
1296 let mut f = func(&mut names, &[]);
1297 let mut build = builder(&mut f);
1298 let to = local(&mut build, 16);
1299 let from = local(&mut build, 16);
1300 let mem = build.func().add_mem(sized(16, 8));
1301 let args = build.func().push_values(&[to, from]);
1302 build.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1303 build.ret(&[]);
1304
1305 let alias = Alias::new(&f, &module);
1306 let copy = first(&f, Opcode::Memcpy);
1307 let read = alias.reads(copy).expect("a copy reads");
1308 let written = alias.writes(copy).expect("a copy writes");
1309 assert_eq!(read.size, Some(16));
1310 assert_eq!(written.size, Some(16));
1311 assert_ne!(read.origin, written.origin);
1312 }
1313
1314 fn call_to(
1316 names: &mut Interner,
1317 module: &mut Module,
1318 f: &mut Func,
1319 attrs: Attrs,
1320 args: &[Value],
1321 ) -> Inst {
1322 let name = names.intern("g");
1323 let params: Vec<Type> = args.iter().map(|_| Type::PTR).collect();
1324 let mut callee = Func::new(name, Signature::new().with_params(¶ms));
1325 callee.attrs = attrs;
1326 module.add_func(callee);
1327 let signature = f.add_signature(Signature::new().with_params(¶ms));
1328 let mut build = builder(f);
1329 build.call(name, signature, args)
1330 }
1331
1332 fn attrs(set: AttrSet) -> Attrs {
1333 Attrs { set, ..Attrs::NONE }
1334 }
1335
1336 #[test]
1337 fn a_call_cannot_touch_a_local_whose_address_stayed_here() {
1338 let mut names = Interner::new();
1339 let mut module = module(&mut names);
1340 let mut f = func(&mut names, &[Type::PTR]);
1341 let outside = param(&f, 0);
1342 let mut build = builder(&mut f);
1343 let object = local(&mut build, 16);
1344 let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1345 let _ = read;
1346 let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[outside]);
1347 let mut build = builder(&mut f);
1348 build.ret(&[]);
1349
1350 let mut alias = Alias::new(&f, &module);
1351 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1352 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Escape));
1353 assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Escape));
1354 }
1355
1356 #[test]
1357 fn a_call_can_touch_a_local_it_was_handed() {
1358 let mut names = Interner::new();
1359 let mut module = module(&mut names);
1360 let mut f = func(&mut names, &[]);
1361 let mut build = builder(&mut f);
1362 let object = local(&mut build, 16);
1363 build.load(Type::int(32), object, plain(4), Flags::NONE);
1364 let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[object]);
1365 let mut build = builder(&mut f);
1366 build.ret(&[]);
1367
1368 let mut alias = Alias::new(&f, &module);
1369 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1370 assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1371 }
1372
1373 #[test]
1374 fn a_pure_callee_reads_memory_and_writes_none() {
1375 let mut names = Interner::new();
1376 let mut module = module(&mut names);
1377 let mut f = func(&mut names, &[Type::PTR]);
1378 let outside = param(&f, 0);
1379 let mut build = builder(&mut f);
1380 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1381 let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READONLY), &[outside]);
1382 let mut build = builder(&mut f);
1383 build.ret(&[]);
1384
1385 let mut alias = Alias::new(&f, &module);
1386 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1387 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1388 assert_eq!(alias.read_by(&reference, call), Answer::May);
1389 }
1390
1391 #[test]
1392 fn a_const_callee_touches_no_memory_at_all() {
1393 let mut names = Interner::new();
1394 let mut module = module(&mut names);
1395 let mut f = func(&mut names, &[Type::PTR]);
1396 let outside = param(&f, 0);
1397 let mut build = builder(&mut f);
1398 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1399 let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READNONE), &[outside]);
1400 let mut build = builder(&mut f);
1401 build.ret(&[]);
1402
1403 let mut alias = Alias::new(&f, &module);
1404 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1405 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1406 assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Attribute));
1407 }
1408
1409 #[test]
1410 fn a_callee_that_touches_only_its_arguments_leaves_a_global_it_was_not_passed_alone() {
1411 let mut names = Interner::new();
1412 let mut module = module(&mut names);
1413 let x = names.intern("x");
1414 let mut f = func(&mut names, &[Type::PTR]);
1415 let outside = param(&f, 0);
1416 let mut build = builder(&mut f);
1417 let object = global(&mut build, &mut module, x);
1418 build.load(Type::int(32), object, plain(4), Flags::NONE);
1419 let call =
1420 call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[outside]);
1421 let mut build = builder(&mut f);
1422 build.ret(&[]);
1423
1424 let mut alias = Alias::new(&f, &module);
1425 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1426 assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1429 }
1430
1431 #[test]
1432 fn a_callee_that_touches_only_its_arguments_and_was_handed_one_object_leaves_the_other() {
1433 let mut names = Interner::new();
1434 let mut module = module(&mut names);
1435 let (x, y) = (names.intern("x"), names.intern("y"));
1436 let mut f = func(&mut names, &[]);
1437 let mut build = builder(&mut f);
1438 let watched = global(&mut build, &mut module, x);
1439 let handed = global(&mut build, &mut module, y);
1440 build.load(Type::int(32), watched, plain(4), Flags::NONE);
1441 let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[handed]);
1442 let mut build = builder(&mut f);
1443 build.ret(&[]);
1444
1445 let mut alias = Alias::new(&f, &module);
1446 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1447 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1448 }
1449
1450 #[test]
1451 fn an_indirect_call_is_not_argued_about() {
1452 let mut names = Interner::new();
1453 let module = module(&mut names);
1454 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1455 let (target, outside) = (param(&f, 0), param(&f, 1));
1456 let mut build = builder(&mut f);
1457 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1458 let signature = build.func().add_signature(Signature::new().with_params(&[Type::PTR]));
1459 let varargs = build.func().push_abis(&[]);
1460 let info = build.func().add_call(CallInfo { callee: None, signature, varargs });
1461 let args = build.func().push_values(&[target, outside]);
1462 let call = build.inst(
1463 InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::CallIndirect) },
1464 &[],
1465 );
1466 build.ret(&[]);
1467
1468 let mut alias = Alias::new(&f, &module);
1469 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1470 assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1471 }
1472
1473 #[test]
1474 fn every_reason_has_a_name_and_a_sentence() {
1475 for reason in Reason::ALL {
1476 assert!(!reason.name().is_empty());
1477 assert!(!reason.describe().is_empty());
1478 assert_eq!(Reason::ALL[reason.index()], reason);
1479 }
1480 assert_eq!(Reason::ALL.len(), Reason::COUNT);
1481 assert_eq!(Answer::No(Reason::Offset).reason(), Some(Reason::Offset));
1482 assert!(Answer::No(Reason::Offset).is_no());
1483 assert_eq!(Answer::May.reason(), None);
1484 assert!(!Answer::May.is_no());
1485 }
1486}