1use cranelift_entity::entity_impl;
2use intrusive_collections::{intrusive_adapter, LinkedList, LinkedListLink};
3
4use self::formatter::PrettyPrint;
5use crate::{
6 diagnostics::{miette, Diagnostic, Spanned},
7 *,
8};
9
10#[derive(Debug, thiserror::Error, Diagnostic)]
12#[error("item named '{}' has already been declared, or cannot be merged", .0)]
13#[diagnostic()]
14pub struct SymbolConflictError(pub FunctionIdent);
15
16#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct FuncRef(u32);
19entity_impl!(FuncRef, "fn");
20
21#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
36#[cfg_attr(
37 feature = "serde",
38 derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr)
39)]
40#[repr(u8)]
41pub enum CallConv {
42 Fast,
49 #[default]
51 SystemV,
52 Wasm,
54 Kernel,
65}
66impl fmt::Display for CallConv {
67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68 match self {
69 Self::Fast => f.write_str("fast"),
70 Self::SystemV => f.write_str("C"),
71 Self::Wasm => f.write_str("wasm"),
72 Self::Kernel => f.write_str("kernel"),
73 }
74 }
75}
76
77#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
80#[cfg_attr(
81 feature = "serde",
82 derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr)
83)]
84#[repr(u8)]
85pub enum ArgumentPurpose {
86 #[default]
88 Default,
89 StructReturn,
92}
93impl fmt::Display for ArgumentPurpose {
94 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95 match self {
96 Self::Default => f.write_str("default"),
97 Self::StructReturn => f.write_str("sret"),
98 }
99 }
100}
101
102#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
110#[cfg_attr(
111 feature = "serde",
112 derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr)
113)]
114#[repr(u8)]
115pub enum ArgumentExtension {
116 #[default]
118 None,
119 Zext,
121 Sext,
123}
124impl fmt::Display for ArgumentExtension {
125 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
126 match self {
127 Self::None => f.write_str("none"),
128 Self::Zext => f.write_str("zext"),
129 Self::Sext => f.write_str("sext"),
130 }
131 }
132}
133
134#[derive(Debug, Clone, PartialEq, Eq)]
136#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
137pub struct AbiParam {
138 pub ty: Type,
140 pub purpose: ArgumentPurpose,
142 pub extension: ArgumentExtension,
145}
146impl AbiParam {
147 pub fn new(ty: Type) -> Self {
148 Self {
149 ty,
150 purpose: ArgumentPurpose::default(),
151 extension: ArgumentExtension::default(),
152 }
153 }
154
155 pub fn sret(ty: Type) -> Self {
156 assert!(ty.is_pointer(), "sret parameters must be pointers");
157 Self {
158 ty,
159 purpose: ArgumentPurpose::StructReturn,
160 extension: ArgumentExtension::default(),
161 }
162 }
163}
164impl formatter::PrettyPrint for AbiParam {
165 fn render(&self) -> formatter::Document {
166 use crate::formatter::*;
167
168 let mut doc = const_text("(") + const_text("param") + const_text(" ");
169 if !matches!(self.purpose, ArgumentPurpose::Default) {
170 doc += const_text("(") + display(self.purpose) + const_text(")") + const_text(" ");
171 }
172 if !matches!(self.extension, ArgumentExtension::None) {
173 doc += const_text("(") + display(self.extension) + const_text(")") + const_text(" ");
174 }
175 doc + text(format!("{}", &self.ty)) + const_text(")")
176 }
177}
178
179#[derive(Debug, Clone)]
185#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
186pub struct Signature {
187 pub params: Vec<AbiParam>,
189 pub results: Vec<AbiParam>,
191 pub cc: CallConv,
193 pub linkage: Linkage,
195}
196impl Signature {
197 pub fn new<P: IntoIterator<Item = AbiParam>, R: IntoIterator<Item = AbiParam>>(
200 params: P,
201 results: R,
202 ) -> Self {
203 Self {
204 params: params.into_iter().collect(),
205 results: results.into_iter().collect(),
206 cc: CallConv::SystemV,
207 linkage: Linkage::External,
208 }
209 }
210
211 pub fn is_public(&self) -> bool {
213 matches!(self.linkage, Linkage::External)
214 }
215
216 pub fn is_private(&self) -> bool {
218 matches!(self.linkage, Linkage::Internal)
219 }
220
221 pub fn is_kernel(&self) -> bool {
223 matches!(self.cc, CallConv::Kernel)
224 }
225
226 pub fn arity(&self) -> usize {
228 self.params().len()
229 }
230
231 pub fn params(&self) -> &[AbiParam] {
233 self.params.as_slice()
234 }
235
236 #[inline]
238 pub fn param(&self, index: usize) -> Option<&AbiParam> {
239 self.params.get(index)
240 }
241
242 pub fn results(&self) -> &[AbiParam] {
244 match self.results.as_slice() {
245 [AbiParam { ty: Type::Unit, .. }] => &[],
246 [AbiParam {
247 ty: Type::Never, ..
248 }] => &[],
249 results => results,
250 }
251 }
252}
253impl Eq for Signature {}
254impl PartialEq for Signature {
255 fn eq(&self, other: &Self) -> bool {
256 self.linkage == other.linkage
257 && self.cc == other.cc
258 && self.params.len() == other.params.len()
259 && self.results.len() == other.results.len()
260 }
261}
262impl formatter::PrettyPrint for Signature {
263 fn render(&self) -> formatter::Document {
264 use crate::formatter::*;
265
266 let cc = if matches!(self.cc, CallConv::SystemV) {
267 None
268 } else {
269 Some(
270 const_text("(")
271 + const_text("cc")
272 + const_text(" ")
273 + display(self.cc)
274 + const_text(")"),
275 )
276 };
277
278 let params = self.params.iter().fold(cc.unwrap_or(Document::Empty), |acc, param| {
279 if acc.is_empty() {
280 param.render()
281 } else {
282 acc + const_text(" ") + param.render()
283 }
284 });
285
286 if self.results.is_empty() {
287 params
288 } else {
289 let open = const_text("(") + const_text("result");
290 let results = self
291 .results
292 .iter()
293 .fold(open, |acc, e| acc + const_text(" ") + text(format!("{}", &e.ty)))
294 + const_text(")");
295 if matches!(params, Document::Empty) {
296 results
297 } else {
298 params + const_text(" ") + results
299 }
300 }
301 }
302}
303
304#[derive(Debug, Clone, PartialEq, Eq)]
315pub struct ExternalFunction {
316 pub id: FunctionIdent,
317 pub signature: Signature,
318}
319impl Ord for ExternalFunction {
320 #[inline]
321 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
322 self.id.cmp(&other.id)
323 }
324}
325impl PartialOrd for ExternalFunction {
326 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
327 Some(self.cmp(other))
328 }
329}
330impl formatter::PrettyPrint for ExternalFunction {
331 fn render(&self) -> formatter::Document {
332 use crate::formatter::*;
333
334 let header = const_text("(")
335 + const_text("func")
336 + const_text(" ")
337 + const_text("(")
338 + const_text("import")
339 + const_text(" ")
340 + display(self.id.module)
341 + const_text(" ")
342 + display(self.id.function)
343 + const_text(")");
344 let signature = (const_text(" ") + self.signature.render() + const_text(")"))
345 | indent(6, nl() + self.signature.render() + const_text(")"));
346
347 header + signature
348 }
349}
350
351intrusive_adapter!(pub FunctionListAdapter = Box<Function>: Function { link: LinkedListLink });
352
353pub type FunctionList = LinkedList<FunctionListAdapter>;
355
356#[derive(Spanned, AnalysisKey)]
375pub struct Function {
376 link: LinkedListLink,
377 #[span]
378 #[analysis_key]
379 pub id: FunctionIdent,
380 pub signature: Signature,
381 pub dfg: DataFlowGraph,
382}
383impl Function {
384 pub fn new(id: FunctionIdent, signature: Signature) -> Self {
389 let mut dfg = DataFlowGraph::default();
390 let entry = dfg.entry_block();
391 for param in signature.params() {
392 dfg.append_block_param(entry, param.ty.clone(), id.span());
393 }
394 dfg.imports.insert(
395 id,
396 ExternalFunction {
397 id,
398 signature: signature.clone(),
399 },
400 );
401 Self {
402 link: Default::default(),
403 id,
404 signature,
405 dfg,
406 }
407 }
408
409 pub(crate) fn new_uninit(id: FunctionIdent, signature: Signature) -> Self {
415 let mut dfg = DataFlowGraph::new_uninit();
416 dfg.imports.insert(
417 id,
418 ExternalFunction {
419 id,
420 signature: signature.clone(),
421 },
422 );
423 Self {
424 link: Default::default(),
425 id,
426 signature,
427 dfg,
428 }
429 }
430
431 pub fn is_detached(&self) -> bool {
433 !self.link.is_linked()
434 }
435
436 pub fn is_kernel(&self) -> bool {
438 self.signature.is_kernel()
439 }
440
441 pub fn is_public(&self) -> bool {
443 self.signature.is_public()
444 }
445
446 #[inline]
448 pub fn signature(&self) -> &Signature {
449 &self.signature
450 }
451
452 #[inline]
454 pub fn signature_mut(&mut self) -> &mut Signature {
455 &mut self.signature
456 }
457
458 pub fn arity(&self) -> usize {
460 self.signature.arity()
461 }
462
463 pub fn linkage(&self) -> Linkage {
465 self.signature.linkage
466 }
467
468 pub fn set_linkage(&mut self, linkage: Linkage) {
470 self.signature.linkage = linkage;
471 }
472
473 pub fn calling_convention(&self) -> CallConv {
475 self.signature.cc
476 }
477
478 pub fn set_calling_convention(&mut self, cc: CallConv) {
480 self.signature.cc = cc;
481 }
482
483 pub fn has_attribute<Q>(&self, name: &Q) -> bool
485 where
486 Q: Ord + ?Sized,
487 Symbol: std::borrow::Borrow<Q>,
488 {
489 self.dfg.has_attribute(name)
490 }
491
492 pub fn imports<'a, 'b: 'a>(&'b self) -> impl Iterator<Item = &'a ExternalFunction> + 'a {
494 self.dfg.imports().filter(|ext| ext.id != self.id)
495 }
496
497 pub fn builder(&mut self) -> FunctionBuilder {
498 FunctionBuilder::new(self)
499 }
500
501 pub fn cfg_printer(&self) -> impl fmt::Display + '_ {
502 CfgPrinter { function: self }
503 }
504}
505impl fmt::Debug for Function {
506 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
507 f.debug_struct("Function")
508 .field("id", &self.id)
509 .field("signature", &self.signature)
510 .finish()
511 }
512}
513impl formatter::PrettyPrint for Function {
514 fn render(&self) -> formatter::Document {
515 use crate::formatter::*;
516
517 let name = if self.is_public() {
518 const_text("(")
519 + const_text("export")
520 + const_text(" ")
521 + self.id.function.render()
522 + const_text(")")
523 } else {
524 self.id.function.render()
525 };
526 let header = const_text("(") + const_text("func") + const_text(" ") + name;
527
528 let signature =
529 (const_text(" ") + self.signature.render()) | indent(6, nl() + self.signature.render());
530
531 let body = self.dfg.blocks().fold(nl(), |acc, (_, block_data)| {
532 let open = const_text("(")
533 + const_text("block")
534 + const_text(" ")
535 + display(block_data.id.as_u32());
536 let params = block_data
537 .params(&self.dfg.value_lists)
538 .iter()
539 .map(|value| {
540 let ty = self.dfg.value_type(*value);
541 const_text("(")
542 + const_text("param")
543 + const_text(" ")
544 + display(*value)
545 + const_text(" ")
546 + text(format!("{ty}"))
547 + const_text(")")
548 })
549 .collect::<Vec<_>>();
550
551 let params_singleline = params
552 .iter()
553 .cloned()
554 .reduce(|acc, e| acc + const_text(" ") + e)
555 .map(|params| const_text(" ") + params)
556 .unwrap_or(Document::Empty);
557 let params_multiline = params
558 .into_iter()
559 .reduce(|acc, e| acc + nl() + e)
560 .map(|doc| indent(8, nl() + doc))
561 .unwrap_or(Document::Empty);
562 let header = open + (params_singleline | params_multiline);
563
564 let body = indent(
565 4,
566 block_data
567 .insts()
568 .map(|inst| {
569 let inst_printer = crate::instruction::InstPrettyPrinter {
570 current_function: self.id,
571 id: inst,
572 dfg: &self.dfg,
573 };
574 inst_printer.render()
575 })
576 .reduce(|acc, doc| acc + nl() + doc)
577 .map(|body| nl() + body)
578 .unwrap_or_default(),
579 );
580
581 if matches!(acc, Document::Newline) {
582 indent(4, acc + header + body + const_text(")"))
583 } else {
584 acc + nl() + indent(4, nl() + header + body + const_text(")"))
585 }
586 });
587
588 header + signature + body + nl() + const_text(")")
589 }
590}
591impl fmt::Display for Function {
592 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
593 self.pretty_print(f)
594 }
595}
596impl Eq for Function {}
597impl PartialEq for Function {
598 fn eq(&self, other: &Self) -> bool {
599 let is_eq = self.id == other.id && self.signature == other.signature;
600 if !is_eq {
601 return false;
602 }
603
604 if self.dfg.entry != other.dfg.entry {
606 return false;
607 }
608
609 for (block_id, block) in self.dfg.blocks() {
612 if let Some(other_block) = other.dfg.blocks.get(block_id) {
613 if block.params.as_slice(&self.dfg.value_lists)
614 != other_block.params.as_slice(&other.dfg.value_lists)
615 {
616 return false;
617 }
618 if !block
620 .insts
621 .iter()
622 .map(|i| InstructionWithValueListPool {
623 inst: i,
624 value_lists: &self.dfg.value_lists,
625 })
626 .eq(other_block.insts.iter().map(|i| InstructionWithValueListPool {
627 inst: i,
628 value_lists: &other.dfg.value_lists,
629 }))
630 {
631 return false;
632 }
633 } else {
634 return false;
635 }
636 }
637
638 self.dfg.imports == other.dfg.imports
640 }
641}
642impl midenc_session::Emit for Function {
643 fn name(&self) -> Option<crate::Symbol> {
644 Some(self.id.function.as_symbol())
645 }
646
647 fn output_type(&self, _mode: midenc_session::OutputMode) -> midenc_session::OutputType {
648 midenc_session::OutputType::Hir
649 }
650
651 fn write_to<W: std::io::Write>(
652 &self,
653 mut writer: W,
654 mode: midenc_session::OutputMode,
655 _session: &midenc_session::Session,
656 ) -> std::io::Result<()> {
657 assert_eq!(
658 mode,
659 midenc_session::OutputMode::Text,
660 "binary mode is not supported for HIR functions"
661 );
662 writer.write_fmt(format_args!("{}\n", self))
663 }
664}
665
666struct CfgPrinter<'a> {
667 function: &'a Function,
668}
669impl<'a> fmt::Display for CfgPrinter<'a> {
670 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
671 use std::collections::{BTreeSet, VecDeque};
672
673 f.write_str("flowchart TB\n")?;
674
675 let mut block_q = VecDeque::from([self.function.dfg.entry_block()]);
676 let mut visited = BTreeSet::<Block>::default();
677 while let Some(block_id) = block_q.pop_front() {
678 if !visited.insert(block_id) {
679 continue;
680 }
681 if let Some(last_inst) = self.function.dfg.last_inst(block_id) {
682 match self.function.dfg.analyze_branch(last_inst) {
683 BranchInfo::NotABranch => {
684 let opcode = self.function.dfg.inst(last_inst).opcode();
686 writeln!(f, " {block_id} --> {opcode}")?;
687 }
688 BranchInfo::SingleDest(info) => {
689 assert!(
690 self.function.dfg.is_block_linked(info.destination),
691 "reference to detached block in attached block {}",
692 info.destination
693 );
694 writeln!(f, " {block_id} --> {}", info.destination)?;
695 block_q.push_back(info.destination);
696 }
697 BranchInfo::MultiDest(ref infos) => {
698 for info in infos {
699 assert!(
700 self.function.dfg.is_block_linked(info.destination),
701 "reference to detached block in attached block {}",
702 info.destination
703 );
704 writeln!(f, " {block_id} --> {}", info.destination)?;
705 block_q.push_back(info.destination);
706 }
707 }
708 }
709 }
710 }
711
712 Ok(())
713 }
714}