Skip to content
This repository has been archived by the owner on Jan 29, 2025. It is now read-only.

[spv-out] support const arrays #669

Closed
wants to merge 6 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 247 additions & 9 deletions src/back/spv/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,26 @@ struct EntryPointContext {
results: Vec<ResultMember>,
}

/// An index used in an access chain can be a constant or a scalar with integer type
#[derive(Debug)]
enum Index {
Constant(u32),
Computed(Handle<crate::Expression>),
}

/// An access chain into a [crate::Expression::Composite]
struct AccessChain {
root: Handle<crate::Expression>,
chain: Vec<Index>,
}

#[derive(Default)]
struct Function {
signature: Option<Instruction>,
parameters: Vec<Instruction>,
variables: crate::FastHashMap<Handle<crate::LocalVariable>, LocalVariable>,
allocated_composites: crate::FastHashMap<Handle<crate::Expression>, Word>,
accesses_used: crate::FastHashMap<Handle<crate::Expression>, AccessChain>,
blocks: Vec<Block>,
entry_point_context: Option<EntryPointContext>,
}
Expand Down Expand Up @@ -279,6 +294,74 @@ pub struct Writer {
temp_list: Vec<Word>,
}

/// A composite expression and its type referenced through a chain of
/// access indices.
struct ReferencedComposite {
root: Handle<crate::Expression>,
ty: Handle<crate::Type>,
chain: Vec<Index>,
}

/// Extract an array type, if any, referenced by the given expression
/// and the index chain to that type.
fn get_referenced_array(
ir_module: &crate::Module,
ir_function: &crate::Function,
mut expr_handle: Handle<crate::Expression>,
) -> Option<ReferencedComposite> {
let mut chain = vec![];
let res = loop {
expr_handle = match ir_function.expressions[expr_handle] {
crate::Expression::Compose { ty, ref components } => match ir_module.types[ty].inner {
crate::TypeInner::Array { .. } => break Some((expr_handle, ty)),
crate::TypeInner::Struct { .. } => match chain.last() {
Some(&Index::Constant(i)) => components[i as usize],
_ => break Some((expr_handle, ty)),
},
_ => break None,
},
crate::Expression::Constant(h) => match ir_module.constants[h].inner {
crate::ConstantInner::Composite { ty, .. } => break Some((expr_handle, ty)),
_ => break None,
},
crate::Expression::FunctionArgument(index) => {
let ty = ir_function.arguments[index as usize].ty;
if let crate::TypeInner::Array { .. } = ir_module.types[ty].inner {
break Some((expr_handle, ty));
} else {
break None;
}
}
crate::Expression::AccessIndex { base, index } => {
chain.push(Index::Constant(index));
base
}
crate::Expression::Access { base, index } => {
chain.push(Index::Computed(index));
base
}
crate::Expression::Load { pointer } => {
let mut res = None;
if let crate::Expression::Compose { ty, .. } = ir_function.expressions[pointer] {
if let crate::TypeInner::Array { .. } = ir_module.types[ty].inner {
res = Some((expr_handle, ty));
}
}
break res;
}
_ => break None,
}
};
res.map(|(expr, ty)| {
chain.reverse();
ReferencedComposite {
root: expr,
ty,
chain,
}
})
}

