2 * Copyright (C) 2009 Nicolai Haehnle.
3 * Copyright 2010 Tom Stellard <tstellar@gmail.com>
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial
17 * portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
23 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 #include "radeon_dataflow.h"
31 #include "radeon_compiler.h"
32 #include "radeon_compiler_util.h"
33 #include "radeon_program.h"
35 struct read_write_mask_data
{
37 rc_read_write_mask_fn Cb
;
40 static void reads_normal_callback(
42 struct rc_instruction
* fullinst
,
43 struct rc_src_register
* src
)
45 struct read_write_mask_data
* cb_data
= userdata
;
46 unsigned int refmask
= 0;
48 for(chan
= 0; chan
< 4; chan
++) {
49 refmask
|= 1 << GET_SWZ(src
->Swizzle
, chan
);
51 refmask
&= RC_MASK_XYZW
;
54 cb_data
->Cb(cb_data
->UserData
, fullinst
, src
->File
,
58 if (refmask
&& src
->RelAddr
) {
59 cb_data
->Cb(cb_data
->UserData
, fullinst
, RC_FILE_ADDRESS
, 0,
64 static void pair_get_src_refmasks(unsigned int * refmasks
,
65 struct rc_pair_instruction
* inst
,
66 unsigned int swz
, unsigned int src
)
71 if (swz
== RC_SWIZZLE_X
|| swz
== RC_SWIZZLE_Y
|| swz
== RC_SWIZZLE_Z
) {
72 if(src
== RC_PAIR_PRESUB_SRC
) {
75 rc_presubtract_src_reg_count(
76 inst
->RGB
.Src
[src
].Index
);
77 for(i
= 0; i
< srcp_regs
; i
++) {
78 refmasks
[i
] |= 1 << swz
;
82 refmasks
[src
] |= 1 << swz
;
86 if (swz
== RC_SWIZZLE_W
) {
87 if (src
== RC_PAIR_PRESUB_SRC
) {
89 int srcp_regs
= rc_presubtract_src_reg_count(
90 inst
->Alpha
.Src
[src
].Index
);
91 for(i
= 0; i
< srcp_regs
; i
++) {
92 refmasks
[i
] |= 1 << swz
;
96 refmasks
[src
] |= 1 << swz
;
101 static void reads_pair(struct rc_instruction
* fullinst
, rc_read_write_mask_fn cb
, void * userdata
)
103 struct rc_pair_instruction
* inst
= &fullinst
->U
.P
;
104 unsigned int refmasks
[3] = { 0, 0, 0 };
108 for(arg
= 0; arg
< 3; ++arg
) {
110 for(chan
= 0; chan
< 3; ++chan
) {
111 unsigned int swz_rgb
=
112 GET_SWZ(inst
->RGB
.Arg
[arg
].Swizzle
, chan
);
113 unsigned int swz_alpha
=
114 GET_SWZ(inst
->Alpha
.Arg
[arg
].Swizzle
, chan
);
115 pair_get_src_refmasks(refmasks
, inst
, swz_rgb
,
116 inst
->RGB
.Arg
[arg
].Source
);
117 pair_get_src_refmasks(refmasks
, inst
, swz_alpha
,
118 inst
->Alpha
.Arg
[arg
].Source
);
122 for(unsigned int src
= 0; src
< 3; ++src
) {
123 if (inst
->RGB
.Src
[src
].Used
&& (refmasks
[src
] & RC_MASK_XYZ
))
124 cb(userdata
, fullinst
, inst
->RGB
.Src
[src
].File
, inst
->RGB
.Src
[src
].Index
,
125 refmasks
[src
] & RC_MASK_XYZ
);
127 if (inst
->Alpha
.Src
[src
].Used
&& (refmasks
[src
] & RC_MASK_W
))
128 cb(userdata
, fullinst
, inst
->Alpha
.Src
[src
].File
, inst
->Alpha
.Src
[src
].Index
, RC_MASK_W
);
132 static void pair_sub_for_all_args(
133 struct rc_instruction
* fullinst
,
134 struct rc_pair_sub_instruction
* sub
,
135 rc_pair_read_arg_fn cb
,
139 const struct rc_opcode_info
* info
= rc_get_opcode_info(sub
->Opcode
);
141 for(i
= 0; i
< info
->NumSrcRegs
; i
++) {
142 cb(userdata
, fullinst
, &sub
->Arg
[i
]);
146 /* This function calls the callback function (cb) for each source used by
149 void rc_for_all_reads_src(
150 struct rc_instruction
* inst
,
154 const struct rc_opcode_info
* opcode
=
155 rc_get_opcode_info(inst
->U
.I
.Opcode
);
157 /* This function only works with normal instructions. */
158 if (inst
->Type
!= RC_INSTRUCTION_NORMAL
) {
163 for(unsigned int src
= 0; src
< opcode
->NumSrcRegs
; ++src
) {
165 if (inst
->U
.I
.SrcReg
[src
].File
== RC_FILE_NONE
)
168 if (inst
->U
.I
.SrcReg
[src
].File
== RC_FILE_PRESUB
) {
170 unsigned int srcp_regs
= rc_presubtract_src_reg_count(
171 inst
->U
.I
.PreSub
.Opcode
);
172 for( i
= 0; i
< srcp_regs
; i
++) {
173 cb(userdata
, inst
, &inst
->U
.I
.PreSub
.SrcReg
[i
]);
176 cb(userdata
, inst
, &inst
->U
.I
.SrcReg
[src
]);
182 * This function calls the callback function (cb) for each arg of the RGB and
185 void rc_pair_for_all_reads_arg(
186 struct rc_instruction
* inst
,
187 rc_pair_read_arg_fn cb
,
190 /* This function only works with pair instructions. */
191 if (inst
->Type
!= RC_INSTRUCTION_PAIR
) {
196 pair_sub_for_all_args(inst
, &inst
->U
.P
.RGB
, cb
, userdata
);
197 pair_sub_for_all_args(inst
, &inst
->U
.P
.Alpha
, cb
, userdata
);
201 * Calls a callback function for all register reads.
203 * This is conservative, i.e. if the same register is referenced multiple times,
204 * the callback may also be called multiple times.
205 * Also, the writemask of the instruction is not taken into account.
207 void rc_for_all_reads_mask(struct rc_instruction
* inst
, rc_read_write_mask_fn cb
, void * userdata
)
209 if (inst
->Type
== RC_INSTRUCTION_NORMAL
) {
210 struct read_write_mask_data cb_data
;
211 cb_data
.UserData
= userdata
;
214 rc_for_all_reads_src(inst
, reads_normal_callback
, &cb_data
);
216 reads_pair(inst
, cb
, userdata
);
222 static void writes_normal(struct rc_instruction
* fullinst
, rc_read_write_mask_fn cb
, void * userdata
)
224 struct rc_sub_instruction
* inst
= &fullinst
->U
.I
;
225 const struct rc_opcode_info
* opcode
= rc_get_opcode_info(inst
->Opcode
);
227 if (opcode
->HasDstReg
&& inst
->DstReg
.WriteMask
)
228 cb(userdata
, fullinst
, inst
->DstReg
.File
, inst
->DstReg
.Index
, inst
->DstReg
.WriteMask
);
230 if (inst
->WriteALUResult
)
231 cb(userdata
, fullinst
, RC_FILE_SPECIAL
, RC_SPECIAL_ALU_RESULT
, RC_MASK_X
);
234 static void writes_pair(struct rc_instruction
* fullinst
, rc_read_write_mask_fn cb
, void * userdata
)
236 struct rc_pair_instruction
* inst
= &fullinst
->U
.P
;
238 if (inst
->RGB
.WriteMask
)
239 cb(userdata
, fullinst
, RC_FILE_TEMPORARY
, inst
->RGB
.DestIndex
, inst
->RGB
.WriteMask
);
241 if (inst
->Alpha
.WriteMask
)
242 cb(userdata
, fullinst
, RC_FILE_TEMPORARY
, inst
->Alpha
.DestIndex
, RC_MASK_W
);
244 if (inst
->WriteALUResult
)
245 cb(userdata
, fullinst
, RC_FILE_SPECIAL
, RC_SPECIAL_ALU_RESULT
, RC_MASK_X
);
249 * Calls a callback function for all register writes in the instruction,
250 * reporting writemasks to the callback function.
252 * \warning Does not report output registers for paired instructions!
254 void rc_for_all_writes_mask(struct rc_instruction
* inst
, rc_read_write_mask_fn cb
, void * userdata
)
256 if (inst
->Type
== RC_INSTRUCTION_NORMAL
) {
257 writes_normal(inst
, cb
, userdata
);
259 writes_pair(inst
, cb
, userdata
);
264 struct mask_to_chan_data
{
266 rc_read_write_chan_fn Fn
;
269 static void mask_to_chan_cb(void * data
, struct rc_instruction
* inst
,
270 rc_register_file file
, unsigned int index
, unsigned int mask
)
272 struct mask_to_chan_data
* d
= data
;
273 for(unsigned int chan
= 0; chan
< 4; ++chan
) {
274 if (GET_BIT(mask
, chan
))
275 d
->Fn(d
->UserData
, inst
, file
, index
, chan
);
280 * Calls a callback function for all sourced register channels.
282 * This is conservative, i.e. channels may be called multiple times,
283 * and the writemask of the instruction is not taken into account.
285 void rc_for_all_reads_chan(struct rc_instruction
* inst
, rc_read_write_chan_fn cb
, void * userdata
)
287 struct mask_to_chan_data d
;
288 d
.UserData
= userdata
;
290 rc_for_all_reads_mask(inst
, &mask_to_chan_cb
, &d
);
294 * Calls a callback function for all written register channels.
296 * \warning Does not report output registers for paired instructions!
298 void rc_for_all_writes_chan(struct rc_instruction
* inst
, rc_read_write_chan_fn cb
, void * userdata
)
300 struct mask_to_chan_data d
;
301 d
.UserData
= userdata
;
303 rc_for_all_writes_mask(inst
, &mask_to_chan_cb
, &d
);
306 static void remap_normal_instruction(struct rc_instruction
* fullinst
,
307 rc_remap_register_fn cb
, void * userdata
)
309 struct rc_sub_instruction
* inst
= &fullinst
->U
.I
;
310 const struct rc_opcode_info
* opcode
= rc_get_opcode_info(inst
->Opcode
);
312 if (opcode
->HasDstReg
) {
313 rc_register_file file
= inst
->DstReg
.File
;
314 unsigned int index
= inst
->DstReg
.Index
;
316 cb(userdata
, fullinst
, &file
, &index
);
318 inst
->DstReg
.File
= file
;
319 inst
->DstReg
.Index
= index
;
322 for(unsigned int src
= 0; src
< opcode
->NumSrcRegs
; ++src
) {
323 rc_register_file file
= inst
->SrcReg
[src
].File
;
324 unsigned int index
= inst
->SrcReg
[src
].Index
;
326 if (file
== RC_FILE_PRESUB
) {
328 unsigned int srcp_srcs
= rc_presubtract_src_reg_count(
329 inst
->PreSub
.Opcode
);
330 for(i
= 0; i
< srcp_srcs
; i
++) {
331 file
= inst
->PreSub
.SrcReg
[i
].File
;
332 index
= inst
->PreSub
.SrcReg
[i
].Index
;
333 cb(userdata
, fullinst
, &file
, &index
);
334 inst
->PreSub
.SrcReg
[i
].File
= file
;
335 inst
->PreSub
.SrcReg
[i
].Index
= index
;
340 cb(userdata
, fullinst
, &file
, &index
);
342 inst
->SrcReg
[src
].File
= file
;
343 inst
->SrcReg
[src
].Index
= index
;
348 static void remap_pair_instruction(struct rc_instruction
* fullinst
,
349 rc_remap_register_fn cb
, void * userdata
)
351 struct rc_pair_instruction
* inst
= &fullinst
->U
.P
;
353 if (inst
->RGB
.WriteMask
) {
354 rc_register_file file
= RC_FILE_TEMPORARY
;
355 unsigned int index
= inst
->RGB
.DestIndex
;
357 cb(userdata
, fullinst
, &file
, &index
);
359 inst
->RGB
.DestIndex
= index
;
362 if (inst
->Alpha
.WriteMask
) {
363 rc_register_file file
= RC_FILE_TEMPORARY
;
364 unsigned int index
= inst
->Alpha
.DestIndex
;
366 cb(userdata
, fullinst
, &file
, &index
);
368 inst
->Alpha
.DestIndex
= index
;
371 for(unsigned int src
= 0; src
< 3; ++src
) {
372 if (inst
->RGB
.Src
[src
].Used
) {
373 rc_register_file file
= inst
->RGB
.Src
[src
].File
;
374 unsigned int index
= inst
->RGB
.Src
[src
].Index
;
376 cb(userdata
, fullinst
, &file
, &index
);
378 inst
->RGB
.Src
[src
].File
= file
;
379 inst
->RGB
.Src
[src
].Index
= index
;
382 if (inst
->Alpha
.Src
[src
].Used
) {
383 rc_register_file file
= inst
->Alpha
.Src
[src
].File
;
384 unsigned int index
= inst
->Alpha
.Src
[src
].Index
;
386 cb(userdata
, fullinst
, &file
, &index
);
388 inst
->Alpha
.Src
[src
].File
= file
;
389 inst
->Alpha
.Src
[src
].Index
= index
;
396 * Remap all register accesses according to the given function.
397 * That is, call the function \p cb for each referenced register (both read and written)
398 * and update the given instruction \p inst accordingly
399 * if it modifies its \ref pfile and \ref pindex contents.
401 void rc_remap_registers(struct rc_instruction
* inst
, rc_remap_register_fn cb
, void * userdata
)
403 if (inst
->Type
== RC_INSTRUCTION_NORMAL
)
404 remap_normal_instruction(inst
, cb
, userdata
);
406 remap_pair_instruction(inst
, cb
, userdata
);
410 * @return RC_OPCODE_NOOP if inst is not a flow control instruction.
411 * @return The opcode of inst if it is a flow control instruction.
413 static rc_opcode
get_flow_control_inst(struct rc_instruction
* inst
)
415 const struct rc_opcode_info
* info
;
416 if (inst
->Type
== RC_INSTRUCTION_NORMAL
) {
417 info
= rc_get_opcode_info(inst
->U
.I
.Opcode
);
419 info
= rc_get_opcode_info(inst
->U
.P
.RGB
.Opcode
);
420 /*A flow control instruction shouldn't have an alpha
422 assert(!info
->IsFlowControl
||
423 inst
->U
.P
.Alpha
.Opcode
== RC_OPCODE_NOP
);
426 if (info
->IsFlowControl
)
429 return RC_OPCODE_NOP
;
433 struct get_readers_callback_data
{
434 struct radeon_compiler
* C
;
435 struct rc_reader_data
* ReaderData
;
436 rc_read_src_fn ReadCB
;
437 rc_read_write_mask_fn WriteCB
;
438 unsigned int AliveWriteMask
;
441 static void add_reader(
442 struct memory_pool
* pool
,
443 struct rc_reader_data
* data
,
444 struct rc_instruction
* inst
,
446 struct rc_src_register
* src
)
448 struct rc_reader
* new;
449 memory_pool_array_reserve(pool
, struct rc_reader
, data
->Readers
,
450 data
->ReaderCount
, data
->ReadersReserved
, 1);
451 new = &data
->Readers
[data
->ReaderCount
++];
453 new->WriteMask
= mask
;
458 * This function is used by rc_get_readers_normal() to determine whether inst
459 * is a reader of userdata->ReaderData->Writer
461 static void get_readers_normal_read_callback(
463 struct rc_instruction
* inst
,
464 struct rc_src_register
* src
)
466 struct get_readers_callback_data
* d
= userdata
;
467 unsigned int read_mask
;
470 d
->ReaderData
->Abort
= 1;
472 unsigned int shared_mask
= rc_src_reads_dst_mask(src
->File
, src
->Index
,
474 d
->ReaderData
->Writer
->U
.I
.DstReg
.File
,
475 d
->ReaderData
->Writer
->U
.I
.DstReg
.Index
,
478 if (shared_mask
== RC_MASK_NONE
)
481 /* If we make it this far, it means that this source reads from the
482 * same register written to by d->ReaderData->Writer. */
484 if (d
->ReaderData
->AbortOnRead
) {
485 d
->ReaderData
->Abort
= 1;
489 read_mask
= rc_swizzle_to_writemask(src
->Swizzle
);
490 /* XXX The behavior in this case should be configurable. */
491 if ((read_mask
& d
->AliveWriteMask
) != read_mask
) {
492 d
->ReaderData
->Abort
= 1;
496 d
->ReadCB(d
->ReaderData
, inst
, src
);
497 if (d
->ReaderData
->Abort
)
500 add_reader(&d
->C
->Pool
, d
->ReaderData
, inst
, shared_mask
, src
);
504 * This function is used by rc_get_readers_normal() to determine when
505 * userdata->ReaderData->Writer is dead (i. e. All compontents of its
506 * destination register have been overwritten by other instructions).
508 static void get_readers_write_callback(
510 struct rc_instruction
* inst
,
511 rc_register_file file
,
515 struct get_readers_callback_data
* d
= userdata
;
517 if (index
== d
->ReaderData
->Writer
->U
.I
.DstReg
.Index
518 && file
== d
->ReaderData
->Writer
->U
.I
.DstReg
.File
) {
519 unsigned int shared_mask
= mask
520 & d
->ReaderData
->Writer
->U
.I
.DstReg
.WriteMask
;
521 if (d
->ReaderData
->InElse
) {
522 if (shared_mask
& d
->AliveWriteMask
) {
523 /* We set AbortOnRead here because the
524 * destination register of d->ReaderData->Writer
525 * is written to in both the IF and the
526 * ELSE block of this IF/ELSE statement.
527 * This means that readers of this
528 * destination register that follow this IF/ELSE
529 * statement use the value of different
530 * instructions depending on the control flow
531 * decisions made by the program. */
532 d
->ReaderData
->AbortOnRead
= 1;
535 d
->AliveWriteMask
&= ~shared_mask
;
539 d
->WriteCB(d
->ReaderData
, inst
, file
, index
, mask
);
543 * This function will create a list of readers via the rc_reader_data struct.
544 * This function will abort (set the flag data->Abort) and return if it
545 * encounters an instruction that reads from @param writer and also a different
546 * instruction. Here are some examples:
548 * writer = instruction 0;
549 * 0 MOV TEMP[0].xy, TEMP[1].xy
550 * 1 MOV TEMP[0].zw, TEMP[2].xy
551 * 2 MOV TEMP[3], TEMP[0]
552 * The Abort flag will be set on instruction 2, because it reads values written
553 * by instructions 0 and 1.
555 * writer = instruction 1;
557 * 1 MOV TEMP[1], TEMP[2]
559 * 3 MOV TEMP[1], TEMP[2]
561 * 5 MOV TEMP[3], TEMP[1]
562 * The Abort flag will be set on instruction 5, because it could read from the
563 * value written by either instruction 1 or 3, depending on the jump decision
564 * made at instruction 0.
566 * writer = instruction 0;
567 * 0 MOV TEMP[0], TEMP[1]
569 * 3 ADD TEMP[0], TEMP[0], none.1
571 * The Abort flag will be set on instruction 3, because in the first iteration
572 * of the loop it reads the value written by instruction 0 and in all other
573 * iterations it reads the value written by instruction 3.
575 * @param read_cb This function will be called for for every instruction that
576 * has been determined to be a reader of writer.
577 * @param write_cb This function will be called for every instruction after
580 void rc_get_readers_normal(
581 struct radeon_compiler
* c
,
582 struct rc_instruction
* writer
,
583 struct rc_reader_data
* data
,
584 rc_read_src_fn read_cb
,
585 rc_read_write_mask_fn write_cb
)
587 struct rc_instruction
* tmp
;
588 struct get_readers_callback_data d
;
589 unsigned int branch_depth
= 0;
591 data
->Writer
= writer
;
593 data
->AbortOnRead
= 0;
595 data
->ReaderCount
= 0;
596 data
->ReadersReserved
= 0;
597 data
->Readers
= NULL
;
600 d
.AliveWriteMask
= writer
->U
.I
.DstReg
.WriteMask
;
603 d
.WriteCB
= write_cb
;
605 if (!writer
->U
.I
.DstReg
.WriteMask
)
608 for(tmp
= writer
->Next
; tmp
!= &c
->Program
.Instructions
;
610 rc_opcode opcode
= get_flow_control_inst(tmp
);
612 case RC_OPCODE_BGNLOOP
:
613 /* XXX We can do better when we see a BGNLOOP if we
614 * add a flag called AbortOnWrite to struct
615 * rc_reader_data and leave it set until the next
617 case RC_OPCODE_ENDLOOP
:
618 /* XXX We can do better when we see an ENDLOOP by
619 * searching backwards from writer and looking for
620 * readers of writer's destination index. If we find a
621 * reader before we get to the BGNLOOP, we must abort
622 * unless there is another writer between that reader
623 * and the BGNLOOP. */
627 /* XXX We can do better here, but this will have to
628 * do until this dataflow analysis is more mature. */
633 if (branch_depth
== 0)
636 case RC_OPCODE_ENDIF
:
637 if (branch_depth
== 0) {
638 data
->AbortOnRead
= 1;
650 rc_for_all_reads_src(tmp
, get_readers_normal_read_callback
, &d
);
651 rc_for_all_writes_mask(tmp
, get_readers_write_callback
, &d
);
656 if (!d
.AliveWriteMask
)