1use std::collections::{HashMap, HashSet};
47
48use rucc_base::Interner;
49use rucc_ir::{
50 CallInfo, Datum, Extra, Flags, Func, Global, Imm, Inst, InstData, Linkage, Meta, Module,
51 Opcode, Signature, Type, Value,
52};
53
54use crate::plane;
55
56pub const WIDTH: u64 = 16;
58
59pub const SECTION: &str = ".rucc_safety_desc";
61
62const DESCRIPTOR: &str = "__rucc_safety_desc";
67
68const ACCESS: u8 = 1;
70
71const DERIVE: u8 = 2;
73
74const FREE: u8 = 6;
80
81const RESTRICT: u8 = 8;
83
84const RACE: u8 = 9;
90
91#[derive(Clone, Copy, Debug, PartialEq, Eq)]
98pub struct Descriptor {
99 pub judgement: u8,
101 pub class: u8,
103 pub size: u16,
106}
107
108pub fn lower(module: &mut Module, names: &mut Interner) -> usize {
117 let word = Type::int(module.datalayout.pointer_bits);
120 let mut written: Vec<Descriptor> = Vec::new();
125 let numbers = plane::numbers(module, names);
129 for id in module.funcs() {
130 if module[id].is_declaration() {
131 continue;
132 }
133 calls(&mut module[id], names, word, &numbers, &mut written);
134 }
135 for (index, row) in written.iter().enumerate() {
136 emit(module, names, index, *row);
137 }
138 written.len()
139}
140
141fn calls(
143 func: &mut Func,
144 names: &mut Interner,
145 word: Type,
146 numbers: &HashMap<Meta, u32>,
147 table: &mut Vec<Descriptor>,
148) {
149 let insts: Vec<Inst> =
150 func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
151 let pairs = pairs(func, &insts);
152 let fused: HashSet<Inst> = pairs.values().copied().collect();
153 for &inst in &insts {
154 if fused.contains(&inst) {
157 continue;
158 }
159 match func[inst].opcode {
160 Opcode::CheckBounds => bounds(func, names, word, table, inst),
161 Opcode::CheckLive => live(func, names, table, inst),
162 Opcode::CheckFree => freed(func, names, table, inst),
163 Opcode::CheckDeriv => deriv(func, names, word, table, inst),
164 Opcode::CheckType => {
165 typed(func, names, word, numbers, table, inst, pairs.get(&inst).copied());
166 }
167 Opcode::CheckInit => began(func, names, word, table, inst),
168 Opcode::CheckRace => raced(func, names, word, table, inst),
169 Opcode::CheckRestrictRead => promised(func, names, word, table, inst, false),
170 Opcode::CheckRestrictWrite => promised(func, names, word, table, inst, true),
171 Opcode::RestrictEnter => opened(func, names, inst),
172 Opcode::RestrictLeave => closed(func, names, inst),
173 Opcode::MetaType => judgement(func, names, word, numbers, inst),
174 Opcode::MetaTypeCopy => carriage(func, names, word, inst),
175 Opcode::MetaInit => written(func, names, word, inst),
176 Opcode::MetaInitCopy => carried(func, names, word, inst),
177 Opcode::CapCopy => relocated(func, names, word, inst),
178 Opcode::MetaEpoch => stamped(func, names, word, inst),
179 Opcode::MetaRelease => published(func, names, inst),
180 Opcode::MetaAcquire => taken(func, names, inst),
181 Opcode::MetaFenceRelease => published_everywhere(func, names, inst),
182 Opcode::MetaFenceAcquire => taken_everywhere(func, names, inst),
183 Opcode::CapExtent => extent(func, names, word, inst, "__rucc_extent"),
184 Opcode::CapExtentBack => extent(func, names, word, inst, "__rucc_extent_back"),
185 Opcode::SafeRegionBegin | Opcode::SafeRegionEnd => declared(func, inst),
186 _ => {}
187 }
188 }
189 crate::slot::frames(func, names, word);
193}
194
195fn bounds(
221 func: &mut Func,
222 names: &mut Interner,
223 word: Type,
224 table: &mut Vec<Descriptor>,
225 inst: Inst,
226) {
227 let args = &func[func[inst].args];
228 let (Some(&capability), Some(&pointer), computed) =
229 (args.first(), args.get(1), args.get(2).copied())
230 else {
231 return;
232 };
233 let Extra::Mem(mem) = func[inst].extra else { return };
234 let size = func[mem].size;
235
236 let row = Descriptor {
237 judgement: ACCESS,
238 class: 0,
239 size: if computed.is_some() { 0 } else { u16::try_from(size).unwrap_or(u16::MAX) },
242 };
243 let desc = record(func, names, table, inst, row);
244 let bytes = match computed {
245 Some(value) => fitted(func, inst, value, word),
246 None => konst(func, inst, Imm::int(i128::from(size), word), word),
247 };
248 let claim = if computed.is_some() { 1 } else { i128::from(func[mem].align) };
249 let align = konst(func, inst, Imm::int(claim, word), word);
250 let params = &[Type::PTR, word, word, Type::PTR, Type::PTR];
251 let args = &[pointer, bytes, align, capability, desc];
252 call(func, names, inst, "__rucc_check_bounds", params, &[], args);
253}
254
255pub(crate) fn fitted(func: &mut Func, inst: Inst, value: Value, word: Type) -> Value {
266 let ty = func[value].ty;
267 if ty == word {
268 return value;
269 }
270 let opcode = if ty.bits() > word.bits() { Opcode::Trunc } else { Opcode::ZExt };
271 let span = func.span(inst);
272 let args = func.push_values(&[value]);
273 let made = func.create_inst(InstData { args, ..InstData::new(opcode) }, &[word], span);
274 func.insert_before(made, inst);
275 func[made].results().next().expect("a cast created with one result has one")
276}
277
278pub(crate) fn keeps(opcode: Opcode) -> bool {
303 matches!(
304 opcode,
305 Opcode::CheckBounds
306 | Opcode::CheckLive
307 | Opcode::CheckFree
308 | Opcode::CapStore
309 | Opcode::CapYield
310 | Opcode::CapPublish
311 )
312}
313
314fn live(func: &mut Func, names: &mut Interner, table: &mut Vec<Descriptor>, inst: Inst) {
327 let [capability, pointer] = func[func[inst].args] else { return };
328 let row = Descriptor { judgement: ACCESS, class: 0, size: 0 };
331 let desc = record(func, names, table, inst, row);
332 let params = &[Type::PTR; 3];
333 call(func, names, inst, "__rucc_check_live", params, &[], &[capability, pointer, desc]);
334}
335
336fn freed(func: &mut Func, names: &mut Interner, table: &mut Vec<Descriptor>, inst: Inst) {
351 let [capability, pointer] = func[func[inst].args] else { return };
352 let row = Descriptor { judgement: FREE, class: 0, size: 0 };
355 let desc = record(func, names, table, inst, row);
356 let params = &[Type::PTR; 3];
357 call(func, names, inst, "__rucc_check_free", params, &[], &[capability, pointer, desc]);
358}
359
360fn deriv(
365 func: &mut Func,
366 names: &mut Interner,
367 word: Type,
368 table: &mut Vec<Descriptor>,
369 inst: Inst,
370) {
371 let [_capability, base, derived, stride] = func[func[inst].args] else { return };
372 let row = Descriptor { judgement: DERIVE, class: 0, size: 0 };
373 let desc = record(func, names, table, inst, row);
374 let params = &[Type::PTR, Type::PTR, word, Type::PTR];
375 call(func, names, inst, "__rucc_check_deriv", params, &[], &[base, derived, stride, desc]);
376}
377
378fn pairs(func: &Func, insts: &[Inst]) -> HashMap<Inst, Inst> {
391 let mut found = HashMap::new();
392 for &inst in insts {
393 if func[inst].opcode != Opcode::CheckType {
394 continue;
395 }
396 if let Some(partner) = partner(func, inst) {
397 found.insert(inst, partner);
398 }
399 }
400 found
401}
402
403fn partner(func: &Func, check: Inst) -> Option<Inst> {
409 let [_capability, pointer] = func[func[check].args] else { return None };
410 let Extra::Mem(mem) = func[check].extra else { return None };
411 let size = func[mem].size;
412 let block = func.block_of(check)?;
413 let mut after = false;
414 for inst in func.insts(block) {
415 if inst == check {
416 after = true;
417 continue;
418 }
419 if !after {
420 continue;
421 }
422 match func[inst].opcode {
423 Opcode::CheckInit => {
424 let [_capability, other] = func[func[inst].args] else { return None };
425 let Extra::Mem(at) = func[inst].extra else { return None };
426 return (other == pointer && func[at].size == size).then_some(inst);
427 }
428 Opcode::CheckType => return None,
429 opcode if opcode.writes_memory() || opcode.is_terminator() => return None,
430 _ => {}
431 }
432 }
433 None
434}
435
436fn typed(
456 func: &mut Func,
457 names: &mut Interner,
458 word: Type,
459 numbers: &HashMap<Meta, u32>,
460 table: &mut Vec<Descriptor>,
461 inst: Inst,
462 partner: Option<Inst>,
463) {
464 let args = &func[func[inst].args];
465 let (Some(&_capability), Some(&pointer), computed) =
466 (args.first(), args.get(1), args.get(2).copied())
467 else {
468 return;
469 };
470 let Extra::Mem(mem) = func[inst].extra else { return };
471 let size = func[mem].size;
472 let Some(node) = func[mem].tbaa else { return };
473 let Some(&number) = numbers.get(&node) else { return };
474
475 let row = Descriptor {
476 judgement: ACCESS,
477 class: 0,
478 size: if computed.is_some() { 0 } else { u16::try_from(size).unwrap_or(u16::MAX) },
480 };
481 let desc = record(func, names, table, inst, row);
482 let bytes = match computed {
483 Some(value) => fitted(func, inst, value, word),
484 None => konst(func, inst, Imm::int(i128::from(size), word), word),
485 };
486 let small = Type::int(32);
487 let ty = konst(func, inst, Imm::int(i128::from(number), small), small);
488 let params = &[Type::PTR, word, small, Type::PTR];
489 let routine = match partner {
490 Some(init) => {
491 func.remove_inst(init);
492 "__rucc_check_typed_init"
493 }
494 None => "__rucc_check_type",
495 };
496 call(func, names, inst, routine, params, &[], &[pointer, bytes, ty, desc]);
497}
498
499fn began(
511 func: &mut Func,
512 names: &mut Interner,
513 word: Type,
514 table: &mut Vec<Descriptor>,
515 inst: Inst,
516) {
517 let args = &func[func[inst].args];
518 let (Some(&_capability), Some(&pointer), computed) =
519 (args.first(), args.get(1), args.get(2).copied())
520 else {
521 return;
522 };
523 let Extra::Mem(mem) = func[inst].extra else { return };
524 let size = func[mem].size;
525
526 let row = Descriptor {
527 judgement: ACCESS,
528 class: 0,
529 size: if computed.is_some() { 0 } else { u16::try_from(size).unwrap_or(u16::MAX) },
531 };
532 let desc = record(func, names, table, inst, row);
533 let bytes = match computed {
534 Some(value) => fitted(func, inst, value, word),
535 None => konst(func, inst, Imm::int(i128::from(size), word), word),
536 };
537 let params = &[Type::PTR, word, Type::PTR];
538 call(func, names, inst, "__rucc_check_init", params, &[], &[pointer, bytes, desc]);
539}
540
541fn raced(
552 func: &mut Func,
553 names: &mut Interner,
554 word: Type,
555 table: &mut Vec<Descriptor>,
556 inst: Inst,
557) {
558 let [_capability, pointer] = func[func[inst].args] else { return };
559 let Extra::Mem(mem) = func[inst].extra else { return };
560 let size = func[mem].size;
561
562 let row = Descriptor {
563 judgement: RACE,
564 class: 0,
565 size: u16::try_from(size).unwrap_or(u16::MAX),
567 };
568 let desc = record(func, names, table, inst, row);
569 let bytes = konst(func, inst, Imm::int(i128::from(size), word), word);
570 let params = &[Type::PTR, word, Type::PTR];
571 call(func, names, inst, "__rucc_check_race", params, &[], &[pointer, bytes, desc]);
572}
573
574fn tag(clique: u16, base: u16) -> u32 {
581 (u32::from(clique) << 16) | u32::from(base)
582}
583
584fn promised(
596 func: &mut Func,
597 names: &mut Interner,
598 word: Type,
599 table: &mut Vec<Descriptor>,
600 inst: Inst,
601 write: bool,
602) {
603 let [pointer] = func[func[inst].args] else { return };
604 let Extra::Mem(mem) = func[inst].extra else { return };
605 let size = func[mem].size;
606 let named = func[mem].restrict;
607
608 let row = Descriptor {
609 judgement: RESTRICT,
610 class: 0,
611 size: u16::try_from(size).unwrap_or(u16::MAX),
613 };
614 let desc = record(func, names, table, inst, row);
615 let bytes = konst(func, inst, Imm::int(i128::from(size), word), word);
616 let small = Type::int(32);
617 let which =
618 konst(func, inst, Imm::int(i128::from(tag(named.clique, named.base)), small), small);
619 let wrote = konst(func, inst, Imm::int(i128::from(u8::from(write)), small), small);
620 let params = &[Type::PTR, word, small, small, Type::PTR];
621 let args = &[pointer, bytes, which, wrote, desc];
622 call(func, names, inst, "__rucc_check_restrict", params, &[], args);
623}
624
625fn opened(func: &mut Func, names: &mut Interner, inst: Inst) {
632 let [scope] = func[func[inst].args] else { return };
633 let Extra::Mem(mem) = func[inst].extra else { return };
634 let named = func[mem].restrict;
635 let small = Type::int(32);
636 let which =
637 konst(func, inst, Imm::int(i128::from(tag(named.clique, named.base)), small), small);
638 call(func, names, inst, "__rucc_restrict_enter", &[Type::PTR, small], &[], &[scope, which]);
639}
640
641fn closed(func: &mut Func, names: &mut Interner, inst: Inst) {
646 let [scope] = func[func[inst].args] else { return };
647 call(func, names, inst, "__rucc_restrict_leave", &[Type::PTR], &[], &[scope]);
648}
649
650fn declared(func: &mut Func, inst: Inst) {
663 func.remove_inst(inst);
664}
665
666fn judgement(
677 func: &mut Func,
678 names: &mut Interner,
679 word: Type,
680 numbers: &HashMap<Meta, u32>,
681 inst: Inst,
682) {
683 let [pointer, length] = func[func[inst].args] else { return };
684 let Extra::Node(node) = func[inst].extra else { return };
685 let Some(&number) = numbers.get(&node) else { return };
686 let bytes = fitted(func, inst, length, word);
687 let small = Type::int(32);
688 let ty = konst(func, inst, Imm::int(i128::from(number), small), small);
689 let params = &[Type::PTR, word, small];
690 call(func, names, inst, "__rucc_meta_type", params, &[], &[pointer, bytes, ty]);
691}
692
693fn carriage(func: &mut Func, names: &mut Interner, word: Type, inst: Inst) {
700 let [to, from, length] = func[func[inst].args] else { return };
701 let bytes = fitted(func, inst, length, word);
702 let params = &[Type::PTR, Type::PTR, word];
703 call(func, names, inst, "__rucc_meta_type_copy", params, &[], &[to, from, bytes]);
704}
705
706fn written(func: &mut Func, names: &mut Interner, word: Type, inst: Inst) {
712 let [pointer, length] = func[func[inst].args] else { return };
713 let bytes = fitted(func, inst, length, word);
714 let params = &[Type::PTR, word];
715 call(func, names, inst, "__rucc_meta_init", params, &[], &[pointer, bytes]);
716}
717
718fn carried(func: &mut Func, names: &mut Interner, word: Type, inst: Inst) {
724 let [to, from, length] = func[func[inst].args] else { return };
725 let bytes = fitted(func, inst, length, word);
726 let params = &[Type::PTR, Type::PTR, word];
727 call(func, names, inst, "__rucc_meta_init_copy", params, &[], &[to, from, bytes]);
728}
729
730fn relocated(func: &mut Func, names: &mut Interner, word: Type, inst: Inst) {
737 let [to, from, length] = func[func[inst].args] else { return };
738 let bytes = fitted(func, inst, length, word);
739 let params = &[Type::PTR, Type::PTR, word];
740 call(func, names, inst, "__rucc_cap_copy", params, &[], &[to, from, bytes]);
741}
742
743fn stamped(func: &mut Func, names: &mut Interner, word: Type, inst: Inst) {
749 let [pointer, length] = func[func[inst].args] else { return };
750 let bytes = fitted(func, inst, length, word);
751 let params = &[Type::PTR, word];
752 call(func, names, inst, "__rucc_meta_epoch", params, &[], &[pointer, bytes]);
753}
754
755fn published(func: &mut Func, names: &mut Interner, inst: Inst) {
763 let [object] = func[func[inst].args] else { return };
764 call(func, names, inst, "__rucc_meta_release", &[Type::PTR], &[], &[object]);
765}
766
767fn taken(func: &mut Func, names: &mut Interner, inst: Inst) {
771 let [object] = func[func[inst].args] else { return };
772 call(func, names, inst, "__rucc_meta_acquire", &[Type::PTR], &[], &[object]);
773}
774
775fn published_everywhere(func: &mut Func, names: &mut Interner, inst: Inst) {
781 call(func, names, inst, "__rucc_meta_fence_release", &[], &[], &[]);
782}
783
784fn taken_everywhere(func: &mut Func, names: &mut Interner, inst: Inst) {
788 call(func, names, inst, "__rucc_meta_fence_acquire", &[], &[], &[]);
789}
790
791fn extent(func: &mut Func, names: &mut Interner, word: Type, inst: Inst, called: &str) {
803 let [_capability, address, want] = func[func[inst].args] else { return };
804 let asked = fitted(func, inst, want, word);
805 let result = func[inst].results().next().expect("an extent query produces one value");
806 let ty = func[result].ty;
807 let params = &[Type::PTR, word];
808 if ty == word {
809 call(func, names, inst, called, params, &[word], &[address, asked]);
810 return;
811 }
812 let made = calling(func, names, called, params, &[word], &[address, asked]);
817 let holder = func.create_inst(made, &[word], func.span(inst));
818 func.insert_before(holder, inst);
819 let got = func[holder].results().next().expect("a call returning one value produces one");
820 let opcode = if word.bits() > ty.bits() { Opcode::Trunc } else { Opcode::ZExt };
821 let args = func.push_values(&[got]);
822 func[inst] = InstData { args, ..InstData::new(opcode) };
823}
824
825fn record(
831 func: &mut Func,
832 names: &mut Interner,
833 table: &mut Vec<Descriptor>,
834 inst: Inst,
835 row: Descriptor,
836) -> Value {
837 let name = names.intern(&label(table.len()));
838 table.push(row);
839 let span = func.span(inst);
840 let data = InstData { extra: Extra::Symbol(name), ..InstData::new(Opcode::GlobalAddr) };
841 let made = func.create_inst(data, &[Type::PTR], span);
842 func.insert_before(made, inst);
843 func[made].results().next().expect("an address created with one result has one")
844}
845
846fn label(index: usize) -> String {
848 format!("{DESCRIPTOR}_{index}")
849}
850
851fn konst(func: &mut Func, inst: Inst, imm: Imm, ty: Type) -> Value {
853 let span = func.span(inst);
854 let extra = Extra::Imm(func.add_imm(imm));
855 let made = func.create_inst(InstData { extra, ..InstData::new(Opcode::IConst) }, &[ty], span);
856 func.insert_before(made, inst);
857 func[made].results().next().expect("a constant created with one result has one")
858}
859
860pub(crate) fn call(
866 func: &mut Func,
867 names: &mut Interner,
868 inst: Inst,
869 routine: &str,
870 params: &[Type],
871 returns: &[Type],
872 args: &[Value],
873) {
874 let made = calling(func, names, routine, params, returns, args);
875 let data = &mut func[inst];
876 data.opcode = made.opcode;
877 data.args = made.args;
878 data.extra = made.extra;
879 data.flags = data.flags.intersection(Flags::legal_on(Opcode::Call));
880}
881
882pub(crate) fn calling(
890 func: &mut Func,
891 names: &mut Interner,
892 routine: &str,
893 params: &[Type],
894 returns: &[Type],
895 args: &[Value],
896) -> InstData {
897 let sig = func.add_signature(Signature::new().with_params(params).with_returns(returns));
898 let callee = names.intern(routine);
899 let varargs = func.push_abis(&[]);
902 let info = func.add_call(CallInfo { callee: Some(callee), signature: sig, varargs });
903 let args = func.push_values(args);
904 InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::Call) }
905}
906
907fn emit(module: &mut Module, names: &mut Interner, index: usize, row: Descriptor) {
914 let byte = Type::int(8);
915 let half = Type::int(16);
916 let judgement = module.add_imm(Imm::int(i128::from(row.judgement), byte));
917 let class = module.add_imm(Imm::int(i128::from(row.class), byte));
918 let size = module.add_imm(Imm::int(i128::from(row.size), half));
919 let image = [
920 Datum::Scalar { ty: byte, value: judgement },
921 Datum::Scalar { ty: byte, value: class },
922 Datum::Scalar { ty: half, value: size },
923 Datum::Zero(4),
927 Datum::Zero(8),
928 ];
929 let init = module.push_data(&image);
930 let mut global = Global::new(names.intern(&label(index)), WIDTH, 8);
931 global.linkage = Linkage::Internal;
932 global.constant = true;
933 global.section = Some(names.intern(SECTION));
934 global.init = Some(init);
935 module.add_global(global);
936}
937
938#[cfg(test)]
939mod tests {
940 use rucc_ir::{
941 Builder, MemInfo, MemOrder, MetaNode, Restrict, RmwOp, TbaaNode, print_func, verify_func,
942 };
943 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
944
945 use super::*;
946 use crate::{Plane, Promise, Races, Subobject, insert};
947
948 fn target() -> TargetInfo {
949 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
950 }
951
952 fn checked(names: &mut Interner) -> Module {
954 let i32_ = Type::int(32);
955 let mut func = Func::new(
956 names.intern("read"),
957 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
958 );
959 let entry = func.create_block();
960 let p = func.append_param(entry, Type::PTR);
961
962 let info = MemInfo {
963 size: 4,
964 align: 4,
965 order: MemOrder::NotAtomic,
966 tbaa: None,
967 owns: 0,
968 restrict: Restrict::NONE,
969 };
970 let mut b = Builder::new(&mut func, entry);
971 let args = b.func().push_values(&[p]);
972 let extra = Extra::Mem(b.func().add_mem(info));
973 let loaded = b.value(InstData { args, extra, ..InstData::new(Opcode::Load) }, i32_);
974 b.ret(&[loaded]);
975
976 insert(&mut func, &planeless(names).0, 8, Subobject::Off, Promise::Off, Races::Off);
977 let mut module = Module::new(names.intern("read.c"), &target());
978 module.add_func(func);
979 module
980 }
981
982 fn unaligned(names: &mut Interner) -> Module {
985 let i32_ = Type::int(32);
986 let mut func = Func::new(
987 names.intern("read"),
988 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
989 );
990 let entry = func.create_block();
991 let p = func.append_param(entry, Type::PTR);
992
993 let info = MemInfo {
994 size: 4,
995 align: 1,
996 order: MemOrder::NotAtomic,
997 tbaa: None,
998 owns: 0,
999 restrict: Restrict::NONE,
1000 };
1001 let mut b = Builder::new(&mut func, entry);
1002 let args = b.func().push_values(&[p]);
1003 let extra = Extra::Mem(b.func().add_mem(info));
1004 let loaded = b.value(InstData { args, extra, ..InstData::new(Opcode::Load) }, i32_);
1005 b.ret(&[loaded]);
1006
1007 insert(&mut func, &planeless(names).0, 8, Subobject::Off, Promise::Off, Races::Off);
1008 let mut module = Module::new(names.intern("read.c"), &target());
1009 module.add_func(func);
1010 module
1011 }
1012
1013 fn planeless(names: &mut Interner) -> (Plane, HashMap<Meta, u32>) {
1019 let mut module = Module::new(names.intern("planeless.c"), &target());
1020 let plane = Plane::build(&mut module);
1021 let numbers = plane::numbers(&module, names);
1022 (plane, numbers)
1023 }
1024
1025 fn copied(names: &mut Interner) -> Module {
1027 let mut func =
1028 Func::new(names.intern("move"), Signature::new().with_params(&[Type::PTR, Type::PTR]));
1029 let entry = func.create_block();
1030 let to = func.append_param(entry, Type::PTR);
1031 let from = func.append_param(entry, Type::PTR);
1032
1033 let info = MemInfo {
1034 size: 24,
1035 align: 8,
1036 order: MemOrder::NotAtomic,
1037 tbaa: None,
1038 owns: 0,
1039 restrict: Restrict::NONE,
1040 };
1041 let mut b = Builder::new(&mut func, entry);
1042 let args = b.func().push_values(&[to, from]);
1043 let extra = Extra::Mem(b.func().add_mem(info));
1044 b.inst(InstData { args, extra, ..InstData::new(Opcode::Memcpy) }, &[]);
1045 b.ret(&[]);
1046
1047 insert(&mut func, &planeless(names).0, 8, Subobject::Off, Promise::Off, Races::Off);
1048 let mut module = Module::new(names.intern("move.c"), &target());
1049 module.add_func(func);
1050 module
1051 }
1052
1053 fn stored(names: &mut Interner) -> Module {
1059 let mut module = Module::new(names.intern("write.c"), &target());
1060 let plane = Plane::build(&mut module);
1061
1062 let i64_ = Type::int(64);
1063 let mut func =
1064 Func::new(names.intern("write"), Signature::new().with_params(&[Type::PTR, i64_]));
1065 let entry = func.create_block();
1066 let p = func.append_param(entry, Type::PTR);
1067 let v = func.append_param(entry, i64_);
1068
1069 let info = MemInfo {
1070 size: 8,
1071 align: 8,
1072 order: MemOrder::NotAtomic,
1073 tbaa: None,
1074 owns: 0,
1075 restrict: Restrict::NONE,
1076 };
1077 let mut b = Builder::new(&mut func, entry);
1078 let args = b.func().push_values(&[v, p]);
1079 let extra = Extra::Mem(b.func().add_mem(info));
1080 b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
1081 b.ret(&[]);
1082
1083 insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
1084 module.add_func(func);
1085 module
1086 }
1087
1088 fn racing(names: &mut Interner) -> Module {
1094 let mut module = Module::new(names.intern("stamp.c"), &target());
1095 let plane = Plane::build(&mut module);
1096
1097 let mut func =
1098 Func::new(names.intern("stamp"), Signature::new().with_params(&[Type::PTR, Type::PTR]));
1099 let entry = func.create_block();
1100 let p = func.append_param(entry, Type::PTR);
1101 let q = func.append_param(entry, Type::PTR);
1102
1103 let info = MemInfo {
1104 size: 8,
1105 align: 8,
1106 order: MemOrder::NotAtomic,
1107 tbaa: None,
1108 owns: 0,
1109 restrict: Restrict::NONE,
1110 };
1111 let mut b = Builder::new(&mut func, entry);
1112 let args = b.func().push_values(&[q, p]);
1113 let extra = Extra::Mem(b.func().add_mem(info));
1114 b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
1115 b.ret(&[]);
1116
1117 insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Metadata);
1118 module.add_func(func);
1119 module
1120 }
1121
1122 fn ordering(names: &mut Interner) -> Module {
1127 let mut module = Module::new(names.intern("edge.c"), &target());
1128 let plane = Plane::build(&mut module);
1129
1130 let i64_ = Type::int(64);
1131 let mut func =
1132 Func::new(names.intern("publish"), Signature::new().with_params(&[Type::PTR, i64_]));
1133 let entry = func.create_block();
1134 let p = func.append_param(entry, Type::PTR);
1135 let v = func.append_param(entry, i64_);
1136
1137 let info = MemInfo {
1138 size: 8,
1139 align: 8,
1140 order: MemOrder::SeqCst,
1141 tbaa: None,
1142 owns: 0,
1143 restrict: Restrict::NONE,
1144 };
1145 let mut b = Builder::new(&mut func, entry);
1146 let at = b.func().add_mem(info);
1147 let args = b.func().push_values(&[p, v]);
1148 let extra = Extra::Rmw(RmwOp::Add, at);
1149 b.inst(InstData { args, extra, ..InstData::new(Opcode::AtomicRmw) }, &[i64_]);
1150 b.ret(&[]);
1151
1152 insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Metadata);
1153 module.add_func(func);
1154 module
1155 }
1156
1157 #[test]
1158 fn the_two_halves_of_an_edge_become_the_calls_the_interposed_locks_already_make() {
1159 let mut names = Interner::new();
1165 let mut module = ordering(&mut names);
1166 lower(&mut module, &mut names);
1167
1168 let id = module.funcs().next().expect("the module has one function");
1169 let printed = print_func(&module, &module[id], &names);
1170 assert!(printed.contains("call @__rucc_meta_release(%0) : (ptr)\n"), "{printed}");
1171 assert!(printed.contains("call @__rucc_meta_acquire(%0) : (ptr)\n"), "{printed}");
1172
1173 let published = printed.find("__rucc_meta_release").expect("the publishing half lowered");
1175 let changed = printed.find("atomic_rmw").expect("the atomic is still there");
1176 let took = printed.find("__rucc_meta_acquire").expect("and the taking half lowered");
1177 assert!(published < changed && changed < took, "{printed}");
1178
1179 if let Err(errors) = verify_func(&module, &module[id], &names) {
1180 panic!("that was expected to be believed: {errors:#?}");
1181 }
1182 }
1183
1184 fn barrier(names: &mut Interner) -> Module {
1186 let mut module = Module::new(names.intern("fence.c"), &target());
1187 let plane = Plane::build(&mut module);
1188
1189 let mut func = Func::new(names.intern("barrier"), Signature::new());
1190 let entry = func.create_block();
1191 let mut b = Builder::new(&mut func, entry);
1192 let extra = Extra::Order(MemOrder::SeqCst);
1193 b.inst(InstData { extra, ..InstData::new(Opcode::Fence) }, &[]);
1194 b.ret(&[]);
1195
1196 insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Metadata);
1197 module.add_func(func);
1198 module
1199 }
1200
1201 #[test]
1202 fn the_edge_a_fence_carries_becomes_a_call_that_takes_nothing_at_all() {
1203 let mut names = Interner::new();
1207 let mut module = barrier(&mut names);
1208 lower(&mut module, &mut names);
1209
1210 let id = module.funcs().next().expect("the module has one function");
1211 let printed = print_func(&module, &module[id], &names);
1212 assert!(printed.contains("call @__rucc_meta_fence_release() : ()\n"), "{printed}");
1213 assert!(printed.contains("call @__rucc_meta_fence_acquire() : ()\n"), "{printed}");
1214
1215 let published = printed.find("fence_release").expect("the publishing half lowered");
1216 let barrier = printed.find(" fence ").expect("the fence is still there");
1217 let took = printed.find("fence_acquire").expect("and the taking half lowered");
1218 assert!(published < barrier && barrier < took, "{printed}");
1219
1220 if let Err(errors) = verify_func(&module, &module[id], &names) {
1221 panic!("that was expected to be believed: {errors:#?}");
1222 }
1223 }
1224
1225 fn asking_the_plane(names: &mut Interner) -> Module {
1231 let mut module = Module::new(names.intern("read.c"), &target());
1232 let root = names.intern("char");
1233 let root =
1234 module.add_meta(MetaNode::Tbaa(TbaaNode { name: root, parent: None, offset: 0 }));
1235 let int = names.intern("int");
1236 let int =
1237 module.add_meta(MetaNode::Tbaa(TbaaNode { name: int, parent: Some(root), offset: 0 }));
1238 let plane = Plane::build(&mut module);
1239
1240 let i32_ = Type::int(32);
1241 let mut func = Func::new(
1242 names.intern("read"),
1243 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
1244 );
1245 let entry = func.create_block();
1246 let p = func.append_param(entry, Type::PTR);
1247 let info = MemInfo {
1248 size: 4,
1249 align: 4,
1250 order: MemOrder::NotAtomic,
1251 tbaa: Some(int),
1252 owns: 0,
1253 restrict: Restrict::NONE,
1254 };
1255 let mut b = Builder::new(&mut func, entry);
1256 let args = b.func().push_values(&[p]);
1257 let extra = Extra::Mem(b.func().add_mem(info));
1258 let loaded = b.value(InstData { args, extra, ..InstData::new(Opcode::Load) }, i32_);
1259 b.ret(&[loaded]);
1260
1261 insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
1262 module.add_func(func);
1263 module
1264 }
1265
1266 fn marker(b: &mut Builder<'_>, opcode: Opcode, info: Option<MemInfo>, on: &[Value]) {
1268 let args = b.func().push_values(on);
1269 let extra = match info {
1270 Some(info) => Extra::Mem(b.func().add_mem(info)),
1271 None => Extra::None,
1272 };
1273 b.inst(InstData { args, extra, ..InstData::new(opcode) }, &[]);
1274 }
1275
1276 fn promising(names: &mut Interner) -> Module {
1282 let i32_ = Type::int(32);
1283 let mut func = Func::new(
1284 names.intern("kernel"),
1285 Signature::new().with_params(&[Type::PTR, Type::PTR]),
1286 );
1287 let entry = func.create_block();
1288 let to = func.append_param(entry, Type::PTR);
1289 let from = func.append_param(entry, Type::PTR);
1290
1291 let empty = MemInfo {
1292 size: 0,
1293 align: 1,
1294 order: MemOrder::NotAtomic,
1295 tbaa: None,
1296 owns: 0,
1297 restrict: Restrict::NONE,
1298 };
1299 let slot = MemInfo { size: 112, align: 8, ..empty };
1301 let mut b = Builder::new(&mut func, entry);
1302 let extra = Extra::Mem(b.func().add_mem(slot));
1303 let scope = b.value(InstData { extra, ..InstData::new(Opcode::Alloca) }, Type::PTR);
1304
1305 let read =
1307 MemInfo { size: 4, align: 4, restrict: Restrict { clique: 1, base: 2 }, ..empty };
1308 let writ =
1309 MemInfo { size: 4, align: 4, restrict: Restrict { clique: 1, base: 1 }, ..empty };
1310 let opening = MemInfo { restrict: Restrict { clique: 1, base: 2 }, ..slot };
1311 marker(&mut b, Opcode::RestrictEnter, Some(opening), &[scope]);
1312 marker(&mut b, Opcode::CheckRestrictRead, Some(read), &[from]);
1313 let args = b.func().push_values(&[from]);
1314 let extra = Extra::Mem(b.func().add_mem(read));
1315 let loaded = b.value(InstData { args, extra, ..InstData::new(Opcode::Load) }, i32_);
1316 marker(&mut b, Opcode::CheckRestrictWrite, Some(writ), &[to]);
1317 let args = b.func().push_values(&[loaded, to]);
1318 let extra = Extra::Mem(b.func().add_mem(writ));
1319 b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
1320 marker(&mut b, Opcode::RestrictLeave, None, &[scope]);
1321 b.ret(&[]);
1322
1323 let mut module = Module::new(names.intern("kernel.c"), &target());
1324 module.add_func(func);
1325 module
1326 }
1327
1328 #[test]
1329 fn a_restrict_check_becomes_the_call_that_says_which_pointer_reached_where() {
1330 let mut names = Interner::new();
1334 let mut module = promising(&mut names);
1335 assert_eq!(lower(&mut module, &mut names), 2);
1336
1337 let id = module.funcs().next().expect("the module has one function");
1338 assert_eq!(
1339 print_func(&module, &module[id], &names),
1340 "func @kernel(ptr, ptr), linkage(external) {\n\
1341 block0(%0: ptr, %1: ptr):\n \
1342 %2 = alloca, size 112, align 8\n \
1343 %3 = iconst.i32 65538\n \
1344 call @__rucc_restrict_enter(%2, %3) : (ptr, i32)\n \
1345 %4 = global_addr @__rucc_safety_desc_0\n \
1346 %5 = iconst.i64 4\n \
1347 %6 = iconst.i32 65538\n \
1348 %7 = iconst.i32 0\n \
1349 call @__rucc_check_restrict(%1, %5, %6, %7, %4) : (ptr, i64, i32, i32, ptr)\n \
1350 %8 = load.i32 %1, size 4, align 4, restrict(1, 2)\n \
1351 %9 = global_addr @__rucc_safety_desc_1\n \
1352 %10 = iconst.i64 4\n \
1353 %11 = iconst.i32 65537\n \
1354 %12 = iconst.i32 1\n \
1355 call @__rucc_check_restrict(%0, %10, %11, %12, %9) : (ptr, i64, i32, i32, ptr)\n \
1356 store %8 -> %0, size 4, align 4, restrict(1, 1)\n \
1357 call @__rucc_restrict_leave(%2) : (ptr)\n \
1358 return\n\
1359 }\n"
1360 );
1361
1362 if let Err(errors) = verify_func(&module, &module[id], &names) {
1363 panic!("that was expected to be believed: {errors:#?}");
1364 }
1365 }
1366
1367 #[test]
1368 fn the_judgement_a_restrict_check_names_is_the_one_about_the_pair() {
1369 let mut names = Interner::new();
1373 let mut module = promising(&mut names);
1374 lower(&mut module, &mut names);
1375
1376 let rows: Vec<u8> = module
1377 .globals()
1378 .map(|id| {
1379 let init = module[id].init.expect("a descriptor is a definition");
1380 match module[init][0] {
1381 Datum::Scalar { value, .. } => {
1382 u8::try_from(module[value].bits()).expect("a judgement is one byte")
1383 }
1384 _ => panic!("a descriptor starts with its judgement"),
1385 }
1386 })
1387 .collect();
1388 assert_eq!(rows, [RESTRICT, RESTRICT]);
1389 }
1390
1391 #[test]
1392 fn the_two_numbers_are_packed_the_way_the_runtime_unpacks_them() {
1393 assert_eq!(tag(1, 2), 0x0001_0002);
1398 assert_eq!(tag(0xffff, 0xffff), u32::MAX);
1399 assert_eq!(tag(0, 0), 0);
1400 }
1401
1402 #[test]
1403 fn a_read_of_the_plane_becomes_the_call_that_carries_the_type_asked_about() {
1404 let mut names = Interner::new();
1410 let mut module = asking_the_plane(&mut names);
1411 assert_eq!(lower(&mut module, &mut names), 3);
1412
1413 let number = i32::from_ne_bytes(plane::identifier("int").to_ne_bytes());
1416 let id = module.funcs().next().expect("the module has one function");
1417 assert_eq!(
1418 print_func(&module, &module[id], &names),
1419 format!(
1420 "func @read(ptr) -> i32, linkage(external) {{\n\
1421 block0(%0: ptr):\n \
1422 %1 = alloca, size 32, align 8\n \
1423 call @__rucc_cap_recover(%1, %0) : (ptr, ptr)\n \
1424 %2 = global_addr @__rucc_safety_desc_0\n \
1425 %3 = iconst.i64 4\n \
1426 %4 = iconst.i64 4\n \
1427 call @__rucc_check_bounds(%0, %3, %4, %1, %2) : (ptr, i64, i64, ptr, ptr)\n \
1428 %5 = global_addr @__rucc_safety_desc_1\n \
1429 call @__rucc_check_live(%1, %0, %5) : (ptr, ptr, ptr)\n \
1430 %6 = global_addr @__rucc_safety_desc_2\n \
1431 %7 = iconst.i64 4\n \
1432 %8 = iconst.i32 {number}\n \
1433 call @__rucc_check_typed_init(%0, %7, %8, %6) : (ptr, i64, i32, ptr)\n \
1434 %9 = load.i32 %0, size 4, align 4, tbaa !1\n \
1435 return %9\n\
1436 }}\n"
1437 )
1438 );
1439
1440 if let Err(errors) = verify_func(&module, &module[id], &names) {
1441 panic!("that was expected to be believed: {errors:#?}");
1442 }
1443 }
1444
1445 enum Between {
1447 Nothing,
1449 Wider,
1451 Store,
1453 Another,
1455 }
1456
1457 fn plane_checks(names: &mut Interner, between: Between) -> (Func, Inst) {
1464 let mut func = Func::new(names.intern("read"), Signature::new().with_params(&[Type::PTR]));
1465 let entry = func.create_block();
1466 let p = func.append_param(entry, Type::PTR);
1467 let info = MemInfo {
1468 size: 4,
1469 align: 4,
1470 order: MemOrder::NotAtomic,
1471 tbaa: None,
1472 owns: 0,
1473 restrict: Restrict::NONE,
1474 };
1475
1476 let mut b = Builder::new(&mut func, entry);
1477 let args = b.func().push_values(&[p]);
1478 let cap = b.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1479 marker(&mut b, Opcode::CheckType, Some(info), &[cap, p]);
1480 let check = b.func().insts(entry).last().expect("the type check was just put in");
1481 match between {
1482 Between::Nothing | Between::Wider => {}
1483 Between::Store => {
1484 let zero = b.iconst(Type::int(32), 0);
1485 marker(&mut b, Opcode::Store, Some(info), &[zero, p]);
1486 }
1487 Between::Another => marker(&mut b, Opcode::CheckType, Some(info), &[cap, p]),
1488 }
1489 let asked = match between {
1490 Between::Wider => MemInfo { size: 8, ..info },
1491 _ => info,
1492 };
1493 marker(&mut b, Opcode::CheckInit, Some(asked), &[cap, p]);
1494 b.ret(&[]);
1495 (func, check)
1496 }
1497
1498 #[test]
1499 fn the_init_check_beside_a_type_check_belongs_to_the_same_read() {
1500 let mut names = Interner::new();
1501 let (func, check) = plane_checks(&mut names, Between::Nothing);
1502 let found = partner(&func, check).expect("the two are one read's");
1503 assert_eq!(func[found].opcode, Opcode::CheckInit);
1504 }
1505
1506 #[test]
1507 fn an_init_check_over_other_bytes_than_the_type_check_asked_about_is_not_its_partner() {
1508 let mut names = Interner::new();
1511 let (func, check) = plane_checks(&mut names, Between::Wider);
1512 assert!(partner(&func, check).is_none());
1513 }
1514
1515 #[test]
1516 fn a_store_between_the_two_plane_checks_keeps_them_apart() {
1517 let mut names = Interner::new();
1520 let (func, check) = plane_checks(&mut names, Between::Store);
1521 assert!(partner(&func, check).is_none());
1522 }
1523
1524 #[test]
1525 fn the_init_check_of_a_later_read_is_not_an_earlier_reads_partner() {
1526 let mut names = Interner::new();
1530 let (func, check) = plane_checks(&mut names, Between::Another);
1531 assert!(partner(&func, check).is_none());
1532 }
1533
1534 #[test]
1535 fn the_judgement_a_type_check_names_is_the_one_about_the_planes() {
1536 let mut names = Interner::new();
1542 let mut module = asking_the_plane(&mut names);
1543 lower(&mut module, &mut names);
1544
1545 let rows: Vec<u8> = module
1546 .globals()
1547 .map(|id| {
1548 let init = module[id].init.expect("a descriptor is a definition");
1549 match module[init][0] {
1550 Datum::Scalar { value, .. } => {
1551 u8::try_from(module[value].bits()).expect("a judgement is one byte")
1552 }
1553 _ => panic!("a descriptor starts with its judgement"),
1554 }
1555 })
1556 .collect();
1557 assert_eq!(rows, [ACCESS, ACCESS, ACCESS]);
1558 }
1559
1560 #[test]
1561 fn a_store_becomes_the_calls_that_record_what_it_wrote() {
1562 let mut names = Interner::new();
1567 let mut module = stored(&mut names);
1568 assert_eq!(lower(&mut module, &mut names), 2);
1569
1570 let id = module.funcs().next().expect("the module has one function");
1571 let printed = print_func(&module, &module[id], &names);
1572 assert!(
1573 printed.contains("call @__rucc_meta_type(%0, %7, %8) : (ptr, i64, i32)\n"),
1574 "{printed}"
1575 );
1576 assert!(printed.contains("call @__rucc_meta_init(%0, %9) : (ptr, i64)\n"), "{printed}");
1577
1578 if let Err(errors) = verify_func(&module, &module[id], &names) {
1579 panic!("that was expected to be believed: {errors:#?}");
1580 }
1581 }
1582
1583 #[test]
1584 fn a_store_of_a_pointer_becomes_the_call_that_says_which_thread_wrote_it() {
1585 let mut names = Interner::new();
1590 let mut module = racing(&mut names);
1591 lower(&mut module, &mut names);
1592
1593 let id = module.funcs().next().expect("the module has one function");
1594 let printed = print_func(&module, &module[id], &names);
1595 assert!(printed.contains("call @__rucc_meta_epoch(%0, %"), "{printed}");
1596 assert!(printed.contains(") : (ptr, i64)\n"), "{printed}");
1597
1598 if let Err(errors) = verify_func(&module, &module[id], &names) {
1599 panic!("that was expected to be believed: {errors:#?}");
1600 }
1601 }
1602
1603 #[test]
1604 fn a_store_of_a_pointer_also_becomes_the_call_that_asks_who_was_there_first() {
1605 let mut names = Interner::new();
1610 let mut module = racing(&mut names);
1611 lower(&mut module, &mut names);
1612
1613 let id = module.funcs().next().expect("the module has one function");
1614 let printed = print_func(&module, &module[id], &names);
1615 assert!(printed.contains("call @__rucc_check_race(%0, %"), "{printed}");
1616 assert!(printed.contains(") : (ptr, i64, ptr)\n"), "{printed}");
1617
1618 let asked = printed.find("__rucc_check_race").expect("the check lowered");
1622 let stamp = printed.find("__rucc_meta_epoch").expect("so did the recording");
1623 assert!(asked < stamp, "{printed}");
1624
1625 let rows: Vec<u8> = module
1626 .globals()
1627 .map(|id| {
1628 let init = module[id].init.expect("a descriptor is a definition");
1629 match module[init][0] {
1630 Datum::Scalar { value, .. } => {
1631 u8::try_from(module[value].bits()).expect("a judgement is one byte")
1632 }
1633 _ => panic!("a descriptor starts with its judgement"),
1634 }
1635 })
1636 .collect();
1637 assert_eq!(rows, [ACCESS, ACCESS, RACE]);
1638
1639 if let Err(errors) = verify_func(&module, &module[id], &names) {
1640 panic!("that was expected to be believed: {errors:#?}");
1641 }
1642 }
1643
1644 #[test]
1645 fn a_copy_becomes_the_calls_that_move_the_planes_across() {
1646 let mut names = Interner::new();
1656 let mut module = copied(&mut names);
1657 assert_eq!(lower(&mut module, &mut names), 4);
1658
1659 let id = module.funcs().next().expect("the module has one function");
1660 assert_eq!(
1661 print_func(&module, &module[id], &names),
1662 "func @move(ptr, ptr), linkage(external) {\n\
1663 block0(%0: ptr, %1: ptr):\n \
1664 %2 = alloca, size 32, align 8\n \
1665 %3 = alloca, size 32, align 8\n \
1666 call @__rucc_cap_recover(%3, %1) : (ptr, ptr)\n \
1667 call @__rucc_cap_recover(%2, %0) : (ptr, ptr)\n \
1668 %4 = global_addr @__rucc_safety_desc_0\n \
1669 %5 = iconst.i64 24\n \
1670 %6 = iconst.i64 8\n \
1671 call @__rucc_check_bounds(%0, %5, %6, %2, %4) : (ptr, i64, i64, ptr, ptr)\n \
1672 %7 = global_addr @__rucc_safety_desc_1\n \
1673 call @__rucc_check_live(%2, %0, %7) : (ptr, ptr, ptr)\n \
1674 %8 = global_addr @__rucc_safety_desc_2\n \
1675 %9 = iconst.i64 24\n \
1676 %10 = iconst.i64 8\n \
1677 call @__rucc_check_bounds(%1, %9, %10, %3, %8) : (ptr, i64, i64, ptr, ptr)\n \
1678 %11 = global_addr @__rucc_safety_desc_3\n \
1679 call @__rucc_check_live(%3, %1, %11) : (ptr, ptr, ptr)\n \
1680 memcpy %0, %1, size 24, align 8\n \
1681 %12 = iconst.i64 24\n \
1682 call @__rucc_meta_type_copy(%0, %1, %12) : (ptr, ptr, i64)\n \
1683 %13 = iconst.i64 24\n \
1684 call @__rucc_meta_init_copy(%0, %1, %13) : (ptr, ptr, i64)\n \
1685 %14 = iconst.i64 24\n \
1686 call @__rucc_cap_copy(%0, %1, %14) : (ptr, ptr, i64)\n \
1687 return\n\
1688 }\n"
1689 );
1690
1691 if let Err(errors) = verify_func(&module, &module[id], &names) {
1692 panic!("that was expected to be believed: {errors:#?}");
1693 }
1694 }
1695
1696 #[test]
1703 fn the_alignment_that_goes_through_is_the_one_the_access_may_assume() {
1704 let mut names = Interner::new();
1705 let mut module = unaligned(&mut names);
1706 assert_eq!(lower(&mut module, &mut names), 3);
1707
1708 let id = module.funcs().next().expect("the module has one function");
1709 let printed = print_func(&module, &module[id], &names);
1710 assert!(printed.contains("%3 = iconst.i64 4\n"), "{printed}");
1711 assert!(printed.contains("%4 = iconst.i64 1\n"), "{printed}");
1712 assert!(
1713 printed.contains(
1714 "call @__rucc_check_bounds(%0, %3, %4, %1, %2) : (ptr, i64, i64, ptr, ptr)\n"
1715 ),
1716 "{printed}"
1717 );
1718 }
1719
1720 #[test]
1721 fn every_check_becomes_a_call_carrying_the_descriptor_it_is_described_by() {
1722 let mut names = Interner::new();
1723 let mut module = checked(&mut names);
1724 assert_eq!(lower(&mut module, &mut names), 3);
1725
1726 let id = module.funcs().next().expect("the module has one function");
1727 assert_eq!(
1728 print_func(&module, &module[id], &names),
1729 "func @read(ptr) -> i32, linkage(external) {\n\
1730 block0(%0: ptr):\n \
1731 %1 = alloca, size 32, align 8\n \
1732 call @__rucc_cap_recover(%1, %0) : (ptr, ptr)\n \
1733 %2 = global_addr @__rucc_safety_desc_0\n \
1734 %3 = iconst.i64 4\n \
1735 %4 = iconst.i64 4\n \
1736 call @__rucc_check_bounds(%0, %3, %4, %1, %2) : (ptr, i64, i64, ptr, ptr)\n \
1737 %5 = global_addr @__rucc_safety_desc_1\n \
1738 call @__rucc_check_live(%1, %0, %5) : (ptr, ptr, ptr)\n \
1739 %6 = global_addr @__rucc_safety_desc_2\n \
1740 %7 = iconst.i64 4\n \
1741 call @__rucc_check_init(%0, %7, %6) : (ptr, i64, ptr)\n \
1742 %8 = load.i32 %0, size 4, align 4\n \
1743 return %8\n\
1744 }\n"
1745 );
1746 }
1747
1748 #[test]
1749 fn the_capabilities_the_checks_were_reading_are_taken_out() {
1750 let mut names = Interner::new();
1753 let mut module = checked(&mut names);
1754 lower(&mut module, &mut names);
1755
1756 let id = module.funcs().next().expect("the module has one function");
1757 let func = &module[id];
1758 let left: Vec<Opcode> = func
1759 .blocks()
1760 .flat_map(|block| func.insts(block).collect::<Vec<_>>())
1761 .map(|inst| func[inst].opcode)
1762 .collect();
1763 assert!(!left.contains(&Opcode::CapOf), "{left:?}");
1764 }
1765
1766 #[test]
1767 fn what_it_produces_is_a_module_the_verifier_believes() {
1768 let mut names = Interner::new();
1769 let mut module = checked(&mut names);
1770 lower(&mut module, &mut names);
1771
1772 let id = module.funcs().next().expect("the module has one function");
1773 if let Err(errors) = verify_func(&module, &module[id], &names) {
1774 panic!("that was expected to be believed: {errors:#?}");
1775 }
1776 }
1777
1778 #[test]
1779 fn the_section_is_one_descriptor_per_check_and_nothing_else() {
1780 let mut names = Interner::new();
1785 let mut module = checked(&mut names);
1786 let rows = lower(&mut module, &mut names);
1787
1788 let globals: Vec<_> = module.globals().collect();
1789 assert_eq!(globals.len(), rows);
1790 for (index, id) in globals.iter().enumerate() {
1791 let desc = &module[*id];
1792 assert_eq!(names.resolve(desc.name), label(index));
1793 assert_eq!(
1794 names.resolve(desc.section.expect("a descriptor names its section")),
1795 SECTION
1796 );
1797 assert_eq!(desc.linkage, Linkage::Internal);
1798 assert!(desc.constant);
1799 assert_eq!(desc.align, 8);
1800 assert_eq!(desc.size, WIDTH);
1801
1802 let init = desc.init.expect("a descriptor is a definition");
1805 let written: u64 = module[init].iter().map(|datum| datum.size(&module)).sum();
1806 assert_eq!(written, WIDTH);
1807 }
1808 }
1809
1810 #[test]
1811 fn the_judgement_a_descriptor_names_is_the_one_the_check_decides() {
1812 let mut names = Interner::new();
1815 let mut func = Func::new(
1816 names.intern("walk"),
1817 Signature::new().with_params(&[Type::PTR, Type::int(64)]).with_returns(&[Type::PTR]),
1818 );
1819 let entry = func.create_block();
1820 let p = func.append_param(entry, Type::PTR);
1821 let n = func.append_param(entry, Type::int(64));
1822 let mut b = Builder::new(&mut func, entry);
1823 let args = b.func().push_values(&[p, n]);
1824 let moved = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1825 b.ret(&[moved]);
1826 let (plane, numbers) = planeless(&mut names);
1827 insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
1828
1829 let mut table = Vec::new();
1830 calls(&mut func, &mut names, Type::int(64), &numbers, &mut table);
1831 assert_eq!(table, [Descriptor { judgement: DERIVE, class: 0, size: 0 }]);
1832 }
1833
1834 #[test]
1835 fn a_check_over_a_length_the_program_worked_out_passes_that_length_along() {
1836 let mut names = Interner::new();
1840 let mut func = Func::new(
1841 names.intern("sweep"),
1842 Signature::new().with_params(&[Type::PTR, Type::int(64)]),
1843 );
1844 let entry = func.create_block();
1845 let p = func.append_param(entry, Type::PTR);
1846 let n = func.append_param(entry, Type::int(64));
1847 let info = MemInfo {
1848 size: 4,
1849 align: 4,
1850 order: MemOrder::NotAtomic,
1851 tbaa: None,
1852 owns: 0,
1853 restrict: Restrict::NONE,
1854 };
1855 let mut b = Builder::new(&mut func, entry);
1856 let of = b.unary(Opcode::CapOf, p, Type::CAP);
1857 let args = b.func().push_values(&[of, p, n]);
1858 let extra = Extra::Mem(b.func().add_mem(info));
1859 b.inst(InstData { args, extra, ..InstData::new(Opcode::CheckBounds) }, &[]);
1860 b.ret(&[]);
1861
1862 let mut table = Vec::new();
1863 let numbers = planeless(&mut names).1;
1864 calls(&mut func, &mut names, Type::int(64), &numbers, &mut table);
1865 assert_eq!(table, [Descriptor { judgement: ACCESS, class: 0, size: 0 }]);
1866
1867 let mut module = Module::new(names.intern("sweep.c"), &target());
1868 module.add_func(func);
1869 let id = module.funcs().next().expect("the module has one function");
1870 assert_eq!(
1871 print_func(&module, &module[id], &names),
1872 "func @sweep(ptr, i64), linkage(external) {\n\
1873 block0(%0: ptr, %1: i64):\n \
1874 %2 = alloca, size 32, align 8\n \
1875 call @__rucc_cap_recover(%2, %0) : (ptr, ptr)\n \
1876 %3 = global_addr @__rucc_safety_desc_0\n \
1877 %4 = iconst.i64 1\n \
1878 call @__rucc_check_bounds(%0, %1, %4, %2, %3) : (ptr, i64, i64, ptr, ptr)\n \
1879 return\n\
1880 }\n"
1881 );
1882 }
1883
1884 #[test]
1885 fn a_length_wider_than_the_word_is_cut_down_to_it() {
1886 let mut names = Interner::new();
1890 let mut func = Func::new(
1891 names.intern("sweep"),
1892 Signature::new().with_params(&[Type::PTR, Type::int(64)]),
1893 );
1894 let entry = func.create_block();
1895 let p = func.append_param(entry, Type::PTR);
1896 let n = func.append_param(entry, Type::int(64));
1897 let info = MemInfo {
1898 size: 4,
1899 align: 4,
1900 order: MemOrder::NotAtomic,
1901 tbaa: None,
1902 owns: 0,
1903 restrict: Restrict::NONE,
1904 };
1905 let mut b = Builder::new(&mut func, entry);
1906 let of = b.unary(Opcode::CapOf, p, Type::CAP);
1907 let args = b.func().push_values(&[of, p, n]);
1908 let extra = Extra::Mem(b.func().add_mem(info));
1909 b.inst(InstData { args, extra, ..InstData::new(Opcode::CheckBounds) }, &[]);
1910 b.ret(&[]);
1911
1912 let mut table = Vec::new();
1913 let numbers = planeless(&mut names).1;
1914 calls(&mut func, &mut names, Type::int(32), &numbers, &mut table);
1915 let opcodes: Vec<Opcode> = func
1916 .blocks()
1917 .flat_map(|block| func.insts(block).collect::<Vec<_>>())
1918 .map(|inst| func[inst].opcode)
1919 .collect();
1920 assert!(opcodes.contains(&Opcode::Trunc), "{opcodes:?}");
1921 }
1922
1923 fn asking(names: &mut Interner, ty: Type) -> Func {
1928 let mut func = Func::new(
1929 names.intern("cover"),
1930 Signature::new().with_params(&[Type::PTR, ty]).with_returns(&[ty]),
1931 );
1932 let entry = func.create_block();
1933 let p = func.append_param(entry, Type::PTR);
1934 let want = func.append_param(entry, ty);
1935 let mut b = Builder::new(&mut func, entry);
1936 let of = b.unary(Opcode::CapOf, p, Type::CAP);
1937 let args = b.func().push_values(&[of, p, want]);
1938 let got = b.value(InstData { args, ..InstData::new(Opcode::CapExtent) }, ty);
1939 b.ret(&[got]);
1940 func
1941 }
1942
1943 #[test]
1944 fn the_extent_query_becomes_a_call_that_carries_no_descriptor() {
1945 let mut names = Interner::new();
1948 let mut func = asking(&mut names, Type::int(64));
1949
1950 let mut table = Vec::new();
1951 let numbers = planeless(&mut names).1;
1952 calls(&mut func, &mut names, Type::int(64), &numbers, &mut table);
1953 assert!(table.is_empty(), "{table:?}");
1954
1955 let mut module = Module::new(names.intern("cover.c"), &target());
1956 module.add_func(func);
1957 let id = module.funcs().next().expect("the module has one function");
1958 assert_eq!(
1959 print_func(&module, &module[id], &names),
1960 "func @cover(ptr, i64) -> i64, linkage(external) {\n\
1961 block0(%0: ptr, %1: i64):\n \
1962 %2 = call @__rucc_extent(%0, %1) : (ptr, i64) -> i64\n \
1963 return %2\n\
1964 }\n"
1965 );
1966 if let Err(errors) = verify_func(&module, &module[id], &names) {
1967 panic!("that was expected to be believed: {errors:#?}");
1968 }
1969 }
1970
1971 #[test]
1972 fn an_extent_asked_for_in_a_width_the_target_does_not_have_is_converted_back() {
1973 let mut names = Interner::new();
1978 let mut func = asking(&mut names, Type::int(64));
1979
1980 let mut table = Vec::new();
1981 let numbers = planeless(&mut names).1;
1982 calls(&mut func, &mut names, Type::int(32), &numbers, &mut table);
1983 let opcodes: Vec<Opcode> = func
1984 .blocks()
1985 .flat_map(|block| func.insts(block).collect::<Vec<_>>())
1986 .map(|inst| func[inst].opcode)
1987 .collect();
1988 assert!(opcodes.contains(&Opcode::Trunc), "the limit goes in narrowed: {opcodes:?}");
1989 assert!(opcodes.contains(&Opcode::ZExt), "and the answer comes back widened: {opcodes:?}");
1990 assert!(
1991 !opcodes.contains(&Opcode::CapExtent),
1992 "with nothing left of the query: {opcodes:?}"
1993 );
1994 }
1995
1996 fn exempt(names: &mut Interner) -> Module {
1998 let reason = names.intern("hand written assembly, checked by review");
1999 let mut func = Func::new(names.intern("driver"), Signature::new());
2000 let entry = func.create_block();
2001 let mut b = Builder::new(&mut func, entry);
2002 b.inst(
2003 InstData { extra: Extra::Reason(reason), ..InstData::new(Opcode::SafeRegionBegin) },
2004 &[],
2005 );
2006 b.inst(InstData::new(Opcode::SafeRegionEnd), &[]);
2007 b.ret(&[]);
2008 let mut module = Module::new(names.intern("driver.c"), &target());
2009 module.add_func(func);
2010 module
2011 }
2012
2013 #[test]
2014 fn the_markers_around_a_declared_region_lower_into_nothing_at_all() {
2015 let mut names = Interner::new();
2019 let mut module = exempt(&mut names);
2020 assert_eq!(lower(&mut module, &mut names), 0);
2021
2022 let id = module.funcs().next().expect("the module has one function");
2023 assert_eq!(
2024 print_func(&module, &module[id], &names),
2025 "func @driver(), linkage(external) {\n\
2026 block0:\n \
2027 return\n\
2028 }\n"
2029 );
2030
2031 if let Err(errors) = verify_func(&module, &module[id], &names) {
2032 panic!("that was expected to be believed: {errors:#?}");
2033 }
2034 }
2035
2036 #[test]
2037 fn a_region_costs_the_object_file_no_descriptor_either() {
2038 let mut names = Interner::new();
2041 let mut module = exempt(&mut names);
2042 lower(&mut module, &mut names);
2043 assert_eq!(module.globals().count(), 0);
2044 }
2045
2046 #[test]
2047 fn a_module_with_nothing_to_check_gets_no_section_at_all() {
2048 let mut names = Interner::new();
2051 let mut module = Module::new(names.intern("empty.c"), &target());
2052 assert_eq!(lower(&mut module, &mut names), 0);
2053 assert_eq!(module.globals().count(), 0);
2054 }
2055}