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