Skip to main content

rucc_ast/
asm.rs

1//! Inline assembly, in GCC's full form.
2//!
3//! Design: `spec/06-lexer-and-parser.md` section 6.7. The constraint language itself is
4//! target-specific and is `spec/11-target-support.md`; nothing here looks inside a constraint
5//! string.
6//!
7//! The kernel needs all of this, including `asm goto` with outputs, which GCC only gained in
8//! 11 and which the kernel started using as soon as it had it. A statement is kept as written,
9//! with its operands in source order, because the template refers to them by position.
10
11use rucc_base::Symbol;
12use rucc_diag::Span;
13
14use crate::ast::{AsmOperandList, StrId, StrList, SymbolList};
15use crate::expr::ExprId;
16
17/// An assembly statement, in the side table.
18pub type AsmId = rucc_base::Idx<Asm>;
19
20/// An `asm` statement or a file-scope `asm`.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct Asm {
23    /// The template, with adjacent literals already joined by phase 7.
24    pub template: StrId,
25    /// The output operands.
26    pub outputs: AsmOperandList,
27    /// The input operands, which are numbered after the outputs.
28    pub inputs: AsmOperandList,
29    /// The clobber list.
30    pub clobbers: StrList,
31    /// The labels of an `asm goto`, and empty otherwise.
32    pub labels: SymbolList,
33    /// The qualifiers written between `asm` and the parenthesis.
34    pub quals: AsmQuals,
35    /// The whole statement.
36    pub span: Span,
37}
38
39/// One operand of an assembly statement.
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub struct AsmOperand {
42    /// The `[name]` in front of the constraint, which lets the template say `%[name]` instead
43    /// of counting operands.
44    pub name: Option<Symbol>,
45    /// The constraint string, which is read by the target and not here.
46    pub constraint: StrId,
47    /// The expression, which is an lvalue for an output.
48    pub value: ExprId,
49    /// The whole operand.
50    pub span: Span,
51}
52
53/// The qualifiers on an assembly statement, as a set.
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
55pub struct AsmQuals(u8);
56
57impl AsmQuals {
58    /// None of them.
59    pub const NONE: AsmQuals = AsmQuals(0);
60    /// `volatile`, which is implied for a statement with no outputs.
61    pub const VOLATILE: AsmQuals = AsmQuals(1);
62    /// `inline`, which tells the inliner to cost the statement as small.
63    pub const INLINE: AsmQuals = AsmQuals(2);
64    /// `goto`, which is what makes the label list legal.
65    pub const GOTO: AsmQuals = AsmQuals(4);
66
67    /// Whether every qualifier in `other` is set here.
68    #[inline]
69    #[must_use]
70    pub const fn has(self, other: AsmQuals) -> bool {
71        self.0 & other.0 == other.0
72    }
73
74    /// This set with `other` added.
75    #[inline]
76    #[must_use]
77    pub const fn with(self, other: AsmQuals) -> AsmQuals {
78        AsmQuals(self.0 | other.0)
79    }
80
81    /// Whether none of them was written.
82    #[inline]
83    #[must_use]
84    pub const fn is_none(self) -> bool {
85        self.0 == 0
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn qualifier_sets_add_up() {
95        let q = AsmQuals::NONE.with(AsmQuals::VOLATILE).with(AsmQuals::GOTO);
96        assert!(q.has(AsmQuals::VOLATILE));
97        assert!(q.has(AsmQuals::GOTO));
98        assert!(!q.has(AsmQuals::INLINE));
99        assert!(AsmQuals::NONE.is_none());
100    }
101}