1 use super::{ 2 help::{MipLevelCoordinate, WrappedArrayLength, WrappedConstructor, WrappedImageQuery}, 3 storage::StoreValue, 4 BackendResult, Error, Options, 5 }; 6 use crate::{ 7 back, 8 proc::{self, NameKey}, 9 valid, Handle, Module, ShaderStage, TypeInner, 10 }; 11 use std::{fmt, mem}; 12 13 const LOCATION_SEMANTIC: &str = "LOC"; 14 const SPECIAL_CBUF_TYPE: &str = "NagaConstants"; 15 const SPECIAL_CBUF_VAR: &str = "_NagaConstants"; 16 const SPECIAL_BASE_VERTEX: &str = "base_vertex"; 17 const SPECIAL_BASE_INSTANCE: &str = "base_instance"; 18 const SPECIAL_OTHER: &str = "other"; 19 20 struct EpStructMember { 21 name: String, 22 ty: Handle<crate::Type>, 23 // technically, this should always be `Some` 24 binding: Option<crate::Binding>, 25 index: u32, 26 } 27 28 /// Structure contains information required for generating 29 /// wrapped structure of all entry points arguments 30 struct EntryPointBinding { 31 /// Name of the fake EP argument that contains the struct 32 /// with all the flattened input data. 33 arg_name: String, 34 /// Generated structure name 35 ty_name: String, 36 /// Members of generated structure 37 members: Vec<EpStructMember>, 38 } 39 40 pub(super) struct EntryPointInterface { 41 /// If `Some`, the input of an entry point is gathered in a special 42 /// struct with members sorted by binding. 43 /// The `EntryPointBinding::members` array is sorted by index, 44 /// so that we can walk it in `write_ep_arguments_initialization`. 45 input: Option<EntryPointBinding>, 46 /// If `Some`, the output of an entry point is flattened. 47 /// The `EntryPointBinding::members` array is sorted by binding, 48 /// So that we can walk it in `Statement::Return` handler. 49 output: Option<EntryPointBinding>, 50 } 51 52 #[derive(Clone, Eq, PartialEq, PartialOrd, Ord)] 53 enum InterfaceKey { 54 Location(u32), 55 BuiltIn(crate::BuiltIn), 56 Other, 57 } 58 59 impl InterfaceKey { new(binding: Option<&crate::Binding>) -> Self60 fn new(binding: Option<&crate::Binding>) -> Self { 61 match binding { 62 Some(&crate::Binding::Location { location, .. }) => Self::Location(location), 63 Some(&crate::Binding::BuiltIn(bi)) => Self::BuiltIn(bi), 64 None => Self::Other, 65 } 66 } 67 } 68 69 #[derive(Copy, Clone, PartialEq)] 70 enum Io { 71 Input, 72 Output, 73 } 74 75 impl<'a, W: fmt::Write> super::Writer<'a, W> { new(out: W, options: &'a Options) -> Self76 pub fn new(out: W, options: &'a Options) -> Self { 77 Self { 78 out, 79 names: crate::FastHashMap::default(), 80 namer: proc::Namer::default(), 81 options, 82 entry_point_io: Vec::new(), 83 named_expressions: crate::NamedExpressions::default(), 84 wrapped: super::Wrapped::default(), 85 temp_access_chain: Vec::new(), 86 } 87 } 88 reset(&mut self, module: &Module)89 fn reset(&mut self, module: &Module) { 90 self.names.clear(); 91 self.namer 92 .reset(module, super::keywords::RESERVED, &[], &mut self.names); 93 self.entry_point_io.clear(); 94 self.named_expressions.clear(); 95 self.wrapped.clear(); 96 } 97 write( &mut self, module: &Module, module_info: &valid::ModuleInfo, ) -> Result<super::ReflectionInfo, Error>98 pub fn write( 99 &mut self, 100 module: &Module, 101 module_info: &valid::ModuleInfo, 102 ) -> Result<super::ReflectionInfo, Error> { 103 self.reset(module); 104 105 // Write special constants, if needed 106 if let Some(ref bt) = self.options.special_constants_binding { 107 writeln!(self.out, "struct {} {{", SPECIAL_CBUF_TYPE)?; 108 writeln!(self.out, "{}int {};", back::INDENT, SPECIAL_BASE_VERTEX)?; 109 writeln!(self.out, "{}int {};", back::INDENT, SPECIAL_BASE_INSTANCE)?; 110 writeln!(self.out, "{}uint {};", back::INDENT, SPECIAL_OTHER)?; 111 writeln!(self.out, "}};")?; 112 write!( 113 self.out, 114 "ConstantBuffer<{}> {}: register(b{}", 115 SPECIAL_CBUF_TYPE, SPECIAL_CBUF_VAR, bt.register 116 )?; 117 if bt.space != 0 { 118 write!(self.out, ", space{}", bt.space)?; 119 } 120 writeln!(self.out, ");")?; 121 } 122 123 // Write all constants 124 // For example, input wgsl shader: 125 // ```wgsl 126 // let c_scale: f32 = 1.2; 127 // return VertexOutput(uv, vec4<f32>(c_scale * pos, 0.0, 1.0)); 128 // ``` 129 // 130 // Output shader: 131 // ```hlsl 132 // static const float c_scale = 1.2; 133 // const VertexOutput vertexoutput1 = { vertexinput.uv3, float4((c_scale * vertexinput.pos1), 0.0, 1.0) }; 134 // ``` 135 // 136 // If we remove `write_global_constant` `c_scale` will be inlined. 137 for (handle, constant) in module.constants.iter() { 138 if constant.name.is_some() { 139 self.write_global_constant(module, &constant.inner, handle)?; 140 } 141 } 142 143 // Extra newline for readability 144 writeln!(self.out)?; 145 146 // Save all entry point output types 147 let ep_results = module 148 .entry_points 149 .iter() 150 .map(|ep| (ep.stage, ep.function.result.clone())) 151 .collect::<Vec<(ShaderStage, Option<crate::FunctionResult>)>>(); 152 153 // Write all structs 154 for (handle, ty) in module.types.iter() { 155 if let TypeInner::Struct { ref members, .. } = ty.inner { 156 if let Some(member) = members.last() { 157 if let TypeInner::Array { 158 size: crate::ArraySize::Dynamic, 159 .. 160 } = module.types[member.ty].inner 161 { 162 // unsized arrays can only be in storage buffers, for which we use `ByteAddressBuffer` anyway. 163 continue; 164 } 165 } 166 167 let ep_result = ep_results.iter().find(|e| { 168 if let Some(ref result) = e.1 { 169 result.ty == handle 170 } else { 171 false 172 } 173 }); 174 175 self.write_struct( 176 module, 177 handle, 178 members, 179 ep_result.map(|r| (r.0, Io::Output)), 180 )?; 181 writeln!(self.out)?; 182 } 183 } 184 185 // Write all globals 186 for (ty, _) in module.global_variables.iter() { 187 self.write_global(module, ty)?; 188 } 189 190 if !module.global_variables.is_empty() { 191 // Add extra newline for readability 192 writeln!(self.out)?; 193 } 194 195 // Write all entry points wrapped structs 196 for ep in module.entry_points.iter() { 197 let ep_io = self.write_ep_interface(module, &ep.function, ep.stage, &ep.name)?; 198 self.entry_point_io.push(ep_io); 199 } 200 201 // Write all regular functions 202 for (handle, function) in module.functions.iter() { 203 let info = &module_info[handle]; 204 205 // Check if all of the globals are accessible 206 if !self.options.fake_missing_bindings { 207 if let Some((var_handle, _)) = 208 module 209 .global_variables 210 .iter() 211 .find(|&(var_handle, var)| match var.binding { 212 Some(ref binding) if !info[var_handle].is_empty() => { 213 self.options.resolve_resource_binding(binding).is_err() 214 } 215 _ => false, 216 }) 217 { 218 log::info!( 219 "Skipping function {:?} (name {:?}) because global {:?} is inaccessible", 220 handle, 221 function.name, 222 var_handle 223 ); 224 continue; 225 } 226 } 227 228 let ctx = back::FunctionCtx { 229 ty: back::FunctionType::Function(handle), 230 info, 231 expressions: &function.expressions, 232 named_expressions: &function.named_expressions, 233 }; 234 let name = self.names[&NameKey::Function(handle)].clone(); 235 236 // Write wrapped function for `Expression::ImageQuery` and `Expressions::ArrayLength` 237 // before writing all statements and expressions. 238 self.write_wrapped_functions(module, &ctx)?; 239 240 self.write_function(module, name.as_str(), function, &ctx)?; 241 242 writeln!(self.out)?; 243 } 244 245 let mut entry_point_names = Vec::with_capacity(module.entry_points.len()); 246 247 // Write all entry points 248 for (index, ep) in module.entry_points.iter().enumerate() { 249 let info = module_info.get_entry_point(index); 250 251 if !self.options.fake_missing_bindings { 252 let mut ep_error = None; 253 for (var_handle, var) in module.global_variables.iter() { 254 match var.binding { 255 Some(ref binding) if !info[var_handle].is_empty() => { 256 if let Err(err) = self.options.resolve_resource_binding(binding) { 257 ep_error = Some(err); 258 break; 259 } 260 } 261 _ => {} 262 } 263 } 264 if let Some(err) = ep_error { 265 entry_point_names.push(Err(err)); 266 continue; 267 } 268 } 269 270 let ctx = back::FunctionCtx { 271 ty: back::FunctionType::EntryPoint(index as u16), 272 info, 273 expressions: &ep.function.expressions, 274 named_expressions: &ep.function.named_expressions, 275 }; 276 277 // Write wrapped function for `Expression::ImageQuery` and `Expressions::ArrayLength` 278 // before writing all statements and expressions. 279 self.write_wrapped_functions(module, &ctx)?; 280 281 if ep.stage == ShaderStage::Compute { 282 // HLSL is calling workgroup size "num threads" 283 let num_threads = ep.workgroup_size; 284 writeln!( 285 self.out, 286 "[numthreads({}, {}, {})]", 287 num_threads[0], num_threads[1], num_threads[2] 288 )?; 289 } 290 291 let name = self.names[&NameKey::EntryPoint(index as u16)].clone(); 292 self.write_function(module, &name, &ep.function, &ctx)?; 293 294 if index < module.entry_points.len() - 1 { 295 writeln!(self.out)?; 296 } 297 298 entry_point_names.push(Ok(name)); 299 } 300 301 Ok(super::ReflectionInfo { entry_point_names }) 302 } 303 304 //TODO: we could force fragment outputs to always go through `entry_point_io.output` path 305 // if they are struct, so that the `stage` argument here could be omitted. write_semantic( &mut self, binding: &crate::Binding, stage: Option<(ShaderStage, Io)>, ) -> BackendResult306 fn write_semantic( 307 &mut self, 308 binding: &crate::Binding, 309 stage: Option<(ShaderStage, Io)>, 310 ) -> BackendResult { 311 match *binding { 312 crate::Binding::BuiltIn(builtin) => { 313 let builtin_str = builtin.to_hlsl_str()?; 314 write!(self.out, " : {}", builtin_str)?; 315 } 316 crate::Binding::Location { location, .. } => { 317 if stage == Some((crate::ShaderStage::Fragment, Io::Output)) { 318 write!(self.out, " : SV_Target{}", location)?; 319 } else { 320 write!(self.out, " : {}{}", LOCATION_SEMANTIC, location)?; 321 } 322 } 323 } 324 325 Ok(()) 326 } 327 write_interface_struct( &mut self, module: &Module, shader_stage: (ShaderStage, Io), struct_name: String, mut members: Vec<EpStructMember>, ) -> Result<EntryPointBinding, Error>328 fn write_interface_struct( 329 &mut self, 330 module: &Module, 331 shader_stage: (ShaderStage, Io), 332 struct_name: String, 333 mut members: Vec<EpStructMember>, 334 ) -> Result<EntryPointBinding, Error> { 335 // Sort the members so that first come the user-defined varyings 336 // in ascending locations, and then built-ins. This allows VS and FS 337 // interfaces to match with regards to order. 338 members.sort_by_key(|m| InterfaceKey::new(m.binding.as_ref())); 339 340 write!(self.out, "struct {}", struct_name)?; 341 writeln!(self.out, " {{")?; 342 for m in members.iter() { 343 write!(self.out, "{}", back::INDENT)?; 344 self.write_type(module, m.ty)?; 345 write!(self.out, " {}", &m.name)?; 346 if let Some(ref binding) = m.binding { 347 self.write_semantic(binding, Some(shader_stage))?; 348 } 349 writeln!(self.out, ";")?; 350 } 351 writeln!(self.out, "}};")?; 352 writeln!(self.out)?; 353 354 match shader_stage.1 { 355 Io::Input => { 356 // bring back the original order 357 members.sort_by_key(|m| m.index); 358 } 359 Io::Output => { 360 // keep it sorted by binding 361 } 362 } 363 364 Ok(EntryPointBinding { 365 arg_name: self.namer.call(struct_name.to_lowercase().as_str()), 366 ty_name: struct_name, 367 members, 368 }) 369 } 370 371 /// Flatten all entry point arguments into a single struct. 372 /// This is needed since we need to re-order them: first placing user locations, 373 /// then built-ins. write_ep_input_struct( &mut self, module: &Module, func: &crate::Function, stage: ShaderStage, entry_point_name: &str, ) -> Result<EntryPointBinding, Error>374 fn write_ep_input_struct( 375 &mut self, 376 module: &Module, 377 func: &crate::Function, 378 stage: ShaderStage, 379 entry_point_name: &str, 380 ) -> Result<EntryPointBinding, Error> { 381 let struct_name = format!("{:?}Input_{}", stage, entry_point_name); 382 383 let mut fake_members = Vec::new(); 384 for arg in func.arguments.iter() { 385 match module.types[arg.ty].inner { 386 TypeInner::Struct { ref members, .. } => { 387 for member in members.iter() { 388 let name = self.namer.call_or(&member.name, "member"); 389 let index = fake_members.len() as u32; 390 fake_members.push(EpStructMember { 391 name, 392 ty: member.ty, 393 binding: member.binding.clone(), 394 index, 395 }); 396 } 397 } 398 _ => { 399 let member_name = self.namer.call_or(&arg.name, "member"); 400 let index = fake_members.len() as u32; 401 fake_members.push(EpStructMember { 402 name: member_name, 403 ty: arg.ty, 404 binding: arg.binding.clone(), 405 index, 406 }); 407 } 408 } 409 } 410 411 self.write_interface_struct(module, (stage, Io::Input), struct_name, fake_members) 412 } 413 414 /// Flatten all entry point results into a single struct. 415 /// This is needed since we need to re-order them: first placing user locations, 416 /// then built-ins. write_ep_output_struct( &mut self, module: &Module, result: &crate::FunctionResult, stage: ShaderStage, entry_point_name: &str, ) -> Result<EntryPointBinding, Error>417 fn write_ep_output_struct( 418 &mut self, 419 module: &Module, 420 result: &crate::FunctionResult, 421 stage: ShaderStage, 422 entry_point_name: &str, 423 ) -> Result<EntryPointBinding, Error> { 424 let struct_name = format!("{:?}Output_{}", stage, entry_point_name); 425 426 let mut fake_members = Vec::new(); 427 let empty = []; 428 let members = match module.types[result.ty].inner { 429 TypeInner::Struct { ref members, .. } => members, 430 ref other => { 431 log::error!("Unexpected {:?} output type without a binding", other); 432 &empty[..] 433 } 434 }; 435 436 for member in members.iter() { 437 let member_name = self.namer.call_or(&member.name, "member"); 438 let index = fake_members.len() as u32; 439 fake_members.push(EpStructMember { 440 name: member_name, 441 ty: member.ty, 442 binding: member.binding.clone(), 443 index, 444 }); 445 } 446 447 self.write_interface_struct(module, (stage, Io::Output), struct_name, fake_members) 448 } 449 450 /// Writes special interface structures for an entry point. The special structures have 451 /// all the fields flattened into them and sorted by binding. They are only needed for 452 /// VS outputs and FS inputs, so that these interfaces match. write_ep_interface( &mut self, module: &Module, func: &crate::Function, stage: ShaderStage, ep_name: &str, ) -> Result<EntryPointInterface, Error>453 fn write_ep_interface( 454 &mut self, 455 module: &Module, 456 func: &crate::Function, 457 stage: ShaderStage, 458 ep_name: &str, 459 ) -> Result<EntryPointInterface, Error> { 460 Ok(EntryPointInterface { 461 input: if !func.arguments.is_empty() && stage == ShaderStage::Fragment { 462 Some(self.write_ep_input_struct(module, func, stage, ep_name)?) 463 } else { 464 None 465 }, 466 output: match func.result { 467 Some(ref fr) if fr.binding.is_none() && stage == ShaderStage::Vertex => { 468 Some(self.write_ep_output_struct(module, fr, stage, ep_name)?) 469 } 470 _ => None, 471 }, 472 }) 473 } 474 475 /// Write an entry point preface that initializes the arguments as specified in IR. write_ep_arguments_initialization( &mut self, module: &Module, func: &crate::Function, ep_index: u16, ) -> BackendResult476 fn write_ep_arguments_initialization( 477 &mut self, 478 module: &Module, 479 func: &crate::Function, 480 ep_index: u16, 481 ) -> BackendResult { 482 let ep_input = match self.entry_point_io[ep_index as usize].input.take() { 483 Some(ep_input) => ep_input, 484 None => return Ok(()), 485 }; 486 let mut fake_iter = ep_input.members.iter(); 487 for (arg_index, arg) in func.arguments.iter().enumerate() { 488 write!(self.out, "{}", back::INDENT)?; 489 self.write_type(module, arg.ty)?; 490 let arg_name = &self.names[&NameKey::EntryPointArgument(ep_index, arg_index as u32)]; 491 write!(self.out, " {}", arg_name)?; 492 match module.types[arg.ty].inner { 493 TypeInner::Array { size, .. } => { 494 self.write_array_size(module, size)?; 495 let fake_member = fake_iter.next().unwrap(); 496 writeln!(self.out, " = {}.{};", ep_input.arg_name, fake_member.name)?; 497 } 498 TypeInner::Struct { ref members, .. } => { 499 write!(self.out, " = {{ ")?; 500 for index in 0..members.len() { 501 if index != 0 { 502 write!(self.out, ", ")?; 503 } 504 let fake_member = fake_iter.next().unwrap(); 505 write!(self.out, "{}.{}", ep_input.arg_name, fake_member.name)?; 506 } 507 writeln!(self.out, " }};")?; 508 } 509 _ => { 510 let fake_member = fake_iter.next().unwrap(); 511 writeln!(self.out, " = {}.{};", ep_input.arg_name, fake_member.name)?; 512 } 513 } 514 } 515 assert!(fake_iter.next().is_none()); 516 Ok(()) 517 } 518 519 /// Helper method used to write global variables 520 /// # Notes 521 /// Always adds a newline write_global( &mut self, module: &Module, handle: Handle<crate::GlobalVariable>, ) -> BackendResult522 fn write_global( 523 &mut self, 524 module: &Module, 525 handle: Handle<crate::GlobalVariable>, 526 ) -> BackendResult { 527 let global = &module.global_variables[handle]; 528 let inner = &module.types[global.ty].inner; 529 530 if let Some(ref binding) = global.binding { 531 if let Err(err) = self.options.resolve_resource_binding(binding) { 532 log::info!( 533 "Skipping global {:?} (name {:?}) for being inaccessible: {}", 534 handle, 535 global.name, 536 err, 537 ); 538 return Ok(()); 539 } 540 } 541 542 // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-register 543 let register_ty = match global.class { 544 crate::StorageClass::Function => unreachable!("Function storage class"), 545 crate::StorageClass::Private => { 546 write!(self.out, "static ")?; 547 self.write_type(module, global.ty)?; 548 "" 549 } 550 crate::StorageClass::WorkGroup => { 551 write!(self.out, "groupshared ")?; 552 self.write_type(module, global.ty)?; 553 "" 554 } 555 crate::StorageClass::Uniform => { 556 // constant buffer declarations are expected to be inlined, e.g. 557 // `cbuffer foo: register(b0) { field1: type1; }` 558 write!(self.out, "cbuffer")?; 559 "b" 560 } 561 crate::StorageClass::Storage { access } => { 562 let (prefix, register) = if access.contains(crate::StorageAccess::STORE) { 563 ("RW", "u") 564 } else { 565 ("", "t") 566 }; 567 write!(self.out, "{}ByteAddressBuffer", prefix)?; 568 register 569 } 570 crate::StorageClass::Handle => { 571 let register = match *inner { 572 TypeInner::Sampler { .. } => "s", 573 // all storage textures are UAV, unconditionally 574 TypeInner::Image { 575 class: crate::ImageClass::Storage { .. }, 576 .. 577 } => "u", 578 _ => "t", 579 }; 580 self.write_type(module, global.ty)?; 581 register 582 } 583 crate::StorageClass::PushConstant => unimplemented!("Push constants"), 584 }; 585 586 let name = &self.names[&NameKey::GlobalVariable(handle)]; 587 write!(self.out, " {}", name)?; 588 if let TypeInner::Array { size, .. } = module.types[global.ty].inner { 589 self.write_array_size(module, size)?; 590 } 591 592 if let Some(ref binding) = global.binding { 593 // this was already resolved earlier when we started evaluating an entry point. 594 let bt = self.options.resolve_resource_binding(binding).unwrap(); 595 write!(self.out, " : register({}{}", register_ty, bt.register)?; 596 if bt.space != 0 { 597 write!(self.out, ", space{}", bt.space)?; 598 } 599 write!(self.out, ")")?; 600 } else if global.class == crate::StorageClass::Private { 601 write!(self.out, " = ")?; 602 if let Some(init) = global.init { 603 self.write_constant(module, init)?; 604 } else { 605 self.write_default_init(module, global.ty)?; 606 } 607 } 608 609 if global.class == crate::StorageClass::Uniform { 610 write!(self.out, " {{ ")?; 611 self.write_type(module, global.ty)?; 612 let name = &self.names[&NameKey::GlobalVariable(handle)]; 613 writeln!(self.out, " {}; }}", name)?; 614 } else { 615 writeln!(self.out, ";")?; 616 } 617 618 Ok(()) 619 } 620 621 /// Helper method used to write global constants 622 /// 623 /// # Notes 624 /// Ends in a newline write_global_constant( &mut self, module: &Module, inner: &crate::ConstantInner, handle: Handle<crate::Constant>, ) -> BackendResult625 fn write_global_constant( 626 &mut self, 627 module: &Module, 628 inner: &crate::ConstantInner, 629 handle: Handle<crate::Constant>, 630 ) -> BackendResult { 631 write!(self.out, "static const ")?; 632 match *inner { 633 crate::ConstantInner::Scalar { 634 width: _, 635 ref value, 636 } => { 637 // Write type 638 let ty_str = match *value { 639 crate::ScalarValue::Sint(_) => "int", 640 crate::ScalarValue::Uint(_) => "uint", 641 crate::ScalarValue::Float(_) => "float", 642 crate::ScalarValue::Bool(_) => "bool", 643 }; 644 let name = &self.names[&NameKey::Constant(handle)]; 645 write!(self.out, "{} {} = ", ty_str, name)?; 646 647 // Second match required to avoid heap allocation by `format!()` 648 match *value { 649 crate::ScalarValue::Sint(value) => write!(self.out, "{}", value)?, 650 crate::ScalarValue::Uint(value) => write!(self.out, "{}", value)?, 651 crate::ScalarValue::Float(value) => { 652 // Floats are written using `Debug` instead of `Display` because it always appends the 653 // decimal part even it's zero 654 write!(self.out, "{:?}", value)? 655 } 656 crate::ScalarValue::Bool(value) => write!(self.out, "{}", value)?, 657 }; 658 } 659 crate::ConstantInner::Composite { ty, ref components } => { 660 self.write_type(module, ty)?; 661 let name = &self.names[&NameKey::Constant(handle)]; 662 write!(self.out, " {} = ", name)?; 663 self.write_composite_constant(module, ty, components)?; 664 } 665 } 666 writeln!(self.out, ";")?; 667 Ok(()) 668 } 669 write_array_size( &mut self, module: &Module, size: crate::ArraySize, ) -> BackendResult670 pub(super) fn write_array_size( 671 &mut self, 672 module: &Module, 673 size: crate::ArraySize, 674 ) -> BackendResult { 675 write!(self.out, "[")?; 676 677 // Write the array size 678 // Writes nothing if `ArraySize::Dynamic` 679 // Panics if `ArraySize::Constant` has a constant that isn't an sint or uint 680 match size { 681 crate::ArraySize::Constant(const_handle) => { 682 let size = module.constants[const_handle].to_array_length().unwrap(); 683 write!(self.out, "{}", size)?; 684 } 685 crate::ArraySize::Dynamic => unreachable!(), 686 } 687 688 write!(self.out, "]")?; 689 Ok(()) 690 } 691 692 /// Helper method used to write structs 693 /// 694 /// # Notes 695 /// Ends in a newline write_struct( &mut self, module: &Module, handle: Handle<crate::Type>, members: &[crate::StructMember], shader_stage: Option<(ShaderStage, Io)>, ) -> BackendResult696 fn write_struct( 697 &mut self, 698 module: &Module, 699 handle: Handle<crate::Type>, 700 members: &[crate::StructMember], 701 shader_stage: Option<(ShaderStage, Io)>, 702 ) -> BackendResult { 703 // Write struct name 704 let struct_name = &self.names[&NameKey::Type(handle)]; 705 writeln!(self.out, "struct {} {{", struct_name)?; 706 707 for (index, member) in members.iter().enumerate() { 708 // The indentation is only for readability 709 write!(self.out, "{}", back::INDENT)?; 710 711 match module.types[member.ty].inner { 712 TypeInner::Array { 713 base, 714 size, 715 stride: _, 716 } => { 717 // HLSL arrays are written as `type name[size]` 718 if let TypeInner::Matrix { .. } = module.types[base].inner { 719 write!(self.out, "row_major ")?; 720 } 721 self.write_type(module, base)?; 722 // Write `name` 723 write!( 724 self.out, 725 " {}", 726 &self.names[&NameKey::StructMember(handle, index as u32)] 727 )?; 728 // Write [size] 729 self.write_array_size(module, size)?; 730 } 731 _ => { 732 // Write interpolation modifier before type 733 if let Some(crate::Binding::Location { 734 interpolation, 735 sampling, 736 .. 737 }) = member.binding 738 { 739 if let Some(interpolation) = interpolation { 740 write!(self.out, "{} ", interpolation.to_hlsl_str())? 741 } 742 743 if let Some(sampling) = sampling { 744 if let Some(string) = sampling.to_hlsl_str() { 745 write!(self.out, "{} ", string)? 746 } 747 } 748 } 749 750 if let TypeInner::Matrix { .. } = module.types[member.ty].inner { 751 write!(self.out, "row_major ")?; 752 } 753 754 // Write the member type and name 755 self.write_type(module, member.ty)?; 756 write!( 757 self.out, 758 " {}", 759 &self.names[&NameKey::StructMember(handle, index as u32)] 760 )?; 761 } 762 } 763 764 if let Some(ref binding) = member.binding { 765 self.write_semantic(binding, shader_stage)?; 766 }; 767 writeln!(self.out, ";")?; 768 } 769 770 writeln!(self.out, "}};")?; 771 Ok(()) 772 } 773 774 /// Helper method used to write non image/sampler types 775 /// 776 /// # Notes 777 /// Adds no trailing or leading whitespace write_type(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult778 pub(super) fn write_type(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult { 779 let inner = &module.types[ty].inner; 780 match *inner { 781 TypeInner::Struct { .. } => write!(self.out, "{}", self.names[&NameKey::Type(ty)])?, 782 // hlsl array has the size separated from the base type 783 TypeInner::Array { base, .. } => self.write_type(module, base)?, 784 ref other => self.write_value_type(module, other)?, 785 } 786 787 Ok(()) 788 } 789 790 /// Helper method used to write value types 791 /// 792 /// # Notes 793 /// Adds no trailing or leading whitespace write_value_type(&mut self, module: &Module, inner: &TypeInner) -> BackendResult794 pub(super) fn write_value_type(&mut self, module: &Module, inner: &TypeInner) -> BackendResult { 795 match *inner { 796 TypeInner::Scalar { kind, width } | TypeInner::Atomic { kind, width } => { 797 write!(self.out, "{}", kind.to_hlsl_str(width)?)?; 798 } 799 TypeInner::Vector { size, kind, width } => { 800 write!( 801 self.out, 802 "{}{}", 803 kind.to_hlsl_str(width)?, 804 back::vector_size_str(size) 805 )?; 806 } 807 TypeInner::Matrix { 808 columns, 809 rows, 810 width, 811 } => { 812 // The IR supports only float matrix 813 // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-matrix 814 815 // Because of the implicit transpose all matrices have in HLSL, we need to tranpose the size as well. 816 write!( 817 self.out, 818 "{}{}x{}", 819 crate::ScalarKind::Float.to_hlsl_str(width)?, 820 back::vector_size_str(rows), 821 back::vector_size_str(columns), 822 )?; 823 } 824 TypeInner::Image { 825 dim, 826 arrayed, 827 class, 828 } => { 829 self.write_image_type(dim, arrayed, class)?; 830 } 831 TypeInner::Sampler { comparison } => { 832 let sampler = if comparison { 833 "SamplerComparisonState" 834 } else { 835 "SamplerState" 836 }; 837 write!(self.out, "{}", sampler)?; 838 } 839 // HLSL arrays are written as `type name[size]` 840 // Current code is written arrays only as `[size]` 841 // Base `type` and `name` should be written outside 842 TypeInner::Array { size, .. } => { 843 self.write_array_size(module, size)?; 844 } 845 _ => { 846 return Err(Error::Unimplemented(format!( 847 "write_value_type {:?}", 848 inner 849 ))) 850 } 851 } 852 853 Ok(()) 854 } 855 856 /// Helper method used to write functions 857 /// # Notes 858 /// Ends in a newline write_function( &mut self, module: &Module, name: &str, func: &crate::Function, func_ctx: &back::FunctionCtx<'_>, ) -> BackendResult859 fn write_function( 860 &mut self, 861 module: &Module, 862 name: &str, 863 func: &crate::Function, 864 func_ctx: &back::FunctionCtx<'_>, 865 ) -> BackendResult { 866 // Function Declaration Syntax - https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-function-syntax 867 if let Some(ref result) = func.result { 868 match func_ctx.ty { 869 back::FunctionType::Function(_) => { 870 self.write_type(module, result.ty)?; 871 } 872 back::FunctionType::EntryPoint(index) => { 873 if let Some(ref ep_output) = self.entry_point_io[index as usize].output { 874 write!(self.out, "{}", ep_output.ty_name)?; 875 } else { 876 self.write_type(module, result.ty)?; 877 } 878 } 879 } 880 } else { 881 write!(self.out, "void")?; 882 } 883 884 // Write function name 885 write!(self.out, " {}(", name)?; 886 887 // Write function arguments for non entry point functions 888 match func_ctx.ty { 889 back::FunctionType::Function(handle) => { 890 for (index, arg) in func.arguments.iter().enumerate() { 891 if index != 0 { 892 write!(self.out, ", ")?; 893 } 894 // Write argument type 895 let arg_ty = match module.types[arg.ty].inner { 896 // pointers in function arguments are expected and resolve to `inout` 897 TypeInner::Pointer { base, .. } => { 898 //TODO: can we narrow this down to just `in` when possible? 899 write!(self.out, "inout ")?; 900 base 901 } 902 _ => arg.ty, 903 }; 904 self.write_type(module, arg_ty)?; 905 906 let argument_name = 907 &self.names[&NameKey::FunctionArgument(handle, index as u32)]; 908 909 // Write argument name. Space is important. 910 write!(self.out, " {}", argument_name)?; 911 if let TypeInner::Array { size, .. } = module.types[arg.ty].inner { 912 self.write_array_size(module, size)?; 913 } 914 } 915 } 916 back::FunctionType::EntryPoint(ep_index) => { 917 if let Some(ref ep_input) = self.entry_point_io[ep_index as usize].input { 918 write!(self.out, "{} {}", ep_input.ty_name, ep_input.arg_name,)?; 919 } else { 920 let stage = module.entry_points[ep_index as usize].stage; 921 for (index, arg) in func.arguments.iter().enumerate() { 922 if index != 0 { 923 write!(self.out, ", ")?; 924 } 925 self.write_type(module, arg.ty)?; 926 927 let argument_name = 928 &self.names[&NameKey::EntryPointArgument(ep_index, index as u32)]; 929 930 write!(self.out, " {}", argument_name)?; 931 if let TypeInner::Array { size, .. } = module.types[arg.ty].inner { 932 self.write_array_size(module, size)?; 933 } 934 935 if let Some(ref binding) = arg.binding { 936 self.write_semantic(binding, Some((stage, Io::Input)))?; 937 } 938 } 939 } 940 } 941 } 942 // Ends of arguments 943 write!(self.out, ")")?; 944 945 // Write semantic if it present 946 if let back::FunctionType::EntryPoint(index) = func_ctx.ty { 947 let stage = module.entry_points[index as usize].stage; 948 if let Some(crate::FunctionResult { 949 binding: Some(ref binding), 950 .. 951 }) = func.result 952 { 953 self.write_semantic(binding, Some((stage, Io::Output)))?; 954 } 955 } 956 957 // Function body start 958 writeln!(self.out)?; 959 writeln!(self.out, "{{")?; 960 961 if let back::FunctionType::EntryPoint(index) = func_ctx.ty { 962 self.write_ep_arguments_initialization(module, func, index)?; 963 } 964 965 // Write function local variables 966 for (handle, local) in func.local_variables.iter() { 967 // Write indentation (only for readability) 968 write!(self.out, "{}", back::INDENT)?; 969 970 // Write the local name 971 // The leading space is important 972 self.write_type(module, local.ty)?; 973 write!(self.out, " {}", self.names[&func_ctx.name_key(handle)])?; 974 // Write size for array type 975 if let TypeInner::Array { size, .. } = module.types[local.ty].inner { 976 self.write_array_size(module, size)?; 977 } 978 979 write!(self.out, " = ")?; 980 // Write the local initializer if needed 981 if let Some(init) = local.init { 982 // Put the equal signal only if there's a initializer 983 // The leading and trailing spaces aren't needed but help with readability 984 985 // Write the constant 986 // `write_constant` adds no trailing or leading space/newline 987 self.write_constant(module, init)?; 988 } else { 989 // Zero initialize local variables 990 self.write_default_init(module, local.ty)?; 991 } 992 993 // Finish the local with `;` and add a newline (only for readability) 994 writeln!(self.out, ";")? 995 } 996 997 if !func.local_variables.is_empty() { 998 writeln!(self.out)?; 999 } 1000 1001 // Write the function body (statement list) 1002 for sta in func.body.iter() { 1003 // The indentation should always be 1 when writing the function body 1004 self.write_stmt(module, sta, func_ctx, back::Level(1))?; 1005 } 1006 1007 writeln!(self.out, "}}")?; 1008 1009 self.named_expressions.clear(); 1010 1011 Ok(()) 1012 } 1013 1014 /// Helper method used to write statements 1015 /// 1016 /// # Notes 1017 /// Always adds a newline write_stmt( &mut self, module: &Module, stmt: &crate::Statement, func_ctx: &back::FunctionCtx<'_>, level: back::Level, ) -> BackendResult1018 fn write_stmt( 1019 &mut self, 1020 module: &Module, 1021 stmt: &crate::Statement, 1022 func_ctx: &back::FunctionCtx<'_>, 1023 level: back::Level, 1024 ) -> BackendResult { 1025 use crate::Statement; 1026 1027 match *stmt { 1028 Statement::Emit(ref range) => { 1029 for handle in range.clone() { 1030 let info = &func_ctx.info[handle]; 1031 let ptr_class = info.ty.inner_with(&module.types).pointer_class(); 1032 let expr_name = if ptr_class.is_some() { 1033 // HLSL can't save a pointer-valued expression in a variable, 1034 // but we shouldn't ever need to: they should never be named expressions, 1035 // and none of the expression types flagged by bake_ref_count can be pointer-valued. 1036 None 1037 } else if let Some(name) = func_ctx.named_expressions.get(&handle) { 1038 // Front end provides names for all variables at the start of writing. 1039 // But we write them to step by step. We need to recache them 1040 // Otherwise, we could accidentally write variable name instead of full expression. 1041 // Also, we use sanitized names! It defense backend from generating variable with name from reserved keywords. 1042 Some(self.namer.call(name)) 1043 } else { 1044 let min_ref_count = func_ctx.expressions[handle].bake_ref_count(); 1045 if min_ref_count <= info.ref_count { 1046 Some(format!("_expr{}", handle.index())) 1047 } else { 1048 None 1049 } 1050 }; 1051 1052 if let Some(name) = expr_name { 1053 write!(self.out, "{}", level)?; 1054 self.write_named_expr(module, handle, name, func_ctx)?; 1055 } 1056 } 1057 } 1058 // TODO: copy-paste from glsl-out 1059 Statement::Block(ref block) => { 1060 write!(self.out, "{}", level)?; 1061 writeln!(self.out, "{{")?; 1062 for sta in block.iter() { 1063 // Increase the indentation to help with readability 1064 self.write_stmt(module, sta, func_ctx, level.next())? 1065 } 1066 writeln!(self.out, "{}}}", level)? 1067 } 1068 // TODO: copy-paste from glsl-out 1069 Statement::If { 1070 condition, 1071 ref accept, 1072 ref reject, 1073 } => { 1074 write!(self.out, "{}", level)?; 1075 write!(self.out, "if (")?; 1076 self.write_expr(module, condition, func_ctx)?; 1077 writeln!(self.out, ") {{")?; 1078 1079 let l2 = level.next(); 1080 for sta in accept { 1081 // Increase indentation to help with readability 1082 self.write_stmt(module, sta, func_ctx, l2)?; 1083 } 1084 1085 // If there are no statements in the reject block we skip writing it 1086 // This is only for readability 1087 if !reject.is_empty() { 1088 writeln!(self.out, "{}}} else {{", level)?; 1089 1090 for sta in reject { 1091 // Increase indentation to help with readability 1092 self.write_stmt(module, sta, func_ctx, l2)?; 1093 } 1094 } 1095 1096 writeln!(self.out, "{}}}", level)? 1097 } 1098 // TODO: copy-paste from glsl-out 1099 Statement::Kill => writeln!(self.out, "{}discard;", level)?, 1100 Statement::Return { value: None } => { 1101 writeln!(self.out, "{}return;", level)?; 1102 } 1103 Statement::Return { value: Some(expr) } => { 1104 let base_ty_res = &func_ctx.info[expr].ty; 1105 let mut resolved = base_ty_res.inner_with(&module.types); 1106 if let TypeInner::Pointer { base, class: _ } = *resolved { 1107 resolved = &module.types[base].inner; 1108 } 1109 1110 if let TypeInner::Struct { .. } = *resolved { 1111 // We can safery unwrap here, since we now we working with struct 1112 let ty = base_ty_res.handle().unwrap(); 1113 let struct_name = &self.names[&NameKey::Type(ty)]; 1114 let variable_name = self.namer.call(&struct_name.to_lowercase()); 1115 write!( 1116 self.out, 1117 "{}const {} {} = ", 1118 level, struct_name, variable_name, 1119 )?; 1120 self.write_expr(module, expr, func_ctx)?; 1121 writeln!(self.out, ";")?; 1122 1123 // for entry point returns, we may need to reshuffle the outputs into a different struct 1124 let ep_output = match func_ctx.ty { 1125 back::FunctionType::Function(_) => None, 1126 back::FunctionType::EntryPoint(index) => { 1127 self.entry_point_io[index as usize].output.as_ref() 1128 } 1129 }; 1130 let final_name = match ep_output { 1131 Some(ep_output) => { 1132 let final_name = self.namer.call(&variable_name); 1133 write!( 1134 self.out, 1135 "{}const {} {} = {{ ", 1136 level, ep_output.ty_name, final_name, 1137 )?; 1138 for (index, m) in ep_output.members.iter().enumerate() { 1139 if index != 0 { 1140 write!(self.out, ", ")?; 1141 } 1142 let member_name = &self.names[&NameKey::StructMember(ty, m.index)]; 1143 write!(self.out, "{}.{}", variable_name, member_name)?; 1144 } 1145 writeln!(self.out, " }};")?; 1146 final_name 1147 } 1148 None => variable_name, 1149 }; 1150 writeln!(self.out, "{}return {};", level, final_name)?; 1151 } else { 1152 write!(self.out, "{}return ", level)?; 1153 self.write_expr(module, expr, func_ctx)?; 1154 writeln!(self.out, ";")? 1155 } 1156 } 1157 Statement::Store { pointer, value } => { 1158 let ty_inner = func_ctx.info[pointer].ty.inner_with(&module.types); 1159 let array_info = match *ty_inner { 1160 TypeInner::Pointer { base, .. } => match module.types[base].inner { 1161 crate::TypeInner::Array { 1162 size: crate::ArraySize::Constant(ch), 1163 .. 1164 } => Some((ch, base)), 1165 _ => None, 1166 }, 1167 _ => None, 1168 }; 1169 1170 if let Some(crate::StorageClass::Storage { .. }) = ty_inner.pointer_class() { 1171 let var_handle = self.fill_access_chain(module, pointer, func_ctx)?; 1172 self.write_storage_store( 1173 module, 1174 var_handle, 1175 StoreValue::Expression(value), 1176 func_ctx, 1177 level, 1178 )?; 1179 } else if let Some((const_handle, base_ty)) = array_info { 1180 let size = module.constants[const_handle].to_array_length().unwrap(); 1181 writeln!(self.out, "{}{{", level)?; 1182 write!(self.out, "{}", level.next())?; 1183 self.write_type(module, base_ty)?; 1184 write!(self.out, " _result[{}]=", size)?; 1185 self.write_expr(module, value, func_ctx)?; 1186 writeln!(self.out, ";")?; 1187 write!( 1188 self.out, 1189 "{}for(int _i=0; _i<{}; ++_i) ", 1190 level.next(), 1191 size 1192 )?; 1193 self.write_expr(module, pointer, func_ctx)?; 1194 writeln!(self.out, "[_i] = _result[_i];")?; 1195 writeln!(self.out, "{}}}", level)?; 1196 } else { 1197 write!(self.out, "{}", level)?; 1198 self.write_expr(module, pointer, func_ctx)?; 1199 write!(self.out, " = ")?; 1200 self.write_expr(module, value, func_ctx)?; 1201 writeln!(self.out, ";")? 1202 } 1203 } 1204 Statement::Loop { 1205 ref body, 1206 ref continuing, 1207 } => { 1208 let l2 = level.next(); 1209 if !continuing.is_empty() { 1210 let gate_name = self.namer.call("loop_init"); 1211 writeln!(self.out, "{}bool {} = true;", level, gate_name)?; 1212 writeln!(self.out, "{}while(true) {{", level)?; 1213 writeln!(self.out, "{}if (!{}) {{", l2, gate_name)?; 1214 for sta in continuing.iter() { 1215 self.write_stmt(module, sta, func_ctx, l2)?; 1216 } 1217 writeln!(self.out, "{}}}", level.next())?; 1218 writeln!(self.out, "{}{} = false;", level.next(), gate_name)?; 1219 } else { 1220 writeln!(self.out, "{}while(true) {{", level)?; 1221 } 1222 1223 for sta in body.iter() { 1224 self.write_stmt(module, sta, func_ctx, l2)?; 1225 } 1226 writeln!(self.out, "{}}}", level)? 1227 } 1228 Statement::Break => writeln!(self.out, "{}break;", level)?, 1229 Statement::Continue => writeln!(self.out, "{}continue;", level)?, 1230 Statement::Barrier(barrier) => { 1231 if barrier.contains(crate::Barrier::STORAGE) { 1232 writeln!(self.out, "{}DeviceMemoryBarrierWithGroupSync();", level)?; 1233 } 1234 1235 if barrier.contains(crate::Barrier::WORK_GROUP) { 1236 writeln!(self.out, "{}GroupMemoryBarrierWithGroupSync();", level)?; 1237 } 1238 } 1239 Statement::ImageStore { 1240 image, 1241 coordinate, 1242 array_index, 1243 value, 1244 } => { 1245 write!(self.out, "{}", level)?; 1246 self.write_expr(module, image, func_ctx)?; 1247 1248 write!(self.out, "[")?; 1249 if let Some(index) = array_index { 1250 // Array index accepted only for texture_storage_2d_array, so we can safety use int3(coordinate, array_index) here 1251 write!(self.out, "int3(")?; 1252 self.write_expr(module, coordinate, func_ctx)?; 1253 write!(self.out, ", ")?; 1254 self.write_expr(module, index, func_ctx)?; 1255 write!(self.out, ")")?; 1256 } else { 1257 self.write_expr(module, coordinate, func_ctx)?; 1258 } 1259 write!(self.out, "]")?; 1260 1261 write!(self.out, " = ")?; 1262 self.write_expr(module, value, func_ctx)?; 1263 writeln!(self.out, ";")?; 1264 } 1265 Statement::Call { 1266 function, 1267 ref arguments, 1268 result, 1269 } => { 1270 write!(self.out, "{}", level)?; 1271 if let Some(expr) = result { 1272 write!(self.out, "const ")?; 1273 let name = format!("{}{}", back::BAKE_PREFIX, expr.index()); 1274 let expr_ty = &func_ctx.info[expr].ty; 1275 match *expr_ty { 1276 proc::TypeResolution::Handle(handle) => self.write_type(module, handle)?, 1277 proc::TypeResolution::Value(ref value) => { 1278 self.write_value_type(module, value)? 1279 } 1280 }; 1281 write!(self.out, " {} = ", name)?; 1282 self.named_expressions.insert(expr, name); 1283 } 1284 let func_name = &self.names[&NameKey::Function(function)]; 1285 write!(self.out, "{}(", func_name)?; 1286 for (index, argument) in arguments.iter().enumerate() { 1287 self.write_expr(module, *argument, func_ctx)?; 1288 // Only write a comma if isn't the last element 1289 if index != arguments.len().saturating_sub(1) { 1290 // The leading space is for readability only 1291 write!(self.out, ", ")?; 1292 } 1293 } 1294 writeln!(self.out, ");")? 1295 } 1296 Statement::Atomic { 1297 pointer, 1298 ref fun, 1299 value, 1300 result, 1301 } => { 1302 write!(self.out, "{}", level)?; 1303 let res_name = format!("{}{}", back::BAKE_PREFIX, result.index()); 1304 match func_ctx.info[result].ty { 1305 proc::TypeResolution::Handle(handle) => self.write_type(module, handle)?, 1306 proc::TypeResolution::Value(ref value) => { 1307 self.write_value_type(module, value)? 1308 } 1309 }; 1310 1311 let var_handle = self.fill_access_chain(module, pointer, func_ctx)?; 1312 // working around the borrow checker in `self.write_expr` 1313 let chain = mem::take(&mut self.temp_access_chain); 1314 let var_name = &self.names[&NameKey::GlobalVariable(var_handle)]; 1315 1316 let fun_str = fun.to_hlsl_suffix(); 1317 write!( 1318 self.out, 1319 " {}; {}.Interlocked{}(", 1320 res_name, var_name, fun_str 1321 )?; 1322 self.write_storage_address(module, &chain, func_ctx)?; 1323 write!(self.out, ", ")?; 1324 // handle the special cases 1325 match *fun { 1326 crate::AtomicFunction::Subtract => { 1327 // we just wrote `InterlockedAdd`, so negate the argument 1328 write!(self.out, "-")?; 1329 } 1330 crate::AtomicFunction::Exchange { compare: Some(_) } => { 1331 return Err(Error::Unimplemented("atomic CompareExchange".to_string())); 1332 } 1333 _ => {} 1334 } 1335 self.write_expr(module, value, func_ctx)?; 1336 writeln!(self.out, ", {});", res_name)?; 1337 self.temp_access_chain = chain; 1338 self.named_expressions.insert(result, res_name); 1339 } 1340 Statement::Switch { 1341 selector, 1342 ref cases, 1343 } => { 1344 // Start the switch 1345 write!(self.out, "{}", level)?; 1346 write!(self.out, "switch(")?; 1347 self.write_expr(module, selector, func_ctx)?; 1348 writeln!(self.out, ") {{")?; 1349 let type_postfix = match *func_ctx.info[selector].ty.inner_with(&module.types) { 1350 crate::TypeInner::Scalar { 1351 kind: crate::ScalarKind::Uint, 1352 .. 1353 } => "u", 1354 _ => "", 1355 }; 1356 1357 // Write all cases 1358 let indent_level_1 = level.next(); 1359 let indent_level_2 = indent_level_1.next(); 1360 1361 for case in cases { 1362 match case.value { 1363 crate::SwitchValue::Integer(value) => writeln!( 1364 self.out, 1365 "{}case {}{}: {{", 1366 indent_level_1, value, type_postfix 1367 )?, 1368 crate::SwitchValue::Default => { 1369 writeln!(self.out, "{}default: {{", indent_level_1)? 1370 } 1371 } 1372 1373 if case.fall_through { 1374 // Generate each fallthrough case statement in a new block. This is done to 1375 // prevent symbol collision of variables declared in these cases statements. 1376 writeln!(self.out, "{}/* fallthrough */", indent_level_2)?; 1377 writeln!(self.out, "{}{{", indent_level_2)?; 1378 } 1379 for sta in case.body.iter() { 1380 self.write_stmt( 1381 module, 1382 sta, 1383 func_ctx, 1384 back::Level(indent_level_2.0 + usize::from(case.fall_through)), 1385 )?; 1386 } 1387 1388 if case.fall_through { 1389 writeln!(self.out, "{}}}", indent_level_2)?; 1390 } else if case.body.last().map_or(true, |s| !s.is_terminator()) { 1391 writeln!(self.out, "{}break;", indent_level_2)?; 1392 } 1393 1394 writeln!(self.out, "{}}}", indent_level_1)?; 1395 } 1396 1397 writeln!(self.out, "{}}}", level)? 1398 } 1399 } 1400 1401 Ok(()) 1402 } 1403 1404 /// Helper method to write expressions 1405 /// 1406 /// # Notes 1407 /// Doesn't add any newlines or leading/trailing spaces write_expr( &mut self, module: &Module, expr: Handle<crate::Expression>, func_ctx: &back::FunctionCtx<'_>, ) -> BackendResult1408 pub(super) fn write_expr( 1409 &mut self, 1410 module: &Module, 1411 expr: Handle<crate::Expression>, 1412 func_ctx: &back::FunctionCtx<'_>, 1413 ) -> BackendResult { 1414 use crate::Expression; 1415 1416 // Handle the special semantics for base vertex/instance 1417 let ff_input = if self.options.special_constants_binding.is_some() { 1418 func_ctx.is_fixed_function_input(expr, module) 1419 } else { 1420 None 1421 }; 1422 let closing_bracket = match ff_input { 1423 Some(crate::BuiltIn::VertexIndex) => { 1424 write!(self.out, "({}.{} + ", SPECIAL_CBUF_VAR, SPECIAL_BASE_VERTEX)?; 1425 ")" 1426 } 1427 Some(crate::BuiltIn::InstanceIndex) => { 1428 write!( 1429 self.out, 1430 "({}.{} + ", 1431 SPECIAL_CBUF_VAR, SPECIAL_BASE_INSTANCE, 1432 )?; 1433 ")" 1434 } 1435 Some(crate::BuiltIn::NumWorkGroups) => { 1436 //Note: despite their names (`BASE_VERTEX` and `BASE_INSTANCE`), 1437 // in compute shaders the special constants contain the number 1438 // of workgroups, which we are using here. 1439 write!( 1440 self.out, 1441 "uint3({}.{}, {}.{}, {}.{})", 1442 SPECIAL_CBUF_VAR, 1443 SPECIAL_BASE_VERTEX, 1444 SPECIAL_CBUF_VAR, 1445 SPECIAL_BASE_INSTANCE, 1446 SPECIAL_CBUF_VAR, 1447 SPECIAL_OTHER, 1448 )?; 1449 return Ok(()); 1450 } 1451 _ => "", 1452 }; 1453 1454 if let Some(name) = self.named_expressions.get(&expr) { 1455 write!(self.out, "{}{}", name, closing_bracket)?; 1456 return Ok(()); 1457 } 1458 1459 let expression = &func_ctx.expressions[expr]; 1460 1461 match *expression { 1462 Expression::Constant(constant) => self.write_constant(module, constant)?, 1463 Expression::Compose { ty, ref components } => { 1464 let (brace_open, brace_close) = match module.types[ty].inner { 1465 TypeInner::Struct { .. } => { 1466 self.write_wrapped_constructor_function_name(WrappedConstructor { ty })?; 1467 ("(", ")") 1468 } 1469 TypeInner::Array { .. } => ("{ ", " }"), 1470 _ => { 1471 self.write_type(module, ty)?; 1472 ("(", ")") 1473 } 1474 }; 1475 1476 write!(self.out, "{}", brace_open)?; 1477 1478 for (index, &component) in components.iter().enumerate() { 1479 if index != 0 { 1480 // The leading space is for readability only 1481 write!(self.out, ", ")?; 1482 } 1483 self.write_expr(module, component, func_ctx)?; 1484 } 1485 1486 write!(self.out, "{}", brace_close)?; 1487 } 1488 // All of the multiplication can be expressed as `mul`, 1489 // except vector * vector, which needs to use the "*" operator. 1490 Expression::Binary { 1491 op: crate::BinaryOperator::Multiply, 1492 left, 1493 right, 1494 } if func_ctx.info[left].ty.inner_with(&module.types).is_matrix() 1495 || func_ctx.info[right] 1496 .ty 1497 .inner_with(&module.types) 1498 .is_matrix() => 1499 { 1500 // We intentionally flip the order of multiplication as our matrices are implicitly transposed. 1501 write!(self.out, "mul(")?; 1502 self.write_expr(module, right, func_ctx)?; 1503 write!(self.out, ", ")?; 1504 self.write_expr(module, left, func_ctx)?; 1505 write!(self.out, ")")?; 1506 } 1507 Expression::Binary { op, left, right } => { 1508 write!(self.out, "(")?; 1509 self.write_expr(module, left, func_ctx)?; 1510 write!(self.out, " {} ", crate::back::binary_operation_str(op))?; 1511 self.write_expr(module, right, func_ctx)?; 1512 write!(self.out, ")")?; 1513 } 1514 Expression::Access { base, index } => { 1515 if let Some(crate::StorageClass::Storage { .. }) = func_ctx.info[expr] 1516 .ty 1517 .inner_with(&module.types) 1518 .pointer_class() 1519 { 1520 // do nothing, the chain is written on `Load`/`Store` 1521 } else { 1522 self.write_expr(module, base, func_ctx)?; 1523 write!(self.out, "[")?; 1524 self.write_expr(module, index, func_ctx)?; 1525 write!(self.out, "]")?; 1526 } 1527 } 1528 Expression::AccessIndex { base, index } => { 1529 if let Some(crate::StorageClass::Storage { .. }) = func_ctx.info[expr] 1530 .ty 1531 .inner_with(&module.types) 1532 .pointer_class() 1533 { 1534 // do nothing, the chain is written on `Load`/`Store` 1535 } else { 1536 self.write_expr(module, base, func_ctx)?; 1537 1538 let base_ty_res = &func_ctx.info[base].ty; 1539 let mut resolved = base_ty_res.inner_with(&module.types); 1540 let base_ty_handle = match *resolved { 1541 TypeInner::Pointer { base, class: _ } => { 1542 resolved = &module.types[base].inner; 1543 Some(base) 1544 } 1545 _ => base_ty_res.handle(), 1546 }; 1547 1548 match *resolved { 1549 TypeInner::Vector { .. } => { 1550 // Write vector access as a swizzle 1551 write!(self.out, ".{}", back::COMPONENTS[index as usize])? 1552 } 1553 TypeInner::Matrix { .. } 1554 | TypeInner::Array { .. } 1555 | TypeInner::ValuePointer { .. } => write!(self.out, "[{}]", index)?, 1556 TypeInner::Struct { .. } => { 1557 // This will never panic in case the type is a `Struct`, this is not true 1558 // for other types so we can only check while inside this match arm 1559 let ty = base_ty_handle.unwrap(); 1560 1561 write!( 1562 self.out, 1563 ".{}", 1564 &self.names[&NameKey::StructMember(ty, index)] 1565 )? 1566 } 1567 ref other => { 1568 return Err(Error::Custom(format!("Cannot index {:?}", other))) 1569 } 1570 } 1571 } 1572 } 1573 Expression::FunctionArgument(pos) => { 1574 let key = match func_ctx.ty { 1575 back::FunctionType::Function(handle) => NameKey::FunctionArgument(handle, pos), 1576 back::FunctionType::EntryPoint(index) => { 1577 NameKey::EntryPointArgument(index, pos) 1578 } 1579 }; 1580 let name = &self.names[&key]; 1581 write!(self.out, "{}", name)?; 1582 } 1583 Expression::ImageSample { 1584 image, 1585 sampler, 1586 gather, 1587 coordinate, 1588 array_index, 1589 offset, 1590 level, 1591 depth_ref, 1592 } => { 1593 use crate::SampleLevel as Sl; 1594 const COMPONENTS: [&str; 4] = ["", "Green", "Blue", "Alpha"]; 1595 1596 let (base_str, component_str) = match gather { 1597 Some(component) => ("Gather", COMPONENTS[component as usize]), 1598 None => ("Sample", ""), 1599 }; 1600 let cmp_str = match depth_ref { 1601 Some(_) => "Cmp", 1602 None => "", 1603 }; 1604 let level_str = match level { 1605 Sl::Zero if gather.is_none() => "LevelZero", 1606 Sl::Auto | Sl::Zero => "", 1607 Sl::Exact(_) => "Level", 1608 Sl::Bias(_) => "Bias", 1609 Sl::Gradient { .. } => "Grad", 1610 }; 1611 1612 self.write_expr(module, image, func_ctx)?; 1613 write!( 1614 self.out, 1615 ".{}{}{}{}(", 1616 base_str, cmp_str, component_str, level_str 1617 )?; 1618 self.write_expr(module, sampler, func_ctx)?; 1619 write!(self.out, ", ")?; 1620 self.write_texture_coordinates( 1621 "float", 1622 coordinate, 1623 array_index, 1624 MipLevelCoordinate::NotApplicable, 1625 module, 1626 func_ctx, 1627 )?; 1628 1629 if let Some(depth_ref) = depth_ref { 1630 write!(self.out, ", ")?; 1631 self.write_expr(module, depth_ref, func_ctx)?; 1632 } 1633 1634 match level { 1635 Sl::Auto | Sl::Zero => {} 1636 Sl::Exact(expr) => { 1637 write!(self.out, ", ")?; 1638 self.write_expr(module, expr, func_ctx)?; 1639 } 1640 Sl::Bias(expr) => { 1641 write!(self.out, ", ")?; 1642 self.write_expr(module, expr, func_ctx)?; 1643 } 1644 Sl::Gradient { x, y } => { 1645 write!(self.out, ", ")?; 1646 self.write_expr(module, x, func_ctx)?; 1647 write!(self.out, ", ")?; 1648 self.write_expr(module, y, func_ctx)?; 1649 } 1650 } 1651 1652 if let Some(offset) = offset { 1653 write!(self.out, ", ")?; 1654 self.write_constant(module, offset)?; 1655 } 1656 1657 write!(self.out, ")")?; 1658 } 1659 Expression::ImageQuery { image, query } => { 1660 // use wrapped image query function 1661 if let TypeInner::Image { 1662 dim, 1663 arrayed, 1664 class, 1665 } = *func_ctx.info[image].ty.inner_with(&module.types) 1666 { 1667 let wrapped_image_query = WrappedImageQuery { 1668 dim, 1669 arrayed, 1670 class, 1671 query: query.into(), 1672 }; 1673 1674 self.write_wrapped_image_query_function_name(wrapped_image_query)?; 1675 write!(self.out, "(")?; 1676 // Image always first param 1677 self.write_expr(module, image, func_ctx)?; 1678 if let crate::ImageQuery::Size { level: Some(level) } = query { 1679 write!(self.out, ", ")?; 1680 self.write_expr(module, level, func_ctx)?; 1681 } 1682 write!(self.out, ")")?; 1683 } 1684 } 1685 Expression::ImageLoad { 1686 image, 1687 coordinate, 1688 array_index, 1689 index, 1690 } => { 1691 // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-to-load 1692 let (ms, storage) = match *func_ctx.info[image].ty.inner_with(&module.types) { 1693 TypeInner::Image { class, .. } => match class { 1694 crate::ImageClass::Sampled { multi, .. } 1695 | crate::ImageClass::Depth { multi } => (multi, false), 1696 crate::ImageClass::Storage { .. } => (false, true), 1697 }, 1698 _ => (false, false), 1699 }; 1700 1701 self.write_expr(module, image, func_ctx)?; 1702 write!(self.out, ".Load(")?; 1703 1704 let mip_level = if ms || storage { 1705 MipLevelCoordinate::NotApplicable 1706 } else { 1707 match index { 1708 Some(expr) => MipLevelCoordinate::Expression(expr), 1709 None => MipLevelCoordinate::Zero, 1710 } 1711 }; 1712 1713 self.write_texture_coordinates( 1714 "int", 1715 coordinate, 1716 array_index, 1717 mip_level, 1718 module, 1719 func_ctx, 1720 )?; 1721 1722 if ms { 1723 write!(self.out, ", ")?; 1724 self.write_expr(module, index.unwrap(), func_ctx)?; 1725 } 1726 1727 // close bracket for Load function 1728 write!(self.out, ")")?; 1729 1730 // return x component if return type is scalar 1731 if let TypeInner::Scalar { .. } = *func_ctx.info[expr].ty.inner_with(&module.types) 1732 { 1733 write!(self.out, ".x")?; 1734 } 1735 } 1736 Expression::GlobalVariable(handle) => match module.global_variables[handle].class { 1737 crate::StorageClass::Storage { .. } => {} 1738 _ => { 1739 let name = &self.names[&NameKey::GlobalVariable(handle)]; 1740 write!(self.out, "{}", name)?; 1741 } 1742 }, 1743 Expression::LocalVariable(handle) => { 1744 write!(self.out, "{}", self.names[&func_ctx.name_key(handle)])? 1745 } 1746 Expression::Load { pointer } => { 1747 match func_ctx.info[pointer] 1748 .ty 1749 .inner_with(&module.types) 1750 .pointer_class() 1751 { 1752 Some(crate::StorageClass::Storage { .. }) => { 1753 let var_handle = self.fill_access_chain(module, pointer, func_ctx)?; 1754 let result_ty = func_ctx.info[expr].ty.clone(); 1755 self.write_storage_load(module, var_handle, result_ty, func_ctx)?; 1756 } 1757 _ => { 1758 self.write_expr(module, pointer, func_ctx)?; 1759 } 1760 } 1761 } 1762 Expression::Unary { op, expr } => { 1763 // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-operators#unary-operators 1764 let op_str = match op { 1765 crate::UnaryOperator::Negate => "-", 1766 crate::UnaryOperator::Not => "!", 1767 }; 1768 write!(self.out, "{}", op_str)?; 1769 self.write_expr(module, expr, func_ctx)?; 1770 } 1771 Expression::As { 1772 expr, 1773 kind, 1774 convert, 1775 } => { 1776 let inner = func_ctx.info[expr].ty.inner_with(&module.types); 1777 let (size_str, src_width) = match *inner { 1778 TypeInner::Vector { size, width, .. } => (back::vector_size_str(size), width), 1779 TypeInner::Scalar { width, .. } => ("", width), 1780 _ => { 1781 return Err(Error::Unimplemented(format!( 1782 "write_expr expression::as {:?}", 1783 inner 1784 ))); 1785 } 1786 }; 1787 let kind_str = kind.to_hlsl_str(convert.unwrap_or(src_width))?; 1788 write!(self.out, "{}{}(", kind_str, size_str,)?; 1789 self.write_expr(module, expr, func_ctx)?; 1790 write!(self.out, ")")?; 1791 } 1792 Expression::Math { 1793 fun, 1794 arg, 1795 arg1, 1796 arg2, 1797 arg3, 1798 } => { 1799 use crate::MathFunction as Mf; 1800 1801 enum Function { 1802 Asincosh { is_sin: bool }, 1803 Atanh, 1804 Regular(&'static str), 1805 } 1806 1807 let fun = match fun { 1808 // comparison 1809 Mf::Abs => Function::Regular("abs"), 1810 Mf::Min => Function::Regular("min"), 1811 Mf::Max => Function::Regular("max"), 1812 Mf::Clamp => Function::Regular("clamp"), 1813 // trigonometry 1814 Mf::Cos => Function::Regular("cos"), 1815 Mf::Cosh => Function::Regular("cosh"), 1816 Mf::Sin => Function::Regular("sin"), 1817 Mf::Sinh => Function::Regular("sinh"), 1818 Mf::Tan => Function::Regular("tan"), 1819 Mf::Tanh => Function::Regular("tanh"), 1820 Mf::Acos => Function::Regular("acos"), 1821 Mf::Asin => Function::Regular("asin"), 1822 Mf::Atan => Function::Regular("atan"), 1823 Mf::Atan2 => Function::Regular("atan2"), 1824 Mf::Asinh => Function::Asincosh { is_sin: true }, 1825 Mf::Acosh => Function::Asincosh { is_sin: false }, 1826 Mf::Atanh => Function::Atanh, 1827 Mf::Radians => Function::Regular("radians"), 1828 Mf::Degrees => Function::Regular("degrees"), 1829 // decomposition 1830 Mf::Ceil => Function::Regular("ceil"), 1831 Mf::Floor => Function::Regular("floor"), 1832 Mf::Round => Function::Regular("round"), 1833 Mf::Fract => Function::Regular("frac"), 1834 Mf::Trunc => Function::Regular("trunc"), 1835 Mf::Modf => Function::Regular("modf"), 1836 Mf::Frexp => Function::Regular("frexp"), 1837 Mf::Ldexp => Function::Regular("ldexp"), 1838 // exponent 1839 Mf::Exp => Function::Regular("exp"), 1840 Mf::Exp2 => Function::Regular("exp2"), 1841 Mf::Log => Function::Regular("log"), 1842 Mf::Log2 => Function::Regular("log2"), 1843 Mf::Pow => Function::Regular("pow"), 1844 // geometry 1845 Mf::Dot => Function::Regular("dot"), 1846 //Mf::Outer => , 1847 Mf::Cross => Function::Regular("cross"), 1848 Mf::Distance => Function::Regular("distance"), 1849 Mf::Length => Function::Regular("length"), 1850 Mf::Normalize => Function::Regular("normalize"), 1851 Mf::FaceForward => Function::Regular("faceforward"), 1852 Mf::Reflect => Function::Regular("reflect"), 1853 Mf::Refract => Function::Regular("refract"), 1854 // computational 1855 Mf::Sign => Function::Regular("sign"), 1856 Mf::Fma => Function::Regular("mad"), 1857 Mf::Mix => Function::Regular("lerp"), 1858 Mf::Step => Function::Regular("step"), 1859 Mf::SmoothStep => Function::Regular("smoothstep"), 1860 Mf::Sqrt => Function::Regular("sqrt"), 1861 Mf::InverseSqrt => Function::Regular("rsqrt"), 1862 //Mf::Inverse =>, 1863 Mf::Transpose => Function::Regular("transpose"), 1864 Mf::Determinant => Function::Regular("determinant"), 1865 // bits 1866 Mf::CountOneBits => Function::Regular("countbits"), 1867 Mf::ReverseBits => Function::Regular("reversebits"), 1868 Mf::FindLsb => Function::Regular("firstbitlow"), 1869 Mf::FindMsb => Function::Regular("firstbithigh"), 1870 _ => return Err(Error::Unimplemented(format!("write_expr_math {:?}", fun))), 1871 }; 1872 1873 match fun { 1874 Function::Asincosh { is_sin } => { 1875 write!(self.out, "log(")?; 1876 self.write_expr(module, arg, func_ctx)?; 1877 write!(self.out, " + sqrt(")?; 1878 self.write_expr(module, arg, func_ctx)?; 1879 write!(self.out, " * ")?; 1880 self.write_expr(module, arg, func_ctx)?; 1881 match is_sin { 1882 true => write!(self.out, " + 1.0))")?, 1883 false => write!(self.out, " - 1.0))")?, 1884 } 1885 } 1886 Function::Atanh => { 1887 write!(self.out, "0.5 * log((1.0 + ")?; 1888 self.write_expr(module, arg, func_ctx)?; 1889 write!(self.out, ") / (1.0 - ")?; 1890 self.write_expr(module, arg, func_ctx)?; 1891 write!(self.out, "))")?; 1892 } 1893 Function::Regular(fun_name) => { 1894 write!(self.out, "{}(", fun_name)?; 1895 self.write_expr(module, arg, func_ctx)?; 1896 if let Some(arg) = arg1 { 1897 write!(self.out, ", ")?; 1898 self.write_expr(module, arg, func_ctx)?; 1899 } 1900 if let Some(arg) = arg2 { 1901 write!(self.out, ", ")?; 1902 self.write_expr(module, arg, func_ctx)?; 1903 } 1904 if let Some(arg) = arg3 { 1905 write!(self.out, ", ")?; 1906 self.write_expr(module, arg, func_ctx)?; 1907 } 1908 write!(self.out, ")")? 1909 } 1910 } 1911 } 1912 Expression::Swizzle { 1913 size, 1914 vector, 1915 pattern, 1916 } => { 1917 self.write_expr(module, vector, func_ctx)?; 1918 write!(self.out, ".")?; 1919 for &sc in pattern[..size as usize].iter() { 1920 self.out.write_char(back::COMPONENTS[sc as usize])?; 1921 } 1922 } 1923 Expression::ArrayLength(expr) => { 1924 let var_handle = match func_ctx.expressions[expr] { 1925 Expression::AccessIndex { base, index: _ } => { 1926 match func_ctx.expressions[base] { 1927 Expression::GlobalVariable(handle) => handle, 1928 _ => unreachable!(), 1929 } 1930 } 1931 Expression::GlobalVariable(handle) => handle, 1932 _ => unreachable!(), 1933 }; 1934 1935 let var = &module.global_variables[var_handle]; 1936 let (offset, stride) = match module.types[var.ty].inner { 1937 TypeInner::Array { stride, .. } => (0, stride), 1938 TypeInner::Struct { ref members, .. } => { 1939 let last = members.last().unwrap(); 1940 let stride = match module.types[last.ty].inner { 1941 TypeInner::Array { stride, .. } => stride, 1942 _ => unreachable!(), 1943 }; 1944 (last.offset, stride) 1945 } 1946 _ => unreachable!(), 1947 }; 1948 1949 let storage_access = match var.class { 1950 crate::StorageClass::Storage { access } => access, 1951 _ => crate::StorageAccess::default(), 1952 }; 1953 let wrapped_array_length = WrappedArrayLength { 1954 writable: storage_access.contains(crate::StorageAccess::STORE), 1955 }; 1956 1957 write!(self.out, "((")?; 1958 self.write_wrapped_array_length_function_name(wrapped_array_length)?; 1959 let var_name = &self.names[&NameKey::GlobalVariable(var_handle)]; 1960 write!(self.out, "({}) - {}) / {})", var_name, offset, stride)? 1961 } 1962 Expression::Derivative { axis, expr } => { 1963 use crate::DerivativeAxis as Da; 1964 1965 let fun_str = match axis { 1966 Da::X => "ddx", 1967 Da::Y => "ddy", 1968 Da::Width => "fwidth", 1969 }; 1970 write!(self.out, "{}(", fun_str)?; 1971 self.write_expr(module, expr, func_ctx)?; 1972 write!(self.out, ")")? 1973 } 1974 Expression::Relational { fun, argument } => { 1975 use crate::RelationalFunction as Rf; 1976 1977 let fun_str = match fun { 1978 Rf::All => "all", 1979 Rf::Any => "any", 1980 Rf::IsNan => "isnan", 1981 Rf::IsInf => "isinf", 1982 Rf::IsFinite => "isfinite", 1983 Rf::IsNormal => "isnormal", 1984 }; 1985 write!(self.out, "{}(", fun_str)?; 1986 self.write_expr(module, argument, func_ctx)?; 1987 write!(self.out, ")")? 1988 } 1989 Expression::Splat { size, value } => { 1990 // hlsl is not supported one value constructor 1991 // if we write, for example, int4(0), dxc returns error: 1992 // error: too few elements in vector initialization (expected 4 elements, have 1) 1993 let number_of_components = match size { 1994 crate::VectorSize::Bi => "xx", 1995 crate::VectorSize::Tri => "xxx", 1996 crate::VectorSize::Quad => "xxxx", 1997 }; 1998 let resolved = func_ctx.info[expr].ty.inner_with(&module.types); 1999 self.write_value_type(module, resolved)?; 2000 write!(self.out, "(")?; 2001 self.write_expr(module, value, func_ctx)?; 2002 write!(self.out, ".{})", number_of_components)? 2003 } 2004 Expression::Select { 2005 condition, 2006 accept, 2007 reject, 2008 } => { 2009 write!(self.out, "(")?; 2010 self.write_expr(module, condition, func_ctx)?; 2011 write!(self.out, " ? ")?; 2012 self.write_expr(module, accept, func_ctx)?; 2013 write!(self.out, " : ")?; 2014 self.write_expr(module, reject, func_ctx)?; 2015 write!(self.out, ")")? 2016 } 2017 // Nothing to do here, since call expression already cached 2018 Expression::CallResult(_) | Expression::AtomicResult { .. } => {} 2019 } 2020 2021 if !closing_bracket.is_empty() { 2022 write!(self.out, "{}", closing_bracket)?; 2023 } 2024 Ok(()) 2025 } 2026 2027 /// Helper method used to write constants 2028 /// 2029 /// # Notes 2030 /// Doesn't add any newlines or leading/trailing spaces write_constant( &mut self, module: &Module, handle: Handle<crate::Constant>, ) -> BackendResult2031 fn write_constant( 2032 &mut self, 2033 module: &Module, 2034 handle: Handle<crate::Constant>, 2035 ) -> BackendResult { 2036 let constant = &module.constants[handle]; 2037 match constant.inner { 2038 crate::ConstantInner::Scalar { 2039 width: _, 2040 ref value, 2041 } => { 2042 if constant.name.is_some() { 2043 write!(self.out, "{}", &self.names[&NameKey::Constant(handle)])?; 2044 } else { 2045 self.write_scalar_value(*value)?; 2046 } 2047 } 2048 crate::ConstantInner::Composite { ty, ref components } => { 2049 self.write_composite_constant(module, ty, components)?; 2050 } 2051 } 2052 2053 Ok(()) 2054 } 2055 write_composite_constant( &mut self, module: &Module, ty: Handle<crate::Type>, components: &[Handle<crate::Constant>], ) -> BackendResult2056 fn write_composite_constant( 2057 &mut self, 2058 module: &Module, 2059 ty: Handle<crate::Type>, 2060 components: &[Handle<crate::Constant>], 2061 ) -> BackendResult { 2062 let (open_b, close_b) = match module.types[ty].inner { 2063 TypeInner::Array { .. } | TypeInner::Struct { .. } => ("{ ", " }"), 2064 _ => { 2065 // We should write type only for non struct/array constants 2066 self.write_type(module, ty)?; 2067 ("(", ")") 2068 } 2069 }; 2070 write!(self.out, "{}", open_b)?; 2071 for (index, constant) in components.iter().enumerate() { 2072 self.write_constant(module, *constant)?; 2073 // Only write a comma if isn't the last element 2074 if index != components.len().saturating_sub(1) { 2075 // The leading space is for readability only 2076 write!(self.out, ", ")?; 2077 } 2078 } 2079 write!(self.out, "{}", close_b)?; 2080 2081 Ok(()) 2082 } 2083 2084 /// Helper method used to write [`ScalarValue`](crate::ScalarValue) 2085 /// 2086 /// # Notes 2087 /// Adds no trailing or leading whitespace write_scalar_value(&mut self, value: crate::ScalarValue) -> BackendResult2088 fn write_scalar_value(&mut self, value: crate::ScalarValue) -> BackendResult { 2089 use crate::ScalarValue as Sv; 2090 2091 match value { 2092 Sv::Sint(value) => write!(self.out, "{}", value)?, 2093 Sv::Uint(value) => write!(self.out, "{}u", value)?, 2094 // Floats are written using `Debug` instead of `Display` because it always appends the 2095 // decimal part even it's zero 2096 Sv::Float(value) => write!(self.out, "{:?}", value)?, 2097 Sv::Bool(value) => write!(self.out, "{}", value)?, 2098 } 2099 2100 Ok(()) 2101 } 2102 write_named_expr( &mut self, module: &Module, handle: Handle<crate::Expression>, name: String, ctx: &back::FunctionCtx, ) -> BackendResult2103 fn write_named_expr( 2104 &mut self, 2105 module: &Module, 2106 handle: Handle<crate::Expression>, 2107 name: String, 2108 ctx: &back::FunctionCtx, 2109 ) -> BackendResult { 2110 match ctx.info[handle].ty { 2111 proc::TypeResolution::Handle(ty_handle) => match module.types[ty_handle].inner { 2112 TypeInner::Struct { .. } => { 2113 let ty_name = &self.names[&NameKey::Type(ty_handle)]; 2114 write!(self.out, "{}", ty_name)?; 2115 } 2116 _ => { 2117 self.write_type(module, ty_handle)?; 2118 } 2119 }, 2120 proc::TypeResolution::Value(ref inner) => { 2121 self.write_value_type(module, inner)?; 2122 } 2123 } 2124 2125 let base_ty_res = &ctx.info[handle].ty; 2126 let resolved = base_ty_res.inner_with(&module.types); 2127 2128 write!(self.out, " {}", name)?; 2129 // If rhs is a array type, we should write array size 2130 if let TypeInner::Array { size, .. } = *resolved { 2131 self.write_array_size(module, size)?; 2132 } 2133 write!(self.out, " = ")?; 2134 self.write_expr(module, handle, ctx)?; 2135 writeln!(self.out, ";")?; 2136 self.named_expressions.insert(handle, name); 2137 2138 Ok(()) 2139 } 2140 2141 /// Helper function that write default zero initialization write_default_init(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult2142 fn write_default_init(&mut self, module: &Module, ty: Handle<crate::Type>) -> BackendResult { 2143 match module.types[ty].inner { 2144 TypeInner::Array { 2145 size: crate::ArraySize::Constant(const_handle), 2146 base, 2147 .. 2148 } => { 2149 write!(self.out, "{{")?; 2150 let count = module.constants[const_handle].to_array_length().unwrap(); 2151 for i in 0..count { 2152 if i != 0 { 2153 write!(self.out, ",")?; 2154 } 2155 self.write_default_init(module, base)?; 2156 } 2157 write!(self.out, "}}")?; 2158 } 2159 _ => { 2160 write!(self.out, "(")?; 2161 self.write_type(module, ty)?; 2162 write!(self.out, ")0")?; 2163 } 2164 } 2165 Ok(()) 2166 } 2167 } 2168