use super::gpu_directive_parse_shared::{
keyword_match_expr, push_found_hash, push_hash_scan, push_keyword_bytes, push_keyword_start,
source_buffer_element, DirectiveSourceLayout,
};
use crate::parsing::c::lex::tokens::{
TOK_PP_DEFINE, TOK_PP_ELIF, TOK_PP_ELSE, TOK_PP_ENDIF, TOK_PP_ERROR, TOK_PP_IDENT, TOK_PP_IF,
TOK_PP_IFDEF, TOK_PP_IFNDEF, TOK_PP_INCLUDE, TOK_PP_INCLUDE_NEXT, TOK_PP_LINE, TOK_PP_NULL,
TOK_PP_PRAGMA, TOK_PP_SCCS, TOK_PP_UNDEF, TOK_PP_WARNING, TOK_PREPROC,
};
use vyre_foundation::ir::{BufferAccess, BufferDecl, DataType, Expr, Node, Program};
pub const OP_ID: &str = "vyre-libs::parsing::c::preprocess::gpu_directive_metadata";
pub const BINDING_TOK_TYPES: u32 = 0;
pub const BINDING_TOK_STARTS: u32 = 1;
pub const BINDING_TOK_LENS: u32 = 2;
pub const BINDING_SOURCE: u32 = 3;
pub const BINDING_DIRECTIVE_KINDS: u32 = 4;
pub const BINDING_DIRECTIVE_VALUES: u32 = 5;
pub const MAX_KEYWORD_LEN: u32 = 12;
#[must_use]
pub fn gpu_directive_metadata(num_tokens: u32, source_len: u32) -> Program {
gpu_directive_metadata_with_source_layout(
num_tokens,
source_len,
DirectiveSourceLayout::PackedU32,
)
}
#[must_use]
pub fn gpu_directive_metadata_u8(num_tokens: u32, source_len: u32) -> Program {
gpu_directive_metadata_with_source_layout(num_tokens, source_len, DirectiveSourceLayout::RawU8)
}
fn gpu_directive_metadata_with_source_layout(
num_tokens: u32,
source_len: u32,
source_layout: DirectiveSourceLayout,
) -> Program {
let _ = source_len;
let t = Expr::var("t");
let mut classify: Vec<Node> = Vec::new();
classify.push(Node::let_bind(
"tok_start",
Expr::load("tok_starts", t.clone()),
));
push_hash_scan(&mut classify, source_layout, "s");
push_keyword_start(&mut classify, source_layout, "p");
push_keyword_bytes(&mut classify, source_layout, MAX_KEYWORD_LEN);
push_found_hash(&mut classify);
let store_kind = |kind: u32| -> Vec<Node> {
vec![Node::store("directive_kinds", t.clone(), Expr::u32(kind))]
};
let fire = |cond_u32: Expr, kind: u32| -> Node {
Node::if_then(
Expr::eq(
Expr::bitand(Expr::var("found_hash"), cond_u32),
Expr::u32(1),
),
store_kind(kind),
)
};
classify.push(fire(
Expr::select(
Expr::eq(Expr::var("k_is_continue_0"), Expr::u32(0)),
Expr::u32(1),
Expr::u32(0),
),
TOK_PP_NULL,
));
classify.push(fire(
keyword_match_expr(&[100, 101, 102, 105, 110, 101]),
TOK_PP_DEFINE,
));
classify.push(fire(
keyword_match_expr(&[117, 110, 100, 101, 102]),
TOK_PP_UNDEF,
));
classify.push(fire(
keyword_match_expr(&[105, 110, 99, 108, 117, 100, 101, 95, 110, 101, 120, 116]),
TOK_PP_INCLUDE_NEXT,
));
classify.push(fire(
keyword_match_expr(&[105, 110, 99, 108, 117, 100, 101]),
TOK_PP_INCLUDE,
));
classify.push(fire(
keyword_match_expr(&[105, 102, 110, 100, 101, 102]),
TOK_PP_IFNDEF,
));
classify.push(fire(
keyword_match_expr(&[105, 102, 100, 101, 102]),
TOK_PP_IFDEF,
));
classify.push(fire(keyword_match_expr(&[105, 102]), TOK_PP_IF));
classify.push(fire(keyword_match_expr(&[101, 108, 105, 102]), TOK_PP_ELIF));
classify.push(fire(keyword_match_expr(&[101, 108, 115, 101]), TOK_PP_ELSE));
classify.push(fire(
keyword_match_expr(&[101, 110, 100, 105, 102]),
TOK_PP_ENDIF,
));
classify.push(fire(
keyword_match_expr(&[112, 114, 97, 103, 109, 97]),
TOK_PP_PRAGMA,
));
classify.push(fire(keyword_match_expr(&[108, 105, 110, 101]), TOK_PP_LINE));
classify.push(fire(
keyword_match_expr(&[101, 114, 114, 111, 114]),
TOK_PP_ERROR,
));
classify.push(fire(
keyword_match_expr(&[119, 97, 114, 110, 105, 110, 103]),
TOK_PP_WARNING,
));
classify.push(fire(
keyword_match_expr(&[105, 100, 101, 110, 116]),
TOK_PP_IDENT,
));
classify.push(fire(keyword_match_expr(&[115, 99, 99, 115]), TOK_PP_SCCS));
let body: Vec<Node> = vec![
Node::let_bind("t", Expr::InvocationId { axis: 0 }),
Node::if_then(
Expr::lt(t.clone(), Expr::buf_len("tok_starts")),
vec![
Node::let_bind("tok_type", Expr::load("tok_types", t.clone())),
Node::store("directive_kinds", t.clone(), Expr::u32(0)),
Node::store("directive_values", t.clone(), Expr::u32(0)),
Node::if_then(
Expr::eq(Expr::var("tok_type"), Expr::u32(TOK_PREPROC)),
classify,
),
],
),
];
let source_element = source_buffer_element(source_layout);
Program::wrapped(
vec![
BufferDecl::storage(
"tok_types",
BINDING_TOK_TYPES,
BufferAccess::ReadOnly,
DataType::U32,
)
.with_count(num_tokens.max(1)),
BufferDecl::storage(
"tok_starts",
BINDING_TOK_STARTS,
BufferAccess::ReadOnly,
DataType::U32,
)
.with_count(num_tokens.max(1)),
BufferDecl::storage(
"tok_lens",
BINDING_TOK_LENS,
BufferAccess::ReadOnly,
DataType::U32,
)
.with_count(num_tokens.max(1)),
BufferDecl::storage(
"source",
BINDING_SOURCE,
BufferAccess::ReadOnly,
source_element,
)
.with_count(0),
BufferDecl::storage(
"directive_kinds",
BINDING_DIRECTIVE_KINDS,
BufferAccess::ReadWrite,
DataType::U32,
)
.with_count(num_tokens.max(1)),
BufferDecl::storage(
"directive_values",
BINDING_DIRECTIVE_VALUES,
BufferAccess::ReadWrite,
DataType::U32,
)
.with_count(num_tokens.max(1)),
],
[256, 1, 1],
body,
)
.with_entry_op_id(OP_ID)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn op_id_is_canonical_and_stable() {
assert_eq!(
OP_ID,
"vyre-libs::parsing::c::preprocess::gpu_directive_metadata"
);
}
#[test]
fn binding_indices_are_canonical_and_stable() {
assert_eq!(BINDING_TOK_TYPES, 0);
assert_eq!(BINDING_TOK_STARTS, 1);
assert_eq!(BINDING_TOK_LENS, 2);
assert_eq!(BINDING_SOURCE, 3);
assert_eq!(BINDING_DIRECTIVE_KINDS, 4);
assert_eq!(BINDING_DIRECTIVE_VALUES, 5);
}
#[test]
fn build_program_returns_well_formed_program() {
let p = gpu_directive_metadata(8, 64);
assert_eq!(p.buffers().len(), 6);
assert_eq!(p.workgroup_size(), [256, 1, 1]);
}
#[test]
fn source_buffer_is_runtime_sized_not_source_length_specialized() {
let p = gpu_directive_metadata(8, 64);
let source = p
.buffers()
.iter()
.find(|buffer| buffer.name() == "source")
.expect("Fix: source buffer must exist after directive metadata allocation");
assert_eq!(
source.count, 0,
"source must be runtime-sized so one directive classifier program serves all source lengths"
);
}
#[test]
fn source_buffer_layouts_preserve_packed_abi_and_raw_u8_variant() {
let packed = gpu_directive_metadata(8, 64);
let raw_u8 = gpu_directive_metadata_u8(8, 64);
let packed_source = packed
.buffers()
.iter()
.find(|buffer| buffer.name() == "source")
.expect("Fix: packed directive metadata source buffer must exist");
let raw_u8_source = raw_u8
.buffers()
.iter()
.find(|buffer| buffer.name() == "source")
.expect("Fix: raw-U8 directive metadata source buffer must exist");
assert_eq!(packed_source.element(), DataType::U32);
assert_eq!(packed_source.count, 0);
assert_eq!(raw_u8_source.element(), DataType::U8);
assert_eq!(raw_u8_source.count, 0);
}
#[test]
fn max_keyword_len_covers_longest_directive() {
assert!(MAX_KEYWORD_LEN >= 12);
}
}