1 /*
2 * Copyright (c) 2018 Lima Project
3 *
4 * Copyright (c) 2013 Codethink (http://www.codethink.co.uk)
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sub license,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 *
25 */
26
27 #include "util/half_float.h"
28
29 #include "ppir.h"
30 #include "codegen.h"
31
32 typedef struct {
33 char *name;
34 unsigned srcs;
35 } asm_op;
36
37 static void
print_swizzle(uint8_t swizzle)38 print_swizzle(uint8_t swizzle)
39 {
40 if (swizzle == 0xE4)
41 return;
42
43 printf(".");
44 for (unsigned i = 0; i < 4; i++, swizzle >>= 2)
45 printf("%c", "xyzw"[swizzle & 3]);
46 }
47
48 static void
print_mask(uint8_t mask)49 print_mask(uint8_t mask)
50 {
51 if (mask == 0xF)
52 return;
53
54 printf(".");
55 if (mask & 1) printf("x");
56 if (mask & 2) printf("y");
57 if (mask & 4) printf("z");
58 if (mask & 8) printf("w");
59 }
60
61 static void
print_reg(ppir_codegen_vec4_reg reg,const char * special)62 print_reg(ppir_codegen_vec4_reg reg, const char *special)
63 {
64 if (special) {
65 printf("%s", special);
66 } else {
67 switch (reg)
68 {
69 case ppir_codegen_vec4_reg_constant0:
70 printf("^const0");
71 break;
72 case ppir_codegen_vec4_reg_constant1:
73 printf("^const1");
74 break;
75 case ppir_codegen_vec4_reg_texture:
76 printf("^texture");
77 break;
78 case ppir_codegen_vec4_reg_uniform:
79 printf("^uniform");
80 break;
81 default:
82 printf("$%u", reg);
83 break;
84 }
85 }
86 }
87
88 static void
print_vector_source(ppir_codegen_vec4_reg reg,const char * special,uint8_t swizzle,bool abs,bool neg)89 print_vector_source(ppir_codegen_vec4_reg reg, const char *special,
90 uint8_t swizzle, bool abs, bool neg)
91 {
92 if (neg)
93 printf("-");
94 if (abs)
95 printf("abs(");
96
97 print_reg(reg, special);
98 print_swizzle(swizzle);
99
100 if (abs)
101 printf(")");
102 }
103
104 static void
print_source_scalar(unsigned reg,const char * special,bool abs,bool neg)105 print_source_scalar(unsigned reg, const char *special, bool abs, bool neg)
106 {
107 if (neg)
108 printf("-");
109 if (abs)
110 printf("abs(");
111
112 print_reg(reg >> 2, special);
113 if (!special)
114 printf(".%c", "xyzw"[reg & 3]);
115
116 if (abs)
117 printf(")");
118 }
119
120 static void
print_varying_source(ppir_codegen_field_varying * varying)121 print_varying_source(ppir_codegen_field_varying *varying)
122 {
123 switch (varying->imm.alignment) {
124 case 0:
125 printf("%u.%c", varying->imm.index >> 2,
126 "xyzw"[varying->imm.index & 3]);
127 break;
128 case 1: {
129 const char *c[2] = {"xy", "zw"};
130 printf("%u.%s", varying->imm.index >> 1, c[varying->imm.index & 1]);
131 break;
132 }
133 default:
134 printf("%u", varying->imm.index);
135 break;
136 }
137
138 if (varying->imm.offset_vector != 15) {
139 unsigned reg = (varying->imm.offset_vector << 2) +
140 varying->imm.offset_scalar;
141 printf("+");
142 print_source_scalar(reg, NULL, false, false);
143 }
144 }
145
146 static void
print_outmod(ppir_codegen_outmod modifier)147 print_outmod(ppir_codegen_outmod modifier)
148 {
149 switch (modifier)
150 {
151 case ppir_codegen_outmod_clamp_fraction:
152 printf(".sat");
153 break;
154 case ppir_codegen_outmod_clamp_positive:
155 printf(".pos");
156 break;
157 case ppir_codegen_outmod_round:
158 printf(".int");
159 break;
160 default:
161 break;
162 }
163 }
164
165 static void
print_dest_scalar(unsigned reg)166 print_dest_scalar(unsigned reg)
167 {
168 printf("$%u", reg >> 2);
169 printf(".%c ", "xyzw"[reg & 3]);
170 }
171
172 static void
print_const(unsigned const_num,uint16_t * val)173 print_const(unsigned const_num, uint16_t *val)
174 {
175 printf("const%u", const_num);
176 for (unsigned i = 0; i < 4; i++)
177 printf(" %f", _mesa_half_to_float(val[i]));
178 }
179
180 static void
print_const0(void * code,unsigned offset)181 print_const0(void *code, unsigned offset)
182 {
183 (void) offset;
184
185 print_const(0, code);
186 }
187
188 static void
print_const1(void * code,unsigned offset)189 print_const1(void *code, unsigned offset)
190 {
191 (void) offset;
192
193 print_const(1, code);
194 }
195
196 static void
print_varying(void * code,unsigned offset)197 print_varying(void *code, unsigned offset)
198 {
199 (void) offset;
200 ppir_codegen_field_varying *varying = code;
201
202 printf("load");
203
204 bool perspective = varying->imm.source_type < 2 && varying->imm.perspective;
205 if (perspective)
206 {
207 printf(".perspective");
208 switch (varying->imm.perspective)
209 {
210 case 2:
211 printf(".z");
212 break;
213 case 3:
214 printf(".w");
215 break;
216 default:
217 printf(".unknown");
218 break;
219 }
220 }
221
222 printf(".v ");
223
224 switch (varying->imm.dest)
225 {
226 case ppir_codegen_vec4_reg_discard:
227 printf("^discard");
228 break;
229 default:
230 printf("$%u", varying->imm.dest);
231 break;
232 }
233 print_mask(varying->imm.mask);
234 printf(" ");
235
236 switch (varying->imm.source_type) {
237 case 1:
238 print_vector_source(varying->reg.source, NULL, varying->reg.swizzle,
239 varying->reg.absolute, varying->reg.negate);
240 break;
241 case 2:
242 switch (varying->imm.perspective) {
243 case 0:
244 printf("cube(");
245 print_varying_source(varying);
246 printf(")");
247 break;
248 case 1:
249 printf("cube(");
250 print_vector_source(varying->reg.source, NULL, varying->reg.swizzle,
251 varying->reg.absolute, varying->reg.negate);
252 printf(")");
253 break;
254 case 2:
255 printf("normalize(");
256 print_vector_source(varying->reg.source, NULL, varying->reg.swizzle,
257 varying->reg.absolute, varying->reg.negate);
258 printf(")");
259 break;
260 default:
261 printf("gl_FragCoord");
262 break;
263 }
264 break;
265 case 3:
266 if (varying->imm.perspective)
267 printf("gl_FrontFacing");
268 else
269 printf("gl_PointCoord");
270 break;
271 default:
272 print_varying_source(varying);
273 break;
274 }
275 }
276
277 static void
print_sampler(void * code,unsigned offset)278 print_sampler(void *code, unsigned offset)
279 {
280 (void) offset;
281 ppir_codegen_field_sampler *sampler = code;
282
283 printf("texld");
284 if (sampler->lod_bias_en)
285 printf(".b");
286
287 switch (sampler->type) {
288 case ppir_codegen_sampler_type_2d:
289 printf(".2d");
290 break;
291 case ppir_codegen_sampler_type_cube:
292 printf(".cube");
293 break;
294 default:
295 printf("_t%u", sampler->type);
296 break;
297 }
298
299 printf(" %u", sampler->index);
300
301 if (sampler->offset_en)
302 {
303 printf("+");
304 print_source_scalar(sampler->index_offset, NULL, false, false);
305 }
306
307 if (sampler->lod_bias_en)
308 {
309 printf(" ");
310 print_source_scalar(sampler->lod_bias, NULL, false, false);
311 }
312 }
313
314 static void
print_uniform(void * code,unsigned offset)315 print_uniform(void *code, unsigned offset)
316 {
317 (void) offset;
318 ppir_codegen_field_uniform *uniform = code;
319
320 printf("load.");
321
322 switch (uniform->source) {
323 case ppir_codegen_uniform_src_uniform:
324 printf("u");
325 break;
326 case ppir_codegen_uniform_src_temporary:
327 printf("t");
328 break;
329 default:
330 printf(".u%u", uniform->source);
331 break;
332 }
333
334 int16_t index = uniform->index;
335 switch (uniform->alignment) {
336 case 2:
337 printf(" %d", index);
338 break;
339 case 1:
340 printf(" %d.%s", index / 2, (index & 1) ? "zw" : "xy");
341 break;
342 default:
343 printf(" %d.%c", index / 4, "xyzw"[index & 3]);
344 break;
345 }
346
347 if (uniform->offset_en) {
348 printf("+");
349 print_source_scalar(uniform->offset_reg, NULL, false, false);
350 }
351 }
352
353 #define CASE(_name, _srcs) \
354 [ppir_codegen_vec4_mul_op_##_name] = { \
355 .name = #_name, \
356 .srcs = _srcs \
357 }
358
359 static const asm_op vec4_mul_ops[] = {
360 [0 ... 7] = {
361 .name = "mul",
362 .srcs = 2
363 },
364 CASE(not, 1),
365 CASE(and, 2),
366 CASE(or, 2),
367 CASE(xor, 2),
368 CASE(ne, 2),
369 CASE(gt, 2),
370 CASE(ge, 2),
371 CASE(eq, 2),
372 CASE(min, 2),
373 CASE(max, 2),
374 CASE(mov, 1),
375 };
376
377 #undef CASE
378
379 static void
print_vec4_mul(void * code,unsigned offset)380 print_vec4_mul(void *code, unsigned offset)
381 {
382 (void) offset;
383 ppir_codegen_field_vec4_mul *vec4_mul = code;
384
385 asm_op op = vec4_mul_ops[vec4_mul->op];
386
387 if (op.name)
388 printf("%s", op.name);
389 else
390 printf("op%u", vec4_mul->op);
391 print_outmod(vec4_mul->dest_modifier);
392 printf(".v0 ");
393
394 if (vec4_mul->mask) {
395 printf("$%u", vec4_mul->dest);
396 print_mask(vec4_mul->mask);
397 printf(" ");
398 }
399
400 print_vector_source(vec4_mul->arg0_source, NULL,
401 vec4_mul->arg0_swizzle,
402 vec4_mul->arg0_absolute,
403 vec4_mul->arg0_negate);
404
405 if (vec4_mul->op < 8 && vec4_mul->op != 0) {
406 printf("<<%u", vec4_mul->op);
407 }
408
409 printf(" ");
410
411 if (op.srcs > 1) {
412 print_vector_source(vec4_mul->arg1_source, NULL,
413 vec4_mul->arg1_swizzle,
414 vec4_mul->arg1_absolute,
415 vec4_mul->arg1_negate);
416 }
417 }
418
419 #define CASE(_name, _srcs) \
420 [ppir_codegen_vec4_acc_op_##_name] = { \
421 .name = #_name, \
422 .srcs = _srcs \
423 }
424
425 static const asm_op vec4_acc_ops[] = {
426 CASE(add, 2),
427 CASE(fract, 1),
428 CASE(ne, 2),
429 CASE(gt, 2),
430 CASE(ge, 2),
431 CASE(eq, 2),
432 CASE(floor, 1),
433 CASE(ceil, 1),
434 CASE(min, 2),
435 CASE(max, 2),
436 CASE(sum3, 1),
437 CASE(sum4, 1),
438 CASE(dFdx, 2),
439 CASE(dFdy, 2),
440 CASE(sel, 2),
441 CASE(mov, 1),
442 };
443
444 #undef CASE
445
446 static void
print_vec4_acc(void * code,unsigned offset)447 print_vec4_acc(void *code, unsigned offset)
448 {
449 (void) offset;
450 ppir_codegen_field_vec4_acc *vec4_acc = code;
451
452 asm_op op = vec4_acc_ops[vec4_acc->op];
453
454 if (op.name)
455 printf("%s", op.name);
456 else
457 printf("op%u", vec4_acc->op);
458 print_outmod(vec4_acc->dest_modifier);
459 printf(".v1 ");
460
461 if (vec4_acc->mask) {
462 printf("$%u", vec4_acc->dest);
463 print_mask(vec4_acc->mask);
464 printf(" ");
465 }
466
467 print_vector_source(vec4_acc->arg0_source, vec4_acc->mul_in ? "^v0" : NULL,
468 vec4_acc->arg0_swizzle,
469 vec4_acc->arg0_absolute,
470 vec4_acc->arg0_negate);
471
472 if (op.srcs > 1) {
473 printf(" ");
474 print_vector_source(vec4_acc->arg1_source, NULL,
475 vec4_acc->arg1_swizzle,
476 vec4_acc->arg1_absolute,
477 vec4_acc->arg1_negate);
478 }
479 }
480
481 #define CASE(_name, _srcs) \
482 [ppir_codegen_float_mul_op_##_name] = { \
483 .name = #_name, \
484 .srcs = _srcs \
485 }
486
487 static const asm_op float_mul_ops[] = {
488 [0 ... 7] = {
489 .name = "mul",
490 .srcs = 2
491 },
492 CASE(not, 1),
493 CASE(and, 2),
494 CASE(or, 2),
495 CASE(xor, 2),
496 CASE(ne, 2),
497 CASE(gt, 2),
498 CASE(ge, 2),
499 CASE(eq, 2),
500 CASE(min, 2),
501 CASE(max, 2),
502 CASE(mov, 1),
503 };
504
505 #undef CASE
506
507 static void
print_float_mul(void * code,unsigned offset)508 print_float_mul(void *code, unsigned offset)
509 {
510 (void) offset;
511 ppir_codegen_field_float_mul *float_mul = code;
512
513 asm_op op = float_mul_ops[float_mul->op];
514
515 if (op.name)
516 printf("%s", op.name);
517 else
518 printf("op%u", float_mul->op);
519 print_outmod(float_mul->dest_modifier);
520 printf(".s0 ");
521
522 if (float_mul->output_en)
523 print_dest_scalar(float_mul->dest);
524
525 print_source_scalar(float_mul->arg0_source, NULL,
526 float_mul->arg0_absolute,
527 float_mul->arg0_negate);
528
529 if (float_mul->op < 8 && float_mul->op != 0) {
530 printf("<<%u", float_mul->op);
531 }
532
533 if (op.srcs > 1) {
534 printf(" ");
535
536 print_source_scalar(float_mul->arg1_source, NULL,
537 float_mul->arg1_absolute,
538 float_mul->arg1_negate);
539 }
540 }
541
542 #define CASE(_name, _srcs) \
543 [ppir_codegen_float_acc_op_##_name] = { \
544 .name = #_name, \
545 .srcs = _srcs \
546 }
547
548 static const asm_op float_acc_ops[] = {
549 CASE(add, 2),
550 CASE(fract, 1),
551 CASE(ne, 2),
552 CASE(gt, 2),
553 CASE(ge, 2),
554 CASE(eq, 2),
555 CASE(floor, 1),
556 CASE(ceil, 1),
557 CASE(min, 2),
558 CASE(max, 2),
559 CASE(dFdx, 2),
560 CASE(dFdy, 2),
561 CASE(sel, 2),
562 CASE(mov, 1),
563 };
564
565 #undef CASE
566
567 static void
print_float_acc(void * code,unsigned offset)568 print_float_acc(void *code, unsigned offset)
569 {
570 (void) offset;
571 ppir_codegen_field_float_acc *float_acc = code;
572
573 asm_op op = float_acc_ops[float_acc->op];
574
575 if (op.name)
576 printf("%s", op.name);
577 else
578 printf("op%u", float_acc->op);
579 print_outmod(float_acc->dest_modifier);
580 printf(".s1 ");
581
582 if (float_acc->output_en)
583 print_dest_scalar(float_acc->dest);
584
585 print_source_scalar(float_acc->arg0_source, float_acc->mul_in ? "^s0" : NULL,
586 float_acc->arg0_absolute,
587 float_acc->arg0_negate);
588
589 if (op.srcs > 1) {
590 printf(" ");
591 print_source_scalar(float_acc->arg1_source, NULL,
592 float_acc->arg1_absolute,
593 float_acc->arg1_negate);
594 }
595 }
596
597 #define CASE(_name, _srcs) \
598 [ppir_codegen_combine_scalar_op_##_name] = { \
599 .name = #_name, \
600 .srcs = _srcs \
601 }
602
603 static const asm_op combine_ops[] = {
604 CASE(rcp, 1),
605 CASE(mov, 1),
606 CASE(sqrt, 1),
607 CASE(rsqrt, 1),
608 CASE(exp2, 1),
609 CASE(log2, 1),
610 CASE(sin, 1),
611 CASE(cos, 1),
612 CASE(atan, 1),
613 CASE(atan2, 1),
614 };
615
616 #undef CASE
617
618 static void
print_combine(void * code,unsigned offset)619 print_combine(void *code, unsigned offset)
620 {
621 (void) offset;
622 ppir_codegen_field_combine *combine = code;
623
624 if (combine->scalar.dest_vec &&
625 combine->scalar.arg1_en) {
626 /* This particular combination can only be valid for scalar * vector
627 * multiplies, and the opcode field is reused for something else.
628 */
629 printf("mul");
630 } else {
631 asm_op op = combine_ops[combine->scalar.op];
632
633 if (op.name)
634 printf("%s", op.name);
635 else
636 printf("op%u", combine->scalar.op);
637 }
638
639 if (!combine->scalar.dest_vec)
640 print_outmod(combine->scalar.dest_modifier);
641 printf(".s2 ");
642
643 if (combine->scalar.dest_vec) {
644 printf("$%u", combine->vector.dest);
645 print_mask(combine->vector.mask);
646 } else {
647 print_dest_scalar(combine->scalar.dest);
648 }
649 printf(" ");
650
651 print_source_scalar(combine->scalar.arg0_src, NULL,
652 combine->scalar.arg0_absolute,
653 combine->scalar.arg0_negate);
654 printf(" ");
655
656 if (combine->scalar.arg1_en) {
657 if (combine->scalar.dest_vec) {
658 print_vector_source(combine->vector.arg1_source, NULL,
659 combine->vector.arg1_swizzle,
660 false, false);
661 } else {
662 print_source_scalar(combine->scalar.arg1_src, NULL,
663 combine->scalar.arg1_absolute,
664 combine->scalar.arg1_negate);
665 }
666 }
667 }
668
669 static void
print_temp_write(void * code,unsigned offset)670 print_temp_write(void *code, unsigned offset)
671 {
672 (void) offset;
673 ppir_codegen_field_temp_write *temp_write = code;
674
675 if (temp_write->fb_read.unknown_0 == 0x7) {
676 if (temp_write->fb_read.source)
677 printf("fb_color");
678 else
679 printf("fb_depth");
680 printf(" $%u", temp_write->fb_read.dest);
681
682 return;
683 }
684
685 printf("store.t");
686
687 int16_t index = temp_write->temp_write.index;
688 switch (temp_write->temp_write.alignment) {
689 case 2:
690 printf(" %d", index);
691 break;
692 case 1:
693 printf(" %d.%s", index / 2, (index & 1) ? "zw" : "xy");
694 break;
695 default:
696 printf(" %d.%c", index / 4, "xyzw"[index & 3]);
697 break;
698 }
699
700 if (temp_write->temp_write.offset_en) {
701 printf("+");
702 print_source_scalar(temp_write->temp_write.offset_reg,
703 NULL, false, false);
704 }
705
706 printf(" ");
707
708 if (temp_write->temp_write.alignment) {
709 print_reg(temp_write->temp_write.source >> 2, NULL);
710 } else {
711 print_source_scalar(temp_write->temp_write.source, NULL, false, false);
712 }
713 }
714
715 static void
print_branch(void * code,unsigned offset)716 print_branch(void *code, unsigned offset)
717 {
718 ppir_codegen_field_branch *branch = code;
719
720 if (branch->discard.word0 == PPIR_CODEGEN_DISCARD_WORD0 &&
721 branch->discard.word1 == PPIR_CODEGEN_DISCARD_WORD1 &&
722 branch->discard.word2 == PPIR_CODEGEN_DISCARD_WORD2) {
723 printf("discard");
724 return;
725 }
726
727
728 const char* cond[] = {
729 "nv", "lt", "eq", "le",
730 "gt", "ne", "ge", "" ,
731 };
732
733 unsigned cond_mask = 0;
734 cond_mask |= (branch->branch.cond_lt ? 1 : 0);
735 cond_mask |= (branch->branch.cond_eq ? 2 : 0);
736 cond_mask |= (branch->branch.cond_gt ? 4 : 0);
737 printf("branch");
738 if (cond_mask != 0x7) {
739 printf(".%s ", cond[cond_mask]);
740 print_source_scalar(branch->branch.arg0_source, NULL, false, false);
741 printf(" ");
742 print_source_scalar(branch->branch.arg1_source, NULL, false, false);
743 }
744
745 printf(" %d", branch->branch.target + offset);
746 }
747
748 typedef void (*print_field_func)(void *, unsigned);
749
750 static const print_field_func print_field[ppir_codegen_field_shift_count] = {
751 [ppir_codegen_field_shift_varying] = print_varying,
752 [ppir_codegen_field_shift_sampler] = print_sampler,
753 [ppir_codegen_field_shift_uniform] = print_uniform,
754 [ppir_codegen_field_shift_vec4_mul] = print_vec4_mul,
755 [ppir_codegen_field_shift_float_mul] = print_float_mul,
756 [ppir_codegen_field_shift_vec4_acc] = print_vec4_acc,
757 [ppir_codegen_field_shift_float_acc] = print_float_acc,
758 [ppir_codegen_field_shift_combine] = print_combine,
759 [ppir_codegen_field_shift_temp_write] = print_temp_write,
760 [ppir_codegen_field_shift_branch] = print_branch,
761 [ppir_codegen_field_shift_vec4_const_0] = print_const0,
762 [ppir_codegen_field_shift_vec4_const_1] = print_const1,
763 };
764
765 static const int ppir_codegen_field_size[] = {
766 34, 62, 41, 43, 30, 44, 31, 30, 41, 73, 64, 64
767 };
768
769 static void
bitcopy(char * src,char * dst,unsigned bits,unsigned src_offset)770 bitcopy(char *src, char *dst, unsigned bits, unsigned src_offset)
771 {
772 src += src_offset / 8;
773 src_offset %= 8;
774
775 for (int b = bits; b > 0; b -= 8, src++, dst++) {
776 unsigned char out = ((unsigned char) *src) >> src_offset;
777 if (src_offset > 0 && src_offset + b > 8)
778 out |= ((unsigned char) *(src + 1)) << (8 - src_offset);
779 *dst = (char) out;
780 }
781 }
782
783 void
ppir_disassemble_instr(uint32_t * instr,unsigned offset)784 ppir_disassemble_instr(uint32_t *instr, unsigned offset)
785 {
786 ppir_codegen_ctrl *ctrl = (ppir_codegen_ctrl *) instr;
787
788 char *instr_code = (char *) (instr + 1);
789 unsigned bit_offset = 0;
790 bool first = true;
791 for (unsigned i = 0; i < ppir_codegen_field_shift_count; i++) {
792 char code[12];
793
794 if (!((ctrl->fields >> i) & 1))
795 continue;
796
797 unsigned bits = ppir_codegen_field_size[i];
798 bitcopy(instr_code, code, bits, bit_offset);
799
800 if (first)
801 first = false;
802 else
803 printf(", ");
804
805 print_field[i](code, offset);
806
807 bit_offset += bits;
808 }
809
810 if (ctrl->sync)
811 printf(", sync");
812 if (ctrl->stop)
813 printf(", stop");
814
815 printf("\n");
816 }
817
818