1use std::fmt::Write as _;
29
30use rucc_base::{Interner, Symbol};
31use rucc_target::Slot;
32
33use crate::func::Func;
34use crate::inst::{
35 Abi, Block, BlockCall, Imm, Inst, InstData, MemInfo, Meta, MetaNode, Param, PlaneNode,
36 Signature, Value,
37};
38use crate::module::{Alias, Datum, Global, Module, Reloc};
39use crate::{Extra, FORMAT_VERSION, Linkage, MemOrder, Opcode, Type, Visibility};
40
41#[derive(Clone, Copy)]
43struct Chain {
44 takes: Option<Value>,
46 gives: bool,
48}
49
50fn without_mem(mut results: Vec<Value>, chain: Chain) -> Vec<Value> {
56 if chain.gives {
57 results.pop();
58 }
59 results
60}
61
62pub(crate) fn implied_result(opcode: Opcode) -> bool {
72 matches!(
73 opcode,
74 Opcode::ICmp
75 | Opcode::FCmp
76 | Opcode::GlobalAddr
77 | Opcode::BlockAddr
78 | Opcode::Alloca
79 | Opcode::Call
80 | Opcode::CallIndirect
81 | Opcode::TailCall
82 | Opcode::CapOf
84 | Opcode::CapLoad
85 | Opcode::CapNull
86 | Opcode::CapNarrow
87 | Opcode::CapRecover
88 )
89}
90
91#[must_use]
93pub fn print(module: &Module, names: &Interner) -> String {
94 let mut printer = Printer::new(module, names);
95 printer.module();
96 printer.finish()
97}
98
99#[must_use]
101pub fn print_func(module: &Module, func: &Func, names: &Interner) -> String {
102 let mut printer = Printer::new(module, names);
103 printer.func(func);
104 printer.finish()
105}
106
107#[derive(Debug)]
109pub struct Printer<'a> {
110 module: &'a Module,
111 names: &'a Interner,
112 out: String,
113 values: Vec<u32>,
117 blocks: Vec<u32>,
118}
119
120impl<'a> Printer<'a> {
121 #[must_use]
123 pub fn new(module: &'a Module, names: &'a Interner) -> Printer<'a> {
124 Printer { module, names, out: String::new(), values: Vec::new(), blocks: Vec::new() }
125 }
126
127 #[must_use]
129 pub fn finish(self) -> String {
130 self.out
131 }
132
133 pub fn module(&mut self) {
135 let module = self.module;
136 let name = self.names.resolve(module.name);
137 let _ = writeln!(self.out, "; ModuleID = '{name}'");
140 let _ = writeln!(self.out, "; format {FORMAT_VERSION}");
141 let _ = writeln!(self.out, "target triple = \"{}\"", module.triple);
142 let _ = writeln!(self.out, "target datalayout = \"{}\"", module.datalayout);
143
144 if module.globals().next().is_some() {
145 self.out.push('\n');
146 for id in module.globals() {
147 self.global(&module[id]);
148 }
149 }
150 if module.aliases().next().is_some() {
151 self.out.push('\n');
152 for id in module.aliases() {
153 self.alias(&module[id]);
154 }
155 }
156 for id in module.funcs() {
157 self.out.push('\n');
158 self.func(&module[id]);
159 }
160 if module.metadata().next().is_some() {
161 self.out.push('\n');
162 for meta in module.metadata() {
163 self.meta_node(meta);
164 }
165 }
166 }
167
168 fn global(&mut self, global: &Global) {
172 let _ = write!(self.out, "global @{} : ", self.names.resolve(global.name));
173 match self.scalar_init(global) {
174 Some((ty, imm)) => {
178 let _ = write!(self.out, "{ty} = ");
179 self.imm(imm, ty);
180 }
181 None => {
182 let _ = write!(self.out, "bytes {}", global.size);
183 if let Some(init) = global.init {
184 let data = &self.module[init];
191 if data.is_empty() {
192 self.out.push_str(" = {}");
193 } else {
194 self.out.push_str(" = { ");
195 for (index, &datum) in data.iter().enumerate() {
196 if index > 0 {
197 self.out.push_str(", ");
198 }
199 self.datum(datum);
200 }
201 self.out.push_str(" }");
202 }
203 }
204 }
205 }
206 let _ = write!(self.out, ", align {}", global.align);
207 self.linkage(global.linkage, global.visibility);
208 if let Some(model) = global.tls {
209 let _ = write!(self.out, ", tls({})", model.name());
210 }
211 if global.constant {
212 self.out.push_str(", constant");
213 }
214 self.section(global.section);
215 self.out.push('\n');
216 }
217
218 fn scalar_init(&self, global: &Global) -> Option<(Type, Imm)> {
220 let init = global.init?;
221 let [datum] = self.module[init] else { return None };
222 let Datum::Scalar { ty, value } = datum else { return None };
223 (datum.size(self.module) == global.size).then(|| (ty, self.module[value]))
224 }
225
226 fn datum(&mut self, datum: Datum) {
228 match datum {
229 Datum::Zero(bytes) => {
230 let _ = write!(self.out, "zero {bytes}");
231 }
232 Datum::Bytes(range) => {
233 self.out.push_str("bytes ");
234 let bytes = &self.module[range];
235 self.string(bytes);
236 }
237 Datum::Scalar { ty, value } => {
238 let _ = write!(self.out, "{ty} ");
239 self.imm(self.module[value], ty);
240 }
241 Datum::Addr(reloc) => {
242 let Reloc { symbol, addend, size } = self.module[reloc];
243 let _ = write!(self.out, "addr.{size} @{}", self.names.resolve(symbol));
244 match addend.signum() {
245 1 => {
246 let _ = write!(self.out, " + {addend}");
247 }
248 -1 => {
249 let _ = match addend.checked_neg() {
253 Some(amount) => write!(self.out, " - {amount}"),
254 None => write!(self.out, " + {addend}"),
255 };
256 }
257 _ => {}
258 }
259 }
260 }
261 }
262
263 fn alias(&mut self, alias: &Alias) {
265 let _ = write!(
266 self.out,
267 "{} @{} = @{}",
268 alias.kind.name(),
269 self.names.resolve(alias.name),
270 self.names.resolve(alias.target)
271 );
272 self.linkage(alias.linkage, alias.visibility);
273 self.out.push('\n');
274 }
275
276 pub fn func(&mut self, func: &Func) {
280 self.number(func);
281 let _ = write!(self.out, "func @{}", self.names.resolve(func.name));
282 self.signature(func.signature());
283 self.linkage(func.linkage, func.visibility);
284 if !func.attrs.is_default() {
285 let _ = write!(self.out, ", {}", func.attrs);
286 }
287 self.section(func.section);
288 if func.is_declaration() {
289 self.out.push_str(";\n");
290 return;
291 }
292 self.out.push_str(" {\n");
293 for (index, block) in func.blocks().enumerate() {
294 if index > 0 {
295 self.out.push('\n');
296 }
297 self.block(func, block);
298 }
299 self.facts(func);
300 self.out.push_str("}\n");
301 }
302
303 fn facts(&mut self, func: &Func) {
311 let mut first = true;
312 for (value, facts) in func.known() {
313 if first {
314 self.out.push_str("\nfacts:\n");
315 first = false;
316 }
317 self.out.push_str(" ");
318 self.value(value);
319 self.out.push_str(" = ");
320 let mut sep = false;
321 let mut comma = |out: &mut String| {
322 if sep {
323 out.push_str(", ");
324 }
325 sep = true;
326 };
327 if let Some(bounds) = facts.bounds {
328 comma(&mut self.out);
329 self.out.push_str("!bounds(");
330 self.value(bounds.lo);
331 self.out.push_str(", ");
332 self.value(bounds.ext);
333 self.out.push(')');
334 }
335 if facts.live {
336 comma(&mut self.out);
337 self.out.push_str("!live");
338 }
339 if let Some(n) = facts.init {
340 comma(&mut self.out);
341 let _ = write!(self.out, "!init({n})");
342 }
343 if let Some(align) = facts.align {
344 comma(&mut self.out);
345 let _ = write!(self.out, "!aligned({align})");
346 }
347 self.out.push('\n');
348 }
349 }
350
351 fn number(&mut self, func: &Func) {
356 let counts = func.counts();
357 self.values.clear();
358 self.values.resize(counts.values, u32::MAX);
359 self.blocks.clear();
360 self.blocks.resize(counts.blocks, u32::MAX);
361 let mut next = 0;
362 for (index, block) in func.blocks().enumerate() {
363 self.blocks[block.index()] = index as u32;
364 for ¶m in &func[block].params {
365 self.values[param.index()] = next;
366 next += 1;
367 }
368 for inst in func.insts(block) {
369 for result in func[inst].results() {
370 self.values[result.index()] = next;
371 next += 1;
372 }
373 }
374 }
375 }
376
377 fn signature(&mut self, signature: &Signature) {
379 self.out.push('(');
380 for (index, param) in signature.params.iter().enumerate() {
381 if index > 0 {
382 self.out.push_str(", ");
383 }
384 self.param(param);
385 }
386 if signature.variadic {
387 if !signature.params.is_empty() {
388 self.out.push_str(", ");
389 }
390 self.out.push_str("...");
391 }
392 self.out.push(')');
393 match signature.returns.as_slice() {
394 [] => {}
395 [param] => {
396 self.out.push_str(" -> ");
397 self.param(param);
398 }
399 params => {
400 self.out.push_str(" -> (");
401 for (index, param) in params.iter().enumerate() {
402 if index > 0 {
403 self.out.push_str(", ");
404 }
405 self.param(param);
406 }
407 self.out.push(')');
408 }
409 }
410 }
411
412 fn param(&mut self, param: &Param) {
414 let _ = write!(self.out, "{}", param.ty);
415 self.abi(param.abi);
416 }
417
418 fn abi(&mut self, abi: Abi) {
421 let _ = match abi {
422 Abi::Plain => Ok(()),
423 Abi::Sext => write!(self.out, " sext"),
424 Abi::Zext => write!(self.out, " zext"),
425 Abi::ByVal { size, align } => write!(self.out, " byval({size}, align {align})"),
426 Abi::Sret { size, align } => write!(self.out, " sret({size}, align {align})"),
427 };
428 }
429
430 fn block(&mut self, func: &Func, block: Block) {
432 let _ = write!(self.out, "block{}", self.blocks[block.index()]);
433 let params = &func[block].params;
434 if !params.is_empty() {
435 self.out.push('(');
436 for (index, ¶m) in params.iter().enumerate() {
437 if index > 0 {
438 self.out.push_str(", ");
439 }
440 self.value(param);
441 let _ = write!(self.out, ": {}", func[param].ty);
442 }
443 self.out.push(')');
444 }
445 self.out.push_str(":\n");
446 for inst in func.insts(block) {
447 self.inst(func, inst);
448 }
449 }
450
451 fn inst(&mut self, func: &Func, inst: Inst) {
453 let data = func[inst];
454 let chain = Chain { takes: func.mem_in(inst), gives: func.mem_out(inst).is_some() };
459 self.out.push_str(" ");
460 for (index, result) in data.results().enumerate() {
461 if index > 0 {
462 self.out.push_str(", ");
463 }
464 self.value(result);
465 }
466 if data.results > 0 {
467 self.out.push_str(" = ");
468 }
469 self.out.push_str(data.opcode.name());
470 self.result_types(func, &data, chain);
471 let _ = write!(self.out, "{}", data.flags);
472 self.operands(func, &data, chain);
473 if let Some(mem) = chain.takes {
474 self.out.push_str(" [mem ");
475 self.value(mem);
476 self.out.push(']');
477 }
478 self.out.push('\n');
479 }
480
481 fn result_types(&mut self, func: &Func, data: &InstData, chain: Chain) {
483 let results = without_mem(data.results().collect(), chain);
484 match results.as_slice() {
485 [] => {}
486 _ if implied_result(data.opcode) => {}
487 [result] => {
488 let ty = func[*result].ty;
489 let takes_the_same = func[data.args].first().is_some_and(|&arg| func[arg].ty == ty);
490 if !takes_the_same {
491 let _ = write!(self.out, ".{ty}");
492 }
493 }
494 types => {
497 self.out.push_str(".(");
498 for (index, &result) in types.iter().enumerate() {
499 if index > 0 {
500 self.out.push_str(", ");
501 }
502 let _ = write!(self.out, "{}", func[result].ty);
503 }
504 self.out.push(')');
505 }
506 }
507 }
508
509 fn operands(&mut self, func: &Func, data: &InstData, chain: Chain) {
511 let all = &func[data.args];
512 let args = &all[..all.len() - usize::from(chain.takes.is_some())];
513 match data.extra {
514 Extra::None => self.value_list_spaced(args),
515 Extra::Imm(imm) => {
516 self.out.push(' ');
517 let ty = data.first_result.map_or(Type::VOID, |result| func[result].ty);
518 self.imm(func[imm], ty);
519 }
520 Extra::Symbol(symbol) => {
521 let _ = write!(self.out, " @{}", self.names.resolve(symbol));
522 if !args.is_empty() {
523 self.out.push('(');
524 self.value_list(args);
525 self.out.push(')');
526 }
527 }
528 Extra::IntPred(pred) => {
529 let _ = write!(self.out, " {}", pred.name());
530 self.value_list_spaced(args);
531 }
532 Extra::FloatPred(pred) => {
533 let _ = write!(self.out, " {}", pred.name());
534 self.value_list_spaced(args);
535 }
536 Extra::Mem(mem) => {
537 match (data.opcode, args) {
538 (Opcode::Store | Opcode::AtomicStore, [value, addr]) => {
541 self.out.push(' ');
542 self.value(*value);
543 self.out.push_str(" -> ");
544 self.value(*addr);
545 }
546 _ => self.value_list_spaced(args),
547 }
548 self.mem(func[mem]);
549 }
550 Extra::VaObject(info) => {
551 let info = func[info];
552 self.value_list_spaced(args);
553 self.mem(func[info.mem]);
554 let slots = &func[info.slots];
555 if !slots.is_empty() {
556 self.out.push_str(", in(");
557 for (index, &slot) in slots.iter().enumerate() {
558 if index > 0 {
559 self.out.push_str(", ");
560 }
561 self.slot(slot);
562 }
563 self.out.push(')');
564 }
565 }
566 Extra::Rmw(op, mem) => {
567 let _ = write!(self.out, " {}", op.name());
568 self.value_list_spaced(args);
569 self.mem(func[mem]);
570 }
571 Extra::Class(class) => {
573 self.value_list_spaced(args);
574 let _ = write!(self.out, ", class {}", class.name());
575 }
576 Extra::Owner(owner) => {
577 self.value_list_spaced(args);
578 let _ = write!(self.out, ", to {}", owner.name());
579 }
580 Extra::Node(node) => {
581 self.value_list_spaced(args);
582 let _ = write!(self.out, ", tbaa !{}", node.index());
583 }
584 Extra::Reason(reason) => {
585 self.out.push(' ');
586 self.string(self.names.resolve(reason).as_bytes());
587 }
588 Extra::Order(order) => {
589 let _ = write!(self.out, " {}", order.name());
590 }
591 Extra::Targets(targets) => {
592 if !args.is_empty() {
595 self.value_list_spaced(args);
596 self.out.push(',');
597 }
598 for (index, &call) in func[targets].iter().enumerate() {
599 self.out.push_str(if index > 0 { ", " } else { " " });
600 self.block_call(func, call);
601 }
602 }
603 Extra::Call(call) => {
604 let info = func[call];
605 let rest = match info.callee {
606 Some(callee) => {
607 let _ = write!(self.out, " @{}", self.names.resolve(callee));
608 args
609 }
610 None => {
613 self.out.push(' ');
614 match args.split_first() {
615 Some((&addr, rest)) => {
616 self.value(addr);
617 rest
618 }
619 None => {
620 self.out.push_str("%?");
621 &[]
622 }
623 }
624 }
625 };
626 self.out.push('(');
627 let named = func[info.signature].params.len();
630 let varargs = &func[info.varargs];
631 for (index, &arg) in rest.iter().enumerate() {
632 if index > 0 {
633 self.out.push_str(", ");
634 }
635 self.value(arg);
636 if let Some(&abi) = index.checked_sub(named).and_then(|at| varargs.get(at)) {
637 self.abi(abi);
638 }
639 }
640 self.out.push_str(") : ");
641 self.signature(&func[info.signature]);
642 }
643 Extra::Switch(switch) => {
644 let info = func[switch];
645 let ty = args.first().map_or(Type::VOID, |&arg| func[arg].ty);
646 self.value_list_spaced(args);
647 if let Some((&default, cases)) = func[info.targets].split_first() {
648 self.out.push_str(", ");
649 self.block_call(func, default);
650 self.out.push_str(", [");
651 for (index, (&case, &value)) in cases.iter().zip(&func[info.cases]).enumerate()
652 {
653 if index > 0 {
654 self.out.push_str(", ");
655 }
656 self.imm(value, ty);
657 self.out.push_str(" => ");
658 self.block_call(func, case);
659 }
660 self.out.push(']');
661 }
662 }
663 Extra::Asm(asm) => {
664 let info = func[asm];
665 self.out.push(' ');
666 self.string(self.names.resolve(info.template).as_bytes());
667 self.out.push_str(", ");
668 self.string(self.names.resolve(info.constraints).as_bytes());
669 self.out.push_str(", ");
670 self.string(self.names.resolve(info.clobbers).as_bytes());
671 self.out.push('(');
672 self.value_list(args);
673 self.out.push(')');
674 if !info.targets.is_empty() {
675 self.out.push_str(", labels [");
676 for (index, &call) in func[info.targets].iter().enumerate() {
677 if index > 0 {
678 self.out.push_str(", ");
679 }
680 self.block_call(func, call);
681 }
682 self.out.push(']');
683 }
684 }
685 }
686 }
687
688 fn value_list_spaced(&mut self, args: &[Value]) {
690 if args.is_empty() {
691 return;
692 }
693 self.out.push(' ');
694 self.value_list(args);
695 }
696
697 fn value_list(&mut self, args: &[Value]) {
699 for (index, &arg) in args.iter().enumerate() {
700 if index > 0 {
701 self.out.push_str(", ");
702 }
703 self.value(arg);
704 }
705 }
706
707 fn block_call(&mut self, func: &Func, call: BlockCall) {
709 let _ = write!(self.out, "block{}", self.blocks[call.block.index()]);
710 let args = &func[call.args];
711 if !args.is_empty() {
712 self.out.push('(');
713 self.value_list(args);
714 self.out.push(')');
715 }
716 }
717
718 fn mem(&mut self, info: MemInfo) {
720 if info.size != 0 {
721 let _ = write!(self.out, ", size {}", info.size);
722 }
723 let _ = write!(self.out, ", align {}", info.align);
724 if info.order != MemOrder::NotAtomic {
725 let _ = write!(self.out, ", {}", info.order.name());
726 }
727 if let Some(tbaa) = info.tbaa {
728 let _ = write!(self.out, ", tbaa !{}", tbaa.index());
729 }
730 if info.restrict.clique != 0 {
731 let _ =
732 write!(self.out, ", restrict({}, {})", info.restrict.clique, info.restrict.base);
733 }
734 }
735
736 fn slot(&mut self, slot: Slot) {
738 match slot {
739 Slot::Integer { offset, size } => {
740 let _ = write!(self.out, "int {size} at {offset}");
741 }
742 Slot::Float { offset, format } => {
743 let _ = write!(self.out, "float {} at {offset}", format.name());
744 }
745 }
746 }
747
748 fn value(&mut self, value: Value) {
750 match self.values.get(value.index()).copied() {
751 Some(number) if number != u32::MAX => {
752 let _ = write!(self.out, "%{number}");
753 }
754 _ => self.out.push_str("%?"),
758 }
759 }
760
761 fn imm(&mut self, imm: Imm, ty: Type) {
763 let scalar = if ty.is_vector() { ty.lane() } else { ty };
764 if scalar.is_float() {
765 let _ = write!(self.out, "{:#x}", imm.bits());
768 } else if scalar.is_int() {
769 let _ = write!(self.out, "{}", imm.signed(scalar));
770 } else {
771 let _ = write!(self.out, "{:#x}", imm.bits());
772 }
773 }
774
775 fn meta_node(&mut self, meta: Meta) {
777 let _ = write!(self.out, "!{} = ", meta.index());
778 match self.module[meta] {
779 MetaNode::Tbaa(node) => {
780 self.out.push_str("tbaa ");
781 self.string(self.names.resolve(node.name).as_bytes());
782 if let Some(parent) = node.parent {
783 let _ = write!(self.out, ", parent !{}", parent.index());
784 }
785 let _ = write!(self.out, ", offset {}", node.offset);
786 }
787 MetaNode::Plane(node) => {
788 self.out.push_str("plane ");
789 let _ = match node {
790 PlaneNode::Type(ty) => write!(self.out, "!{}", ty.index()),
791 PlaneNode::NoType => self.out.write_str("no_type"),
792 PlaneNode::Character => self.out.write_str("character"),
793 PlaneNode::PointerSlot(k) => write!(self.out, "pointer_slot {k}"),
794 };
795 }
796 }
797 self.out.push('\n');
798 }
799
800 fn linkage(&mut self, linkage: Linkage, visibility: Visibility) {
802 let _ = write!(self.out, ", linkage({})", linkage.name());
803 if visibility != Visibility::Default {
804 let _ = write!(self.out, ", visibility({})", visibility.name());
805 }
806 }
807
808 fn section(&mut self, section: Option<Symbol>) {
810 if let Some(section) = section {
811 self.out.push_str(", section ");
812 self.string(self.names.resolve(section).as_bytes());
813 }
814 }
815
816 fn string(&mut self, bytes: &[u8]) {
818 self.out.push('"');
819 for &byte in bytes {
820 match byte {
821 b'"' => self.out.push_str("\\\""),
822 b'\\' => self.out.push_str("\\\\"),
823 0x20..=0x7e => self.out.push(byte as char),
824 _ => {
825 let _ = write!(self.out, "\\{byte:02x}");
826 }
827 }
828 }
829 self.out.push('"');
830 }
831}
832
833#[cfg(test)]
834mod tests {
835 use rucc_base::Interner;
836 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
837
838 use super::*;
839 use crate::Restrict;
840 use crate::func::Builder;
841 use crate::inst::{AsmInfo, CallInfo, MetaNode, PlaneNode, SwitchInfo, TbaaNode, VaInfo};
842 use crate::module::{AliasKind, TlsModel};
843 use crate::{
844 AttrSet, Attrs, Bounds, Facts, Flags, FloatPred, FpContract, IntPred, Owner, RmwOp,
845 StorageClass,
846 };
847
848 fn target() -> TargetInfo {
849 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
850 }
851
852 #[test]
853 fn the_example_in_the_spec() {
854 let mut names = Interner::new();
855 let mut module = Module::new(names.intern("example.c"), &target());
856
857 let char_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
858 name: names.intern("omnipotent char"),
859 parent: None,
860 offset: 0,
861 }));
862 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
863 name: names.intern("int"),
864 parent: Some(char_node),
865 offset: 0,
866 }));
867
868 let i32_ = Type::int(32);
869 let zero_bits = module.add_imm(Imm::int(0, i32_));
870 let init = module.push_data(&[Datum::Scalar { ty: i32_, value: zero_bits }]);
871 let mut counter = Global::new(names.intern("counter"), 4, 4);
872 counter.linkage = Linkage::Internal;
873 counter.init = Some(init);
874 module.add_global(counter);
875
876 let mut func = Func::new(
877 names.intern("sum"),
878 Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
879 );
880 func.attrs = Attrs { set: AttrSet::NOUNWIND, fp_contract: FpContract::On };
881 let entry = func.create_block();
882 let n = func.append_param(entry, i32_);
883 let header = func.create_block();
884 let acc = func.append_param(header, i32_);
885 let i = func.append_param(header, i32_);
886 let exit = func.create_block();
887 let result = func.append_param(exit, i32_);
888
889 let mut b = Builder::new(&mut func, entry);
890 let zero = b.iconst(i32_, 0);
891 let cmp = b.icmp(IntPred::Sle, n, zero);
892 b.br_if(cmp, exit, &[zero], header, &[zero, zero]);
893
894 let mut b = Builder::new(&mut func, header);
895 let one = b.iconst(i32_, 1);
896 let next = b.binary(Opcode::Add, i, one, Flags::NSW);
897 let total = b.binary(Opcode::Add, acc, next, Flags::NSW);
898 let done = b.icmp(IntPred::Sge, next, n);
899 b.br_if(done, exit, &[total], header, &[total, next]);
900
901 let mut b = Builder::new(&mut func, exit);
902 let address = b.value(
903 InstData {
904 extra: Extra::Symbol(names.intern("counter")),
905 ..InstData::new(Opcode::GlobalAddr)
906 },
907 Type::PTR,
908 );
909 b.store(
910 result,
911 address,
912 MemInfo {
913 size: 0,
914 align: 4,
915 order: MemOrder::NotAtomic,
916 tbaa: Some(int_node),
917 restrict: Restrict::NONE,
918 },
919 Flags::NONE,
920 );
921 b.ret(&[result]);
922 module.add_func(func);
923
924 assert_eq!(print(&module, &names), crate::fixtures::EXAMPLE);
925 }
926
927 #[test]
928 fn the_memory_safety_instructions() {
929 let mut names = Interner::new();
930 let mut module = Module::new(names.intern("safety.c"), &target());
931 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
932 name: names.intern("int"),
933 parent: None,
934 offset: 0,
935 }));
936 let int_plane = module.add_meta(MetaNode::Plane(PlaneNode::Type(int_node)));
937 let character = module.add_meta(MetaNode::Plane(PlaneNode::Character));
938 module.add_meta(MetaNode::Plane(PlaneNode::NoType));
939 module.add_meta(MetaNode::Plane(PlaneNode::PointerSlot(3)));
940
941 let i64_ = Type::int(64);
942 let mut func = Func::new(
943 names.intern("safety"),
944 Signature::new().with_params(&[Type::PTR, i64_]).with_returns(&[Type::PTR]),
945 );
946 let entry = func.create_block();
947 let p = func.append_param(entry, Type::PTR);
948 let off = func.append_param(entry, i64_);
949
950 let mut b = Builder::new(&mut func, entry);
951 let of = b.unary(Opcode::CapOf, p, Type::CAP);
952 b.inst(InstData::new(Opcode::CapNull), &[Type::CAP]);
953 b.unary(Opcode::CapRecover, p, Type::CAP);
954 b.unary(Opcode::CapLoad, p, Type::CAP);
955 let len = b.iconst(i64_, 8);
956 let args = b.func().push_values(&[of, off, len]);
957 let narrow = b.value(InstData { args, ..InstData::new(Opcode::CapNarrow) }, Type::CAP);
958 let args = b.func().push_values(&[p, narrow]);
959 b.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
960
961 let args = b.func().push_values(&[p, off]);
962 let derived = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
963 let four = MemInfo {
964 size: 4,
965 align: 4,
966 order: MemOrder::NotAtomic,
967 tbaa: None,
968 restrict: Restrict::NONE,
969 };
970 let mut check = |opcode, info: Option<MemInfo>, on: &[Value]| {
971 let args = b.func().push_values(on);
972 let extra = match info {
973 Some(info) => Extra::Mem(b.func().add_mem(info)),
974 None => Extra::None,
975 };
976 b.inst(InstData { args, extra, ..InstData::new(opcode) }, &[]);
977 };
978 check(Opcode::CheckBounds, Some(four), &[of, p]);
979 check(Opcode::CheckLive, None, &[of, p]);
980 check(Opcode::CheckType, Some(MemInfo { tbaa: Some(int_plane), ..four }), &[of, p]);
981 check(Opcode::CheckInit, Some(MemInfo { align: 1, ..four }), &[of, p]);
982 check(Opcode::CheckDeriv, None, &[of, p, derived]);
983 check(Opcode::CheckRace, None, &[of, p]);
984
985 let mut plane = |opcode, extra| {
986 let args = b.func().push_values(&[p, off]);
987 b.inst(InstData { args, extra, ..InstData::new(opcode) }, &[]);
988 };
989 plane(Opcode::MetaBegin, Extra::Class(StorageClass::Allocated));
990 plane(Opcode::MetaType, Extra::Node(character));
991 plane(Opcode::MetaInit, Extra::None);
992 plane(Opcode::MetaTransfer, Extra::Owner(Owner::Device));
993 plane(Opcode::MetaEnd, Extra::None);
994
995 let reason = names.intern("hand written assembly, checked by review");
996 b.inst(
997 InstData { extra: Extra::Reason(reason), ..InstData::new(Opcode::SafeRegionBegin) },
998 &[],
999 );
1000 b.inst(InstData::new(Opcode::SafeRegionEnd), &[]);
1001 b.ret(&[p]);
1002
1003 func.set_facts(
1004 p,
1005 Facts {
1006 bounds: Some(Bounds { lo: p, ext: off }),
1007 init: Some(4),
1008 align: Some(8),
1009 live: true,
1010 },
1011 );
1012 func.set_facts(derived, Facts { align: Some(4), ..Facts::NONE });
1013 module.add_func(func);
1014
1015 assert_eq!(print(&module, &names), crate::fixtures::SAFETY);
1016 }
1017
1018 #[test]
1019 fn one_of_almost_everything() {
1020 let mut names = Interner::new();
1021 let mut module = Module::new(names.intern("zoo.c"), &target());
1022 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
1023 name: names.intern("int"),
1024 parent: None,
1025 offset: 0,
1026 }));
1027
1028 let i32_ = Type::int(32);
1029 let i64_ = Type::int(64);
1030 let f64_ = Type::float(crate::Float::F64);
1031 let mut func = Func::new(
1032 names.intern("zoo"),
1033 Signature::new().with_params(&[i32_, Type::PTR]).with_returns(&[i32_]),
1034 );
1035 let entry = func.create_block();
1036 let n = func.append_param(entry, i32_);
1037 let p = func.append_param(entry, Type::PTR);
1038 let middle = func.create_block();
1039 let other = func.create_block();
1040 let exit = func.create_block();
1041 let taken = func.append_param(exit, i32_);
1042 let arrival = func.create_block();
1043
1044 let mut b = Builder::new(&mut func, entry);
1045 let minus_one = b.iconst(i64_, -1);
1046 let half = b.fconst(f64_, 0x3ff8_0000_0000_0000);
1047 let seven = b.func().add_imm(Imm::int(7, i32_));
1048 let vector = b.value(
1049 InstData { extra: Extra::Imm(seven), ..InstData::new(Opcode::Splat) },
1050 Type::vector(i32_, 4),
1051 );
1052 let stack = b.func().add_mem(MemInfo {
1053 size: 16,
1054 align: 8,
1055 order: MemOrder::NotAtomic,
1056 tbaa: None,
1057 restrict: Restrict::NONE,
1058 });
1059 let slot = b.value(
1060 InstData { extra: Extra::Mem(stack), ..InstData::new(Opcode::Alloca) },
1061 Type::PTR,
1062 );
1063 let args = b.func().push_values(&[slot, minus_one]);
1064 let addr = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1065 let plain = MemInfo {
1066 size: 0,
1067 align: 4,
1068 order: MemOrder::NotAtomic,
1069 tbaa: Some(int_node),
1070 restrict: Restrict::NONE,
1071 };
1072 let loaded = b.load(i32_, addr, plain, Flags::NONE);
1073 b.store(loaded, addr, plain, Flags::VOLATILE);
1074
1075 let atomic = b.func().add_mem(MemInfo {
1076 size: 0,
1077 align: 4,
1078 order: MemOrder::SeqCst,
1079 tbaa: None,
1080 restrict: Restrict::NONE,
1081 });
1082 let args = b.func().push_values(&[addr, n]);
1083 let old = b.value(
1084 InstData {
1085 args,
1086 extra: Extra::Rmw(RmwOp::Add, atomic),
1087 ..InstData::new(Opcode::AtomicRmw)
1088 },
1089 i32_,
1090 );
1091 let args = b.func().push_values(&[addr, old, n]);
1092 b.inst(
1093 InstData { args, extra: Extra::Mem(atomic), ..InstData::new(Opcode::Cmpxchg) },
1094 &[i32_, Type::I1],
1095 );
1096 b.inst(
1097 InstData { extra: Extra::Order(MemOrder::SeqCst), ..InstData::new(Opcode::Fence) },
1098 &[],
1099 );
1100 b.unary(Opcode::SExt, n, i64_);
1101 b.fcmp(FloatPred::Oeq, half, half, Flags::NONE);
1102 let args = b.func().push_values(&[n, n]);
1103 b.inst(InstData { args, ..InstData::new(Opcode::SAddOverflow) }, &[i32_, Type::I1]);
1104 let puts = b.func().add_signature(
1105 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]).variadic(),
1106 );
1107 b.call_varargs(
1108 names.intern("puts"),
1109 puts,
1110 &[p, slot],
1111 &[Abi::ByVal { size: 16, align: 8 }],
1112 );
1113 let indirect =
1114 b.func().add_signature(Signature::new().with_params(&[i32_]).with_returns(&[i32_]));
1115 let varargs = b.func().push_abis(&[]);
1116 let info = b.func().add_call(CallInfo { callee: None, signature: indirect, varargs });
1117 let args = b.func().push_values(&[p, n]);
1118 b.value(
1119 InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::CallIndirect) },
1120 i32_,
1121 );
1122 let copy = b.func().add_mem(MemInfo {
1123 size: 16,
1124 align: 8,
1125 order: MemOrder::NotAtomic,
1126 tbaa: None,
1127 restrict: Restrict::NONE,
1128 });
1129 let args = b.func().push_values(&[slot, p]);
1130 b.inst(InstData { args, extra: Extra::Mem(copy), ..InstData::new(Opcode::Memcpy) }, &[]);
1131 let asm = b.func().add_asm(AsmInfo {
1132 template: names.intern("pause"),
1133 constraints: names.intern(""),
1134 clobbers: names.intern("memory"),
1135 targets: crate::inst::BlockCallList::EMPTY,
1136 });
1137 b.inst(
1138 InstData {
1139 flags: Flags::VOLATILE,
1140 extra: Extra::Asm(asm),
1141 ..InstData::new(Opcode::InlineAsm)
1142 },
1143 &[],
1144 );
1145 let object = b.func().add_mem(MemInfo {
1146 size: 16,
1147 align: 8,
1148 order: MemOrder::NotAtomic,
1149 tbaa: None,
1150 restrict: Restrict::NONE,
1151 });
1152 let slots = b.func().push_slots(&[
1153 Slot::Integer { offset: 0, size: 8 },
1154 Slot::Float { offset: 8, format: rucc_base::float::Format::Double },
1155 ]);
1156 let read = b.func().add_va_object(VaInfo { mem: object, slots });
1157 let args = b.func().push_values(&[p]);
1158 b.value(
1159 InstData { args, extra: Extra::VaObject(read), ..InstData::new(Opcode::VaObject) },
1160 Type::PTR,
1161 );
1162 let args = b.func().push_values(&[vector]);
1163 b.value(
1164 InstData {
1165 args,
1166 extra: Extra::Symbol(names.intern("x86.sse2.pmovmskb")),
1167 ..InstData::new(Opcode::TargetIntrinsic)
1168 },
1169 i32_,
1170 );
1171 b.jump(middle, &[]);
1172
1173 let mut b = Builder::new(&mut func, middle);
1174 let cases = b.func().push_imms(&[Imm::int(0, i32_), Imm::int(-1, i32_)]);
1175 let default = BlockCall { block: other, args: crate::inst::ValueList::EMPTY };
1176 let first = BlockCall { block: exit, args: b.func().push_values(&[n]) };
1177 let second = BlockCall { block: other, args: crate::inst::ValueList::EMPTY };
1178 let targets = b.func().push_block_calls(&[default, first, second]);
1179 let switch = b.func().add_switch(SwitchInfo { targets, cases });
1180 let args = b.func().push_values(&[n]);
1181 b.inst(
1182 InstData { args, extra: Extra::Switch(switch), ..InstData::new(Opcode::Switch) },
1183 &[],
1184 );
1185
1186 let mut b = Builder::new(&mut func, other);
1187 let address = b.block_addr(arrival);
1188 b.indirect_br(address, &[arrival]);
1189
1190 let mut b = Builder::new(&mut func, exit);
1191 b.ret(&[taken]);
1192
1193 let mut b = Builder::new(&mut func, arrival);
1194 let call = BlockCall { block: exit, args: b.func().push_values(&[n]) };
1195 let targets = b.func().push_block_calls(&[call]);
1196 let goto = b.func().add_asm(AsmInfo {
1197 template: names.intern("jmp %l0"),
1198 constraints: names.intern(""),
1199 clobbers: names.intern(""),
1200 targets,
1201 });
1202 b.inst(InstData { extra: Extra::Asm(goto), ..InstData::new(Opcode::InlineAsm) }, &[]);
1203
1204 module.add_func(func);
1205
1206 assert_eq!(print(&module, &names), crate::fixtures::ZOO);
1207 }
1208
1209 #[test]
1210 fn the_shapes_a_symbol_comes_in() {
1211 let mut names = Interner::new();
1212 let mut module = Module::new(names.intern("data.c"), &target());
1213
1214 let i32_ = Type::int(32);
1215 let text = module.push_bytes(b"hi\x00\xff\"\\");
1216 let entry_name = names.intern("hi.str");
1217 let forward = module.add_reloc(Reloc { symbol: entry_name, addend: 8, size: 8 });
1218 let backward = module.add_reloc(Reloc { symbol: entry_name, addend: -8, size: 8 });
1219 let seven = module.add_imm(Imm::int(7, i32_));
1220 let image = module.push_data(&[
1221 Datum::Bytes(text),
1222 Datum::Zero(2),
1223 Datum::Scalar { ty: i32_, value: seven },
1224 Datum::Addr(forward),
1225 Datum::Addr(backward),
1226 ]);
1227 let mut table = Global::new(names.intern("table"), 28, 8);
1228 table.init = Some(image);
1229 table.constant = true;
1230 table.section = Some(names.intern(".rodata.rel"));
1231 module.add_global(table);
1232
1233 let mut errno = Global::new(names.intern("errno"), 4, 4);
1234 errno.tls = Some(TlsModel::InitialExec);
1235 errno.visibility = Visibility::Hidden;
1236 module.add_global(errno);
1237
1238 let mut nothing = Global::new(names.intern("nothing"), 0, 1);
1243 nothing.init = Some(module.push_data(&[]));
1244 nothing.linkage = Linkage::Internal;
1245 module.add_global(nothing);
1246
1247 let mut alias = Alias::new(names.intern("total"), names.intern("table"));
1248 alias.linkage = Linkage::Weak;
1249 module.add_alias(alias);
1250 let mut memcpy = Alias::new(names.intern("memcpy"), names.intern("memcpy.resolve"));
1251 memcpy.kind = AliasKind::IFunc;
1252 memcpy.visibility = Visibility::Protected;
1253 module.add_alias(memcpy);
1254
1255 let mut puts = Func::new(
1256 names.intern("puts"),
1257 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
1258 );
1259 puts.linkage = Linkage::External;
1260 puts.attrs.set = AttrSet::NOUNWIND | AttrSet::WILLRETURN;
1261 module.add_func(puts);
1262
1263 let mut helper =
1264 Func::new(names.intern("helper"), Signature::new().with_returns(&[i32_, i32_]));
1265 helper.linkage = Linkage::Internal;
1266 helper.section = Some(names.intern(".text.hot"));
1267 helper.attrs.set = AttrSet::READNONE | AttrSet::ALWAYS_INLINE;
1268 let block = helper.create_block();
1269 let mut b = Builder::new(&mut helper, block);
1270 let one = b.iconst(i32_, 1);
1271 b.ret(&[one, one]);
1272 module.add_func(helper);
1273
1274 assert_eq!(print(&module, &names), crate::fixtures::SYMBOLS);
1275 }
1276
1277 #[test]
1278 fn a_signature_writes_what_the_abi_asks_of_each_parameter() {
1279 let mut names = Interner::new();
1280 let module = Module::new(names.intern("abi.c"), &target());
1281 let mut func = Func::new(
1282 names.intern("f"),
1283 Signature::new()
1284 .and_param(Param::with_abi(Type::PTR, Abi::Sret { size: 24, align: 8 }))
1285 .and_param(Param::with_abi(Type::PTR, Abi::ByVal { size: 16, align: 8 }))
1286 .and_param(Param::with_abi(Type::int(8), Abi::Zext))
1287 .and_param(Param::new(Type::int(32))),
1288 );
1289 let entry = func.create_block();
1290 for param in [Type::PTR, Type::PTR, Type::int(8), Type::int(32)] {
1291 func.append_param(entry, param);
1292 }
1293 let mut b = Builder::new(&mut func, entry);
1294 b.ret(&[]);
1295
1296 assert_eq!(
1297 print_func(&module, &func, &names),
1298 "\
1299func @f(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext, i32), linkage(external) {
1300block0(%0: ptr, %1: ptr, %2: i8, %3: i32):
1301 return
1302}
1303"
1304 );
1305 }
1306
1307 #[test]
1308 fn a_call_writes_what_the_abi_asks_of_an_argument_its_signature_does_not_name() {
1309 let mut names = Interner::new();
1310 let module = Module::new(names.intern("varargs.c"), &target());
1311 let i32_ = Type::int(32);
1312 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR]));
1313 let entry = func.create_block();
1314 let p = func.append_param(entry, Type::PTR);
1315 let mut b = Builder::new(&mut func, entry);
1316 let sig = b.func().add_signature(
1317 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]).variadic(),
1318 );
1319 let one = b.iconst(i32_, 1);
1320 b.call_varargs(
1321 names.intern("printf"),
1322 sig,
1323 &[p, one, p],
1324 &[Abi::Plain, Abi::ByVal { size: 24, align: 8 }],
1325 );
1326 b.ret(&[]);
1327
1328 assert_eq!(
1329 print_func(&module, &func, &names),
1330 "\
1331func @f(ptr), linkage(external) {
1332block0(%0: ptr):
1333 %1 = iconst.i32 1
1334 %2 = call @printf(%0, %1, %0 byval(24, align 8)) : (ptr, ...) -> i32
1335 return
1336}
1337"
1338 );
1339 }
1340
1341 #[test]
1342 fn numbering_follows_the_text_and_not_the_tables() {
1343 let mut names = Interner::new();
1348 let mut module = Module::new(names.intern("order.c"), &target());
1349 let i32_ = Type::int(32);
1350 let mut func = Func::new(names.intern("f"), Signature::new().with_returns(&[i32_]));
1351 let entry = func.create_block();
1352 let middle = func.create_block();
1353 let exit = func.create_block();
1354 let arrived = func.append_param(exit, i32_);
1355
1356 let mut b = Builder::new(&mut func, exit);
1357 b.ret(&[arrived]);
1358 let mut b = Builder::new(&mut func, middle);
1359 let two = b.iconst(i32_, 2);
1360 b.jump(exit, &[two]);
1361 let mut b = Builder::new(&mut func, entry);
1362 b.jump(middle, &[]);
1363 module.add_func(func);
1364
1365 assert_eq!(
1366 print_func(&module, &module[module.funcs().next().unwrap()], &names),
1367 "\
1368func @f() -> i32, linkage(external) {
1369block0:
1370 jump block1
1371
1372block1:
1373 %0 = iconst.i32 2
1374 jump block2(%0)
1375
1376block2(%1: i32):
1377 return %1
1378}
1379"
1380 );
1381 }
1382}