1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // Copyright 2018 Jacob Lifshay
4 use super::{Context, IdKind, Ids, ParsedShader, ParsedShaderFunction};
5 use shader_compiler_backend::{
6 types::TypeBuilder, BuildableBasicBlock, DetachedBuilder, Function, Module,
8 use spirv_parser::Decoration;
9 use spirv_parser::{FunctionControl, IdRef, IdResult, IdResultType, Instruction};
11 use std::collections::hash_map;
12 use std::collections::{HashMap, HashSet};
16 pub(crate) trait ParsedShaderCompile<'ctx, C: shader_compiler_backend::Context<'ctx>> {
19 frontend_context: &mut Context,
20 backend_context: &'ctx C,
21 module: &mut C::Module,
22 function_name_prefix: &str,
31 impl<T: Eq + Hash + Clone> Worklist<T> {
32 fn get_next(&mut self) -> Option<T> {
35 fn add(&mut self, v: T) -> bool {
36 if self.set.insert(v.clone()) {
45 impl<T: Eq + Hash + Clone> Default for Worklist<T> {
46 fn default() -> Self {
54 struct FunctionInstruction {
55 id_result_type: IdResultType,
57 function_control: FunctionControl,
61 struct FunctionState<'ctx, C: shader_compiler_backend::Context<'ctx>> {
62 function_instruction: FunctionInstruction,
63 instructions: Vec<Instruction>,
64 decorations: Vec<Decoration>,
65 backend_function: Cell<Option<C::Function>>,
66 backend_function_value: C::Value,
69 struct GetOrAddFunctionState<'ctx, 'tb, 'fnp, C: shader_compiler_backend::Context<'ctx>>
73 reachable_functions: HashMap<IdRef, Rc<FunctionState<'ctx, C>>>,
74 type_builder: &'tb C::TypeBuilder,
75 function_name_prefix: &'fnp str,
78 impl<'ctx, 'tb, 'fnp, C: shader_compiler_backend::Context<'ctx>>
79 GetOrAddFunctionState<'ctx, 'tb, 'fnp, C>
83 reachable_functions_worklist: &mut Vec<IdRef>,
84 ids: &mut Ids<'ctx, C>,
85 module: &mut C::Module,
87 ) -> Rc<FunctionState<'ctx, C>> {
88 match self.reachable_functions.entry(function_id) {
89 hash_map::Entry::Occupied(v) => v.get().clone(),
90 hash_map::Entry::Vacant(v) => {
91 reachable_functions_worklist.push(function_id);
92 let ParsedShaderFunction {
95 } = match &mut ids[function_id].kind {
96 IdKind::Function(function) => function.take().unwrap(),
97 _ => unreachable!("id is not a function"),
99 let function_instruction = match instructions.get(0) {
100 Some(&Instruction::Function {
103 ref function_control,
105 }) => FunctionInstruction {
108 function_control: function_control.clone(),
111 _ => unreachable!("missing OpFunction"),
113 for decoration in &decorations {
116 "unimplemented function decoration: {:?} on {}",
117 decoration, function_id
121 let function_type = match &ids[function_instruction.function_type].kind {
122 IdKind::FunctionType {
126 let return_type = match return_type {
128 Some(v) => unimplemented!(),
130 let arguments: Vec<_> = arguments
133 .map(|(argument_index, argument)| unimplemented!())
135 self.type_builder.build_function(&arguments, return_type)
137 _ => unreachable!("not a function type"),
139 let backend_function = module.add_function(
140 &format!("{}{}", self.function_name_prefix, function_id.0),
143 let backend_function_value = backend_function.as_value();
144 v.insert(Rc::new(FunctionState {
145 function_instruction,
148 backend_function: Cell::new(Some(backend_function)),
149 backend_function_value,
157 impl<'ctx, C: shader_compiler_backend::Context<'ctx>> ParsedShaderCompile<'ctx, C>
158 for ParsedShader<'ctx, C>
162 frontend_context: &mut Context,
163 backend_context: &'ctx C,
164 module: &mut C::Module,
165 function_name_prefix: &str,
174 let type_builder = backend_context.create_type_builder();
175 let mut reachable_functions_worklist = Vec::new();
176 let mut get_or_add_function_state = GetOrAddFunctionState {
177 reachable_functions: HashMap::new(),
178 type_builder: &type_builder,
179 function_name_prefix,
181 let mut get_or_add_function = |reachable_functions_worklist: &mut Vec<IdRef>,
182 ids: &mut Ids<'ctx, C>,
183 module: &mut C::Module,
184 function_id: IdRef| {
185 get_or_add_function_state.call(reachable_functions_worklist, ids, module, function_id)
187 let get_or_add_basic_block =
188 |ids: &mut Ids<'ctx, C>, label_id: IdRef, backend_function: &mut C::Function| {
189 if let IdKind::BasicBlock { basic_block, .. } = &ids[label_id].kind {
190 return basic_block.clone();
192 let buildable_basic_block =
193 backend_function.append_new_basic_block(Some(&format!("L{}", label_id.0)));
194 let basic_block = buildable_basic_block.as_basic_block();
195 ids[label_id].set_kind(IdKind::BasicBlock {
196 buildable_basic_block: Some(buildable_basic_block),
197 basic_block: basic_block.clone(),
202 &mut reachable_functions_worklist,
207 while let Some(function_id) = reachable_functions_worklist.pop() {
208 let function_state = get_or_add_function(
209 &mut reachable_functions_worklist,
214 let mut backend_function = function_state.backend_function.replace(None).unwrap();
215 enum BasicBlockState<'ctx, C: shader_compiler_backend::Context<'ctx>> {
217 builder: C::DetachedBuilder,
220 builder: C::AttachedBuilder,
221 current_label: IdRef,
224 let mut current_basic_block: BasicBlockState<C> = BasicBlockState::Detached {
225 builder: backend_context.create_builder(),
227 for instruction in &function_state.instructions {
228 match current_basic_block {
229 BasicBlockState::Attached {
232 } => match instruction {
233 _ => unimplemented!("unimplemented instruction:\n{}", instruction),
235 BasicBlockState::Detached { builder } => match instruction {
236 Instruction::Function { .. } => {
237 current_basic_block = BasicBlockState::Detached { builder };
239 Instruction::Label { id_result } => {
240 ids[id_result.0].assert_no_decorations(id_result.0);
241 get_or_add_basic_block(&mut ids, id_result.0, &mut backend_function);
242 let buildable_basic_block = match ids[id_result.0].kind {
244 ref mut buildable_basic_block,
246 } => buildable_basic_block.take().expect("duplicate OpLabel"),
249 current_basic_block = BasicBlockState::Attached {
250 builder: builder.attach(buildable_basic_block),
251 current_label: id_result.0,
254 _ => unimplemented!("unimplemented instruction:\n{}", instruction),