Compare commits

..

No commits in common. 'master' and 'master' have entirely different histories.

@ -1,13 +1,11 @@
#[cfg(test)]
mod tests;
use crate::compiler::{
AddressingModel, BuiltinDecoration, Decoration, ExecutionMode, ExecutionModel, Instruction,
MemoryModel, Module, StorageClass,
};
use crate::compiler::Module;
use std::fmt;
use std::fmt::Write;
use crate::parser::Ast;
use super::StorageClass;
#[derive(Debug, PartialEq, Clone)]
pub enum Type {
@ -44,43 +42,48 @@ pub fn parse_type(typ: &String) -> Type {
// So, *v4f32i is a pointer to a vector of 4 floats, with the storage class Input.
// *v4f32o is a pointer to a vector of 4 floats, with the storage class Output.
let mut chars = typ.chars();
match chars.next() {
Some('<') => {
let c = typ.chars().next().unwrap();
match c {
'<' => {
assert_eq!(typ.as_str(), "<>");
Type::Void
}
Some('*') => {
let mut typ = chars.collect::<String>();
let storage_class = match typ.pop() {
Some('i') => StorageClass::Input,
Some('o') => StorageClass::Output,
'*' => {
let mut chars = typ.chars();
chars.next();
let typ = chars.collect::<String>();
let storage_class = match typ.chars().last().unwrap() {
'i' => StorageClass::Input,
'o' => StorageClass::Output,
_ => panic!("Invalid storage class"),
};
let typ = typ.chars().take(typ.len() - 1).collect::<String>();
Type::Pointer(Box::new(parse_type(&typ)), storage_class)
}
Some('v') => {
let size = chars.next().map(|s| s.to_digit(10)).flatten().unwrap();
'v' => {
let mut chars = typ.chars();
chars.next();
let size = chars.next().unwrap().to_digit(10).unwrap();
let typ = chars.collect::<String>();
Type::Vector(Box::new(parse_type(&typ)), size)
}
Some('f') => {
let size = chars.collect::<String>().parse().unwrap();
'f' => {
let size = typ.chars().skip(1).collect::<String>().parse().unwrap();
Type::Float(size)
}
Some('s') => {
let size = chars.collect::<String>().parse().unwrap();
's' => {
let size = typ.chars().skip(1).collect::<String>().parse().unwrap();
Type::Int(size)
}
Some('u') => {
let size = chars.collect::<String>().parse().unwrap();
'u' => {
let size = typ.chars().skip(1).collect::<String>().parse().unwrap();
Type::Unsigned(size)
}
_ => panic!("Invalid type"),
}
}
fn emit_type(typ: &Type, ops: &mut Vec<(String, String)>) {
fn emit_type(typ: Type, ops: &mut Vec<(String, String)>) {
match &typ {
Type::Void => {
ops.push(("%void".to_string(), "OpTypeVoid".to_string()));
@ -104,14 +107,18 @@ fn emit_type(typ: &Type, ops: &mut Vec<(String, String)>) {
));
}
Type::Vector(in_typ, size) => {
emit_type(in_typ, ops);
emit_type(*in_typ.clone(), ops);
ops.push((
fix_name(&typ.to_string()),
format!("OpTypeVector {} {}", fix_name(&in_typ.to_string()), size),
format!(
"OpTypeVector {} {}",
fix_name(&in_typ.clone().to_string()),
size
),
))
}
Type::Pointer(in_typ, storage_class) => {
emit_type(in_typ, ops);
emit_type(*in_typ.clone(), ops);
let typ_id = fix_name(&typ.to_string());
let storage_class = match storage_class {
StorageClass::Input => "Input",
@ -130,23 +137,27 @@ fn emit_type(typ: &Type, ops: &mut Vec<(String, String)>) {
}
}
fn fix_name(name: &str) -> String {
fn fix_name(name: &String) -> String {
format!(
"%{}",
name.replace("-", "_")
name.clone()
.replace("-", "_")
.replace("*", "p")
.replace("<>", "void")
.replace(".", "_")
)
}
fn has_id<T>(name: &str, ops: &Vec<(Option<String>, T)>) -> bool {
ops.iter()
.find(|op| op.0.as_ref().map(|s| *s == name).unwrap_or(false))
.is_some()
fn has_id(name: String, ops: &Vec<(Option<String>, Vec<String>)>) -> bool {
for op in ops {
if op.0.is_some() && op.0.clone().unwrap() == name {
return true;
}
}
false
}
pub fn spirv_meta(module: &mut Module) -> Vec<(Option<String>, Vec<String>)> {
pub fn spirv_meta(module: Module) -> String {
let mut spirv_asm = String::new();
let mut ops: Vec<(Option<String>, Vec<String>)> = Vec::new();
let capabilities: Vec<String> = module
@ -159,15 +170,15 @@ pub fn spirv_meta(module: &mut Module) -> Vec<(Option<String>, Vec<String>)> {
}
let memory_model_address = match module.memory_model.addressing_model {
AddressingModel::Logical => "Logical",
AddressingModel::Physical32 => "Physical32",
AddressingModel::Physical64 => "Physical64",
AddressingModel::PhysicalStorageBuffer64 => "PhysicalStorageBuffer64",
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 {
MemoryModel::Simple => "Simple",
MemoryModel::GLSL450 => "GLSL450",
MemoryModel::OpenCL => "OpenCL",
crate::compiler::MemoryModel::Simple => "Simple",
crate::compiler::MemoryModel::GLSL450 => "GLSL450",
crate::compiler::MemoryModel::OpenCL => "OpenCL",
_ => todo!(),
};
ops.push((
@ -179,10 +190,10 @@ pub fn spirv_meta(module: &mut Module) -> Vec<(Option<String>, Vec<String>)> {
],
));
for entry in module.entry_points.clone() {
for entry in module.entry_points {
let exec_model = match entry.execution_model {
ExecutionModel::Fragment => "Fragment",
ExecutionModel::Vertex => "Vertex",
crate::compiler::ExecutionModel::Fragment => "Fragment",
crate::compiler::ExecutionModel::Vertex => "Vertex",
};
let name = entry.name;
let interface: Vec<String> = entry
@ -191,7 +202,7 @@ pub fn spirv_meta(module: &mut Module) -> Vec<(Option<String>, Vec<String>)> {
.map(|i| fix_name(&i.to_string()))
.collect();
let exec_mode = match entry.execution_mode {
ExecutionMode::OriginUpperLeft => "OriginUpperLeft",
crate::compiler::ExecutionMode::OriginUpperLeft => "OriginUpperLeft",
};
ops.push((
None,
@ -212,36 +223,19 @@ pub fn spirv_meta(module: &mut Module) -> Vec<(Option<String>, Vec<String>)> {
],
));
}
for global in module.globals.clone() {
let name: String = fix_name(&global.name);
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 global in module.globals.clone() {
for global in module.globals {
let name = fix_name(&global.name);
let typ = global.typ;
let _typ = global.typ;
let storage_class = match global.storage_class {
StorageClass::Input => "Input",
StorageClass::Output => "Output",
StorageClass::Undefined => panic!("Bound a non-declared variable"),
crate::compiler::StorageClass::Input => "Input",
crate::compiler::StorageClass::Output => "Output",
crate::compiler::StorageClass::Undefined => panic!("Bound a non-declared variable"),
};
let mut type_ops = Vec::new();
emit_type(&parse_type(&typ), &mut type_ops);
emit_type(parse_type(&_typ), &mut type_ops);
for op in type_ops {
if has_id(&op.0, &ops) {
if has_id(op.0.clone(), &ops) {
continue;
}
ops.push((Some(op.0), vec![op.1]));
@ -250,423 +244,51 @@ pub fn spirv_meta(module: &mut Module) -> Vec<(Option<String>, Vec<String>)> {
Some(name.clone()),
vec![
"OpVariable".to_string(),
fix_name(&typ),
fix_name(&_typ),
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 fun in module.functions.clone() {
let name = fix_name(&format!("l_{}", fun.name));
for fun in module.functions {
let name = fix_name(&fun.name);
let return_type = fix_name(&fun.return_type);
let mut type_ops = Vec::new();
emit_type(&parse_type(&fun.return_type), &mut type_ops);
emit_type(parse_type(&fun.return_type), &mut type_ops);
for op in type_ops {
if has_id(&op.0, &ops) {
if has_id(op.0.clone(), &ops) {
continue;
}
ops.push((Some(op.0), vec![op.1]));
}
// Push OpFunctionType
ops.push((Some(name), vec!["OpTypeFunction".to_string(), return_type]));
}
// 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
ops
}
enum Number {
Int(i32),
Float(f32),
NotANumber,
}
fn match_number(s: &str) -> Number {
// floats have to be in the format of [0-9]+\.[0-9]*
// integers have to be in the format of [0-9]+
let mut chars = s.chars();
let mut has_dot = false;
while let Some(c) = chars.next() {
if c == '.' {
if has_dot {
// only one dot allowed.
return Number::NotANumber;
}
has_dot = true;
} else if !c.is_digit(10) {
// not a number;
// has a character that is not a digit or a dot.
return Number::NotANumber;
}
}
if has_dot {
// we have checked that the whole thing is numbers and dots
// and that there is precisely one dot
// that matches the regex.
Number::Float(s.parse().unwrap())
} else {
// this is an integer, since no dots.
Number::Int(s.parse().unwrap())
}
}
fn compile_biop(
op: &str,
lst: &mut Vec<Ast>,
vars: &mut Vec<(String, String)>,
constants: &mut Vec<(String, String)>,
types: &mut Vec<String>,
counter: &mut i32,
stack: &mut Vec<String>,
out: &mut Vec<(Option<String>, String)>,
typ: &str,
) {
assert!(lst.len() == 2);
let rhs = lst.pop().unwrap();
let lhs = lst.pop().unwrap();
compile_ast_ssa(lhs, vars, constants, types, counter, stack, out);
compile_ast_ssa(rhs, vars, constants, types, counter, stack, out);
let rhs_id = stack.pop().unwrap();
let lhs_id = stack.pop().unwrap();
let id = String::from(counter.to_string());
*counter += 1;
out.push((
Some(id.clone()),
format!(
"{} {} {} {}",
op,
fix_name(&String::from(typ)),
fix_name(&lhs_id),
fix_name(&rhs_id),
),
));
stack.push(id);
}
fn compile_comp_constr(
lst: &mut Vec<Ast>,
vars: &mut Vec<(String, String)>,
constants: &mut Vec<(String, String)>,
types: &mut Vec<String>,
counter: &mut i32,
stack: &mut Vec<String>,
out: &mut Vec<(Option<String>, String)>,
typ: &str,
) {
types.push(String::from(typ));
let typ_parsed = parse_type(&String::from(typ));
match typ_parsed {
Type::Vector(_, size) => {
assert!(lst.len() == size as usize);
let mut inners: Vec<String> = vec![];
for _ in 0..size {
let ast = lst.pop().unwrap();
compile_ast_ssa(ast, vars, constants, types, counter, stack, out);
let res_id = stack.pop().unwrap();
inners.push(res_id);
}
let id = String::from(counter.to_string());
*counter += 1;
let mut out_str = String::new();
out_str.push_str(
format!("OpCompositeConstruct {}", fix_name(&String::from(typ))).as_str(),
);
inners.reverse();
for inn in inners {
out_str.push_str(format!(" {}", fix_name(&inn)).as_str());
}
out.push((Some(id.clone()), out_str));
stack.push(id);
}
_ => {
panic!("Invalid vector!")
}
}
}
fn compile_comp_extract(
lst: &mut Vec<Ast>,
vars: &mut Vec<(String, String)>,
constants: &mut Vec<(String, String)>,
types: &mut Vec<String>,
counter: &mut i32,
stack: &mut Vec<String>,
out: &mut Vec<(Option<String>, String)>,
typ: &str,
inx: u32,
) {
assert!(lst.len() == 1);
let vec = lst.pop().unwrap();
compile_ast_ssa(vec, vars, constants, types, counter, stack, out);
let vec_id = stack.pop().unwrap();
let id = counter.to_string();
*counter += 1;
out.push((
Some(id.clone()),
format!(
"OpCompositeExtract {} {} {}",
fix_name(&String::from(typ)),
fix_name(&vec_id),
inx
),
));
stack.push(id);
}
pub fn compile_ast_ssa(
ast: Ast,
vars: &mut Vec<(String, String)>,
constants: &mut Vec<(String, String)>,
types: &mut Vec<String>,
counter: &mut i32,
stack: &mut Vec<String>,
out: &mut Vec<(Option<String>, String)>,
) {
match ast.clone().list() {
Some(mut lst) => {
assert!(!lst.is_empty());
let fun = lst.remove(0);
assert!(true); // no safe remove, thanks Rust
let fun_name = fun.symbol();
assert!(fun_name.is_some());
let fun_name = fun_name.unwrap();
if fun_name.starts_with(".") {
let to_split = &fun_name[1..fun_name.len()];
let split = to_split.split("-").collect::<Vec<&str>>();
assert!(split.len() == 2);
let inx = split[0];
let typ = split[1];
let inx = str::parse::<u32>(inx).expect("Invalid index!");
compile_comp_extract(
&mut lst, vars, constants, types, counter, stack, out, typ, inx,
);
return;
}
if fun_name.starts_with("/") {
let typ = &fun_name[1..fun_name.len()];
compile_biop(
"OpFDiv", &mut lst, vars, constants, types, counter, stack, out, typ,
);
return;
}
if fun_name.starts_with("*") {
let typ = &fun_name[1..fun_name.len()];
compile_biop(
"OpFMul", &mut lst, vars, constants, types, counter, stack, out, typ,
);
return;
}
if fun_name.starts_with("+") {
let typ = &fun_name[1..fun_name.len()];
compile_biop(
"OpFAdd", &mut lst, vars, constants, types, counter, stack, out, typ,
);
return;
}
if fun_name.starts_with("-") {
let typ = &fun_name[1..fun_name.len()];
compile_biop(
"OpFSub", &mut lst, vars, constants, types, counter, stack, out, typ,
);
return;
}
if fun_name.starts_with("v") {
compile_comp_constr(
&mut lst,
vars,
constants,
types,
counter,
stack,
out,
fun_name.as_str(),
);
return;
}
if fun_name.starts_with("load-ptr-") {
let typ = &fun_name[9..fun_name.len()];
assert!(lst.len() == 1);
let ptr = lst.pop().unwrap();
compile_ast_ssa(ptr, vars, constants, types, counter, stack, out);
let ptr_id = stack.pop().unwrap();
let id: String = counter.to_string();
*counter += 1;
out.push((
Some(id.clone()),
format!(
"OpLoad {} {}",
fix_name(&String::from(typ)),
fix_name(&ptr_id)
),
ops.push((
Some(name.clone()),
vec!["OpTypeFunction".to_string(), return_type.clone()],
));
stack.push(id);
return;
}
match fun_name.as_str() {
"store-ptr" => {
assert!(lst.len() == 2);
let val = lst.pop().unwrap();
let ptr = lst.pop().unwrap();
compile_ast_ssa(val, vars, constants, types, counter, stack, out);
compile_ast_ssa(ptr, vars, constants, types, counter, stack, out);
let val_id = stack.pop().unwrap();
let ptr_id = stack.pop().unwrap();
out.push((
None,
format!("OpStore {} {}", fix_name(&val_id), fix_name(&ptr_id)),
));
}
s => {
panic!(
"Unknown function: {} with params {:#?} in context:\n{:#?}",
s, lst, ast
);
}
}
}
None => {
let sym = ast.symbol().expect("This must be a symbol");
match match_number(&sym) {
Number::Int(i) => {
let key = format!("i32_{}", i);
let mut contains = false;
for c in constants.iter() {
if c.0 == key {
contains = true;
}
}
if !contains {
constants.push((key.clone(), format!("OpConstant %i32 {}", i.to_string())));
}
stack.push(key);
}
Number::Float(f) => {
let key = format!("f32_{}", f);
let mut contains = false;
for c in constants.iter() {
if c.0 == key {
contains = true;
}
}
if !contains {
constants.push((key.clone(), format!("OpConstant %f32 {}", f.to_string())));
}
for t in types.iter() {
if t == "f32" {
contains = true;
}
}
if !contains {
types.push("f32".to_string());
}
stack.push(key);
}
Number::NotANumber => {
for v in vars.iter() {
if v.0 == sym {
stack.push(v.0.clone());
return;
}
}
panic!("Unknown variable or constant: {}", sym);
}
}
}
}
}
pub fn compile_fun_ssa(module: &mut Module, ops: &Vec<(Option<String>, String)>) {
let mut label_counter = Box::new(0);
for fun in module.functions.iter_mut() {
assert!(fun.ast.is_some());
let ast = fun.ast.as_mut().unwrap();
let block = ast.clone().list().unwrap().get(0).unwrap().clone();
let mut vars = vec![];
let mut constants = vec![];
let mut types = vec![];
let mut counter = Box::new(0);
let mut stack = vec![];
let mut out_op = vec![];
for v in &module.globals {
vars.push((v.name.clone(), v.typ.clone()));
}
compile_ast_ssa(
block,
&mut vars,
&mut constants,
&mut types,
&mut counter,
&mut stack,
&mut out_op,
);
let mut out_pre = vec![];
for t in &types {
let typ = parse_type(t);
if has_id(&fix_name(&typ.to_string()), ops) {
continue;
}
if has_id(&fix_name(&typ.to_string()), &out_pre) {
continue;
}
let mut type_ops = vec![];
emit_type(&typ, &mut type_ops);
for type_op in type_ops {
out_pre.push((Some(type_op.0), type_op.1));
}
}
for c in &constants {
out_pre.push((Some(fix_name(&c.0.clone())), c.1.clone()));
}
// TODO non-void type
out_pre.push((
Some(fix_name(&fun.name)),
format!("OpFunction %void None %l_{}", fun.name),
));
out_pre.push((
Some(format!("%n_{}", label_counter.to_string())),
"OpLabel".to_string(),
));
*label_counter += 1;
let mut out_ops = out_pre.clone();
for op in out_op {
for op in ops {
if op.0.is_some() {
out_ops.push((Some(fix_name(&op.0.unwrap())), op.1));
} else {
out_ops.push((None, op.1));
}
write!(spirv_asm, "{} = ", op.0.unwrap()).unwrap();
}
out_ops.push((None, String::from("OpReturn")));
out_ops.push((None, String::from("OpFunctionEnd")));
for op in out_ops {
let split: Vec<String> = op.1.split(" ").map(|s| s.to_string()).collect();
let op_name: String = (&split[0]).clone();
let op_args: Vec<String> = split[1..].iter().map(|s| s.clone()).collect();
let op_id = op.0.clone();
let ins: Instruction = Instruction {
result_id: op_id,
op: op_name,
operands: op_args,
};
fun.body.as_mut().unwrap().push(ins);
for arg in op.1 {
write!(spirv_asm, "{} ", arg).unwrap();
}
writeln!(spirv_asm).unwrap();
}
spirv_asm
}

@ -12,17 +12,16 @@ fn test_emit() {
(dec out-color:*v4f32o Output)
(entry main Fragment OriginUpperLeft (:frag-coord :out-color))
(fun (main) :<>
(store-ptr out-color
(store-ptr (out-color)
(v4f32i (/ (.xy (load-ptr frag-coord))
(v2f32 1920.0 1080.0))
1.0
1.0)))
";
let ast = parse(tokenize(src));
let mut module = meta_compile(ast.unwrap()).unwrap();
let res = spirv_meta(&mut module);
println!("{:#?}", res);
// TODO add an assert
let module = meta_compile(ast.unwrap()).unwrap();
let res = spirv_meta(module);
println!("{}", res);
}
#[test]
@ -50,161 +49,3 @@ fn test_type_parse() {
assert_eq!(parse_type(&"f32".to_string()), Float(32));
assert_eq!(parse_type(&"s32".to_string()), Int(32));
}
#[test]
fn test_block_ssa() {
use crate::compiler::backend::*;
use crate::compiler::*;
use crate::parser::*;
let src = "
(module Shader Logical GLSL450)
(import :std GLSL.std.450)
(bind (out-color:*f32o) (Location 0))
(dec out-color:*f32o Output)
(entry main Fragment OriginUpperLeft (:out-color))
(fun (main) :<>
(store-ptr out-color (/f32 1.0 (+f32 1.0 1.0))))
";
let ast = parse(tokenize(src));
let mut module = meta_compile(ast.unwrap()).unwrap();
let fun = module.functions.pop().unwrap();
let block = fun.ast.unwrap();
let block = block.list().unwrap().get(0).unwrap().clone();
println!("{:#?}", block);
let mut vars = vec![];
let mut constants = vec![];
let mut types = vec![];
let mut counter = Box::new(0);
let mut stack = vec![];
let mut out = vec![];
for v in &module.globals {
vars.push((v.name.clone(), v.typ.clone()));
}
compile_ast_ssa(
block,
&mut vars,
&mut constants,
&mut types,
&mut counter,
&mut stack,
&mut out,
);
println!("\n---vars:---\n{:#?}\n-----------", vars);
println!("\n---constants:---\n{:#?}\n----------------", constants);
println!("\n---types:---\n{:#?}\n------------", types);
println!("\n---stack:---\n{:#?}\n------------", stack);
println!("\n---counter:---\n{}\n--------------", counter);
println!("\n---out:---");
for (a, b) in &out {
match a {
Some(a) => println!("%{} = {}", a, b),
None => println!("{}", b),
}
}
println!("----------");
assert!(stack.is_empty());
}
#[test]
fn test_fun_ssa() {
use crate::compiler::backend::*;
use crate::compiler::*;
use crate::parser::*;
let src = "
(module Shader Logical GLSL450)
(import :std GLSL.std.450)
(bind (out-color:*f32o) (Location 0))
(dec out-color:*f32o Output)
(entry main Fragment OriginUpperLeft (:out-color))
(fun (main) :<>
(store-ptr out-color (/f32 1.0 (+f32 1.0 1.0))))
";
let ast = parse(tokenize(src));
let mut module = meta_compile(ast.unwrap()).unwrap();
let ops = vec![];
compile_fun_ssa(&mut module, &ops);
let res = module.functions.pop().unwrap();
let res = res.body.unwrap();
println!("{:#?}", res);
// TODO we need to unify the place where we call fix_name
let res_spv: Vec<Instruction> = vec![
Instruction {
result_id: Some("%f32".to_string()),
op: "OpTypeFloat".to_string(),
operands: vec!["32".to_string()],
},
Instruction {
result_id: Some("%f32_1".to_string()),
op: "OpConstant".to_string(),
operands: vec!["%f32".to_string(), "1".to_string()],
},
Instruction {
result_id: Some("%main".to_string()),
op: "OpFunction".to_string(),
operands: vec![
"%void".to_string(),
"None".to_string(),
"%l_main".to_string(),
],
},
Instruction {
result_id: Some("%n_0".to_string()),
op: "OpLabel".to_string(),
operands: vec![],
},
Instruction {
result_id: Some("%0".to_string()),
op: "OpFAdd".to_string(),
operands: vec![
"%f32".to_string(),
"%f32_1".to_string(),
"%f32_1".to_string(),
],
},
Instruction {
result_id: Some("%1".to_string()),
op: "OpFDiv".to_string(),
operands: vec!["%f32".to_string(), "%f32_1".to_string(), "%0".to_string()],
},
Instruction {
result_id: None,
op: "OpStore".to_string(),
operands: vec!["%out_color".to_string(), "%1".to_string()],
},
Instruction {
result_id: None,
op: "OpReturn".to_string(),
operands: vec![],
},
Instruction {
result_id: None,
op: "OpFunctionEnd".to_string(),
operands: vec![],
},
];
assert_eq!(res, res_spv);
}
#[test]
fn test_fun_ssa_vec() {
use crate::compiler::backend::*;
use crate::compiler::*;
use crate::parser::*;
let src = "
(module Shader Logical GLSL450)
(import :std GLSL.std.450)
(bind (out-color:*v4f32o) (Location 0))
(dec out-color:*v4f32o Output)
(entry main Fragment OriginUpperLeft (:out-color))
(fun (main) :<>
(store-ptr out-color (v4f32 1.0 1.0 0.0 1.0)))
";
let ast = parse(tokenize(src));
let mut module = meta_compile(ast.unwrap()).unwrap();
let ops = vec![];
compile_fun_ssa(&mut module, &ops);
let res = module.functions.pop().unwrap();
let res = res.body.unwrap();
println!("{:#?}", res);
println!("{:#?}", module)
}

@ -194,7 +194,7 @@ pub fn compile_fun<I: Iterator<Item = Ast>>(
};
let fun = Function {
name: name.to_string(),
return_type,
return_type: return_type,
arguments: vec![],
body: Some(vec![]),
ast: Some(Ast::List(Localised {

@ -1,213 +0,0 @@
use std::fmt::{self, Display, Write};
use crate::{
compiler::CompilerError,
parser::{Location, ParseError},
};
pub fn print_error<E: FormattedLocatedError>(err: E, src: Option<&str>) {
let mut buf = String::new();
match src {
Some(src) => {
err.fmt_with_src(&mut buf, &src).unwrap();
}
None => err.fmt(&mut buf).unwrap(),
}
print!("{buf}");
}
pub trait FormattedLocatedError {
fn fmt<W: fmt::Write>(&self, w: &mut W) -> fmt::Result;
fn fmt_with_src<W: fmt::Write>(&self, w: &mut W, src: &str) -> fmt::Result;
}
impl Display for Location {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Location::Char { line, col } => write!(f, "{line}:{col}"),
Location::String { lines, cols } => write!(f, "{}:{}", lines.start(), cols.start()),
Location::All => write!(f, "Entire file"),
}
}
}
impl FormattedLocatedError for ParseError {
fn fmt<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
match self {
ParseError::UnexpectedParenClose(location) => {
writeln!(w, "Unexpected closing paren at {location}")?
}
ParseError::UnexpectedEof => writeln!(w, "Unexpected end of file")?,
}
Ok(())
}
fn fmt_with_src<W: fmt::Write>(&self, w: &mut W, src: &str) -> fmt::Result {
match self {
ParseError::UnexpectedParenClose(location) => {
writeln!(w, "Unexpected closing paren at {location}")?;
here(location, src, Some("Unexpected ')'".to_string()), w)?;
}
ParseError::UnexpectedEof => writeln!(w, "Unexpected end of file")?,
}
Ok(())
}
}
impl FormattedLocatedError for CompilerError {
fn fmt<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
match self {
CompilerError::ExpectedSymbol(location) => {
writeln!(
w,
"Expected a symbol but got a list or nothing at {location}"
)
}
CompilerError::ExpectedList(location) => {
writeln!(
w,
"Expected a list but got a symbol or nothing at {location}"
)
}
CompilerError::UnknownKeyword(k, location) => {
writeln!(w, "Got unexpected keyword {k} at {location}")?;
writeln!(w, "Expected one of: module, import, bind, dec, entry, fun")
}
CompilerError::ExpectedOneOf(vec, k, location) => {
writeln!(w, "Got unexpected token {k} at {location}")?;
writeln!(w, "Expected one of: {}", vec.join(", "))
}
CompilerError::TrailingTokens(location) => {
writeln!(w, "Got unexpected trailing tokens at {location}")?;
writeln!(w, "Expected end of list")
}
CompilerError::UnknownBind(b, location) => {
writeln!(w, "Got unexpected keyword {b} at {location}")?;
writeln!(w, "Expected one of: BuiltIn, Location")
}
CompilerError::ExpectedType(location) => {
writeln!(w, "Expected name:type but got name at {location}")
}
}
}
fn fmt_with_src<W: fmt::Write>(&self, w: &mut W, src: &str) -> fmt::Result {
match self {
CompilerError::ExpectedSymbol(location) => {
writeln!(
w,
"Expected a symbol but got a list or nothing at {location}"
)?;
here(location, src, Some("Expected symbol".to_string()), w)
}
CompilerError::ExpectedList(location) => {
writeln!(
w,
"Expected a list but got a symbol or nothing at {location}"
)?;
here(location, src, Some("Expected List".to_string()), w)
}
CompilerError::UnknownKeyword(k, location) => {
writeln!(w, "Got unexpected keyword {k} at {location}")?;
writeln!(w, "Expected one of: module, import, bind, dec, entry, fun")?;
here(location, src, Some("Unexpected keyword".to_string()), w)
}
CompilerError::ExpectedOneOf(vec, k, location) => {
writeln!(w, "Got unexpected token {k} at {location}")?;
writeln!(w, "Expected one of: {}", vec.join(", "))?;
here(location, src, Some("Unexpected token".to_string()), w)
}
CompilerError::TrailingTokens(location) => {
writeln!(w, "Got unexpected trailing tokens at {location}")?;
writeln!(w, "Expected end of list")?;
here(location, src, Some("Unexpected token".to_string()), w)
}
CompilerError::UnknownBind(b, location) => {
writeln!(w, "Got unexpected keyword {b} at {location}")?;
writeln!(w, "Expected one of: BuiltIn, Location")?;
here(location, src, Some("Unexpected keyword".to_string()), w)
}
CompilerError::ExpectedType(location) => {
writeln!(w, "Expected name:type but got name at {location}")?;
here(location, src, None, w)
}
}
}
}
fn here<W: fmt::Write>(
loc: &Location,
src: &str,
comment: Option<String>,
w: &mut W,
) -> fmt::Result {
writeln!(w, "Here:")?;
writeln!(
w,
"{}",
annotate_src(&loc, src, comment.unwrap_or_default())
)?;
Ok(())
}
fn annotate_src(loc: &Location, src: &str, comment: String) -> String {
let src_lines = src.split('\n').collect::<Vec<_>>();
let mut buf = String::new();
match loc {
Location::Char { line, col } => {
writeln!(buf, "{}|{}", line_num(*line - 1), src_lines[*line - 2]).unwrap();
writeln!(buf, "{}|{}", line_num(*line), src_lines[*line - 1]).unwrap();
writeln!(buf, "{}|{}^ {}", " ", " ".repeat(col - 1), comment).unwrap();
writeln!(buf, "{}|{}", line_num(*line + 1), src_lines[*line]).unwrap();
}
Location::String { lines, cols } => {
writeln!(
buf,
"{}|{}",
line_num(*lines.start() - 1),
src_lines[*lines.start() - 2]
)
.unwrap();
writeln!(
buf,
"{}",
src_lines[(lines.start() - 1)..*lines.end()]
.iter()
.zip(lines.clone())
.map(|(l, n)| format!("{}|{}", line_num(n), l))
.collect::<Vec<_>>()
.join("\n")
)
.unwrap();
writeln!(
buf,
"{}|{}{} {}",
" ",
" ".repeat(cols.start() - 1),
"^".repeat(cols.end() - cols.start() + 1),
comment
)
.unwrap();
writeln!(
buf,
"{}|{}",
line_num(*lines.end() + 1),
src_lines[*lines.end()]
)
.unwrap();
}
Location::All => todo!(),
}
buf
}
fn line_num(num: usize) -> String {
let num = format!("{num}");
format!("{num}{}", " ".repeat(6 - num.len()))
}

@ -1,257 +1,115 @@
use compiler::{
backend::{compile_fun_ssa, spirv_meta},
meta_compile,
};
use error::print_error;
use std::{env, fs, path::PathBuf, process::ExitCode};
use parser::parse_string;
use std::fmt::Write;
pub mod compiler;
mod error;
pub mod parser;
#[derive(Debug, Default)]
struct CompilerArgs {
input: Option<PathBuf>,
output: Option<PathBuf>,
print_help: bool,
print_version: bool,
}
const SPIRV_VERSION: &'static str = "1.0";
fn main() -> ExitCode {
let args = parse_args();
if args.print_version || args.print_help {
if args.print_help {
print_help();
}
if args.print_version {
print_version();
}
return ExitCode::FAILURE;
}
let Some(input) = args.input else {
eprintln!("No input specified");
return ExitCode::FAILURE;
};
let output = args.output.unwrap_or(PathBuf::from("./out.spvas"));
let src = match fs::read_to_string(input) {
Ok(i) => i,
Err(e) => {
eprintln!("{e}");
return ExitCode::FAILURE;
}
};
let ast = match parse_string(&src) {
Ok(i) => i,
Err(e) => {
print_error(e, Some(&src));
return ExitCode::FAILURE;
}
};
let mut module = match meta_compile(ast) {
Ok(m) => m,
Err(e) => {
print_error(e, Some(&src));
return ExitCode::FAILURE;
}
};
use std::fmt::Write;
let ops_split_params = spirv_meta(&mut module);
let mut ops_merged_param = vec![];
for op in ops_split_params.clone() {
ops_merged_param.push((op.0, op.1.join(" ")));
}
compile_fun_ssa(&mut module, &ops_merged_param);
let mut res = String::new();
for op in ops_split_params {
fn main() {
let mut ops: Vec<(Option<String>, Vec<String>)> = Vec::new();
// OpMemoryModel Logical GLSL450
// OpEntryPoint Fragment %main "main"
// OpExecutionMode %main OriginUpperLeft
// OpSource GLSL 450
// OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
// OpSourceExtension "GL_GOOGLE_include_directive"
// OpName %main "main"
//%void = OpTypeVoid
//%3 = OpTypeFunction %void
//%main = OpFunction %void None %3
//%5 = OpLabel
// OpReturn
// OpFunctionEnd
ops.push((None, vec!["OpCapability".to_string(), "Shader".to_string()]));
ops.push((
Some("%1".to_string()),
vec![
"OpExtInstImport".to_string(),
"\"GLSL.std.450\"".to_string(),
],
));
ops.push((
None,
vec![
"OpMemoryModel".to_string(),
"Logical".to_string(),
"GLSL450".to_string(),
],
));
ops.push((
None,
vec![
"OpEntryPoint".to_string(),
"Fragment".to_string(),
"%main".to_string(),
"\"main\"".to_string(),
],
));
ops.push((
None,
vec![
"OpExecutionMode".to_string(),
"%main".to_string(),
"OriginUpperLeft".to_string(),
],
));
ops.push((
None,
vec![
"OpSource".to_string(),
"GLSL".to_string(),
"450".to_string(),
],
));
ops.push((
None,
vec![
"OpSourceExtension".to_string(),
"\"GL_GOOGLE_cpp_style_line_directive\"".to_string(),
],
));
ops.push((
None,
vec![
"OpSourceExtension".to_string(),
"\"GL_GOOGLE_include_directive\"".to_string(),
],
));
ops.push((
None,
vec![
"OpName".to_string(),
"%main".to_string(),
"\"main\"".to_string(),
],
));
ops.push((Some("%void".to_string()), vec!["OpTypeVoid".to_string()]));
ops.push((
Some("%3".to_string()),
vec!["OpTypeFunction".to_string(), "%void".to_string()],
));
ops.push((
Some("%main".to_string()),
vec![
"OpFunction".to_string(),
"%void".to_string(),
"None".to_string(),
"%3".to_string(),
],
));
ops.push((Some("%5".to_string()), vec!["OpLabel".to_string()]));
ops.push((None, vec!["OpReturn".to_string()]));
ops.push((None, vec!["OpFunctionEnd".to_string()]));
let mut out: String = String::new();
for op in ops {
if op.0.is_some() {
write!(res, "{} = ", op.0.unwrap()).unwrap();
write!(out, "{} = ", op.0.unwrap()).unwrap();
}
for arg in op.1 {
write!(res, "{} ", arg).unwrap();
}
writeln!(res).unwrap();
}
for fun in module.functions.iter() {
// TODO non-void type
for inst in fun.body.clone().unwrap().iter() {
if let Some(id) = inst.result_id.as_ref() {
write!(res, "{} = ", id).unwrap();
write!(out, "{} ", arg).unwrap();
}
write!(res, "{}", inst.op).unwrap();
for op in inst.operands.iter() {
write!(res, " {}", op).unwrap();
writeln!(out).unwrap();
}
writeln!(res).unwrap();
}
}
fs::write(output, res).unwrap();
ExitCode::SUCCESS
}
fn parse_args() -> CompilerArgs {
let mut args = env::args();
let _prog = args.next().unwrap();
let mut parsed_args = CompilerArgs::default();
while let Some(arg) = args.next() {
if let Some(arg) = arg.strip_prefix("--") {
match arg {
"help" => parsed_args.print_help = true,
"version" => parsed_args.print_version = true,
"input" => {
if parsed_args.input.is_none() {
let Some(input) = args.next() else {
eprintln!("input needs a file");
eprintln!();
parsed_args.print_help = true;
return parsed_args;
};
parsed_args.input = Some(PathBuf::from(input));
} else {
eprintln!("input can be passed only once");
eprintln!();
parsed_args.print_help = true;
return parsed_args;
}
}
"output" => {
if parsed_args.output.is_none() {
let Some(output) = args.next() else {
parsed_args.print_help = true;
eprintln!("output needs a file");
eprintln!();
return parsed_args;
};
parsed_args.output = Some(PathBuf::from(output));
} else {
eprintln!("output can be passed only once");
eprintln!();
parsed_args.print_help = true;
return parsed_args;
}
}
a => {
eprintln!("unknown arg --{a}");
eprintln!();
parsed_args.print_help = true;
return parsed_args;
}
}
continue;
}
if let Some(arg) = arg.strip_prefix("-") {
let mut chars = arg.chars();
while let Some(arg) = chars.next() {
match arg {
'i' => {
if chars.next().is_some() {
parsed_args.print_help = true;
eprintln!("-i needs to be last in combined args");
return parsed_args;
}
if parsed_args.input.is_none() {
let Some(input) = args.next() else {
eprintln!("input needs a file");
eprintln!();
parsed_args.print_help = true;
return parsed_args;
};
parsed_args.input = Some(PathBuf::from(input));
} else {
eprintln!("input can be passed only once");
eprintln!();
parsed_args.print_help = true;
return parsed_args;
}
}
'o' => {
if chars.next().is_some() {
parsed_args.print_help = true;
eprintln!("-o needs to be last in combined args");
eprintln!();
return parsed_args;
}
if parsed_args.output.is_none() {
let Some(output) = args.next() else {
parsed_args.print_help = true;
eprintln!("output needs a file");
return parsed_args;
};
parsed_args.output = Some(PathBuf::from(output));
} else {
eprintln!("output can be passed only once");
eprintln!();
parsed_args.print_help = true;
return parsed_args;
}
}
'h' => parsed_args.print_help = true,
'v' => parsed_args.print_version = true,
a => {
eprintln!("unknown arg -{a}");
eprintln!();
parsed_args.print_help = true;
return parsed_args;
}
}
}
continue;
}
eprintln!("unknown arg {arg}");
eprintln!();
parsed_args.print_help = true;
return parsed_args;
}
parsed_args
}
fn print_help() {
println!("-- ShaderC shader compiler --");
println!();
println!("Arguments:");
println!("\t-i --input:");
println!("\t\tThe shader to be parsed and compiled, manadory argument");
println!();
println!("\t-o --output:");
println!("\t\tWhere to output the compiled spirv assembly to, default: out.spvas");
println!();
println!("\t-h --help:");
println!("\t\tPrint this and exit");
println!("\t-v --version:");
println!("\t\tPrint the compilers version");
println!();
}
fn print_version() {
println!(
"ShaderC version: {}; SPIR-V version: {}",
env!("CARGO_PKG_VERSION"),
SPIRV_VERSION
)
println!("{}", out);
}

@ -148,13 +148,6 @@ impl Ast {
}
}
pub fn list(self) -> Option<Vec<Ast>> {
match self {
Ast::List(Localised { item, .. }) => Some(item),
_ => None,
}
}
pub fn location(&self) -> &Location {
match self {
Ast::Symbol(Localised { location, .. }) => location,

@ -44,7 +44,7 @@ fn test_parse() {
(dec out-color:*v4f32o Output)
(entry main Fragment OriginUpperLeft (:frag-coord :out-color))
(fun (main) :<>
(store-ptr out-color
(store-ptr (out-color)
(v4f32i (/ (.xy (load-ptr frag-coord))
(v2f32 1920.0 1080.0))
1.0
@ -111,7 +111,9 @@ fn test_parse() {
Ast::Symbol(Localised::dummy_location(":<>".to_string())),
Ast::List(Localised::dummy_location(vec![
Ast::Symbol(Localised::dummy_location("store-ptr".to_string())),
Ast::Symbol(Localised::dummy_location("out-color".to_string())),
Ast::List(Localised::dummy_location(vec![Ast::Symbol(
Localised::dummy_location("out-color".to_string()),
)])),
Ast::List(Localised::dummy_location(vec![
Ast::Symbol(Localised::dummy_location("v4f32i".to_string())),
Ast::List(Localised::dummy_location(vec![

Loading…
Cancel
Save