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.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 let args = b.func().push_values(&[of, p, off]);
963 b.value(InstData { args, ..InstData::new(Opcode::CapExtent) }, i64_);
964
965 let args = b.func().push_values(&[p, off]);
966 let derived = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
967 let four = MemInfo {
968 size: 4,
969 align: 4,
970 order: MemOrder::NotAtomic,
971 tbaa: None,
972 restrict: Restrict::NONE,
973 };
974 let mut check = |opcode, info: Option<MemInfo>, on: &[Value]| {
975 let args = b.func().push_values(on);
976 let extra = match info {
977 Some(info) => Extra::Mem(b.func().add_mem(info)),
978 None => Extra::None,
979 };
980 b.inst(InstData { args, extra, ..InstData::new(opcode) }, &[]);
981 };
982 check(Opcode::CheckBounds, Some(four), &[of, p]);
983 check(Opcode::CheckBounds, Some(four), &[of, p, off]);
986 check(Opcode::CheckLive, None, &[of, p]);
987 check(Opcode::CheckType, Some(MemInfo { tbaa: Some(int_plane), ..four }), &[of, p]);
988 check(Opcode::CheckInit, Some(MemInfo { align: 1, ..four }), &[of, p]);
989 check(Opcode::CheckDeriv, None, &[of, p, derived, len]);
990 check(Opcode::CheckRace, None, &[of, p]);
991
992 let mut plane = |opcode, extra| {
993 let args = b.func().push_values(&[p, off]);
994 b.inst(InstData { args, extra, ..InstData::new(opcode) }, &[]);
995 };
996 plane(Opcode::MetaBegin, Extra::Class(StorageClass::Allocated));
997 plane(Opcode::MetaType, Extra::Node(character));
998 plane(Opcode::MetaInit, Extra::None);
999 plane(Opcode::MetaTransfer, Extra::Owner(Owner::Device));
1000 plane(Opcode::MetaEnd, Extra::None);
1001
1002 let reason = names.intern("hand written assembly, checked by review");
1003 b.inst(
1004 InstData { extra: Extra::Reason(reason), ..InstData::new(Opcode::SafeRegionBegin) },
1005 &[],
1006 );
1007 b.inst(InstData::new(Opcode::SafeRegionEnd), &[]);
1008 b.ret(&[p]);
1009
1010 func.set_facts(
1011 p,
1012 Facts {
1013 bounds: Some(Bounds { lo: p, ext: off }),
1014 init: Some(4),
1015 align: Some(8),
1016 live: true,
1017 },
1018 );
1019 func.set_facts(derived, Facts { align: Some(4), ..Facts::NONE });
1020 module.add_func(func);
1021
1022 assert_eq!(print(&module, &names), crate::fixtures::SAFETY);
1023 }
1024
1025 #[test]
1026 fn one_of_almost_everything() {
1027 let mut names = Interner::new();
1028 let mut module = Module::new(names.intern("zoo.c"), &target());
1029 let int_node = module.add_meta(MetaNode::Tbaa(TbaaNode {
1030 name: names.intern("int"),
1031 parent: None,
1032 offset: 0,
1033 }));
1034
1035 let i32_ = Type::int(32);
1036 let i64_ = Type::int(64);
1037 let f64_ = Type::float(crate::Float::F64);
1038 let mut func = Func::new(
1039 names.intern("zoo"),
1040 Signature::new().with_params(&[i32_, Type::PTR]).with_returns(&[i32_]),
1041 );
1042 let entry = func.create_block();
1043 let n = func.append_param(entry, i32_);
1044 let p = func.append_param(entry, Type::PTR);
1045 let middle = func.create_block();
1046 let other = func.create_block();
1047 let exit = func.create_block();
1048 let taken = func.append_param(exit, i32_);
1049 let arrival = func.create_block();
1050
1051 let mut b = Builder::new(&mut func, entry);
1052 let minus_one = b.iconst(i64_, -1);
1053 let half = b.fconst(f64_, 0x3ff8_0000_0000_0000);
1054 let seven = b.func().add_imm(Imm::int(7, i32_));
1055 let vector = b.value(
1056 InstData { extra: Extra::Imm(seven), ..InstData::new(Opcode::Splat) },
1057 Type::vector(i32_, 4),
1058 );
1059 let stack = b.func().add_mem(MemInfo {
1060 size: 16,
1061 align: 8,
1062 order: MemOrder::NotAtomic,
1063 tbaa: None,
1064 restrict: Restrict::NONE,
1065 });
1066 let slot = b.value(
1067 InstData { extra: Extra::Mem(stack), ..InstData::new(Opcode::Alloca) },
1068 Type::PTR,
1069 );
1070 let args = b.func().push_values(&[slot, minus_one]);
1071 let addr = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1072 let plain = MemInfo {
1073 size: 0,
1074 align: 4,
1075 order: MemOrder::NotAtomic,
1076 tbaa: Some(int_node),
1077 restrict: Restrict::NONE,
1078 };
1079 let loaded = b.load(i32_, addr, plain, Flags::NONE);
1080 b.store(loaded, addr, plain, Flags::VOLATILE);
1081
1082 let atomic = b.func().add_mem(MemInfo {
1083 size: 0,
1084 align: 4,
1085 order: MemOrder::SeqCst,
1086 tbaa: None,
1087 restrict: Restrict::NONE,
1088 });
1089 let args = b.func().push_values(&[addr, n]);
1090 let old = b.value(
1091 InstData {
1092 args,
1093 extra: Extra::Rmw(RmwOp::Add, atomic),
1094 ..InstData::new(Opcode::AtomicRmw)
1095 },
1096 i32_,
1097 );
1098 let args = b.func().push_values(&[addr, old, n]);
1099 b.inst(
1100 InstData { args, extra: Extra::Mem(atomic), ..InstData::new(Opcode::Cmpxchg) },
1101 &[i32_, Type::I1],
1102 );
1103 b.inst(
1104 InstData { extra: Extra::Order(MemOrder::SeqCst), ..InstData::new(Opcode::Fence) },
1105 &[],
1106 );
1107 b.unary(Opcode::SExt, n, i64_);
1108 b.fcmp(FloatPred::Oeq, half, half, Flags::NONE);
1109 let args = b.func().push_values(&[n, n]);
1110 b.inst(InstData { args, ..InstData::new(Opcode::SAddOverflow) }, &[i32_, Type::I1]);
1111 let puts = b.func().add_signature(
1112 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]).variadic(),
1113 );
1114 b.call_varargs(
1115 names.intern("puts"),
1116 puts,
1117 &[p, slot],
1118 &[Abi::ByVal { size: 16, align: 8 }],
1119 );
1120 let indirect =
1121 b.func().add_signature(Signature::new().with_params(&[i32_]).with_returns(&[i32_]));
1122 let varargs = b.func().push_abis(&[]);
1123 let info = b.func().add_call(CallInfo { callee: None, signature: indirect, varargs });
1124 let args = b.func().push_values(&[p, n]);
1125 b.value(
1126 InstData {
1127 args,
1128 extra: Extra::Call(info),
1129 flags: Flags::NOFREE,
1130 ..InstData::new(Opcode::CallIndirect)
1131 },
1132 i32_,
1133 );
1134 let copy = b.func().add_mem(MemInfo {
1135 size: 16,
1136 align: 8,
1137 order: MemOrder::NotAtomic,
1138 tbaa: None,
1139 restrict: Restrict::NONE,
1140 });
1141 let args = b.func().push_values(&[slot, p]);
1142 b.inst(InstData { args, extra: Extra::Mem(copy), ..InstData::new(Opcode::Memcpy) }, &[]);
1143 let asm = b.func().add_asm(AsmInfo {
1144 template: names.intern("pause"),
1145 constraints: names.intern(""),
1146 clobbers: names.intern("memory"),
1147 targets: crate::inst::BlockCallList::EMPTY,
1148 });
1149 b.inst(
1150 InstData {
1151 flags: Flags::VOLATILE,
1152 extra: Extra::Asm(asm),
1153 ..InstData::new(Opcode::InlineAsm)
1154 },
1155 &[],
1156 );
1157 let object = b.func().add_mem(MemInfo {
1158 size: 16,
1159 align: 8,
1160 order: MemOrder::NotAtomic,
1161 tbaa: None,
1162 restrict: Restrict::NONE,
1163 });
1164 let slots = b.func().push_slots(&[
1165 Slot::Integer { offset: 0, size: 8 },
1166 Slot::Float { offset: 8, format: rucc_base::float::Format::Double },
1167 ]);
1168 let read = b.func().add_va_object(VaInfo { mem: object, slots });
1169 let args = b.func().push_values(&[p]);
1170 b.value(
1171 InstData { args, extra: Extra::VaObject(read), ..InstData::new(Opcode::VaObject) },
1172 Type::PTR,
1173 );
1174 let args = b.func().push_values(&[vector]);
1175 b.value(
1176 InstData {
1177 args,
1178 extra: Extra::Symbol(names.intern("x86.sse2.pmovmskb")),
1179 ..InstData::new(Opcode::TargetIntrinsic)
1180 },
1181 i32_,
1182 );
1183 b.jump(middle, &[]);
1184
1185 let mut b = Builder::new(&mut func, middle);
1186 let cases = b.func().push_imms(&[Imm::int(0, i32_), Imm::int(-1, i32_)]);
1187 let default = BlockCall { block: other, args: crate::inst::ValueList::EMPTY };
1188 let first = BlockCall { block: exit, args: b.func().push_values(&[n]) };
1189 let second = BlockCall { block: other, args: crate::inst::ValueList::EMPTY };
1190 let targets = b.func().push_block_calls(&[default, first, second]);
1191 let switch = b.func().add_switch(SwitchInfo { targets, cases });
1192 let args = b.func().push_values(&[n]);
1193 b.inst(
1194 InstData { args, extra: Extra::Switch(switch), ..InstData::new(Opcode::Switch) },
1195 &[],
1196 );
1197
1198 let mut b = Builder::new(&mut func, other);
1199 let address = b.block_addr(arrival);
1200 b.indirect_br(address, &[arrival]);
1201
1202 let mut b = Builder::new(&mut func, exit);
1203 b.ret(&[taken]);
1204
1205 let mut b = Builder::new(&mut func, arrival);
1206 let call = BlockCall { block: exit, args: b.func().push_values(&[n]) };
1207 let targets = b.func().push_block_calls(&[call]);
1208 let goto = b.func().add_asm(AsmInfo {
1209 template: names.intern("jmp %l0"),
1210 constraints: names.intern(""),
1211 clobbers: names.intern(""),
1212 targets,
1213 });
1214 b.inst(InstData { extra: Extra::Asm(goto), ..InstData::new(Opcode::InlineAsm) }, &[]);
1215
1216 module.add_func(func);
1217
1218 assert_eq!(print(&module, &names), crate::fixtures::ZOO);
1219 }
1220
1221 #[test]
1222 fn the_shapes_a_symbol_comes_in() {
1223 let mut names = Interner::new();
1224 let mut module = Module::new(names.intern("data.c"), &target());
1225
1226 let i32_ = Type::int(32);
1227 let text = module.push_bytes(b"hi\x00\xff\"\\");
1228 let entry_name = names.intern("hi.str");
1229 let forward = module.add_reloc(Reloc { symbol: entry_name, addend: 8, size: 8 });
1230 let backward = module.add_reloc(Reloc { symbol: entry_name, addend: -8, size: 8 });
1231 let seven = module.add_imm(Imm::int(7, i32_));
1232 let image = module.push_data(&[
1233 Datum::Bytes(text),
1234 Datum::Zero(2),
1235 Datum::Scalar { ty: i32_, value: seven },
1236 Datum::Addr(forward),
1237 Datum::Addr(backward),
1238 ]);
1239 let mut table = Global::new(names.intern("table"), 28, 8);
1240 table.init = Some(image);
1241 table.constant = true;
1242 table.section = Some(names.intern(".rodata.rel"));
1243 module.add_global(table);
1244
1245 let mut errno = Global::new(names.intern("errno"), 4, 4);
1246 errno.tls = Some(TlsModel::InitialExec);
1247 errno.visibility = Visibility::Hidden;
1248 module.add_global(errno);
1249
1250 let mut nothing = Global::new(names.intern("nothing"), 0, 1);
1255 nothing.init = Some(module.push_data(&[]));
1256 nothing.linkage = Linkage::Internal;
1257 module.add_global(nothing);
1258
1259 let mut alias = Alias::new(names.intern("total"), names.intern("table"));
1260 alias.linkage = Linkage::Weak;
1261 module.add_alias(alias);
1262 let mut memcpy = Alias::new(names.intern("memcpy"), names.intern("memcpy.resolve"));
1263 memcpy.kind = AliasKind::IFunc;
1264 memcpy.visibility = Visibility::Protected;
1265 module.add_alias(memcpy);
1266
1267 let mut puts = Func::new(
1268 names.intern("puts"),
1269 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
1270 );
1271 puts.linkage = Linkage::External;
1272 puts.attrs.set = AttrSet::NOUNWIND | AttrSet::WILLRETURN;
1273 module.add_func(puts);
1274
1275 let mut helper =
1276 Func::new(names.intern("helper"), Signature::new().with_returns(&[i32_, i32_]));
1277 helper.linkage = Linkage::Internal;
1278 helper.section = Some(names.intern(".text.hot"));
1279 helper.attrs.set = AttrSet::READNONE | AttrSet::ALWAYS_INLINE;
1280 let block = helper.create_block();
1281 let mut b = Builder::new(&mut helper, block);
1282 let one = b.iconst(i32_, 1);
1283 b.ret(&[one, one]);
1284 module.add_func(helper);
1285
1286 assert_eq!(print(&module, &names), crate::fixtures::SYMBOLS);
1287 }
1288
1289 #[test]
1290 fn a_signature_writes_what_the_abi_asks_of_each_parameter() {
1291 let mut names = Interner::new();
1292 let module = Module::new(names.intern("abi.c"), &target());
1293 let mut func = Func::new(
1294 names.intern("f"),
1295 Signature::new()
1296 .and_param(Param::with_abi(Type::PTR, Abi::Sret { size: 24, align: 8 }))
1297 .and_param(Param::with_abi(Type::PTR, Abi::ByVal { size: 16, align: 8 }))
1298 .and_param(Param::with_abi(Type::int(8), Abi::Zext))
1299 .and_param(Param::new(Type::int(32))),
1300 );
1301 let entry = func.create_block();
1302 for param in [Type::PTR, Type::PTR, Type::int(8), Type::int(32)] {
1303 func.append_param(entry, param);
1304 }
1305 let mut b = Builder::new(&mut func, entry);
1306 b.ret(&[]);
1307
1308 assert_eq!(
1309 print_func(&module, &func, &names),
1310 "\
1311func @f(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext, i32), linkage(external) {
1312block0(%0: ptr, %1: ptr, %2: i8, %3: i32):
1313 return
1314}
1315"
1316 );
1317 }
1318
1319 #[test]
1320 fn a_call_writes_what_the_abi_asks_of_an_argument_its_signature_does_not_name() {
1321 let mut names = Interner::new();
1322 let module = Module::new(names.intern("varargs.c"), &target());
1323 let i32_ = Type::int(32);
1324 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR]));
1325 let entry = func.create_block();
1326 let p = func.append_param(entry, Type::PTR);
1327 let mut b = Builder::new(&mut func, entry);
1328 let sig = b.func().add_signature(
1329 Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]).variadic(),
1330 );
1331 let one = b.iconst(i32_, 1);
1332 b.call_varargs(
1333 names.intern("printf"),
1334 sig,
1335 &[p, one, p],
1336 &[Abi::Plain, Abi::ByVal { size: 24, align: 8 }],
1337 );
1338 b.ret(&[]);
1339
1340 assert_eq!(
1341 print_func(&module, &func, &names),
1342 "\
1343func @f(ptr), linkage(external) {
1344block0(%0: ptr):
1345 %1 = iconst.i32 1
1346 %2 = call @printf(%0, %1, %0 byval(24, align 8)) : (ptr, ...) -> i32
1347 return
1348}
1349"
1350 );
1351 }
1352
1353 #[test]
1354 fn numbering_follows_the_text_and_not_the_tables() {
1355 let mut names = Interner::new();
1360 let mut module = Module::new(names.intern("order.c"), &target());
1361 let i32_ = Type::int(32);
1362 let mut func = Func::new(names.intern("f"), Signature::new().with_returns(&[i32_]));
1363 let entry = func.create_block();
1364 let middle = func.create_block();
1365 let exit = func.create_block();
1366 let arrived = func.append_param(exit, i32_);
1367
1368 let mut b = Builder::new(&mut func, exit);
1369 b.ret(&[arrived]);
1370 let mut b = Builder::new(&mut func, middle);
1371 let two = b.iconst(i32_, 2);
1372 b.jump(exit, &[two]);
1373 let mut b = Builder::new(&mut func, entry);
1374 b.jump(middle, &[]);
1375 module.add_func(func);
1376
1377 assert_eq!(
1378 print_func(&module, &module[module.funcs().next().unwrap()], &names),
1379 "\
1380func @f() -> i32, linkage(external) {
1381block0:
1382 jump block1
1383
1384block1:
1385 %0 = iconst.i32 2
1386 jump block2(%0)
1387
1388block2(%1: i32):
1389 return %1
1390}
1391"
1392 );
1393 }
1394}