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.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.owns != 0 {
731 let _ = write!(self.out, ", owns {}", info.owns);
732 }
733 if info.restrict.clique != 0 {
734 let _ =
735 write!(self.out, ", restrict({}, {})", info.restrict.clique, info.restrict.base);
736 }
737 }
738
739 fn slot(&mut self, slot: Slot) {
741 match slot {
742 Slot::Integer { offset, size } => {
743 let _ = write!(self.out, "int {size} at {offset}");
744 }
745 Slot::Float { offset, format } => {
746 let _ = write!(self.out, "float {} at {offset}", format.name());
747 }
748 }
749 }
750
751 fn value(&mut self, value: Value) {
753 match self.values.get(value.index()).copied() {
754 Some(number) if number != u32::MAX => {
755 let _ = write!(self.out, "%{number}");
756 }
757 _ => self.out.push_str("%?"),
761 }
762 }
763
764 fn imm(&mut self, imm: Imm, ty: Type) {
766 let scalar = if ty.is_vector() { ty.lane() } else { ty };
767 if scalar.is_float() {
768 let _ = write!(self.out, "{:#x}", imm.bits());
771 } else if scalar.is_int() {
772 let _ = write!(self.out, "{}", imm.signed(scalar));
773 } else {
774 let _ = write!(self.out, "{:#x}", imm.bits());
775 }
776 }
777
778 fn meta_node(&mut self, meta: Meta) {
780 let _ = write!(self.out, "!{} = ", meta.index());
781 match self.module[meta] {
782 MetaNode::Tbaa(node) => {
783 self.out.push_str("tbaa ");
784 self.string(self.names.resolve(node.name).as_bytes());
785 if let Some(parent) = node.parent {
786 let _ = write!(self.out, ", parent !{}", parent.index());
787 }
788 let _ = write!(self.out, ", offset {}", node.offset);
789 }
790 MetaNode::Plane(node) => {
791 self.out.push_str("plane ");
792 let _ = match node {
793 PlaneNode::Type(ty) => write!(self.out, "!{}", ty.index()),
794 PlaneNode::NoType => self.out.write_str("no_type"),
795 PlaneNode::Character => self.out.write_str("character"),
796 PlaneNode::PointerSlot(k) => write!(self.out, "pointer_slot {k}"),
797 };
798 }
799 }
800 self.out.push('\n');
801 }
802
803 fn linkage(&mut self, linkage: Linkage, visibility: Visibility) {
805 let _ = write!(self.out, ", linkage({})", linkage.name());
806 if visibility != Visibility::Default {
807 let _ = write!(self.out, ", visibility({})", visibility.name());
808 }
809 }
810
811 fn section(&mut self, section: Option<Symbol>) {
813 if let Some(section) = section {
814 self.out.push_str(", section ");
815 self.string(self.names.resolve(section).as_bytes());
816 }
817 }
818
819 fn string(&mut self, bytes: &[u8]) {
821 self.out.push('"');
822 for &byte in bytes {
823 match byte {
824 b'"' => self.out.push_str("\\\""),
825 b'\\' => self.out.push_str("\\\\"),
826 0x20..=0x7e => self.out.push(byte as char),
827 _ => {
828 let _ = write!(self.out, "\\{byte:02x}");
829 }
830 }
831 }
832 self.out.push('"');
833 }
834}
835
836#[cfg(test)]
837mod tests {
838 use rucc_base::Interner;
839 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
840
841 use super::*;
842 use crate::Restrict;
843 use crate::func::Builder;
844 use crate::inst::{AsmInfo, CallInfo, MetaNode, PlaneNode, SwitchInfo, TbaaNode, VaInfo};
845 use crate::module::{AliasKind, TlsModel};
846 use crate::{
847 AttrSet, Attrs, Bounds, Facts, Flags, FloatPred, FpContract, IntPred, Owner, RmwOp,
848 StorageClass,
849 };
850
851 fn target() -> TargetInfo {
852 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
853 }
854
855 #[test]
856 fn the_example_in_the_spec() {
857 let mut names = Interner::new();
858 let mut module = Module::new(names.intern("example.c"), &target());
859
860 let char_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
861 name: names.intern("omnipotent char"),
862 parent: None,
863 offset: 0,
864 }));
865 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
866 name: names.intern("int"),
867 parent: Some(char_node),
868 offset: 0,
869 }));
870
871 let i32_ = Type::int(32);
872 let zero_bits = module.add_imm(Imm::int(0, i32_));
873 let init = module.push_data(&[Datum::Scalar { ty: i32_, value: zero_bits }]);
874 let mut counter = Global::new(names.intern("counter"), 4, 4);
875 counter.linkage = Linkage::Internal;
876 counter.init = Some(init);
877 module.add_global(counter);
878
879 let mut func = Func::new(
880 names.intern("sum"),
881 Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
882 );
883 func.attrs = Attrs { set: AttrSet::NOUNWIND, fp_contract: FpContract::On };
884 let entry = func.create_block();
885 let n = func.append_param(entry, i32_);
886 let header = func.create_block();
887 let acc = func.append_param(header, i32_);
888 let i = func.append_param(header, i32_);
889 let exit = func.create_block();
890 let result = func.append_param(exit, i32_);
891
892 let mut b = Builder::new(&mut func, entry);
893 let zero = b.iconst(i32_, 0);
894 let cmp = b.icmp(IntPred::Sle, n, zero);
895 b.br_if(cmp, exit, &[zero], header, &[zero, zero]);
896
897 let mut b = Builder::new(&mut func, header);
898 let one = b.iconst(i32_, 1);
899 let next = b.binary(Opcode::Add, i, one, Flags::NSW);
900 let total = b.binary(Opcode::Add, acc, next, Flags::NSW);
901 let done = b.icmp(IntPred::Sge, next, n);
902 b.br_if(done, exit, &[total], header, &[total, next]);
903
904 let mut b = Builder::new(&mut func, exit);
905 let address = b.value(
906 InstData {
907 extra: Extra::Symbol(names.intern("counter")),
908 ..InstData::new(Opcode::GlobalAddr)
909 },
910 Type::PTR,
911 );
912 b.store(
913 result,
914 address,
915 MemInfo {
916 size: 0,
917 align: 4,
918 order: MemOrder::NotAtomic,
919 tbaa: Some(int_node),
920 owns: 0,
921 restrict: Restrict::NONE,
922 },
923 Flags::NONE,
924 );
925 b.ret(&[result]);
926 module.add_func(func);
927
928 assert_eq!(print(&module, &names), crate::fixtures::EXAMPLE);
929 }
930
931 #[test]
932 fn the_memory_safety_instructions() {
933 let mut names = Interner::new();
934 let mut module = Module::new(names.intern("safety.c"), &target());
935 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
936 name: names.intern("int"),
937 parent: None,
938 offset: 0,
939 }));
940 let int_plane = module.add_meta(MetaNode::Plane(PlaneNode::Type(int_node)));
941 let character = module.add_meta(MetaNode::Plane(PlaneNode::Character));
942 module.add_meta(MetaNode::Plane(PlaneNode::NoType));
943 module.add_meta(MetaNode::Plane(PlaneNode::PointerSlot(3)));
944
945 let i64_ = Type::int(64);
946 let mut func = Func::new(
947 names.intern("safety"),
948 Signature::new().with_params(&[Type::PTR, i64_]).with_returns(&[Type::PTR]),
949 );
950 let entry = func.create_block();
951 let p = func.append_param(entry, Type::PTR);
952 let off = func.append_param(entry, i64_);
953
954 let mut b = Builder::new(&mut func, entry);
955 let of = b.unary(Opcode::CapOf, p, Type::CAP);
956 b.inst(InstData::new(Opcode::CapNull), &[Type::CAP]);
957 b.unary(Opcode::CapRecover, p, Type::CAP);
958 b.unary(Opcode::CapLoad, p, Type::CAP);
959 let len = b.iconst(i64_, 8);
960 let args = b.func().push_values(&[of, off, len]);
961 let narrow = b.value(InstData { args, ..InstData::new(Opcode::CapNarrow) }, Type::CAP);
962 let args = b.func().push_values(&[p, narrow]);
963 b.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
964 let args = b.func().push_values(&[of, p, off]);
967 b.value(InstData { args, ..InstData::new(Opcode::CapExtent) }, i64_);
968
969 let args = b.func().push_values(&[p, off]);
970 let derived = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
971 let four = MemInfo {
972 size: 4,
973 align: 4,
974 order: MemOrder::NotAtomic,
975 tbaa: None,
976 owns: 0,
977 restrict: Restrict::NONE,
978 };
979 let mut check = |opcode, info: Option<MemInfo>, on: &[Value]| {
980 let args = b.func().push_values(on);
981 let extra = match info {
982 Some(info) => Extra::Mem(b.func().add_mem(info)),
983 None => Extra::None,
984 };
985 b.inst(InstData { args, extra, ..InstData::new(opcode) }, &[]);
986 };
987 check(Opcode::CheckBounds, Some(four), &[of, p]);
988 check(Opcode::CheckBounds, Some(four), &[of, p, off]);
991 check(Opcode::CheckLive, None, &[of, p]);
992 check(Opcode::CheckType, Some(MemInfo { tbaa: Some(int_plane), ..four }), &[of, p]);
993 check(Opcode::CheckInit, Some(MemInfo { align: 1, ..four }), &[of, p]);
994 check(Opcode::CheckDeriv, None, &[of, p, derived, len]);
995 check(Opcode::CheckRace, None, &[of, p]);
996 let named = Restrict { clique: 1, base: 2 };
1000 check(Opcode::CheckRestrictRead, Some(MemInfo { restrict: named, ..four }), &[p]);
1001 check(Opcode::CheckRestrictWrite, Some(MemInfo { restrict: named, ..four }), &[p]);
1002
1003 let mut plane = |opcode, extra| {
1004 let args = b.func().push_values(&[p, off]);
1005 b.inst(InstData { args, extra, ..InstData::new(opcode) }, &[]);
1006 };
1007 plane(Opcode::MetaBegin, Extra::Class(StorageClass::Allocated));
1008 plane(Opcode::MetaType, Extra::Node(character));
1009 plane(Opcode::MetaInit, Extra::None);
1010 plane(Opcode::MetaTransfer, Extra::Owner(Owner::Device));
1011 plane(Opcode::MetaEnd, Extra::None);
1012
1013 let args = b.func().push_values(&[p, derived, off]);
1015 b.inst(InstData { args, ..InstData::new(Opcode::MetaTypeCopy) }, &[]);
1016 let args = b.func().push_values(&[p, derived, off]);
1017 b.inst(InstData { args, ..InstData::new(Opcode::MetaInitCopy) }, &[]);
1018
1019 let reason = names.intern("hand written assembly, checked by review");
1020 b.inst(
1021 InstData { extra: Extra::Reason(reason), ..InstData::new(Opcode::SafeRegionBegin) },
1022 &[],
1023 );
1024 b.inst(InstData::new(Opcode::SafeRegionEnd), &[]);
1025
1026 let scope = MemInfo {
1030 size: 112,
1031 align: 8,
1032 order: MemOrder::NotAtomic,
1033 tbaa: None,
1034 owns: 0,
1035 restrict: Restrict { clique: 1, base: 2 },
1036 };
1037 let args = b.func().push_values(&[p]);
1038 let extra = Extra::Mem(b.func().add_mem(scope));
1039 b.inst(InstData { args, extra, ..InstData::new(Opcode::RestrictEnter) }, &[]);
1040 let args = b.func().push_values(&[p]);
1041 b.inst(InstData { args, ..InstData::new(Opcode::RestrictLeave) }, &[]);
1042 b.ret(&[p]);
1043
1044 func.set_facts(
1045 p,
1046 Facts {
1047 bounds: Some(Bounds { lo: p, ext: off }),
1048 init: Some(4),
1049 align: Some(8),
1050 live: true,
1051 },
1052 );
1053 func.set_facts(derived, Facts { align: Some(4), ..Facts::NONE });
1054 module.add_func(func);
1055
1056 assert_eq!(print(&module, &names), crate::fixtures::SAFETY);
1057 }
1058
1059 #[test]
1060 fn one_of_almost_everything() {
1061 let mut names = Interner::new();
1062 let mut module = Module::new(names.intern("zoo.c"), &target());
1063 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
1064 name: names.intern("int"),
1065 parent: None,
1066 offset: 0,
1067 }));
1068
1069 let i32_ = Type::int(32);
1070 let i64_ = Type::int(64);
1071 let f64_ = Type::float(crate::Float::F64);
1072 let mut func = Func::new(
1073 names.intern("zoo"),
1074 Signature::new().with_params(&[i32_, Type::PTR]).with_returns(&[i32_]),
1075 );
1076 let entry = func.create_block();
1077 let n = func.append_param(entry, i32_);
1078 let p = func.append_param(entry, Type::PTR);
1079 let middle = func.create_block();
1080 let other = func.create_block();
1081 let exit = func.create_block();
1082 let taken = func.append_param(exit, i32_);
1083 let arrival = func.create_block();
1084
1085 let mut b = Builder::new(&mut func, entry);
1086 let minus_one = b.iconst(i64_, -1);
1087 let half = b.fconst(f64_, 0x3ff8_0000_0000_0000);
1088 let seven = b.func().add_imm(Imm::int(7, i32_));
1089 let vector = b.value(
1090 InstData { extra: Extra::Imm(seven), ..InstData::new(Opcode::Splat) },
1091 Type::vector(i32_, 4),
1092 );
1093 let stack = b.func().add_mem(MemInfo {
1094 size: 16,
1095 align: 8,
1096 order: MemOrder::NotAtomic,
1097 tbaa: None,
1098 owns: 0,
1099 restrict: Restrict::NONE,
1100 });
1101 let slot = b.value(
1102 InstData { extra: Extra::Mem(stack), ..InstData::new(Opcode::Alloca) },
1103 Type::PTR,
1104 );
1105 let args = b.func().push_values(&[slot, minus_one]);
1106 let addr = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1107 let plain = MemInfo {
1108 size: 0,
1109 align: 4,
1110 order: MemOrder::NotAtomic,
1111 tbaa: Some(int_node),
1112 owns: 0,
1113 restrict: Restrict::NONE,
1114 };
1115 let loaded = b.load(i32_, addr, plain, Flags::NONE);
1116 b.store(loaded, addr, plain, Flags::VOLATILE);
1117
1118 let atomic = b.func().add_mem(MemInfo {
1119 size: 0,
1120 align: 4,
1121 order: MemOrder::SeqCst,
1122 tbaa: None,
1123 owns: 0,
1124 restrict: Restrict::NONE,
1125 });
1126 let args = b.func().push_values(&[addr, n]);
1127 let old = b.value(
1128 InstData {
1129 args,
1130 extra: Extra::Rmw(RmwOp::Add, atomic),
1131 ..InstData::new(Opcode::AtomicRmw)
1132 },
1133 i32_,
1134 );
1135 let args = b.func().push_values(&[addr, old, n]);
1136 b.inst(
1137 InstData { args, extra: Extra::Mem(atomic), ..InstData::new(Opcode::Cmpxchg) },
1138 &[i32_, Type::I1],
1139 );
1140 b.inst(
1141 InstData { extra: Extra::Order(MemOrder::SeqCst), ..InstData::new(Opcode::Fence) },
1142 &[],
1143 );
1144 b.unary(Opcode::SExt, n, i64_);
1145 b.fcmp(FloatPred::Oeq, half, half, Flags::NONE);
1146 let args = b.func().push_values(&[n, n]);
1147 b.inst(InstData { args, ..InstData::new(Opcode::SAddOverflow) }, &[i32_, Type::I1]);
1148 let puts = b.func().add_signature(
1149 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]).variadic(),
1150 );
1151 b.call_varargs(
1152 names.intern("puts"),
1153 puts,
1154 &[p, slot],
1155 &[Abi::ByVal { size: 16, align: 8 }],
1156 );
1157 let indirect =
1158 b.func().add_signature(Signature::new().with_params(&[i32_]).with_returns(&[i32_]));
1159 let varargs = b.func().push_abis(&[]);
1160 let info = b.func().add_call(CallInfo { callee: None, signature: indirect, varargs });
1161 let args = b.func().push_values(&[p, n]);
1162 b.value(
1163 InstData {
1164 args,
1165 extra: Extra::Call(info),
1166 flags: Flags::NOFREE,
1167 ..InstData::new(Opcode::CallIndirect)
1168 },
1169 i32_,
1170 );
1171 let copy = b.func().add_mem(MemInfo {
1172 size: 16,
1173 align: 8,
1174 order: MemOrder::NotAtomic,
1175 tbaa: None,
1176 owns: 0,
1177 restrict: Restrict::NONE,
1178 });
1179 let args = b.func().push_values(&[slot, p]);
1180 b.inst(InstData { args, extra: Extra::Mem(copy), ..InstData::new(Opcode::Memcpy) }, &[]);
1181 let asm = b.func().add_asm(AsmInfo {
1182 template: names.intern("pause"),
1183 constraints: names.intern(""),
1184 clobbers: names.intern("memory"),
1185 targets: crate::inst::BlockCallList::EMPTY,
1186 });
1187 b.inst(
1188 InstData {
1189 flags: Flags::VOLATILE,
1190 extra: Extra::Asm(asm),
1191 ..InstData::new(Opcode::InlineAsm)
1192 },
1193 &[],
1194 );
1195 let object = b.func().add_mem(MemInfo {
1196 size: 16,
1197 align: 8,
1198 order: MemOrder::NotAtomic,
1199 tbaa: None,
1200 owns: 0,
1201 restrict: Restrict::NONE,
1202 });
1203 let slots = b.func().push_slots(&[
1204 Slot::Integer { offset: 0, size: 8 },
1205 Slot::Float { offset: 8, format: rucc_base::float::Format::Double },
1206 ]);
1207 let read = b.func().add_va_object(VaInfo { mem: object, slots });
1208 let args = b.func().push_values(&[p]);
1209 b.value(
1210 InstData { args, extra: Extra::VaObject(read), ..InstData::new(Opcode::VaObject) },
1211 Type::PTR,
1212 );
1213 let args = b.func().push_values(&[vector]);
1214 b.value(
1215 InstData {
1216 args,
1217 extra: Extra::Symbol(names.intern("x86.sse2.pmovmskb")),
1218 ..InstData::new(Opcode::TargetIntrinsic)
1219 },
1220 i32_,
1221 );
1222 b.jump(middle, &[]);
1223
1224 let mut b = Builder::new(&mut func, middle);
1225 let cases = b.func().push_imms(&[Imm::int(0, i32_), Imm::int(-1, i32_)]);
1226 let default = BlockCall { block: other, args: crate::inst::ValueList::EMPTY };
1227 let first = BlockCall { block: exit, args: b.func().push_values(&[n]) };
1228 let second = BlockCall { block: other, args: crate::inst::ValueList::EMPTY };
1229 let targets = b.func().push_block_calls(&[default, first, second]);
1230 let switch = b.func().add_switch(SwitchInfo { targets, cases });
1231 let args = b.func().push_values(&[n]);
1232 b.inst(
1233 InstData { args, extra: Extra::Switch(switch), ..InstData::new(Opcode::Switch) },
1234 &[],
1235 );
1236
1237 let mut b = Builder::new(&mut func, other);
1238 let address = b.block_addr(arrival);
1239 b.indirect_br(address, &[arrival]);
1240
1241 let mut b = Builder::new(&mut func, exit);
1242 b.ret(&[taken]);
1243
1244 let mut b = Builder::new(&mut func, arrival);
1245 let call = BlockCall { block: exit, args: b.func().push_values(&[n]) };
1246 let targets = b.func().push_block_calls(&[call]);
1247 let goto = b.func().add_asm(AsmInfo {
1248 template: names.intern("jmp %l0"),
1249 constraints: names.intern(""),
1250 clobbers: names.intern(""),
1251 targets,
1252 });
1253 b.inst(InstData { extra: Extra::Asm(goto), ..InstData::new(Opcode::InlineAsm) }, &[]);
1254
1255 module.add_func(func);
1256
1257 assert_eq!(print(&module, &names), crate::fixtures::ZOO);
1258 }
1259
1260 #[test]
1261 fn the_shapes_a_symbol_comes_in() {
1262 let mut names = Interner::new();
1263 let mut module = Module::new(names.intern("data.c"), &target());
1264
1265 let i32_ = Type::int(32);
1266 let text = module.push_bytes(b"hi\x00\xff\"\\");
1267 let entry_name = names.intern("hi.str");
1268 let forward = module.add_reloc(Reloc { symbol: entry_name, addend: 8, size: 8 });
1269 let backward = module.add_reloc(Reloc { symbol: entry_name, addend: -8, size: 8 });
1270 let seven = module.add_imm(Imm::int(7, i32_));
1271 let image = module.push_data(&[
1272 Datum::Bytes(text),
1273 Datum::Zero(2),
1274 Datum::Scalar { ty: i32_, value: seven },
1275 Datum::Addr(forward),
1276 Datum::Addr(backward),
1277 ]);
1278 let mut table = Global::new(names.intern("table"), 28, 8);
1279 table.init = Some(image);
1280 table.constant = true;
1281 table.section = Some(names.intern(".rodata.rel"));
1282 module.add_global(table);
1283
1284 let mut errno = Global::new(names.intern("errno"), 4, 4);
1285 errno.tls = Some(TlsModel::InitialExec);
1286 errno.visibility = Visibility::Hidden;
1287 module.add_global(errno);
1288
1289 let mut nothing = Global::new(names.intern("nothing"), 0, 1);
1294 nothing.init = Some(module.push_data(&[]));
1295 nothing.linkage = Linkage::Internal;
1296 module.add_global(nothing);
1297
1298 let mut alias = Alias::new(names.intern("total"), names.intern("table"));
1299 alias.linkage = Linkage::Weak;
1300 module.add_alias(alias);
1301 let mut memcpy = Alias::new(names.intern("memcpy"), names.intern("memcpy.resolve"));
1302 memcpy.kind = AliasKind::IFunc;
1303 memcpy.visibility = Visibility::Protected;
1304 module.add_alias(memcpy);
1305
1306 let mut puts = Func::new(
1307 names.intern("puts"),
1308 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
1309 );
1310 puts.linkage = Linkage::External;
1311 puts.attrs.set = AttrSet::NOUNWIND | AttrSet::WILLRETURN;
1312 module.add_func(puts);
1313
1314 let mut helper =
1315 Func::new(names.intern("helper"), Signature::new().with_returns(&[i32_, i32_]));
1316 helper.linkage = Linkage::Internal;
1317 helper.section = Some(names.intern(".text.hot"));
1318 helper.attrs.set = AttrSet::READNONE | AttrSet::ALWAYS_INLINE;
1319 let block = helper.create_block();
1320 let mut b = Builder::new(&mut helper, block);
1321 let one = b.iconst(i32_, 1);
1322 b.ret(&[one, one]);
1323 module.add_func(helper);
1324
1325 assert_eq!(print(&module, &names), crate::fixtures::SYMBOLS);
1326 }
1327
1328 #[test]
1329 fn a_signature_writes_what_the_abi_asks_of_each_parameter() {
1330 let mut names = Interner::new();
1331 let module = Module::new(names.intern("abi.c"), &target());
1332 let mut func = Func::new(
1333 names.intern("f"),
1334 Signature::new()
1335 .and_param(Param::with_abi(Type::PTR, Abi::Sret { size: 24, align: 8 }))
1336 .and_param(Param::with_abi(Type::PTR, Abi::ByVal { size: 16, align: 8 }))
1337 .and_param(Param::with_abi(Type::int(8), Abi::Zext))
1338 .and_param(Param::new(Type::int(32))),
1339 );
1340 let entry = func.create_block();
1341 for param in [Type::PTR, Type::PTR, Type::int(8), Type::int(32)] {
1342 func.append_param(entry, param);
1343 }
1344 let mut b = Builder::new(&mut func, entry);
1345 b.ret(&[]);
1346
1347 assert_eq!(
1348 print_func(&module, &func, &names),
1349 "\
1350func @f(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext, i32), linkage(external) {
1351block0(%0: ptr, %1: ptr, %2: i8, %3: i32):
1352 return
1353}
1354"
1355 );
1356 }
1357
1358 #[test]
1359 fn a_call_writes_what_the_abi_asks_of_an_argument_its_signature_does_not_name() {
1360 let mut names = Interner::new();
1361 let module = Module::new(names.intern("varargs.c"), &target());
1362 let i32_ = Type::int(32);
1363 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR]));
1364 let entry = func.create_block();
1365 let p = func.append_param(entry, Type::PTR);
1366 let mut b = Builder::new(&mut func, entry);
1367 let sig = b.func().add_signature(
1368 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]).variadic(),
1369 );
1370 let one = b.iconst(i32_, 1);
1371 b.call_varargs(
1372 names.intern("printf"),
1373 sig,
1374 &[p, one, p],
1375 &[Abi::Plain, Abi::ByVal { size: 24, align: 8 }],
1376 );
1377 b.ret(&[]);
1378
1379 assert_eq!(
1380 print_func(&module, &func, &names),
1381 "\
1382func @f(ptr), linkage(external) {
1383block0(%0: ptr):
1384 %1 = iconst.i32 1
1385 %2 = call @printf(%0, %1, %0 byval(24, align 8)) : (ptr, ...) -> i32
1386 return
1387}
1388"
1389 );
1390 }
1391
1392 #[test]
1393 fn numbering_follows_the_text_and_not_the_tables() {
1394 let mut names = Interner::new();
1399 let mut module = Module::new(names.intern("order.c"), &target());
1400 let i32_ = Type::int(32);
1401 let mut func = Func::new(names.intern("f"), Signature::new().with_returns(&[i32_]));
1402 let entry = func.create_block();
1403 let middle = func.create_block();
1404 let exit = func.create_block();
1405 let arrived = func.append_param(exit, i32_);
1406
1407 let mut b = Builder::new(&mut func, exit);
1408 b.ret(&[arrived]);
1409 let mut b = Builder::new(&mut func, middle);
1410 let two = b.iconst(i32_, 2);
1411 b.jump(exit, &[two]);
1412 let mut b = Builder::new(&mut func, entry);
1413 b.jump(middle, &[]);
1414 module.add_func(func);
1415
1416 assert_eq!(
1417 print_func(&module, &module[module.funcs().next().unwrap()], &names),
1418 "\
1419func @f() -> i32, linkage(external) {
1420block0:
1421 jump block1
1422
1423block1:
1424 %0 = iconst.i32 2
1425 jump block2(%0)
1426
1427block2(%1: i32):
1428 return %1
1429}
1430"
1431 );
1432 }
1433}