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:
Ashley Hauck 2021-11-30 14:12:13 +01:00 committed by GitHub
parent c6b75601b9
commit 2e4de94a66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 236 additions and 214 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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();
}

View File

@ -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,
}
}
}
}

View File

@ -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)
}
}

View File

@ -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"),

View File

@ -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 {

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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 |

View File

@ -14,9 +14,7 @@ fn asm_noreturn_open() {
// No active basic block without `noreturn`.
fn asm_closed() {
unsafe {
asm!(
"OpUnreachable",
);
asm!("OpUnreachable");
}
}

View File

@ -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

View File

@ -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
);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();
}
});
}
}

View File

@ -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;
}

View File

@ -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,

View 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,
) {
}

View 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

View 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,
) {
}