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.tuple.to_llvm_string());
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.labels(func);
300 self.facts(func);
301 self.out.push_str("}\n");
302 }
303
304 fn labels(&mut self, func: &Func) {
312 let mut first = true;
313 for (block, name) in func.named_blocks() {
314 if first {
315 self.out.push_str("\nlabels:\n");
316 first = false;
317 }
318 let number = self.blocks[block.index()];
319 let _ = writeln!(self.out, " block{number} = @{}", self.names.resolve(name));
320 }
321 }
322
323 fn facts(&mut self, func: &Func) {
331 let mut first = true;
332 for (value, facts) in func.known() {
333 if first {
334 self.out.push_str("\nfacts:\n");
335 first = false;
336 }
337 self.out.push_str(" ");
338 self.value(value);
339 self.out.push_str(" = ");
340 let mut sep = false;
341 let mut comma = |out: &mut String| {
342 if sep {
343 out.push_str(", ");
344 }
345 sep = true;
346 };
347 if let Some(bounds) = facts.bounds {
348 comma(&mut self.out);
349 self.out.push_str("!bounds(");
350 self.value(bounds.lo);
351 self.out.push_str(", ");
352 self.value(bounds.ext);
353 self.out.push(')');
354 }
355 if facts.live {
356 comma(&mut self.out);
357 self.out.push_str("!live");
358 }
359 if let Some(n) = facts.init {
360 comma(&mut self.out);
361 let _ = write!(self.out, "!init({n})");
362 }
363 if let Some(align) = facts.align {
364 comma(&mut self.out);
365 let _ = write!(self.out, "!aligned({align})");
366 }
367 self.out.push('\n');
368 }
369 }
370
371 fn number(&mut self, func: &Func) {
376 let counts = func.counts();
377 self.values.clear();
378 self.values.resize(counts.values, u32::MAX);
379 self.blocks.clear();
380 self.blocks.resize(counts.blocks, u32::MAX);
381 let mut next = 0;
382 for (index, block) in func.blocks().enumerate() {
383 self.blocks[block.index()] = index as u32;
384 for ¶m in &func[block].params {
385 self.values[param.index()] = next;
386 next += 1;
387 }
388 for inst in func.insts(block) {
389 for result in func[inst].results() {
390 self.values[result.index()] = next;
391 next += 1;
392 }
393 }
394 }
395 }
396
397 fn signature(&mut self, signature: &Signature) {
399 self.out.push('(');
400 for (index, param) in signature.params.iter().enumerate() {
401 if index > 0 {
402 self.out.push_str(", ");
403 }
404 self.param(param);
405 }
406 if signature.variadic {
407 if !signature.params.is_empty() {
408 self.out.push_str(", ");
409 }
410 self.out.push_str("...");
411 }
412 self.out.push(')');
413 match signature.returns.as_slice() {
414 [] => {}
415 [param] => {
416 self.out.push_str(" -> ");
417 self.param(param);
418 }
419 params => {
420 self.out.push_str(" -> (");
421 for (index, param) in params.iter().enumerate() {
422 if index > 0 {
423 self.out.push_str(", ");
424 }
425 self.param(param);
426 }
427 self.out.push(')');
428 }
429 }
430 }
431
432 fn param(&mut self, param: &Param) {
434 let _ = write!(self.out, "{}", param.ty);
435 self.abi(param.abi);
436 }
437
438 fn abi(&mut self, abi: Abi) {
441 let _ = match abi {
442 Abi::Plain => Ok(()),
443 Abi::Sext => write!(self.out, " sext"),
444 Abi::Zext => write!(self.out, " zext"),
445 Abi::ByVal { size, align } => write!(self.out, " byval({size}, align {align})"),
446 Abi::Sret { size, align } => write!(self.out, " sret({size}, align {align})"),
447 };
448 }
449
450 fn block(&mut self, func: &Func, block: Block) {
452 let _ = write!(self.out, "block{}", self.blocks[block.index()]);
453 let params = &func[block].params;
454 if !params.is_empty() {
455 self.out.push('(');
456 for (index, ¶m) in params.iter().enumerate() {
457 if index > 0 {
458 self.out.push_str(", ");
459 }
460 self.value(param);
461 let _ = write!(self.out, ": {}", func[param].ty);
462 }
463 self.out.push(')');
464 }
465 self.out.push_str(":\n");
466 for inst in func.insts(block) {
467 self.inst(func, inst);
468 }
469 }
470
471 fn inst(&mut self, func: &Func, inst: Inst) {
473 let data = func[inst];
474 let chain = Chain { takes: func.mem_in(inst), gives: func.mem_out(inst).is_some() };
479 self.out.push_str(" ");
480 for (index, result) in data.results().enumerate() {
481 if index > 0 {
482 self.out.push_str(", ");
483 }
484 self.value(result);
485 }
486 if data.results > 0 {
487 self.out.push_str(" = ");
488 }
489 self.out.push_str(data.opcode.name());
490 self.result_types(func, &data, chain);
491 let _ = write!(self.out, "{}", data.flags);
492 self.operands(func, &data, chain);
493 if let Some(mem) = chain.takes {
494 self.out.push_str(" [mem ");
495 self.value(mem);
496 self.out.push(']');
497 }
498 self.out.push('\n');
499 }
500
501 fn result_types(&mut self, func: &Func, data: &InstData, chain: Chain) {
503 let results = without_mem(data.results().collect(), chain);
504 match results.as_slice() {
505 [] => {}
506 _ if implied_result(data.opcode) => {}
507 [result] => {
508 let ty = func[*result].ty;
509 let takes_the_same = func[data.args].first().is_some_and(|&arg| func[arg].ty == ty);
510 if !takes_the_same {
511 let _ = write!(self.out, ".{ty}");
512 }
513 }
514 types => {
517 self.out.push_str(".(");
518 for (index, &result) in types.iter().enumerate() {
519 if index > 0 {
520 self.out.push_str(", ");
521 }
522 let _ = write!(self.out, "{}", func[result].ty);
523 }
524 self.out.push(')');
525 }
526 }
527 }
528
529 fn operands(&mut self, func: &Func, data: &InstData, chain: Chain) {
531 let all = &func[data.args];
532 let args = &all[..all.len() - usize::from(chain.takes.is_some())];
533 match data.extra {
534 Extra::None => self.value_list_spaced(args),
535 Extra::Imm(imm) => {
536 self.out.push(' ');
537 let ty = data.first_result.map_or(Type::VOID, |result| func[result].ty);
538 self.imm(func[imm], ty);
539 }
540 Extra::Symbol(symbol) => {
541 let _ = write!(self.out, " @{}", self.names.resolve(symbol));
542 if !args.is_empty() {
543 self.out.push('(');
544 self.value_list(args);
545 self.out.push(')');
546 }
547 }
548 Extra::IntPred(pred) => {
549 let _ = write!(self.out, " {}", pred.name());
550 self.value_list_spaced(args);
551 }
552 Extra::FloatPred(pred) => {
553 let _ = write!(self.out, " {}", pred.name());
554 self.value_list_spaced(args);
555 }
556 Extra::Mem(mem) => {
557 match (data.opcode, args) {
558 (Opcode::Store | Opcode::AtomicStore, [value, addr]) => {
561 self.out.push(' ');
562 self.value(*value);
563 self.out.push_str(" -> ");
564 self.value(*addr);
565 }
566 _ => self.value_list_spaced(args),
567 }
568 self.mem(func[mem]);
569 }
570 Extra::VaObject(info) => {
571 let info = func[info];
572 self.value_list_spaced(args);
573 self.mem(func[info.mem]);
574 let slots = &func[info.slots];
575 if !slots.is_empty() {
576 self.out.push_str(", in(");
577 for (index, &slot) in slots.iter().enumerate() {
578 if index > 0 {
579 self.out.push_str(", ");
580 }
581 self.slot(slot);
582 }
583 self.out.push(')');
584 }
585 }
586 Extra::Rmw(op, mem) => {
587 let _ = write!(self.out, " {}", op.name());
588 self.value_list_spaced(args);
589 self.mem(func[mem]);
590 }
591 Extra::Class(class) => {
593 self.value_list_spaced(args);
594 let _ = write!(self.out, ", class {}", class.name());
595 }
596 Extra::Owner(owner) => {
597 self.value_list_spaced(args);
598 let _ = write!(self.out, ", to {}", owner.name());
599 }
600 Extra::Node(node) => {
601 self.value_list_spaced(args);
602 let _ = write!(self.out, ", tbaa !{}", node.index());
603 }
604 Extra::Reason(reason) => {
605 self.out.push(' ');
606 self.string(self.names.resolve(reason).as_bytes());
607 }
608 Extra::Order(order) => {
609 let _ = write!(self.out, " {}", order.name());
610 }
611 Extra::Prefetch(hint) => {
612 self.value_list_spaced(args);
613 let _ = write!(self.out, ", {hint}");
614 }
615 Extra::Depth(depth) => {
618 let _ = write!(self.out, " depth {depth}");
619 }
620 Extra::Targets(targets) => {
621 if !args.is_empty() {
624 self.value_list_spaced(args);
625 self.out.push(',');
626 }
627 for (index, &call) in func[targets].iter().enumerate() {
628 self.out.push_str(if index > 0 { ", " } else { " " });
629 self.block_call(func, call);
630 }
631 }
632 Extra::Call(call) => {
633 let info = func[call];
634 let rest = match info.callee {
635 Some(callee) => {
636 let _ = write!(self.out, " @{}", self.names.resolve(callee));
637 args
638 }
639 None => {
642 self.out.push(' ');
643 match args.split_first() {
644 Some((&addr, rest)) => {
645 self.value(addr);
646 rest
647 }
648 None => {
649 self.out.push_str("%?");
650 &[]
651 }
652 }
653 }
654 };
655 self.out.push('(');
656 let named = func[info.signature].params.len();
659 let varargs = &func[info.varargs];
660 for (index, &arg) in rest.iter().enumerate() {
661 if index > 0 {
662 self.out.push_str(", ");
663 }
664 self.value(arg);
665 if let Some(&abi) = index.checked_sub(named).and_then(|at| varargs.get(at)) {
666 self.abi(abi);
667 }
668 }
669 self.out.push_str(") : ");
670 self.signature(&func[info.signature]);
671 }
672 Extra::Switch(switch) => {
673 let info = func[switch];
674 let ty = args.first().map_or(Type::VOID, |&arg| func[arg].ty);
675 self.value_list_spaced(args);
676 if let Some((&default, cases)) = func[info.targets].split_first() {
677 self.out.push_str(", ");
678 self.block_call(func, default);
679 self.out.push_str(", [");
680 for (index, (&case, &value)) in cases.iter().zip(&func[info.cases]).enumerate()
681 {
682 if index > 0 {
683 self.out.push_str(", ");
684 }
685 self.imm(value, ty);
686 self.out.push_str(" => ");
687 self.block_call(func, case);
688 }
689 self.out.push(']');
690 }
691 }
692 Extra::Asm(asm) => {
693 let info = func[asm];
694 self.out.push(' ');
695 self.string(self.names.resolve(info.template).as_bytes());
696 self.out.push_str(", ");
697 self.string(self.names.resolve(info.constraints).as_bytes());
698 self.out.push_str(", ");
699 self.string(self.names.resolve(info.clobbers).as_bytes());
700 self.out.push('(');
701 self.value_list(args);
702 self.out.push(')');
703 if !info.targets.is_empty() {
704 self.out.push_str(", labels [");
705 for (index, &call) in func[info.targets].iter().enumerate() {
706 if index > 0 {
707 self.out.push_str(", ");
708 }
709 self.block_call(func, call);
710 }
711 self.out.push(']');
712 }
713 }
714 }
715 }
716
717 fn value_list_spaced(&mut self, args: &[Value]) {
719 if args.is_empty() {
720 return;
721 }
722 self.out.push(' ');
723 self.value_list(args);
724 }
725
726 fn value_list(&mut self, args: &[Value]) {
728 for (index, &arg) in args.iter().enumerate() {
729 if index > 0 {
730 self.out.push_str(", ");
731 }
732 self.value(arg);
733 }
734 }
735
736 fn block_call(&mut self, func: &Func, call: BlockCall) {
741 let _ = write!(self.out, "block{}", self.blocks[call.block.index()]);
742 let args = &func[call.args];
743 if !args.is_empty() {
744 self.out.push('(');
745 self.value_list(args);
746 self.out.push(')');
747 }
748 if let Some(parts) = call.hint.taken() {
749 let _ = write!(self.out, " taken {parts}");
750 }
751 }
752
753 fn mem(&mut self, info: MemInfo) {
755 if info.size != 0 {
756 let _ = write!(self.out, ", size {}", info.size);
757 }
758 let _ = write!(self.out, ", align {}", info.align);
759 if info.order != MemOrder::NotAtomic {
760 let _ = write!(self.out, ", {}", info.order.name());
761 }
762 if let Some(tbaa) = info.tbaa {
763 let _ = write!(self.out, ", tbaa !{}", tbaa.index());
764 }
765 if info.owns != 0 {
766 let _ = write!(self.out, ", owns {}", info.owns);
767 }
768 if info.restrict.clique != 0 {
769 let _ =
770 write!(self.out, ", restrict({}, {})", info.restrict.clique, info.restrict.base);
771 }
772 }
773
774 fn slot(&mut self, slot: Slot) {
776 match slot {
777 Slot::Integer { offset, size } => {
778 let _ = write!(self.out, "int {size} at {offset}");
779 }
780 Slot::Float { offset, format } => {
781 let _ = write!(self.out, "float {} at {offset}", format.name());
782 }
783 }
784 }
785
786 fn value(&mut self, value: Value) {
788 match self.values.get(value.index()).copied() {
789 Some(number) if number != u32::MAX => {
790 let _ = write!(self.out, "%{number}");
791 }
792 _ => self.out.push_str("%?"),
796 }
797 }
798
799 fn imm(&mut self, imm: Imm, ty: Type) {
801 let scalar = if ty.is_vector() { ty.lane() } else { ty };
802 if scalar.is_float() {
803 let _ = write!(self.out, "{:#x}", imm.bits());
806 } else if scalar.is_int() {
807 let _ = write!(self.out, "{}", imm.signed(scalar));
808 } else {
809 let _ = write!(self.out, "{:#x}", imm.bits());
810 }
811 }
812
813 fn meta_node(&mut self, meta: Meta) {
815 let _ = write!(self.out, "!{} = ", meta.index());
816 match self.module[meta] {
817 MetaNode::Tbaa(node) => {
818 self.out.push_str("tbaa ");
819 self.string(self.names.resolve(node.name).as_bytes());
820 if let Some(parent) = node.parent {
821 let _ = write!(self.out, ", parent !{}", parent.index());
822 }
823 let _ = write!(self.out, ", offset {}", node.offset);
824 }
825 MetaNode::Plane(node) => {
826 self.out.push_str("plane ");
827 let _ = match node {
828 PlaneNode::Type(ty) => write!(self.out, "!{}", ty.index()),
829 PlaneNode::NoType => self.out.write_str("no_type"),
830 PlaneNode::Character => self.out.write_str("character"),
831 PlaneNode::PointerSlot(k) => write!(self.out, "pointer_slot {k}"),
832 };
833 }
834 }
835 self.out.push('\n');
836 }
837
838 fn linkage(&mut self, linkage: Linkage, visibility: Visibility) {
840 let _ = write!(self.out, ", linkage({})", linkage.name());
841 if visibility != Visibility::Default {
842 let _ = write!(self.out, ", visibility({})", visibility.name());
843 }
844 }
845
846 fn section(&mut self, section: Option<Symbol>) {
848 if let Some(section) = section {
849 self.out.push_str(", section ");
850 self.string(self.names.resolve(section).as_bytes());
851 }
852 }
853
854 fn string(&mut self, bytes: &[u8]) {
856 self.out.push('"');
857 for &byte in bytes {
858 match byte {
859 b'"' => self.out.push_str("\\\""),
860 b'\\' => self.out.push_str("\\\\"),
861 0x20..=0x7e => self.out.push(byte as char),
862 _ => {
863 let _ = write!(self.out, "\\{byte:02x}");
864 }
865 }
866 }
867 self.out.push('"');
868 }
869}
870
871#[cfg(test)]
872mod tests {
873 use rucc_base::Interner;
874 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
875
876 use super::*;
877 use crate::Restrict;
878 use crate::func::Builder;
879 use crate::inst::{AsmInfo, CallInfo, MetaNode, PlaneNode, SwitchInfo, TbaaNode, VaInfo};
880 use crate::module::{AliasKind, TlsModel};
881 use crate::{
882 AttrSet, Attrs, Bounds, Facts, Flags, FloatPred, FpContract, IntPred, Owner, RmwOp,
883 StorageClass,
884 };
885
886 fn target() -> TargetInfo {
887 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
888 }
889
890 #[test]
891 fn the_example_in_the_spec() {
892 let mut names = Interner::new();
893 let mut module = Module::new(names.intern("example.c"), &target());
894
895 let char_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
896 name: names.intern("omnipotent char"),
897 parent: None,
898 offset: 0,
899 }));
900 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
901 name: names.intern("int"),
902 parent: Some(char_node),
903 offset: 0,
904 }));
905
906 let i32_ = Type::int(32);
907 let zero_bits = module.add_imm(Imm::int(0, i32_));
908 let init = module.push_data(&[Datum::Scalar { ty: i32_, value: zero_bits }]);
909 let mut counter = Global::new(names.intern("counter"), 4, 4);
910 counter.linkage = Linkage::Internal;
911 counter.init = Some(init);
912 module.add_global(counter);
913
914 let mut func = Func::new(
915 names.intern("sum"),
916 Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
917 );
918 func.attrs = Attrs { set: AttrSet::NOUNWIND, fp_contract: FpContract::On };
919 let entry = func.create_block();
920 let n = func.append_param(entry, i32_);
921 let header = func.create_block();
922 let acc = func.append_param(header, i32_);
923 let i = func.append_param(header, i32_);
924 let exit = func.create_block();
925 let result = func.append_param(exit, i32_);
926
927 let mut b = Builder::new(&mut func, entry);
928 let zero = b.iconst(i32_, 0);
929 let cmp = b.icmp(IntPred::Sle, n, zero);
930 b.br_if(cmp, exit, &[zero], header, &[zero, zero]);
931
932 let mut b = Builder::new(&mut func, header);
933 let one = b.iconst(i32_, 1);
934 let next = b.binary(Opcode::Add, i, one, Flags::NSW);
935 let total = b.binary(Opcode::Add, acc, next, Flags::NSW);
936 let done = b.icmp(IntPred::Sge, next, n);
937 b.br_if(done, exit, &[total], header, &[total, next]);
938
939 let mut b = Builder::new(&mut func, exit);
940 let address = b.value(
941 InstData {
942 extra: Extra::Symbol(names.intern("counter")),
943 ..InstData::new(Opcode::GlobalAddr)
944 },
945 Type::PTR,
946 );
947 b.store(
948 result,
949 address,
950 MemInfo {
951 size: 0,
952 align: 4,
953 order: MemOrder::NotAtomic,
954 tbaa: Some(int_node),
955 owns: 0,
956 restrict: Restrict::NONE,
957 },
958 Flags::NONE,
959 );
960 b.ret(&[result]);
961 module.add_func(func);
962
963 assert_eq!(print(&module, &names), crate::fixtures::EXAMPLE);
964 }
965
966 #[test]
967 fn the_memory_safety_instructions() {
968 let mut names = Interner::new();
969 let mut module = Module::new(names.intern("safety.c"), &target());
970 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
971 name: names.intern("int"),
972 parent: None,
973 offset: 0,
974 }));
975 let int_plane = module.add_meta(MetaNode::Plane(PlaneNode::Type(int_node)));
976 let character = module.add_meta(MetaNode::Plane(PlaneNode::Character));
977 module.add_meta(MetaNode::Plane(PlaneNode::NoType));
978 module.add_meta(MetaNode::Plane(PlaneNode::PointerSlot(3)));
979
980 let i64_ = Type::int(64);
981 let mut func = Func::new(
982 names.intern("safety"),
983 Signature::new().with_params(&[Type::PTR, i64_]).with_returns(&[Type::PTR]),
984 );
985 let entry = func.create_block();
986 let p = func.append_param(entry, Type::PTR);
987 let off = func.append_param(entry, i64_);
988
989 let mut b = Builder::new(&mut func, entry);
990 let of = b.unary(Opcode::CapOf, p, Type::CAP);
991 b.inst(InstData::new(Opcode::CapNull), &[Type::CAP]);
992 b.unary(Opcode::CapRecover, p, Type::CAP);
993 let args = b.func().push_values(&[of, p, p]);
994 b.value(InstData { args, ..InstData::new(Opcode::CapLoad) }, Type::CAP);
995 let len = b.iconst(i64_, 8);
996 let args = b.func().push_values(&[of, off, len]);
997 let narrow = b.value(InstData { args, ..InstData::new(Opcode::CapNarrow) }, Type::CAP);
998 let args = b.func().push_values(&[of, p, p, narrow]);
999 b.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
1000 let args = b.func().push_values(&[of, p, off]);
1003 b.value(InstData { args, ..InstData::new(Opcode::CapExtent) }, i64_);
1004
1005 let args = b.func().push_values(&[p, off]);
1006 let derived = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1007 let four = MemInfo {
1008 size: 4,
1009 align: 4,
1010 order: MemOrder::NotAtomic,
1011 tbaa: None,
1012 owns: 0,
1013 restrict: Restrict::NONE,
1014 };
1015 let mut check = |opcode, info: Option<MemInfo>, on: &[Value]| {
1016 let args = b.func().push_values(on);
1017 let extra = match info {
1018 Some(info) => Extra::Mem(b.func().add_mem(info)),
1019 None => Extra::None,
1020 };
1021 b.inst(InstData { args, extra, ..InstData::new(opcode) }, &[]);
1022 };
1023 check(Opcode::CheckBounds, Some(four), &[of, p]);
1024 check(Opcode::CheckBounds, Some(four), &[of, p, off]);
1027 check(Opcode::CheckLive, None, &[of, p]);
1028 check(Opcode::CheckType, Some(MemInfo { tbaa: Some(int_plane), ..four }), &[of, p]);
1029 check(Opcode::CheckInit, Some(MemInfo { align: 1, ..four }), &[of, p]);
1030 check(Opcode::CheckDeriv, None, &[of, p, derived, len]);
1031 check(Opcode::CheckRace, Some(MemInfo { align: 1, ..four }), &[of, p]);
1032 check(Opcode::CheckFree, None, &[of, p]);
1035 let named = Restrict { clique: 1, base: 2 };
1039 check(Opcode::CheckRestrictRead, Some(MemInfo { restrict: named, ..four }), &[p]);
1040 check(Opcode::CheckRestrictWrite, Some(MemInfo { restrict: named, ..four }), &[p]);
1041
1042 let mut plane = |opcode, extra| {
1043 let args = b.func().push_values(&[p, off]);
1044 b.inst(InstData { args, extra, ..InstData::new(opcode) }, &[]);
1045 };
1046 plane(Opcode::MetaBegin, Extra::Class(StorageClass::Allocated));
1047 plane(Opcode::MetaType, Extra::Node(character));
1048 plane(Opcode::MetaInit, Extra::None);
1049 plane(Opcode::MetaEpoch, Extra::None);
1050 plane(Opcode::MetaTransfer, Extra::Owner(Owner::Device));
1051 plane(Opcode::MetaEnd, Extra::None);
1052
1053 let args = b.func().push_values(&[p, derived, off]);
1056 b.inst(InstData { args, ..InstData::new(Opcode::MetaTypeCopy) }, &[]);
1057 let args = b.func().push_values(&[p, derived, off]);
1058 b.inst(InstData { args, ..InstData::new(Opcode::MetaInitCopy) }, &[]);
1059 let args = b.func().push_values(&[p, derived, off]);
1060 b.inst(InstData { args, ..InstData::new(Opcode::CapCopy) }, &[]);
1061
1062 let mut edge = |opcode| {
1065 let args = b.func().push_values(&[p]);
1066 b.inst(InstData { args, ..InstData::new(opcode) }, &[]);
1067 };
1068 edge(Opcode::MetaRelease);
1069 edge(Opcode::MetaAcquire);
1070
1071 b.inst(InstData::new(Opcode::MetaFenceRelease), &[]);
1074 b.inst(InstData::new(Opcode::MetaFenceAcquire), &[]);
1075
1076 let reason = names.intern("hand written assembly, checked by review");
1077 b.inst(
1078 InstData { extra: Extra::Reason(reason), ..InstData::new(Opcode::SafeRegionBegin) },
1079 &[],
1080 );
1081 b.inst(InstData::new(Opcode::SafeRegionEnd), &[]);
1082
1083 let scope = MemInfo {
1087 size: 112,
1088 align: 8,
1089 order: MemOrder::NotAtomic,
1090 tbaa: None,
1091 owns: 0,
1092 restrict: Restrict { clique: 1, base: 2 },
1093 };
1094 let args = b.func().push_values(&[p]);
1095 let extra = Extra::Mem(b.func().add_mem(scope));
1096 b.inst(InstData { args, extra, ..InstData::new(Opcode::RestrictEnter) }, &[]);
1097 let args = b.func().push_values(&[p]);
1098 b.inst(InstData { args, ..InstData::new(Opcode::RestrictLeave) }, &[]);
1099 b.ret(&[p]);
1100
1101 func.set_facts(
1102 p,
1103 Facts {
1104 bounds: Some(Bounds { lo: p, ext: off }),
1105 init: Some(4),
1106 align: Some(8),
1107 live: true,
1108 },
1109 );
1110 func.set_facts(derived, Facts { align: Some(4), ..Facts::NONE });
1111 module.add_func(func);
1112
1113 assert_eq!(print(&module, &names), crate::fixtures::SAFETY);
1114 }
1115
1116 #[test]
1117 fn one_of_almost_everything() {
1118 let mut names = Interner::new();
1119 let mut module = Module::new(names.intern("zoo.c"), &target());
1120 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
1121 name: names.intern("int"),
1122 parent: None,
1123 offset: 0,
1124 }));
1125
1126 let i32_ = Type::int(32);
1127 let i64_ = Type::int(64);
1128 let f64_ = Type::float(crate::Float::F64);
1129 let mut func = Func::new(
1130 names.intern("zoo"),
1131 Signature::new().with_params(&[i32_, Type::PTR]).with_returns(&[i32_]),
1132 );
1133 let entry = func.create_block();
1134 let n = func.append_param(entry, i32_);
1135 let p = func.append_param(entry, Type::PTR);
1136 let middle = func.create_block();
1137 let other = func.create_block();
1138 let exit = func.create_block();
1139 let taken = func.append_param(exit, i32_);
1140 let arrival = func.create_block();
1141
1142 let mut b = Builder::new(&mut func, entry);
1143 let minus_one = b.iconst(i64_, -1);
1144 let half = b.fconst(f64_, 0x3ff8_0000_0000_0000);
1145 let seven = b.func().add_imm(Imm::int(7, i32_));
1146 let vector = b.value(
1147 InstData { extra: Extra::Imm(seven), ..InstData::new(Opcode::Splat) },
1148 Type::vector(i32_, 4),
1149 );
1150 let stack = b.func().add_mem(MemInfo {
1151 size: 16,
1152 align: 8,
1153 order: MemOrder::NotAtomic,
1154 tbaa: None,
1155 owns: 0,
1156 restrict: Restrict::NONE,
1157 });
1158 let slot = b.value(
1159 InstData { extra: Extra::Mem(stack), ..InstData::new(Opcode::Alloca) },
1160 Type::PTR,
1161 );
1162 let args = b.func().push_values(&[slot, minus_one]);
1163 let addr = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1164 let plain = MemInfo {
1165 size: 0,
1166 align: 4,
1167 order: MemOrder::NotAtomic,
1168 tbaa: Some(int_node),
1169 owns: 0,
1170 restrict: Restrict::NONE,
1171 };
1172 let loaded = b.load(i32_, addr, plain, Flags::NONE);
1173 b.store(loaded, addr, plain, Flags::VOLATILE);
1174
1175 let atomic = b.func().add_mem(MemInfo {
1176 size: 0,
1177 align: 4,
1178 order: MemOrder::SeqCst,
1179 tbaa: None,
1180 owns: 0,
1181 restrict: Restrict::NONE,
1182 });
1183 let args = b.func().push_values(&[addr, n]);
1184 let old = b.value(
1185 InstData {
1186 args,
1187 extra: Extra::Rmw(RmwOp::Add, atomic),
1188 ..InstData::new(Opcode::AtomicRmw)
1189 },
1190 i32_,
1191 );
1192 let args = b.func().push_values(&[addr, old, n]);
1193 b.inst(
1194 InstData { args, extra: Extra::Mem(atomic), ..InstData::new(Opcode::Cmpxchg) },
1195 &[i32_, Type::I1],
1196 );
1197 b.inst(
1198 InstData { extra: Extra::Order(MemOrder::SeqCst), ..InstData::new(Opcode::Fence) },
1199 &[],
1200 );
1201 b.unary(Opcode::SExt, n, i64_);
1202 b.fcmp(FloatPred::Oeq, half, half, Flags::NONE);
1203 let args = b.func().push_values(&[n, n]);
1204 b.inst(InstData { args, ..InstData::new(Opcode::SAddOverflow) }, &[i32_, Type::I1]);
1205 let puts = b.func().add_signature(
1206 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]).variadic(),
1207 );
1208 b.call_varargs(
1209 names.intern("puts"),
1210 puts,
1211 &[p, slot],
1212 &[Abi::ByVal { size: 16, align: 8 }],
1213 );
1214 let indirect =
1215 b.func().add_signature(Signature::new().with_params(&[i32_]).with_returns(&[i32_]));
1216 let varargs = b.func().push_abis(&[]);
1217 let info = b.func().add_call(CallInfo { callee: None, signature: indirect, varargs });
1218 let args = b.func().push_values(&[p, n]);
1219 b.value(
1220 InstData {
1221 args,
1222 extra: Extra::Call(info),
1223 flags: Flags::NOFREE,
1224 ..InstData::new(Opcode::CallIndirect)
1225 },
1226 i32_,
1227 );
1228 let copy = b.func().add_mem(MemInfo {
1229 size: 16,
1230 align: 8,
1231 order: MemOrder::NotAtomic,
1232 tbaa: None,
1233 owns: 0,
1234 restrict: Restrict::NONE,
1235 });
1236 let args = b.func().push_values(&[slot, p]);
1237 b.inst(InstData { args, extra: Extra::Mem(copy), ..InstData::new(Opcode::Memcpy) }, &[]);
1238 let asm = b.func().add_asm(AsmInfo {
1239 template: names.intern("pause"),
1240 constraints: names.intern(""),
1241 clobbers: names.intern("memory"),
1242 targets: crate::inst::BlockCallList::EMPTY,
1243 });
1244 b.inst(
1245 InstData {
1246 flags: Flags::VOLATILE,
1247 extra: Extra::Asm(asm),
1248 ..InstData::new(Opcode::InlineAsm)
1249 },
1250 &[],
1251 );
1252 let object = b.func().add_mem(MemInfo {
1253 size: 16,
1254 align: 8,
1255 order: MemOrder::NotAtomic,
1256 tbaa: None,
1257 owns: 0,
1258 restrict: Restrict::NONE,
1259 });
1260 let slots = b.func().push_slots(&[
1261 Slot::Integer { offset: 0, size: 8 },
1262 Slot::Float { offset: 8, format: rucc_base::float::Format::Double },
1263 ]);
1264 let read = b.func().add_va_object(VaInfo { mem: object, slots });
1265 let args = b.func().push_values(&[p]);
1266 b.value(
1267 InstData { args, extra: Extra::VaObject(read), ..InstData::new(Opcode::VaObject) },
1268 Type::PTR,
1269 );
1270 let args = b.func().push_values(&[vector]);
1271 b.value(
1272 InstData {
1273 args,
1274 extra: Extra::Symbol(names.intern("x86.sse2.pmovmskb")),
1275 ..InstData::new(Opcode::TargetIntrinsic)
1276 },
1277 i32_,
1278 );
1279 b.jump(middle, &[]);
1280
1281 let mut b = Builder::new(&mut func, middle);
1282 let cases = b.func().push_imms(&[Imm::int(0, i32_), Imm::int(-1, i32_)]);
1283 let default = BlockCall::to(other);
1284 let first = BlockCall::new(exit, b.func().push_values(&[n]));
1285 let second = BlockCall::to(other);
1286 let targets = b.func().push_block_calls(&[default, first, second]);
1287 let switch = b.func().add_switch(SwitchInfo { targets, cases });
1288 let args = b.func().push_values(&[n]);
1289 b.inst(
1290 InstData { args, extra: Extra::Switch(switch), ..InstData::new(Opcode::Switch) },
1291 &[],
1292 );
1293
1294 let mut b = Builder::new(&mut func, other);
1295 let address = b.block_addr(arrival);
1296 b.indirect_br(address, &[arrival]);
1297
1298 let mut b = Builder::new(&mut func, exit);
1299 b.ret(&[taken]);
1300
1301 let mut b = Builder::new(&mut func, arrival);
1302 let call = BlockCall::new(exit, b.func().push_values(&[n]));
1303 let targets = b.func().push_block_calls(&[call]);
1304 let goto = b.func().add_asm(AsmInfo {
1305 template: names.intern("jmp %l0"),
1306 constraints: names.intern(""),
1307 clobbers: names.intern(""),
1308 targets,
1309 });
1310 b.inst(InstData { extra: Extra::Asm(goto), ..InstData::new(Opcode::InlineAsm) }, &[]);
1311
1312 module.add_func(func);
1313
1314 assert_eq!(print(&module, &names), crate::fixtures::ZOO);
1315 }
1316
1317 #[test]
1318 fn the_shapes_a_symbol_comes_in() {
1319 let mut names = Interner::new();
1320 let mut module = Module::new(names.intern("data.c"), &target());
1321
1322 let i32_ = Type::int(32);
1323 let text = module.push_bytes(b"hi\x00\xff\"\\");
1324 let entry_name = names.intern("hi.str");
1325 let forward = module.add_reloc(Reloc { symbol: entry_name, addend: 8, size: 8 });
1326 let backward = module.add_reloc(Reloc { symbol: entry_name, addend: -8, size: 8 });
1327 let seven = module.add_imm(Imm::int(7, i32_));
1328 let image = module.push_data(&[
1329 Datum::Bytes(text),
1330 Datum::Zero(2),
1331 Datum::Scalar { ty: i32_, value: seven },
1332 Datum::Addr(forward),
1333 Datum::Addr(backward),
1334 ]);
1335 let mut table = Global::new(names.intern("table"), 28, 8);
1336 table.init = Some(image);
1337 table.constant = true;
1338 table.section = Some(names.intern(".rodata.rel"));
1339 module.add_global(table);
1340
1341 let mut errno = Global::new(names.intern("errno"), 4, 4);
1342 errno.tls = Some(TlsModel::InitialExec);
1343 errno.visibility = Visibility::Hidden;
1344 module.add_global(errno);
1345
1346 let mut nothing = Global::new(names.intern("nothing"), 0, 1);
1351 nothing.init = Some(module.push_data(&[]));
1352 nothing.linkage = Linkage::Internal;
1353 module.add_global(nothing);
1354
1355 let mut alias = Alias::new(names.intern("total"), names.intern("table"));
1356 alias.linkage = Linkage::Weak;
1357 module.add_alias(alias);
1358 let mut memcpy = Alias::new(names.intern("memcpy"), names.intern("memcpy.resolve"));
1359 memcpy.kind = AliasKind::IFunc;
1360 memcpy.visibility = Visibility::Protected;
1361 module.add_alias(memcpy);
1362
1363 let mut puts = Func::new(
1364 names.intern("puts"),
1365 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
1366 );
1367 puts.linkage = Linkage::External;
1368 puts.attrs.set = AttrSet::NOUNWIND | AttrSet::WILLRETURN;
1369 module.add_func(puts);
1370
1371 let mut helper =
1372 Func::new(names.intern("helper"), Signature::new().with_returns(&[i32_, i32_]));
1373 helper.linkage = Linkage::Internal;
1374 helper.section = Some(names.intern(".text.hot"));
1375 helper.attrs.set = AttrSet::READNONE | AttrSet::ALWAYS_INLINE;
1376 let block = helper.create_block();
1377 let mut b = Builder::new(&mut helper, block);
1378 let one = b.iconst(i32_, 1);
1379 b.ret(&[one, one]);
1380 module.add_func(helper);
1381
1382 assert_eq!(print(&module, &names), crate::fixtures::SYMBOLS);
1383 }
1384
1385 #[test]
1386 fn a_signature_writes_what_the_abi_asks_of_each_parameter() {
1387 let mut names = Interner::new();
1388 let module = Module::new(names.intern("abi.c"), &target());
1389 let mut func = Func::new(
1390 names.intern("f"),
1391 Signature::new()
1392 .and_param(Param::with_abi(Type::PTR, Abi::Sret { size: 24, align: 8 }))
1393 .and_param(Param::with_abi(Type::PTR, Abi::ByVal { size: 16, align: 8 }))
1394 .and_param(Param::with_abi(Type::int(8), Abi::Zext))
1395 .and_param(Param::new(Type::int(32))),
1396 );
1397 let entry = func.create_block();
1398 for param in [Type::PTR, Type::PTR, Type::int(8), Type::int(32)] {
1399 func.append_param(entry, param);
1400 }
1401 let mut b = Builder::new(&mut func, entry);
1402 b.ret(&[]);
1403
1404 assert_eq!(
1405 print_func(&module, &func, &names),
1406 "\
1407func @f(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext, i32), linkage(external) {
1408block0(%0: ptr, %1: ptr, %2: i8, %3: i32):
1409 return
1410}
1411"
1412 );
1413 }
1414
1415 #[test]
1416 fn a_call_writes_what_the_abi_asks_of_an_argument_its_signature_does_not_name() {
1417 let mut names = Interner::new();
1418 let module = Module::new(names.intern("varargs.c"), &target());
1419 let i32_ = Type::int(32);
1420 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR]));
1421 let entry = func.create_block();
1422 let p = func.append_param(entry, Type::PTR);
1423 let mut b = Builder::new(&mut func, entry);
1424 let sig = b.func().add_signature(
1425 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]).variadic(),
1426 );
1427 let one = b.iconst(i32_, 1);
1428 b.call_varargs(
1429 names.intern("printf"),
1430 sig,
1431 &[p, one, p],
1432 &[Abi::Plain, Abi::ByVal { size: 24, align: 8 }],
1433 );
1434 b.ret(&[]);
1435
1436 assert_eq!(
1437 print_func(&module, &func, &names),
1438 "\
1439func @f(ptr), linkage(external) {
1440block0(%0: ptr):
1441 %1 = iconst.i32 1
1442 %2 = call @printf(%0, %1, %0 byval(24, align 8)) : (ptr, ...) -> i32
1443 return
1444}
1445"
1446 );
1447 }
1448
1449 #[test]
1450 fn numbering_follows_the_text_and_not_the_tables() {
1451 let mut names = Interner::new();
1456 let mut module = Module::new(names.intern("order.c"), &target());
1457 let i32_ = Type::int(32);
1458 let mut func = Func::new(names.intern("f"), Signature::new().with_returns(&[i32_]));
1459 let entry = func.create_block();
1460 let middle = func.create_block();
1461 let exit = func.create_block();
1462 let arrived = func.append_param(exit, i32_);
1463
1464 let mut b = Builder::new(&mut func, exit);
1465 b.ret(&[arrived]);
1466 let mut b = Builder::new(&mut func, middle);
1467 let two = b.iconst(i32_, 2);
1468 b.jump(exit, &[two]);
1469 let mut b = Builder::new(&mut func, entry);
1470 b.jump(middle, &[]);
1471 module.add_func(func);
1472
1473 assert_eq!(
1474 print_func(&module, &module[module.funcs().next().unwrap()], &names),
1475 "\
1476func @f() -> i32, linkage(external) {
1477block0:
1478 jump block1
1479
1480block1:
1481 %0 = iconst.i32 2
1482 jump block2(%0)
1483
1484block2(%1: i32):
1485 return %1
1486}
1487"
1488 );
1489 }
1490}