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 Plane,
127}
128
129impl Reason {
130 pub const ALL: [Self; 7] = [
132 Self::Distinct,
133 Self::Escape,
134 Self::Offset,
135 Self::Tbaa,
136 Self::Restrict,
137 Self::Attribute,
138 Self::Plane,
139 ];
140
141 pub const COUNT: usize = Self::ALL.len();
143
144 #[must_use]
146 pub const fn index(self) -> usize {
147 match self {
148 Self::Distinct => 0,
149 Self::Escape => 1,
150 Self::Offset => 2,
151 Self::Tbaa => 3,
152 Self::Restrict => 4,
153 Self::Attribute => 5,
154 Self::Plane => 6,
155 }
156 }
157
158 #[must_use]
160 pub const fn name(self) -> &'static str {
161 match self {
162 Self::Distinct => "distinct",
163 Self::Escape => "escape",
164 Self::Offset => "offset",
165 Self::Tbaa => "tbaa",
166 Self::Restrict => "restrict",
167 Self::Attribute => "attribute",
168 Self::Plane => "plane",
169 }
170 }
171
172 #[must_use]
174 pub const fn describe(self) -> &'static str {
175 match self {
176 Self::Distinct => "they are two different objects",
177 Self::Escape => "the address of that local never leaves this function",
178 Self::Offset => "they are parts of one object that do not overlap",
179 Self::Tbaa => "no object has both of those types",
180 Self::Restrict => "restrict says those two pointers do not reach the same object",
181 Self::Attribute => "the callee is declared not to touch memory that way",
182 Self::Plane => "that one touches only the planes, which the program cannot name",
183 }
184 }
185}
186
187#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
189pub enum Answer {
190 May,
192 No(Reason),
194}
195
196impl Answer {
197 #[must_use]
199 pub const fn is_no(self) -> bool {
200 matches!(self, Self::No(_))
201 }
202
203 #[must_use]
205 pub const fn reason(self) -> Option<Reason> {
206 match self {
207 Self::No(reason) => Some(reason),
208 Self::May => None,
209 }
210 }
211}
212
213#[derive(Clone, Copy, Debug, PartialEq, Eq)]
219pub struct Options {
220 pub strict_aliasing: bool,
223}
224
225impl Default for Options {
226 fn default() -> Self {
227 Self { strict_aliasing: true }
228 }
229}
230
231#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
239pub enum Origin {
240 Local(Inst),
243 Global(Symbol),
245 Unknown(Value),
248}
249
250impl Origin {
251 #[must_use]
253 pub const fn is_object(self) -> bool {
254 matches!(self, Self::Local(_) | Self::Global(_))
255 }
256}
257
258#[must_use]
264pub fn origin(func: &Func, mut value: Value) -> (Origin, Option<i64>) {
265 let mut offset = Some(0i64);
266 for _ in 0..CHASE_LIMIT {
267 let Def::Result { inst, .. } = func[value].def else {
268 return (Origin::Unknown(value), offset);
270 };
271 let data = func[inst];
272 match data.opcode {
273 Opcode::Alloca => return (Origin::Local(inst), offset),
274 Opcode::GlobalAddr => {
275 let Extra::Symbol(name) = data.extra else {
276 return (Origin::Unknown(value), offset);
277 };
278 return (Origin::Global(name), offset);
279 }
280 Opcode::PtrAdd => {
281 let args = &func[data.args];
282 let (base, by) = (args[0], args[1]);
283 offset = offset
284 .and_then(|so_far| Some((so_far, constant(func, by)?)))
285 .and_then(|(so_far, by)| so_far.checked_add(by));
286 value = base;
287 }
288 Opcode::Bitcast => value = func[data.args][0],
291 Opcode::CapOf => value = func[data.args][0],
298 _ => return (Origin::Unknown(value), offset),
299 }
300 }
301 (Origin::Unknown(value), None)
302}
303
304#[derive(Clone, Copy, Debug, PartialEq, Eq)]
310pub struct Access {
311 pub origin: Origin,
313 pub offset: Option<i64>,
315 pub size: Option<u64>,
317 pub tbaa: Option<Meta>,
319 pub restrict: Restrict,
321 pub volatile: bool,
323}
324
325impl Access {
326 #[must_use]
332 pub fn through(func: &Func, pointer: Value) -> Self {
333 let (origin, offset) = origin(func, pointer);
334 Self { origin, offset, size: None, tbaa: None, restrict: Restrict::NONE, volatile: false }
335 }
336
337 #[must_use]
339 pub fn range(&self) -> Option<(i128, i128)> {
340 let (offset, size) = (self.offset?, self.size?);
341 let start = i128::from(offset);
342 Some((start, start + i128::from(size)))
343 }
344}
345
346#[derive(Clone, Debug, Default)]
358pub struct Escapes {
359 escaped: HashSet<Inst>,
360}
361
362impl Escapes {
363 #[must_use]
365 pub fn of(func: &Func) -> Self {
366 let mut escaped = HashSet::new();
367 for block in func.blocks() {
368 for inst in func.insts(block) {
369 let data = func[inst];
370 for (index, &arg) in func[data.args].iter().enumerate() {
371 if keeps_address(data.opcode, index) {
372 continue;
373 }
374 if let (Origin::Local(local), _) = origin(func, arg) {
375 escaped.insert(local);
376 }
377 }
378 for call in func.successors(inst) {
381 for &arg in &func[call.args] {
382 if let (Origin::Local(local), _) = origin(func, arg) {
383 escaped.insert(local);
384 }
385 }
386 }
387 }
388 }
389 Self { escaped }
390 }
391
392 #[must_use]
394 pub fn escaped(&self, local: Inst) -> bool {
395 self.escaped.contains(&local)
396 }
397
398 #[must_use]
400 pub fn count(&self) -> usize {
401 self.escaped.len()
402 }
403}
404
405#[must_use]
409pub const fn keeps_address(opcode: Opcode, index: usize) -> bool {
410 match (opcode, index) {
411 (Opcode::Load | Opcode::AtomicLoad, 0)
413 | (Opcode::Store | Opcode::AtomicStore, 1)
414 | (Opcode::AtomicRmw | Opcode::Cmpxchg, 0)
415 | (Opcode::Memcpy | Opcode::Memmove, 0 | 1)
416 | (Opcode::Memset | Opcode::Prefetch, 0) => true,
417 (Opcode::PtrAdd | Opcode::Bitcast, 0) => true,
420 (Opcode::ICmp, 0 | 1) => true,
423 (op, _) if op.touches_only_planes() => true,
429 (Opcode::CapLoad | Opcode::CapStore | Opcode::CapCopy, 0 | 1) => true,
438 (Opcode::CapOf, 0) => true,
447 _ => false,
448 }
449}
450
451#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
457pub struct Counts {
458 queries: u64,
459 answered: [u64; Reason::COUNT],
460}
461
462impl Counts {
463 #[must_use]
465 pub const fn queries(&self) -> u64 {
466 self.queries
467 }
468
469 #[must_use]
471 pub const fn answered(&self, reason: Reason) -> u64 {
472 self.answered[reason.index()]
473 }
474
475 #[must_use]
477 pub fn total(&self) -> u64 {
478 self.answered.iter().sum()
479 }
480}
481
482#[derive(Debug)]
493pub struct Alias<'a> {
494 func: &'a Func,
495 outside: &'a Outside,
496 options: Options,
497 escapes: Escapes,
498 counts: Counts,
499}
500
501impl<'a> Alias<'a> {
502 #[must_use]
504 pub fn new(func: &'a Func, outside: &'a Outside) -> Self {
505 Self::with(func, outside, Options::default())
506 }
507
508 #[must_use]
510 pub fn with(func: &'a Func, outside: &'a Outside, options: Options) -> Self {
511 Self { func, outside, options, escapes: Escapes::of(func), counts: Counts::default() }
512 }
513
514 #[must_use]
516 pub const fn escapes(&self) -> &Escapes {
517 &self.escapes
518 }
519
520 #[must_use]
522 pub const fn counts(&self) -> &Counts {
523 &self.counts
524 }
525
526 #[must_use]
528 pub fn reads(&self, inst: Inst) -> Option<Access> {
529 let data = self.func[inst];
530 let args = &self.func[data.args];
531 let info = self.mem(inst);
532 let (pointer, size) = match data.opcode {
533 Opcode::Load | Opcode::AtomicLoad => (args[0], self.width(self.result_type(inst)?)),
534 Opcode::Memcpy | Opcode::Memmove => (args[1], Some(info?.size)),
536 Opcode::AtomicRmw => (args[0], self.width(self.func[args[1]].ty)),
539 Opcode::Cmpxchg => (args[0], self.width(self.func[args[1]].ty)),
540 Opcode::VaObject => (args[0], Some(info?.size)),
541 _ => return None,
542 };
543 Some(self.access(pointer, size, info, data.flags))
544 }
545
546 #[must_use]
548 pub fn writes(&self, inst: Inst) -> Option<Access> {
549 let data = self.func[inst];
550 let args = &self.func[data.args];
551 let info = self.mem(inst);
552 let (pointer, size) = match data.opcode {
553 Opcode::Store | Opcode::AtomicStore => (args[1], self.width(self.func[args[0]].ty)),
554 Opcode::Memcpy | Opcode::Memmove | Opcode::Memset => (args[0], Some(info?.size)),
555 Opcode::AtomicRmw | Opcode::Cmpxchg => (args[0], self.width(self.func[args[1]].ty)),
556 _ => return None,
557 };
558 Some(self.access(pointer, size, info, data.flags))
559 }
560
561 pub fn query(&mut self, a: &Access, b: &Access) -> Answer {
563 self.counts.queries += 1;
564 let answer = self.decide(a, b);
565 if let Answer::No(reason) = answer {
566 self.counts.answered[reason.index()] += 1;
567 }
568 answer
569 }
570
571 pub fn clobbered_by(&mut self, reference: &Access, call: Inst) -> Answer {
579 self.touched_by(reference, call, true)
580 }
581
582 pub fn read_by(&mut self, reference: &Access, call: Inst) -> Answer {
586 self.touched_by(reference, call, false)
587 }
588
589 fn decide(&self, a: &Access, b: &Access) -> Answer {
592 if a.volatile && b.volatile {
596 return Answer::May;
597 }
598
599 if a.origin.is_object() && b.origin.is_object() {
607 if self.distinct(a.origin, b.origin) {
608 return Answer::No(Reason::Distinct);
609 }
610 if a.origin == b.origin {
611 return by_offset(a, b);
612 }
613 return Answer::May;
614 }
615
616 if let Some(local) = self.private(a).or_else(|| self.private(b)) {
619 let _ = local;
620 return Answer::No(Reason::Escape);
621 }
622
623 if a.restrict.disjoint(b.restrict) {
624 return Answer::No(Reason::Restrict);
625 }
626
627 if self.options.strict_aliasing {
628 if let (Some(one), Some(other)) = (a.tbaa, b.tbaa) {
629 if !self.types_conflict(one, other) {
630 return Answer::No(Reason::Tbaa);
631 }
632 }
633 }
634
635 if a.origin == b.origin {
637 return by_offset(a, b);
638 }
639
640 Answer::May
641 }
642
643 fn private(&self, reference: &Access) -> Option<Inst> {
646 match reference.origin {
647 Origin::Local(local) if !self.escapes.escaped(local) => Some(local),
648 _ => None,
649 }
650 }
651
652 fn distinct(&self, a: Origin, b: Origin) -> bool {
654 match (a, b) {
655 (Origin::Local(one), Origin::Local(other)) => one != other,
656 (Origin::Local(_), Origin::Global(_)) | (Origin::Global(_), Origin::Local(_)) => true,
658 (Origin::Global(one), Origin::Global(other)) => {
659 one != other && self.one_object(one) && self.one_object(other)
660 }
661 _ => false,
662 }
663 }
664
665 fn one_object(&self, name: Symbol) -> bool {
672 self.outside.one_object(name)
673 }
674
675 fn types_conflict(&self, one: Meta, other: Meta) -> bool {
681 self.at_or_below(one, other) || self.at_or_below(other, one)
682 }
683
684 fn at_or_below(&self, mut node: Meta, ancestor: Meta) -> bool {
686 for _ in 0..TREE_LIMIT {
687 if node == ancestor {
688 return true;
689 }
690 match self.outside.parent(node) {
691 Some(up) => node = up,
692 None => return false,
693 }
694 }
695 true
698 }
699
700 fn touched_by(&mut self, reference: &Access, call: Inst, writing: bool) -> Answer {
701 self.counts.queries += 1;
702 let answer = self.decide_call(reference, call, writing);
703 if let Answer::No(reason) = answer {
704 self.counts.answered[reason.index()] += 1;
705 }
706 answer
707 }
708
709 fn decide_call(&self, reference: &Access, call: Inst, writing: bool) -> Answer {
710 if self.func[call].opcode.touches_only_planes() {
716 return Answer::No(Reason::Plane);
717 }
718
719 if self.private(reference).is_some() {
723 return Answer::No(Reason::Escape);
724 }
725
726 let Some(attrs) = self.callee(call) else {
727 return Answer::May;
728 };
729 if attrs.set.contains(AttrSet::READNONE)
731 || (writing && attrs.set.contains(AttrSet::READONLY))
732 {
733 return Answer::No(Reason::Attribute);
734 }
735
736 if attrs.set.contains(AttrSet::ARGMEM_ONLY) {
743 let args = &self.func[self.func[call].args];
744 let mut all = true;
745 for &arg in args {
746 if !self.func[arg].ty.is_ptr() {
747 continue;
748 }
749 let through = Access::through(self.func, arg);
750 all &= self.decide(reference, &through).is_no();
751 }
752 if all {
753 return Answer::No(Reason::Attribute);
754 }
755 }
756
757 Answer::May
758 }
759
760 fn callee(&self, call: Inst) -> Option<Attrs> {
765 let Extra::Call(info) = self.func[call].extra else {
766 return None;
767 };
768 let name = self.func[info].callee?;
769 self.outside.attrs(name)
770 }
771
772 fn mem(&self, inst: Inst) -> Option<MemInfo> {
773 match self.func[inst].extra {
774 Extra::Mem(info) | Extra::Rmw(_, info) => Some(self.func[info]),
775 Extra::VaObject(object) => Some(self.func[self.func[object].mem]),
776 _ => None,
777 }
778 }
779
780 fn result_type(&self, inst: Inst) -> Option<Type> {
781 self.func[inst].results().next().map(|value| self.func[value].ty)
782 }
783
784 fn access(
785 &self,
786 pointer: Value,
787 size: Option<u64>,
788 info: Option<MemInfo>,
789 flags: Flags,
790 ) -> Access {
791 let (origin, offset) = origin(self.func, pointer);
792 Access {
793 origin,
794 offset,
795 size,
796 tbaa: info.and_then(|info| info.tbaa),
797 restrict: info.map_or(Restrict::NONE, |info| info.restrict),
798 volatile: flags.contains(Flags::VOLATILE),
799 }
800 }
801
802 fn width(&self, ty: Type) -> Option<u64> {
805 if ty.is_ptr() {
806 return self.outside.pointer_bytes();
807 }
808 let bits = u64::from(ty.bits()) * u64::from(ty.lanes());
809 (bits > 0).then(|| bits.div_ceil(8))
810 }
811}
812
813fn by_offset(a: &Access, b: &Access) -> Answer {
815 let (Some((a_start, a_end)), Some((b_start, b_end))) = (a.range(), b.range()) else {
816 return Answer::May;
817 };
818 if a_end <= b_start || b_end <= a_start {
819 return Answer::No(Reason::Offset);
820 }
821 Answer::May
822}
823
824fn constant(func: &Func, value: Value) -> Option<i64> {
826 let Def::Result { inst, .. } = func[value].def else {
827 return None;
828 };
829 let data = func[inst];
830 if data.opcode != Opcode::IConst {
831 return None;
832 }
833 let Extra::Imm(imm) = data.extra else {
834 return None;
835 };
836 i64::try_from(Imm::signed(func[imm], func[value].ty)).ok()
837}
838
839#[cfg(test)]
840mod tests {
841 use rucc_base::{Interner, Symbol};
842 use rucc_ir::{
843 AttrSet, Attrs, Builder, CallInfo, Extra, Flags, Func, Global, InstData, IntPred, MemInfo,
844 MemOrder, MetaNode, Module, Opcode, Restrict, Signature, TbaaNode, Type, Value,
845 };
846 use rucc_target::{TargetInfo, Triple};
847
848 use super::*;
849
850 fn module(names: &mut Interner) -> Module {
852 let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
853 Module::new(names.intern("t.c"), &target)
854 }
855
856 fn func(names: &mut Interner, params: &[Type]) -> Func {
858 let mut func = Func::new(names.intern("f"), Signature::new().with_params(params));
859 let entry = func.create_block();
860 for &ty in params {
861 func.append_param(entry, ty);
862 }
863 func
864 }
865
866 fn builder(func: &mut Func) -> Builder<'_> {
868 let entry = func.entry().expect("the function has an entry block");
869 Builder::new(func, entry)
870 }
871
872 fn param(func: &Func, index: usize) -> Value {
873 let entry = func.entry().expect("the function has an entry block");
874 func[entry].params[index]
875 }
876
877 fn plain(align: u32) -> MemInfo {
878 MemInfo {
879 size: 0,
880 align,
881 order: MemOrder::NotAtomic,
882 tbaa: None,
883 owns: 0,
884 restrict: Restrict::NONE,
885 }
886 }
887
888 fn sized(size: u64, align: u32) -> MemInfo {
889 MemInfo { size, ..plain(align) }
890 }
891
892 fn local(build: &mut Builder<'_>, size: u64) -> Value {
894 let mem = build.func().add_mem(sized(size, 8));
895 build.value(InstData { extra: Extra::Mem(mem), ..InstData::new(Opcode::Alloca) }, Type::PTR)
896 }
897
898 fn at(build: &mut Builder<'_>, base: Value, offset: i64) -> Value {
900 let by = build.iconst(Type::int(64), i128::from(offset));
901 build.binary(Opcode::PtrAdd, base, by, Flags::NONE)
902 }
903
904 fn global(build: &mut Builder<'_>, module: &mut Module, name: Symbol) -> Value {
906 module.add_global(Global::new(name, 16, 8));
907 build.value(
908 InstData { extra: Extra::Symbol(name), ..InstData::new(Opcode::GlobalAddr) },
909 Type::PTR,
910 )
911 }
912
913 #[test]
914 fn two_different_locals_are_two_objects() {
915 let mut names = Interner::new();
916 let module = module(&mut names);
917 let mut f = func(&mut names, &[]);
918 let mut build = builder(&mut f);
919 let one = local(&mut build, 16);
920 let other = local(&mut build, 16);
921 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
922 build.store(read, other, plain(4), Flags::NONE);
923 build.ret(&[]);
924
925 let outside = Outside::of(&module);
926 let mut alias = Alias::new(&f, &outside);
927 let (a, b) = two(&alias, &f);
928 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
929 assert_eq!(alias.counts().answered(Reason::Distinct), 1);
930 assert_eq!(alias.counts().queries(), 1);
931 }
932
933 fn two(alias: &Alias<'_>, func: &Func) -> (Access, Access) {
935 let mut read = None;
936 let mut written = None;
937 for block in func.blocks() {
938 for inst in func.insts(block) {
939 if read.is_none() {
940 read = alias.reads(inst);
941 }
942 if written.is_none() {
943 written = alias.writes(inst);
944 }
945 }
946 }
947 (read.expect("a read"), written.expect("a write"))
948 }
949
950 #[test]
951 fn a_local_and_a_global_are_two_objects() {
952 let mut names = Interner::new();
953 let mut module = module(&mut names);
954 let x = names.intern("x");
955 let mut f = func(&mut names, &[]);
956 let mut build = builder(&mut f);
957 let one = local(&mut build, 16);
958 let other = global(&mut build, &mut module, x);
959 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
960 build.store(read, other, plain(4), Flags::NONE);
961 build.ret(&[]);
962
963 let outside = Outside::of(&module);
964 let mut alias = Alias::new(&f, &outside);
965 let (a, b) = two(&alias, &f);
966 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
967 }
968
969 #[test]
970 fn two_different_globals_are_two_objects() {
971 let mut names = Interner::new();
972 let mut module = module(&mut names);
973 let (x, y) = (names.intern("x"), names.intern("y"));
974 let mut f = func(&mut names, &[]);
975 let mut build = builder(&mut f);
976 let one = global(&mut build, &mut module, x);
977 let other = global(&mut build, &mut module, y);
978 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
979 build.store(read, other, plain(4), Flags::NONE);
980 build.ret(&[]);
981
982 let outside = Outside::of(&module);
983 let mut alias = Alias::new(&f, &outside);
984 let (a, b) = two(&alias, &f);
985 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
986 }
987
988 #[test]
989 fn a_global_the_module_does_not_have_is_not_argued_about() {
990 let mut names = Interner::new();
993 let mut module = module(&mut names);
994 let (x, y) = (names.intern("x"), names.intern("y"));
995 let mut f = func(&mut names, &[]);
996 let mut build = builder(&mut f);
997 let one = global(&mut build, &mut module, x);
998 let other = build.value(
999 InstData { extra: Extra::Symbol(y), ..InstData::new(Opcode::GlobalAddr) },
1000 Type::PTR,
1001 );
1002 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1003 build.store(read, other, plain(4), Flags::NONE);
1004 build.ret(&[]);
1005
1006 let outside = Outside::of(&module);
1007 let mut alias = Alias::new(&f, &outside);
1008 let (a, b) = two(&alias, &f);
1009 assert_eq!(alias.query(&a, &b), Answer::May);
1010 }
1011
1012 #[test]
1013 fn two_parts_of_one_object_that_do_not_overlap_are_disjoint() {
1014 let mut names = Interner::new();
1015 let module = module(&mut names);
1016 let mut f = func(&mut names, &[]);
1017 let mut build = builder(&mut f);
1018 let object = local(&mut build, 16);
1019 let first = at(&mut build, object, 0);
1020 let second = at(&mut build, object, 4);
1021 let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
1022 build.store(read, second, plain(4), Flags::NONE);
1023 build.ret(&[]);
1024
1025 let outside = Outside::of(&module);
1026 let mut alias = Alias::new(&f, &outside);
1027 let (a, b) = two(&alias, &f);
1028 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Offset));
1029 }
1030
1031 #[test]
1032 fn two_parts_of_one_object_that_do_overlap_are_not() {
1033 let mut names = Interner::new();
1034 let module = module(&mut names);
1035 let mut f = func(&mut names, &[]);
1036 let mut build = builder(&mut f);
1037 let object = local(&mut build, 16);
1038 let first = at(&mut build, object, 0);
1039 let second = at(&mut build, object, 2);
1040 let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
1041 build.store(read, second, plain(4), Flags::NONE);
1042 build.ret(&[]);
1043
1044 let outside = Outside::of(&module);
1045 let mut alias = Alias::new(&f, &outside);
1046 let (a, b) = two(&alias, &f);
1047 assert_eq!(alias.query(&a, &b), Answer::May);
1048 }
1049
1050 #[test]
1051 fn an_offset_nobody_knows_gives_up_the_offset_and_keeps_the_object() {
1052 let mut names = Interner::new();
1053 let module = module(&mut names);
1054 let mut f = func(&mut names, &[Type::int(64)]);
1055 let n = param(&f, 0);
1056 let mut build = builder(&mut f);
1057 let object = local(&mut build, 16);
1058 let somewhere = build.binary(Opcode::PtrAdd, object, n, Flags::NONE);
1059 let read = build.load(Type::int(32), somewhere, plain(4), Flags::NONE);
1060 build.store(read, object, plain(4), Flags::NONE);
1061 build.ret(&[]);
1062
1063 let outside = Outside::of(&module);
1064 let mut alias = Alias::new(&f, &outside);
1065 let (a, b) = two(&alias, &f);
1066 assert_eq!(a.origin, b.origin, "both are still that one object");
1067 assert_eq!(a.offset, None);
1068 assert_eq!(alias.query(&a, &b), Answer::May);
1069 }
1070
1071 #[test]
1072 fn a_local_whose_address_stays_here_is_not_what_a_parameter_points_at() {
1073 let mut names = Interner::new();
1074 let module = module(&mut names);
1075 let mut f = func(&mut names, &[Type::PTR]);
1076 let outside = param(&f, 0);
1077 let mut build = builder(&mut f);
1078 let object = local(&mut build, 16);
1079 let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1080 build.store(read, outside, plain(4), Flags::NONE);
1081 build.ret(&[]);
1082
1083 let outside = Outside::of(&module);
1084 let mut alias = Alias::new(&f, &outside);
1085 assert_eq!(alias.escapes().count(), 0);
1086 let (a, b) = two(&alias, &f);
1087 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Escape));
1088 }
1089
1090 #[test]
1091 fn a_local_whose_address_was_stored_somewhere_is() {
1092 let mut names = Interner::new();
1093 let module = module(&mut names);
1094 let mut f = func(&mut names, &[Type::PTR]);
1095 let outside = param(&f, 0);
1096 let mut build = builder(&mut f);
1097 let object = local(&mut build, 16);
1098 build.store(object, outside, plain(8), Flags::NONE);
1101 let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1102 build.store(read, outside, plain(4), Flags::NONE);
1103 build.ret(&[]);
1104
1105 let outside = Outside::of(&module);
1106 let mut alias = Alias::new(&f, &outside);
1107 assert_eq!(alias.escapes().count(), 1);
1108 let read = first(&f, Opcode::Load);
1109 let write = last(&f, Opcode::Store);
1110 let a = alias.reads(read).unwrap();
1111 let b = alias.writes(write).unwrap();
1112 assert_eq!(alias.query(&a, &b), Answer::May);
1113 }
1114
1115 fn first(func: &Func, opcode: Opcode) -> Inst {
1116 func.blocks()
1117 .flat_map(|block| func.insts(block))
1118 .find(|&inst| func[inst].opcode == opcode)
1119 .expect("an instruction with that opcode")
1120 }
1121
1122 fn last(func: &Func, opcode: Opcode) -> Inst {
1123 func.blocks()
1124 .flat_map(|block| func.insts(block))
1125 .filter(|&inst| func[inst].opcode == opcode)
1126 .last()
1127 .expect("an instruction with that opcode")
1128 }
1129
1130 #[test]
1131 fn an_address_carried_through_a_block_parameter_has_left_the_function() {
1132 let mut names = Interner::new();
1133 let module = module(&mut names);
1134 let mut f = func(&mut names, &[]);
1135 let start = f.entry().expect("an entry block");
1136 let next = f.create_block();
1137 f.append_param(next, Type::PTR);
1138
1139 let mut build = Builder::new(&mut f, start);
1140 let object = local(&mut build, 16);
1141 build.jump(next, &[object]);
1142 let mut build = Builder::new(&mut f, next);
1143 build.ret(&[]);
1144
1145 let outside = Outside::of(&module);
1146 let alias = Alias::new(&f, &outside);
1147 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1148 }
1149
1150 #[test]
1151 fn comparing_two_addresses_does_not_let_either_of_them_out() {
1152 let mut names = Interner::new();
1153 let module = module(&mut names);
1154 let mut f = func(&mut names, &[Type::PTR]);
1155 let outside = param(&f, 0);
1156 let mut build = builder(&mut f);
1157 let object = local(&mut build, 16);
1158 build.icmp(IntPred::Eq, object, outside);
1159 build.ret(&[]);
1160
1161 let outside = Outside::of(&module);
1162 let alias = Alias::new(&f, &outside);
1163 assert_eq!(alias.escapes().count(), 0);
1164 }
1165
1166 #[test]
1167 fn a_plane_write_on_a_local_does_not_let_its_address_out() {
1168 let mut names = Interner::new();
1172 let module = module(&mut names);
1173 let mut f = func(&mut names, &[]);
1174 let mut build = builder(&mut f);
1175 let object = local(&mut build, 16);
1176 let width = build.iconst(Type::int(64), 16);
1177 let args = build.func().push_values(&[object, width]);
1178 build.inst(InstData { args, ..InstData::new(Opcode::MetaInit) }, &[]);
1179 build.ret(&[]);
1180
1181 let outside = Outside::of(&module);
1182 let alias = Alias::new(&f, &outside);
1183 assert_eq!(alias.escapes().count(), 0);
1184 }
1185
1186 #[test]
1187 fn a_local_that_is_only_asked_about_and_checked_does_not_leave_the_function() {
1188 let mut names = Interner::new();
1193 let module = module(&mut names);
1194 let mut f = func(&mut names, &[]);
1195 let mut build = builder(&mut f);
1196 let object = local(&mut build, 16);
1197 let args = build.func().push_values(&[object]);
1198 let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1199 let args = build.func().push_values(&[capability, object]);
1200 build.inst(InstData { args, ..InstData::new(Opcode::CheckBounds) }, &[]);
1201 build.ret(&[]);
1202
1203 let outside = Outside::of(&module);
1204 let alias = Alias::new(&f, &outside);
1205 assert_eq!(alias.escapes().count(), 0);
1206 }
1207
1208 #[test]
1209 fn a_capability_of_a_local_used_for_anything_else_does_let_it_out() {
1210 let mut names = Interner::new();
1215 let module = module(&mut names);
1216 let mut f = func(&mut names, &[]);
1217 let mut build = builder(&mut f);
1218 let object = local(&mut build, 16);
1219 let args = build.func().push_values(&[object]);
1220 let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1221 let base = build.iconst(Type::int(64), 0);
1222 let size = build.iconst(Type::int(64), 4);
1223 let args = build.func().push_values(&[capability, base, size]);
1224 build.value(InstData { args, ..InstData::new(Opcode::CapNarrow) }, Type::CAP);
1225 build.ret(&[]);
1226
1227 let outside = Outside::of(&module);
1228 let alias = Alias::new(&f, &outside);
1229 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1230 }
1231
1232 #[test]
1233 fn the_whitelist_says_yes_to_a_plane_access_at_every_operand() {
1234 for opcode in Opcode::all().filter(|opcode| opcode.touches_only_planes()) {
1238 for index in 0..4 {
1239 assert!(keeps_address(opcode, index), "{opcode} at {index}");
1240 }
1241 }
1242 for opcode in [Opcode::CapNarrow, Opcode::CapRecover] {
1243 assert!(!keeps_address(opcode, 0), "{opcode}");
1244 }
1245 for opcode in [Opcode::CapLoad, Opcode::CapStore, Opcode::CapCopy] {
1248 for index in 0..2 {
1249 assert!(keeps_address(opcode, index), "{opcode} at {index}");
1250 }
1251 }
1252 assert!(!keeps_address(Opcode::CapStore, 2));
1253 assert!(!keeps_address(Opcode::CapStore, 3));
1254 assert!(keeps_address(Opcode::CapOf, 0));
1255 }
1256
1257 #[test]
1258 fn a_local_a_pointer_is_written_into_does_not_leave_the_function_for_the_writing_down() {
1259 let mut names = Interner::new();
1264 let module = module(&mut names);
1265 let mut f = func(&mut names, &[Type::PTR]);
1266 let written = param(&f, 0);
1267 let mut build = builder(&mut f);
1268 let object = local(&mut build, 8);
1269 let args = build.func().push_values(&[object]);
1270 let container = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1271 let args = build.func().push_values(&[written]);
1272 let held = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1273 build.store(written, object, plain(8), Flags::NONE);
1274 let args = build.func().push_values(&[container, object, written, held]);
1275 build.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
1276 build.ret(&[]);
1277
1278 let outside = Outside::of(&module);
1279 let alias = Alias::new(&f, &outside);
1280 assert_eq!(alias.escapes().count(), 0);
1281 }
1282
1283 #[test]
1284 fn a_local_whose_capability_is_written_into_a_slot_does_leave_the_function() {
1285 let mut names = Interner::new();
1290 let module = module(&mut names);
1291 let mut f = func(&mut names, &[Type::PTR]);
1292 let into = param(&f, 0);
1293 let mut build = builder(&mut f);
1294 let object = local(&mut build, 8);
1295 let args = build.func().push_values(&[into]);
1296 let container = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1297 let args = build.func().push_values(&[object]);
1298 let held = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1299 let args = build.func().push_values(&[container, into, object, held]);
1300 build.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
1301 build.ret(&[]);
1302
1303 let outside = Outside::of(&module);
1304 let alias = Alias::new(&f, &outside);
1305 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1306 }
1307
1308 #[test]
1309 fn an_address_turned_into_a_number_has_left_the_function() {
1310 let mut names = Interner::new();
1313 let module = module(&mut names);
1314 let mut f = func(&mut names, &[]);
1315 let mut build = builder(&mut f);
1316 let object = local(&mut build, 16);
1317 build.unary(Opcode::PtrToInt, object, Type::int(64));
1318 build.ret(&[]);
1319
1320 let outside = Outside::of(&module);
1321 let alias = Alias::new(&f, &outside);
1322 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1323 }
1324
1325 #[test]
1326 fn two_restrict_pointers_in_one_scope_do_not_reach_the_same_object() {
1327 let mut names = Interner::new();
1328 let module = module(&mut names);
1329 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1330 let (one, other) = (param(&f, 0), param(&f, 1));
1331 let mut build = builder(&mut f);
1332 let mut info = plain(4);
1333 info.restrict = Restrict { clique: 1, base: 1 };
1334 let read = build.load(Type::int(32), one, info, Flags::NONE);
1335 info.restrict = Restrict { clique: 1, base: 2 };
1336 build.store(read, other, info, Flags::NONE);
1337 build.ret(&[]);
1338
1339 let outside = Outside::of(&module);
1340 let mut alias = Alias::new(&f, &outside);
1341 let (a, b) = two(&alias, &f);
1342 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Restrict));
1343 }
1344
1345 #[test]
1346 fn two_restrict_pointers_in_different_scopes_say_nothing_about_each_other() {
1347 let mut names = Interner::new();
1348 let module = module(&mut names);
1349 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1350 let (one, other) = (param(&f, 0), param(&f, 1));
1351 let mut build = builder(&mut f);
1352 let mut info = plain(4);
1353 info.restrict = Restrict { clique: 1, base: 1 };
1354 let read = build.load(Type::int(32), one, info, Flags::NONE);
1355 info.restrict = Restrict { clique: 2, base: 1 };
1356 build.store(read, other, info, Flags::NONE);
1357 build.ret(&[]);
1358
1359 let outside = Outside::of(&module);
1360 let mut alias = Alias::new(&f, &outside);
1361 let (a, b) = two(&alias, &f);
1362 assert_eq!(alias.query(&a, &b), Answer::May);
1363 }
1364
1365 fn types(module: &mut Module, names: &mut Interner) -> (Meta, Meta, Meta) {
1367 let root = module.add_meta(MetaNode::Tbaa(TbaaNode {
1368 name: names.intern("char"),
1369 parent: None,
1370 offset: 0,
1371 }));
1372 let int = module.add_meta(MetaNode::Tbaa(TbaaNode {
1373 name: names.intern("int"),
1374 parent: Some(root),
1375 offset: 0,
1376 }));
1377 let float = module.add_meta(MetaNode::Tbaa(TbaaNode {
1378 name: names.intern("float"),
1379 parent: Some(root),
1380 offset: 0,
1381 }));
1382 (root, int, float)
1383 }
1384
1385 #[test]
1386 fn two_unrelated_types_describe_no_object_in_common() {
1387 let mut names = Interner::new();
1388 let mut module = module(&mut names);
1389 let (_, int, float) = types(&mut module, &mut names);
1390 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1391 let (one, other) = (param(&f, 0), param(&f, 1));
1392 let mut build = builder(&mut f);
1393 let mut info = plain(4);
1394 info.tbaa = Some(int);
1395 let read = build.load(Type::int(32), one, info, Flags::NONE);
1396 info.tbaa = Some(float);
1397 build.store(read, other, info, Flags::NONE);
1398 build.ret(&[]);
1399
1400 let outside = Outside::of(&module);
1401 let mut alias = Alias::new(&f, &outside);
1402 let (a, b) = two(&alias, &f);
1403 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Tbaa));
1404 }
1405
1406 #[test]
1407 fn an_access_through_char_conflicts_with_everything() {
1408 let mut names = Interner::new();
1409 let mut module = module(&mut names);
1410 let (root, int, _) = types(&mut module, &mut names);
1411 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1412 let (one, other) = (param(&f, 0), param(&f, 1));
1413 let mut build = builder(&mut f);
1414 let mut info = plain(4);
1415 info.tbaa = Some(int);
1416 let read = build.load(Type::int(32), one, info, Flags::NONE);
1417 info.tbaa = Some(root);
1418 build.store(read, other, info, Flags::NONE);
1419 build.ret(&[]);
1420
1421 let outside = Outside::of(&module);
1422 let mut alias = Alias::new(&f, &outside);
1423 let (a, b) = two(&alias, &f);
1424 assert_eq!(alias.query(&a, &b), Answer::May);
1425 }
1426
1427 #[test]
1428 fn turning_strict_aliasing_off_turns_off_that_layer_and_no_other() {
1429 let mut names = Interner::new();
1430 let mut module = module(&mut names);
1431 let (_, int, float) = types(&mut module, &mut names);
1432 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1433 let (one, other) = (param(&f, 0), param(&f, 1));
1434 let mut build = builder(&mut f);
1435 let mut info = plain(4);
1436 info.tbaa = Some(int);
1437 info.restrict = Restrict { clique: 1, base: 1 };
1438 let read = build.load(Type::int(32), one, info, Flags::NONE);
1439 info.tbaa = Some(float);
1440 info.restrict = Restrict { clique: 1, base: 2 };
1441 build.store(read, other, info, Flags::NONE);
1442 build.ret(&[]);
1443
1444 let options = Options { strict_aliasing: false };
1445 let outside = Outside::of(&module);
1446 let mut alias = Alias::with(&f, &outside, options);
1447 let (a, b) = two(&alias, &f);
1448 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Restrict));
1451
1452 let mut without = Alias::with(&f, &outside, options);
1453 let plainer = Access { restrict: Restrict::NONE, ..a };
1454 let other = Access { restrict: Restrict::NONE, ..b };
1455 assert_eq!(without.query(&plainer, &other), Answer::May);
1456
1457 let mut with = Alias::new(&f, &outside);
1458 assert_eq!(with.query(&plainer, &other), Answer::No(Reason::Tbaa));
1459 }
1460
1461 #[test]
1462 fn writing_one_member_of_a_union_and_reading_another_is_one_object() {
1463 let mut names = Interner::new();
1468 let mut module = module(&mut names);
1469 let (_, int, float) = types(&mut module, &mut names);
1470 let mut f = func(&mut names, &[]);
1471 let mut build = builder(&mut f);
1472 let object = local(&mut build, 4);
1473 let mut info = plain(4);
1474 info.tbaa = Some(float);
1475 let read = build.load(Type::int(32), object, info, Flags::NONE);
1476 info.tbaa = Some(int);
1477 build.store(read, object, info, Flags::NONE);
1478 build.ret(&[]);
1479
1480 let outside = Outside::of(&module);
1481 let mut alias = Alias::new(&f, &outside);
1482 let (a, b) = two(&alias, &f);
1483 assert_eq!(alias.query(&a, &b), Answer::May);
1484 }
1485
1486 #[test]
1487 fn two_volatile_accesses_conflict_whatever_else_is_true_of_them() {
1488 let mut names = Interner::new();
1489 let module = module(&mut names);
1490 let mut f = func(&mut names, &[]);
1491 let mut build = builder(&mut f);
1492 let one = local(&mut build, 16);
1493 let other = local(&mut build, 16);
1494 let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1495 build.store(read, other, plain(4), Flags::VOLATILE);
1496 build.ret(&[]);
1497
1498 let outside = Outside::of(&module);
1499 let mut alias = Alias::new(&f, &outside);
1500 let (a, b) = two(&alias, &f);
1501 assert_eq!(alias.query(&a, &b), Answer::May);
1504 }
1505
1506 #[test]
1507 fn one_volatile_access_and_one_ordinary_one_are_argued_about_as_usual() {
1508 let mut names = Interner::new();
1509 let module = module(&mut names);
1510 let mut f = func(&mut names, &[]);
1511 let mut build = builder(&mut f);
1512 let one = local(&mut build, 16);
1513 let other = local(&mut build, 16);
1514 let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1515 build.store(read, other, plain(4), Flags::NONE);
1516 build.ret(&[]);
1517
1518 let outside = Outside::of(&module);
1519 let mut alias = Alias::new(&f, &outside);
1520 let (a, b) = two(&alias, &f);
1521 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1522 }
1523
1524 #[test]
1525 fn a_copy_reads_its_source_and_writes_its_destination() {
1526 let mut names = Interner::new();
1527 let module = module(&mut names);
1528 let mut f = func(&mut names, &[]);
1529 let mut build = builder(&mut f);
1530 let to = local(&mut build, 16);
1531 let from = local(&mut build, 16);
1532 let mem = build.func().add_mem(sized(16, 8));
1533 let args = build.func().push_values(&[to, from]);
1534 build.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1535 build.ret(&[]);
1536
1537 let outside = Outside::of(&module);
1538 let alias = Alias::new(&f, &outside);
1539 let copy = first(&f, Opcode::Memcpy);
1540 let read = alias.reads(copy).expect("a copy reads");
1541 let written = alias.writes(copy).expect("a copy writes");
1542 assert_eq!(read.size, Some(16));
1543 assert_eq!(written.size, Some(16));
1544 assert_ne!(read.origin, written.origin);
1545 }
1546
1547 fn call_to(
1549 names: &mut Interner,
1550 module: &mut Module,
1551 f: &mut Func,
1552 attrs: Attrs,
1553 args: &[Value],
1554 ) -> Inst {
1555 let name = names.intern("g");
1556 let params: Vec<Type> = args.iter().map(|_| Type::PTR).collect();
1557 let mut callee = Func::new(name, Signature::new().with_params(¶ms));
1558 callee.attrs = attrs;
1559 module.add_func(callee);
1560 let signature = f.add_signature(Signature::new().with_params(¶ms));
1561 let mut build = builder(f);
1562 build.call(name, signature, args)
1563 }
1564
1565 fn attrs(set: AttrSet) -> Attrs {
1566 Attrs { set, ..Attrs::NONE }
1567 }
1568
1569 #[test]
1570 fn a_call_cannot_touch_a_local_whose_address_stayed_here() {
1571 let mut names = Interner::new();
1572 let mut module = module(&mut names);
1573 let mut f = func(&mut names, &[Type::PTR]);
1574 let outside = param(&f, 0);
1575 let mut build = builder(&mut f);
1576 let object = local(&mut build, 16);
1577 let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1578 let _ = read;
1579 let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[outside]);
1580 let mut build = builder(&mut f);
1581 build.ret(&[]);
1582
1583 let outside = Outside::of(&module);
1584 let mut alias = Alias::new(&f, &outside);
1585 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1586 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Escape));
1587 assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Escape));
1588 }
1589
1590 #[test]
1591 fn a_call_can_touch_a_local_it_was_handed() {
1592 let mut names = Interner::new();
1593 let mut module = module(&mut names);
1594 let mut f = func(&mut names, &[]);
1595 let mut build = builder(&mut f);
1596 let object = local(&mut build, 16);
1597 build.load(Type::int(32), object, plain(4), Flags::NONE);
1598 let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[object]);
1599 let mut build = builder(&mut f);
1600 build.ret(&[]);
1601
1602 let outside = Outside::of(&module);
1603 let mut alias = Alias::new(&f, &outside);
1604 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1605 assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1606 }
1607
1608 #[test]
1609 fn a_pure_callee_reads_memory_and_writes_none() {
1610 let mut names = Interner::new();
1611 let mut module = module(&mut names);
1612 let mut f = func(&mut names, &[Type::PTR]);
1613 let outside = param(&f, 0);
1614 let mut build = builder(&mut f);
1615 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1616 let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READONLY), &[outside]);
1617 let mut build = builder(&mut f);
1618 build.ret(&[]);
1619
1620 let outside = Outside::of(&module);
1621 let mut alias = Alias::new(&f, &outside);
1622 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1623 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1624 assert_eq!(alias.read_by(&reference, call), Answer::May);
1625 }
1626
1627 #[test]
1628 fn a_plane_write_is_not_a_write_to_the_address_it_names() {
1629 let mut names = Interner::new();
1635 let module = module(&mut names);
1636 let mut f = func(&mut names, &[Type::PTR]);
1637 let outside = param(&f, 0);
1638 let mut build = builder(&mut f);
1639 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1640 let width = build.iconst(Type::int(64), 4);
1641 let args = build.func().push_values(&[outside, width]);
1642 build.inst(InstData { args, ..InstData::new(Opcode::MetaInit) }, &[]);
1643 build.ret(&[]);
1644
1645 let outside = Outside::of(&module);
1646 let mut alias = Alias::new(&f, &outside);
1647 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1648 let plane = first(&f, Opcode::MetaInit);
1649 assert_eq!(alias.clobbered_by(&reference, plane), Answer::No(Reason::Plane));
1650 assert_eq!(alias.read_by(&reference, plane), Answer::No(Reason::Plane));
1652 }
1653
1654 #[test]
1655 fn a_check_reads_a_plane_and_not_what_it_is_about() {
1656 let mut names = Interner::new();
1660 let module = module(&mut names);
1661 let mut f = func(&mut names, &[Type::PTR]);
1662 let outside = param(&f, 0);
1663 let mut build = builder(&mut f);
1664 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1665 let width = build.iconst(Type::int(64), 4);
1666 let args = build.func().push_values(&[outside, width]);
1667 build.inst(InstData { args, ..InstData::new(Opcode::CheckBounds) }, &[]);
1668 build.ret(&[]);
1669
1670 let outside = Outside::of(&module);
1671 let mut alias = Alias::new(&f, &outside);
1672 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1673 let check = first(&f, Opcode::CheckBounds);
1674 assert_eq!(alias.clobbered_by(&reference, check), Answer::No(Reason::Plane));
1675 assert_eq!(alias.read_by(&reference, check), Answer::No(Reason::Plane));
1676 }
1677
1678 #[test]
1679 fn a_const_callee_touches_no_memory_at_all() {
1680 let mut names = Interner::new();
1681 let mut module = module(&mut names);
1682 let mut f = func(&mut names, &[Type::PTR]);
1683 let outside = param(&f, 0);
1684 let mut build = builder(&mut f);
1685 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1686 let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READNONE), &[outside]);
1687 let mut build = builder(&mut f);
1688 build.ret(&[]);
1689
1690 let outside = Outside::of(&module);
1691 let mut alias = Alias::new(&f, &outside);
1692 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1693 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1694 assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Attribute));
1695 }
1696
1697 #[test]
1698 fn a_callee_that_touches_only_its_arguments_leaves_a_global_it_was_not_passed_alone() {
1699 let mut names = Interner::new();
1700 let mut module = module(&mut names);
1701 let x = names.intern("x");
1702 let mut f = func(&mut names, &[Type::PTR]);
1703 let outside = param(&f, 0);
1704 let mut build = builder(&mut f);
1705 let object = global(&mut build, &mut module, x);
1706 build.load(Type::int(32), object, plain(4), Flags::NONE);
1707 let call =
1708 call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[outside]);
1709 let mut build = builder(&mut f);
1710 build.ret(&[]);
1711
1712 let outside = Outside::of(&module);
1713 let mut alias = Alias::new(&f, &outside);
1714 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1715 assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1718 }
1719
1720 #[test]
1721 fn a_callee_that_touches_only_its_arguments_and_was_handed_one_object_leaves_the_other() {
1722 let mut names = Interner::new();
1723 let mut module = module(&mut names);
1724 let (x, y) = (names.intern("x"), names.intern("y"));
1725 let mut f = func(&mut names, &[]);
1726 let mut build = builder(&mut f);
1727 let watched = global(&mut build, &mut module, x);
1728 let handed = global(&mut build, &mut module, y);
1729 build.load(Type::int(32), watched, plain(4), Flags::NONE);
1730 let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[handed]);
1731 let mut build = builder(&mut f);
1732 build.ret(&[]);
1733
1734 let outside = Outside::of(&module);
1735 let mut alias = Alias::new(&f, &outside);
1736 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1737 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1738 }
1739
1740 #[test]
1741 fn an_indirect_call_is_not_argued_about() {
1742 let mut names = Interner::new();
1743 let module = module(&mut names);
1744 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1745 let (target, outside) = (param(&f, 0), param(&f, 1));
1746 let mut build = builder(&mut f);
1747 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1748 let signature = build.func().add_signature(Signature::new().with_params(&[Type::PTR]));
1749 let varargs = build.func().push_abis(&[]);
1750 let info = build.func().add_call(CallInfo { callee: None, signature, varargs });
1751 let args = build.func().push_values(&[target, outside]);
1752 let call = build.inst(
1753 InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::CallIndirect) },
1754 &[],
1755 );
1756 build.ret(&[]);
1757
1758 let outside = Outside::of(&module);
1759 let mut alias = Alias::new(&f, &outside);
1760 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1761 assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1762 }
1763
1764 #[test]
1765 fn every_reason_has_a_name_and_a_sentence() {
1766 for reason in Reason::ALL {
1767 assert!(!reason.name().is_empty());
1768 assert!(!reason.describe().is_empty());
1769 assert_eq!(Reason::ALL[reason.index()], reason);
1770 }
1771 assert_eq!(Reason::ALL.len(), Reason::COUNT);
1772 assert_eq!(Answer::No(Reason::Offset).reason(), Some(Reason::Offset));
1773 assert!(Answer::No(Reason::Offset).is_no());
1774 assert_eq!(Answer::May.reason(), None);
1775 assert!(!Answer::May.is_no());
1776 }
1777}