Add spirv-val flags to spirv-builder (#635)

This commit is contained in:
Ashley Hauck 2021-05-31 11:44:09 +02:00 committed by GitHub
parent 5d22f600e7
commit 3fa15c99ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 171 additions and 16 deletions

View File

@ -277,6 +277,14 @@ pub struct CodegenArgs {
pub disassemble_fn: Option<String>,
pub disassemble_entry: Option<String>,
pub disassemble_globals: bool,
// spirv-val flags
pub relax_struct_store: bool,
pub relax_logical_pointer: bool,
pub relax_block_layout: Option<bool>,
pub uniform_buffer_standard_layout: bool,
pub scalar_block_layout: bool,
pub skip_block_layout: bool,
}
impl CodegenArgs {
@ -305,6 +313,14 @@ impl CodegenArgs {
"NAME",
);
opts.optflagopt("", "disassemble-globals", "print globals to stderr", "");
opts.optflagopt("", "relax-struct-store", "Allow store from one struct type to a different type with compatible layout and members.", "");
opts.optflagopt("", "relax-logical-pointer", "Allow allocating an object of a pointer type and returning a pointer value from a function in logical addressing mode", "");
opts.optflagopt("", "relax-block-layout", "Enable VK_KHR_relaxed_block_layout when checking standard uniform, storage buffer, and push constant layouts. This is the default when targeting Vulkan 1.1 or later.", "");
opts.optflagopt("", "uniform-buffer-standard-layout", "Enable VK_KHR_uniform_buffer_standard_layout when checking standard uniform buffer layouts.", "");
opts.optflagopt("", "scalar-block-layout", "Enable VK_EXT_scalar_block_layout when checking standard uniform, storage buffer, and push constant layouts. Scalar layout rules are more permissive than relaxed block layout so in effect this will override the --relax-block-layout option.", "");
opts.optflagopt("", "skip-block-layout", "Skip checking standard uniform/storage buffer layout. Overrides any --relax-block-layout or --scalar-block-layout option.", "");
let matches = opts.parse(args)?;
let module_output_type =
matches.opt_get_default("module-output", ModuleOutputType::Single)?;
@ -312,12 +328,29 @@ impl CodegenArgs {
let disassemble_fn = matches.opt_str("disassemble-fn");
let disassemble_entry = matches.opt_str("disassemble-entry");
let disassemble_globals = matches.opt_present("disassemble-globals");
let relax_struct_store = matches.opt_present("relax-struct-store");
let relax_logical_pointer = matches.opt_present("relax-logical-pointer");
let relax_block_layout = matches.opt_present("relax-block-layout");
let uniform_buffer_standard_layout = matches.opt_present("uniform-buffer-standard-layout");
let scalar_block_layout = matches.opt_present("scalar-block-layout");
let skip_block_layout = matches.opt_present("skip-block-layout");
let relax_block_layout = if relax_block_layout { Some(true) } else { None };
Ok(Self {
module_output_type,
disassemble,
disassemble_fn,
disassemble_entry,
disassemble_globals,
relax_struct_store,
relax_logical_pointer,
relax_block_layout,
uniform_buffer_standard_layout,
scalar_block_layout,
skip_block_layout,
})
}

View File

@ -153,7 +153,7 @@ fn link_exe(
linker::LinkResult::SingleModule(spv_binary) => {
let mut module_filename = out_dir;
module_filename.push("module");
post_link_single_module(sess, spv_binary.assemble(), &module_filename);
post_link_single_module(sess, &cg_args, spv_binary.assemble(), &module_filename);
cg_args.do_disassemble(&spv_binary);
let module_result = ModuleResult::SingleModule(module_filename);
CompileResult {
@ -167,7 +167,7 @@ fn link_exe(
for (name, spv_binary) in map {
let mut module_filename = out_dir.clone();
module_filename.push(sanitize_filename::sanitize(&name));
post_link_single_module(sess, spv_binary.assemble(), &module_filename);
post_link_single_module(sess, &cg_args, spv_binary.assemble(), &module_filename);
hashmap.insert(name, module_filename);
}
let module_result = ModuleResult::MultiModule(hashmap);
@ -191,7 +191,12 @@ fn entry_points(module: &rspirv::dr::Module) -> Vec<String> {
.collect()
}
fn post_link_single_module(sess: &Session, spv_binary: Vec<u32>, out_filename: &Path) {
fn post_link_single_module(
sess: &Session,
cg_args: &crate::codegen_cx::CodegenArgs,
spv_binary: Vec<u32>,
out_filename: &Path,
) {
if let Ok(ref path) = std::env::var("DUMP_POST_LINK") {
File::create(path)
.unwrap()
@ -199,16 +204,33 @@ fn post_link_single_module(sess: &Session, spv_binary: Vec<u32>, out_filename: &
.unwrap();
}
let val_options = spirv_tools::val::ValidatorOptions {
relax_struct_store: cg_args.relax_struct_store,
relax_logical_pointer: cg_args.relax_logical_pointer,
before_legalization: false,
relax_block_layout: cg_args.relax_block_layout,
uniform_buffer_standard_layout: cg_args.uniform_buffer_standard_layout,
scalar_block_layout: cg_args.scalar_block_layout,
skip_block_layout: cg_args.skip_block_layout,
max_limits: vec![],
};
let opt_options = spirv_tools::opt::Options {
validator_options: Some(val_options.clone()),
max_id_bound: None,
preserve_bindings: false,
preserve_spec_constants: false,
};
let spv_binary = if sess.opts.optimize != OptLevel::No || sess.opts.debuginfo == DebugInfo::None
{
let _timer = sess.timer("link_spirv_opt");
do_spirv_opt(sess, spv_binary, out_filename)
do_spirv_opt(sess, spv_binary, out_filename, opt_options)
} else {
spv_binary
};
if env::var("NO_SPIRV_VAL").is_err() {
do_spirv_val(sess, &spv_binary, out_filename);
do_spirv_val(sess, &spv_binary, out_filename, val_options);
}
{
@ -225,13 +247,18 @@ fn post_link_single_module(sess: &Session, spv_binary: Vec<u32>, out_filename: &
}
}
fn do_spirv_opt(sess: &Session, spv_binary: Vec<u32>, filename: &Path) -> Vec<u32> {
fn do_spirv_opt(
sess: &Session,
spv_binary: Vec<u32>,
filename: &Path,
options: spirv_tools::opt::Options,
) -> Vec<u32> {
use spirv_tools::{
error,
opt::{self, Optimizer},
};
let mut optimizer = opt::create(None);
let mut optimizer = opt::create(sess.target.options.env.parse().ok());
match sess.opts.optimize {
OptLevel::No => {}
@ -266,9 +293,7 @@ fn do_spirv_opt(sess: &Session, spv_binary: Vec<u32>, filename: &Path) -> Vec<u3
err.note(&format!("module `{}`", filename.display()));
err.emit();
},
// We currently run the validator separately after optimization or even
// if we don't run optimization, the default options don't run the validator
None,
Some(options),
);
match result {
@ -283,12 +308,17 @@ fn do_spirv_opt(sess: &Session, spv_binary: Vec<u32>, filename: &Path) -> Vec<u3
}
}
fn do_spirv_val(sess: &Session, spv_binary: &[u32], filename: &Path) {
fn do_spirv_val(
sess: &Session,
spv_binary: &[u32],
filename: &Path,
options: spirv_tools::val::ValidatorOptions,
) {
use spirv_tools::val::{self, Validator};
let validator = val::create(sess.target.options.env.parse().ok());
if let Err(e) = validator.validate(spv_binary, None) {
if let Err(e) = validator.validate(spv_binary, Some(options)) {
let mut err = sess.struct_err(&e.to_string());
err.note("spirv-val failed");
err.note(&format!("module `{}`", filename.display()));

View File

@ -61,6 +61,7 @@ use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::fmt;
use std::fmt::Write;
use std::fs::File;
use std::io::BufReader;
use std::path::{Path, PathBuf};
@ -115,6 +116,14 @@ pub struct SpirvBuilder {
multimodule: bool,
capabilities: Vec<Capability>,
extensions: Vec<String>,
// spirv-val flags
pub relax_struct_store: bool,
pub relax_logical_pointer: bool,
pub relax_block_layout: bool,
pub uniform_buffer_standard_layout: bool,
pub scalar_block_layout: bool,
pub skip_block_layout: bool,
}
impl SpirvBuilder {
@ -128,6 +137,13 @@ impl SpirvBuilder {
multimodule: false,
capabilities: Vec::new(),
extensions: Vec::new(),
relax_struct_store: false,
relax_logical_pointer: false,
relax_block_layout: false,
uniform_buffer_standard_layout: false,
scalar_block_layout: false,
skip_block_layout: false,
}
}
@ -172,6 +188,48 @@ impl SpirvBuilder {
self
}
/// Allow store from one struct type to a different type with compatible layout and members.
pub fn relax_struct_store(mut self, v: bool) -> Self {
self.relax_struct_store = v;
self
}
/// Allow allocating an object of a pointer type and returning a pointer value from a function
/// in logical addressing mode
pub fn relax_logical_pointer(mut self, v: bool) -> Self {
self.relax_logical_pointer = v;
self
}
/// Enable `VK_KHR_relaxed_block_layout` when checking standard uniform, storage buffer, and
/// push constant layouts. This is the default when targeting Vulkan 1.1 or later.
pub fn relax_block_layout(mut self, v: bool) -> Self {
self.relax_block_layout = v;
self
}
/// Enable `VK_KHR_uniform_buffer_standard_layout` when checking standard uniform buffer
/// layouts.
pub fn uniform_buffer_standard_layout(mut self, v: bool) -> Self {
self.uniform_buffer_standard_layout = v;
self
}
/// Enable `VK_EXT_scalar_block_layout` when checking standard uniform, storage buffer, and
/// push constant layouts. Scalar layout rules are more permissive than relaxed block layout so
/// in effect this will override the --relax-block-layout option.
pub fn scalar_block_layout(mut self, v: bool) -> Self {
self.scalar_block_layout = v;
self
}
/// Skip checking standard uniform/storage buffer layout. Overrides any --relax-block-layout or
/// --scalar-block-layout option.
pub fn skip_block_layout(mut self, v: bool) -> Self {
self.skip_block_layout = v;
self
}
/// Builds the module. If `print_metadata` is true, you usually don't have to inspect the path
/// in the result, as the environment variable for the path to the module will already be set.
pub fn build(self) -> Result<CompileResult, SpirvBuilderError> {
@ -245,10 +303,44 @@ fn invoke_rustc(builder: &SpirvBuilder) -> Result<PathBuf, SpirvBuilderError> {
// rustc expects a full path, instead of a filename looked up via LD_LIBRARY_PATH, so we need
// to copy cargo's understanding of library lookup and find the library and its full path.
let rustc_codegen_spirv = find_rustc_codegen_spirv();
let llvm_args = builder
.multimodule
.then(|| " -C llvm-args=--module-output=multiple")
.unwrap_or_default();
let mut llvm_args = Vec::new();
if builder.multimodule {
llvm_args.push("--module-output=multiple");
}
if builder.relax_struct_store {
llvm_args.push("--relax-struct-store");
}
if builder.relax_logical_pointer {
llvm_args.push("--relax-logical-pointer");
}
if builder.relax_block_layout {
llvm_args.push("--relax-block-layout");
}
if builder.uniform_buffer_standard_layout {
llvm_args.push("--uniform-buffer-standard-layout");
}
if builder.scalar_block_layout {
llvm_args.push("--scalar-block-layout");
}
if builder.skip_block_layout {
llvm_args.push("--skip-block-layout");
}
let llvm_args = if llvm_args.is_empty() {
String::new()
} else {
// Cargo's handling of RUSTFLAGS is a little cursed. -Cllvm-args is documented as "The list
// must be separated by spaces", but if we set RUSTFLAGS='-C llvm-args="--foo --bar"', then
// cargo will pass -C 'llvm-args="--foo' '--bar"' to rustc. Like, really? c'mon.
// Thankfully, passing -C llvm-args multiple times appends to a list, instead of
// overwriting.
let mut result = String::new();
for arg in llvm_args {
write!(result, " -C llvm-args={}", arg).unwrap();
}
result
};
let mut target_features = Vec::new();