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}