1use crate::binary::tracker::Type;
2use crate::binary::tracker::Type::{Float, Integer};
3use crate::dr;
4use crate::dr::Operand;
5use crate::dr::Operand::{LiteralBit32, LiteralBit64};
6use crate::spirv;
7
8use super::tracker;
9
10pub trait Disassemble {
12 fn disassemble(&self) -> String;
14}
15
16impl Disassemble for dr::ModuleHeader {
17 fn disassemble(&self) -> String {
18 let (major, minor) = self.version();
19 let (vendor, _) = self.generator();
20 format!(
21 "; SPIR-V\n; Version: {}.{}\n; Generator: {}\n; Bound: {}",
22 major, minor, vendor, self.bound
23 )
24 }
25}
26
27include!("autogen_disas_operand.rs");
28
29impl Disassemble for dr::Operand {
30 fn disassemble(&self) -> String {
31 match *self {
32 dr::Operand::IdMemorySemantics(v) | dr::Operand::IdScope(v) | dr::Operand::IdRef(v) => {
33 format!("%{}", v)
34 }
35 dr::Operand::ImageOperands(v) => v.disassemble(),
36 dr::Operand::FPFastMathMode(v) => v.disassemble(),
37 dr::Operand::SelectionControl(v) => v.disassemble(),
38 dr::Operand::LoopControl(v) => v.disassemble(),
39 dr::Operand::FunctionControl(v) => v.disassemble(),
40 dr::Operand::MemorySemantics(v) => v.disassemble(),
41 dr::Operand::MemoryAccess(v) => v.disassemble(),
42 dr::Operand::KernelProfilingInfo(v) => v.disassemble(),
43 _ => format!("{}", self),
44 }
45 }
46}
47
48fn disas_join(insts: &[impl Disassemble], delimiter: &str) -> String {
51 insts
52 .iter()
53 .map(|i| i.disassemble())
54 .collect::<Vec<String>>()
55 .join(delimiter)
56}
57
58fn disas_instruction<F>(inst: &dr::Instruction, space: &str, disas_operands: F) -> String
59where
60 F: Fn(&Vec<Operand>) -> String,
61{
62 format!(
63 "{rid}Op{opcode}{rtype}{space}{operands}",
64 rid = inst
65 .result_id
66 .map_or(String::new(), |w| format!("%{} = ", w)),
67 opcode = inst.class.opname,
68 rtype = inst
70 .result_type
71 .map_or(String::new(), |w| format!(" %{}{}", w, space)),
72 space = space,
73 operands = disas_operands(&inst.operands)
74 )
75}
76
77impl Disassemble for dr::Instruction {
78 fn disassemble(&self) -> String {
79 let space = if !self.operands.is_empty() { " " } else { "" };
80 disas_instruction(self, space, |operands| disas_join(operands, " "))
81 }
82}
83
84impl Disassemble for dr::Block {
85 fn disassemble(&self) -> String {
86 let label = self
87 .label
88 .as_ref()
89 .map_or(String::new(), |i| i.disassemble());
90 format!(
91 "{label}\n{insts}",
92 label = label,
93 insts = disas_join(&self.instructions, "\n")
94 )
95 }
96}
97
98impl Disassemble for dr::Function {
99 fn disassemble(&self) -> String {
100 let def = self.def.as_ref().map_or(String::new(), |i| i.disassemble());
101 let end = self.end.as_ref().map_or(String::new(), |i| i.disassemble());
102 if self.parameters.is_empty() {
103 format!(
104 "{def}\n{blocks}\n{end}",
105 def = def,
106 blocks = disas_join(&self.blocks, "\n"),
107 end = end
108 )
109 } else {
110 format!(
111 "{def}\n{params}\n{blocks}\n{end}",
112 def = def,
113 params = disas_join(&self.parameters, "\n"),
114 blocks = disas_join(&self.blocks, "\n"),
115 end = end
116 )
117 }
118 }
119}
120
121macro_rules! push {
123 ($container: expr, $val: expr) => {
124 if !$val.is_empty() {
125 $container.push($val)
126 }
127 };
128}
129
130impl Disassemble for dr::Module {
131 fn disassemble(&self) -> String {
137 let mut ext_inst_set_tracker = tracker::ExtInstSetTracker::new();
138 for i in &self.ext_inst_imports {
139 ext_inst_set_tracker.track(i)
140 }
141
142 let mut text = vec![];
143 if let Some(ref header) = self.header {
144 push!(&mut text, header.disassemble());
145 }
146
147 let mut global_type_tracker = tracker::TypeTracker::new();
148 for t in &self.types_global_values {
149 global_type_tracker.track(t)
150 }
151
152 let global_insts = self
153 .global_inst_iter()
154 .map(|i| match i.class.opcode {
155 spirv::Op::Constant => disas_constant(i, &global_type_tracker),
156 _ => i.disassemble(),
157 })
158 .collect::<Vec<String>>()
159 .join("\n");
160 push!(&mut text, global_insts);
161
162 for f in &self.functions {
166 push!(
167 &mut text,
168 f.def.as_ref().map_or(String::new(), |i| i.disassemble())
169 );
170 push!(&mut text, disas_join(&f.parameters, "\n"));
171 for bb in &f.blocks {
172 push!(
173 &mut text,
174 bb.label.as_ref().map_or(String::new(), |i| i.disassemble())
175 );
176 for inst in &bb.instructions {
177 match inst.class.opcode {
178 spirv::Op::ExtInst => {
179 push!(&mut text, disas_ext_inst(inst, &ext_inst_set_tracker))
180 }
181 _ => push!(&mut text, inst.disassemble()),
182 }
183 }
184 }
185 push!(
186 &mut text,
187 f.end.as_ref().map_or(String::new(), |i| i.disassemble())
188 );
189 }
190
191 text.join("\n")
192 }
193}
194
195fn disas_constant(inst: &dr::Instruction, type_tracker: &tracker::TypeTracker) -> String {
198 debug_assert_eq!(inst.class.opcode, spirv::Op::Constant);
199 debug_assert_eq!(inst.operands.len(), 1);
200 let literal_type = type_tracker.resolve(inst.result_type.unwrap());
201 match inst.operands[0] {
202 LiteralBit32(value) => disas_instruction(inst, " ", |_| {
203 disas_literal_bit_operand(value, &literal_type.unwrap())
204 }),
205 LiteralBit64(value) => disas_instruction(inst, " ", |_| {
206 disas_literal_bit_operand(value, &literal_type.unwrap())
207 }),
208 _ => inst.disassemble(),
209 }
210}
211
212#[inline]
213fn disas_literal_bit_operand<T: DisassembleLiteralBit>(value: T, literal_type: &Type) -> String {
214 DisassembleLiteralBit::disas_literal_bit(value, literal_type)
215}
216
217trait DisassembleLiteralBit {
218 fn disas_literal_bit(value: Self, literal_type: &Type) -> String;
219}
220
221impl DisassembleLiteralBit for u32 {
222 fn disas_literal_bit(value: u32, literal_type: &Type) -> String {
223 match literal_type {
224 Integer(_, true) => (value as i32).to_string(),
225 Integer(_, false) => value.to_string(),
226 Float(_) => f32::from_bits(value).to_string(),
227 }
228 }
229}
230
231impl DisassembleLiteralBit for u64 {
232 fn disas_literal_bit(value: u64, literal_type: &Type) -> String {
233 match literal_type {
234 Integer(_, true) => (value as i64).to_string(),
235 Integer(_, false) => value.to_string(),
236 Float(_) => f64::from_bits(value).to_string(),
237 }
238 }
239}
240
241fn disas_ext_inst(
242 inst: &dr::Instruction,
243 ext_inst_set_tracker: &tracker::ExtInstSetTracker,
244) -> String {
245 if inst.operands.len() < 2 {
246 return inst.disassemble();
247 }
248 if let (&dr::Operand::IdRef(id), &dr::Operand::LiteralExtInstInteger(opcode)) =
249 (&inst.operands[0], &inst.operands[1])
250 {
251 if let Some(grammar) = ext_inst_set_tracker.resolve(id, opcode) {
252 let mut operands = vec![inst.operands[0].disassemble(), grammar.opname.to_string()];
253 for operand in &inst.operands[2..] {
254 operands.push(operand.disassemble())
255 }
256 disas_instruction(inst, " ", |_| operands.join(" "))
257 } else {
258 inst.disassemble()
259 }
260 } else {
261 inst.disassemble()
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use crate::binary::Disassemble;
268 use crate::dr;
269 use crate::spirv;
270
271 #[test]
272 fn test_disassemble_operand_function_control() {
273 let o = dr::Operand::FunctionControl(spirv::FunctionControl::NONE);
274 assert_eq!("None", o.disassemble());
275 let o = dr::Operand::FunctionControl(spirv::FunctionControl::INLINE);
276 assert_eq!("Inline", o.disassemble());
277 let o = dr::Operand::FunctionControl(
278 spirv::FunctionControl::INLINE | spirv::FunctionControl::PURE,
279 );
280 assert_eq!("Inline|Pure", o.disassemble());
281 let o = dr::Operand::FunctionControl(spirv::FunctionControl::all());
282 assert_eq!("Inline|DontInline|Pure|Const|OptNoneEXT", o.disassemble());
283 }
284
285 #[test]
286 fn test_disassemble_operand_memory_semantics() {
287 let o = dr::Operand::MemorySemantics(spirv::MemorySemantics::RELAXED);
288 assert_eq!("None", o.disassemble());
289 let o = dr::Operand::MemorySemantics(spirv::MemorySemantics::RELEASE);
290 assert_eq!("Release", o.disassemble());
291 let o = dr::Operand::MemorySemantics(
292 spirv::MemorySemantics::RELEASE | spirv::MemorySemantics::WORKGROUP_MEMORY,
293 );
294 assert_eq!("Release|WorkgroupMemory", o.disassemble());
295 }
296
297 #[test]
298 fn test_disassemble_module_one_inst_in_each_section() {
299 let mut b = dr::Builder::new();
300
301 b.capability(spirv::Capability::Shader);
302 b.extension("awesome-extension");
303 b.ext_inst_import("GLSL.std.450");
304 b.memory_model(spirv::AddressingModel::Logical, spirv::MemoryModel::Simple);
305 b.source(spirv::SourceLanguage::GLSL, 450, None, None::<String>);
306
307 let void = b.type_void();
308 let float32 = b.type_float(32, None);
309 let voidfvoid = b.type_function(void, vec![void]);
310
311 let f = b
312 .begin_function(
313 void,
314 None,
315 spirv::FunctionControl::DONT_INLINE | spirv::FunctionControl::CONST,
316 voidfvoid,
317 )
318 .unwrap();
319 b.begin_block(None).unwrap();
320 let var = b.variable(float32, None, spirv::StorageClass::Function, None);
321 b.ret().unwrap();
322 b.end_function().unwrap();
323
324 b.entry_point(spirv::ExecutionModel::Fragment, f, "main", vec![]);
325 b.execution_mode(f, spirv::ExecutionMode::OriginUpperLeft, vec![]);
326 b.name(f, "main");
327 b.decorate(var, spirv::Decoration::RelaxedPrecision, vec![]);
328
329 assert_eq!(
330 b.module().disassemble(),
331 "; SPIR-V\n\
332 ; Version: 1.6\n\
333 ; Generator: rspirv\n\
334 ; Bound: 8\n\
335 OpCapability Shader\n\
336 OpExtension \"awesome-extension\"\n\
337 %1 = OpExtInstImport \"GLSL.std.450\"\n\
338 OpMemoryModel Logical Simple\n\
339 OpEntryPoint Fragment %5 \"main\"\n\
340 OpExecutionMode %5 OriginUpperLeft\n\
341 OpSource GLSL 450\n\
342 OpName %5 \"main\"\n\
343 OpDecorate %7 RelaxedPrecision\n\
344 %2 = OpTypeVoid\n\
345 %3 = OpTypeFloat 32\n\
346 %4 = OpTypeFunction %2 %2\n\
347 %5 = OpFunction %2 DontInline|Const %4\n\
348 %6 = OpLabel\n\
349 %7 = OpVariable %3 Function\n\
350 OpReturn\n\
351 OpFunctionEnd"
352 );
353 }
354
355 #[test]
356 fn test_disassemble_literal_bit_constants() {
357 let mut b = dr::Builder::new();
358
359 b.capability(spirv::Capability::Shader);
360 b.ext_inst_import("GLSL.std.450");
361 b.source(spirv::SourceLanguage::GLSL, 450, None, None::<String>);
362
363 let void = b.type_void();
364 let int32 = b.type_int(32, 1);
365 let int64 = b.type_int(64, 1);
366 let uint32 = b.type_int(32, 0);
367 let uint64 = b.type_int(64, 0);
368 let float32 = b.type_float(32, None);
369 let float64 = b.type_float(64, None);
370 let voidfvoid = b.type_function(void, vec![void]);
371
372 let f = b
373 .begin_function(
374 void,
375 None,
376 spirv::FunctionControl::DONT_INLINE | spirv::FunctionControl::CONST,
377 voidfvoid,
378 )
379 .unwrap();
380 b.begin_block(None).unwrap();
381 let signed_i32_value: i32 = -1;
382 let signed_i64_value: i64 = -1;
383 let f32_value: f32 = -2.0;
384 let f64_value: f64 = 9.26;
385 b.constant_bit32(int32, signed_i32_value as u32);
386 b.constant_bit64(int64, signed_i64_value as u64);
387 b.constant_bit32(uint32, signed_i32_value as u32);
388 b.constant_bit64(uint64, signed_i64_value as u64);
389 b.constant_bit32(float32, f32_value.to_bits());
390 b.constant_bit64(float64, f64_value.to_bits());
391 b.ret().unwrap();
392 b.end_function().unwrap();
393
394 b.entry_point(spirv::ExecutionModel::Fragment, f, "main", vec![]);
395 b.execution_mode(f, spirv::ExecutionMode::OriginUpperLeft, vec![]);
396 b.name(f, "main");
397
398 assert_eq!(
399 b.module().disassemble(),
400 "; SPIR-V\n\
401 ; Version: 1.6\n\
402 ; Generator: rspirv\n\
403 ; Bound: 18\n\
404 OpCapability Shader\n\
405 %1 = OpExtInstImport \"GLSL.std.450\"\n\
406 OpEntryPoint Fragment %10 \"main\"\n\
407 OpExecutionMode %10 OriginUpperLeft\n\
408 OpSource GLSL 450\n\
409 OpName %10 \"main\"\n\
410 %2 = OpTypeVoid\n\
411 %3 = OpTypeInt 32 1\n\
412 %4 = OpTypeInt 64 1\n\
413 %5 = OpTypeInt 32 0\n\
414 %6 = OpTypeInt 64 0\n\
415 %7 = OpTypeFloat 32\n\
416 %8 = OpTypeFloat 64\n\
417 %9 = OpTypeFunction %2 %2\n\
418 %12 = OpConstant %3 -1\n\
419 %13 = OpConstant %4 -1\n\
420 %14 = OpConstant %5 4294967295\n\
421 %15 = OpConstant %6 18446744073709551615\n\
422 %16 = OpConstant %7 -2\n\
423 %17 = OpConstant %8 9.26\n\
424 %10 = OpFunction %2 DontInline|Const %9\n\
425 %11 = OpLabel\n\
426 OpReturn\n\
427 OpFunctionEnd"
428 );
429 }
430
431 #[test]
432 fn test_disassemble_ext_inst_glsl() {
433 let mut b = dr::Builder::new();
434
435 b.capability(spirv::Capability::Shader);
436 let glsl = b.ext_inst_import("GLSL.std.450");
437 b.memory_model(spirv::AddressingModel::Logical, spirv::MemoryModel::Simple);
438
439 let void = b.type_void();
440 let float32 = b.type_float(32, None);
441 let voidfvoid = b.type_function(void, vec![void]);
442
443 assert!(b
444 .begin_function(void, None, spirv::FunctionControl::NONE, voidfvoid)
445 .is_ok());
446 b.begin_block(None).unwrap();
447 let var = b.variable(float32, None, spirv::StorageClass::Function, None);
448 let args = std::iter::once(dr::Operand::IdRef(var));
449 b.ext_inst(float32, None, glsl, 6, args).unwrap();
450 b.ret().unwrap();
451 b.end_function().unwrap();
452
453 assert_eq!(
454 b.module().disassemble(),
455 "; SPIR-V\n\
456 ; Version: 1.6\n\
457 ; Generator: rspirv\n\
458 ; Bound: 9\n\
459 OpCapability Shader\n\
460 %1 = OpExtInstImport \"GLSL.std.450\"\n\
461 OpMemoryModel Logical Simple\n\
462 %2 = OpTypeVoid\n\
463 %3 = OpTypeFloat 32\n\
464 %4 = OpTypeFunction %2 %2\n\
465 %5 = OpFunction %2 None %4\n\
466 %6 = OpLabel\n\
467 %7 = OpVariable %3 Function\n\
468 %8 = OpExtInst %3 %1 FSign %7\n\
469 OpReturn\n\
470 OpFunctionEnd"
471 );
472 }
473
474 #[test]
475 fn test_disassemble_ext_inst_opencl() {
476 let mut b = dr::Builder::new();
477
478 let opencl = b.ext_inst_import("OpenCL.std");
479 b.memory_model(spirv::AddressingModel::Logical, spirv::MemoryModel::OpenCL);
480
481 let void = b.type_void();
482 let float32 = b.type_float(32, None);
483 let voidfvoid = b.type_function(void, vec![void]);
484
485 assert!(b
486 .begin_function(void, None, spirv::FunctionControl::NONE, voidfvoid)
487 .is_ok());
488 b.begin_block(None).unwrap();
489 let var = b.variable(float32, None, spirv::StorageClass::Function, None);
490
491 let args = std::iter::once(dr::Operand::IdRef(var));
492 b.ext_inst(float32, None, opencl, 15, args).unwrap();
493 b.ret().unwrap();
494
495 b.end_function().unwrap();
496
497 assert_eq!(
498 b.module().disassemble(),
499 "; SPIR-V\n\
500 ; Version: 1.6\n\
501 ; Generator: rspirv\n\
502 ; Bound: 9\n\
503 %1 = OpExtInstImport \"OpenCL.std\"\n\
504 OpMemoryModel Logical OpenCL\n\
505 %2 = OpTypeVoid\n\
506 %3 = OpTypeFloat 32\n\
507 %4 = OpTypeFunction %2 %2\n\
508 %5 = OpFunction %2 None %4\n\
509 %6 = OpLabel\n\
510 %7 = OpVariable %3 Function\n\
511 %8 = OpExtInst %3 %1 cosh %7\n\
512 OpReturn\n\
513 OpFunctionEnd"
514 );
515 }
516}