Add new handling for SPIR-V targets (#559)

* Add new handling for SPIR-V targets

* Ensure target is always three components
This commit is contained in:
XAMPPRocky 2021-04-12 13:19:28 +02:00 committed by GitHub
parent 4824d205ba
commit b6311789bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 285 additions and 210 deletions

View File

@ -52,4 +52,4 @@ cargo_test_no_features examples/runners/cpu
cargo_test_no_features examples/shaders/sky-shader
cargo_test_no_features examples/shaders/simplest-shader
cargo compiletest --target-env unknown,vulkan1.1,spv1.3
cargo compiletest --target-env vulkan1.1,spv1.3

View File

@ -415,7 +415,7 @@ fn trans_scalar<'tcx>(
match scalar.value {
Primitive::Int(width, mut signedness) => {
if cx.kernel_mode {
if cx.target.is_kernel() {
signedness = false;
}
SpirvType::Integer(width.size().bits() as u32, signedness).def(span, cx)
@ -644,7 +644,7 @@ pub fn auto_struct_layout<'tcx>(
fn trans_struct<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) -> Word {
if let TyKind::Foreign(_) = ty.ty.kind() {
// "An unsized FFI type that is opaque to Rust", `extern type A;` (currently unstable)
if cx.kernel_mode {
if cx.target.is_kernel() {
// TODO: This should use the name of the struct as the name. However, names are not stable across crates,
// e.g. core::fmt::Opaque in one crate and fmt::Opaque in core.
return SpirvType::Opaque {

View File

@ -17,7 +17,7 @@ pub struct ExtInst {
impl ExtInst {
pub fn import_glsl<'a, 'tcx>(&mut self, bx: &Builder<'a, 'tcx>) -> Word {
assert!(!bx.kernel_mode);
assert!(!bx.target.is_kernel());
match self.glsl {
Some(id) => id,
None => {
@ -29,7 +29,7 @@ impl ExtInst {
}
pub fn import_opencl<'a, 'tcx>(&mut self, bx: &Builder<'a, 'tcx>) -> Word {
assert!(bx.kernel_mode);
assert!(bx.target.is_kernel());
match self.opencl {
Some(id) => id,
None => {
@ -42,7 +42,7 @@ impl ExtInst {
pub fn import_integer_functions_2_intel<'tcx>(&mut self, cx: &CodegenCx<'tcx>) {
if !self.integer_functions_2_intel {
assert!(!cx.kernel_mode);
assert!(!cx.target.is_kernel());
self.integer_functions_2_intel = true;
cx.emit_global()
.extension("SPV_INTEL_shader_integer_functions2");

View File

@ -154,14 +154,14 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
// TODO: Configure these to be ocl vs. gl ext instructions, etc.
sym::sqrtf32 | sym::sqrtf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::sqrt, ret_ty, [args[0].immediate()])
} else {
self.gl_op(GLOp::Sqrt, ret_ty, [args[0].immediate()])
}
}
sym::powif32 | sym::powif64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(
CLOp::pown,
ret_ty,
@ -173,21 +173,21 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
}
}
sym::sinf32 | sym::sinf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::sin, ret_ty, [args[0].immediate()])
} else {
self.gl_op(GLOp::Sin, ret_ty, [args[0].immediate()])
}
}
sym::cosf32 | sym::cosf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::cos, ret_ty, [args[0].immediate()])
} else {
self.gl_op(GLOp::Cos, ret_ty, [args[0].immediate()])
}
}
sym::powf32 | sym::powf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(
CLOp::pow,
ret_ty,
@ -202,35 +202,35 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
}
}
sym::expf32 | sym::expf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::exp, ret_ty, [args[0].immediate()])
} else {
self.gl_op(GLOp::Exp, ret_ty, [args[0].immediate()])
}
}
sym::exp2f32 | sym::exp2f64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::exp2, ret_ty, [args[0].immediate()])
} else {
self.gl_op(GLOp::Exp2, ret_ty, [args[0].immediate()])
}
}
sym::logf32 | sym::logf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::log, ret_ty, [args[0].immediate()])
} else {
self.gl_op(GLOp::Log, ret_ty, [args[0].immediate()])
}
}
sym::log2f32 | sym::log2f64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::log2, ret_ty, [args[0].immediate()])
} else {
self.gl_op(GLOp::Log2, ret_ty, [args[0].immediate()])
}
}
sym::log10f32 | sym::log10f64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::log10, ret_ty, [args[0].immediate()])
} else {
// spir-v glsl doesn't have log10, so,
@ -241,7 +241,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
}
}
sym::fmaf32 | sym::fmaf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(
CLOp::fma,
ret_ty,
@ -264,14 +264,14 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
}
}
sym::fabsf32 | sym::fabsf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::fabs, ret_ty, [args[0].immediate()])
} else {
self.gl_op(GLOp::FAbs, ret_ty, [args[0].immediate()])
}
}
sym::minnumf32 | sym::minnumf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(
CLOp::fmin,
ret_ty,
@ -286,7 +286,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
}
}
sym::maxnumf32 | sym::maxnumf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(
CLOp::fmax,
ret_ty,
@ -301,7 +301,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
}
}
sym::copysignf32 | sym::copysignf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(
CLOp::copysign,
ret_ty,
@ -314,21 +314,21 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
}
}
sym::floorf32 | sym::floorf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::floor, ret_ty, [args[0].immediate()])
} else {
self.gl_op(GLOp::Floor, ret_ty, [args[0].immediate()])
}
}
sym::ceilf32 | sym::ceilf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::ceil, ret_ty, [args[0].immediate()])
} else {
self.gl_op(GLOp::Ceil, ret_ty, [args[0].immediate()])
}
}
sym::truncf32 | sym::truncf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::trunc, ret_ty, [args[0].immediate()])
} else {
self.gl_op(GLOp::Trunc, ret_ty, [args[0].immediate()])
@ -336,14 +336,14 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
}
// TODO: Correctness of all these rounds
sym::rintf32 | sym::rintf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::rint, ret_ty, [args[0].immediate()])
} else {
self.gl_op(GLOp::Round, ret_ty, [args[0].immediate()])
}
}
sym::nearbyintf32 | sym::nearbyintf64 | sym::roundf32 | sym::roundf64 => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::round, ret_ty, [args[0].immediate()])
} else {
self.gl_op(GLOp::Round, ret_ty, [args[0].immediate()])
@ -359,7 +359,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
// TODO: Do we want to manually implement these instead of using intel instructions?
sym::ctlz | sym::ctlz_nonzero => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::clz, ret_ty, [args[0].immediate()])
} else {
self.ext_inst
@ -376,7 +376,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> {
}
}
sym::cttz | sym::cttz_nonzero => {
if self.kernel_mode {
if self.target.is_kernel() {
self.cl_op(CLOp::ctz, ret_ty, [args[0].immediate()])
} else {
self.ext_inst

View File

@ -1,6 +1,7 @@
use crate::builder;
use crate::codegen_cx::CodegenCx;
use crate::spirv_type::SpirvType;
use crate::target::SpirvTarget;
use rspirv::dr::{Block, Builder, Module, Operand};
use rspirv::spirv::{AddressingModel, Capability, MemoryModel, Op, StorageClass, Word};
use rspirv::{binary::Assemble, binary::Disassemble};
@ -302,17 +303,14 @@ pub struct BuilderSpirv {
}
impl BuilderSpirv {
pub fn new(
version: Option<(u8, u8)>,
memory_model: Option<MemoryModel>,
kernel_mode: bool,
) -> Self {
pub fn new(target: &SpirvTarget) -> Self {
let version = target.spirv_version();
let memory_model = target.memory_model();
let mut builder = Builder::new();
// Default to spir-v 1.3
let version = version.unwrap_or((1, 3));
builder.set_version(version.0, version.1);
let memory_model = memory_model.unwrap_or(MemoryModel::Vulkan);
if kernel_mode {
if target.is_kernel() {
builder.capability(Capability::Kernel);
} else {
builder.capability(Capability::Shader);
@ -327,18 +325,23 @@ impl BuilderSpirv {
builder.extension("SPV_KHR_variable_pointers");
}
}
// The linker will always be ran on this module
builder.capability(Capability::Linkage);
builder.capability(Capability::Int8);
builder.capability(Capability::Int16);
builder.capability(Capability::Int64);
builder.capability(Capability::Float64);
if kernel_mode {
let addressing_model = if target.is_kernel() {
builder.capability(Capability::Addresses);
builder.memory_model(AddressingModel::Physical32, MemoryModel::OpenCL);
AddressingModel::Physical32
} else {
builder.memory_model(AddressingModel::Logical, memory_model);
}
AddressingModel::Logical
};
builder.memory_model(addressing_model, memory_model);
Self {
builder: RefCell::new(builder),
const_to_id: Default::default(),

View File

@ -25,7 +25,7 @@ impl<'tcx> CodegenCx<'tcx> {
}
pub fn constant_i32(&self, span: Span, val: i32) -> SpirvValue {
let ty = SpirvType::Integer(32, !self.kernel_mode).def(span, self);
let ty = SpirvType::Integer(32, !self.target.is_kernel()).def(span, self);
self.builder.def_constant(ty, SpirvConst::U32(val as u32))
}
@ -219,7 +219,7 @@ impl<'tcx> ConstMethods<'tcx> for CodegenCx<'tcx> {
Primitive::Int(int_size, int_signedness) => match self.lookup_type(ty) {
SpirvType::Integer(width, spirv_signedness) => {
assert_eq!(width as u64, int_size.size().bits());
if !self.kernel_mode {
if !self.target.is_kernel() {
assert_eq!(spirv_signedness, int_signedness);
}
self.constant_int(ty, data as u64)

View File

@ -10,8 +10,10 @@ use crate::decorations::{
};
use crate::spirv_type::{SpirvType, SpirvTypePrinter, TypeCache};
use crate::symbols::Symbols;
use crate::target::SpirvTarget;
use rspirv::dr::{Module, Operand};
use rspirv::spirv::{AddressingModel, Decoration, LinkageType, MemoryModel, Word};
use rspirv::spirv::{AddressingModel, Decoration, LinkageType, Word};
use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, VariableKind};
use rustc_codegen_ssa::traits::{
AsmMethods, BackendTypes, CoverageInfoMethods, DebugInfoMethods, MiscMethods,
@ -55,7 +57,6 @@ pub struct CodegenCx<'tcx> {
/// get `LoopControl::UNROLL` applied to all of their loops' `OpLoopMerge`
/// instructions, during structuralization.
unroll_loops_decorations: RefCell<FxHashMap<Word, UnrollLoopsDecoration>>,
pub kernel_mode: bool,
/// Cache of all the builtin symbols we need
pub sym: Rc<Symbols>,
pub instruction_table: InstructionTable,
@ -71,44 +72,24 @@ pub struct CodegenCx<'tcx> {
pub i8_i16_atomics_allowed: bool,
pub codegen_args: CodegenArgs,
/// Information about the SPIR-V target.
pub target: SpirvTarget,
}
impl<'tcx> CodegenCx<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, codegen_unit: &'tcx CodegenUnit<'tcx>) -> Self {
let sym = Symbols::get();
let mut spirv_version = None;
let mut memory_model = None;
let mut kernel_mode = false;
for &feature in &tcx.sess.target_features {
if feature == sym.kernel {
kernel_mode = true;
} else if feature == sym.spirv10 {
spirv_version = Some((1, 0));
} else if feature == sym.spirv11 {
spirv_version = Some((1, 1));
} else if feature == sym.spirv12 {
spirv_version = Some((1, 2));
} else if feature == sym.spirv13 {
spirv_version = Some((1, 3));
} else if feature == sym.spirv14 {
spirv_version = Some((1, 4));
} else if feature == sym.spirv15 {
spirv_version = Some((1, 5));
} else if feature == sym.simple {
memory_model = Some(MemoryModel::Simple);
} else if feature == sym.vulkan {
memory_model = Some(MemoryModel::Vulkan);
} else if feature == sym.glsl450 {
memory_model = Some(MemoryModel::GLSL450);
} else {
tcx.sess.err(&format!("Unknown feature {}", feature));
}
tcx.sess.err(&format!("Unknown feature {}", feature));
}
let codegen_args = CodegenArgs::from_session(tcx.sess);
let target = tcx.sess.target.llvm_target.parse().unwrap();
Self {
tcx,
codegen_unit,
builder: BuilderSpirv::new(spirv_version, memory_model, kernel_mode),
builder: BuilderSpirv::new(&target),
instances: Default::default(),
function_parameter_values: Default::default(),
type_cache: Default::default(),
@ -116,7 +97,7 @@ impl<'tcx> CodegenCx<'tcx> {
ext_inst: Default::default(),
zombie_decorations: Default::default(),
unroll_loops_decorations: Default::default(),
kernel_mode,
target,
sym,
instruction_table: InstructionTable::new(),
libm_intrinsics: Default::default(),

View File

@ -116,6 +116,7 @@ mod linker;
mod spirv_type;
mod spirv_type_constraints;
mod symbols;
mod target;
use builder::Builder;
use codegen_cx::{CodegenArgs, CodegenCx, ModuleOutputType};
@ -146,7 +147,7 @@ use rustc_session::config::{self, OptLevel, OutputFilenames, OutputType};
use rustc_session::Session;
use rustc_span::symbol::{sym, Symbol};
use rustc_target::spec::abi::Abi;
use rustc_target::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions, TargetTriple};
use rustc_target::spec::{Target, TargetTriple};
use std::any::Any;
use std::env;
use std::fs::{create_dir_all, File};
@ -211,34 +212,6 @@ fn is_blocklisted_fn<'tcx>(
false
}
fn target_options(env: Option<spirv_tools::TargetEnv>) -> Target {
Target {
llvm_target: "no-llvm".to_string(),
pointer_width: 32,
data_layout: "e-m:e-p:32:32:32-i64:64-n8:16:32:64".to_string(),
arch: "spirv".to_string(),
options: TargetOptions {
simd_types_indirect: false,
allows_weak_linkage: false,
crt_static_allows_dylibs: true,
dll_prefix: "".to_string(),
dll_suffix: ".spv".to_string(),
dynamic_linking: true,
emit_debug_gdb_scripts: false,
linker_flavor: LinkerFlavor::Ld,
panic_strategy: PanicStrategy::Abort,
os: "unknown".to_string(),
env: env
.as_ref()
.map(ToString::to_string)
.unwrap_or_else(|| "unknown".to_string()),
// TODO: Investigate if main_needs_argc_argv is useful (for building exes)
main_needs_argc_argv: false,
..Default::default()
},
}
}
// TODO: Should this store Vec or Module?
struct SpirvModuleBuffer(Vec<u32>);
@ -294,20 +267,10 @@ impl CodegenBackend for SpirvCodegenBackend {
fn target_override(&self, opts: &config::Options) -> Option<Target> {
match opts.target_triple {
TargetTriple::TargetTriple(ref target_triple) => {
const ARCH_VENDOR: &str = "spirv-unknown-";
if !target_triple.starts_with(ARCH_VENDOR) {
return None;
}
let env = &target_triple[ARCH_VENDOR.len()..];
match env.parse() {
Ok(env) => Some(target_options(Some(env))),
Err(_) if env == "unknown" => Some(target_options(None)),
Err(_) => None,
}
}
TargetTriple::TargetTriple(ref target) => target
.parse::<target::SpirvTarget>()
.map(|target| target.rustc_target())
.ok(),
TargetTriple::TargetPath(_) => None,
}
}

View File

@ -147,7 +147,7 @@ impl SpirvType {
let id = emit.id();
let result = emit.type_struct_id(Some(id), field_types.iter().cloned());
// The struct size is only used in our own sizeof_in_bits() (used in e.g. ArrayStride decoration)
if !cx.kernel_mode {
if !cx.target.is_kernel() {
// TODO: kernel mode can't do this??
for (index, offset) in field_offsets.iter().copied().enumerate() {
emit.member_decorate(
@ -177,7 +177,7 @@ impl SpirvType {
.expect("Element of sized array must be sized")
.bytes();
let result = cx.emit_global().type_array(element, count.def_cx(cx));
if !cx.kernel_mode {
if !cx.target.is_kernel() {
// TODO: kernel mode can't do this??
cx.emit_global().decorate(
result,
@ -200,7 +200,7 @@ impl SpirvType {
Decoration::ArrayStride,
iter::once(Operand::LiteralInt32(element_size as u32)),
);
if cx.kernel_mode {
if cx.target.is_kernel() {
cx.zombie_with_span(result, def_span, "RuntimeArray in kernel mode");
}
result

View File

@ -20,16 +20,6 @@ pub struct Symbols {
pub spirv_std: Symbol,
pub libm: Symbol,
pub num_traits: Symbol,
pub kernel: Symbol,
pub simple: Symbol,
pub vulkan: Symbol,
pub glsl450: Symbol,
pub spirv10: Symbol,
pub spirv11: Symbol,
pub spirv12: Symbol,
pub spirv13: Symbol,
pub spirv14: Symbol,
pub spirv15: Symbol,
pub entry_point_name: Symbol,
descriptor_set: Symbol,
binding: Symbol,
@ -376,16 +366,6 @@ impl Symbols {
spirv_std: Symbol::intern("spirv_std"),
libm: Symbol::intern("libm"),
num_traits: Symbol::intern("num_traits"),
kernel: Symbol::intern("kernel"),
simple: Symbol::intern("simple"),
vulkan: Symbol::intern("vulkan"),
glsl450: Symbol::intern("glsl450"),
spirv10: Symbol::intern("spirv1.0"),
spirv11: Symbol::intern("spirv1.1"),
spirv12: Symbol::intern("spirv1.2"),
spirv13: Symbol::intern("spirv1.3"),
spirv14: Symbol::intern("spirv1.4"),
spirv15: Symbol::intern("spirv1.5"),
descriptor_set: Symbol::intern("descriptor_set"),
binding: Symbol::intern("binding"),
image_type: Symbol::intern("image_type"),

View File

@ -0,0 +1,149 @@
use rspirv::spirv::MemoryModel;
use rustc_target::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions};
use spirv_tools::TargetEnv;
const ARCH: &str = "spirv";
pub struct SpirvTarget {
env: TargetEnv,
vendor: String,
}
impl SpirvTarget {
pub fn is_kernel(&self) -> bool {
self.memory_model() == MemoryModel::OpenCL
}
pub fn memory_model(&self) -> MemoryModel {
match self.env {
TargetEnv::Universal_1_0
| TargetEnv::Universal_1_1
| TargetEnv::Universal_1_2
| TargetEnv::Universal_1_3
| TargetEnv::Universal_1_4
| TargetEnv::Universal_1_5 => MemoryModel::Simple,
TargetEnv::OpenGL_4_0
| TargetEnv::OpenGL_4_1
| TargetEnv::OpenGL_4_2
| TargetEnv::OpenGL_4_3
| TargetEnv::OpenGL_4_5 => MemoryModel::GLSL450,
TargetEnv::OpenCL_2_1
| TargetEnv::OpenCL_2_2
| TargetEnv::OpenCL_1_2
| TargetEnv::OpenCLEmbedded_1_2
| TargetEnv::OpenCL_2_0
| TargetEnv::OpenCLEmbedded_2_0
| TargetEnv::OpenCLEmbedded_2_1
| TargetEnv::OpenCLEmbedded_2_2 => MemoryModel::OpenCL,
TargetEnv::Vulkan_1_0
| TargetEnv::Vulkan_1_1
| TargetEnv::WebGPU_0
| TargetEnv::Vulkan_1_1_Spirv_1_4
| TargetEnv::Vulkan_1_2 => MemoryModel::Vulkan,
}
}
pub fn spirv_version(&self) -> (u8, u8) {
#[allow(clippy::match_same_arms)]
match self.env {
TargetEnv::Universal_1_0 => (1, 0),
TargetEnv::Universal_1_1 => (1, 1),
TargetEnv::Universal_1_2 => (1, 2),
TargetEnv::Universal_1_3 => (1, 3),
TargetEnv::Universal_1_4 => (1, 4),
TargetEnv::Universal_1_5 => (1, 5),
TargetEnv::OpenGL_4_0 => (1, 0),
TargetEnv::OpenGL_4_1 => (1, 0),
TargetEnv::OpenGL_4_2 => (1, 0),
TargetEnv::OpenGL_4_3 => (1, 0),
TargetEnv::OpenGL_4_5 => (1, 3),
TargetEnv::OpenCL_1_2 => (1, 0),
TargetEnv::OpenCL_2_0 => (1, 0),
TargetEnv::OpenCL_2_1 => (1, 0),
TargetEnv::OpenCL_2_2 => (1, 2),
TargetEnv::OpenCLEmbedded_1_2 => (1, 0),
TargetEnv::OpenCLEmbedded_2_0 => (1, 0),
TargetEnv::OpenCLEmbedded_2_1 => (1, 0),
TargetEnv::OpenCLEmbedded_2_2 => (1, 2),
TargetEnv::Vulkan_1_0 => (1, 0),
TargetEnv::Vulkan_1_1 => (1, 3),
TargetEnv::WebGPU_0 => (1, 3),
TargetEnv::Vulkan_1_1_Spirv_1_4 => (1, 4),
TargetEnv::Vulkan_1_2 => (1, 5),
}
}
pub fn rustc_target(&self) -> Target {
Target {
llvm_target: self.to_string(),
pointer_width: 32,
data_layout: "e-m:e-p:32:32:32-i64:64-n8:16:32:64".to_string(),
arch: String::from(ARCH),
options: TargetOptions {
simd_types_indirect: false,
allows_weak_linkage: false,
crt_static_allows_dylibs: true,
dll_prefix: "".to_string(),
dll_suffix: ".spv".to_string(),
dynamic_linking: true,
emit_debug_gdb_scripts: false,
linker_flavor: LinkerFlavor::Ld,
panic_strategy: PanicStrategy::Abort,
os: "unknown".to_string(),
env: self.env.to_string(),
vendor: self.vendor.clone(),
// TODO: Investigate if main_needs_argc_argv is useful (for building exes)
main_needs_argc_argv: false,
..Default::default()
},
}
}
}
impl std::str::FromStr for SpirvTarget {
type Err = InvalidTarget;
fn from_str(target: &str) -> Result<Self, Self::Err> {
let mut iter = target.split('-');
let error = || InvalidTarget(target.into());
if iter.next().map_or(true, |arch| arch != ARCH) {
return Err(error());
}
let vendor = iter.next().map(From::from).ok_or_else(error)?;
let env = iter
.next()
.and_then(|env| env.parse().ok())
.ok_or_else(error)?;
if iter.next().is_some() {
return Err(error());
}
Ok(Self { env, vendor })
}
}
impl std::fmt::Display for SpirvTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}-{}-{}", ARCH, self.vendor, self.env)
}
}
#[derive(Debug)]
pub struct InvalidTarget(String);
impl std::error::Error for InvalidTarget {}
impl std::fmt::Display for InvalidTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Invalid target `{}`.", self.0)
}
}

View File

@ -106,17 +106,16 @@ pub struct SpirvBuilder {
path_to_crate: PathBuf,
print_metadata: bool,
release: bool,
spirv_version: Option<(u8, u8)>,
memory_model: Option<MemoryModel>,
target: String,
}
impl SpirvBuilder {
pub fn new(path_to_crate: impl AsRef<Path>) -> Self {
pub fn new(path_to_crate: impl AsRef<Path>, target: impl Into<String>) -> Self {
Self {
path_to_crate: path_to_crate.as_ref().to_owned(),
print_metadata: true,
release: true,
spirv_version: None,
memory_model: None,
target: target.into(),
}
}
@ -132,18 +131,6 @@ impl SpirvBuilder {
self
}
/// Sets the SPIR-V binary version to use. Defaults to v1.3.
pub fn spirv_version(mut self, major: u8, minor: u8) -> Self {
self.spirv_version = Some((major, minor));
self
}
/// Sets the SPIR-V memory model. Defaults to Vulkan.
pub fn memory_model(mut self, memory_model: MemoryModel) -> Self {
self.memory_model = Some(memory_model);
self
}
/// Builds the module. Returns the path to the built spir-v file. If `print_metadata` is true,
/// you usually don't have to inspect the path, as the environment variable will already be
/// set.
@ -212,35 +199,13 @@ fn invoke_rustc(builder: &SpirvBuilder, multimodule: bool) -> Result<PathBuf, Sp
// 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 mut target_features = Vec::new();
// these must match codegen_cx/mod.rs
if let Some((major, minor)) = builder.spirv_version {
target_features.push(format!("+spirv{}.{}", major, minor));
}
if let Some(memory_model) = &builder.memory_model {
target_features.push(
match memory_model {
MemoryModel::Simple => "+simple",
MemoryModel::Vulkan => "+vulkan",
MemoryModel::GLSL450 => "+glsl450",
}
.to_string(),
);
}
let feature_flag = if target_features.is_empty() {
String::new()
} else {
format!(" -C target-feature={}", target_features.join(","))
};
let llvm_args = if multimodule {
" -C llvm-args=--module-output=multiple"
} else {
""
};
let llvm_args = multimodule
.then(|| " -C llvm-args=--module-output=multiple")
.unwrap_or_default();
let rustflags = format!(
"-Z codegen-backend={} -Z symbol-mangling-version=v0{}{}",
"-Z codegen-backend={} -Z symbol-mangling-version=v0{}",
rustc_codegen_spirv.display(),
feature_flag,
llvm_args,
);
let mut cargo = Command::new("cargo");
@ -250,7 +215,7 @@ fn invoke_rustc(builder: &SpirvBuilder, multimodule: bool) -> Result<PathBuf, Sp
"-Zbuild-std=core",
"-Zbuild-std-features=compiler-builtins-mem",
"--target",
"spirv-unknown-unknown",
&*builder.target,
]);
if builder.release {
cargo.arg("--release");

View File

@ -27,10 +27,8 @@ fn custom_entry_point() {
pub fn main() { }
"#,
r#"OpCapability Shader
OpCapability VulkanMemoryModel
OpCapability VariablePointers
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical Vulkan
OpMemoryModel Logical Simple
OpEntryPoint Fragment %1 "hello_world"
OpExecutionMode %1 OriginUpperLeft
OpName %2 "test_project::main"
@ -83,7 +81,7 @@ fn asm() {
asm!(
"%int = OpTypeInt 32 0",
"%scope = OpConstant %int 2",
"%semantics = OpConstant %int 8452",
"%semantics = OpConstant %int 16",
"OpMemoryBarrier %scope %semantics",
);
}
@ -173,12 +171,10 @@ fn asm_op_decorate() {
add_decorate();
}"#,
r#"OpCapability Shader
OpCapability VulkanMemoryModel
OpCapability VariablePointers
OpCapability RuntimeDescriptorArray
OpExtension "SPV_KHR_vulkan_memory_model"
OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical Vulkan
OpMemoryModel Logical Simple
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpName %2 "test_project::add_decorate"

View File

@ -79,7 +79,7 @@ fn setup(src: &str) -> Result<PathBuf, Box<dyn Error>> {
fn build(src: &str) -> PathBuf {
let project = setup(src).expect("Failed to set up project");
crate::SpirvBuilder::new(&project)
crate::SpirvBuilder::new(&project, "spirv-unknown-spv1.3")
.print_metadata(false)
.release(false)
.build()

View File

@ -25,6 +25,45 @@ The `rust-gpu` project currently supports a limited number of platforms and grap
| WGPU | 0.6 | Primary | Uses a translation layer to Metal/DX12
| OpenGL | ??? | Tertiary |
### SPIR-V Targets
- `spirv-unknown-spv1.0`
- `spirv-unknown-spv1.1`
- `spirv-unknown-spv1.2`
- `spirv-unknown-spv1.3`
- `spirv-unknown-spv1.4`
- `spirv-unknown-spv1.5`
### Vulkan Targets
- `spirv-unknown-vulkan1.0`
- `spirv-unknown-vulkan1.1`
- `spirv-unknown-vulkan1.1spv1.4`
- `spirv-unknown-vulkan1.2`
### WebGPU Targets
- `spirv-unknown-webgpu0`
### OpenGL Targets
- `spirv-unknown-opengl4.0`
- `spirv-unknown-opengl4.1`
- `spirv-unknown-opengl4.2`
- `spirv-unknown-opengl4.3`
- `spirv-unknown-opengl4.5`
### OpenCL Targets
- `spirv-unknown-opencl1.2`
- `spirv-unknown-opencl1.2embedded`
- `spirv-unknown-opencl2.0`
- `spirv-unknown-opencl2.0embedded`
- `spirv-unknown-opencl2.1`
- `spirv-unknown-opencl2.1embedded`
- `spirv-unknown-opencl2.2`
- `spirv-unknown-opencl2.2embedded`
## GPU
Currently we don't have specific generations of GPUs for support, as long they support Vulkan 1.1+ with the latest officially installed drivers it should be able build and run the examples. You can check your Vulkan version using the [`vulkaninfo`] command from the `vulkan-sdk`.

View File

@ -15,7 +15,7 @@ You can now test out and try building shaders with rust-gpu from the browser!
and run shaders on the web.
- [Shader Playground] A playground for building and checking the output of
shader code similar to godbolt or play.rust-lang.org.
[SHADERed]: https://shadered.org/template
[shader playground]: http://shader-playground.timjones.io/9d744d5893beb6a8f129fda50ad4aeeb
@ -70,15 +70,14 @@ Now you should have a `librustc_codegen_spirv` dynamic library available in
`target/release`. You'll need to keep this somewhere stable that you can
reference from your shader project.
Now we need to add our `.cargo/config` file. This tells cargo to build for the
`spirv-unknown-unknown` target, and provides a path to the codegen backend for
that target. We have to also provide `-Zbuild-std` as the
`spirv-unknown-unknown` sysroot is not currently available in the
default installation.
Now we need to add our `.cargo/config` file. This is a configuration file that
tells cargo how to build for SPIR-V. You need provide the target you're
compiling for (see [platform support](./platform-support.md)) and provide a path to your built `rustc_codegen_spirv` dynamic
library. We have to also provide `-Zbuild-std`.
```toml
[build]
target = "spirv-unknown-unknown"
target = "spirv-unknown-spv1.3"
rustflags = [
"-Zcodegen-backend=<path_to_librustc_codegen_spirv>",
"-Zsymbol-mangling-version=v0"
@ -89,7 +88,7 @@ build-std=["core"]
build-std-features=["compiler-builtins-mem"]
```
Now we can build our crate with cargo as normal.
Now we can build our crate with cargo as normal.
```bash
cargo build
```

View File

@ -1,9 +1,8 @@
use spirv_builder::SpirvBuilder;
fn main() {
let result = SpirvBuilder::new("../shaders/sky-shader")
let result = SpirvBuilder::new("../shaders/sky-shader", "spirv-unknown-spv1.3")
.print_metadata(false)
.spirv_version(1, 0)
.build_multimodule()
.unwrap();
println!("{:#?}", result);

View File

@ -171,10 +171,13 @@ pub fn main() {
}
pub fn compile_shaders() -> Vec<SpvFile> {
let spv_paths: Vec<PathBuf> = vec![SpirvBuilder::new("examples/shaders/sky-shader")
.print_metadata(false)
.build()
.unwrap()];
let spv_paths: Vec<PathBuf> =
vec![
SpirvBuilder::new("examples/shaders/sky-shader", "spirv-unknown-vulkan1.1")
.print_metadata(false)
.build()
.unwrap(),
];
let mut spv_files = Vec::<SpvFile>::with_capacity(spv_paths.len());
for path in spv_paths.iter() {
spv_files.push(SpvFile {

View File

@ -2,9 +2,7 @@ use spirv_builder::SpirvBuilder;
use std::error::Error;
fn build_shader(path_to_create: &str) -> Result<(), Box<dyn Error>> {
SpirvBuilder::new(path_to_create)
.spirv_version(1, 0)
.build()?;
SpirvBuilder::new(path_to_create, "spirv-unknown-vulkan1.0").build()?;
Ok(())
}

View File

@ -30,7 +30,7 @@ impl Opt {
pub fn environments(&self) -> Vec<String> {
match &self.target_env {
Some(env) => env.split(',').map(String::from).collect(),
None => vec!["unknown".into()],
None => vec!["spv1.3".into()],
}
}
}