qbe_parser/ast/
functions.rs

1use crate::ast::data::Constant;
2use crate::ast::linkage::Linkage;
3use crate::ast::types::{AbiType, BaseType};
4use crate::ast::{BlockName, FloatLiteral, GlobalName, Ident, Span, Spanned, TemporaryName};
5use crate::lexer::Keyword;
6use crate::print::{IndentedPrinter, impl_display_via_print};
7use crate::utils::{IterExt, delegate_enum_getters, impl_enum_display};
8use std::fmt;
9use std::fmt::{Display, Formatter, Write};
10use std::str::FromStr;
11
12mod parse;
13#[cfg(test)]
14mod test;
15
16#[derive(Clone, Debug, Eq, PartialEq, Hash)]
17pub struct FunctionDef {
18    pub span: Span,
19    pub linkage: Linkage,
20    pub return_type: Option<AbiType>,
21    pub name: GlobalName,
22    pub params: Vec<ParamDef>,
23    pub body: FunctionBody,
24}
25impl FunctionDef {
26    // dummy method for enum getter
27    pub(crate) fn span(&self) -> Span {
28        self.span
29    }
30    pub fn validate(&self) -> Result<(), Vec<InvalidFunctionReason>> {
31        let mut res = Vec::new();
32        for (index, param) in self.params.iter().enumerate() {
33            match param {
34                ParamDef::Regular(_) => {}
35                ParamDef::Environment(_) => {
36                    if index > 0 {
37                        res.push(InvalidFunctionReason::EnvironmentParamMustComeFirst {
38                            span: param.span(),
39                        });
40                    }
41                }
42                ParamDef::Variadic(_) => {
43                    if index < self.params.len() - 1 {
44                        res.push(InvalidFunctionReason::VariadicParamMustComeLast {
45                            span: param.span(),
46                        });
47                    }
48                }
49            }
50        }
51        if res.is_empty() { Ok(()) } else { Err(res) }
52    }
53    fn print(&self, out: &mut IndentedPrinter<'_>) -> fmt::Result {
54        if !self.linkage.is_empty() {
55            write!(out, "{} ", self.linkage)?;
56        }
57        out.write_str("function ")?;
58        if let Some(ref return_type) = self.return_type {
59            write!(out, "{return_type} ")?;
60        }
61        write!(out, "{}({}) ", self.name, self.params.iter().format(", "))?;
62        self.body.print(out)?;
63        Ok(())
64    }
65}
66impl_display_via_print!(FunctionDef);
67
68/// An error that occurs calling [`FunctionDef::validate`].
69#[derive(Debug, Clone, Eq, PartialEq, thiserror::Error)]
70#[non_exhaustive]
71pub enum InvalidFunctionReason {
72    #[error("Variadic parameter must come last")]
73    VariadicParamMustComeLast { span: Span },
74    #[error("Environment parameter must come first")]
75    EnvironmentParamMustComeFirst { span: Span },
76}
77
78#[derive(Clone, Debug, Eq, PartialEq, Hash)]
79pub enum ParamDef {
80    Regular(RegularParamDef),
81    Environment(EnvironmentParamDef),
82    Variadic(VariadicParamDef),
83}
84impl ParamDef {
85    pub fn span(&self) -> Span {
86        match self {
87            ParamDef::Regular(param) => param.span,
88            ParamDef::Environment(param) => param.span,
89            ParamDef::Variadic(param) => param.span,
90        }
91    }
92    pub fn name(&self) -> Result<&'_ TemporaryName, UnnamedParamError> {
93        Ok(match self {
94            ParamDef::Regular(param) => &param.name,
95            ParamDef::Environment(param) => &param.name,
96            ParamDef::Variadic(param) => {
97                return Err(UnnamedParamError::Variadic { span: param.span });
98            }
99        })
100    }
101}
102impl Display for ParamDef {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        match self {
105            ParamDef::Regular(param) => write!(f, "{param}"),
106            ParamDef::Environment(param) => write!(f, "{param}"),
107            ParamDef::Variadic(param) => write!(f, "{param}"),
108        }
109    }
110}
111#[derive(thiserror::Error, Debug, Eq, PartialEq)]
112#[non_exhaustive]
113pub enum UnnamedParamError {
114    #[error("Variadic parameter has no name")]
115    Variadic { span: Span },
116}
117impl UnnamedParamError {
118    pub fn span(&self) -> Span {
119        match self {
120            UnnamedParamError::Variadic { span } => *span,
121        }
122    }
123}
124
125#[derive(Clone, Debug, Eq, PartialEq, Hash)]
126pub struct RegularParamDef {
127    pub span: Span,
128    pub name: TemporaryName,
129    pub ty: AbiType,
130}
131impl Display for RegularParamDef {
132    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        write!(f, "{} {}", self.ty, self.name)
134    }
135}
136#[derive(Clone, Debug, Eq, PartialEq, Hash)]
137pub struct EnvironmentParamDef {
138    pub span: Span,
139    pub name: TemporaryName,
140}
141impl Display for EnvironmentParamDef {
142    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143        write!(f, "env {}", self.name)
144    }
145}
146
147#[derive(Clone, Debug, Eq, PartialEq, Hash)]
148pub struct VariadicParamDef {
149    pub span: Span,
150}
151impl Display for VariadicParamDef {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        f.write_str("...")
154    }
155}
156
157#[derive(Clone, Debug, Eq, PartialEq, Hash)]
158pub struct FunctionBody {
159    pub span: Span,
160    pub blocks: Vec<FunctionBlock>,
161}
162impl FunctionBody {
163    fn print(&self, out: &mut IndentedPrinter<'_>) -> fmt::Result {
164        out.write_str("{\n")?;
165        for block in &self.blocks {
166            block.print(out)?;
167        }
168        out.maybe_writeln()?;
169        out.write_char('}')
170    }
171}
172impl_display_via_print!(FunctionBody);
173#[derive(Clone, Debug, Eq, PartialEq, Hash)]
174pub struct FunctionBlock {
175    pub span: Span,
176    pub label: BlockName,
177    pub phis: Vec<PhiInstruction>,
178    pub instructions: Vec<RegularInstruction>,
179    pub terminator: Option<JumpInstruction>,
180}
181impl FunctionBlock {
182    fn print(&self, out: &mut IndentedPrinter<'_>) -> fmt::Result {
183        writeln!(out, "{}", self.label)?;
184        out.indented(|out| {
185            for phi in &self.phis {
186                writeln!(out, "{phi}")?;
187            }
188            for insn in &self.instructions {
189                writeln!(out, "{insn}")?;
190            }
191            if let Some(ref term) = self.terminator {
192                writeln!(out, "{term}")?;
193            }
194            Ok(())
195        })
196    }
197}
198impl_display_via_print!(FunctionBlock);
199#[derive(Clone, Debug, Eq, PartialEq, Hash)]
200pub struct PhiInstruction {
201    pub span: Span,
202    pub dest_info: InsnDestInfo,
203    pub args: Vec<PhiArg>,
204}
205impl PhiInstruction {
206    #[inline]
207    pub fn dest(&self) -> &'_ TemporaryName {
208        &self.dest_info.dest
209    }
210    #[inline]
211    pub fn dest_type(&self) -> &'_ BaseType {
212        &self.dest_info.ty
213    }
214}
215impl Display for PhiInstruction {
216    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
217        write!(f, "{} phi ", self.dest_info)?;
218        write!(f, "{}", self.args.iter().format(", "))?;
219        Ok(())
220    }
221}
222#[derive(Clone, Debug, Eq, PartialEq, Hash)]
223pub struct PhiArg {
224    pub span: Span,
225    pub block: BlockName,
226    pub value: Value,
227}
228impl Display for PhiArg {
229    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
230        write!(f, "{} {}", self.block, self.value)
231    }
232}
233
234/// The destination where the result of an instruction is stored.
235#[derive(Clone, Debug, Eq, PartialEq, Hash)]
236pub struct InsnDestInfo {
237    pub span: Span,
238    pub dest: TemporaryName,
239    pub ty: BaseType,
240}
241impl Display for InsnDestInfo {
242    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
243        write!(f, "{} ={}", self.dest, self.ty)
244    }
245}
246/// An instruction that is not a [`JumpInstruction`].
247#[derive(Clone, Debug, Eq, PartialEq, Hash)]
248#[non_exhaustive]
249pub enum RegularInstruction {
250    Simple(SimpleInstruction),
251    Call(CallInstruction),
252}
253delegate_enum_getters! {
254    enum RegularInstruction {
255        Simple,
256        Call
257    } get {
258        pub fn dest_info(&self) -> Option<&'_ InsnDestInfo>;
259        pub fn dest(&self) -> Option<&'_ TemporaryName>;
260        pub fn dest_type(&self) -> Option<&'_ BaseType>;
261        pub fn name(&self) -> Ident;
262        pub fn span(&self) -> Span;
263    }
264}
265impl From<SimpleInstruction> for RegularInstruction {
266    fn from(value: SimpleInstruction) -> Self {
267        RegularInstruction::Simple(value)
268    }
269}
270impl From<CallInstruction> for RegularInstruction {
271    fn from(value: CallInstruction) -> Self {
272        RegularInstruction::Call(value)
273    }
274}
275impl_enum_display!(
276    enum RegularInstruction {
277        Simple,
278        Call,
279    }
280);
281#[derive(Clone, Debug, Eq, PartialEq, Hash)]
282pub struct SimpleInstruction {
283    pub span: Span,
284    pub dest_info: Option<InsnDestInfo>,
285    pub args: Vec<Value>,
286    pub name: Ident,
287}
288impl SimpleInstruction {
289    fn name(&self) -> Ident {
290        self.name.clone()
291    }
292}
293macro_rules! regular_insn_common {
294    ($target:ident) => {
295        impl $target {
296            // this is an internal method, only needed for the macro
297            fn dest_info(&self) -> Option<&'_ InsnDestInfo> {
298                self.dest_info.as_ref()
299            }
300            #[inline]
301            fn span(&self) -> Span {
302                self.span
303            }
304            pub fn dest(&self) -> Option<&'_ TemporaryName> {
305                self.dest_info.as_ref().map(|info| &info.dest)
306            }
307            pub fn dest_type(&self) -> Option<&'_ BaseType> {
308                self.dest_info.as_ref().map(|info| &info.ty)
309            }
310        }
311    };
312}
313regular_insn_common!(SimpleInstruction);
314impl Display for SimpleInstruction {
315    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
316        if let Some(ref info) = self.dest_info {
317            write!(f, "{info} ")?;
318        }
319        write!(f, "{}", self.name)?;
320        if !self.args.is_empty() {
321            f.write_char(' ')?;
322        }
323        write!(f, "{}", self.args.iter().format(", "))?;
324        Ok(())
325    }
326}
327#[derive(Clone, Debug, Eq, PartialEq, Hash)]
328pub struct CallInstruction {
329    pub span: Span,
330    /// Span of the "call" keyword, used for [`Self::name`].
331    pub call_kw_span: Span,
332    pub dest_info: Option<InsnDestInfo>,
333    pub target: Value,
334    pub args: Vec<CallArgument>,
335}
336impl Display for CallInstruction {
337    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
338        if let Some(ref info) = self.dest_info {
339            write!(f, "{info} ")?;
340        }
341        write!(f, "call {}({})", self.target, self.args.iter().format(", "))
342    }
343}
344impl CallInstruction {
345    pub fn name(&self) -> Ident {
346        Spanned {
347            span: self.call_kw_span,
348            value: Keyword::Call,
349        }
350        .into()
351    }
352}
353regular_insn_common!(CallInstruction);
354
355/// An argument to a [`CallInstruction`].
356#[derive(Clone, Debug, Eq, PartialEq, Hash)]
357pub enum CallArgument {
358    Regular(RegularCallArgument),
359    Environment(Value),
360    VariadicMarker(Span),
361}
362impl Display for CallArgument {
363    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
364        match self {
365            CallArgument::Regular(inner) => Display::fmt(inner, f),
366            CallArgument::Environment(value) => write!(f, "env {value}"),
367            CallArgument::VariadicMarker(_) => f.write_str("..."),
368        }
369    }
370}
371/// A regular [`CallArgument`], including both a value and its type.
372#[derive(Clone, Debug, Eq, PartialEq, Hash)]
373pub struct RegularCallArgument {
374    pub span: Span,
375    pub ty: AbiType,
376    pub value: Value,
377}
378impl Display for RegularCallArgument {
379    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
380        write!(f, "{} {}", self.ty, self.value)
381    }
382}
383
384#[derive(Clone, Debug, Eq, PartialEq, Hash)]
385pub struct ThreadLocalRef {
386    pub span: Span,
387    pub name: GlobalName,
388}
389impl Display for ThreadLocalRef {
390    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
391        write!(f, "thread {}", self.name)
392    }
393}
394
395#[derive(Clone, Debug, Eq, PartialEq, Hash)]
396#[non_exhaustive]
397pub enum Value {
398    Constant(Constant),
399    ThreadLocalRef(ThreadLocalRef),
400    Temporary(TemporaryName),
401}
402impl_enum_display!(
403    enum Value {
404        Constant,
405        ThreadLocalRef,
406        Temporary,
407    }
408);
409macro_rules! impl_from_constant {
410    ($($target:ty),+) => {
411        $(impl From<$target> for Value {
412            fn from(value: $target) -> Self {
413                Value::Constant(value.into())
414            }
415        })*
416    };
417}
418impl_from_constant!(Constant, i128, i64, i32, u64, FloatLiteral);
419impl From<TemporaryName> for Value {
420    fn from(name: TemporaryName) -> Self {
421        Value::Temporary(name)
422    }
423}
424
425macro_rules! insn_kind_names {
426    ($target:ident {
427        const KIND_DESC = $kind_desc:literal;
428        $($variant:ident => $name:literal),+ $(,)?
429    }) => {
430        impl $target {
431            pub fn name(&self) -> &'static str {
432                match self {
433                    $(Self::$variant => $name,)*
434                }
435            }
436            pub fn from_name(name: &str) -> Option<Self> {
437                match name {
438                    $($name => Some(Self::$variant),)*
439                    _ => None,
440                }
441            }
442        }
443        impl FromStr for $target {
444            type Err = UnknownInstructionNameError;
445            fn from_str(s: &str) -> Result<Self, Self::Err> {
446                Self::from_name(s).ok_or_else(|| UnknownInstructionNameError {
447                    kind_desc: Some($kind_desc),
448                    name: s.into()
449                })
450            }
451        }
452    };
453}
454#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
455pub enum JumpInstructionKind {
456    Jump,
457    JumpNonZero,
458    Return,
459    Halt,
460}
461insn_kind_names!(JumpInstructionKind {
462    const KIND_DESC = "jump";
463    Jump => "jmp",
464    JumpNonZero => "jnz",
465    Return => "ret",
466    Halt => "hlt",
467});
468#[derive(Clone, Debug, Eq, PartialEq, Hash)]
469pub enum JumpInstruction {
470    Jump {
471        span: Span,
472        target: BlockName,
473    },
474    JumpNonZero {
475        span: Span,
476        op: Value,
477        target: BlockName,
478        fallthrough: BlockName,
479    },
480    Return {
481        span: Span,
482        value: Option<Value>,
483    },
484    Halt {
485        span: Span,
486    },
487}
488impl JumpInstruction {
489    #[inline]
490    pub fn dest_info(&self) -> Option<&InsnDestInfo> {
491        None
492    }
493    pub fn span(&self) -> Span {
494        match *self {
495            JumpInstruction::Jump { span, .. }
496            | JumpInstruction::JumpNonZero { span, .. }
497            | JumpInstruction::Return { span, .. }
498            | JumpInstruction::Halt { span, .. } => span,
499        }
500    }
501    pub fn kind(self) -> JumpInstructionKind {
502        match self {
503            JumpInstruction::Jump { .. } => JumpInstructionKind::Jump,
504            JumpInstruction::JumpNonZero { .. } => JumpInstructionKind::JumpNonZero,
505            JumpInstruction::Return { .. } => JumpInstructionKind::Return,
506            JumpInstruction::Halt { .. } => JumpInstructionKind::Halt,
507        }
508    }
509}
510impl Display for JumpInstruction {
511    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
512        match self {
513            JumpInstruction::Jump { span: _, target } => {
514                write!(f, "jmp {target}")
515            }
516            JumpInstruction::JumpNonZero {
517                span: _,
518                op,
519                fallthrough,
520                target,
521            } => {
522                write!(f, "jnz {op}, {target}, {fallthrough}")
523            }
524            JumpInstruction::Return { span: _, value } => {
525                f.write_str("ret")?;
526                if let Some(value) = value {
527                    write!(f, " {value}")?;
528                }
529                Ok(())
530            }
531            JumpInstruction::Halt { span: _ } => f.write_str("hlt"),
532        }
533    }
534}
535
536#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
537pub struct UnknownInstructionNameError {
538    kind_desc: Option<&'static str>,
539    name: String,
540}
541impl UnknownInstructionNameError {
542    pub fn name(&self) -> &'_ str {
543        &self.name
544    }
545}
546impl Display for UnknownInstructionNameError {
547    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
548        f.write_str("Unknown")?;
549        if let Some(kind) = self.kind_desc {
550            write!(f, " {kind}")?;
551        }
552        write!(f, " instruction name: {:?}", self.name)
553    }
554}