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, 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 {
651 self.touched_by(reference, call, true)
652 }
653
654 pub fn read_by(&mut self, reference: &Access, call: Inst) -> Answer {
658 self.touched_by(reference, call, false)
659 }
660
661 fn decide(&self, a: &Access, b: &Access) -> Answer {
664 if a.volatile && b.volatile {
668 return Answer::May;
669 }
670
671 if a.origin.is_object() && b.origin.is_object() {
679 if self.distinct(a.origin, b.origin) {
680 return Answer::No(Reason::Distinct);
681 }
682 if a.origin == b.origin {
683 return by_offset(a, b);
684 }
685 return Answer::May;
686 }
687
688 if let Some(local) = self.private(a).or_else(|| self.private(b)) {
691 let _ = local;
692 return Answer::No(Reason::Escape);
693 }
694
695 if a.restrict.disjoint(b.restrict) {
696 return Answer::No(Reason::Restrict);
697 }
698
699 if self.options.strict_aliasing {
700 if let (Some(one), Some(other)) = (a.tbaa, b.tbaa) {
701 if !self.types_conflict(one, other) {
702 return Answer::No(Reason::Tbaa);
703 }
704 }
705 }
706
707 if a.origin == b.origin {
709 return by_offset(a, b);
710 }
711
712 Answer::May
713 }
714
715 fn private(&self, reference: &Access) -> Option<Inst> {
718 match reference.origin {
719 Origin::Local(local) if !self.escapes.escaped(local) => Some(local),
720 _ => None,
721 }
722 }
723
724 fn handed(&self, local: Inst, call: Inst) -> bool {
729 self.func[self.func[call].args]
730 .iter()
731 .any(|&arg| matches!(origin(self.func, arg).0, Origin::Local(it) if it == local))
732 }
733
734 fn distinct(&self, a: Origin, b: Origin) -> bool {
736 match (a, b) {
737 (Origin::Local(one), Origin::Local(other)) => one != other,
738 (Origin::Local(_), Origin::Global(_)) | (Origin::Global(_), Origin::Local(_)) => true,
740 (Origin::Global(one), Origin::Global(other)) => {
741 one != other && self.one_object(one) && self.one_object(other)
742 }
743 _ => false,
744 }
745 }
746
747 fn one_object(&self, name: Symbol) -> bool {
754 self.outside.one_object(name)
755 }
756
757 fn types_conflict(&self, one: Meta, other: Meta) -> bool {
763 self.at_or_below(one, other) || self.at_or_below(other, one)
764 }
765
766 fn at_or_below(&self, mut node: Meta, ancestor: Meta) -> bool {
768 for _ in 0..TREE_LIMIT {
769 if node == ancestor {
770 return true;
771 }
772 match self.outside.parent(node) {
773 Some(up) => node = up,
774 None => return false,
775 }
776 }
777 true
780 }
781
782 fn touched_by(&mut self, reference: &Access, call: Inst, writing: bool) -> Answer {
783 self.counts.queries += 1;
784 let answer = self.decide_call(reference, call, writing);
785 if let Answer::No(reason) = answer {
786 self.counts.answered[reason.index()] += 1;
787 }
788 answer
789 }
790
791 fn decide_call(&self, reference: &Access, call: Inst, writing: bool) -> Answer {
792 if self.func[call].opcode.touches_only_planes() {
798 return Answer::No(Reason::Plane);
799 }
800
801 if self.func[call].opcode.is_jump_marker() {
806 return Answer::May;
807 }
808
809 if let Some(local) = self.private(reference) {
815 if !self.handed(local, call) {
816 return Answer::No(Reason::Escape);
817 }
818 }
819
820 let Some(attrs) = self.callee(call) else {
821 return Answer::May;
822 };
823 if attrs.set.contains(AttrSet::READNONE)
825 || (writing && attrs.set.contains(AttrSet::READONLY))
826 {
827 return Answer::No(Reason::Attribute);
828 }
829
830 if attrs.set.contains(AttrSet::ARGMEM_ONLY) {
837 let args = &self.func[self.func[call].args];
838 let mut all = true;
839 for &arg in args {
840 if !self.func[arg].ty.is_ptr() {
841 continue;
842 }
843 let through = Access::through(self.func, arg);
844 all &= self.decide(reference, &through).is_no();
845 }
846 if all {
847 return Answer::No(Reason::Attribute);
848 }
849 }
850
851 if let Some(summary) = self.summaries.and_then(|known| known.at(self.func, call)) {
856 if summary.touches_nothing() || (writing && summary.writes_nothing()) {
857 return Answer::No(Reason::Summary);
858 }
859 if summary.only_through_arguments() {
864 let args = &self.func[self.func[call].args];
865 let mut all = true;
866 for (at, &arg) in args.iter().enumerate() {
867 if !self.func[arg].ty.is_ptr() {
868 continue;
869 }
870 let touch = summary.param(at);
874 let reached =
875 if writing { touch.effect.writes() } else { touch.effect.reads() };
876 if !reached {
877 continue;
878 }
879 let through = Access::through(self.func, arg);
880 all &= self.decide(reference, &through).is_no();
881 }
882 if all {
883 return Answer::No(Reason::Summary);
884 }
885 }
886 }
887
888 Answer::May
889 }
890
891 fn callee(&self, call: Inst) -> Option<Attrs> {
896 let Extra::Call(info) = self.func[call].extra else {
897 return None;
898 };
899 let name = self.func[info].callee?;
900 self.outside.attrs(name)
901 }
902
903 fn mem(&self, inst: Inst) -> Option<MemInfo> {
904 match self.func[inst].extra {
905 Extra::Mem(info) | Extra::Rmw(_, info) => Some(self.func[info]),
906 Extra::VaObject(object) => Some(self.func[self.func[object].mem]),
907 _ => None,
908 }
909 }
910
911 fn result_type(&self, inst: Inst) -> Option<Type> {
912 self.func[inst].results().next().map(|value| self.func[value].ty)
913 }
914
915 fn access(
916 &self,
917 pointer: Value,
918 size: Option<u64>,
919 info: Option<MemInfo>,
920 flags: Flags,
921 ) -> Access {
922 let (origin, offset) = origin(self.func, pointer);
923 Access {
924 origin,
925 offset,
926 size,
927 tbaa: info.and_then(|info| info.tbaa),
928 restrict: info.map_or(Restrict::NONE, |info| info.restrict),
929 volatile: flags.contains(Flags::VOLATILE),
930 }
931 }
932
933 fn width(&self, ty: Type) -> Option<u64> {
936 if ty.is_ptr() {
937 return self.outside.pointer_bytes();
938 }
939 let bits = u64::from(ty.bits()) * u64::from(ty.lanes());
940 (bits > 0).then(|| bits.div_ceil(8))
941 }
942}
943
944fn by_offset(a: &Access, b: &Access) -> Answer {
946 let (Some((a_start, a_end)), Some((b_start, b_end))) = (a.range(), b.range()) else {
947 return Answer::May;
948 };
949 if a_end <= b_start || b_end <= a_start {
950 return Answer::No(Reason::Offset);
951 }
952 Answer::May
953}
954
955fn constant(func: &Func, value: Value) -> Option<i64> {
957 let Def::Result { inst, .. } = func[value].def else {
958 return None;
959 };
960 let data = func[inst];
961 if data.opcode != Opcode::IConst {
962 return None;
963 }
964 let Extra::Imm(imm) = data.extra else {
965 return None;
966 };
967 i64::try_from(Imm::signed(func[imm], func[value].ty)).ok()
968}
969
970#[cfg(test)]
971mod tests {
972 use rucc_base::{Interner, Symbol};
973 use rucc_ir::{
974 AttrSet, Attrs, Builder, CallInfo, Extra, Flags, Func, Global, InstData, IntPred, MemInfo,
975 MemOrder, MetaNode, Module, Opcode, Pic, Restrict, Signature, TbaaNode, Type, Value,
976 };
977
978 use crate::callgraph::CallGraph;
979 use crate::modref::{Summaries, summarize};
980 use rucc_target::{TargetInfo, Triple};
981
982 use super::*;
983
984 fn module(names: &mut Interner) -> Module {
986 let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
987 Module::new(names.intern("t.c"), &target)
988 }
989
990 fn func(names: &mut Interner, params: &[Type]) -> Func {
992 let mut func = Func::new(names.intern("f"), Signature::new().with_params(params));
993 let entry = func.create_block();
994 for &ty in params {
995 func.append_param(entry, ty);
996 }
997 func
998 }
999
1000 fn builder(func: &mut Func) -> Builder<'_> {
1002 let entry = func.entry().expect("the function has an entry block");
1003 Builder::new(func, entry)
1004 }
1005
1006 fn param(func: &Func, index: usize) -> Value {
1007 let entry = func.entry().expect("the function has an entry block");
1008 func[entry].params[index]
1009 }
1010
1011 fn plain(align: u32) -> MemInfo {
1012 MemInfo {
1013 size: 0,
1014 align,
1015 order: MemOrder::NotAtomic,
1016 tbaa: None,
1017 owns: 0,
1018 restrict: Restrict::NONE,
1019 }
1020 }
1021
1022 fn sized(size: u64, align: u32) -> MemInfo {
1023 MemInfo { size, ..plain(align) }
1024 }
1025
1026 fn local(build: &mut Builder<'_>, size: u64) -> Value {
1028 let mem = build.func().add_mem(sized(size, 8));
1029 build.value(InstData { extra: Extra::Mem(mem), ..InstData::new(Opcode::Alloca) }, Type::PTR)
1030 }
1031
1032 fn at(build: &mut Builder<'_>, base: Value, offset: i64) -> Value {
1034 let by = build.iconst(Type::int(64), i128::from(offset));
1035 build.binary(Opcode::PtrAdd, base, by, Flags::NONE)
1036 }
1037
1038 fn global(build: &mut Builder<'_>, module: &mut Module, name: Symbol) -> Value {
1040 module.add_global(Global::new(name, 16, 8));
1041 build.value(
1042 InstData { extra: Extra::Symbol(name), ..InstData::new(Opcode::GlobalAddr) },
1043 Type::PTR,
1044 )
1045 }
1046
1047 #[test]
1048 fn two_different_locals_are_two_objects() {
1049 let mut names = Interner::new();
1050 let module = module(&mut names);
1051 let mut f = func(&mut names, &[]);
1052 let mut build = builder(&mut f);
1053 let one = local(&mut build, 16);
1054 let other = local(&mut build, 16);
1055 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1056 build.store(read, other, plain(4), Flags::NONE);
1057 build.ret(&[]);
1058
1059 let outside = Outside::of(&module);
1060 let mut alias = Alias::new(&f, &outside);
1061 let (a, b) = two(&alias, &f);
1062 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1063 assert_eq!(alias.counts().answered(Reason::Distinct), 1);
1064 assert_eq!(alias.counts().queries(), 1);
1065 }
1066
1067 fn two(alias: &Alias<'_>, func: &Func) -> (Access, Access) {
1069 let mut read = None;
1070 let mut written = None;
1071 for block in func.blocks() {
1072 for inst in func.insts(block) {
1073 if read.is_none() {
1074 read = alias.reads(inst);
1075 }
1076 if written.is_none() {
1077 written = alias.writes(inst);
1078 }
1079 }
1080 }
1081 (read.expect("a read"), written.expect("a write"))
1082 }
1083
1084 #[test]
1085 fn a_local_and_a_global_are_two_objects() {
1086 let mut names = Interner::new();
1087 let mut module = module(&mut names);
1088 let x = names.intern("x");
1089 let mut f = func(&mut names, &[]);
1090 let mut build = builder(&mut f);
1091 let one = local(&mut build, 16);
1092 let other = global(&mut build, &mut module, x);
1093 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1094 build.store(read, other, plain(4), Flags::NONE);
1095 build.ret(&[]);
1096
1097 let outside = Outside::of(&module);
1098 let mut alias = Alias::new(&f, &outside);
1099 let (a, b) = two(&alias, &f);
1100 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1101 }
1102
1103 #[test]
1104 fn two_different_globals_are_two_objects() {
1105 let mut names = Interner::new();
1106 let mut module = module(&mut names);
1107 let (x, y) = (names.intern("x"), names.intern("y"));
1108 let mut f = func(&mut names, &[]);
1109 let mut build = builder(&mut f);
1110 let one = global(&mut build, &mut module, x);
1111 let other = global(&mut build, &mut module, y);
1112 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1113 build.store(read, other, plain(4), Flags::NONE);
1114 build.ret(&[]);
1115
1116 let outside = Outside::of(&module);
1117 let mut alias = Alias::new(&f, &outside);
1118 let (a, b) = two(&alias, &f);
1119 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1120 }
1121
1122 #[test]
1123 fn a_global_the_module_does_not_have_is_not_argued_about() {
1124 let mut names = Interner::new();
1127 let mut module = module(&mut names);
1128 let (x, y) = (names.intern("x"), names.intern("y"));
1129 let mut f = func(&mut names, &[]);
1130 let mut build = builder(&mut f);
1131 let one = global(&mut build, &mut module, x);
1132 let other = build.value(
1133 InstData { extra: Extra::Symbol(y), ..InstData::new(Opcode::GlobalAddr) },
1134 Type::PTR,
1135 );
1136 let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1137 build.store(read, other, plain(4), Flags::NONE);
1138 build.ret(&[]);
1139
1140 let outside = Outside::of(&module);
1141 let mut alias = Alias::new(&f, &outside);
1142 let (a, b) = two(&alias, &f);
1143 assert_eq!(alias.query(&a, &b), Answer::May);
1144 }
1145
1146 #[test]
1147 fn two_parts_of_one_object_that_do_not_overlap_are_disjoint() {
1148 let mut names = Interner::new();
1149 let module = module(&mut names);
1150 let mut f = func(&mut names, &[]);
1151 let mut build = builder(&mut f);
1152 let object = local(&mut build, 16);
1153 let first = at(&mut build, object, 0);
1154 let second = at(&mut build, object, 4);
1155 let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
1156 build.store(read, second, plain(4), Flags::NONE);
1157 build.ret(&[]);
1158
1159 let outside = Outside::of(&module);
1160 let mut alias = Alias::new(&f, &outside);
1161 let (a, b) = two(&alias, &f);
1162 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Offset));
1163 }
1164
1165 #[test]
1166 fn two_parts_of_one_object_that_do_overlap_are_not() {
1167 let mut names = Interner::new();
1168 let module = module(&mut names);
1169 let mut f = func(&mut names, &[]);
1170 let mut build = builder(&mut f);
1171 let object = local(&mut build, 16);
1172 let first = at(&mut build, object, 0);
1173 let second = at(&mut build, object, 2);
1174 let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
1175 build.store(read, second, plain(4), Flags::NONE);
1176 build.ret(&[]);
1177
1178 let outside = Outside::of(&module);
1179 let mut alias = Alias::new(&f, &outside);
1180 let (a, b) = two(&alias, &f);
1181 assert_eq!(alias.query(&a, &b), Answer::May);
1182 }
1183
1184 #[test]
1185 fn an_offset_nobody_knows_gives_up_the_offset_and_keeps_the_object() {
1186 let mut names = Interner::new();
1187 let module = module(&mut names);
1188 let mut f = func(&mut names, &[Type::int(64)]);
1189 let n = param(&f, 0);
1190 let mut build = builder(&mut f);
1191 let object = local(&mut build, 16);
1192 let somewhere = build.binary(Opcode::PtrAdd, object, n, Flags::NONE);
1193 let read = build.load(Type::int(32), somewhere, plain(4), Flags::NONE);
1194 build.store(read, object, plain(4), Flags::NONE);
1195 build.ret(&[]);
1196
1197 let outside = Outside::of(&module);
1198 let mut alias = Alias::new(&f, &outside);
1199 let (a, b) = two(&alias, &f);
1200 assert_eq!(a.origin, b.origin, "both are still that one object");
1201 assert_eq!(a.offset, None);
1202 assert_eq!(alias.query(&a, &b), Answer::May);
1203 }
1204
1205 #[test]
1206 fn a_local_whose_address_stays_here_is_not_what_a_parameter_points_at() {
1207 let mut names = Interner::new();
1208 let module = module(&mut names);
1209 let mut f = func(&mut names, &[Type::PTR]);
1210 let outside = param(&f, 0);
1211 let mut build = builder(&mut f);
1212 let object = local(&mut build, 16);
1213 let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1214 build.store(read, outside, plain(4), Flags::NONE);
1215 build.ret(&[]);
1216
1217 let outside = Outside::of(&module);
1218 let mut alias = Alias::new(&f, &outside);
1219 assert_eq!(alias.escapes().count(), 0);
1220 let (a, b) = two(&alias, &f);
1221 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Escape));
1222 }
1223
1224 #[test]
1225 fn a_local_whose_address_was_stored_somewhere_is() {
1226 let mut names = Interner::new();
1227 let module = module(&mut names);
1228 let mut f = func(&mut names, &[Type::PTR]);
1229 let outside = param(&f, 0);
1230 let mut build = builder(&mut f);
1231 let object = local(&mut build, 16);
1232 build.store(object, outside, plain(8), Flags::NONE);
1235 let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1236 build.store(read, outside, plain(4), Flags::NONE);
1237 build.ret(&[]);
1238
1239 let outside = Outside::of(&module);
1240 let mut alias = Alias::new(&f, &outside);
1241 assert_eq!(alias.escapes().count(), 1);
1242 let read = first(&f, Opcode::Load);
1243 let write = last(&f, Opcode::Store);
1244 let a = alias.reads(read).unwrap();
1245 let b = alias.writes(write).unwrap();
1246 assert_eq!(alias.query(&a, &b), Answer::May);
1247 }
1248
1249 fn first(func: &Func, opcode: Opcode) -> Inst {
1250 func.blocks()
1251 .flat_map(|block| func.insts(block))
1252 .find(|&inst| func[inst].opcode == opcode)
1253 .expect("an instruction with that opcode")
1254 }
1255
1256 fn last(func: &Func, opcode: Opcode) -> Inst {
1257 func.blocks()
1258 .flat_map(|block| func.insts(block))
1259 .filter(|&inst| func[inst].opcode == opcode)
1260 .last()
1261 .expect("an instruction with that opcode")
1262 }
1263
1264 #[test]
1265 fn an_address_carried_through_a_block_parameter_has_left_the_function() {
1266 let mut names = Interner::new();
1267 let module = module(&mut names);
1268 let mut f = func(&mut names, &[]);
1269 let start = f.entry().expect("an entry block");
1270 let next = f.create_block();
1271 f.append_param(next, Type::PTR);
1272
1273 let mut build = Builder::new(&mut f, start);
1274 let object = local(&mut build, 16);
1275 build.jump(next, &[object]);
1276 let mut build = Builder::new(&mut f, next);
1277 build.ret(&[]);
1278
1279 let outside = Outside::of(&module);
1280 let alias = Alias::new(&f, &outside);
1281 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1282 }
1283
1284 #[test]
1285 fn comparing_two_addresses_does_not_let_either_of_them_out() {
1286 let mut names = Interner::new();
1287 let module = module(&mut names);
1288 let mut f = func(&mut names, &[Type::PTR]);
1289 let outside = param(&f, 0);
1290 let mut build = builder(&mut f);
1291 let object = local(&mut build, 16);
1292 build.icmp(IntPred::Eq, object, outside);
1293 build.ret(&[]);
1294
1295 let outside = Outside::of(&module);
1296 let alias = Alias::new(&f, &outside);
1297 assert_eq!(alias.escapes().count(), 0);
1298 }
1299
1300 #[test]
1301 fn a_plane_write_on_a_local_does_not_let_its_address_out() {
1302 let mut names = Interner::new();
1306 let module = module(&mut names);
1307 let mut f = func(&mut names, &[]);
1308 let mut build = builder(&mut f);
1309 let object = local(&mut build, 16);
1310 let width = build.iconst(Type::int(64), 16);
1311 let args = build.func().push_values(&[object, width]);
1312 build.inst(InstData { args, ..InstData::new(Opcode::MetaInit) }, &[]);
1313 build.ret(&[]);
1314
1315 let outside = Outside::of(&module);
1316 let alias = Alias::new(&f, &outside);
1317 assert_eq!(alias.escapes().count(), 0);
1318 }
1319
1320 #[test]
1321 fn a_local_that_is_only_asked_about_and_checked_does_not_leave_the_function() {
1322 let mut names = Interner::new();
1327 let module = module(&mut names);
1328 let mut f = func(&mut names, &[]);
1329 let mut build = builder(&mut f);
1330 let object = local(&mut build, 16);
1331 let args = build.func().push_values(&[object]);
1332 let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1333 let args = build.func().push_values(&[capability, object]);
1334 build.inst(InstData { args, ..InstData::new(Opcode::CheckBounds) }, &[]);
1335 build.ret(&[]);
1336
1337 let outside = Outside::of(&module);
1338 let alias = Alias::new(&f, &outside);
1339 assert_eq!(alias.escapes().count(), 0);
1340 }
1341
1342 #[test]
1343 fn a_capability_of_a_local_used_for_anything_else_does_let_it_out() {
1344 let mut names = Interner::new();
1349 let module = module(&mut names);
1350 let mut f = func(&mut names, &[]);
1351 let mut build = builder(&mut f);
1352 let object = local(&mut build, 16);
1353 let args = build.func().push_values(&[object]);
1354 let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1355 let base = build.iconst(Type::int(64), 0);
1356 let size = build.iconst(Type::int(64), 4);
1357 let args = build.func().push_values(&[capability, base, size]);
1358 build.value(InstData { args, ..InstData::new(Opcode::CapNarrow) }, Type::CAP);
1359 build.ret(&[]);
1360
1361 let outside = Outside::of(&module);
1362 let alias = Alias::new(&f, &outside);
1363 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1364 }
1365
1366 #[test]
1367 fn the_whitelist_says_yes_to_a_plane_access_at_every_operand() {
1368 for opcode in Opcode::all().filter(|opcode| opcode.touches_only_planes()) {
1372 for index in 0..4 {
1373 assert!(keeps_address(opcode, index), "{opcode} at {index}");
1374 }
1375 }
1376 for opcode in [Opcode::CapNarrow, Opcode::CapRecover] {
1377 assert!(!keeps_address(opcode, 0), "{opcode}");
1378 }
1379 for opcode in [Opcode::CapLoad, Opcode::CapStore, Opcode::CapCopy] {
1382 for index in 0..2 {
1383 assert!(keeps_address(opcode, index), "{opcode} at {index}");
1384 }
1385 }
1386 assert!(!keeps_address(Opcode::CapStore, 2));
1387 assert!(!keeps_address(Opcode::CapStore, 3));
1388 assert!(keeps_address(Opcode::CapOf, 0));
1389 }
1390
1391 #[test]
1392 fn a_local_a_pointer_is_written_into_does_not_leave_the_function_for_the_writing_down() {
1393 let mut names = Interner::new();
1398 let module = module(&mut names);
1399 let mut f = func(&mut names, &[Type::PTR]);
1400 let written = param(&f, 0);
1401 let mut build = builder(&mut f);
1402 let object = local(&mut build, 8);
1403 let args = build.func().push_values(&[object]);
1404 let container = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1405 let args = build.func().push_values(&[written]);
1406 let held = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1407 build.store(written, object, plain(8), Flags::NONE);
1408 let args = build.func().push_values(&[container, object, written, held]);
1409 build.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
1410 build.ret(&[]);
1411
1412 let outside = Outside::of(&module);
1413 let alias = Alias::new(&f, &outside);
1414 assert_eq!(alias.escapes().count(), 0);
1415 }
1416
1417 #[test]
1418 fn a_local_whose_capability_is_written_into_a_slot_does_leave_the_function() {
1419 let mut names = Interner::new();
1424 let module = module(&mut names);
1425 let mut f = func(&mut names, &[Type::PTR]);
1426 let into = param(&f, 0);
1427 let mut build = builder(&mut f);
1428 let object = local(&mut build, 8);
1429 let args = build.func().push_values(&[into]);
1430 let container = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1431 let args = build.func().push_values(&[object]);
1432 let held = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1433 let args = build.func().push_values(&[container, into, object, held]);
1434 build.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
1435 build.ret(&[]);
1436
1437 let outside = Outside::of(&module);
1438 let alias = Alias::new(&f, &outside);
1439 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1440 }
1441
1442 #[test]
1443 fn an_address_turned_into_a_number_has_left_the_function() {
1444 let mut names = Interner::new();
1447 let module = module(&mut names);
1448 let mut f = func(&mut names, &[]);
1449 let mut build = builder(&mut f);
1450 let object = local(&mut build, 16);
1451 build.unary(Opcode::PtrToInt, object, Type::int(64));
1452 build.ret(&[]);
1453
1454 let outside = Outside::of(&module);
1455 let alias = Alias::new(&f, &outside);
1456 assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1457 }
1458
1459 #[test]
1460 fn two_restrict_pointers_in_one_scope_do_not_reach_the_same_object() {
1461 let mut names = Interner::new();
1462 let module = module(&mut names);
1463 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1464 let (one, other) = (param(&f, 0), param(&f, 1));
1465 let mut build = builder(&mut f);
1466 let mut info = plain(4);
1467 info.restrict = Restrict { clique: 1, base: 1 };
1468 let read = build.load(Type::int(32), one, info, Flags::NONE);
1469 info.restrict = Restrict { clique: 1, base: 2 };
1470 build.store(read, other, info, Flags::NONE);
1471 build.ret(&[]);
1472
1473 let outside = Outside::of(&module);
1474 let mut alias = Alias::new(&f, &outside);
1475 let (a, b) = two(&alias, &f);
1476 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Restrict));
1477 }
1478
1479 #[test]
1480 fn two_restrict_pointers_in_different_scopes_say_nothing_about_each_other() {
1481 let mut names = Interner::new();
1482 let module = module(&mut names);
1483 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1484 let (one, other) = (param(&f, 0), param(&f, 1));
1485 let mut build = builder(&mut f);
1486 let mut info = plain(4);
1487 info.restrict = Restrict { clique: 1, base: 1 };
1488 let read = build.load(Type::int(32), one, info, Flags::NONE);
1489 info.restrict = Restrict { clique: 2, base: 1 };
1490 build.store(read, other, info, Flags::NONE);
1491 build.ret(&[]);
1492
1493 let outside = Outside::of(&module);
1494 let mut alias = Alias::new(&f, &outside);
1495 let (a, b) = two(&alias, &f);
1496 assert_eq!(alias.query(&a, &b), Answer::May);
1497 }
1498
1499 fn types(module: &mut Module, names: &mut Interner) -> (Meta, Meta, Meta) {
1501 let root = module.add_meta(MetaNode::Tbaa(TbaaNode {
1502 name: names.intern("char"),
1503 parent: None,
1504 offset: 0,
1505 }));
1506 let int = module.add_meta(MetaNode::Tbaa(TbaaNode {
1507 name: names.intern("int"),
1508 parent: Some(root),
1509 offset: 0,
1510 }));
1511 let float = module.add_meta(MetaNode::Tbaa(TbaaNode {
1512 name: names.intern("float"),
1513 parent: Some(root),
1514 offset: 0,
1515 }));
1516 (root, int, float)
1517 }
1518
1519 #[test]
1520 fn two_unrelated_types_describe_no_object_in_common() {
1521 let mut names = Interner::new();
1522 let mut module = module(&mut names);
1523 let (_, int, float) = types(&mut module, &mut names);
1524 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1525 let (one, other) = (param(&f, 0), param(&f, 1));
1526 let mut build = builder(&mut f);
1527 let mut info = plain(4);
1528 info.tbaa = Some(int);
1529 let read = build.load(Type::int(32), one, info, Flags::NONE);
1530 info.tbaa = Some(float);
1531 build.store(read, other, info, Flags::NONE);
1532 build.ret(&[]);
1533
1534 let outside = Outside::of(&module);
1535 let mut alias = Alias::new(&f, &outside);
1536 let (a, b) = two(&alias, &f);
1537 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Tbaa));
1538 }
1539
1540 #[test]
1541 fn an_access_through_char_conflicts_with_everything() {
1542 let mut names = Interner::new();
1543 let mut module = module(&mut names);
1544 let (root, int, _) = types(&mut module, &mut names);
1545 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1546 let (one, other) = (param(&f, 0), param(&f, 1));
1547 let mut build = builder(&mut f);
1548 let mut info = plain(4);
1549 info.tbaa = Some(int);
1550 let read = build.load(Type::int(32), one, info, Flags::NONE);
1551 info.tbaa = Some(root);
1552 build.store(read, other, info, Flags::NONE);
1553 build.ret(&[]);
1554
1555 let outside = Outside::of(&module);
1556 let mut alias = Alias::new(&f, &outside);
1557 let (a, b) = two(&alias, &f);
1558 assert_eq!(alias.query(&a, &b), Answer::May);
1559 }
1560
1561 #[test]
1562 fn turning_strict_aliasing_off_turns_off_that_layer_and_no_other() {
1563 let mut names = Interner::new();
1564 let mut module = module(&mut names);
1565 let (_, int, float) = types(&mut module, &mut names);
1566 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1567 let (one, other) = (param(&f, 0), param(&f, 1));
1568 let mut build = builder(&mut f);
1569 let mut info = plain(4);
1570 info.tbaa = Some(int);
1571 info.restrict = Restrict { clique: 1, base: 1 };
1572 let read = build.load(Type::int(32), one, info, Flags::NONE);
1573 info.tbaa = Some(float);
1574 info.restrict = Restrict { clique: 1, base: 2 };
1575 build.store(read, other, info, Flags::NONE);
1576 build.ret(&[]);
1577
1578 let options = Options { strict_aliasing: false };
1579 let outside = Outside::of(&module);
1580 let mut alias = Alias::with(&f, &outside, options);
1581 let (a, b) = two(&alias, &f);
1582 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Restrict));
1585
1586 let mut without = Alias::with(&f, &outside, options);
1587 let plainer = Access { restrict: Restrict::NONE, ..a };
1588 let other = Access { restrict: Restrict::NONE, ..b };
1589 assert_eq!(without.query(&plainer, &other), Answer::May);
1590
1591 let mut with = Alias::new(&f, &outside);
1592 assert_eq!(with.query(&plainer, &other), Answer::No(Reason::Tbaa));
1593 }
1594
1595 #[test]
1596 fn writing_one_member_of_a_union_and_reading_another_is_one_object() {
1597 let mut names = Interner::new();
1602 let mut module = module(&mut names);
1603 let (_, int, float) = types(&mut module, &mut names);
1604 let mut f = func(&mut names, &[]);
1605 let mut build = builder(&mut f);
1606 let object = local(&mut build, 4);
1607 let mut info = plain(4);
1608 info.tbaa = Some(float);
1609 let read = build.load(Type::int(32), object, info, Flags::NONE);
1610 info.tbaa = Some(int);
1611 build.store(read, object, info, Flags::NONE);
1612 build.ret(&[]);
1613
1614 let outside = Outside::of(&module);
1615 let mut alias = Alias::new(&f, &outside);
1616 let (a, b) = two(&alias, &f);
1617 assert_eq!(alias.query(&a, &b), Answer::May);
1618 }
1619
1620 #[test]
1621 fn two_volatile_accesses_conflict_whatever_else_is_true_of_them() {
1622 let mut names = Interner::new();
1623 let module = module(&mut names);
1624 let mut f = func(&mut names, &[]);
1625 let mut build = builder(&mut f);
1626 let one = local(&mut build, 16);
1627 let other = local(&mut build, 16);
1628 let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1629 build.store(read, other, plain(4), Flags::VOLATILE);
1630 build.ret(&[]);
1631
1632 let outside = Outside::of(&module);
1633 let mut alias = Alias::new(&f, &outside);
1634 let (a, b) = two(&alias, &f);
1635 assert_eq!(alias.query(&a, &b), Answer::May);
1638 }
1639
1640 #[test]
1641 fn one_volatile_access_and_one_ordinary_one_are_argued_about_as_usual() {
1642 let mut names = Interner::new();
1643 let module = module(&mut names);
1644 let mut f = func(&mut names, &[]);
1645 let mut build = builder(&mut f);
1646 let one = local(&mut build, 16);
1647 let other = local(&mut build, 16);
1648 let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1649 build.store(read, other, plain(4), Flags::NONE);
1650 build.ret(&[]);
1651
1652 let outside = Outside::of(&module);
1653 let mut alias = Alias::new(&f, &outside);
1654 let (a, b) = two(&alias, &f);
1655 assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1656 }
1657
1658 #[test]
1659 fn a_copy_reads_its_source_and_writes_its_destination() {
1660 let mut names = Interner::new();
1661 let module = module(&mut names);
1662 let mut f = func(&mut names, &[]);
1663 let mut build = builder(&mut f);
1664 let to = local(&mut build, 16);
1665 let from = local(&mut build, 16);
1666 let mem = build.func().add_mem(sized(16, 8));
1667 let args = build.func().push_values(&[to, from]);
1668 build.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1669 build.ret(&[]);
1670
1671 let outside = Outside::of(&module);
1672 let alias = Alias::new(&f, &outside);
1673 let copy = first(&f, Opcode::Memcpy);
1674 let read = alias.reads(copy).expect("a copy reads");
1675 let written = alias.writes(copy).expect("a copy writes");
1676 assert_eq!(read.size, Some(16));
1677 assert_eq!(written.size, Some(16));
1678 assert_ne!(read.origin, written.origin);
1679 }
1680
1681 #[test]
1682 fn a_copy_of_a_length_the_program_works_out_is_an_access_of_no_known_size() {
1683 let mut names = Interner::new();
1684 let module = module(&mut names);
1685 let mut f = func(&mut names, &[Type::int(64)]);
1686 let length = param(&f, 0);
1687 let mut build = builder(&mut f);
1688 let to = local(&mut build, 16);
1689 let from = local(&mut build, 16);
1690 let mem = build.func().add_mem(sized(0, 8));
1691 let args = build.func().push_values(&[to, from, length]);
1692 build.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1693 build.ret(&[]);
1694
1695 let outside = Outside::of(&module);
1696 let alias = Alias::new(&f, &outside);
1697 let copy = first(&f, Opcode::Memcpy);
1698 assert_eq!(alias.reads(copy).expect("a copy reads").size, None);
1701 assert_eq!(alias.writes(copy).expect("a copy writes").size, None);
1702 }
1703
1704 fn call_to(
1706 names: &mut Interner,
1707 module: &mut Module,
1708 f: &mut Func,
1709 attrs: Attrs,
1710 args: &[Value],
1711 ) -> Inst {
1712 let name = names.intern("g");
1713 let params: Vec<Type> = args.iter().map(|_| Type::PTR).collect();
1714 let mut callee = Func::new(name, Signature::new().with_params(¶ms));
1715 callee.attrs = attrs;
1716 module.add_func(callee);
1717 let signature = f.add_signature(Signature::new().with_params(¶ms));
1718 let mut build = builder(f);
1719 build.call(name, signature, args)
1720 }
1721
1722 fn attrs(set: AttrSet) -> Attrs {
1723 Attrs { set, ..Attrs::NONE }
1724 }
1725
1726 #[test]
1727 fn a_call_cannot_touch_a_local_whose_address_stayed_here() {
1728 let mut names = Interner::new();
1729 let mut module = module(&mut names);
1730 let mut f = func(&mut names, &[Type::PTR]);
1731 let outside = param(&f, 0);
1732 let mut build = builder(&mut f);
1733 let object = local(&mut build, 16);
1734 let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1735 let _ = read;
1736 let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[outside]);
1737 let mut build = builder(&mut f);
1738 build.ret(&[]);
1739
1740 let outside = Outside::of(&module);
1741 let mut alias = Alias::new(&f, &outside);
1742 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1743 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Escape));
1744 assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Escape));
1745 }
1746
1747 #[test]
1748 fn a_call_can_touch_a_local_it_was_handed() {
1749 let mut names = Interner::new();
1750 let mut module = module(&mut names);
1751 let mut f = func(&mut names, &[]);
1752 let mut build = builder(&mut f);
1753 let object = local(&mut build, 16);
1754 build.load(Type::int(32), object, plain(4), Flags::NONE);
1755 let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[object]);
1756 let mut build = builder(&mut f);
1757 build.ret(&[]);
1758
1759 let outside = Outside::of(&module);
1760 let mut alias = Alias::new(&f, &outside);
1761 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1762 assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1763 }
1764
1765 #[test]
1766 fn a_setjmp_marker_can_touch_a_local_whose_address_stayed_here() {
1767 let mut names = Interner::new();
1772 let mut module = module(&mut names);
1773 let name = names.intern("jmp_buf");
1774 let mut f = func(&mut names, &[]);
1775 let mut build = builder(&mut f);
1776 let object = local(&mut build, 16);
1777 build.load(Type::int(32), object, plain(4), Flags::NONE);
1778 let buffer = global(&mut build, &mut module, name);
1779 let args = build.func().push_values(&[buffer]);
1780 let marker =
1781 build.inst(InstData { args, ..InstData::new(Opcode::SetjmpMarker) }, &[Type::int(32)]);
1782 build.ret(&[]);
1783
1784 let outside = Outside::of(&module);
1785 let mut alias = Alias::new(&f, &outside);
1786 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1787 assert_eq!(alias.clobbered_by(&reference, marker), Answer::May);
1788 assert_eq!(alias.read_by(&reference, marker), Answer::May);
1789 }
1790
1791 #[test]
1792 fn a_pure_callee_reads_memory_and_writes_none() {
1793 let mut names = Interner::new();
1794 let mut module = module(&mut names);
1795 let mut f = func(&mut names, &[Type::PTR]);
1796 let outside = param(&f, 0);
1797 let mut build = builder(&mut f);
1798 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1799 let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READONLY), &[outside]);
1800 let mut build = builder(&mut f);
1801 build.ret(&[]);
1802
1803 let outside = Outside::of(&module);
1804 let mut alias = Alias::new(&f, &outside);
1805 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1806 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1807 assert_eq!(alias.read_by(&reference, call), Answer::May);
1808 }
1809
1810 #[test]
1811 fn a_plane_write_is_not_a_write_to_the_address_it_names() {
1812 let mut names = Interner::new();
1818 let module = module(&mut names);
1819 let mut f = func(&mut names, &[Type::PTR]);
1820 let outside = param(&f, 0);
1821 let mut build = builder(&mut f);
1822 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1823 let width = build.iconst(Type::int(64), 4);
1824 let args = build.func().push_values(&[outside, width]);
1825 build.inst(InstData { args, ..InstData::new(Opcode::MetaInit) }, &[]);
1826 build.ret(&[]);
1827
1828 let outside = Outside::of(&module);
1829 let mut alias = Alias::new(&f, &outside);
1830 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1831 let plane = first(&f, Opcode::MetaInit);
1832 assert_eq!(alias.clobbered_by(&reference, plane), Answer::No(Reason::Plane));
1833 assert_eq!(alias.read_by(&reference, plane), Answer::No(Reason::Plane));
1835 }
1836
1837 #[test]
1838 fn a_check_reads_a_plane_and_not_what_it_is_about() {
1839 let mut names = Interner::new();
1843 let module = module(&mut names);
1844 let mut f = func(&mut names, &[Type::PTR]);
1845 let outside = param(&f, 0);
1846 let mut build = builder(&mut f);
1847 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1848 let width = build.iconst(Type::int(64), 4);
1849 let args = build.func().push_values(&[outside, width]);
1850 build.inst(InstData { args, ..InstData::new(Opcode::CheckBounds) }, &[]);
1851 build.ret(&[]);
1852
1853 let outside = Outside::of(&module);
1854 let mut alias = Alias::new(&f, &outside);
1855 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1856 let check = first(&f, Opcode::CheckBounds);
1857 assert_eq!(alias.clobbered_by(&reference, check), Answer::No(Reason::Plane));
1858 assert_eq!(alias.read_by(&reference, check), Answer::No(Reason::Plane));
1859 }
1860
1861 #[test]
1862 fn a_const_callee_touches_no_memory_at_all() {
1863 let mut names = Interner::new();
1864 let mut module = module(&mut names);
1865 let mut f = func(&mut names, &[Type::PTR]);
1866 let outside = param(&f, 0);
1867 let mut build = builder(&mut f);
1868 build.load(Type::int(32), outside, plain(4), Flags::NONE);
1869 let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READNONE), &[outside]);
1870 let mut build = builder(&mut f);
1871 build.ret(&[]);
1872
1873 let outside = Outside::of(&module);
1874 let mut alias = Alias::new(&f, &outside);
1875 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1876 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1877 assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Attribute));
1878 }
1879
1880 #[test]
1881 fn a_callee_that_touches_only_its_arguments_leaves_a_global_it_was_not_passed_alone() {
1882 let mut names = Interner::new();
1883 let mut module = module(&mut names);
1884 let x = names.intern("x");
1885 let mut f = func(&mut names, &[Type::PTR]);
1886 let outside = param(&f, 0);
1887 let mut build = builder(&mut f);
1888 let object = global(&mut build, &mut module, x);
1889 build.load(Type::int(32), object, plain(4), Flags::NONE);
1890 let call =
1891 call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[outside]);
1892 let mut build = builder(&mut f);
1893 build.ret(&[]);
1894
1895 let outside = Outside::of(&module);
1896 let mut alias = Alias::new(&f, &outside);
1897 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1898 assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1901 }
1902
1903 #[test]
1904 fn a_callee_that_touches_only_its_arguments_and_was_handed_one_object_leaves_the_other() {
1905 let mut names = Interner::new();
1906 let mut module = module(&mut names);
1907 let (x, y) = (names.intern("x"), names.intern("y"));
1908 let mut f = func(&mut names, &[]);
1909 let mut build = builder(&mut f);
1910 let watched = global(&mut build, &mut module, x);
1911 let handed = global(&mut build, &mut module, y);
1912 build.load(Type::int(32), watched, plain(4), Flags::NONE);
1913 let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[handed]);
1914 let mut build = builder(&mut f);
1915 build.ret(&[]);
1916
1917 let outside = Outside::of(&module);
1918 let mut alias = Alias::new(&f, &outside);
1919 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1920 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1921 }
1922
1923 fn call_to_body(
1928 names: &mut Interner,
1929 module: &mut Module,
1930 f: &mut Func,
1931 arity: usize,
1932 body: fn(&mut Builder<'_>, &[Value]),
1933 args: &[Value],
1934 ) -> Inst {
1935 defines(names, module, "g", arity, body);
1936 calls_it(names, f, "g", arity, args)
1937 }
1938
1939 fn defines(
1942 names: &mut Interner,
1943 module: &mut Module,
1944 called: &str,
1945 arity: usize,
1946 body: fn(&mut Builder<'_>, &[Value]),
1947 ) {
1948 let name = names.intern(called);
1949 let params = vec![Type::PTR; arity];
1950 let mut callee = Func::new(name, Signature::new().with_params(¶ms));
1951 let entry = callee.create_block();
1952 let got: Vec<Value> = params.iter().map(|&ty| callee.append_param(entry, ty)).collect();
1953 let mut build = Builder::new(&mut callee, entry);
1954 body(&mut build, &got);
1955 module.add_func(callee);
1956 }
1957
1958 fn calls_it(
1960 names: &mut Interner,
1961 f: &mut Func,
1962 called: &str,
1963 arity: usize,
1964 args: &[Value],
1965 ) -> Inst {
1966 let name = names.intern(called);
1967 let signature = f.add_signature(Signature::new().with_params(&vec![Type::PTR; arity]));
1968 let mut build = builder(f);
1969 build.call(name, signature, args)
1970 }
1971
1972 fn worked_out(module: &Module) -> Summaries {
1974 let mut summaries = Summaries::of_module(module);
1975 summarize(module, &CallGraph::of(module, Pic::Executable), &mut summaries);
1976 summaries
1977 }
1978
1979 fn body_does_nothing(build: &mut Builder<'_>, _: &[Value]) {
1981 build.ret(&[]);
1982 }
1983
1984 fn body_reads_the_first(build: &mut Builder<'_>, args: &[Value]) {
1986 let value = build.load(Type::int(32), args[0], plain(4), Flags::NONE);
1987 build.ret(&[value]);
1988 }
1989
1990 fn body_writes_the_first(build: &mut Builder<'_>, args: &[Value]) {
1992 let zero = build.iconst(Type::int(32), 0);
1993 build.store(zero, args[0], plain(4), Flags::NONE);
1994 build.ret(&[]);
1995 }
1996
1997 fn body_keeps_the_first(build: &mut Builder<'_>, args: &[Value]) {
1999 build.store(args[0], args[1], plain(8), Flags::NONE);
2000 build.ret(&[]);
2001 }
2002
2003 #[test]
2004 fn a_callee_nobody_declared_anything_about_is_read_out_of_its_body() {
2005 let mut names = Interner::new();
2006 let mut module = module(&mut names);
2007 let x = names.intern("x");
2008 let mut f = func(&mut names, &[]);
2009 let mut build = builder(&mut f);
2010 let object = global(&mut build, &mut module, x);
2011 build.load(Type::int(32), object, plain(4), Flags::NONE);
2012 let call = call_to_body(&mut names, &mut module, &mut f, 0, body_does_nothing, &[]);
2013 let mut build = builder(&mut f);
2014 build.ret(&[]);
2015
2016 let outside = Outside::of(&module);
2017 let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2018 let mut blind = Alias::new(&f, &outside);
2020 assert_eq!(blind.clobbered_by(&reference, call), Answer::May);
2021
2022 let summaries = worked_out(&module);
2023 let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2024 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Summary));
2025 assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Summary));
2026 }
2027
2028 #[test]
2029 fn a_callee_worked_out_to_write_nothing_clobbers_nothing() {
2030 let mut names = Interner::new();
2031 let mut module = module(&mut names);
2032 let mut f = func(&mut names, &[Type::PTR]);
2033 let handed = param(&f, 0);
2034 let mut build = builder(&mut f);
2035 build.load(Type::int(32), handed, plain(4), Flags::NONE);
2036 let call =
2037 call_to_body(&mut names, &mut module, &mut f, 1, body_reads_the_first, &[handed]);
2038 let mut build = builder(&mut f);
2039 build.ret(&[]);
2040
2041 let outside = Outside::of(&module);
2042 let summaries = worked_out(&module);
2043 let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2044 let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2045 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Summary));
2046 assert_eq!(alias.read_by(&reference, call), Answer::May);
2048 }
2049
2050 #[test]
2051 fn a_callee_that_writes_one_of_the_two_it_was_handed_leaves_the_other() {
2052 let mut names = Interner::new();
2055 let mut module = module(&mut names);
2056 let (x, y) = (names.intern("x"), names.intern("y"));
2057 let mut f = func(&mut names, &[]);
2058 let mut build = builder(&mut f);
2059 let watched = global(&mut build, &mut module, x);
2060 let written = global(&mut build, &mut module, y);
2061 build.load(Type::int(32), watched, plain(4), Flags::NONE);
2062 let call = call_to_body(
2063 &mut names,
2064 &mut module,
2065 &mut f,
2066 2,
2067 body_writes_the_first,
2068 &[written, watched],
2069 );
2070 let mut build = builder(&mut f);
2071 build.ret(&[]);
2072
2073 let outside = Outside::of(&module);
2074 let summaries = worked_out(&module);
2075 let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2076 let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2077 assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Summary));
2078 }
2079
2080 #[test]
2081 fn a_local_lent_to_a_callee_that_keeps_it_not_is_still_private_everywhere_else() {
2082 let mut names = Interner::new();
2086 let mut module = module(&mut names);
2087 let x = names.intern("x");
2088 let mut f = func(&mut names, &[]);
2089 let mut build = builder(&mut f);
2090 let object = local(&mut build, 16);
2091 let elsewhere = global(&mut build, &mut module, x);
2092 build.load(Type::int(32), object, plain(4), Flags::NONE);
2093 defines(&mut names, &mut module, "g", 1, body_writes_the_first);
2094 let lent = calls_it(&mut names, &mut f, "g", 1, &[object]);
2095 let other = calls_it(&mut names, &mut f, "g", 1, &[elsewhere]);
2096 let mut build = builder(&mut f);
2097 build.ret(&[]);
2098
2099 let outside = Outside::of(&module);
2100 let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2101 let mut blind = Alias::new(&f, &outside);
2103 assert_eq!(blind.clobbered_by(&reference, lent), Answer::May);
2104 assert_eq!(blind.clobbered_by(&reference, other), Answer::May);
2105
2106 let summaries = worked_out(&module);
2107 let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2108 assert_eq!(alias.clobbered_by(&reference, lent), Answer::May);
2111 assert_eq!(alias.clobbered_by(&reference, other), Answer::No(Reason::Escape));
2113 assert_eq!(alias.escapes().count(), 0);
2114 }
2115
2116 #[test]
2117 fn a_local_written_down_by_a_callee_is_gone_exactly_as_before() {
2118 let mut names = Interner::new();
2119 let mut module = module(&mut names);
2120 let x = names.intern("x");
2121 let mut f = func(&mut names, &[]);
2122 let mut build = builder(&mut f);
2123 let object = local(&mut build, 16);
2124 let elsewhere = global(&mut build, &mut module, x);
2125 build.load(Type::int(32), object, plain(4), Flags::NONE);
2126 defines(&mut names, &mut module, "g", 2, body_keeps_the_first);
2127 defines(&mut names, &mut module, "h", 1, body_does_nothing);
2128 calls_it(&mut names, &mut f, "g", 2, &[object, elsewhere]);
2129 let other = calls_it(&mut names, &mut f, "h", 1, &[elsewhere]);
2130 let mut build = builder(&mut f);
2131 build.ret(&[]);
2132
2133 let outside = Outside::of(&module);
2134 let summaries = worked_out(&module);
2135 let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2136 let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2137 assert_eq!(alias.escapes().count(), 1);
2140 assert_eq!(alias.private(&reference), None);
2141 assert_eq!(alias.clobbered_by(&reference, other), Answer::No(Reason::Summary));
2143 }
2144
2145 #[test]
2146 fn a_local_handed_to_a_const_declaration_is_still_gone() {
2147 let mut names = Interner::new();
2152 let mut module = module(&mut names);
2153 let mut f = func(&mut names, &[]);
2154 let mut build = builder(&mut f);
2155 let object = local(&mut build, 16);
2156 build.load(Type::int(32), object, plain(4), Flags::NONE);
2157 call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READNONE), &[object]);
2158 let mut build = builder(&mut f);
2159 build.ret(&[]);
2160
2161 let outside = Outside::of(&module);
2162 let summaries = worked_out(&module);
2163 let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2164 let alias = Alias::new(&f, &outside).knowing(&summaries);
2165 assert_eq!(alias.escapes().count(), 1);
2166 assert_eq!(alias.private(&reference), None);
2167 }
2168
2169 #[test]
2170 fn an_indirect_call_is_not_argued_about() {
2171 let mut names = Interner::new();
2172 let module = module(&mut names);
2173 let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
2174 let (target, outside) = (param(&f, 0), param(&f, 1));
2175 let mut build = builder(&mut f);
2176 build.load(Type::int(32), outside, plain(4), Flags::NONE);
2177 let signature = build.func().add_signature(Signature::new().with_params(&[Type::PTR]));
2178 let varargs = build.func().push_abis(&[]);
2179 let info = build.func().add_call(CallInfo { callee: None, signature, varargs });
2180 let args = build.func().push_values(&[target, outside]);
2181 let call = build.inst(
2182 InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::CallIndirect) },
2183 &[],
2184 );
2185 build.ret(&[]);
2186
2187 let outside = Outside::of(&module);
2188 let mut alias = Alias::new(&f, &outside);
2189 let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
2190 assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
2191 }
2192
2193 #[test]
2194 fn every_reason_has_a_name_and_a_sentence() {
2195 for reason in Reason::ALL {
2196 assert!(!reason.name().is_empty());
2197 assert!(!reason.describe().is_empty());
2198 assert_eq!(Reason::ALL[reason.index()], reason);
2199 }
2200 assert_eq!(Reason::ALL.len(), Reason::COUNT);
2201 assert_eq!(Answer::No(Reason::Offset).reason(), Some(Reason::Offset));
2202 assert!(Answer::No(Reason::Offset).is_no());
2203 assert_eq!(Answer::May.reason(), None);
2204 assert!(!Answer::May.is_no());
2205 }
2206}