mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2025-02-16 08:54:56 +00:00
Generate bools as bools instead of u8 (#809)
* Generate bools as bools instead of u8 * convert bool->int select to cast
This commit is contained in:
parent
c6b75601b9
commit
2e4de94a66
@ -150,9 +150,9 @@ pub(crate) fn provide(providers: &mut Providers) {
|
||||
let TyAndLayout { ty, mut layout } =
|
||||
(rustc_interface::DEFAULT_QUERY_PROVIDERS.layout_of)(tcx, key)?;
|
||||
|
||||
// FIXME(eddyb) make use of this - at this point, it's just a placeholder.
|
||||
#[allow(clippy::match_single_binding)]
|
||||
#[allow(clippy::match_like_matches_macro)]
|
||||
let hide_niche = match ty.kind() {
|
||||
ty::Bool => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
@ -284,13 +284,6 @@ enum PointeeDefState {
|
||||
/// provides a uniform way of translating them.
|
||||
pub trait ConvSpirvType<'tcx> {
|
||||
fn spirv_type(&self, span: Span, cx: &CodegenCx<'tcx>) -> Word;
|
||||
/// spirv (and llvm) do not allow storing booleans in memory, they are abstract unsized values.
|
||||
/// So, if we're dealing with a "memory type", convert bool to u8. The opposite is an
|
||||
/// "immediate type", which keeps bools as bools. See also the functions `from_immediate` and
|
||||
/// `to_immediate`, which convert between the two.
|
||||
fn spirv_type_immediate(&self, span: Span, cx: &CodegenCx<'tcx>) -> Word {
|
||||
self.spirv_type(span, cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ConvSpirvType<'tcx> for PointeeTy<'tcx> {
|
||||
@ -302,14 +295,6 @@ impl<'tcx> ConvSpirvType<'tcx> for PointeeTy<'tcx> {
|
||||
.spirv_type(span, cx),
|
||||
}
|
||||
}
|
||||
fn spirv_type_immediate(&self, span: Span, cx: &CodegenCx<'tcx>) -> Word {
|
||||
match *self {
|
||||
PointeeTy::Ty(ty) => ty.spirv_type_immediate(span, cx),
|
||||
PointeeTy::Fn(ty) => cx
|
||||
.fn_abi_of_fn_ptr(ty, ty::List::empty())
|
||||
.spirv_type_immediate(span, cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ConvSpirvType<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||
@ -318,9 +303,7 @@ impl<'tcx> ConvSpirvType<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||
|
||||
let return_type = match self.ret.mode {
|
||||
PassMode::Ignore => SpirvType::Void.def(span, cx),
|
||||
PassMode::Direct(_) | PassMode::Pair(..) => {
|
||||
self.ret.layout.spirv_type_immediate(span, cx)
|
||||
}
|
||||
PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.spirv_type(span, cx),
|
||||
PassMode::Cast(_) | PassMode::Indirect { .. } => span_bug!(
|
||||
span,
|
||||
"query hooks should've made this `PassMode` impossible: {:#?}",
|
||||
@ -331,14 +314,10 @@ impl<'tcx> ConvSpirvType<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||
for arg in &self.args {
|
||||
let arg_type = match arg.mode {
|
||||
PassMode::Ignore => continue,
|
||||
PassMode::Direct(_) => arg.layout.spirv_type_immediate(span, cx),
|
||||
PassMode::Direct(_) => arg.layout.spirv_type(span, cx),
|
||||
PassMode::Pair(_, _) => {
|
||||
argument_types.push(scalar_pair_element_backend_type(
|
||||
cx, span, arg.layout, 0, true,
|
||||
));
|
||||
argument_types.push(scalar_pair_element_backend_type(
|
||||
cx, span, arg.layout, 1, true,
|
||||
));
|
||||
argument_types.push(scalar_pair_element_backend_type(cx, span, arg.layout, 0));
|
||||
argument_types.push(scalar_pair_element_backend_type(cx, span, arg.layout, 1));
|
||||
continue;
|
||||
}
|
||||
PassMode::Cast(_) | PassMode::Indirect { .. } => span_bug!(
|
||||
@ -359,77 +338,69 @@ impl<'tcx> ConvSpirvType<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||
}
|
||||
|
||||
impl<'tcx> ConvSpirvType<'tcx> for TyAndLayout<'tcx> {
|
||||
fn spirv_type(&self, span: Span, cx: &CodegenCx<'tcx>) -> Word {
|
||||
trans_type_impl(cx, span, *self, false)
|
||||
}
|
||||
fn spirv_type_immediate(&self, span: Span, cx: &CodegenCx<'tcx>) -> Word {
|
||||
trans_type_impl(cx, span, *self, true)
|
||||
}
|
||||
}
|
||||
fn spirv_type(&self, mut span: Span, cx: &CodegenCx<'tcx>) -> Word {
|
||||
if let TyKind::Adt(adt, substs) = *self.ty.kind() {
|
||||
if span == DUMMY_SP {
|
||||
span = cx.tcx.def_span(adt.did);
|
||||
}
|
||||
|
||||
fn trans_type_impl<'tcx>(
|
||||
cx: &CodegenCx<'tcx>,
|
||||
mut span: Span,
|
||||
ty: TyAndLayout<'tcx>,
|
||||
is_immediate: bool,
|
||||
) -> Word {
|
||||
if let TyKind::Adt(adt, substs) = *ty.ty.kind() {
|
||||
if span == DUMMY_SP {
|
||||
span = cx.tcx.def_span(adt.did);
|
||||
}
|
||||
let attrs = AggregatedSpirvAttributes::parse(cx, cx.tcx.get_attrs(adt.did));
|
||||
|
||||
let attrs = AggregatedSpirvAttributes::parse(cx, cx.tcx.get_attrs(adt.did));
|
||||
|
||||
if let Some(intrinsic_type_attr) = attrs.intrinsic_type.map(|attr| attr.value) {
|
||||
if let Ok(spirv_type) = trans_intrinsic_type(cx, span, ty, substs, intrinsic_type_attr)
|
||||
{
|
||||
return spirv_type;
|
||||
if let Some(intrinsic_type_attr) = attrs.intrinsic_type.map(|attr| attr.value) {
|
||||
if let Ok(spirv_type) =
|
||||
trans_intrinsic_type(cx, span, *self, substs, intrinsic_type_attr)
|
||||
{
|
||||
return spirv_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: ty.layout is orthogonal to ty.ty, e.g. `ManuallyDrop<Result<isize, isize>>` has abi
|
||||
// `ScalarPair`.
|
||||
// There's a few layers that we go through here. First we inspect layout.abi, then if relevant, layout.fields, etc.
|
||||
match ty.abi {
|
||||
Abi::Uninhabited => SpirvType::Adt {
|
||||
def_id: def_id_for_spirv_type_adt(ty),
|
||||
size: Some(Size::ZERO),
|
||||
align: Align::from_bytes(0).unwrap(),
|
||||
field_types: Vec::new(),
|
||||
field_offsets: Vec::new(),
|
||||
field_names: None,
|
||||
}
|
||||
.def_with_name(cx, span, TyLayoutNameKey::from(ty)),
|
||||
Abi::Scalar(ref scalar) => trans_scalar(cx, span, ty, scalar, Size::ZERO, is_immediate),
|
||||
Abi::ScalarPair(ref a, ref b) => {
|
||||
// Note: We can't use auto_struct_layout here because the spirv types here might be undefined due to
|
||||
// recursive pointer types.
|
||||
let a_offset = Size::ZERO;
|
||||
let b_offset = a.value.size(cx).align_to(b.value.align(cx).abi);
|
||||
// Note! Do not pass through is_immediate here - they're wrapped in a struct, hence, not immediate.
|
||||
let a = trans_scalar(cx, span, ty, a, a_offset, false);
|
||||
let b = trans_scalar(cx, span, ty, b, b_offset, false);
|
||||
let size = if ty.is_unsized() { None } else { Some(ty.size) };
|
||||
SpirvType::Adt {
|
||||
def_id: def_id_for_spirv_type_adt(ty),
|
||||
size,
|
||||
align: ty.align.abi,
|
||||
field_types: vec![a, b],
|
||||
field_offsets: vec![a_offset, b_offset],
|
||||
// Note: ty.layout is orthogonal to ty.ty, e.g. `ManuallyDrop<Result<isize, isize>>` has abi
|
||||
// `ScalarPair`.
|
||||
// There's a few layers that we go through here. First we inspect layout.abi, then if relevant, layout.fields, etc.
|
||||
match self.abi {
|
||||
Abi::Uninhabited => SpirvType::Adt {
|
||||
def_id: def_id_for_spirv_type_adt(*self),
|
||||
size: Some(Size::ZERO),
|
||||
align: Align::from_bytes(0).unwrap(),
|
||||
field_types: Vec::new(),
|
||||
field_offsets: Vec::new(),
|
||||
field_names: None,
|
||||
}
|
||||
.def_with_name(cx, span, TyLayoutNameKey::from(ty))
|
||||
}
|
||||
Abi::Vector { ref element, count } => {
|
||||
let elem_spirv = trans_scalar(cx, span, ty, element, Size::ZERO, false);
|
||||
SpirvType::Vector {
|
||||
element: elem_spirv,
|
||||
count: count as u32,
|
||||
.def_with_name(cx, span, TyLayoutNameKey::from(*self)),
|
||||
Abi::Scalar(ref scalar) => trans_scalar(cx, span, *self, scalar, Size::ZERO),
|
||||
Abi::ScalarPair(ref a, ref b) => {
|
||||
// Note: We can't use auto_struct_layout here because the spirv types here might be undefined due to
|
||||
// recursive pointer types.
|
||||
let a_offset = Size::ZERO;
|
||||
let b_offset = a.value.size(cx).align_to(b.value.align(cx).abi);
|
||||
let a = trans_scalar(cx, span, *self, a, a_offset);
|
||||
let b = trans_scalar(cx, span, *self, b, b_offset);
|
||||
let size = if self.is_unsized() {
|
||||
None
|
||||
} else {
|
||||
Some(self.size)
|
||||
};
|
||||
SpirvType::Adt {
|
||||
def_id: def_id_for_spirv_type_adt(*self),
|
||||
size,
|
||||
align: self.align.abi,
|
||||
field_types: vec![a, b],
|
||||
field_offsets: vec![a_offset, b_offset],
|
||||
field_names: None,
|
||||
}
|
||||
.def_with_name(cx, span, TyLayoutNameKey::from(*self))
|
||||
}
|
||||
.def(span, cx)
|
||||
Abi::Vector { ref element, count } => {
|
||||
let elem_spirv = trans_scalar(cx, span, *self, element, Size::ZERO);
|
||||
SpirvType::Vector {
|
||||
element: elem_spirv,
|
||||
count: count as u32,
|
||||
}
|
||||
.def(span, cx)
|
||||
}
|
||||
Abi::Aggregate { sized: _ } => trans_aggregate(cx, span, *self),
|
||||
}
|
||||
Abi::Aggregate { sized: _ } => trans_aggregate(cx, span, ty),
|
||||
}
|
||||
}
|
||||
|
||||
@ -440,7 +411,6 @@ pub fn scalar_pair_element_backend_type<'tcx>(
|
||||
span: Span,
|
||||
ty: TyAndLayout<'tcx>,
|
||||
index: usize,
|
||||
is_immediate: bool,
|
||||
) -> Word {
|
||||
let [a, b] = match &ty.layout.abi {
|
||||
Abi::ScalarPair(a, b) => [a, b],
|
||||
@ -455,7 +425,7 @@ pub fn scalar_pair_element_backend_type<'tcx>(
|
||||
1 => a.value.size(cx).align_to(b.value.align(cx).abi),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
trans_scalar(cx, span, ty, [a, b][index], offset, is_immediate)
|
||||
trans_scalar(cx, span, ty, [a, b][index], offset)
|
||||
}
|
||||
|
||||
/// A "scalar" is a basic building block: bools, ints, floats, pointers. (i.e. not something complex like a struct)
|
||||
@ -471,9 +441,8 @@ fn trans_scalar<'tcx>(
|
||||
ty: TyAndLayout<'tcx>,
|
||||
scalar: &Scalar,
|
||||
offset: Size,
|
||||
is_immediate: bool,
|
||||
) -> Word {
|
||||
if is_immediate && scalar.is_bool() {
|
||||
if scalar.is_bool() {
|
||||
return SpirvType::Bool.def(span, cx);
|
||||
}
|
||||
|
||||
@ -608,7 +577,7 @@ fn trans_aggregate<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>
|
||||
}
|
||||
}
|
||||
FieldsShape::Array { stride, count } => {
|
||||
let element_type = trans_type_impl(cx, span, ty.field(cx, 0), false);
|
||||
let element_type = ty.field(cx, 0).spirv_type(span, cx);
|
||||
if ty.is_unsized() {
|
||||
// There's a potential for this array to be sized, but the element to be unsized, e.g. `[[u8]; 5]`.
|
||||
// However, I think rust disallows all these cases, so assert this here.
|
||||
@ -676,7 +645,7 @@ fn trans_struct<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) -
|
||||
let mut field_names = Vec::new();
|
||||
for i in ty.fields.index_by_increasing_offset() {
|
||||
let field_ty = ty.field(cx, i);
|
||||
field_types.push(trans_type_impl(cx, span, field_ty, false));
|
||||
field_types.push(field_ty.spirv_type(span, cx));
|
||||
let offset = ty.fields.offset(i);
|
||||
field_offsets.push(offset);
|
||||
if let Variants::Single { index } = ty.variants {
|
||||
@ -887,7 +856,7 @@ fn trans_intrinsic_type<'tcx>(
|
||||
// The spirv type of it will be generated by querying the type of the first generic.
|
||||
if let Some(image_ty) = substs.types().next() {
|
||||
// TODO: enforce that the generic param is an image type?
|
||||
let image_type = trans_type_impl(cx, span, cx.layout_of(image_ty), false);
|
||||
let image_type = cx.layout_of(image_ty).spirv_type(span, cx);
|
||||
Ok(SpirvType::SampledImage { image_type }.def(span, cx))
|
||||
} else {
|
||||
cx.tcx
|
||||
@ -907,7 +876,7 @@ fn trans_intrinsic_type<'tcx>(
|
||||
// We use a generic to indicate the underlying element type.
|
||||
// The spirv type of it will be generated by querying the type of the first generic.
|
||||
if let Some(elem_ty) = substs.types().next() {
|
||||
let element = trans_type_impl(cx, span, cx.layout_of(elem_ty), false);
|
||||
let element = cx.layout_of(elem_ty).spirv_type(span, cx);
|
||||
Ok(SpirvType::RuntimeArray { element }.def(span, cx))
|
||||
} else {
|
||||
cx.tcx
|
||||
@ -922,7 +891,7 @@ fn trans_intrinsic_type<'tcx>(
|
||||
.expect("#[spirv(matrix)] must be added to a type which has DefId");
|
||||
|
||||
let field_types = (0..ty.fields.count())
|
||||
.map(|i| trans_type_impl(cx, span, ty.field(cx, i), false))
|
||||
.map(|i| ty.field(cx, i).spirv_type(span, cx))
|
||||
.collect::<Vec<_>>();
|
||||
if field_types.len() < 2 {
|
||||
cx.tcx
|
||||
|
@ -782,22 +782,24 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
|
||||
result
|
||||
}
|
||||
|
||||
// rustc has the concept of an immediate vs. memory type - bools are compiled to LLVM bools as
|
||||
// immediates, but if they're behind a pointer, they're compiled to u8. The reason for this is
|
||||
// because LLVM is bad at bools behind pointers (something something u1 bitmasking on load).
|
||||
//
|
||||
// SPIR-V allows bools behind *some* pointers, and disallows others - specifically, it allows
|
||||
// bools behind the storage classes Workgroup, CrossWorkgroup, Private, Function, Input, and
|
||||
// Output. In other words, "For stuff the CPU can't see, bools are OK. For stuff the CPU *can*
|
||||
// see, no bools allowed". So, we always compile rust bools to SPIR-V bools instead of u8 as
|
||||
// rustc does, even if they're behind a pointer, and error if bools are in an interface (the
|
||||
// user should choose u8, u32, or something else instead). That means that immediate types and
|
||||
// memory types are the same, and no conversion needs to happen here.
|
||||
fn from_immediate(&mut self, val: Self::Value) -> Self::Value {
|
||||
if self.lookup_type(val.ty) == SpirvType::Bool {
|
||||
let i8 = SpirvType::Integer(8, false).def(self.span(), self);
|
||||
self.zext(val, i8)
|
||||
} else {
|
||||
val
|
||||
}
|
||||
val
|
||||
}
|
||||
|
||||
// silly clippy, we can't rename this!
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn to_immediate_scalar(&mut self, val: Self::Value, scalar: Scalar) -> Self::Value {
|
||||
if scalar.is_bool() {
|
||||
let bool = SpirvType::Bool.def(self.span(), self);
|
||||
return self.trunc(val, bool);
|
||||
}
|
||||
fn to_immediate_scalar(&mut self, val: Self::Value, _scalar: Scalar) -> Self::Value {
|
||||
val
|
||||
}
|
||||
|
||||
|
@ -407,25 +407,15 @@ impl BuilderSpirv {
|
||||
}
|
||||
|
||||
pub fn dump_module_str(&self) -> String {
|
||||
let mut module = self.builder.borrow().module_ref().clone();
|
||||
let mut header = rspirv::dr::ModuleHeader::new(0);
|
||||
header.set_version(0, 0);
|
||||
module.header = Some(header);
|
||||
module.disassemble()
|
||||
self.builder.borrow().module_ref().disassemble()
|
||||
}
|
||||
|
||||
/// Helper function useful to place right before a crash, to debug the module state.
|
||||
pub fn dump_module(&self, path: impl AsRef<Path>) {
|
||||
let mut module = self.builder.borrow().module_ref().clone();
|
||||
let mut header = rspirv::dr::ModuleHeader::new(0);
|
||||
header.set_version(0, 0);
|
||||
module.header = Some(header);
|
||||
let disas = module.disassemble();
|
||||
println!("{}", disas);
|
||||
let spirv_module = module.assemble();
|
||||
let module = self.builder.borrow().module_ref().assemble();
|
||||
File::create(path)
|
||||
.unwrap()
|
||||
.write_all(spirv_tools::binary::from_binary(&spirv_module))
|
||||
.write_all(spirv_tools::binary::from_binary(&module))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
@ -517,6 +517,13 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
);
|
||||
}
|
||||
|
||||
self.check_for_bools(
|
||||
hir_param.ty_span,
|
||||
var_ptr_spirv_type,
|
||||
storage_class,
|
||||
attrs.builtin.is_some(),
|
||||
);
|
||||
|
||||
// Assign locations from left to right, incrementing each storage class
|
||||
// individually.
|
||||
// TODO: Is this right for UniformConstant? Do they share locations with
|
||||
@ -554,4 +561,46 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Booleans are only allowed in some storage classes. Error if they're in others.
|
||||
fn check_for_bools(&self, span: Span, ty: Word, storage_class: StorageClass, is_builtin: bool) {
|
||||
// private and function are allowed here, but they can't happen.
|
||||
// SPIR-V technically allows all input/output variables to be booleans, not just builtins,
|
||||
// but has a note:
|
||||
// > Khronos Issue #363: OpTypeBool can be used in the Input and Output storage classes,
|
||||
// but the client APIs still only allow built-in Boolean variables (e.g. FrontFacing),
|
||||
// not user variables.
|
||||
// spirv-val disallows non-builtin inputs/outputs, so we do too, I guess.
|
||||
if matches!(
|
||||
storage_class,
|
||||
StorageClass::Workgroup | StorageClass::CrossWorkgroup
|
||||
) || is_builtin && matches!(storage_class, StorageClass::Input | StorageClass::Output)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if recurse(self, ty) {
|
||||
self.tcx
|
||||
.sess
|
||||
.span_err(span, "entrypoint parameter cannot contain a boolean");
|
||||
}
|
||||
fn recurse(cx: &CodegenCx<'_>, ty: Word) -> bool {
|
||||
match cx.lookup_type(ty) {
|
||||
SpirvType::Bool => true,
|
||||
SpirvType::Adt { field_types, .. } => field_types.iter().any(|&f| recurse(cx, f)),
|
||||
SpirvType::Vector { element, .. }
|
||||
| SpirvType::Matrix { element, .. }
|
||||
| SpirvType::Array { element, .. }
|
||||
| SpirvType::RuntimeArray { element }
|
||||
| SpirvType::Pointer { pointee: element }
|
||||
| SpirvType::InterfaceBlock {
|
||||
inner_type: element,
|
||||
} => recurse(cx, element),
|
||||
SpirvType::Function {
|
||||
return_type,
|
||||
arguments,
|
||||
} => recurse(cx, return_type) || arguments.iter().any(|&a| recurse(cx, a)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ impl<'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'tcx> {
|
||||
}
|
||||
|
||||
fn immediate_backend_type(&self, layout: TyAndLayout<'tcx>) -> Self::Type {
|
||||
layout.spirv_type_immediate(DUMMY_SP, self)
|
||||
layout.spirv_type(DUMMY_SP, self)
|
||||
}
|
||||
|
||||
fn is_backend_immediate(&self, layout: TyAndLayout<'tcx>) -> bool {
|
||||
@ -133,9 +133,9 @@ impl<'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'tcx> {
|
||||
&self,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
index: usize,
|
||||
immediate: bool,
|
||||
_immediate: bool,
|
||||
) -> Self::Type {
|
||||
crate::abi::scalar_pair_element_backend_type(self, DUMMY_SP, layout, index, immediate)
|
||||
crate::abi::scalar_pair_element_backend_type(self, DUMMY_SP, layout, index)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,9 +101,7 @@ impl SpirvType {
|
||||
Self::Void => cx.emit_global().type_void_id(id),
|
||||
Self::Bool => cx.emit_global().type_bool_id(id),
|
||||
Self::Integer(width, signedness) => {
|
||||
let result =
|
||||
cx.emit_global()
|
||||
.type_int_id(id, width, if signedness { 1 } else { 0 });
|
||||
let result = cx.emit_global().type_int_id(id, width, signedness as u32);
|
||||
match width {
|
||||
8 if !cx.builder.has_capability(Capability::Int8) => cx
|
||||
.zombie_even_in_user_code(result, def_span, "u8 without OpCapability Int8"),
|
||||
|
@ -23,7 +23,6 @@ pub enum Arrayed {
|
||||
True = 1,
|
||||
}
|
||||
|
||||
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
|
||||
impl From<bool> for Arrayed {
|
||||
fn from(val: bool) -> Self {
|
||||
if val {
|
||||
@ -71,7 +70,7 @@ pub enum ImageDepth {
|
||||
Unknown = 2,
|
||||
}
|
||||
|
||||
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
impl From<Option<bool>> for ImageDepth {
|
||||
fn from(val: Option<bool>) -> Self {
|
||||
match val {
|
||||
@ -82,7 +81,6 @@ impl From<Option<bool>> for ImageDepth {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
|
||||
impl From<bool> for ImageDepth {
|
||||
fn from(val: bool) -> Self {
|
||||
match val {
|
||||
@ -102,7 +100,6 @@ pub enum Multisampled {
|
||||
True = 1,
|
||||
}
|
||||
|
||||
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
|
||||
impl From<bool> for Multisampled {
|
||||
fn from(val: bool) -> Self {
|
||||
if val {
|
||||
@ -126,7 +123,7 @@ pub enum Sampled {
|
||||
No = 2,
|
||||
}
|
||||
|
||||
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
impl From<Option<bool>> for Sampled {
|
||||
fn from(val: Option<bool>) -> Self {
|
||||
match val {
|
||||
@ -137,7 +134,6 @@ impl From<Option<bool>> for Sampled {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(not(target_arch = "spirv"), target_feature = "Int8"))]
|
||||
impl From<bool> for Sampled {
|
||||
fn from(val: bool) -> Self {
|
||||
match val {
|
||||
|
@ -31,22 +31,11 @@ pub fn any<V: Vector<bool, N>, const N: usize>(vector: V) -> bool {
|
||||
|
||||
unsafe {
|
||||
asm! {
|
||||
// Types & Constants
|
||||
"%bool = OpTypeBool",
|
||||
"%u8 = OpTypeInt 8 0",
|
||||
"%u8_0 = OpConstant %u8 0",
|
||||
"%u8_1 = OpConstant %u8 1",
|
||||
"%glam_vec_type = OpTypeVector %u8 {len}",
|
||||
"%bool_vec_type = OpTypeVector %bool {len}",
|
||||
"%false_vec = OpConstantNull %glam_vec_type",
|
||||
// Code
|
||||
"%vector = OpLoad %glam_vec_type {vector}",
|
||||
"%bool_vec = OpINotEqual %bool_vec_type %vector %false_vec",
|
||||
"%result = OpAny %bool %bool_vec",
|
||||
"%boolean = OpSelect %u8 %result %u8_1 %u8_0",
|
||||
"OpStore {result} %boolean",
|
||||
"%vector = OpLoad _ {vector}",
|
||||
"%result = OpAny %bool %vector",
|
||||
"OpStore {result} %result",
|
||||
vector = in(reg) &vector,
|
||||
len = const N,
|
||||
result = in(reg) &mut result
|
||||
}
|
||||
}
|
||||
@ -64,23 +53,12 @@ pub fn all<V: Vector<bool, N>, const N: usize>(vector: V) -> bool {
|
||||
|
||||
unsafe {
|
||||
asm! {
|
||||
// Types & Constants
|
||||
"%bool = OpTypeBool",
|
||||
"%u8 = OpTypeInt 8 0",
|
||||
"%u8_0 = OpConstant %u8 0",
|
||||
"%u8_1 = OpConstant %u8 1",
|
||||
"%glam_vec_type = OpTypeVector %u8 {len}",
|
||||
"%bool_vec_type = OpTypeVector %bool {len}",
|
||||
"%false_vec = OpConstantNull %glam_vec_type",
|
||||
// Code
|
||||
"%vector = OpLoad %glam_vec_type {vector}",
|
||||
"%bool_vec = OpINotEqual %bool_vec_type %vector %false_vec",
|
||||
"%result = OpAll %bool %bool_vec",
|
||||
"%boolean = OpSelect %u8 %result %u8_1 %u8_0",
|
||||
"OpStore {element} %boolean",
|
||||
"%vector = OpLoad _ {vector}",
|
||||
"%result = OpAll %bool %vector",
|
||||
"OpStore {result} %result",
|
||||
vector = in(reg) &vector,
|
||||
len = const N,
|
||||
element = in(reg) &mut result
|
||||
result = in(reg) &mut result
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,19 +30,16 @@ pub unsafe fn demote_to_helper_invocation() {
|
||||
#[spirv_std_macros::gpu_only]
|
||||
#[doc(alias = "OpIsHelperInvocationEXT")]
|
||||
pub fn is_helper_invocation() -> bool {
|
||||
let result: u32;
|
||||
let mut result = false;
|
||||
|
||||
unsafe {
|
||||
asm! {
|
||||
"%bool = OpTypeBool",
|
||||
"%u32 = OpTypeInt 32 0",
|
||||
"%zero = OpConstant %u32 0",
|
||||
"%one = OpConstant %u32 1",
|
||||
"%result = OpIsHelperInvocationEXT %bool",
|
||||
"{} = OpSelect %u32 %result %one %zero",
|
||||
out(reg) result
|
||||
"OpStore {result} %result",
|
||||
result = in(reg) &mut result,
|
||||
};
|
||||
}
|
||||
|
||||
result != 0
|
||||
result
|
||||
}
|
||||
|
@ -19,21 +19,18 @@
|
||||
#[doc(alias = "OpReportIntersectionKHR")]
|
||||
#[inline]
|
||||
pub unsafe fn report_intersection(hit: f32, hit_kind: u32) -> bool {
|
||||
let result: u32;
|
||||
let mut result = false;
|
||||
|
||||
asm! {
|
||||
"%bool = OpTypeBool",
|
||||
"%u32 = OpTypeInt 32 0",
|
||||
"%zero = OpConstant %u32 0",
|
||||
"%one = OpConstant %u32 1",
|
||||
"%result = OpReportIntersectionKHR %bool {hit} {hit_kind}",
|
||||
"{result} = OpSelect %u32 %result %one %zero",
|
||||
result = out(reg) result,
|
||||
"OpStore {result} %result",
|
||||
result = in(reg) &mut result,
|
||||
hit = in(reg) hit,
|
||||
hit_kind = in(reg) hit_kind,
|
||||
};
|
||||
|
||||
result != 0
|
||||
result
|
||||
}
|
||||
|
||||
/// Ignores the current potential intersection, terminating the invocation that
|
||||
|
@ -274,20 +274,17 @@ impl RayQuery {
|
||||
#[doc(alias = "OpRayQueryProceedKHR")]
|
||||
#[inline]
|
||||
pub unsafe fn proceed(&self) -> bool {
|
||||
let result: u32;
|
||||
let mut result = false;
|
||||
|
||||
asm! {
|
||||
"%u32 = OpTypeInt 32 0",
|
||||
"%bool = OpTypeBool",
|
||||
"%u32_0 = OpConstant %u32 0",
|
||||
"%u32_1 = OpConstant %u32 1",
|
||||
"%result = OpRayQueryProceedKHR %bool {ray_query}",
|
||||
"{result} = OpSelect %u32 %result %u32_1 %u32_0",
|
||||
"OpStore {result} %result",
|
||||
ray_query = in(reg) self,
|
||||
result = out(reg) result,
|
||||
result = in(reg) &mut result,
|
||||
}
|
||||
|
||||
result != 0
|
||||
result
|
||||
}
|
||||
|
||||
/// Terminates further execution of a ray query; further calls to
|
||||
|
@ -62,11 +62,6 @@ pub unsafe fn vector_extract_dynamic<T: Scalar, V: Vector<T>>(vector: V, index:
|
||||
}
|
||||
```
|
||||
|
||||
One potential hiccup is that in rustc, any booleans stored "in memory" behind a
|
||||
pointer (which includes local variables) are converted to u8 instead of bool. If
|
||||
passing booleans to/from `asm!` blocks, you probably need to treat it as a u8
|
||||
from within the `asm!` block, even if it's a `bool` in rust.
|
||||
|
||||
### Additional syntax
|
||||
|
||||
| Syntax | Description |
|
||||
|
@ -14,9 +14,7 @@ fn asm_noreturn_open() {
|
||||
// No active basic block without `noreturn`.
|
||||
fn asm_closed() {
|
||||
unsafe {
|
||||
asm!(
|
||||
"OpUnreachable",
|
||||
);
|
||||
asm!("OpUnreachable");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,19 +7,17 @@ error: `noreturn` requires a terminator at the end
|
||||
error: trailing terminator Unreachable requires `options(noreturn)`
|
||||
--> $DIR/block_tracking_fail.rs:17:9
|
||||
|
|
||||
17 | / asm!(
|
||||
18 | | "OpUnreachable",
|
||||
19 | | );
|
||||
| |_________^
|
||||
17 | asm!("OpUnreachable");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected OpLabel after terminator Kill
|
||||
--> $DIR/block_tracking_fail.rs:26:9
|
||||
--> $DIR/block_tracking_fail.rs:24:9
|
||||
|
|
||||
26 | / asm!(
|
||||
27 | | "OpKill",
|
||||
28 | | "%sum = OpFAdd _ {x} {x}",
|
||||
29 | | x = in(reg) x,
|
||||
30 | | );
|
||||
24 | / asm!(
|
||||
25 | | "OpKill",
|
||||
26 | | "%sum = OpFAdd _ {x} {x}",
|
||||
27 | | x = in(reg) x,
|
||||
28 | | );
|
||||
| |_________^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
@ -7,7 +7,7 @@ use spirv_std as _;
|
||||
fn asm_label() {
|
||||
unsafe {
|
||||
asm!(
|
||||
"OpReturn", // close active block
|
||||
"OpReturn", // close active block
|
||||
"%unused = OpLabel", // open new block
|
||||
);
|
||||
}
|
||||
|
@ -24,10 +24,10 @@ fn deep_transpose(r: &'static &'static Mat2) -> Mat2 {
|
||||
pub fn main(
|
||||
scalar_out: &mut u32,
|
||||
#[spirv(push_constant)] vec_in: &Vec2,
|
||||
bool_out: &mut bool,
|
||||
bool_out: &mut u32,
|
||||
vec_out: &mut Vec2,
|
||||
) {
|
||||
*scalar_out = deep_load(&&123);
|
||||
*bool_out = vec_in == &Vec2::ZERO;
|
||||
*bool_out = (vec_in == &Vec2::ZERO) as u32;
|
||||
*vec_out = deep_transpose(&ROT90) * *vec_in;
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ fn scalar_load(r: &'static u32) -> u32 {
|
||||
const ROT90: Mat2 = const_mat2![[0.0, 1.0], [-1.0, 0.0]];
|
||||
|
||||
#[spirv(fragment)]
|
||||
pub fn main(scalar_out: &mut u32, vec_in: Vec2, bool_out: &mut bool, vec_out: &mut Vec2) {
|
||||
pub fn main(scalar_out: &mut u32, vec_in: Vec2, bool_out: &mut u32, vec_out: &mut Vec2) {
|
||||
*scalar_out = scalar_load(&123);
|
||||
*bool_out = vec_in == Vec2::ZERO;
|
||||
*bool_out = (vec_in == Vec2::ZERO) as u32;
|
||||
*vec_out = ROT90.transpose() * vec_in;
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ fn closure_user<F: FnMut(&u32, u32)>(ptr: &u32, xmax: u32, mut callback: F) {
|
||||
#[spirv(fragment)]
|
||||
pub fn main(ptr: &mut u32) {
|
||||
closure_user(ptr, 10, |ptr, i| {
|
||||
if *ptr == i { spirv_std::arch::kill(); }
|
||||
if *ptr == i {
|
||||
spirv_std::arch::kill();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,6 @@ fn has_two_decimal_digits(x: u32) -> bool {
|
||||
}
|
||||
|
||||
#[spirv(fragment)]
|
||||
pub fn main(i: u32, o: &mut bool) {
|
||||
*o = has_two_decimal_digits(i);
|
||||
pub fn main(i: u32, o: &mut u32) {
|
||||
*o = has_two_decimal_digits(i) as u32;
|
||||
}
|
||||
|
@ -82,9 +82,6 @@ pub fn vertex(
|
||||
#[spirv(viewport_mask_per_view_nv)] viewport_mask_per_view_nv: u32,
|
||||
#[spirv(warp_id_nv)] warp_id_nv: u32,
|
||||
#[spirv(warps_per_sm_nv)] warps_per_sm_nv: u32,
|
||||
// #[spirv(front_facing)] front_facing: bool, -- ICE
|
||||
// #[spirv(fully_covered_ext)] fully_covered_ext: bool, -- ICE
|
||||
// #[spirv(helper_invocation)] helper_invocation: bool, -- ICE
|
||||
// #[spirv(vertex_id)] vertex_id: u32, -- not allowed with vulkan
|
||||
) {
|
||||
}
|
||||
@ -96,6 +93,9 @@ pub fn fragment(
|
||||
#[spirv(frag_coord)] frag_coord: Vec4,
|
||||
#[spirv(frag_invocation_count_ext)] frag_invocation_count_ext: u32,
|
||||
#[spirv(frag_size_ext)] frag_size_ext: UVec2,
|
||||
#[spirv(front_facing)] front_facing: bool,
|
||||
#[spirv(fully_covered_ext)] fully_covered_ext: bool,
|
||||
#[spirv(helper_invocation)] helper_invocation: bool,
|
||||
#[spirv(layer)] layer: u32,
|
||||
#[spirv(point_coord)] point_coord: Vec2,
|
||||
#[spirv(primitive_id)] primitive_id: u32,
|
||||
|
18
tests/ui/spirv-attr/bool-inputs-err.rs
Normal file
18
tests/ui/spirv-attr/bool-inputs-err.rs
Normal file
@ -0,0 +1,18 @@
|
||||
// build-fail
|
||||
|
||||
use spirv_std as _;
|
||||
|
||||
pub struct Boolthing {
|
||||
x: u32,
|
||||
y: u32,
|
||||
b: bool,
|
||||
}
|
||||
|
||||
#[spirv(fragment)]
|
||||
pub fn fragment(
|
||||
input: bool,
|
||||
output: &mut bool,
|
||||
#[spirv(push_constant)] push: &bool,
|
||||
#[spirv(uniform)] uniform: &Boolthing,
|
||||
) {
|
||||
}
|
26
tests/ui/spirv-attr/bool-inputs-err.stderr
Normal file
26
tests/ui/spirv-attr/bool-inputs-err.stderr
Normal file
@ -0,0 +1,26 @@
|
||||
error: entrypoint parameter cannot contain a boolean
|
||||
--> $DIR/bool-inputs-err.rs:13:12
|
||||
|
|
||||
13 | input: bool,
|
||||
| ^^^^
|
||||
|
||||
error: entrypoint parameter cannot contain a boolean
|
||||
--> $DIR/bool-inputs-err.rs:14:13
|
||||
|
|
||||
14 | output: &mut bool,
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: entrypoint parameter cannot contain a boolean
|
||||
--> $DIR/bool-inputs-err.rs:15:35
|
||||
|
|
||||
15 | #[spirv(push_constant)] push: &bool,
|
||||
| ^^^^^
|
||||
|
||||
error: entrypoint parameter cannot contain a boolean
|
||||
--> $DIR/bool-inputs-err.rs:16:32
|
||||
|
|
||||
16 | #[spirv(uniform)] uniform: &Boolthing,
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
12
tests/ui/spirv-attr/bool-inputs.rs
Normal file
12
tests/ui/spirv-attr/bool-inputs.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// build-pass
|
||||
// compile-flags: -Ctarget-feature=+FragmentFullyCoveredEXT,+ext:SPV_EXT_fragment_fully_covered
|
||||
|
||||
use spirv_std as _;
|
||||
|
||||
#[spirv(fragment)]
|
||||
pub fn fragment(
|
||||
#[spirv(front_facing)] front_facing: bool,
|
||||
#[spirv(fully_covered_ext)] fully_covered_ext: bool,
|
||||
#[spirv(helper_invocation)] helper_invocation: bool,
|
||||
) {
|
||||
}
|
Loading…
Reference in New Issue
Block a user