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