Skip to content

Commit 5dcb068

Browse files
committed
Add assembler and example
1 parent 342f3de commit 5dcb068

File tree

8 files changed

+236
-0
lines changed

8 files changed

+236
-0
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

spirv-tools-sys/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ build = "build.rs"
77
license = "MIT OR Apache-2.0"
88

99
[features]
10+
as = ["tools"]
1011
opt = ["val"]
1112
val = ["tools"]
1213
tools = []

spirv-tools-sys/src/assembler.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use crate::shared;
2+
3+
#[repr(u32)] // SPV_FORCE_32_BIT_ENUM
4+
pub enum BinaryOptions {
5+
None = 0x1,
6+
PreserveNumberIds = 1 << 1,
7+
}
8+
9+
#[repr(C)]
10+
pub struct Binary {
11+
pub code: *const u32,
12+
pub size: usize,
13+
}
14+
15+
extern "C" {
16+
/// Encodes the given SPIR-V assembly text to its binary representation. The
17+
/// length parameter specifies the number of bytes for text. Encoded binary will
18+
/// be stored into *binary. Any error will be written into *diagnostic if
19+
/// diagnostic is non-null, otherwise the context's message consumer will be
20+
/// used. The generated binary is independent of the context and may outlive it.
21+
/// The SPIR-V binary version is set to the highest version of SPIR-V supported
22+
/// by the context's target environment.
23+
///
24+
/// The options parameter is a bit field of
25+
/// spv_text_to_binary_options_t.
26+
#[link_name = "spvTextToBinaryWithOptions"]
27+
pub fn assemble(
28+
tool: *const shared::ToolContext,
29+
text: *const std::os::raw::c_char,
30+
size: usize,
31+
options: u32,
32+
binary: *mut *mut Binary,
33+
diagnostic: *mut *mut shared::Diagnostic,
34+
) -> crate::shared::SpirvResult;
35+
36+
/// Frees a binary stream from memory. This is a no-op if binary is a null
37+
/// pointer.
38+
#[link_name = "spvBinaryDestroy"]
39+
pub fn binary_destroy(binary: *mut Binary);
40+
}

spirv-tools-sys/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ pub mod opt;
55

66
#[cfg(feature = "val")]
77
pub mod val;
8+
9+
#[cfg(feature = "as")]
10+
pub mod assembler;

spirv-tools/Cargo.toml

+8
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,16 @@ edition = "2018"
66
license = "MIT OR Apache-2.0"
77

88
[features]
9+
as = ["spirv-tools-sys/as"]
910
opt = ["spirv-tools-sys/opt"]
1011
val = ["spirv-tools-sys/val"]
1112

1213
[dependencies]
1314
spirv-tools-sys = { path = "../spirv-tools-sys" }
15+
16+
[dev-dependencies]
17+
structopt = "0.3"
18+
19+
[[example]]
20+
name = "as"
21+
required-features = ["as"]

spirv-tools/examples/as.rs

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use structopt::StructOpt;
2+
3+
/// Create a SPIR-V binary module from SPIR-V assembly text
4+
#[derive(StructOpt)]
5+
struct Args {
6+
/// Set the output filename. Use '-' for stdout.
7+
#[structopt(short, default_value = "out.spv")]
8+
output: String,
9+
/// Numeric IDs in the binary will have the same values as in the
10+
/// source. Non-numeric IDs are allocated by filling in the gaps,
11+
/// starting with 1 and going up.
12+
#[structopt(long = "preserve-numeric-ids")]
13+
preserve_ids: bool,
14+
/// Use specified environment.
15+
#[structopt(long = "target-env", parse(try_from_str))]
16+
target_env: Option<spirv_tools::shared::TargetEnv>,
17+
/// The input file. Use '-' for stdin.
18+
#[structopt(name = "FILE")]
19+
input: String,
20+
}
21+
22+
fn main() {
23+
use spirv_tools::assembler;
24+
25+
let args = Args::from_args();
26+
27+
let contents = if args.input == "-" {
28+
use std::io::Read;
29+
let mut v = Vec::with_capacity(1024);
30+
std::io::stdin()
31+
.read_to_end(&mut v)
32+
.expect("failed to read stdin");
33+
String::from_utf8(v).expect("stdin had invalid utf-8")
34+
} else {
35+
std::fs::read_to_string(&args.input).expect("failed to read input file")
36+
};
37+
38+
let assembler_opts = assembler::AssemblerOptions {
39+
preserve_numeric_ids: args.preserve_ids,
40+
};
41+
42+
let assembler = assembler::Assembler::new(args.target_env.unwrap_or_default());
43+
44+
match assembler.assemble(&contents, assembler_opts) {
45+
Ok(binary) => {
46+
if args.output == "-" {
47+
use std::io::Write;
48+
std::io::stdout()
49+
.lock()
50+
.write(binary.as_ref())
51+
.expect("failed to write binary to stdout");
52+
} else {
53+
std::fs::write(args.output, &binary).expect("failed to write binary");
54+
}
55+
}
56+
Err(e) => {
57+
eprintln!("{}", e);
58+
std::process::exit(1);
59+
}
60+
}
61+
}

