diff --git a/src/back/spv/mod.rs b/src/back/spv/mod.rs index f1522a5c4d..0c3db44b2f 100644 --- a/src/back/spv/mod.rs +++ b/src/back/spv/mod.rs @@ -72,10 +72,26 @@ impl IdGenerator { } } +/// A SPIR-V block to which we are still adding instructions. +/// +/// A `Block` represents a SPIR-V block that does not yet have a termination +/// instruction like `OpBranch` or `OpReturn`. +/// +/// The `OpLabel` that starts the block is implicit. It will be emitted based on +/// `label_id` when we write the block to a `LogicalLayout`. +/// +/// To terminate a `Block`, pass the block and the termination instruction to +/// `Function::consume`. This takes ownership of the `Block` and transforms it +/// into a `TerminatedBlock`. struct Block { label_id: Word, body: Vec, - termination: Option, +} + +/// A SPIR-V block that ends with a termination instruction. +struct TerminatedBlock { + label_id: Word, + body: Vec, } impl Block { @@ -83,7 +99,6 @@ impl Block { Block { label_id, body: Vec::new(), - termination: None, } } } @@ -109,14 +124,17 @@ struct Function { signature: Option, parameters: Vec, variables: crate::FastHashMap, LocalVariable>, - blocks: Vec, + blocks: Vec, entry_point_context: Option, } impl Function { fn consume(&mut self, mut block: Block, termination: Instruction) { - block.termination = Some(termination); - self.blocks.push(block); + block.body.push(termination); + self.blocks.push(TerminatedBlock { + label_id: block.label_id, + body: block.body, + }) } fn parameter_id(&self, index: u32) -> Word { diff --git a/src/back/spv/writer.rs b/src/back/spv/writer.rs index 223960b0e2..09f8fff8ee 100644 --- a/src/back/spv/writer.rs +++ b/src/back/spv/writer.rs @@ -47,7 +47,6 @@ impl Function { for instruction in block.body.iter() { instruction.to_words(sink); } - block.termination.as_ref().unwrap().to_words(sink); } } } @@ -2369,9 +2368,6 @@ impl Writer { let mut block = Block::new(label_id); for statement in statements { - if block.termination.is_some() { - unimplemented!("No statements are expected after block termination"); - } match *statement { crate::Statement::Emit(ref range) => { for handle in range.clone() { @@ -2576,11 +2572,15 @@ impl Writer { block = Block::new(merge_id); } crate::Statement::Break => { - block.termination = Some(Instruction::branch(loop_context.break_id.unwrap())); + function.consume(block, Instruction::branch(loop_context.break_id.unwrap())); + return Ok(()); } crate::Statement::Continue => { - block.termination = - Some(Instruction::branch(loop_context.continuing_id.unwrap())); + function.consume( + block, + Instruction::branch(loop_context.continuing_id.unwrap()), + ); + return Ok(()); } crate::Statement::Return { value: Some(value) } => { let value_id = self.cached[value]; @@ -2598,13 +2598,16 @@ impl Writer { } None => Instruction::return_value(value_id), }; - block.termination = Some(instruction); + function.consume(block, instruction); + return Ok(()); } crate::Statement::Return { value: None } => { - block.termination = Some(Instruction::return_void()); + function.consume(block, Instruction::return_void()); + return Ok(()); } crate::Statement::Kill => { - block.termination = Some(Instruction::kill()); + function.consume(block, Instruction::kill()); + return Ok(()); } crate::Statement::Barrier(flags) => { let memory_scope = if flags.contains(crate::Barrier::STORAGE) { @@ -2734,24 +2737,22 @@ impl Writer { } } - if block.termination.is_none() { - block.termination = Some(match exit_id { - Some(id) => Instruction::branch(id), - // This can happen if the last branch had all the paths - // leading out of the graph (i.e. returning). - // Or it may be the end of the function. - None => match ir_function.result { - Some(ref result) if function.entry_point_context.is_none() => { - let type_id = self.get_type_id(LookupType::Handle(result.ty))?; - let null_id = self.write_constant_null(type_id); - Instruction::return_value(null_id) - } - _ => Instruction::return_void(), - }, - }); - } + let termination = match exit_id { + Some(id) => Instruction::branch(id), + // This can happen if the last branch had all the paths + // leading out of the graph (i.e. returning). + // Or it may be the end of the function. + None => match ir_function.result { + Some(ref result) if function.entry_point_context.is_none() => { + let type_id = self.get_type_id(LookupType::Handle(result.ty))?; + let null_id = self.write_constant_null(type_id); + Instruction::return_value(null_id) + } + _ => Instruction::return_void(), + }, + }; - function.blocks.push(block); + function.consume(block, termination); Ok(()) } diff --git a/src/back/wgsl/writer.rs b/src/back/wgsl/writer.rs index 713bb3b8eb..354acd1915 100644 --- a/src/back/wgsl/writer.rs +++ b/src/back/wgsl/writer.rs @@ -528,7 +528,7 @@ impl Writer { if let Some(storage_class) = storage_class_str(class) { write!(self.out, "<{}>", storage_class)?; } - }, + } _ => { return Err(Error::Unimplemented(format!( "write_value_type {:?}",