vyre_libs/parsing/c/parse/
gnu_builtins.rs1use crate::parsing::c::parse::vast_kinds::{
2 C_AST_KIND_BUILTIN_CHOOSE_EXPR, C_AST_KIND_BUILTIN_EXPECT_EXPR,
3 C_AST_KIND_BUILTIN_OBJECT_SIZE_EXPR, C_AST_KIND_BUILTIN_OFFSETOF_EXPR,
4 C_AST_KIND_BUILTIN_PREFETCH_EXPR, C_AST_KIND_BUILTIN_UNREACHABLE_STMT,
5};
6use crate::region::wrap_anonymous;
7use vyre::ir::{BufferAccess, BufferDecl, DataType, Expr, Node, Program};
8
9pub const GNU_BUILTIN_EXPECT_OPCODE: u32 = 0x4558_5043;
11pub const GNU_BUILTIN_OFFSETOF_OPCODE: u32 = 0x4F46_5354;
13pub const GNU_BUILTIN_OBJECT_SIZE_OPCODE: u32 = 0x4F42_4A53;
15pub const GNU_BUILTIN_PREFETCH_OPCODE: u32 = 0x5052_4546;
17pub const GNU_BUILTIN_UNREACHABLE_OPCODE: u32 = 0x554E_5243;
19pub const GNU_BUILTIN_RESERVED_PREFIX: u32 = 0x474E_5500;
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct GnuBuiltinError {
25 pub len: usize,
27 pub message: &'static str,
29}
30
31impl core::fmt::Display for GnuBuiltinError {
32 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33 write!(f, "{} for {} bytes", self.message, self.len)
34 }
35}
36
37impl std::error::Error for GnuBuiltinError {}
38
39pub fn try_classify_gnu_builtin_name(name: &[u8]) -> Result<Option<u32>, GnuBuiltinError> {
49 if let Some(kind) = super::gnu_builtin_catalog::classify_gnu_builtin_name(name) {
50 return Ok(Some(kind));
51 }
52 if name.starts_with(b"__builtin_") {
53 return Err(GnuBuiltinError {
54 len: name.len(),
55 message: "Fix: add explicit GNU builtin semantics before accepting this intrinsic",
56 });
57 }
58 Ok(None)
59}
60
61pub const GPU_BUILTIN_HASH_TABLE_SEED: u32 = 0x27b4;
63pub const GPU_BUILTIN_HASH_TABLE_SIZE: usize = 5003;
65
66#[must_use]
68pub fn gpu_builtin_hash_table_words() -> Vec<u32> {
69 let mut table = vec![0u32; GPU_BUILTIN_HASH_TABLE_SIZE];
70 for entry in super::gnu_builtin_catalog::GNU_BUILTIN_NAME_KINDS {
71 let slot = gpu_builtin_hash_slot(entry.hash);
72 assert_eq!(
73 table[slot], 0,
74 "Fix: GPU builtin hash table seed must keep catalog hashes collision-free"
75 );
76 table[slot] = entry.hash;
77 }
78 table
79}
80
81fn gpu_builtin_hash_slot(hash: u32) -> usize {
82 (hash.wrapping_mul(GPU_BUILTIN_HASH_TABLE_SEED) % GPU_BUILTIN_HASH_TABLE_SIZE as u32) as usize
83}
84
85#[must_use]
91pub fn c11_gnu_builtins_pass(
92 ast_opcodes: &str,
93 out_ast_opcodes: &str,
94 num_ast_nodes: Expr,
95) -> Program {
96 let t = Expr::InvocationId { axis: 0 };
97
98 let loop_body = vec![
99 Node::let_bind("opcode", Expr::load(ast_opcodes, t.clone())),
100 Node::let_bind("normalized", Expr::var("opcode")),
101 Node::if_then(
102 Expr::eq(Expr::var("opcode"), Expr::u32(GNU_BUILTIN_EXPECT_OPCODE)),
103 vec![Node::assign(
104 "normalized",
105 Expr::u32(C_AST_KIND_BUILTIN_EXPECT_EXPR),
106 )],
107 ),
108 Node::if_then(
109 Expr::eq(Expr::var("opcode"), Expr::u32(GNU_BUILTIN_OFFSETOF_OPCODE)),
110 vec![Node::assign(
111 "normalized",
112 Expr::u32(C_AST_KIND_BUILTIN_OFFSETOF_EXPR),
113 )],
114 ),
115 Node::if_then(
116 Expr::eq(
117 Expr::var("opcode"),
118 Expr::u32(GNU_BUILTIN_OBJECT_SIZE_OPCODE),
119 ),
120 vec![Node::assign(
121 "normalized",
122 Expr::u32(C_AST_KIND_BUILTIN_OBJECT_SIZE_EXPR),
123 )],
124 ),
125 Node::if_then(
126 Expr::eq(Expr::var("opcode"), Expr::u32(GNU_BUILTIN_PREFETCH_OPCODE)),
127 vec![Node::assign(
128 "normalized",
129 Expr::u32(C_AST_KIND_BUILTIN_PREFETCH_EXPR),
130 )],
131 ),
132 Node::if_then(
133 Expr::eq(
134 Expr::var("opcode"),
135 Expr::u32(GNU_BUILTIN_UNREACHABLE_OPCODE),
136 ),
137 vec![Node::assign(
138 "normalized",
139 Expr::u32(C_AST_KIND_BUILTIN_UNREACHABLE_STMT),
140 )],
141 ),
142 Node::if_then(
143 Expr::eq(
144 Expr::bitand(Expr::var("opcode"), Expr::u32(0xFFFF_FF00)),
145 Expr::u32(GNU_BUILTIN_RESERVED_PREFIX),
146 ),
147 vec![Node::trap(
148 Expr::var("opcode"),
149 "unsupported-gnu-builtin-opcode",
150 )],
151 ),
152 Node::store(out_ast_opcodes, t.clone(), Expr::var("normalized")),
153 ];
154
155 let ast_count = match &num_ast_nodes {
156 Expr::LitU32(n) => *n,
157 _ => 1,
158 };
159 Program::wrapped(
160 vec![
161 BufferDecl::storage(ast_opcodes, 0, BufferAccess::ReadOnly, DataType::U32)
162 .with_count(ast_count),
163 BufferDecl::storage(out_ast_opcodes, 1, BufferAccess::ReadWrite, DataType::U32)
164 .with_count(ast_count),
165 ],
166 [256, 1, 1],
167 vec![wrap_anonymous(
168 "vyre-libs::parsing::c11_gnu_builtins_pass",
169 vec![Node::if_then(Expr::lt(t.clone(), num_ast_nodes), loop_body)],
170 )],
171 )
172 .with_entry_op_id("vyre-libs::parsing::c11_gnu_builtins_pass")
173 .with_non_composable_with_self(true)
174}
175
176inventory::submit! {
177 crate::harness::OpEntry {
178 id: "vyre-libs::parsing::c11_gnu_builtins_pass",
179 build: || c11_gnu_builtins_pass("ast", "out_ast", Expr::u32(4)),
180 test_inputs: Some(|| {
181 let ast = [
182 0x11u32,
183 GNU_BUILTIN_EXPECT_OPCODE,
184 GNU_BUILTIN_OBJECT_SIZE_OPCODE,
185 C_AST_KIND_BUILTIN_CHOOSE_EXPR,
186 ];
187 let ast_bytes = vyre_primitives::wire::pack_u32_slice(&ast);
188 vec![vec![ast_bytes, vec![0u8; 4 * 4]]]
189 }),
190 expected_output: Some(|| {
191 let out = [
192 0x11u32,
193 C_AST_KIND_BUILTIN_EXPECT_EXPR,
194 C_AST_KIND_BUILTIN_OBJECT_SIZE_EXPR,
195 C_AST_KIND_BUILTIN_CHOOSE_EXPR,
196 ];
197 let out_bytes = vyre_primitives::wire::pack_u32_slice(&out);
198 vec![vec![out_bytes]]
199 }),
200 category: Some("parsing"),
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use crate::parsing::c::parse::vast_kinds::{
208 C_AST_KIND_BUILTIN_ASSUME_INTRIN_EXPR, C_AST_KIND_BUILTIN_BPF_CORE_INTRIN_EXPR,
209 C_AST_KIND_BUILTIN_FRAME_INTRIN_EXPR, C_AST_KIND_BUILTIN_LIBC_INTRIN_EXPR,
210 C_AST_KIND_BUILTIN_VA_INTRIN_EXPR,
211 };
212
213 #[test]
214 fn classifier_accepts_common_real_header_gnu_builtins() {
215 let cases: &[(&[u8], u32)] = &[
216 (b"__builtin_memchr", C_AST_KIND_BUILTIN_LIBC_INTRIN_EXPR),
217 (b"__builtin_strnlen", C_AST_KIND_BUILTIN_LIBC_INTRIN_EXPR),
218 (
219 b"__builtin___memcpy_chk",
220 C_AST_KIND_BUILTIN_LIBC_INTRIN_EXPR,
221 ),
222 (
223 b"__builtin___strcpy_chk",
224 C_AST_KIND_BUILTIN_LIBC_INTRIN_EXPR,
225 ),
226 (b"__builtin_ms_va_start", C_AST_KIND_BUILTIN_VA_INTRIN_EXPR),
227 (b"__builtin_next_arg", C_AST_KIND_BUILTIN_VA_INTRIN_EXPR),
228 (
229 b"__builtin_frob_return_addr",
230 C_AST_KIND_BUILTIN_FRAME_INTRIN_EXPR,
231 ),
232 (
233 b"__builtin_unwind_init",
234 C_AST_KIND_BUILTIN_FRAME_INTRIN_EXPR,
235 ),
236 (
237 b"__builtin_speculation_safe_value",
238 C_AST_KIND_BUILTIN_ASSUME_INTRIN_EXPR,
239 ),
240 (
241 b"__builtin_is_aligned",
242 C_AST_KIND_BUILTIN_ASSUME_INTRIN_EXPR,
243 ),
244 (b"__builtin_align_up", C_AST_KIND_BUILTIN_ASSUME_INTRIN_EXPR),
245 (
246 b"__builtin_align_down",
247 C_AST_KIND_BUILTIN_ASSUME_INTRIN_EXPR,
248 ),
249 (
250 b"__builtin_preserve_access_index",
251 C_AST_KIND_BUILTIN_BPF_CORE_INTRIN_EXPR,
252 ),
253 (
254 b"__builtin_btf_type_id",
255 C_AST_KIND_BUILTIN_BPF_CORE_INTRIN_EXPR,
256 ),
257 ];
258
259 for (name, expected) in cases {
260 assert_eq!(
261 try_classify_gnu_builtin_name(name).unwrap(),
262 Some(*expected),
263 "{}",
264 String::from_utf8_lossy(name)
265 );
266 }
267 }
268
269 #[test]
270 fn classifier_still_rejects_unknown_builtin_names() {
271 let error = try_classify_gnu_builtin_name(b"__builtin_vyre_unknown")
272 .expect_err("unknown compiler builtins must not become ordinary calls");
273 assert_eq!(error.len, b"__builtin_vyre_unknown".len());
274 }
275
276 #[test]
277 fn gpu_hash_table_is_exact_for_catalog_hashes() {
278 let table = gpu_builtin_hash_table_words();
279 assert!(
280 table.len() == GPU_BUILTIN_HASH_TABLE_SIZE,
281 "Fix: GPU __has_builtin table size must match its shader slot mask"
282 );
283 for entry in super::super::gnu_builtin_catalog::GNU_BUILTIN_NAME_KINDS {
284 assert_eq!(table[gpu_builtin_hash_slot(entry.hash)], entry.hash);
285 }
286 }
287}