vyre_libs/parsing/c/parse/
inline_asm.rs1use crate::atomic_collect_u32;
2use crate::parsing::c::lex::tokens::{
3 TOK_COLON, TOK_GNU_ASM, TOK_GOTO, TOK_LPAREN, TOK_RPAREN, TOK_STRING, TOK_VOLATILE,
4};
5use vyre::ir::{Expr, Program};
6
7pub const GNU_INLINE_ASM_OPCODE: u32 = 0x4153_4D00;
9const OP_ID: &str = "vyre-libs::parsing::c11_gnu_inline_asm_pass";
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct GnuInlineAsmSummary {
14 pub asm_token: usize,
16 pub is_volatile: bool,
18 pub is_goto: bool,
20 pub template_token: usize,
22 pub end_token: usize,
24 pub top_level_colons: u32,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct GnuInlineAsmError {
31 pub token_index: usize,
33 pub message: &'static str,
35}
36
37impl core::fmt::Display for GnuInlineAsmError {
38 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
39 write!(f, "{} at token {}", self.message, self.token_index)
40 }
41}
42
43impl std::error::Error for GnuInlineAsmError {}
44
45pub fn try_classify_gnu_inline_asm_tokens(
56 tok_types: &[u32],
57 asm_token: usize,
58) -> Result<GnuInlineAsmSummary, GnuInlineAsmError> {
59 if tok_types.get(asm_token).copied() != Some(TOK_GNU_ASM) {
60 return Err(GnuInlineAsmError {
61 token_index: asm_token,
62 message: "Fix: GNU inline asm parser must start at TOK_GNU_ASM",
63 });
64 }
65
66 let mut cursor = asm_token + 1;
67 let mut is_volatile = false;
68 let mut is_goto = false;
69 while let Some(kind) = tok_types.get(cursor).copied() {
70 match kind {
71 TOK_VOLATILE => {
72 is_volatile = true;
73 cursor += 1;
74 }
75 TOK_GOTO => {
76 is_goto = true;
77 cursor += 1;
78 }
79 _ => break,
80 }
81 }
82
83 if tok_types.get(cursor).copied() != Some(TOK_LPAREN) {
84 return Err(GnuInlineAsmError {
85 token_index: cursor,
86 message: "Fix: GNU inline asm requires an opening parenthesis",
87 });
88 }
89
90 let template_token = cursor + 1;
91 if tok_types.get(template_token).copied() != Some(TOK_STRING) {
92 return Err(GnuInlineAsmError {
93 token_index: template_token,
94 message: "Fix: GNU inline asm requires a string template as the first operand",
95 });
96 }
97
98 let mut depth = 1u32;
99 let mut top_level_colons = 0u32;
100 cursor += 1;
101 while cursor + 1 < tok_types.len() {
102 cursor += 1;
103 match tok_types[cursor] {
104 TOK_LPAREN => depth = depth.saturating_add(1),
105 TOK_RPAREN => {
106 depth = depth.saturating_sub(1);
107 if depth == 0 {
108 return Ok(GnuInlineAsmSummary {
109 asm_token,
110 is_volatile,
111 is_goto,
112 template_token,
113 end_token: cursor + 1,
114 top_level_colons,
115 });
116 }
117 }
118 TOK_COLON if depth == 1 => top_level_colons = top_level_colons.saturating_add(1),
119 _ => {}
120 }
121 }
122
123 Err(GnuInlineAsmError {
124 token_index: tok_types.len(),
125 message: "Fix: GNU inline asm operand list is missing its closing parenthesis",
126 })
127}
128
129#[must_use]
137pub fn c11_gnu_inline_asm_pass(
138 ast_opcodes: &str,
139 out_asm_blocks: &str,
140 num_ast_nodes: Expr,
141) -> Program {
142 atomic_collect_u32(
143 OP_ID,
144 ast_opcodes,
145 out_asm_blocks,
146 "out_asm_counts",
147 num_ast_nodes,
148 1,
149 Some("inline-asm-registry-overflow"),
150 |opcode, _t| Expr::eq(opcode, Expr::u32(GNU_INLINE_ASM_OPCODE)),
151 |_t, asm_id| asm_id,
152 |t, _asm_id| t,
153 )
154}
155
156inventory::submit! {
157 crate::harness::OpEntry {
158 id: OP_ID,
159 build: || c11_gnu_inline_asm_pass("ast", "out_asm", Expr::u32(4)),
160 test_inputs: Some(|| {
165 let ast = [0u32, 1, GNU_INLINE_ASM_OPCODE, 3];
166 let ast_bytes = vyre_primitives::wire::pack_u32_slice(&ast);
167 vec![vec![ast_bytes, vec![0u8; 4 * 4], vec![0u8; 4]]]
168 }),
169 expected_output: Some(|| {
170 let mut out = vec![0u8; 4 * 4];
174 out[0..4].copy_from_slice(&2u32.to_le_bytes());
175 let mut count = vec![0u8; 4];
176 count.copy_from_slice(&1u32.to_le_bytes());
177 vec![vec![out, count]]
178 }),
179 category: Some("parsing"),
180 }
181}