spirv-tools/src/assembler.rs

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use spirv_tools_sys::{assembler, shared};
2+
3+
pub struct Binary {
4+
inner: *mut assembler::Binary,
5+
}
6+
7+
impl AsRef<[u32]> for Binary {
8+
fn as_ref(&self) -> &[u32] {
9+
unsafe { std::slice::from_raw_parts((*self.inner).code, (*self.inner).size) }
10+
}
11+
}
12+
13+
impl AsRef<[u8]> for Binary {
14+
fn as_ref(&self) -> &[u8] {
15+
unsafe {
16+
std::slice::from_raw_parts(
17+
(*self.inner).code as *const u8,
18+
(*self.inner).size * std::mem::size_of::<u32>(),
19+
)
20+
}
21+
}
22+
}
23+
24+
impl Drop for Binary {
25+
fn drop(&mut self) {
26+
unsafe {
27+
assembler::binary_destroy(self.inner);
28+
}
29+
}
30+
}
31+
32+
#[derive(Copy, Clone, Default)]
33+
pub struct AssemblerOptions {
34+
/// Numeric IDs in the binary will have the same values as in the source.
35+
/// Non-numeric IDs are allocated by filling in the gaps, starting with 1
36+
/// and going up.
37+
pub preserve_numeric_ids: bool,
38+
}
39+
40+
impl Into<u32> for AssemblerOptions {
41+
fn into(self) -> u32 {
42+
// This is weird, the "none" is 1, so I'm not sure if that means having
43+
// it disables all other options or...?
44+
let mut res = 0; //assembler::BinaryOptions::None as u32;
45+
46+
if self.preserve_numeric_ids {
47+
res |= assembler::BinaryOptions::PreserveNumberIds as u32;
48+
}
49+
50+
res
51+
}
52+
}
53+
54+
pub struct Assembler {
55+
inner: *mut shared::ToolContext,
56+
}
57+
58+
impl Assembler {
59+
pub fn new(target_env: shared::TargetEnv) -> Self {
60+
Self {
61+
inner: unsafe { shared::context_create(target_env) },
62+
}
63+
}
64+
65+
pub fn assemble(
66+
&self,
67+
text: &str,
68+
options: AssemblerOptions,
69+
) -> Result<Binary, crate::error::Error> {
70+
unsafe {
71+
let mut binary = std::ptr::null_mut();
72+
let mut diagnostic = std::ptr::null_mut();
73+
74+
let res = assembler::assemble(
75+
self.inner,
76+
text.as_ptr() as *const _,
77+
text.len(),
78+
options.into(),
79+
&mut binary,
80+
&mut diagnostic,
81+
);
82+
83+
// Always wrap diagnostic, it's fine if it's null
84+
use std::convert::TryFrom;
85+
let diagnostic = crate::error::Diagnostic::try_from(diagnostic).ok();
86+
87+
match res {
88+
shared::SpirvResult::Success => {
89+
if binary.is_null() {
90+
return Err(crate::error::Error {
91+
inner: shared::SpirvResult::InternalError,
92+
diagnostic: Some(crate::error::Diagnostic {
93+
line: 0,
94+
column: 0,
95+
index: 0,
96+
message: "spirv assemble indicated success but did not return a valid binary".to_owned(),
97+
is_text: true,
98+
}),
99+
});
100+
}
101+
102+
Ok(Binary { inner: binary })
103+
}
104+
other => Err(crate::error::Error {
105+
inner: other,
106+
diagnostic,
107+
}),
108+
}
109+
}
110+
}
111+
}
112+
113+
impl Drop for Assembler {
114+
fn drop(&mut self) {
115+
unsafe {
116+
shared::context_destroy(self.inner);
117+
}
118+
}
119+
}

spirv-tools/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ pub mod opt;
33
#[cfg(feature = "val")]
44
pub mod val;
55

6+
#[cfg(feature = "as")]
7+
pub mod assembler;
8+
69
pub use spirv_tools_sys::shared;
710

811
pub mod error;

0 commit comments

Comments
 (0)