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