impl Writer {
pub fn new(options: &Options) -> Result<Self, Error> {
let (major, minor) = options.lang_version;
Expand Down Expand Up @@ -399,6 +482,26 @@ impl Writer {
Ok(id)
}

/// Generate an instruction to allocate storage to be associated
/// with an expression of some type. Returns the `id` of the
/// pointer and the allocation instruction.
fn associate_storage(
&mut self,
types: &Arena<crate::Type>,
ty: Handle<crate::Type>,
name: &Option<String>,
init: Option<Word>,
) -> Result<(Word, Instruction), Error> {
let id = self.id_gen.next();
let pointer_type_id = self.get_pointer_id(types, ty, spirv::StorageClass::Function)?;
if let Some(name) = name {
self.debugs.push(Instruction::name(id, name));
}
let instruction =
Instruction::variable(pointer_type_id, id, spirv::StorageClass::Function, init);
Ok((id, instruction))
}

fn write_function(
&mut self,
ir_function: &crate::Function,
Expand Down Expand Up @@ -440,6 +543,43 @@ impl Writer {
results: Vec::new(),
};

// Array usage requires local storage. Add an `OpVariable` for
// every array-typed expression if it used as something other
// than an intermediate value of an access chain.

// Map each of those accesses to a root composite
for (handle, _expression) in ir_function.expressions.iter() {
if let Some(ReferencedComposite { root, ty, chain }) =
get_referenced_array(ir_module, ir_function, handle)
{
match function.allocated_composites.entry(root) {
Entry::Occupied(_) => {}
Entry::Vacant(entry) => {
log::debug!(
"Associating storage with {:?}",
ir_function.expressions[root]
);
let name = if self.flags.contains(WriterFlags::DEBUG) {
match ir_function.expressions[root] {
crate::Expression::Constant(h) => &ir_module.constants[h].name,
_ => &None,
}
} else {
&None
};
let (id, instruction) =
self.associate_storage(&ir_module.types, ty, name, None)?;
prelude.body.push(instruction);
entry.insert(id);
}
}

function
.accesses_used
.insert(handle, AccessChain { root, chain });
}
}

let mut parameter_type_ids = Vec::with_capacity(ir_function.arguments.len());
for argument in ir_function.arguments.iter() {
let class = spirv::StorageClass::Input;
Expand Down Expand Up @@ -1330,10 +1470,78 @@ impl Writer {
));
id
}
//TODO: support `crate::TypeInner::Array { .. }` ?
ref other => {
log::error!("Unable to access {:?}", other);
return Err(Error::FeatureNotImplemented("access for type"));
crate::TypeInner::Array {
base: array_base, ..
} if function.accesses_used.contains_key(&expr_handle) => {
if let Some(&AccessChain { root, ref chain }) =
function.accesses_used.get(&expr_handle)
{
if let Some(&var_id) = function.allocated_composites.get(&root) {
let ptr_id = self.id_gen.next();
let ptr_type_id = self.get_pointer_id(
&ir_module.types,
array_base,
spirv::StorageClass::Function,
)?;
let mut resolved_indices = Vec::with_capacity(chain.len());
for i in chain {
match *i {
Index::Constant(i) => {
// TODO: cache this
let index_id = self.id_gen.next();
self.write_constant_scalar(
index_id,
&crate::ScalarValue::Uint(i as u64),
4,
None,
&ir_module.types,
)?;
resolved_indices.push(index_id)
}
Index::Computed(i) => {
resolved_indices.push(self.cached[i])
}
}
}

block.body.push(Instruction::access_chain(
ptr_type_id,
ptr_id,
var_id,
&resolved_indices,
));
let id = self.id_gen.next();
block.body.push(Instruction::load(
result_type_id,
id,
ptr_id,
None,
));
id
} else {
log::error!(
"No allocated array for {:?} {:?} at root {:?}",
expr_handle,
ir_function.expressions[expr_handle],
root
);
return Err(Error::FeatureNotImplemented(
"Failed to synthesize allocation for a constant",
));
}
} else {
log::error!("No access chain despite guard");
return Err(Error::FeatureNotImplemented(
"Access chain resolution failed",
));
}
}
_ => {
// An Access that is never used other than
// as an intermediate value in another
// access chain.
// NOTE: This ID is never used, but we must return something
self.id_gen.next()
}
}
}
Expand Down Expand Up @@ -1361,6 +1569,9 @@ impl Writer {
base_id,
&[index],
));
if let Some(var_id) = function.allocated_composites.get(&expr_handle) {
block.body.push(Instruction::store(*var_id, id, None));
}
id
}
ref other => {
Expand All @@ -1371,7 +1582,16 @@ impl Writer {
}
}
crate::Expression::GlobalVariable(handle) => self.global_variables[handle.index()].id,
crate::Expression::Constant(handle) => self.constant_ids[handle.index()],
crate::Expression::Constant(handle) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the near future, when we know how each constant is used by each function - #656 - we'll be able to do this at the beginning of the function only. Maybe we can leave a small TODO pointing to this issue?

// If this is a const array, store it into the allocated variable
let composite_id = self.constant_ids[handle.index()];
if let Some(&var_id) = function.allocated_composites.get(&expr_handle) {
block
.body
.push(Instruction::store(var_id, composite_id, None));
}
composite_id
}
crate::Expression::Splat { size, value } => {
let value_id = self.cached[value];
self.temp_list.clear();
Expand Down Expand Up @@ -1400,6 +1620,11 @@ impl Writer {
id,
&self.temp_list,
));
// If the composite is an array, emit a store to write
// it into an allocated variable
if let Some(&var_id) = function.allocated_composites.get(&expr_handle) {
block.body.push(Instruction::store(var_id, id, None));
}
id
}
crate::Expression::Unary { op, expr } => {
Expand Down Expand Up @@ -1705,12 +1930,25 @@ impl Writer {
block
.body
.push(Instruction::load(result_type_id, id, pointer_id, None));

if let Some(&var_id) = function.allocated_composites.get(&expr_handle) {
block
.body
.push(Instruction::store(var_id, pointer_id, None));
}

id
}
crate::Expression::FunctionArgument(index) => match function.entry_point_context {
Some(ref context) => context.argument_ids[index as usize],
None => function.parameters[index as usize].result_id.unwrap(),
},
crate::Expression::FunctionArgument(index) => {
let arg_id = match function.entry_point_context {
Some(ref context) => context.argument_ids[index as usize],
None => function.parameters[index as usize].result_id.unwrap(),
};
if let Some(&var_id) = function.allocated_composites.get(&expr_handle) {
block.body.push(Instruction::store(var_id, arg_id, None));
}
arg_id
}
crate::Expression::Call(_function) => self.lookup_function_call[&expr_handle],
crate::Expression::As {
expr,
Expand Down