From 6036a5fe9056015ceb39e85680813944d93855cc Mon Sep 17 00:00:00 2001 From: itycodes Date: Thu, 6 Mar 2025 10:48:40 +0100 Subject: [PATCH] Initial work on SPIR-V assembly generation Atm it only handles the basic metadata in modules. Doesn't generate code for functions, or types. Type handling is just a placeholder %undefined_type. --- src/compiler/backend/mod.rs | 87 +++++++++++++++++++++++++++++++++++ src/compiler/backend/tests.rs | 25 ++++++++++ src/compiler/mod.rs | 13 ++++-- src/compiler/tests.rs | 5 +- src/parser/tests.rs | 4 +- 5 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 src/compiler/backend/mod.rs create mode 100644 src/compiler/backend/tests.rs diff --git a/src/compiler/backend/mod.rs b/src/compiler/backend/mod.rs new file mode 100644 index 0000000..77e4b81 --- /dev/null +++ b/src/compiler/backend/mod.rs @@ -0,0 +1,87 @@ +#[cfg(test)] +mod tests; + +use crate::compiler::Module; +use std::{fmt::Write, ops::Add}; + +pub fn fix_name(name: &String) -> String { + format!("%{}", name.clone().replace("-", "_")) +} + +pub fn spirv_meta(module: Module) -> String { + let mut spirv_asm = String::new(); + let mut ops: Vec<(Option, Vec)> = Vec::new(); + + let capabilities: Vec = module.capabilities.iter() + .map(|c| format!("{:?}", c)) + .collect(); + for cap in capabilities { + ops.push((None, vec!["OpCapability".to_string(), cap])); + } + + let memory_model_address = match module.memory_model.addressing_model { + crate::compiler::AddressingModel::Logical => "Logical", + crate::compiler::AddressingModel::Physical32 => "Physical32", + crate::compiler::AddressingModel::Physical64 => "Physical64", + crate::compiler::AddressingModel::PhysicalStorageBuffer64 => "PhysicalStorageBuffer64", + }; + let memory_model_model = match module.memory_model.memory_model { + crate::compiler::MemoryModel::Simple => "Simple", + crate::compiler::MemoryModel::GLSL450 => "GLSL450", + crate::compiler::MemoryModel::OpenCL => "OpenCL", + _ => todo!(), + }; + ops.push((None, vec!["OpMemoryModel".to_string(), memory_model_address.to_string(), memory_model_model.to_string()])); + + for entry in module.entry_points { + let exec_model = match entry.execution_model { + crate::compiler::ExecutionModel::Fragment => "Fragment", + crate::compiler::ExecutionModel::Vertex => "Vertex", + }; + let name = entry.name; + let interface: Vec = entry.interface.iter() + .map(|i| fix_name(&i.to_string())) + .collect(); + let exec_mode = match entry.execution_mode { + crate::compiler::ExecutionMode::OriginUpperLeft => "OriginUpperLeft", + }; + ops.push((None, vec!["OpEntryPoint".to_string(), exec_model.to_string(), fix_name(&name), format!("\"{}\"", name), interface.join(" ")])); + ops.push((None, vec!["OpExecutionMode".to_string(), fix_name(&name), exec_mode.to_string()])); + } + + for global in module.globals { + let name = fix_name(&global.name); + let typ = global.typ; + let storage_class = match global.storage_class { + crate::compiler::StorageClass::Input => "Input", + crate::compiler::StorageClass::Output => "Output", + crate::compiler::StorageClass::Undefined => panic!("Bound a non-declared variable"), + }; + let typ_id = "%undefined_type"; + ops.push((Some(name.clone()), vec!["OpVariable".to_string(), typ_id.to_string(), storage_class.to_string()])); + for dec in global.decorations { + // Decorations have the format Location 0, or Builtin FragCoord + let dec = match dec { + crate::compiler::Decoration::Location(loc) => format!("Location {}", loc), + crate::compiler::Decoration::BuiltIn(builtin) => { + let builtin = match builtin { + crate::compiler::BuiltinDecoration::FragCoord => "FragCoord", + }; + format!("BuiltIn {}", builtin) + }, + }; + ops.push((None, vec!["OpDecorate".to_string(), name.clone(), dec])); + } + } + + for op in ops { + if op.0.is_some() { + write!(spirv_asm, "{} = ", op.0.unwrap()).unwrap(); + } + for arg in op.1 { + write!(spirv_asm, "{} ", arg).unwrap(); + } + writeln!(spirv_asm).unwrap(); + } + spirv_asm +} \ No newline at end of file diff --git a/src/compiler/backend/tests.rs b/src/compiler/backend/tests.rs new file mode 100644 index 0000000..87c258d --- /dev/null +++ b/src/compiler/backend/tests.rs @@ -0,0 +1,25 @@ +#[test] +fn test_emit() { + use crate::compiler::*; + use crate::parser::*; + use crate::compiler::backend::*; + let src = " +(module Shader Logical GLSL450) +(import :std GLSL.std.450) +(bind (frag-coord:*v4f32i) (BuiltIn FragCoord)) +(bind (out-color:*v4f32o) (Location 0)) +(dec frag-coord:*v4f32i Input) +(dec out-color:*v4f32o Output) +(entry main Fragment OriginUpperLeft (:frag-coord :out-color)) +(fun (main) + (store-ptr (out-color) + (v4f32i (/ (.xy (load-ptr frag-coord)) + (v2f32 1920.0 1080.0)) + 1.0 + 1.0))) +"; + let mut ast = parse(tokenize(src)); + let module = meta_compile(&mut ast); + let res = spirv_meta(module); + println!("{}", res); +} \ No newline at end of file diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs index 2bf5e06..9a83957 100644 --- a/src/compiler/mod.rs +++ b/src/compiler/mod.rs @@ -1,5 +1,7 @@ use crate::parser::Ast; +pub mod backend; + #[cfg(test)] mod tests; @@ -18,7 +20,8 @@ pub enum ExecutionMode { #[derive(Debug, PartialEq, Default, Clone)] pub enum ExecutionModel { #[default] - Fragment + Fragment, + Vertex, } #[derive(Debug, PartialEq, Default, Clone)] @@ -60,7 +63,7 @@ pub enum BuiltinDecoration { #[derive(Debug, PartialEq, Clone)] pub enum Decoration { - Builtin(BuiltinDecoration), + BuiltIn(BuiltinDecoration), Location(u32), } @@ -175,7 +178,7 @@ pub fn meta_compile(ast: &mut Ast) -> Module { } }).collect(); let bind_name = match bind[0].as_str() { - "Builtin" => Decoration::Builtin(BuiltinDecoration::FragCoord), + "BuiltIn" => Decoration::BuiltIn(BuiltinDecoration::FragCoord), "Location" => Decoration::Location(bind[1].parse::().unwrap()), _ => panic!("Unknown bind! {:?}", bind), }; @@ -249,7 +252,9 @@ pub fn meta_compile(ast: &mut Ast) -> Module { }; let interface: Vec = interface.iter().map(|x| { match x { - Ast::Symbol(s) => s.to_string(), + Ast::Symbol(s) => { + s.to_string().replace(":", "") + }, _ => panic!("Expected symbol! {:?}", x), } }).collect(); diff --git a/src/compiler/tests.rs b/src/compiler/tests.rs index 2915262..08b18e9 100644 --- a/src/compiler/tests.rs +++ b/src/compiler/tests.rs @@ -6,7 +6,7 @@ fn test_compile() { let src = " (module Shader Logical GLSL450) (import :std GLSL.std.450) -(bind (frag-coord:*v4f32i) (Builtin FragCoord)) +(bind (frag-coord:*v4f32i) (BuiltIn FragCoord)) (bind (out-color:*v4f32o) (Location 0)) (dec frag-coord:*v4f32i Input) (dec out-color:*v4f32o Output) @@ -22,7 +22,6 @@ fn test_compile() { println!("{:#?}", ast); let module = meta_compile(&mut ast); println!("{:#?}", module); - //let test_module: Module = Module { capabilities: [Shader], entry_points: [EntryPoint { execution_model: Fragment, execution_mode: OriginUpperLeft, name: "main", interface: [":frag-coord", ":out-color"] }], globals: [GlobalVariable { name: "frag-coord", typ: "*v4f32i", storage_class: Input, decorations: [Builtin(FragCoord)] }, GlobalVariable { name: "out-color", typ: "*v4f32o", storage_class: Output, decorations: [Location(0)] }], functions: [Function { name: "main", return_type: "void", arguments: [], body: Some([]), ast: Some(List([List([Symbol("store-ptr"), List([Symbol("out-color")]), List([Symbol("v4f32i"), List([Symbol("/"), List([Symbol(".xy"), List([Symbol("load-ptr"), Symbol("frag-coord")])]), List([Symbol("v2f32"), Symbol("1920.0"), Symbol("1080.0")])]), Symbol("1.0"), Symbol("1.0")])])])) }], memory_model: Memory { addressing_model: Logical, memory_model: GLSL450 }, imports: [Import { name: ":std", value: "GLSL.std.450" }] }; let test_module = Module { capabilities: vec![Capability::Shader], entry_points: vec![EntryPoint { @@ -36,7 +35,7 @@ fn test_compile() { name: "frag-coord".to_string(), typ: "*v4f32i".to_string(), storage_class: StorageClass::Input, - decorations: vec![Decoration::Builtin(BuiltinDecoration::FragCoord)], + decorations: vec![Decoration::BuiltIn(BuiltinDecoration::FragCoord)], }, GlobalVariable { name: "out-color".to_string(), diff --git a/src/parser/tests.rs b/src/parser/tests.rs index 6251515..dd6a947 100644 --- a/src/parser/tests.rs +++ b/src/parser/tests.rs @@ -22,7 +22,7 @@ fn test_parse() { let src = " (module Shader Logical GLSL450) (import :std GLSL.std.450) -(bind (frag-coord:*v4f32i) (Builtin FragCoord)) +(bind (frag-coord:*v4f32i) (BuiltIn FragCoord)) (bind (out-color:*v4f32o) (Location 0)) (dec frag-coord:*v4f32i Input) (dec out-color:*v4f32o Output) @@ -54,7 +54,7 @@ fn test_parse() { Ast::Symbol("frag-coord:*v4f32i".to_string()), ]), Ast::List(vec![ - Ast::Symbol("Builtin".to_string()), + Ast::Symbol("BuiltIn".to_string()), Ast::Symbol("FragCoord".to_string()), ]), ]),