1use std::cell::OnceCell;
84use std::collections::HashSet;
85
86use rucc_base::Symbol;
87use rucc_ir::{
88 AttrSet, Attrs, Def, Extra, Flags, Func, Imm, Inst, MemInfo, Meta, Opcode, Restrict, Type,
89 Value,
90};
91
92use crate::modref::Summaries;
93use crate::outside::Outside;
94
95const CHASE_LIMIT: u32 = 64;
102
103const TREE_LIMIT: u32 = 32;
109
110#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
114pub enum Reason {
115 Distinct,
117 Escape,
119 Offset,
121 Tbaa,
123 Restrict,
125 Attribute,
127 Summary,
129 Plane,
131}
132
133impl Reason {
134 pub const ALL: [Self; 8] = [
136 Self::Distinct,
137 Self::Escape,
138 Self::Offset,
139 Self::Tbaa,
140 Self::Restrict,
141 Self::Attribute,
142 Self::Summary,
143 Self::Plane,
144 ];
145
146 pub const COUNT: usize = Self::ALL.len();
148
149 #[must_use]
151 pub const fn index(self) -> usize {
152 match self {
153 Self::Distinct => 0,
154 Self::Escape => 1,
155 Self::Offset => 2,
156 Self::Tbaa => 3,
157 Self::Restrict => 4,
158 Self::Attribute => 5,
159 Self::Summary => 6,
160 Self::Plane => 7,
161 }
162 }
163
164 #[must_use]
166 pub const fn name(self) -> &'static str {
167 match self {
168 Self::Distinct => "distinct",
169 Self::Escape => "escape",
170 Self::Offset => "offset",
171 Self::Tbaa => "tbaa",
172 Self::Restrict => "restrict",
173 Self::Attribute => "attribute",
174 Self::Summary => "summary",
175 Self::Plane => "plane",
176 }
177 }
178
179 #[must_use]
181 pub const fn describe(self) -> &'static str {
182 match self {
183 Self::Distinct => "they are two different objects",
184 Self::Escape => "the address of that local never leaves this function",
185 Self::Offset => "they are parts of one object that do not overlap",
186 Self::Tbaa => "no object has both of those types",
187 Self::Restrict => "restrict says those two pointers do not reach the same object",
188 Self::Attribute => "the callee is declared not to touch memory that way",
189 Self::Summary => "what that callee does to memory was worked out, and it does not",
190 Self::Plane => "that one touches only the planes, which the program cannot name",
191 }
192 }
193}
194
195#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
197pub enum Answer {
198 May,
200 No(Reason),
202}
203
204impl Answer {
205 #[must_use]
207 pub const fn is_no(self) -> bool {
208 matches!(self, Self::No(_))
209 }
210
211 #[must_use]
213 pub const fn reason(self) -> Option<Reason> {
214 match self {
215 Self::No(reason) => Some(reason),
216 Self::May => None,
217 }
218 }
219}
220
221#[derive(Clone, Copy, Debug, PartialEq, Eq)]
227pub struct Options {
228 pub strict_aliasing: bool,
231}
232
233impl Default for Options {
234 fn default() -> Self {
235 Self { strict_aliasing: true }
236 }
237}
238
239#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
247pub enum Origin {
248 Local(Inst),
251 Global(Symbol),
253 Unknown(Value),
256}
257
258impl Origin {
259 #[must_use]
261 pub const fn is_object(self) -> bool {
262 matches!(self, Self::Local(_) | Self::Global(_))
263 }
264}
265
266#[must_use]
272pub fn origin(func: &Func, mut value: Value) -> (Origin, Option<i64>) {
273 let mut offset = Some(0i64);
274 for _ in 0..CHASE_LIMIT {
275 let Def::Result { inst, .. } = func[value].def else {
276 return (Origin::Unknown(value), offset);
278 };
279 let data = func[inst];
280 match data.opcode {
281 Opcode::Alloca => return (Origin::Local(inst), offset),
282 Opcode::GlobalAddr => {
283 let Extra::Symbol(name) = data.extra else {
284 return (Origin::Unknown(value), offset);
285 };
286 return (Origin::Global(name), offset);
287 }
288 Opcode::PtrAdd => {
289 let args = &func[data.args];
290 let (base, by) = (args[0], args[1]);
291 offset = offset
292 .and_then(|so_far| Some((so_far, constant(func, by)?)))
293 .and_then(|(so_far, by)| so_far.checked_add(by));
294 value = base;
295 }
296 Opcode::Bitcast => value = func[data.args][0],
299 Opcode::CapOf => value = func[data.args][0],
306 _ => return (Origin::Unknown(value), offset),
307 }
308 }
309 (Origin::Unknown(value), None)
310}
311
312#[derive(Clone, Copy, Debug, PartialEq, Eq)]
318pub struct Access {
319 pub origin: Origin,
321 pub offset: Option<i64>,
323 pub size: Option<u64>,
325 pub tbaa: Option<Meta>,
327 pub restrict: Restrict,
329 pub volatile: bool,
331}
332
333impl Access {
334 #[must_use]
340 pub fn through(func: &Func, pointer: Value) -> Self {
341 let (origin, offset) = origin(func, pointer);
342 Self { origin, offset, size: None, tbaa: None, restrict: Restrict::NONE, volatile: false }
343 }
344
345 #[must_use]
347 pub fn range(&self) -> Option<(i128, i128)> {
348 let (offset, size) = (self.offset?, self.size?);
349 let start = i128::from(offset);
350 Some((start, start + i128::from(size)))
351 }
352}
353
354#[derive(Clone, Debug, Default)]
366pub struct Escapes {
367 escaped: HashSet<Inst>,
368}
369
370impl Escapes {
371 #[must_use]
373 pub fn of(func: &Func) -> Self {
374 Self::with(func, |_, _| false)
375 }
376
377 #[must_use]
390 pub fn knowing(func: &Func, summaries: &Summaries) -> Self {
391 Self::with(func, |inst, index| {
392 summaries.at(func, inst).is_some_and(|summary| !summary.param(index).escapes)
393 })
394 }
395
396 #[must_use]
400 pub fn with(func: &Func, kept: impl Fn(Inst, usize) -> bool) -> Self {
401 let mut escaped = HashSet::new();
402 for block in func.blocks() {
403 for inst in func.insts(block) {
404 let data = func[inst];
405 for (index, &arg) in func[data.args].iter().enumerate() {
406 if keeps_address(data.opcode, index) || kept(inst, index) {
407 continue;
408 }
409 if let (Origin::Local(local), _) = origin(func, arg) {
410 escaped.insert(local);
411 }
412 }
413 for call in func.successors(inst) {
416 for &arg in &func[call.args] {
417 if let (Origin::Local(local), _) = origin(func, arg) {
418 escaped.insert(local);
419 }
420 }
421 }
422 }
423 }
424 Self { escaped }
425 }
426
427 #[must_use]
429 pub fn escaped(&self, local: Inst) -> bool {
430 self.escaped.contains(&local)
431 }
432
433 #[must_use]
435 pub fn count(&self) -> usize {
436 self.escaped.len()
437 }
438}
439
440#[must_use]
444pub const fn keeps_address(opcode: Opcode, index: usize) -> bool {
445 match (opcode, index) {
446 (Opcode::Load | Opcode::AtomicLoad, 0)
448 | (Opcode::Store | Opcode::AtomicStore, 1)
449 | (Opcode::AtomicRmw | Opcode::Cmpxchg, 0)
450 | (Opcode::Memcpy | Opcode::Memmove, 0 | 1)
451 | (Opcode::Memset | Opcode::Prefetch, 0) => true,
452 (Opcode::PtrAdd | Opcode::Bitcast, 0) => true,
455 (Opcode::ICmp, 0 | 1) => true,
458 (op, _) if op.touches_only_planes() => true,
464 (Opcode::CapLoad | Opcode::CapStore, 0 | 1) => true,
473 (Opcode::CapOf, 0) => true,
482 _ => false,
483 }
484}
485
486#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
492pub struct Counts {
493 queries: u64,
494 answered: [u64; Reason::COUNT],
495}
496
497impl Counts {
498 #[must_use]
500 pub const fn queries(&self) -> u64 {
501 self.queries
502 }
503
504 #[must_use]
506 pub const fn answered(&self, reason: Reason) -> u64 {
507 self.answered[reason.index()]
508 }
509
510 #[must_use]
512 pub fn total(&self) -> u64 {
513 self.answered.iter().sum()
514 }
515}
516
517#[derive(Debug)]
531pub struct Alias<'a> {
532 func: &'a Func,
533 outside: &'a Outside,
534 summaries: Option<&'a Summaries>,
535 options: Options,
536 escapes: OnceCell<Escapes>,
537 counts: Counts,
538}
539
540impl<'a> Alias<'a> {
541 #[must_use]
543 pub fn new(func: &'a Func, outside: &'a Outside) -> Self {
544 Self::with(func, outside, Options::default())
545 }
546
547 #[must_use]
549 pub fn with(func: &'a Func, outside: &'a Outside, options: Options) -> Self {
550 Self {
551 func,
552 outside,
553 summaries: None,
554 options,
555 escapes: OnceCell::new(),
556 counts: Counts::default(),
557 }
558 }
559
560 #[must_use]
566 pub fn knowing(mut self, summaries: &'a Summaries) -> Self {
567 self.escapes = OnceCell::new();
568 self.summaries = Some(summaries);
569 self
570 }
571
572 #[must_use]
574 pub fn escapes(&self) -> &Escapes {
575 self.escapes.get_or_init(|| match self.summaries {
576 Some(summaries) => Escapes::knowing(self.func, summaries),
577 None => Escapes::of(self.func),
578 })
579 }
580
581 #[must_use]
583 pub const fn counts(&self) -> &Counts {
584 &self.counts
585 }
586
587 #[must_use]
589 pub fn reads(&self, inst: Inst) -> Option<Access> {
590 let data = self.func[inst];
591 let args = &self.func[data.args];
592 let info = self.mem(inst);
593 let (pointer, size) = match data.opcode {
594 Opcode::Load | Opcode::AtomicLoad => (args[0], self.width(self.result_type(inst)?)),
595 Opcode::Memcpy | Opcode::Memmove => (args[1], self.bytes(inst, info?)),
599 Opcode::AtomicRmw => (args[0], self.width(self.func[args[1]].ty)),
602 Opcode::Cmpxchg => (args[0], self.width(self.func[args[1]].ty)),
603 Opcode::VaObject => (args[0], Some(info?.size)),
604 _ => return None,
605 };
606 Some(self.access(pointer, size, info, data.flags))
607 }
608
609 fn bytes(&self, inst: Inst, info: MemInfo) -> Option<u64> {
616 match self.func.bulk(inst) {
617 Some(bulk) if bulk.length.is_some() => None,
618 _ => Some(info.size),
619 }
620 }
621
622 #[must_use]
624 pub fn writes(&self, inst: Inst) -> Option<Access> {
625 let data = self.func[inst];
626 let args = &self.func[data.args];
627 let info = self.mem(inst);
628 let (pointer, size) = match data.opcode {
629 Opcode::Store | Opcode::AtomicStore => (args[1], self.width(self.func[args[0]].ty)),
630 Opcode::Memcpy | Opcode::Memmove | Opcode::Memset => (args[0], self.bytes(inst, info?)),
631 Opcode::AtomicRmw | Opcode::Cmpxchg => (args[0], self.width(self.func[args[1]].ty)),
632 _ => return None,
633 };
634 Some(self.access(pointer, size, info, data.flags))
635 }
636
637 pub fn query(&mut self, a: &Access, b: &Access) -> Answer {
639 self.counts.queries += 1;
640 let answer = self.decide(a, b);
641 if let Answer::No(reason) = answer {
642 self.counts.answered[reason.index()] += 1;
643 }
644 answer
645 }
646
647 pub fn clobbered_by(&mut self, reference: &Access, call: Inst) -> Answer {
658 self.touched_by(reference, call, true)
659 }
660
661 pub fn read_by(&mut self, reference: &Access, call: Inst) -> Answer {
665 self.touched_by(reference, call, false)
666 }
667
668 fn decide(&self, a: &Access, b: &Access) -> Answer {
671 if a.volatile && b.volatile {
675 return Answer::May;
676 }
677
678 if a.origin.is_object() && b.origin.is_object() {
686 if self.distinct(a.origin, b.origin) {
687 return Answer::No(Reason::Distinct);
688 }
689 if a.origin == b.origin {
690 return by_offset(a, b);
691 }
692 return Answer::May;
693 }
694
695 if let Some(local) = self.private(a).or_else(|| self.private(b)) {
698 let _ = local;
699 return Answer::No(Reason::Escape);
700 }
701
702 if a.restrict.disjoint(b.restrict) {
703 return Answer::No(Reason::Restrict);
704 }
705
706 if self.options.strict_aliasing {
707 if let (Some(one), Some(other)) = (a.tbaa, b.tbaa) {
708 if !self.types_conflict(one, other) {
709 return Answer::No(Reason::Tbaa);
710 }
711 }
712 }
713
714 if a.origin == b.origin {
716 return by_offset(a, b);
717 }
718
719 Answer::May
720 }
721
722 fn private(&self, reference: &Access) -> Option<Inst> {
725 match reference.origin {
726 Origin::Local(local) if !self.escapes().escaped(local) => Some(local),
727 _ => None,
728 }
729 }
730
731 fn handed(&self, local: Inst, call: Inst) -> bool {
736 self.func[self.func[call].args]
737 .iter()
738 .any(|&arg| matches!(origin(self.func, arg).0, Origin::Local(it) if it == local))
739 }
740
741 fn distinct(&self, a: Origin, b: Origin) -> bool {
743 match (a, b) {
744 (Origin::Local(one), Origin::Local(other)) => one != other,
745 (Origin::Local(_), Origin::Global(_)) | (Origin::Global(_), Origin::Local(_)) => true,
747 (Origin::Global(one), Origin::Global(other)) => {
748 one != other && self.one_object(one) && self.one_object(other)
749 }
750 _ => false,
751 }
752 }
753
754 fn one_object(&self, name: Symbol) -> bool {
761 self.outside.one_object(name)
762 }
763
764 fn types_conflict(&self, one: Meta, other: Meta) -> bool {
770 self.at_or_below(one, other) || self.at_or_below(other, one)
771 }
772
773 fn at_or_below(&self, mut node: Meta, ancestor: Meta) -> bool {
775 for _ in 0..TREE_LIMIT {
776 if node == ancestor {
777 return true;
778 }
779 match self.outside.parent(node) {
780 Some(up) => node = up,
781 None => return false,
782 }
783 }
784 true
787 }
788
789 fn touched_by(&mut self, reference: &Access, call: Inst, writing: bool) -> Answer {
790 self.counts.queries += 1;
791 let answer = self.decide_call(reference, call, writing);
792 if let Answer::No(reason) = answer {
793 self.counts.answered[reason.index()] += 1;
794 }
795 answer
796 }
797
798 fn decide_call(&self, reference: &Access, call: Inst, writing: bool) -> Answer {
799 if self.func[call].opcode.touches_only_planes() {
805 return Answer::No(Reason::Plane);
806 }
807
808 if self.func[call].opcode.is_jump_marker() {
813 return Answer::May;
814 }
815
816 if let Some(local) = self.private(reference) {
822 if !self.handed(local, call) {
823 return Answer::No(Reason::Escape);
824 }
825 }
826
827 let Some(attrs) = self.callee(call) else {
828 return Answer::May;
829 };
830 if attrs.set.contains(AttrSet::READNONE)
832 || (writing && attrs.set.contains(AttrSet::READONLY))
833 {
834 return Answer::No(Reason::Attribute);
835 }
836
837 if attrs.set.contains(AttrSet::ARGMEM_ONLY) {
844 let args = &self.func[self.func[call].args];
845 let mut all = true;
846 for &arg in args {
847 if !self.func[arg].ty.is_ptr() {
848 continue;
849 }
850 let through = Access::through(self.func, arg);
851 all &= self.decide(reference, &through).is_no();
852 }
853 if all {
854 return Answer::No(Reason::Attribute);
855 }
856 }
857
858 if let Some(summary) = self.summaries.and_then(|known| known.at(self.func, call)) {
863 if summary.touches_nothing() || (writing && summary.writes_nothing()) {
864 return Answer::No(Reason::Summary);
865 }
866 if summary.only_through_arguments() {
871 let args = &self.func[self.func[call].args];
872 let mut all = true;
873 for (at, &arg) in args.iter().enumerate() {
874 if !self.func[arg].ty.is_ptr() {
875 continue;
876 }
877 let touch = summary.param(at);
881 let reached =
882 if writing { touch.effect.writes() } else { touch.effect.reads() };
883 if !reached {
884 continue;
885 }
886 let through = Access::through(self.func, arg);
887 all &= self.decide(reference, &through).is_no();
888 }
889 if all {
890 return Answer::No(Reason::Summary);
891 }
892 }
893 }
894
895 Answer::May
896 }
897
898 fn callee(&self, call: Inst) -> Option<Attrs> {
903 let Extra::Call(info) = self.func[call].extra else {
904 return None;
905 };
906 let name = self.func[info].callee?;
907 self.outside.attrs(name)
908 }
909
910 fn mem(&self, inst: Inst) -> Option<MemInfo> {
911 match self.func[inst].extra {
912 Extra::Mem(info) | Extra::Rmw(_, info) => Some(self.func[info]),
913 Extra::VaObject(object) => Some(self.func[self.func[object].mem]),
914 _ => None,
915 }
916 }
917
918 fn result_type(&self, inst: Inst) -> Option<Type> {
919 self.func[inst].results().next().map(|value| self.func[value].ty)
920 }
921
922 fn access(
923 &self,
924 pointer: Value,
925 size: Option<u64>,
926 info: Option<MemInfo>,
927 flags: Flags,
928 ) -> Access {
929 let (origin, offset) = origin(self.func, pointer);
930 Access {
931 origin,
932 offset,
933 size,
934 tbaa: info.and_then(|info| info.tbaa),
935 restrict: info.map_or(Restrict::NONE, |info| info.restrict),
936 volatile: flags.contains(Flags::VOLATILE),
937 }
938 }
939
940 fn width(&self, ty: Type) -> Option<u64> {
943 if ty.is_ptr() {
944 return self.outside.pointer_bytes();
945 }
946 let bits = u64::from(ty.bits()) * u64::from(ty.lanes());
947 (bits > 0).then(|| bits.div_ceil(8))
948 }
949}
950
951fn by_offset(a: &Access, b: &Access) -> Answer {
953 let (Some((a_start, a_end)), Some((b_start, b_end))) = (a.range(), b.range()) else {
954 return Answer::May;
955 };
956 if a_end <= b_start || b_end <= a_start {
957 return Answer::No(Reason::Offset);
958 }
959 Answer::May
960}
961
962fn constant(func: &Func, value: Value) -> Option<i64> {
964 let Def::Result { inst, .. } = func[value].def else {
965 return None;
966 };
967 let data = func[inst];
968 if data.opcode != Opcode::IConst {
969 return None;
970 }
971 let Extra::Imm(imm) = data.extra else {
972 return None;
973 };
974 i64::try_from(Imm::signed(func[imm], func[value].ty)).ok()
975}
976
977#[cfg(test)]
978mod tests {
979 use rucc_base::{Interner, Symbol};
980 use rucc_ir::{
981 AttrSet, Attrs, Builder, CallInfo, Extra, Flags, Func, Global, InstData, IntPred, MemInfo,
982 MemOrder, MetaNode, Module, Opcode, Pic, Restrict, Signature, TbaaNode, Type, Value,
983 };
984
985 use crate::callgraph::CallGraph;
986 use crate::modref::{Summaries, summarize};
987 use rucc_target::{TargetInfo, Triple};
988
989 use super::*;
990
991 fn module(names: &mut Interner) -> Module {
993 let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
994 Module::new(names.intern("t.c"), &target)
995 }
996
997 fn func(names: &mut Interner, params: &[Type]) -> Func {
999 let mut func = Func::new(names.intern("f"), Signature::new().with_params(params));
1000 let entry = func.create_block();
1001 for &ty in params {
1002 func.append_param(entry, ty);
1003 }
1004 func
1005 }
1006
1007 fn builder(func: &mut Func) -> Builder<'_> {
1009 let entry = func.entry().expect("the function has an entry block");
1010 Builder::new(func, entry)
1011 }
1012
1013 fn param(func: &Func, index: usize) -> Value {
1014 let entry = func.entry().expect("the function has an entry block");
1015 func[entry].params[index]
1016 }
1017
1018 fn plain(align: u32) -> MemInfo {
1019 MemInfo {
1020 size: 0,
1021 align,
1022 order: MemOrder::NotAtomic,
1023 tbaa: None,
1024 owns: 0,
1025 restrict: Restrict::NONE,
1026 }
1027 }
1028
1029 fn sized(size: u64, align: u32) -> MemInfo {
1030 MemInfo { size, ..plain(align) }
1031 }
1032
1033 fn local(build: &mut Builder<'_>, size: u64) -> Value {
1035 let mem = build.func().add_mem(sized(size, 8));
1036 build.value(InstData { extra: Extra::Mem(mem), ..InstData::new(Opcode::Alloca) }, Type::PTR)
1037 }
1038
1039 fn at(build: &mut Builder<'_>, base: Value, offset: i64) -> Value {
1041 let by = build.iconst(Type::int(64), i128::from(offset));
1042 build.binary(Opcode::PtrAdd, base, by, Flags::NONE)
1043 }
1044
1045 fn global(build: &mut Builder<'_>, module: &mut Module, name: Symbol) -> Value {
1047 module.add_global(Global::new(name, 16, 8));
1048 build.value(
1049 InstData { extra: Extra::Symbol(name), ..InstData::new(Opcode::GlobalAddr) },
1050 Type::PTR,
1051 )
1052 }
1053
1054 #[test]
1055 fn two_different_locals_are_two_objects() {
1056 let mut names = Interner::new();
1057 let module = module(&mut names);
1058 let mut f = func(&mut names, &[]);
1059 let mut build = builder(&mut f);
1060 let one = local(&mut build, 16);
1061 let other = local(&mut build, 16);
1062 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1063 build.store(read, other, plain(4), Flags::NONE);
1064 build.ret(&[]);
1065
1066 let outside = Outside::of(&module);
1067 let mut alias = Alias::new(&f, &outside);
1068 let (a, b) = two(&alias, &f);
1069 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1070 assert_eq!(alias.counts().answered(Reason::Distinct), 1);
1071 assert_eq!(alias.counts().queries(), 1);
1072 }
1073
1074 fn two(alias: &Alias<'_>, func: &Func) -> (Access, Access) {
1076 let mut read = None;
1077 let mut written = None;
1078 for block in func.blocks() {
1079 for inst in func.insts(block) {
1080 if read.is_none() {
1081 read = alias.reads(inst);
1082 }
1083 if written.is_none() {
1084 written = alias.writes(inst);
1085 }
1086 }
1087 }
1088 (read.expect("a read"), written.expect("a write"))
1089 }
1090
1091 #[test]
1092 fn a_local_and_a_global_are_two_objects() {
1093 let mut names = Interner::new();
1094 let mut module = module(&mut names);
1095 let x = names.intern("x");
1096 let mut f = func(&mut names, &[]);
1097 let mut build = builder(&mut f);
1098 let one = local(&mut build, 16);
1099 let other = global(&mut build, &mut module, x);
1100 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1101 build.store(read, other, plain(4), Flags::NONE);
1102 build.ret(&[]);
1103
1104 let outside = Outside::of(&module);
1105 let mut alias = Alias::new(&f, &outside);
1106 let (a, b) = two(&alias, &f);
1107 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1108 }
1109
1110 #[test]
1111 fn two_different_globals_are_two_objects() {
1112 let mut names = Interner::new();
1113 let mut module = module(&mut names);
1114 let (x, y) = (names.intern("x"), names.intern("y"));
1115 let mut f = func(&mut names, &[]);
1116 let mut build = builder(&mut f);
1117 let one = global(&mut build, &mut module, x);
1118 let other = global(&mut build, &mut module, y);
1119 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1120 build.store(read, other, plain(4), Flags::NONE);
1121 build.ret(&[]);
1122
1123 let outside = Outside::of(&module);
1124 let mut alias = Alias::new(&f, &outside);
1125 let (a, b) = two(&alias, &f);
1126 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1127 }
1128
1129 #[test]
1130 fn a_global_the_module_does_not_have_is_not_argued_about() {
1131 let mut names = Interner::new();
1134 let mut module = module(&mut names);
1135 let (x, y) = (names.intern("x"), names.intern("y"));
1136 let mut f = func(&mut names, &[]);
1137 let mut build = builder(&mut f);
1138 let one = global(&mut build, &mut module, x);
1139 let other = build.value(
1140 InstData { extra: Extra::Symbol(y), ..InstData::new(Opcode::GlobalAddr) },
1141 Type::PTR,
1142 );
1143 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1144 build.store(read, other, plain(4), Flags::NONE);
1145 build.ret(&[]);
1146
1147 let outside = Outside::of(&module);
1148 let mut alias = Alias::new(&f, &outside);
1149 let (a, b) = two(&alias, &f);
1150 assert_eq!(alias.query(&a, &b), Answer::May);
1151 }
1152
1153 #[test]
1154 fn two_parts_of_one_object_that_do_not_overlap_are_disjoint() {
1155 let mut names = Interner::new();
1156 let module = module(&mut names);
1157 let mut f = func(&mut names, &[]);
1158 let mut build = builder(&mut f);
1159 let object = local(&mut build, 16);
1160 let first = at(&mut build, object, 0);
1161 let second = at(&mut build, object, 4);
1162 let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
1163 build.store(read, second, plain(4), Flags::NONE);
1164 build.ret(&[]);
1165
1166 let outside = Outside::of(&module);
1167 let mut alias = Alias::new(&f, &outside);
1168 let (a, b) = two(&alias, &f);
1169 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Offset));
1170 }
1171
1172 #[test]
1173 fn two_parts_of_one_object_that_do_overlap_are_not() {
1174 let mut names = Interner::new();
1175 let module = module(&mut names);
1176 let mut f = func(&mut names, &[]);
1177 let mut build = builder(&mut f);
1178 let object = local(&mut build, 16);
1179 let first = at(&mut build, object, 0);
1180 let second = at(&mut build, object, 2);
1181 let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
1182 build.store(read, second, plain(4), Flags::NONE);
1183 build.ret(&[]);
1184
1185 let outside = Outside::of(&module);
1186 let mut alias = Alias::new(&f, &outside);
1187 let (a, b) = two(&alias, &f);
1188 assert_eq!(alias.query(&a, &b), Answer::May);
1189 }
1190
1191 #[test]
1192 fn an_offset_nobody_knows_gives_up_the_offset_and_keeps_the_object() {
1193 let mut names = Interner::new();
1194 let module = module(&mut names);
1195 let mut f = func(&mut names, &[Type::int(64)]);
1196 let n = param(&f, 0);
1197 let mut build = builder(&mut f);
1198 let object = local(&mut build, 16);
1199 let somewhere = build.binary(Opcode::PtrAdd, object, n, Flags::NONE);
1200 let read = build.load(Type::int(32), somewhere, plain(4), Flags::NONE);
1201 build.store(read, object, plain(4), Flags::NONE);
1202 build.ret(&[]);
1203
1204 let outside = Outside::of(&module);
1205 let mut alias = Alias::new(&f, &outside);
1206 let (a, b) = two(&alias, &f);
1207 assert_eq!(a.origin, b.origin, "both are still that one object");
1208 assert_eq!(a.offset, None);
1209 assert_eq!(alias.query(&a, &b), Answer::May);
1210 }
1211
1212 #[test]
1213 fn a_local_whose_address_stays_here_is_not_what_a_parameter_points_at() {
1214 let mut names = Interner::new();
1215 let module = module(&mut names);
1216 let mut f = func(&mut names, &[Type::PTR]);
1217 let outside = param(&f, 0);
1218 let mut build = builder(&mut f);
1219 let object = local(&mut build, 16);
1220 let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1221 build.store(read, outside, plain(4), Flags::NONE);
1222 build.ret(&[]);
1223
1224 let outside = Outside::of(&module);
1225 let mut alias = Alias::new(&f, &outside);
1226 assert_eq!(alias.escapes().count(), 0);
1227 let (a, b) = two(&alias, &f);
1228 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Escape));
1229 }
1230
1231 #[test]
1232 fn a_local_whose_address_was_stored_somewhere_is() {
1233 let mut names = Interner::new();
1234 let module = module(&mut names);
1235 let mut f = func(&mut names, &[Type::PTR]);
1236 let outside = param(&f, 0);
1237 let mut build = builder(&mut f);
1238 let object = local(&mut build, 16);
1239 build.store(object, outside, plain(8), Flags::NONE);
1242 let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1243 build.store(read, outside, plain(4), Flags::NONE);
1244 build.ret(&[]);
1245
1246 let outside = Outside::of(&module);
1247 let mut alias = Alias::new(&f, &outside);
1248 assert_eq!(alias.escapes().count(), 1);
1249 let read = first(&f, Opcode::Load);
1250 let write = last(&f, Opcode::Store);
1251 let a = alias.reads(read).unwrap();
1252 let b = alias.writes(write).unwrap();
1253 assert_eq!(alias.query(&a, &b), Answer::May);
1254 }
1255
1256 fn first(func: &Func, opcode: Opcode) -> Inst {
1257 func.blocks()
1258 .flat_map(|block| func.insts(block))
1259 .find(|&inst| func[inst].opcode == opcode)
1260 .expect("an instruction with that opcode")
1261 }
1262
1263 fn last(func: &Func, opcode: Opcode) -> Inst {
1264 func.blocks()
1265 .flat_map(|block| func.insts(block))
1266 .filter(|&inst| func[inst].opcode == opcode)
1267 .last()
1268 .expect("an instruction with that opcode")
1269 }
1270
1271 #[test]
1272 fn an_address_carried_through_a_block_parameter_has_left_the_function() {
1273 let mut names = Interner::new();
1274 let module = module(&mut names);
1275 let mut f = func(&mut names, &[]);
1276 let start = f.entry().expect("an entry block");
1277 let next = f.create_block();
1278 f.append_param(next, Type::PTR);
1279
1280 let mut build = Builder::new(&mut f, start);
1281 let object = local(&mut build, 16);
1282 build.jump(next, &[object]);
1283 let mut build = Builder::new(&mut f, next);
1284 build.ret(&[]);
1285
1286 let outside = Outside::of(&module);
1287 let alias = Alias::new(&f, &outside);
1288 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1289 }
1290
1291 #[test]
1292 fn comparing_two_addresses_does_not_let_either_of_them_out() {
1293 let mut names = Interner::new();
1294 let module = module(&mut names);
1295 let mut f = func(&mut names, &[Type::PTR]);
1296 let outside = param(&f, 0);
1297 let mut build = builder(&mut f);
1298 let object = local(&mut build, 16);
1299 build.icmp(IntPred::Eq, object, outside);
1300 build.ret(&[]);
1301
1302 let outside = Outside::of(&module);
1303 let alias = Alias::new(&f, &outside);
1304 assert_eq!(alias.escapes().count(), 0);
1305 }
1306
1307 #[test]
1308 fn a_plane_write_on_a_local_does_not_let_its_address_out() {
1309 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 let width = build.iconst(Type::int(64), 16);
1318 let args = build.func().push_values(&[object, width]);
1319 build.inst(InstData { args, ..InstData::new(Opcode::MetaInit) }, &[]);
1320 build.ret(&[]);
1321
1322 let outside = Outside::of(&module);
1323 let alias = Alias::new(&f, &outside);
1324 assert_eq!(alias.escapes().count(), 0);
1325 }
1326
1327 #[test]
1328 fn a_local_that_is_only_asked_about_and_checked_does_not_leave_the_function() {
1329 let mut names = Interner::new();
1334 let module = module(&mut names);
1335 let mut f = func(&mut names, &[]);
1336 let mut build = builder(&mut f);
1337 let object = local(&mut build, 16);
1338 let args = build.func().push_values(&[object]);
1339 let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1340 let args = build.func().push_values(&[capability, object]);
1341 build.inst(InstData { args, ..InstData::new(Opcode::CheckBounds) }, &[]);
1342 build.ret(&[]);
1343
1344 let outside = Outside::of(&module);
1345 let alias = Alias::new(&f, &outside);
1346 assert_eq!(alias.escapes().count(), 0);
1347 }
1348
1349 #[test]
1350 fn a_capability_of_a_local_used_for_anything_else_does_let_it_out() {
1351 let mut names = Interner::new();
1356 let module = module(&mut names);
1357 let mut f = func(&mut names, &[]);
1358 let mut build = builder(&mut f);
1359 let object = local(&mut build, 16);
1360 let args = build.func().push_values(&[object]);
1361 let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1362 let base = build.iconst(Type::int(64), 0);
1363 let size = build.iconst(Type::int(64), 4);
1364 let args = build.func().push_values(&[capability, base, size]);
1365 build.value(InstData { args, ..InstData::new(Opcode::CapNarrow) }, Type::CAP);
1366 build.ret(&[]);
1367
1368 let outside = Outside::of(&module);
1369 let alias = Alias::new(&f, &outside);
1370 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1371 }
1372
1373 #[test]
1374 fn the_whitelist_says_yes_to_a_plane_access_at_every_operand() {
1375 for opcode in Opcode::all().filter(|opcode| opcode.touches_only_planes()) {
1379 for index in 0..4 {
1380 assert!(keeps_address(opcode, index), "{opcode} at {index}");
1381 }
1382 }
1383 for opcode in [Opcode::CapNarrow, Opcode::CapRecover] {
1384 assert!(!keeps_address(opcode, 0), "{opcode}");
1385 }
1386 for opcode in [Opcode::CapLoad, Opcode::CapStore, Opcode::CapCopy] {
1389 for index in 0..2 {
1390 assert!(keeps_address(opcode, index), "{opcode} at {index}");
1391 }
1392 }
1393 assert!(!keeps_address(Opcode::CapStore, 2));
1394 assert!(!keeps_address(Opcode::CapStore, 3));
1395 assert!(keeps_address(Opcode::CapOf, 0));
1396 }
1397
1398 #[test]
1399 fn a_local_a_pointer_is_written_into_does_not_leave_the_function_for_the_writing_down() {
1400 let mut names = Interner::new();
1405 let module = module(&mut names);
1406 let mut f = func(&mut names, &[Type::PTR]);
1407 let written = param(&f, 0);
1408 let mut build = builder(&mut f);
1409 let object = local(&mut build, 8);
1410 let args = build.func().push_values(&[object]);
1411 let container = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1412 let args = build.func().push_values(&[written]);
1413 let held = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1414 build.store(written, object, plain(8), Flags::NONE);
1415 let args = build.func().push_values(&[container, object, written, held]);
1416 build.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
1417 build.ret(&[]);
1418
1419 let outside = Outside::of(&module);
1420 let alias = Alias::new(&f, &outside);
1421 assert_eq!(alias.escapes().count(), 0);
1422 }
1423
1424 #[test]
1425 fn a_local_whose_capability_is_written_into_a_slot_does_leave_the_function() {
1426 let mut names = Interner::new();
1431 let module = module(&mut names);
1432 let mut f = func(&mut names, &[Type::PTR]);
1433 let into = param(&f, 0);
1434 let mut build = builder(&mut f);
1435 let object = local(&mut build, 8);
1436 let args = build.func().push_values(&[into]);
1437 let container = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1438 let args = build.func().push_values(&[object]);
1439 let held = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1440 let args = build.func().push_values(&[container, into, object, held]);
1441 build.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
1442 build.ret(&[]);
1443
1444 let outside = Outside::of(&module);
1445 let alias = Alias::new(&f, &outside);
1446 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1447 }
1448
1449 #[test]
1450 fn an_address_turned_into_a_number_has_left_the_function() {
1451 let mut names = Interner::new();
1454 let module = module(&mut names);
1455 let mut f = func(&mut names, &[]);
1456 let mut build = builder(&mut f);
1457 let object = local(&mut build, 16);
1458 build.unary(Opcode::PtrToInt, object, Type::int(64));
1459 build.ret(&[]);
1460
1461 let outside = Outside::of(&module);
1462 let alias = Alias::new(&f, &outside);
1463 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1464 }
1465
1466 #[test]
1467 fn two_restrict_pointers_in_one_scope_do_not_reach_the_same_object() {
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 (one, other) = (param(&f, 0), param(&f, 1));
1472 let mut build = builder(&mut f);
1473 let mut info = plain(4);
1474 info.restrict = Restrict { clique: 1, base: 1 };
1475 let read = build.load(Type::int(32), one, info, Flags::NONE);
1476 info.restrict = Restrict { clique: 1, base: 2 };
1477 build.store(read, other, 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::No(Reason::Restrict));
1484 }
1485
1486 #[test]
1487 fn two_restrict_pointers_in_different_scopes_say_nothing_about_each_other() {
1488 let mut names = Interner::new();
1489 let module = module(&mut names);
1490 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1491 let (one, other) = (param(&f, 0), param(&f, 1));
1492 let mut build = builder(&mut f);
1493 let mut info = plain(4);
1494 info.restrict = Restrict { clique: 1, base: 1 };
1495 let read = build.load(Type::int(32), one, info, Flags::NONE);
1496 info.restrict = Restrict { clique: 2, base: 1 };
1497 build.store(read, other, info, Flags::NONE);
1498 build.ret(&[]);
1499
1500 let outside = Outside::of(&module);
1501 let mut alias = Alias::new(&f, &outside);
1502 let (a, b) = two(&alias, &f);
1503 assert_eq!(alias.query(&a, &b), Answer::May);
1504 }
1505
1506 fn types(module: &mut Module, names: &mut Interner) -> (Meta, Meta, Meta) {
1508 let root = module.add_meta(MetaNode::Tbaa(TbaaNode {
1509 name: names.intern("char"),
1510 parent: None,
1511 offset: 0,
1512 }));
1513 let int = module.add_meta(MetaNode::Tbaa(TbaaNode {
1514 name: names.intern("int"),
1515 parent: Some(root),
1516 offset: 0,
1517 }));
1518 let float = module.add_meta(MetaNode::Tbaa(TbaaNode {
1519 name: names.intern("float"),
1520 parent: Some(root),
1521 offset: 0,
1522 }));
1523 (root, int, float)
1524 }
1525
1526 #[test]
1527 fn two_unrelated_types_describe_no_object_in_common() {
1528 let mut names = Interner::new();
1529 let mut module = module(&mut names);
1530 let (_, int, float) = types(&mut module, &mut names);
1531 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1532 let (one, other) = (param(&f, 0), param(&f, 1));
1533 let mut build = builder(&mut f);
1534 let mut info = plain(4);
1535 info.tbaa = Some(int);
1536 let read = build.load(Type::int(32), one, info, Flags::NONE);
1537 info.tbaa = Some(float);
1538 build.store(read, other, info, Flags::NONE);
1539 build.ret(&[]);
1540
1541 let outside = Outside::of(&module);
1542 let mut alias = Alias::new(&f, &outside);
1543 let (a, b) = two(&alias, &f);
1544 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Tbaa));
1545 }
1546
1547 #[test]
1548 fn an_access_through_char_conflicts_with_everything() {
1549 let mut names = Interner::new();
1550 let mut module = module(&mut names);
1551 let (root, int, _) = types(&mut module, &mut names);
1552 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1553 let (one, other) = (param(&f, 0), param(&f, 1));
1554 let mut build = builder(&mut f);
1555 let mut info = plain(4);
1556 info.tbaa = Some(int);
1557 let read = build.load(Type::int(32), one, info, Flags::NONE);
1558 info.tbaa = Some(root);
1559 build.store(read, other, info, Flags::NONE);
1560 build.ret(&[]);
1561
1562 let outside = Outside::of(&module);
1563 let mut alias = Alias::new(&f, &outside);
1564 let (a, b) = two(&alias, &f);
1565 assert_eq!(alias.query(&a, &b), Answer::May);
1566 }
1567
1568 #[test]
1569 fn turning_strict_aliasing_off_turns_off_that_layer_and_no_other() {
1570 let mut names = Interner::new();
1571 let mut module = module(&mut names);
1572 let (_, int, float) = types(&mut module, &mut names);
1573 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1574 let (one, other) = (param(&f, 0), param(&f, 1));
1575 let mut build = builder(&mut f);
1576 let mut info = plain(4);
1577 info.tbaa = Some(int);
1578 info.restrict = Restrict { clique: 1, base: 1 };
1579 let read = build.load(Type::int(32), one, info, Flags::NONE);
1580 info.tbaa = Some(float);
1581 info.restrict = Restrict { clique: 1, base: 2 };
1582 build.store(read, other, info, Flags::NONE);
1583 build.ret(&[]);
1584
1585 let options = Options { strict_aliasing: false };
1586 let outside = Outside::of(&module);
1587 let mut alias = Alias::with(&f, &outside, options);
1588 let (a, b) = two(&alias, &f);
1589 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Restrict));
1592
1593 let mut without = Alias::with(&f, &outside, options);
1594 let plainer = Access { restrict: Restrict::NONE, ..a };
1595 let other = Access { restrict: Restrict::NONE, ..b };
1596 assert_eq!(without.query(&plainer, &other), Answer::May);
1597
1598 let mut with = Alias::new(&f, &outside);
1599 assert_eq!(with.query(&plainer, &other), Answer::No(Reason::Tbaa));
1600 }
1601
1602 #[test]
1603 fn writing_one_member_of_a_union_and_reading_another_is_one_object() {
1604 let mut names = Interner::new();
1609 let mut module = module(&mut names);
1610 let (_, int, float) = types(&mut module, &mut names);
1611 let mut f = func(&mut names, &[]);
1612 let mut build = builder(&mut f);
1613 let object = local(&mut build, 4);
1614 let mut info = plain(4);
1615 info.tbaa = Some(float);
1616 let read = build.load(Type::int(32), object, info, Flags::NONE);
1617 info.tbaa = Some(int);
1618 build.store(read, object, info, Flags::NONE);
1619 build.ret(&[]);
1620
1621 let outside = Outside::of(&module);
1622 let mut alias = Alias::new(&f, &outside);
1623 let (a, b) = two(&alias, &f);
1624 assert_eq!(alias.query(&a, &b), Answer::May);
1625 }
1626
1627 #[test]
1628 fn two_volatile_accesses_conflict_whatever_else_is_true_of_them() {
1629 let mut names = Interner::new();
1630 let module = module(&mut names);
1631 let mut f = func(&mut names, &[]);
1632 let mut build = builder(&mut f);
1633 let one = local(&mut build, 16);
1634 let other = local(&mut build, 16);
1635 let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1636 build.store(read, other, plain(4), Flags::VOLATILE);
1637 build.ret(&[]);
1638
1639 let outside = Outside::of(&module);
1640 let mut alias = Alias::new(&f, &outside);
1641 let (a, b) = two(&alias, &f);
1642 assert_eq!(alias.query(&a, &b), Answer::May);
1645 }
1646
1647 #[test]
1648 fn one_volatile_access_and_one_ordinary_one_are_argued_about_as_usual() {
1649 let mut names = Interner::new();
1650 let module = module(&mut names);
1651 let mut f = func(&mut names, &[]);
1652 let mut build = builder(&mut f);
1653 let one = local(&mut build, 16);
1654 let other = local(&mut build, 16);
1655 let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1656 build.store(read, other, plain(4), Flags::NONE);
1657 build.ret(&[]);
1658
1659 let outside = Outside::of(&module);
1660 let mut alias = Alias::new(&f, &outside);
1661 let (a, b) = two(&alias, &f);
1662 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1663 }
1664
1665 #[test]
1666 fn a_copy_reads_its_source_and_writes_its_destination() {
1667 let mut names = Interner::new();
1668 let module = module(&mut names);
1669 let mut f = func(&mut names, &[]);
1670 let mut build = builder(&mut f);
1671 let to = local(&mut build, 16);
1672 let from = local(&mut build, 16);
1673 let mem = build.func().add_mem(sized(16, 8));
1674 let args = build.func().push_values(&[to, from]);
1675 build.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1676 build.ret(&[]);
1677
1678 let outside = Outside::of(&module);
1679 let alias = Alias::new(&f, &outside);
1680 let copy = first(&f, Opcode::Memcpy);
1681 let read = alias.reads(copy).expect("a copy reads");
1682 let written = alias.writes(copy).expect("a copy writes");
1683 assert_eq!(read.size, Some(16));
1684 assert_eq!(written.size, Some(16));
1685 assert_ne!(read.origin, written.origin);
1686 }
1687
1688 #[test]
1689 fn a_copy_of_a_length_the_program_works_out_is_an_access_of_no_known_size() {
1690 let mut names = Interner::new();
1691 let module = module(&mut names);
1692 let mut f = func(&mut names, &[Type::int(64)]);
1693 let length = param(&f, 0);
1694 let mut build = builder(&mut f);
1695 let to = local(&mut build, 16);
1696 let from = local(&mut build, 16);
1697 let mem = build.func().add_mem(sized(0, 8));
1698 let args = build.func().push_values(&[to, from, length]);
1699 build.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1700 build.ret(&[]);
1701
1702 let outside = Outside::of(&module);
1703 let alias = Alias::new(&f, &outside);
1704 let copy = first(&f, Opcode::Memcpy);
1705 assert_eq!(alias.reads(copy).expect("a copy reads").size, None);
1708 assert_eq!(alias.writes(copy).expect("a copy writes").size, None);
1709 }
1710
1711 fn call_to(
1713 names: &mut Interner,
1714 module: &mut Module,
1715 f: &mut Func,
1716 attrs: Attrs,
1717 args: &[Value],
1718 ) -> Inst {
1719 let name = names.intern("g");
1720 let params: Vec<Type> = args.iter().map(|_| Type::PTR).collect();
1721 let mut callee = Func::new(name, Signature::new().with_params(¶ms));
1722 callee.attrs = attrs;
1723 module.add_func(callee);
1724 let signature = f.add_signature(Signature::new().with_params(¶ms));
1725 let mut build = builder(f);
1726 build.call(name, signature, args)
1727 }
1728
1729 fn attrs(set: AttrSet) -> Attrs {
1730 Attrs { set, ..Attrs::NONE }
1731 }
1732
1733 #[test]
1734 fn a_call_cannot_touch_a_local_whose_address_stayed_here() {
1735 let mut names = Interner::new();
1736 let mut module = module(&mut names);
1737 let mut f = func(&mut names, &[Type::PTR]);
1738 let outside = param(&f, 0);
1739 let mut build = builder(&mut f);
1740 let object = local(&mut build, 16);
1741 let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1742 let _ = read;
1743 let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[outside]);
1744 let mut build = builder(&mut f);
1745 build.ret(&[]);
1746
1747 let outside = Outside::of(&module);
1748 let mut alias = Alias::new(&f, &outside);
1749 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1750 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Escape));
1751 assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Escape));
1752 }
1753
1754 #[test]
1755 fn a_call_can_touch_a_local_it_was_handed() {
1756 let mut names = Interner::new();
1757 let mut module = module(&mut names);
1758 let mut f = func(&mut names, &[]);
1759 let mut build = builder(&mut f);
1760 let object = local(&mut build, 16);
1761 build.load(Type::int(32), object, plain(4), Flags::NONE);
1762 let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[object]);
1763 let mut build = builder(&mut f);
1764 build.ret(&[]);
1765
1766 let outside = Outside::of(&module);
1767 let mut alias = Alias::new(&f, &outside);
1768 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1769 assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1770 }
1771
1772 #[test]
1773 fn a_setjmp_marker_can_touch_a_local_whose_address_stayed_here() {
1774 let mut names = Interner::new();
1779 let mut module = module(&mut names);
1780 let name = names.intern("jmp_buf");
1781 let mut f = func(&mut names, &[]);
1782 let mut build = builder(&mut f);
1783 let object = local(&mut build, 16);
1784 build.load(Type::int(32), object, plain(4), Flags::NONE);
1785 let buffer = global(&mut build, &mut module, name);
1786 let args = build.func().push_values(&[buffer]);
1787 let marker =
1788 build.inst(InstData { args, ..InstData::new(Opcode::SetjmpMarker) }, &[Type::int(32)]);
1789 build.ret(&[]);
1790
1791 let outside = Outside::of(&module);
1792 let mut alias = Alias::new(&f, &outside);
1793 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1794 assert_eq!(alias.clobbered_by(&reference, marker), Answer::May);
1795 assert_eq!(alias.read_by(&reference, marker), Answer::May);
1796 }
1797
1798 #[test]
1799 fn a_pure_callee_reads_memory_and_writes_none() {
1800 let mut names = Interner::new();
1801 let mut module = module(&mut names);
1802 let mut f = func(&mut names, &[Type::PTR]);
1803 let outside = param(&f, 0);
1804 let mut build = builder(&mut f);
1805 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1806 let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READONLY), &[outside]);
1807 let mut build = builder(&mut f);
1808 build.ret(&[]);
1809
1810 let outside = Outside::of(&module);
1811 let mut alias = Alias::new(&f, &outside);
1812 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1813 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1814 assert_eq!(alias.read_by(&reference, call), Answer::May);
1815 }
1816
1817 #[test]
1818 fn a_plane_write_is_not_a_write_to_the_address_it_names() {
1819 let mut names = Interner::new();
1825 let module = module(&mut names);
1826 let mut f = func(&mut names, &[Type::PTR]);
1827 let outside = param(&f, 0);
1828 let mut build = builder(&mut f);
1829 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1830 let width = build.iconst(Type::int(64), 4);
1831 let args = build.func().push_values(&[outside, width]);
1832 build.inst(InstData { args, ..InstData::new(Opcode::MetaInit) }, &[]);
1833 build.ret(&[]);
1834
1835 let outside = Outside::of(&module);
1836 let mut alias = Alias::new(&f, &outside);
1837 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1838 let plane = first(&f, Opcode::MetaInit);
1839 assert_eq!(alias.clobbered_by(&reference, plane), Answer::No(Reason::Plane));
1840 assert_eq!(alias.read_by(&reference, plane), Answer::No(Reason::Plane));
1842 }
1843
1844 #[test]
1845 fn a_check_reads_a_plane_and_not_what_it_is_about() {
1846 let mut names = Interner::new();
1850 let module = module(&mut names);
1851 let mut f = func(&mut names, &[Type::PTR]);
1852 let outside = param(&f, 0);
1853 let mut build = builder(&mut f);
1854 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1855 let width = build.iconst(Type::int(64), 4);
1856 let args = build.func().push_values(&[outside, width]);
1857 build.inst(InstData { args, ..InstData::new(Opcode::CheckBounds) }, &[]);
1858 build.ret(&[]);
1859
1860 let outside = Outside::of(&module);
1861 let mut alias = Alias::new(&f, &outside);
1862 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1863 let check = first(&f, Opcode::CheckBounds);
1864 assert_eq!(alias.clobbered_by(&reference, check), Answer::No(Reason::Plane));
1865 assert_eq!(alias.read_by(&reference, check), Answer::No(Reason::Plane));
1866 }
1867
1868 #[test]
1869 fn a_const_callee_touches_no_memory_at_all() {
1870 let mut names = Interner::new();
1871 let mut module = module(&mut names);
1872 let mut f = func(&mut names, &[Type::PTR]);
1873 let outside = param(&f, 0);
1874 let mut build = builder(&mut f);
1875 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1876 let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READNONE), &[outside]);
1877 let mut build = builder(&mut f);
1878 build.ret(&[]);
1879
1880 let outside = Outside::of(&module);
1881 let mut alias = Alias::new(&f, &outside);
1882 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1883 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1884 assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Attribute));
1885 }
1886
1887 #[test]
1888 fn a_callee_that_touches_only_its_arguments_leaves_a_global_it_was_not_passed_alone() {
1889 let mut names = Interner::new();
1890 let mut module = module(&mut names);
1891 let x = names.intern("x");
1892 let mut f = func(&mut names, &[Type::PTR]);
1893 let outside = param(&f, 0);
1894 let mut build = builder(&mut f);
1895 let object = global(&mut build, &mut module, x);
1896 build.load(Type::int(32), object, plain(4), Flags::NONE);
1897 let call =
1898 call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[outside]);
1899 let mut build = builder(&mut f);
1900 build.ret(&[]);
1901
1902 let outside = Outside::of(&module);
1903 let mut alias = Alias::new(&f, &outside);
1904 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1905 assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1908 }
1909
1910 #[test]
1911 fn a_callee_that_touches_only_its_arguments_and_was_handed_one_object_leaves_the_other() {
1912 let mut names = Interner::new();
1913 let mut module = module(&mut names);
1914 let (x, y) = (names.intern("x"), names.intern("y"));
1915 let mut f = func(&mut names, &[]);
1916 let mut build = builder(&mut f);
1917 let watched = global(&mut build, &mut module, x);
1918 let handed = global(&mut build, &mut module, y);
1919 build.load(Type::int(32), watched, plain(4), Flags::NONE);
1920 let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[handed]);
1921 let mut build = builder(&mut f);
1922 build.ret(&[]);
1923
1924 let outside = Outside::of(&module);
1925 let mut alias = Alias::new(&f, &outside);
1926 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1927 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1928 }
1929
1930 fn call_to_body(
1935 names: &mut Interner,
1936 module: &mut Module,
1937 f: &mut Func,
1938 arity: usize,
1939 body: fn(&mut Builder<'_>, &[Value]),
1940 args: &[Value],
1941 ) -> Inst {
1942 defines(names, module, "g", arity, body);
1943 calls_it(names, f, "g", arity, args)
1944 }
1945
1946 fn defines(
1949 names: &mut Interner,
1950 module: &mut Module,
1951 called: &str,
1952 arity: usize,
1953 body: fn(&mut Builder<'_>, &[Value]),
1954 ) {
1955 let name = names.intern(called);
1956 let params = vec![Type::PTR; arity];
1957 let mut callee = Func::new(name, Signature::new().with_params(¶ms));
1958 let entry = callee.create_block();
1959 let got: Vec<Value> = params.iter().map(|&ty| callee.append_param(entry, ty)).collect();
1960 let mut build = Builder::new(&mut callee, entry);
1961 body(&mut build, &got);
1962 module.add_func(callee);
1963 }
1964
1965 fn calls_it(
1967 names: &mut Interner,
1968 f: &mut Func,
1969 called: &str,
1970 arity: usize,
1971 args: &[Value],
1972 ) -> Inst {
1973 let name = names.intern(called);
1974 let signature = f.add_signature(Signature::new().with_params(&vec![Type::PTR; arity]));
1975 let mut build = builder(f);
1976 build.call(name, signature, args)
1977 }
1978
1979 fn worked_out(module: &Module) -> Summaries {
1981 let mut summaries = Summaries::of_module(module);
1982 summarize(module, &CallGraph::of(module, Pic::Executable), &mut summaries);
1983 summaries
1984 }
1985
1986 fn body_does_nothing(build: &mut Builder<'_>, _: &[Value]) {
1988 build.ret(&[]);
1989 }
1990
1991 fn body_reads_the_first(build: &mut Builder<'_>, args: &[Value]) {
1993 let value = build.load(Type::int(32), args[0], plain(4), Flags::NONE);
1994 build.ret(&[value]);
1995 }
1996
1997 fn body_writes_the_first(build: &mut Builder<'_>, args: &[Value]) {
1999 let zero = build.iconst(Type::int(32), 0);
2000 build.store(zero, args[0], plain(4), Flags::NONE);
2001 build.ret(&[]);
2002 }
2003
2004 fn body_keeps_the_first(build: &mut Builder<'_>, args: &[Value]) {
2006 build.store(args[0], args[1], plain(8), Flags::NONE);
2007 build.ret(&[]);
2008 }
2009
2010 #[test]
2011 fn a_callee_nobody_declared_anything_about_is_read_out_of_its_body() {
2012 let mut names = Interner::new();
2013 let mut module = module(&mut names);
2014 let x = names.intern("x");
2015 let mut f = func(&mut names, &[]);
2016 let mut build = builder(&mut f);
2017 let object = global(&mut build, &mut module, x);
2018 build.load(Type::int(32), object, plain(4), Flags::NONE);
2019 let call = call_to_body(&mut names, &mut module, &mut f, 0, body_does_nothing, &[]);
2020 let mut build = builder(&mut f);
2021 build.ret(&[]);
2022
2023 let outside = Outside::of(&module);
2024 let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2025 let mut blind = Alias::new(&f, &outside);
2027 assert_eq!(blind.clobbered_by(&reference, call), Answer::May);
2028
2029 let summaries = worked_out(&module);
2030 let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2031 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Summary));
2032 assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Summary));
2033 }
2034
2035 #[test]
2036 fn a_callee_worked_out_to_write_nothing_clobbers_nothing() {
2037 let mut names = Interner::new();
2038 let mut module = module(&mut names);
2039 let mut f = func(&mut names, &[Type::PTR]);
2040 let handed = param(&f, 0);
2041 let mut build = builder(&mut f);
2042 build.load(Type::int(32), handed, plain(4), Flags::NONE);
2043 let call =
2044 call_to_body(&mut names, &mut module, &mut f, 1, body_reads_the_first, &[handed]);
2045 let mut build = builder(&mut f);
2046 build.ret(&[]);
2047
2048 let outside = Outside::of(&module);
2049 let summaries = worked_out(&module);
2050 let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2051 let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2052 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Summary));
2053 assert_eq!(alias.read_by(&reference, call), Answer::May);
2055 }
2056
2057 #[test]
2058 fn a_callee_that_writes_one_of_the_two_it_was_handed_leaves_the_other() {
2059 let mut names = Interner::new();
2062 let mut module = module(&mut names);
2063 let (x, y) = (names.intern("x"), names.intern("y"));
2064 let mut f = func(&mut names, &[]);
2065 let mut build = builder(&mut f);
2066 let watched = global(&mut build, &mut module, x);
2067 let written = global(&mut build, &mut module, y);
2068 build.load(Type::int(32), watched, plain(4), Flags::NONE);
2069 let call = call_to_body(
2070 &mut names,
2071 &mut module,
2072 &mut f,
2073 2,
2074 body_writes_the_first,
2075 &[written, watched],
2076 );
2077 let mut build = builder(&mut f);
2078 build.ret(&[]);
2079
2080 let outside = Outside::of(&module);
2081 let summaries = worked_out(&module);
2082 let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2083 let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2084 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Summary));
2085 }
2086
2087 #[test]
2088 fn a_local_lent_to_a_callee_that_keeps_it_not_is_still_private_everywhere_else() {
2089 let mut names = Interner::new();
2093 let mut module = module(&mut names);
2094 let x = names.intern("x");
2095 let mut f = func(&mut names, &[]);
2096 let mut build = builder(&mut f);
2097 let object = local(&mut build, 16);
2098 let elsewhere = global(&mut build, &mut module, x);
2099 build.load(Type::int(32), object, plain(4), Flags::NONE);
2100 defines(&mut names, &mut module, "g", 1, body_writes_the_first);
2101 let lent = calls_it(&mut names, &mut f, "g", 1, &[object]);
2102 let other = calls_it(&mut names, &mut f, "g", 1, &[elsewhere]);
2103 let mut build = builder(&mut f);
2104 build.ret(&[]);
2105
2106 let outside = Outside::of(&module);
2107 let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2108 let mut blind = Alias::new(&f, &outside);
2110 assert_eq!(blind.clobbered_by(&reference, lent), Answer::May);
2111 assert_eq!(blind.clobbered_by(&reference, other), Answer::May);
2112
2113 let summaries = worked_out(&module);
2114 let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2115 assert_eq!(alias.clobbered_by(&reference, lent), Answer::May);
2118 assert_eq!(alias.clobbered_by(&reference, other), Answer::No(Reason::Escape));
2120 assert_eq!(alias.escapes().count(), 0);
2121 }
2122
2123 #[test]
2124 fn a_local_written_down_by_a_callee_is_gone_exactly_as_before() {
2125 let mut names = Interner::new();
2126 let mut module = module(&mut names);
2127 let x = names.intern("x");
2128 let mut f = func(&mut names, &[]);
2129 let mut build = builder(&mut f);
2130 let object = local(&mut build, 16);
2131 let elsewhere = global(&mut build, &mut module, x);
2132 build.load(Type::int(32), object, plain(4), Flags::NONE);
2133 defines(&mut names, &mut module, "g", 2, body_keeps_the_first);
2134 defines(&mut names, &mut module, "h", 1, body_does_nothing);
2135 calls_it(&mut names, &mut f, "g", 2, &[object, elsewhere]);
2136 let other = calls_it(&mut names, &mut f, "h", 1, &[elsewhere]);
2137 let mut build = builder(&mut f);
2138 build.ret(&[]);
2139
2140 let outside = Outside::of(&module);
2141 let summaries = worked_out(&module);
2142 let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2143 let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2144 assert_eq!(alias.escapes().count(), 1);
2147 assert_eq!(alias.private(&reference), None);
2148 assert_eq!(alias.clobbered_by(&reference, other), Answer::No(Reason::Summary));
2150 }
2151
2152 #[test]
2153 fn a_local_handed_to_a_const_declaration_is_still_gone() {
2154 let mut names = Interner::new();
2159 let mut module = module(&mut names);
2160 let mut f = func(&mut names, &[]);
2161 let mut build = builder(&mut f);
2162 let object = local(&mut build, 16);
2163 build.load(Type::int(32), object, plain(4), Flags::NONE);
2164 call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READNONE), &[object]);
2165 let mut build = builder(&mut f);
2166 build.ret(&[]);
2167
2168 let outside = Outside::of(&module);
2169 let summaries = worked_out(&module);
2170 let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2171 let alias = Alias::new(&f, &outside).knowing(&summaries);
2172 assert_eq!(alias.escapes().count(), 1);
2173 assert_eq!(alias.private(&reference), None);
2174 }
2175
2176 #[test]
2177 fn an_indirect_call_is_not_argued_about() {
2178 let mut names = Interner::new();
2179 let module = module(&mut names);
2180 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
2181 let (target, outside) = (param(&f, 0), param(&f, 1));
2182 let mut build = builder(&mut f);
2183 build.load(Type::int(32), outside, plain(4), Flags::NONE);
2184 let signature = build.func().add_signature(Signature::new().with_params(&[Type::PTR]));
2185 let varargs = build.func().push_abis(&[]);
2186 let info = build.func().add_call(CallInfo { callee: None, signature, varargs });
2187 let args = build.func().push_values(&[target, outside]);
2188 let call = build.inst(
2189 InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::CallIndirect) },
2190 &[],
2191 );
2192 build.ret(&[]);
2193
2194 let outside = Outside::of(&module);
2195 let mut alias = Alias::new(&f, &outside);
2196 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
2197 assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
2198 }
2199
2200 #[test]
2201 fn every_reason_has_a_name_and_a_sentence() {
2202 for reason in Reason::ALL {
2203 assert!(!reason.name().is_empty());
2204 assert!(!reason.describe().is_empty());
2205 assert_eq!(Reason::ALL[reason.index()], reason);
2206 }
2207 assert_eq!(Reason::ALL.len(), Reason::COUNT);
2208 assert_eq!(Answer::No(Reason::Offset).reason(), Some(Reason::Offset));
2209 assert!(Answer::No(Reason::Offset).is_no());
2210 assert_eq!(Answer::May.reason(), None);
2211 assert!(!Answer::May.is_no());
2212 }
2213}