Skip to content

Commit 9d18197

Browse files
committed
Start splitting spirv-tools into a compiled vs tool feature set
1 parent 0fc3d34 commit 9d18197

22 files changed

+541
-423
lines changed

rustc_codegen_spirv/Cargo.toml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ repository = "https://github.com/EmbarkStudios/rust-gpu"
1212
[lib]
1313
crate-type = ["dylib"]
1414

15+
[features]
16+
# If enabled, uses spirv-tools binaries installed in PATH, instead of
17+
# compiling and linking the spirv-tools C++ code
18+
use-installed-tools = ["spirv-tools/use-installed"]
19+
1520
[dependencies]
1621
bimap = "0.5"
1722
rspirv = "0.7.0"
@@ -22,13 +27,7 @@ topological-sort = "0.1"
2227
[dependencies.spirv-tools]
2328
version = "0.1.0"
2429
path = "../spirv-tools"
25-
features = ["opt", "val"]
2630

2731
[dev-dependencies]
2832
pretty_assertions = "0.6"
2933
tempfile = "3.1"
30-
31-
[dev-dependencies.spirv-tools]
32-
version = "0.1.0"
33-
path = "../spirv-tools"
34-
features = ["as"]

rustc_codegen_spirv/src/link.rs

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -155,41 +155,41 @@ fn link_exe(
155155
}
156156

