1use crate::unparser::common::push_register;
2use crate::{
3 lexer::PtxToken,
4 r#type::{function::*, variable::ParameterDirective},
5 unparser::*,
6};
7
8fn push_register_components(tokens: &mut Vec<PtxToken>, name: &str) {
9 if let Some(stripped) = name.strip_prefix('%') {
10 let mut parts = stripped.split('.');
11 if let Some(first) = parts.next() {
12 let register_name = format!("%{first}");
13 push_register(tokens, ®ister_name);
14 }
15 for part in parts {
16 if part.is_empty() {
17 continue;
18 }
19 push_directive(tokens, part);
20 }
21 } else {
22 push_identifier(tokens, name);
23 }
24}
25
26fn unparse_param(tokens: &mut Vec<PtxToken>, param: &ParameterDirective, spaced: bool) {
27 match param {
28 ParameterDirective::Parameter {
29 align,
30 ty,
31 ptr,
32 space,
33 name,
34 array,
35 ..
36 } => {
37 push_directive(tokens, "param");
38 push_space(tokens, spaced);
39 ty.unparse_tokens_mode(tokens, spaced);
40 if *ptr {
41 push_directive(tokens, "ptr");
42 }
43 if let Some(address_space) = space {
44 address_space.unparse_tokens_mode(tokens, spaced);
45 }
46 if let Some(value) = align {
47 push_directive(tokens, "align");
48 push_space(tokens, spaced);
49 push_decimal(tokens, *value);
50 }
51 push_space(tokens, spaced);
52 push_identifier(tokens, &name.val);
53 for extent in array {
54 tokens.push(PtxToken::LBracket);
55 if let Some(value) = extent {
56 push_decimal(tokens, *value);
57 }
58 tokens.push(PtxToken::RBracket);
59 }
60 }
61 ParameterDirective::Register { ty, name, .. } => {
62 push_directive(tokens, "reg");
63 push_space(tokens, spaced);
64 ty.unparse_tokens_mode(tokens, spaced);
65 push_space(tokens, spaced);
66 push_register_components(tokens, &name.val);
67 }
68 }
69}
70
71fn unparse_param_list(tokens: &mut Vec<PtxToken>, params: &[ParameterDirective], spaced: bool) {
72 for (idx, param) in params.iter().enumerate() {
73 if idx > 0 {
74 tokens.push(PtxToken::Comma);
75 push_space(tokens, spaced);
76 }
77 unparse_param(tokens, param, spaced);
78 }
79}
80
81fn unparse_section_line(
82 tokens: &mut Vec<PtxToken>,
83 line: &StatementSectionDirectiveLine,
84 spaced: bool,
85) {
86 match line {
87 StatementSectionDirectiveLine::B8 { values, .. } => {
88 push_directive(tokens, "b8");
89 for (idx, value) in values.iter().enumerate() {
90 if idx > 0 {
91 tokens.push(PtxToken::Comma);
92 push_space(tokens, spaced);
93 }
94 push_space(tokens, spaced);
95 push_signed_decimal_i64(tokens, *value as i64);
96 }
97 push_newline(tokens, spaced);
98 }
99 StatementSectionDirectiveLine::B16 { values, .. } => {
100 push_directive(tokens, "b16");
101 for (idx, value) in values.iter().enumerate() {
102 if idx > 0 {
103 tokens.push(PtxToken::Comma);
104 push_space(tokens, spaced);
105 }
106 push_space(tokens, spaced);
107 push_signed_decimal_i64(tokens, *value as i64);
108 }
109 push_newline(tokens, spaced);
110 }
111 StatementSectionDirectiveLine::B32Immediate { values, .. } => {
112 push_directive(tokens, "b32");
113 for (idx, value) in values.iter().enumerate() {
114 if idx > 0 {
115 tokens.push(PtxToken::Comma);
116 push_space(tokens, spaced);
117 }
118 push_space(tokens, spaced);
119 push_signed_decimal_i64(tokens, *value);
120 }
121 push_newline(tokens, spaced);
122 }
123 StatementSectionDirectiveLine::B64Immediate { values, .. } => {
124 push_directive(tokens, "b64");
125 for (idx, value) in values.iter().enumerate() {
126 if idx > 0 {
127 tokens.push(PtxToken::Comma);
128 push_space(tokens, spaced);
129 }
130 push_space(tokens, spaced);
131 push_signed_decimal_i128(tokens, *value);
132 }
133 push_newline(tokens, spaced);
134 }
135 StatementSectionDirectiveLine::B32Label { labels, .. } => {
136 push_directive(tokens, "b32");
137 push_space(tokens, spaced);
138 push_identifier(tokens, &labels.val);
139 push_newline(tokens, spaced);
140 }
141 StatementSectionDirectiveLine::B64Label { labels, .. } => {
142 push_directive(tokens, "b64");
143 push_space(tokens, spaced);
144 push_identifier(tokens, &labels.val);
145 push_newline(tokens, spaced);
146 }
147 StatementSectionDirectiveLine::B32LabelPlusImm { entries, .. } => {
148 push_directive(tokens, "b32");
149 let (label, offset) = entries;
150 push_space(tokens, spaced);
151 push_identifier(tokens, &label.val);
152 if *offset >= 0 {
153 tokens.push(PtxToken::Plus);
154 push_decimal(tokens, *offset);
155 } else {
156 tokens.push(PtxToken::Minus);
157 let magnitude = (*offset as i128).abs();
158 push_decimal(tokens, magnitude);
159 }
160 push_newline(tokens, spaced);
161 }
162 StatementSectionDirectiveLine::B64LabelPlusImm { entries, .. } => {
163 push_directive(tokens, "b64");
164 let (label, offset) = entries;
165 push_space(tokens, spaced);
166 push_identifier(tokens, &label.val);
167 if *offset >= 0 {
168 tokens.push(PtxToken::Plus);
169 push_decimal(tokens, *offset);
170 } else {
171 tokens.push(PtxToken::Minus);
172 let magnitude = (*offset as i128).abs();
173 push_decimal(tokens, magnitude);
174 }
175 push_newline(tokens, spaced);
176 }
177 StatementSectionDirectiveLine::B32LabelDiff { entries, .. } => {
178 push_directive(tokens, "b32");
179 let (left, right) = entries;
180 push_space(tokens, spaced);
181 push_identifier(tokens, &left.val);
182 tokens.push(PtxToken::Minus);
183 push_space(tokens, spaced);
184 push_identifier(tokens, &right.val);
185 push_newline(tokens, spaced);
186 }
187 StatementSectionDirectiveLine::B64LabelDiff { entries, .. } => {
188 push_directive(tokens, "b64");
189 let (left, right) = entries;
190 push_space(tokens, spaced);
191 push_identifier(tokens, &left.val);
192 tokens.push(PtxToken::Minus);
193 push_space(tokens, spaced);
194 push_identifier(tokens, &right.val);
195 push_newline(tokens, spaced);
196 }
197 }
198}
199
200fn push_signed_decimal_i64(tokens: &mut Vec<PtxToken>, value: i64) {
201 if value < 0 {
202 tokens.push(PtxToken::Minus);
203 push_decimal(tokens, (-value) as i128);
204 } else {
205 push_decimal(tokens, value);
206 }
207}
208
209fn push_signed_decimal_i128(tokens: &mut Vec<PtxToken>, value: i128) {
210 if value < 0 {
211 tokens.push(PtxToken::Minus);
212 push_decimal(tokens, -value);
213 } else {
214 push_decimal(tokens, value);
215 }
216}
217
218impl PtxUnparser for RegisterDirective {
219 fn unparse_tokens(&self, tokens: &mut Vec<PtxToken>) {
220 self.unparse_tokens_mode(tokens, false);
221 }
222
223 fn unparse_tokens_mode(&self, tokens: &mut Vec<PtxToken>, spaced: bool) {
224 push_directive(tokens, "reg");
225 push_space(tokens, spaced);
226 self.ty.unparse_tokens_mode(tokens, spaced);
227 for (idx, target) in self.registers.iter().enumerate() {
228 if idx > 0 {
229 tokens.push(PtxToken::Comma);
230 push_space(tokens, spaced);
231 } else {
232 push_space(tokens, spaced);
233 }
234 push_register_components(tokens, &target.name.val);
235 if let Some(range) = target.range {
236 tokens.push(PtxToken::LAngle);
237 push_decimal(tokens, range);
238 tokens.push(PtxToken::RAngle);
239 }
240 }
241 tokens.push(PtxToken::Semicolon);
242 push_newline(tokens, spaced);
243 }
244}
245
246impl PtxUnparser for StatementDirective {
247 fn unparse_tokens(&self, tokens: &mut Vec<PtxToken>) {
248 self.unparse_tokens_mode(tokens, false);
249 }
250
251 fn unparse_tokens_mode(&self, tokens: &mut Vec<PtxToken>, spaced: bool) {
252 match self {
253 StatementDirective::Reg {
254 directive: register,
255 ..
256 } => register.unparse_tokens_mode(tokens, spaced),
257 StatementDirective::Local {
258 directive: variable,
259 ..
260 } => {
261 push_directive(tokens, "local");
262 push_space(tokens, spaced);
263 variable.unparse_tokens_mode(tokens, spaced);
264 }
265 StatementDirective::Param {
266 directive: variable,
267 ..
268 } => {
269 push_directive(tokens, "param");
270 push_space(tokens, spaced);
271 variable.unparse_tokens_mode(tokens, spaced);
272 }
273 StatementDirective::Shared {
274 directive: variable,
275 ..
276 } => {
277 push_directive(tokens, "shared");
278 push_space(tokens, spaced);
279 variable.unparse_tokens_mode(tokens, spaced);
280 }
281 StatementDirective::Pragma {
282 directive: pragma, ..
283 } => {
284 push_directive(tokens, "pragma");
285 push_space(tokens, spaced);
286 let text = match &pragma.kind {
287 PragmaDirectiveKind::Nounroll => "nounroll".to_string(),
288 PragmaDirectiveKind::EnableSmemSpilling => "enable_smem_spilling".to_string(),
289 PragmaDirectiveKind::UsedBytesMask { mask } => {
290 format!("used_bytes_mask {}", mask)
291 }
292 PragmaDirectiveKind::Frequency { value } => {
293 format!("frequency {}", value)
294 }
295 PragmaDirectiveKind::Raw(text) => text.clone(),
296 };
297 tokens.push(PtxToken::StringLiteral(text));
298 tokens.push(PtxToken::Semicolon);
299 push_newline(tokens, spaced);
300 }
301 StatementDirective::BranchTargets { directive, .. } => {
302 push_directive(tokens, "branchtargets");
303 push_space(tokens, spaced);
304 for (idx, label) in directive.labels.iter().enumerate() {
305 if idx > 0 {
306 tokens.push(PtxToken::Comma);
307 push_space(tokens, spaced);
308 }
309 push_token_from_str(tokens, &label.val);
310 }
311 tokens.push(PtxToken::Semicolon);
312 push_newline(tokens, spaced);
313 }
314 StatementDirective::CallTargets { directive, .. } => {
315 push_directive(tokens, "calltargets");
316 push_space(tokens, spaced);
317 for (idx, target) in directive.targets.iter().enumerate() {
318 if idx > 0 {
319 tokens.push(PtxToken::Comma);
320 push_space(tokens, spaced);
321 }
322 push_token_from_str(tokens, &target.val);
323 }
324 tokens.push(PtxToken::Semicolon);
325 push_newline(tokens, spaced);
326 }
327 StatementDirective::Loc { directive: loc, .. } => {
328 push_directive(tokens, "loc");
329 push_space(tokens, spaced);
330 push_decimal(tokens, loc.file_index);
331 push_space(tokens, spaced);
332 push_decimal(tokens, loc.line);
333 push_space(tokens, spaced);
334 push_decimal(tokens, loc.column);
335 if let Some(inline) = &loc.inlined_at {
336 tokens.push(PtxToken::Comma);
337 push_space(tokens, spaced);
338 push_identifier(tokens, "inlined_at");
339 push_space(tokens, spaced);
340 push_decimal(tokens, inline.file_index);
341 push_space(tokens, spaced);
342 push_decimal(tokens, inline.line);
343 push_space(tokens, spaced);
344 push_decimal(tokens, inline.column);
345 tokens.push(PtxToken::Comma);
346 push_space(tokens, spaced);
347 push_identifier(tokens, &inline.function_name.val);
348 push_space(tokens, spaced);
349 push_identifier(tokens, &inline.label.val);
350 if let Some(offset) = inline.label_offset {
351 if offset >= 0 {
352 tokens.push(PtxToken::Plus);
353 } else {
354 tokens.push(PtxToken::Minus);
355 }
356 push_decimal(tokens, offset.abs());
357 }
358 }
359 push_newline(tokens, spaced);
360 }
361 StatementDirective::Dwarf {
362 directive: dwarf, ..
363 } => {
364 dwarf.unparse_tokens_mode(tokens, spaced);
365 push_newline(tokens, spaced);
366 }
367 StatementDirective::Section {
368 directive: section, ..
369 } => {
370 section.unparse_tokens_mode(tokens, spaced);
371 }
372 StatementDirective::CallPrototype { directive, .. } => {
373 push_directive(tokens, "callprototype");
374 push_space(tokens, spaced);
375 if let Some(ret) = &directive.return_param {
376 unparse_param(tokens, ret, spaced);
377 } else {
378 push_identifier(tokens, "_");
379 }
380 tokens.push(PtxToken::LParen);
381 unparse_param_list(tokens, &directive.params, spaced);
382 tokens.push(PtxToken::RParen);
383 if directive.noreturn {
384 push_space(tokens, spaced);
385 push_directive(tokens, "noreturn");
386 }
387 if let Some(value) = directive.abi_preserve {
388 push_space(tokens, spaced);
389 push_directive(tokens, "abi_preserve");
390 push_space(tokens, spaced);
391 push_decimal(tokens, value);
392 }
393 if let Some(value) = directive.abi_preserve_control {
394 push_space(tokens, spaced);
395 push_directive(tokens, "abi_preserve_control");
396 push_space(tokens, spaced);
397 push_decimal(tokens, value);
398 }
399 tokens.push(PtxToken::Semicolon);
400 push_newline(tokens, spaced);
401 }
402 }
403 }
404}
405
406impl PtxUnparser for SectionDirective {
407 fn unparse_tokens(&self, tokens: &mut Vec<PtxToken>) {
408 self.unparse_tokens_mode(tokens, false);
409 }
410
411 fn unparse_tokens_mode(&self, tokens: &mut Vec<PtxToken>, spaced: bool) {
412 push_directive(tokens, "section");
413 push_space(tokens, spaced);
414 push_token_from_str(tokens, &self.name);
415 push_space(tokens, spaced);
416 tokens.push(PtxToken::LBrace);
417 push_newline(tokens, spaced);
418 for entry in &self.entries {
419 match entry {
420 SectionEntry::Label { label, .. } => {
421 push_identifier(tokens, &label.val);
422 tokens.push(PtxToken::Colon);
423 push_newline(tokens, spaced);
424 }
425 SectionEntry::Directive(line) => {
426 unparse_section_line(tokens, line, spaced);
427 }
428 }
429 }
430 tokens.push(PtxToken::RBrace);
431 push_newline(tokens, spaced);
432 }
433}
434
435impl PtxUnparser for FunctionStatement {
436 fn unparse_tokens(&self, tokens: &mut Vec<PtxToken>) {
437 self.unparse_tokens_mode(tokens, false);
438 }
439
440 fn unparse_tokens_mode(&self, tokens: &mut Vec<PtxToken>, spaced: bool) {
441 match self {
442 FunctionStatement::Label { label, .. } => {
443 push_identifier(tokens, &label.val);
444 tokens.push(PtxToken::Colon);
445 push_newline(tokens, spaced);
446 }
447 FunctionStatement::Instruction { instruction, .. } => {
448 instruction.unparse_tokens_mode(tokens, spaced)
449 }
450 FunctionStatement::Directive { directive, .. } => {
451 directive.unparse_tokens_mode(tokens, spaced)
452 }
453 FunctionStatement::Block {
454 statements: block, ..
455 } => {
456 tokens.push(PtxToken::LBrace);
457 for statement in block {
458 statement.unparse_tokens_mode(tokens, spaced);
459 }
460 tokens.push(PtxToken::RBrace);
461 push_newline(tokens, spaced);
462 }
463 }
464 }
465}
466
467impl PtxUnparser for FunctionBody {
468 fn unparse_tokens(&self, tokens: &mut Vec<PtxToken>) {
469 self.unparse_tokens_mode(tokens, false);
470 }
471
472 fn unparse_tokens_mode(&self, tokens: &mut Vec<PtxToken>, spaced: bool) {
473 tokens.push(PtxToken::LBrace);
474 push_newline(tokens, spaced);
475 for statement in &self.statements {
476 statement.unparse_tokens_mode(tokens, spaced);
477 }
478 tokens.push(PtxToken::RBrace);
479 push_newline(tokens, spaced);
480 }
481}
482
483impl PtxUnparser for FunctionDim {
484 fn unparse_tokens(&self, tokens: &mut Vec<PtxToken>) {
485 self.unparse_tokens_mode(tokens, false);
486 }
487
488 fn unparse_tokens_mode(&self, tokens: &mut Vec<PtxToken>, spaced: bool) {
489 match self {
490 FunctionDim::X { x, .. } => {
491 push_decimal(tokens, *x);
492 }
493 FunctionDim::XY { x, y, .. } => {
494 push_decimal(tokens, *x);
495 tokens.push(PtxToken::Comma);
496 push_space(tokens, spaced);
497 push_decimal(tokens, *y);
498 }
499 FunctionDim::XYZ { x, y, z, .. } => {
500 push_decimal(tokens, *x);
501 tokens.push(PtxToken::Comma);
502 push_space(tokens, spaced);
503 push_decimal(tokens, *y);
504 tokens.push(PtxToken::Comma);
505 push_space(tokens, spaced);
506 push_decimal(tokens, *z);
507 }
508 }
509 }
510}
511
512impl PtxUnparser for EntryFunctionHeaderDirective {
513 fn unparse_tokens(&self, tokens: &mut Vec<PtxToken>) {
514 self.unparse_tokens_mode(tokens, false);
515 }
516
517 fn unparse_tokens_mode(&self, tokens: &mut Vec<PtxToken>, spaced: bool) {
518 match self {
519 EntryFunctionHeaderDirective::MaxNReg { value, .. } => {
520 push_directive(tokens, "maxnreg");
521 push_space(tokens, spaced);
522 push_decimal(tokens, *value);
523 }
524 EntryFunctionHeaderDirective::MaxNTid { dim, .. } => {
525 push_directive(tokens, "maxntid");
526 push_space(tokens, spaced);
527 dim.unparse_tokens_mode(tokens, spaced);
528 }
529 EntryFunctionHeaderDirective::ReqNTid { dim, .. } => {
530 push_directive(tokens, "reqntid");
531 push_space(tokens, spaced);
532 dim.unparse_tokens_mode(tokens, spaced);
533 }
534 EntryFunctionHeaderDirective::MinNCtaPerSm { value, .. } => {
535 push_directive(tokens, "minnctapersm");
536 push_space(tokens, spaced);
537 push_decimal(tokens, *value);
538 }
539 EntryFunctionHeaderDirective::MaxNCtaPerSm { value, .. } => {
540 push_directive(tokens, "maxnctapersm");
541 push_space(tokens, spaced);
542 push_decimal(tokens, *value);
543 }
544 EntryFunctionHeaderDirective::Pragma {
545 args: arguments, ..
546 } => {
547 push_directive(tokens, "pragma");
548 push_space(tokens, spaced);
549 for argument in arguments {
550 tokens.push(PtxToken::StringLiteral(argument.clone()));
551 push_space(tokens, spaced);
552 }
553 if spaced {
554 if let Some(last) = tokens.last() {
555 if matches!(last, PtxToken::Space) {
556 tokens.pop();
557 }
558 }
559 }
560 }
561 EntryFunctionHeaderDirective::ReqNctaPerCluster { dim, .. } => {
562 push_directive(tokens, "reqnctapercluster");
563 push_space(tokens, spaced);
564 dim.unparse_tokens_mode(tokens, spaced);
565 }
566 EntryFunctionHeaderDirective::ExplicitCluster { .. } => {
567 push_directive(tokens, "explicitcluster");
568 }
569 EntryFunctionHeaderDirective::MaxClusterRank { value, .. } => {
570 push_directive(tokens, "maxclusterrank");
571 push_space(tokens, spaced);
572 push_decimal(tokens, *value);
573 }
574 EntryFunctionHeaderDirective::BlocksAreClusters { .. } => {
575 push_directive(tokens, "blocksareclusters")
576 }
577 }
578 }
579}
580
581impl PtxUnparser for FuncFunctionHeaderDirective {
582 fn unparse_tokens(&self, tokens: &mut Vec<PtxToken>) {
583 self.unparse_tokens_mode(tokens, false);
584 }
585
586 fn unparse_tokens_mode(&self, tokens: &mut Vec<PtxToken>, spaced: bool) {
587 match self {
588 FuncFunctionHeaderDirective::NoReturn { .. } => push_directive(tokens, "noreturn"),
589 FuncFunctionHeaderDirective::Pragma {
590 args: arguments, ..
591 } => {
592 push_directive(tokens, "pragma");
593 push_space(tokens, spaced);
594 for argument in arguments {
595 tokens.push(PtxToken::StringLiteral(argument.clone()));
596 push_space(tokens, spaced);
597 }
598 if spaced {
599 if let Some(PtxToken::Space) = tokens.last() {
600 tokens.pop();
601 }
602 }
603 }
604 FuncFunctionHeaderDirective::AbiPreserve { value, .. } => {
605 push_directive(tokens, "abi_preserve");
606 push_space(tokens, spaced);
607 push_decimal(tokens, *value);
608 }
609 FuncFunctionHeaderDirective::AbiPreserveControl { value, .. } => {
610 push_directive(tokens, "abi_preserve_control");
611 push_space(tokens, spaced);
612 push_decimal(tokens, *value);
613 }
614 }
615 }
616}
617
618impl PtxUnparser for AliasFunctionDirective {
619 fn unparse_tokens(&self, tokens: &mut Vec<PtxToken>) {
620 self.unparse_tokens_mode(tokens, false);
621 }
622
623 fn unparse_tokens_mode(&self, tokens: &mut Vec<PtxToken>, spaced: bool) {
624 push_directive(tokens, "alias");
625 push_space(tokens, spaced);
626 push_identifier(tokens, &self.alias.val);
627 tokens.push(PtxToken::Comma);
628 push_space(tokens, spaced);
629 push_identifier(tokens, &self.target.val);
630 tokens.push(PtxToken::Semicolon);
631 push_newline(tokens, spaced);
632 }
633}
634
635impl PtxUnparser for EntryFunctionDirective {
636 fn unparse_tokens(&self, tokens: &mut Vec<PtxToken>) {
637 self.unparse_tokens_mode(tokens, false);
638 }
639
640 fn unparse_tokens_mode(&self, tokens: &mut Vec<PtxToken>, spaced: bool) {
641 for directive in &self.directives {
642 directive.unparse_tokens_mode(tokens, spaced);
643 if spaced {
644 push_space(tokens, spaced);
645 }
646 }
647 push_directive(tokens, "entry");
648 push_space(tokens, spaced);
649 push_identifier(tokens, &self.name.val);
650 tokens.push(PtxToken::LParen);
651 unparse_param_list(tokens, &self.params, spaced);
652 tokens.push(PtxToken::RParen);
653 match &self.body {
654 Some(body) => body.unparse_tokens_mode(tokens, spaced),
655 None => {
656 tokens.push(PtxToken::Semicolon);
657 push_newline(tokens, spaced);
658 }
659 }
660 }
661}
662
663impl PtxUnparser for FuncFunctionDirective {
664 fn unparse_tokens(&self, tokens: &mut Vec<PtxToken>) {
665 self.unparse_tokens_mode(tokens, false);
666 }
667
668 fn unparse_tokens_mode(&self, tokens: &mut Vec<PtxToken>, spaced: bool) {
669 for attribute in &self.attributes {
670 attribute.unparse_tokens_mode(tokens, spaced);
671 if spaced {
672 push_space(tokens, spaced);
673 }
674 }
675 for directive in &self.directives {
676 directive.unparse_tokens_mode(tokens, spaced);
677 if spaced {
678 push_space(tokens, spaced);
679 }
680 }
681 push_directive(tokens, "func");
682 if let Some(ret) = &self.return_param {
683 push_space(tokens, spaced);
684 tokens.push(PtxToken::LParen);
685 unparse_param(tokens, ret, spaced);
686 tokens.push(PtxToken::RParen);
687 }
688 push_space(tokens, spaced);
689 push_identifier(tokens, &self.name.val);
690 tokens.push(PtxToken::LParen);
691 unparse_param_list(tokens, &self.params, spaced);
692 tokens.push(PtxToken::RParen);
693 match &self.body {
694 Some(body) => body.unparse_tokens_mode(tokens, spaced),
695 None => {
696 tokens.push(PtxToken::Semicolon);
697 push_newline(tokens, spaced);
698 }
699 }
700 }
701}