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 !ext_inst_set_tracker.have(id) {
252 return inst.disassemble();
253 }
254 if let Some(grammar) = ext_inst_set_tracker.resolve(id, opcode) {
255 let mut operands = vec![inst.operands[0].disassemble(), grammar.opname.to_string()];
256 for operand in &inst.operands[2..] {
257 operands.push(operand.disassemble())
258 }
259 disas_instruction(inst, " ", |_| operands.join(" "))
260 } else {
261 inst.disassemble()
262 }
263 } else {
264 inst.disassemble()
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use crate::binary::Disassemble;
271 use crate::dr;
272 use crate::spirv;
273
274 #[test]
275 fn test_disassemble_operand_function_control() {
276 let o = dr::Operand::FunctionControl(spirv::FunctionControl::NONE);
277 assert_eq!("None", o.disassemble());
278 let o = dr::Operand::FunctionControl(spirv::FunctionControl::INLINE);
279 assert_eq!("Inline", o.disassemble());
280 let o = dr::Operand::FunctionControl(
281 spirv::FunctionControl::INLINE | spirv::FunctionControl::PURE,
282 );
283 assert_eq!("Inline|Pure", o.disassemble());
284 let o = dr::Operand::FunctionControl(spirv::FunctionControl::all());
285 assert_eq!("Inline|DontInline|Pure|Const|OptNoneINTEL", o.disassemble());
286 }
287
288 #[test]
289 fn test_disassemble_operand_memory_semantics() {
290 let o = dr::Operand::MemorySemantics(spirv::MemorySemantics::NONE);
291 assert_eq!("None", o.disassemble());
292 let o = dr::Operand::MemorySemantics(spirv::MemorySemantics::RELAXED);
293 assert_eq!("None", o.disassemble());
294 let o = dr::Operand::MemorySemantics(spirv::MemorySemantics::RELEASE);
295 assert_eq!("Release", o.disassemble());
296 let o = dr::Operand::MemorySemantics(
297 spirv::MemorySemantics::RELEASE | spirv::MemorySemantics::WORKGROUP_MEMORY,
298 );
299 assert_eq!("Release|WorkgroupMemory", o.disassemble());
300 }
301
302 #[test]
303 fn test_disassemble_module_one_inst_in_each_section() {
304 let mut b = dr::Builder::new();
305
306 b.capability(spirv::Capability::Shader);
307 b.extension("awesome-extension");
308 b.ext_inst_import("GLSL.std.450");
309 b.memory_model(spirv::AddressingModel::Logical, spirv::MemoryModel::Simple);
310 b.source(spirv::SourceLanguage::GLSL, 450, None, None::<String>);
311
312 let void = b.type_void();
313 let float32 = b.type_float(32);
314 let voidfvoid = b.type_function(void, vec![void]);
315
316 let f = b
317 .begin_function(
318 void,
319 None,
320 spirv::FunctionControl::DONT_INLINE | spirv::FunctionControl::CONST,
321 voidfvoid,
322 )
323 .unwrap();
324 b.begin_block(None).unwrap();
325 let var = b.variable(float32, None, spirv::StorageClass::Function, None);
326 b.ret().unwrap();
327 b.end_function().unwrap();
328
329 b.entry_point(spirv::ExecutionModel::Fragment, f, "main", vec![]);
330 b.execution_mode(f, spirv::ExecutionMode::OriginUpperLeft, vec![]);
331 b.name(f, "main");
332 b.decorate(var, spirv::Decoration::RelaxedPrecision, vec![]);
333
334 assert_eq!(
335 b.module().disassemble(),
336 "; SPIR-V\n\
337 ; Version: 1.6\n\
338 ; Generator: rspirv\n\
339 ; Bound: 8\n\
340 OpCapability Shader\n\
341 OpExtension \"awesome-extension\"\n\
342 %1 = OpExtInstImport \"GLSL.std.450\"\n\
343 OpMemoryModel Logical Simple\n\
344 OpEntryPoint Fragment %5 \"main\"\n\
345 OpExecutionMode %5 OriginUpperLeft\n\
346 OpSource GLSL 450\n\
347 OpName %5 \"main\"\n\
348 OpDecorate %7 RelaxedPrecision\n\
349 %2 = OpTypeVoid\n\
350 %3 = OpTypeFloat 32\n\
351 %4 = OpTypeFunction %2 %2\n\
352 %5 = OpFunction %2 DontInline|Const %4\n\
353 %6 = OpLabel\n\
354 %7 = OpVariable %3 Function\n\
355 OpReturn\n\
356 OpFunctionEnd"
357 );
358 }
359
360 #[test]
361 fn test_disassemble_literal_bit_constants() {
362 let mut b = dr::Builder::new();
363
364 b.capability(spirv::Capability::Shader);
365 b.ext_inst_import("GLSL.std.450");
366 b.source(spirv::SourceLanguage::GLSL, 450, None, None::<String>);
367
368 let void = b.type_void();
369 let int32 = b.type_int(32, 1);
370 let int64 = b.type_int(64, 1);
371 let uint32 = b.type_int(32, 0);
372 let uint64 = b.type_int(64, 0);
373 let float32 = b.type_float(32);
374 let float64 = b.type_float(64);
375 let voidfvoid = b.type_function(void, vec![void]);
376
377 let f = b
378 .begin_function(
379 void,
380 None,
381 spirv::FunctionControl::DONT_INLINE | spirv::FunctionControl::CONST,
382 voidfvoid,
383 )
384 .unwrap();
385 b.begin_block(None).unwrap();
386 let signed_i32_value: i32 = -1;
387 let signed_i64_value: i64 = -1;
388 let f32_value: f32 = -2.0;
389 let f64_value: f64 = 9.26;
390 b.constant_bit32(int32, signed_i32_value as u32);
391 b.constant_bit64(int64, signed_i64_value as u64);
392 b.constant_bit32(uint32, signed_i32_value as u32);
393 b.constant_bit64(uint64, signed_i64_value as u64);
394 b.constant_bit32(float32, f32_value.to_bits());
395 b.constant_bit64(float64, f64_value.to_bits());
396 b.ret().unwrap();
397 b.end_function().unwrap();
398
399 b.entry_point(spirv::ExecutionModel::Fragment, f, "main", vec![]);
400 b.execution_mode(f, spirv::ExecutionMode::OriginUpperLeft, vec![]);
401 b.name(f, "main");
402
403 assert_eq!(
404 b.module().disassemble(),
405 "; SPIR-V\n\
406 ; Version: 1.6\n\
407 ; Generator: rspirv\n\
408 ; Bound: 18\n\
409 OpCapability Shader\n\
410 %1 = OpExtInstImport \"GLSL.std.450\"\n\
411 OpEntryPoint Fragment %10 \"main\"\n\
412 OpExecutionMode %10 OriginUpperLeft\n\
413 OpSource GLSL 450\n\
414 OpName %10 \"main\"\n\
415 %2 = OpTypeVoid\n\
416 %3 = OpTypeInt 32 1\n\
417 %4 = OpTypeInt 64 1\n\
418 %5 = OpTypeInt 32 0\n\
419 %6 = OpTypeInt 64 0\n\
420 %7 = OpTypeFloat 32\n\
421 %8 = OpTypeFloat 64\n\
422 %9 = OpTypeFunction %2 %2\n\
423 %12 = OpConstant %3 -1\n\
424 %13 = OpConstant %4 -1\n\
425 %14 = OpConstant %5 4294967295\n\
426 %15 = OpConstant %6 18446744073709551615\n\
427 %16 = OpConstant %7 -2\n\
428 %17 = OpConstant %8 9.26\n\
429 %10 = OpFunction %2 DontInline|Const %9\n\
430 %11 = OpLabel\n\
431 OpReturn\n\
432 OpFunctionEnd"
433 );
434 }
435
436 #[test]
437 fn test_disassemble_ext_inst_glsl() {
438 let mut b = dr::Builder::new();
439
440 b.capability(spirv::Capability::Shader);
441 let glsl = b.ext_inst_import("GLSL.std.450");
442 b.memory_model(spirv::AddressingModel::Logical, spirv::MemoryModel::Simple);
443
444 let void = b.type_void();
445 let float32 = b.type_float(32);
446 let voidfvoid = b.type_function(void, vec![void]);
447
448 assert!(b
449 .begin_function(void, None, spirv::FunctionControl::NONE, voidfvoid)
450 .is_ok());
451 b.begin_block(None).unwrap();
452 let var = b.variable(float32, None, spirv::StorageClass::Function, None);
453 let args = std::iter::once(dr::Operand::IdRef(var));
454 b.ext_inst(float32, None, glsl, 6, args).unwrap();
455 b.ret().unwrap();
456 b.end_function().unwrap();
457
458 assert_eq!(
459 b.module().disassemble(),
460 "; SPIR-V\n\
461 ; Version: 1.6\n\
462 ; Generator: rspirv\n\
463 ; Bound: 9\n\
464 OpCapability Shader\n\
465 %1 = OpExtInstImport \"GLSL.std.450\"\n\
466 OpMemoryModel Logical Simple\n\
467 %2 = OpTypeVoid\n\
468 %3 = OpTypeFloat 32\n\
469 %4 = OpTypeFunction %2 %2\n\
470 %5 = OpFunction %2 None %4\n\
471 %6 = OpLabel\n\
472 %7 = OpVariable %3 Function\n\
473 %8 = OpExtInst %3 %1 FSign %7\n\
474 OpReturn\n\
475 OpFunctionEnd"
476 );
477 }
478
479 #[test]
480 fn test_disassemble_ext_inst_opencl() {
481 let mut b = dr::Builder::new();
482
483 let opencl = b.ext_inst_import("OpenCL.std");
484 b.memory_model(spirv::AddressingModel::Logical, spirv::MemoryModel::OpenCL);
485
486 let void = b.type_void();
487 let float32 = b.type_float(32);
488 let voidfvoid = b.type_function(void, vec![void]);
489
490 assert!(b
491 .begin_function(void, None, spirv::FunctionControl::NONE, voidfvoid)
492 .is_ok());
493 b.begin_block(None).unwrap();
494 let var = b.variable(float32, None, spirv::StorageClass::Function, None);
495
496 let args = std::iter::once(dr::Operand::IdRef(var));
497 b.ext_inst(float32, None, opencl, 15, args).unwrap();
498 b.ret().unwrap();
499
500 b.end_function().unwrap();
501
502 assert_eq!(
503 b.module().disassemble(),
504 "; SPIR-V\n\
505 ; Version: 1.6\n\
506 ; Generator: rspirv\n\
507 ; Bound: 9\n\
508 %1 = OpExtInstImport \"OpenCL.std\"\n\
509 OpMemoryModel Logical OpenCL\n\
510 %2 = OpTypeVoid\n\
511 %3 = OpTypeFloat 32\n\
512 %4 = OpTypeFunction %2 %2\n\
513 %5 = OpFunction %2 None %4\n\
514 %6 = OpLabel\n\
515 %7 = OpVariable %3 Function\n\
516 %8 = OpExtInst %3 %1 cosh %7\n\
517 OpReturn\n\
518 OpFunctionEnd"
519 );
520 }
521}