157157
fn do_spirv_opt(sess: &Session, spv_binary: Vec<u32>, filename: &Path) -> Vec<u32> {
158-
use spirv_tools::{opt, shared};
158+
use spirv_tools::{error, opt};
159159

160-
// This is the same default target environment that spirv-opt uses
161-
let mut optimizer = opt::Optimizer::new(shared::TargetEnv::Universal_1_5);
160+
let mut optimizer = opt::Optimizer::new(spirv_tools::TargetEnv::default());
162161

163162
optimizer
164163
.register_size_passes()
165164
.register_pass(opt::Passes::EliminateDeadConstant)
166165
.register_pass(opt::Passes::StripDebugInfo);
167166

168-
let mut opts = opt::Options::new();
167+
let result = optimizer.optimize(
168+
&spv_binary,
169+
&mut |msg: error::Message<'_>| {
170+
use error::MessageLevel as Level;
169171

170-
// We run the validator separately
171-
opts.run_validator(false);
172+
// TODO: Adds spans here? Not sure how useful with binary, but maybe?
172173

173-
let mut optimized_binary = None;
174+
let mut err = match msg.level {
175+
Level::Fatal | Level::InternalError => sess.struct_fatal(&msg.message),
176+
Level::Error => sess.struct_err(&msg.message),
177+
Level::Warning => sess.struct_warn(&msg.message),
178+
Level::Info | Level::Debug => sess.struct_note_without_error(&msg.message),
179+
};
174180

175-
let result = optimizer.optimize(
176-
&spv_binary,
177-
&mut |data: &[u32]| {
178-
optimized_binary = Some(data.to_vec());
181+
err.note(&format!("module {:?}", filename));
182+
err.emit();
179183
},
180-
Some(&opts),
184+
// We currently run the validator separately after optimization or even
185+
// if we don't run optimization, the default options don't run the validator
186+
None,
181187
);
182188

183189
match result {
184-
Err(opt::RunResult::OptimizerFailed) => {
185-
let mut err = sess.struct_warn("spirv-opt failed, leaving as unoptimized");
186-
err.note(&format!("module {:?}", filename));
187-
spv_binary
188-
}
189-
Ok(_) => optimized_binary.unwrap(),
190+
Ok(binary) => binary.as_ref().to_vec(),
190191
Err(_) => {
191-
let mut err =
192-
sess.struct_warn("invalid arguments supplied to spirv-opt, leaving as unoptimized");
192+
let mut err = sess.struct_warn("spirv-opt failed, leaving as unoptimized");
193193
err.note(&format!("module {:?}", filename));
194194
spv_binary
195195
}
@@ -199,8 +199,7 @@ fn do_spirv_opt(sess: &Session, spv_binary: Vec<u32>, filename: &Path) -> Vec<u3
199199
fn do_spirv_val(sess: &Session, spv_binary: &[u32], filename: &Path) {
200200
use spirv_tools::{shared, val};
201201

202-
// This is the same default target environment that spirv-val uses
203-
let validator = val::Validator::new(shared::TargetEnv::Universal_1_5);
202+
let validator = val::Validator::new(spirv_tools::TargetEnv::default());
204203
let opts = val::ValidatorOptions::default();
205204

206205
if validator.validate(spv_binary, &opts).is_err() {

spirv-tools-sys/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ build = "build.rs"
77
license = "MIT OR Apache-2.0"
88

99
[features]
10-
as = ["tools"]
11-
opt = ["val"]
12-
val = ["tools"]
13-
tools = []
10+
# Using this feature disables the compilation in the build script, but
11+
# preserves the types so that spirv-tools can still work without needing
12+
# to keep copies of some of the basic enums etc
13+
use-installed = []
1414

1515
[build-dependencies]
1616
cc = { version = "1.0", features = ["parallel"] }

spirv-tools-sys/build.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ fn val(build: &mut Build) {
219219
}
220220

221221
fn main() {
222+
if std::env::var("CARGO_FEATURE_USE_INSTALLED").is_ok() {
223+
println!("use-installed feature on, skipping compilation of C++ code");
224+
return;
225+
}
226+
222227
let mut build = Build::new();
223228

224229
add_includes(&mut build, "spirv-tools", &["", "include"]);
@@ -230,15 +235,8 @@ fn main() {
230235
);
231236

232237
shared(&mut build);
233-
234-
// Some opt code requires val as well
235-
if cfg!(any(feature = "opt", feature = "val")) {
236-
val(&mut build);
237-
}
238-
239-
if cfg!(feature = "opt") {
240-
opt(&mut build);
241-
}
238+
val(&mut build);
239+
opt(&mut build);
242240

243241
build.define("SPIRV_CHECK_CONTEXT", None);
244242

spirv-tools-sys/src/assembler.rs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,6 @@ pub enum BinaryOptions {
66
PreserveNumberIds = 1 << 1,
77
}
88

9-
#[repr(C)]
10-
pub struct Binary {
11-
pub code: *const u32,
12-
pub size: usize,
13-
}
14-
159
extern "C" {
1610
/// Encodes the given SPIR-V assembly text to its binary representation. The
1711
/// length parameter specifies the number of bytes for text. Encoded binary will
@@ -29,12 +23,7 @@ extern "C" {
2923
text: *const std::os::raw::c_char,
3024
size: usize,
3125
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);
26+
binary: *mut *mut shared::Binary,
27+
diagnostic: *mut *mut crate::diagnostics::Diagnostic,
28+
) -> shared::SpirvResult;
4029
}

spirv-tools-sys/src/c/opt.cpp

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "spirv-tools/optimizer.hpp"
2+
#include <cstring>
23

34
struct Optimus;
45

@@ -64,17 +65,13 @@ enum Passes {
6465
AmdExtToKhr,
6566
};
6667

67-
enum RunResult {
68-
InvalidInputBuffer,
69-
InvalidInputSize,
70-
InvalidOutputBuffer,
71-
InvalidOutputSize,
72-
InvalidCallback,
73-
OptimizerFailed,
74-
OptimizerSucceeded,
75-
};
76-
77-
typedef void (*optimized_callback)(const uint32_t* data, size_t len, void* ctx);
68+
typedef void (*message_callback)(
69+
spv_message_level_t level,
70+
const char* source,
71+
const spv_position_t* position,
72+
const char* message,
73+
void* ctx
74+
);
7875

7976
extern "C" {
8077
SPIRV_TOOLS_EXPORT Optimus* optimizer_create(spv_target_env target_env) {
@@ -87,25 +84,47 @@ extern "C" {
8784
delete (spvtools::Optimizer*)optimizer;
8885
}
8986

90-
SPIRV_TOOLS_EXPORT RunResult optimizer_run(
87+
SPIRV_TOOLS_EXPORT spv_result_t optimizer_run(
9188
const Optimus* optimizer,
9289
const uint32_t* input_ptr,
9390
size_t input_size,
94-
optimized_callback callback,
91+
spv_binary* out_binary,
92+
message_callback msg_callback,
9593
void* ctx,
9694
const spv_optimizer_options options
9795
) {
9896
if (input_ptr == nullptr) {
99-
return RunResult::InvalidInputBuffer;
97+
return SPV_ERROR_INVALID_POINTER;
10098
}
10199

102-
if (callback == nullptr) {
103-
return RunResult::InvalidCallback;
100+
if (out_binary == nullptr) {
101+
return SPV_ERROR_INVALID_POINTER;
104102
}
105103

106-
auto output_buff = std::vector<uint32_t>();
107-
108104
auto op = (spvtools::Optimizer*)optimizer;
105+
106+
if (msg_callback) {
107+
op->SetMessageConsumer([msg_callback, ctx](
108+
spv_message_level_t level,
109+
const char* source,
110+
const spv_position_t& position,
111+
const char* message) {
112+
msg_callback(level, source, &position, message, ctx);
113+
});
114+
} else {
115+
// The optimizer keeps the message consumer as state, so if no
116+
// callback is passed to us, we insert a noop callback to ensure
117+
// we don't use the state from a previous optimizer run
118+
op->SetMessageConsumer([](
119+
spv_message_level_t,
120+
const char*,
121+
const spv_position_t&,
122+
const char*)
123+
{}
124+
);
125+
}
126+
127+
auto output_buff = std::vector<uint32_t>();
109128
bool success = false;
110129
if (options == nullptr) {
111130
success = op->Run(input_ptr, input_size, &output_buff);
@@ -114,12 +133,24 @@ extern "C" {
114133
}
115134

116135
if (!success) {
117-
return RunResult::OptimizerFailed;
136+
return SPV_ERROR_INTERNAL;
137+
}
138+
139+
uint32_t* data = new uint32_t[output_buff.size()];
140+
if (data == nullptr) {
141+
return SPV_ERROR_OUT_OF_MEMORY;
142+
}
143+
144+
spv_binary binary = new spv_binary_t();
145+
if (binary == nullptr) {
146+
delete[] data;
147+
return SPV_ERROR_OUT_OF_MEMORY;
118148
}
119149

120-
callback(output_buff.data(), output_buff.size(), ctx);
150+
memcpy(data, output_buff.data(), output_buff.size());
151+
*out_binary = binary;
121152

122-
return RunResult::OptimizerSucceeded;
153+
return SPV_SUCCESS;
123154
}
124155

125156
SPIRV_TOOLS_EXPORT void optimizer_register_pass(Optimus* optimizer, Passes pass) {

spirv-tools-sys/src/diagnostics.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#[repr(C)]
2+
pub struct Position {
3+
pub line: usize,
4+
pub column: usize,
5+
pub index: usize,
6+
}
7+
8+
#[repr(C)]
9+
pub struct Diagnostic {
10+
pub position: Position,
11+
pub error: *const std::os::raw::c_char,
12+
pub is_text_source: bool,
13+
}
14+
15+
#[derive(Copy, Clone, PartialEq, Debug)]
16+
#[repr(C)]
17+
pub enum MessageLevel {
18+
/// Unrecoverable error due to environment.
19+
/// Will exit the program immediately. E.g.,
20+
/// out of memory.
21+
Fatal,
22+
/// Unrecoverable error due to SPIRV-Tools
23+
/// internals.
24+
/// Will exit the program immediately. E.g.,
25+
/// unimplemented feature.
26+
InternalError,
27+
/// Normal error due to user input.
28+
Error,
29+
/// Warning information.
30+
Warning,
31+
/// General information.
32+
Info,
33+
/// Debug information.
34+
Debug,
35+
}
36+
37+
pub type MessageCallback = extern "C" fn(
38+
MessageLevel, // level
39+
*const std::os::raw::c_char, // source
40+
*const Position, // source position
41+
*const std::os::raw::c_char, // the actual message
42+
*mut std::ffi::c_void, // context we use for mapping
43+
);
44+
45+
extern "C" {
46+
/// Destroys a diagnostic object. This is a no-op if diagnostic is a null
47+
/// pointer.
48+
#[link_name = "spvDiagnosticDestroy"]
49+
pub fn diagnostic_destroy(diag: *mut Diagnostic);
50+
}

spirv-tools-sys/src/lib.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
pub mod shared;
2-
3-
#[cfg(feature = "opt")]
1+
pub mod assembler;
2+
pub mod diagnostics;
43
pub mod opt;
5-
6-
#[cfg(feature = "val")]
4+
pub mod shared;
75
pub mod val;
8-
9-
#[cfg(feature = "as")]
10-
pub mod assembler;

spirv-tools-sys/src/opt.rs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -499,18 +499,6 @@ pub enum Passes {
499499
AmdExtToKhr,
500500
}
501501

502-
#[derive(Copy, Clone, Debug, PartialEq)]
503-
#[repr(C)]
504-
pub enum RunResult {
505-
InvalidInputBuffer,
506-
InvalidInputSize,
507-
InvalidOutputBuffer,
508-
InvalidOutputSize,
509-
InvalidCallback,
510-
OptimizerFailed,
511-
OptimizerSucceeded,
512-
}
513-
514502
extern "C" {
515503
pub fn optimizer_create(env: crate::shared::TargetEnv) -> *mut Optimizer;
516504
pub fn optimizer_destroy(opt: *mut Optimizer);
@@ -519,10 +507,11 @@ extern "C" {
519507
opt: *const Optimizer,
520508
input_ptr: *const u32,
521509
input_size: usize,
522-
callback: extern "C" fn(*const u32, usize, *mut std::ffi::c_void),
523-
ctx: *mut std::ffi::c_void,
510+
binary: *mut *mut crate::shared::Binary,
511+
message_callback: crate::diagnostics::MessageCallback,
512+
message_ctx: *mut std::ffi::c_void,
524513
options: *const OptimizerOptions,
525-
) -> RunResult;
514+
) -> crate::shared::SpirvResult;
526515

527516
/// Creates an optimizer options object with default options. Returns a valid
528517
/// options object. The object remains valid until it is passed into

0 commit comments

Comments
 (0)