Implement basic SPIR-V back-end (#46)

This commit is contained in:
Timo de Kort
2020-06-15 22:01:00 +02:00
committed by GitHub
parent f9fa281da5
commit 82573b288b
10 changed files with 1168 additions and 5 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
**/*.rs.bk
Cargo.lock
.DS_Store
.idea

View File

@@ -18,7 +18,7 @@ Rust | | |
Back-end | Status | Notes |
--------------- | ------------------ | ----- |
SPIR-V (binary) | | |
SPIR-V (binary) | :construction: | |
WGSL | | |
Metal | :construction: | |
HLSL | | |

View File

@@ -30,7 +30,7 @@ fn main() {
return;
} else if args[1].ends_with(".spv") {
let input = fs::read(&args[1]).unwrap();
naga::front::spirv::parse_u8_slice(&input).unwrap()
naga::front::spv::parse_u8_slice(&input).unwrap()
} else if args[1].ends_with(".wgsl") {
let input = fs::read_to_string(&args[1]).unwrap();
naga::front::wgsl::parse_str(&input).unwrap()
@@ -71,6 +71,27 @@ fn main() {
};
let msl = msl::write_string(&module, options).unwrap();
fs::write(&args[2], msl).unwrap();
} else if args[2].ends_with(".spv") {
use naga::back::spv;
let debug_flag = args.get(3).map_or(spv::WriterFlags::DEBUG, |arg| {
if arg.parse().unwrap() {
spv::WriterFlags::DEBUG
} else {
spv::WriterFlags::NONE
}
});
let spv = spv::writer::Writer::new(&module.header, debug_flag).write(&module);
let bytes = spv
.iter()
.fold(Vec::with_capacity(spv.len() * 4), |mut v, w| {
v.extend_from_slice(&w.to_le_bytes());
v
});
fs::write(&args[2], bytes.as_slice()).unwrap();
} else {
panic!("Unknown output: {:?}", args[2]);
}

View File

@@ -2,7 +2,7 @@ use std::{fmt, hash, marker::PhantomData, num::NonZeroU32};
/// An unique index in the arena array that a handle points to.
///
/// This type is independent of `spirv::Word`. `spirv::Word` is used in data
/// This type is independent of `spv::Word`. `spv::Word` is used in data
/// representation. It holds a SPIR-V and refers to that instruction. In
/// structured representation, we use Handle to refer to an SPIR-V instruction.
/// `Index` is an implementation detail to `Handle`.

View File

@@ -1 +1,2 @@
pub mod msl;
pub mod spv;

85
src/back/spv/layout.rs Normal file
View File

@@ -0,0 +1,85 @@
use crate::back::spv::{Instruction, LogicalLayout, PhysicalLayout};
use spirv::*;
use std::iter;
impl PhysicalLayout {
pub(crate) fn new(header: &crate::Header) -> Self {
let version: Word = ((header.version.0 as u32) << 16)
| ((header.version.1 as u32) << 8)
| header.version.2 as u32;
PhysicalLayout {
magic_number: MAGIC_NUMBER,
version,
generator: header.generator,
bound: 0,
instruction_schema: 0x0u32,
}
}
pub(crate) fn in_words(&self, sink: &mut impl Extend<Word>) {
sink.extend(iter::once(self.magic_number));
sink.extend(iter::once(self.version));
sink.extend(iter::once(self.generator));
sink.extend(iter::once(self.bound));
sink.extend(iter::once(self.instruction_schema));
}
}
impl LogicalLayout {
pub(crate) fn in_words(&self, sink: &mut impl Extend<Word>) {
sink.extend(self.capabilities.iter().cloned());
sink.extend(self.extensions.iter().cloned());
sink.extend(self.ext_inst_imports.iter().cloned());
sink.extend(self.memory_model.iter().cloned());
sink.extend(self.entry_points.iter().cloned());
sink.extend(self.execution_modes.iter().cloned());
sink.extend(self.debugs.iter().cloned());
sink.extend(self.annotations.iter().cloned());
sink.extend(self.declarations.iter().cloned());
sink.extend(self.function_declarations.iter().cloned());
sink.extend(self.function_definitions.iter().cloned());
}
}
impl Instruction {
pub(crate) fn new(op: Op) -> Self {
Instruction {
op,
wc: 1, // Always start at 1 for the first word (OP + WC),
type_id: None,
result_id: None,
operands: vec![],
}
}
pub(crate) fn set_type(&mut self, id: Word) {
assert!(self.type_id.is_none(), "Type can only be set once");
self.type_id = Some(id);
self.wc += 1;
}
pub(crate) fn set_result(&mut self, id: Word) {
assert!(self.result_id.is_none(), "Result can only be set once");
self.result_id = Some(id);
self.wc += 1;
}
pub(crate) fn add_operand(&mut self, operand: Word) {
self.operands.push(operand);
self.wc += 1;
}
pub(crate) fn add_operands(&mut self, operands: Vec<Word>) {
for operand in operands.into_iter() {
self.add_operand(operand)
}
}
pub(crate) fn to_words(&self, sink: &mut impl Extend<Word>) {
sink.extend(Some((self.wc << 16 | self.op as u32) as u32));
sink.extend(self.type_id);
sink.extend(self.result_id);
sink.extend(self.operands.iter().cloned());
}
}

42
src/back/spv/mod.rs Normal file
View File

@@ -0,0 +1,42 @@
mod layout;
pub mod writer;
use spirv::*;
bitflags::bitflags! {
pub struct WriterFlags: u32 {
const NONE = 0x0;
const DEBUG = 0x1;
}
}
struct PhysicalLayout {
magic_number: Word,
version: Word,
generator: Word,
bound: Word,
instruction_schema: Word,
}
#[derive(Default)]
pub(crate) struct LogicalLayout {
capabilities: Vec<Word>,
extensions: Vec<Word>,
ext_inst_imports: Vec<Word>,
memory_model: Vec<Word>,
entry_points: Vec<Word>,
execution_modes: Vec<Word>,
debugs: Vec<Word>,
annotations: Vec<Word>,
declarations: Vec<Word>,
function_declarations: Vec<Word>,
function_definitions: Vec<Word>,
}
struct Instruction {
op: Op,
wc: u32,
type_id: Option<Word>,
result_id: Option<Word>,
operands: Vec<Word>,
}

1013
src/back/spv/writer.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
#[cfg(feature = "glsl")]
pub mod glsl;
pub mod spirv;
pub mod spv;
pub mod wgsl;
use crate::arena::Arena;

View File

@@ -4,7 +4,7 @@
Our IR links to everything with `Handle`, while SPIR-V uses IDs.
In order to keep track of the associations, the parser has many lookup tables.
There map `spirv::Word` into a specific IR handle, plus potentially a bit of
There map `spv::Word` into a specific IR handle, plus potentially a bit of
extra info, such as the related SPIR-V type ID.
TODO: would be nice to find ways that avoid looking up as much