charon_lib/ast/bodies/asm.rs
1//! Inline assembly inside a function body.
2//!
3//! See the [Rust Reference](https://doc.rust-lang.org/reference/inline-assembly.html) for the
4//! meaning of operands and options.
5use crate::ast::*;
6use derive_generic_visitor::{Drive, DriveMut, DriveTwo};
7use macros::EnumIsA;
8use serde_state::{DeserializeState, SerializeState};
9use ustr::Ustr;
10
11generate_index_type!(AsmOperandId, "AsmOperand");
12
13/// Contents of an inline assembly block.
14#[derive(Debug, Clone, PartialEq, Eq)]
15#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
16pub struct InlineAsm {
17 /// Whether this is an `asm!` or `naked_asm!` assembly block.
18 pub kind: AsmKind,
19 /// The assembly code, parsed into a series of text fragments interspersed with placeholders.
20 pub template: Vec<AsmTemplatePiece>,
21 /// Input/outputs of the asm block. Not all of them may be referred to by placeholders, e.g.
22 /// explicit register operands may not.
23 pub operands: IndexVec<AsmOperandId, AsmOperand>,
24 /// The flags set in `options(...)`.
25 pub options: AsmOptions,
26}
27
28/// The kind of assembly block this is.
29#[derive(Debug, Copy, Clone, PartialEq, Eq)]
30#[derive(EnumIsA)]
31#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
32pub enum AsmKind {
33 /// Assembly generated by the `asm!` macro.
34 Asm,
35 /// Assembly generated by the `naked_asm!` macro. This forms the entire function body and only
36 /// accepts `const` and `sym` operands.
37 NakedAsm,
38}
39
40/// A fragment of assembly code.
41#[derive(Debug, Clone, PartialEq, Eq)]
42#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
43#[cfg_attr(feature = "charon_on_charon", charon::variants_prefix("AsmTemplate"))]
44pub enum AsmTemplatePiece {
45 /// Literal assembly text.
46 Text(#[serde_state(stateless)] Ustr),
47 /// Substitutes an operand, optionally with an architecture-specific register modifier.
48 Placeholder {
49 operand_id: AsmOperandId,
50 modifier: Option<char>,
51 span: Span,
52 },
53}
54
55/// An explicit register (such as `"rax"`) or a register class (such as `reg`).
56#[derive(Debug, Clone, PartialEq, Eq)]
57#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
58#[serde_state(stateless)]
59pub enum AsmRegister {
60 Explicit(Ustr),
61 Class(Ustr),
62}
63
64/// An operand supplied to inline assembly.
65#[derive(Debug, Clone, PartialEq, Eq)]
66#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
67pub enum AsmOperand {
68 /// `in(reg) value`: read `value` into the register before running the assembly.
69 In { reg: AsmRegister, value: Operand },
70 /// `out(reg) place` or `lateout(reg) place`: the contents of the register are written to
71 /// `place` at the end of the block, or discarded if `place` is `None` (essentially acting as a
72 /// clobber).
73 Out {
74 reg: AsmRegister,
75 late: bool,
76 place: Option<Place>,
77 },
78 /// `inout(reg) value => place` or `inlateout(reg) value => place`. Combines `in(reg) value`
79 /// and `out/lateout(reg) place`. The input and output places may be the same.
80 InOut {
81 reg: AsmRegister,
82 late: bool,
83 in_value: Operand,
84 out_place: Option<Place>,
85 },
86 /// An integer constant substituted into the template.
87 Const(ConstantExpr),
88 /// A function symbol substituted into the template.
89 SymFn(FnPtr),
90 /// A static symbol substituted into the template.
91 SymStatic(GlobalDeclRef),
92 /// The address of a block that the assembly may jump to. That `BranchId` corresponds to the
93 /// targets listed in [`ullbc_ast::TerminatorKind::InlineAsm`] and
94 /// [`llbc_ast::StatementKind::InlineAsm`].
95 Label(BranchId),
96}
97
98/// The flags set in `options(...)`. These flags constrain the assembly's effects; they do not
99/// describe instructions in the template itself.
100#[derive(Debug, Copy, Clone, PartialEq, Eq)]
101#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
102pub struct AsmOptions {
103 /// The assembly code has no side effects, must eventually return, and its outputs depend only
104 /// on its direct inputs or values read from memory.
105 pub pure: bool,
106 /// The assembly code does not read from or write to any memory accessible outside of the
107 /// assembly code.
108 pub nomem: bool,
109 /// The assembly code does not write to any memory accessible outside of the assembly code.
110 pub readonly: bool,
111 /// The assembly code does not modify the flags register.
112 pub preserves_flags: bool,
113 /// The assembly code does not fall through; it may still jump to label blocks.
114 pub noreturn: bool,
115 /// The assembly code does not push data to the stack, or write to the stack red-zone.
116 pub nostack: bool,
117 /// Only valid on x86; causes the assembler to use the .att_syntax prefix mode of the GNU assembler.
118 pub att_syntax: bool,
119 /// Allows unwinding out of an `asm!` block (unstable, see
120 /// https://github.com/rust-lang/rust/issues/93334). `naked_asm!` may unwind regardless of this
121 /// option.
122 pub may_unwind: bool,
123}