-
Notifications
You must be signed in to change notification settings - Fork 244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add basic support for struct DSTs #504
Add basic support for struct DSTs #504
Conversation
Wait, we shouldn't need to use // Simple single entrypoint function test.
// build-pass
use spirv_std::storage_class::{Output, Uniform};
#[spirv(fragment)]
pub fn shader(
#[spirv(descriptor_set = 0, binding = 0)] slice: Uniform<Slice<f32>>,
mut out: Output<f32>,
) {
let x = slice.data[5];
*out = x;
}
pub struct Slice<T> {
blah: f32,
data: [T],
} However, there's issues with the pointercast->GEP conversion code ( // Simple single entrypoint function test.
// build-pass
use spirv_std::storage_class::{Output, Uniform};
#[spirv(fragment)]
pub fn shader(
#[spirv(descriptor_set = 0, binding = 0)] slice: Uniform<Slice<f32>>,
mut out: Output<f32>,
) {
let x = slice.data[5];
*out = x;
}
pub struct Slice<T> {
data: [T],
} which fails with
so that needs to be fixed |
Fixed the issue in PR 510 ^ |
Sweet! Thanks 😄 I'll add a couple tests to this. |
@@ -36,8 +38,27 @@ impl<'tcx> CodegenCx<'tcx> { | |||
}; | |||
let fn_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id); | |||
let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(fn_hir_id)); | |||
const EMPTY: ArgAttribute = ArgAttribute::empty(); | |||
for (abi, arg) in fn_abi.args.iter().zip(body.params) { | |||
if let PassMode::Direct(_) = abi.mode { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this should probably be turned into a match
, the reason it's an if let
is because there's only one case
arguments.push(u32::MAX); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems pretty ad-hoc and confusing to follow (splitting it up into functions would probably help), and has some issues with it. For example, this compiletest passes, but it shouldn't (no way to get the length of the slice):
// build-pass
use spirv_std::storage_class::{Output, Uniform};
#[spirv(fragment)]
pub fn shader(
#[spirv(descriptor_set = 0, binding = 0)] slice: Uniform<&[f32]>,
mut out: Output<f32>,
) {
let x = slice[5];
*out = x;
}
#443 will simplify this, so I'll wait on that. |
Changed to a much cleaner implementation now that #443 has landed, which addresses both review points. This only adds support for DSTs that are structs whose last field is a slice. pub struct MySlice<T>([T]);
pub struct TrailingSlice<T> {
fields: u32,
last_field: [T],
}
pub struct NestedSlice<T>(MySlice<T>);
pub fn my_shader(
// supported
my_slice: MySlice<f32>,
trailing_slice: TrailingSlice<f32>,
// error!
slice: &[f32],
nested_slice: &NestedSlice<f32>,
) {/* .. */} Adding support for nested slices is a lil tricky. OpArrayLength doesn't take a chain of indices so you need to dig into the Adt's fields until you find the one whose last field is TyKind::Slice, collecting the field indices as you go. Then, loop over the field indices excluding the final one and generate OpConstants, pass the constants into OpAccessChain, and pass the result from that into the OpArrayLength with the index of the last field. I can do that in a follow up, but I wanted to keep this basic. |
match entry_fn_arg.layout.ty.kind() { | ||
TyKind::Ref(..) => var, | ||
|
||
TyKind::Ref(_, ty, _) if ty.is_sized(self.tcx.at(span), self.param_env()) => iter::once(var).chain(None), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you doing chain(None)
here (and below)?
Also, could we extract this into a function? 10 levels of indentation is giving me a headache
hir_param.ty_span, | ||
"Trait objects are not supported.", | ||
), | ||
_ => unreachable!(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is unreachable. For example, here's a compiletest that hits it:
// build-pass
#![feature(extern_types)]
use spirv_std as _;
extern "C" {
pub type Blah;
}
#[spirv(fragment)]
pub fn main(#[spirv(uniform)] x: &mut Blah) {}
@khyperia I addressed your review. Let me know if there's anything else 🙂 |
Added ArrayStride decoration to OpTypeRuntimeArray. I am getting a spurious test fail on my machine from a double define of |
Thank you for your PR! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only have a few nits/simplifications, overall I don't mind this having landed.
let id = module | ||
.entry_points | ||
.iter() | ||
.find(|inst| inst.operands.last().unwrap().unwrap_literal_string() == func) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it always operands[1]
for the name, whereas further operands are interface variables? https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpEntryPoint
@@ -159,6 +159,33 @@ fn dis_fn(src: &str, func: &str, expect: &str) { | |||
assert_str_eq(expect, &func.disassemble()) | |||
} | |||
|
|||
fn dis_entry_fn(src: &str, func: &str, expect: &str) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was confused by this so maybe dis_entry_stub
might be easier to get? A comment would also help, but the name made me think the high-level Rust fn
, not the generated stub.
let mut func = module | ||
.functions | ||
.into_iter() | ||
.find(|f| f.def_id().unwrap() == id) | ||
.unwrap(); | ||
// Compact to make IDs more stable | ||
compact_ids(&mut func); | ||
use rspirv::binary::Disassemble; | ||
assert_str_eq(expect, &func.disassemble()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part could maybe be shared between the two functions (so only the picking the function ID would be different).
// DST struct with fields before the DST member | ||
| PassMode::Pair( | ||
ArgAttributes { .. }, | ||
ArgAttributes { | ||
pointee_size: Size::ZERO, | ||
.. | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second element in a pair for &SomeUnsizedType
is always the "metadata" like usize
for anything based on slices. I assume pointee_size
is just zero for non-pointers (and I'm not sure checking pointee_size
even makes sense?).
You could probably just check that the type of the PassMode::Pair
argument is TyKind::Ref
, that should limit it to &SomeUnsizedType
.
entry_fn_abi: &FnAbi<'tcx, Ty<'tcx>>, | ||
arg_abis: &[ArgAbi<'tcx, Ty<'tcx>>], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this is a good idea, especially if we want to do something with the return type at some point.
* Add basic support for struct DSTs * Add tests * cleanup tests * Update with entry changes, address review * Address review * Update allocate_const_scalar.stderr * Add ArrayStride decoration to OpTypeRuntimeArray
Adds support for the following:
However,
asm
doesn't seem to allow&Slice
or&[T]
, and storage class inference has some issues when trying to useasm
to implement indexing into the array:produces:
Trying to do
&self.rta
or usingself
with no reference produces: