2 * Copyright © 2014 Broadcom
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
27 #include "vc4_context.h"
32 vc4_dump_program(struct qcompile
*c
)
34 fprintf(stderr
, "%s:\n", qir_get_stage_name(c
->stage
));
36 for (int i
= 0; i
< c
->num_qpu_insts
; i
++) {
37 fprintf(stderr
, "0x%016"PRIx64
" ", c
->qpu_insts
[i
]);
38 vc4_qpu_disasm(&c
->qpu_insts
[i
], 1);
39 fprintf(stderr
, "\n");
44 * This is used to resolve the fact that we might register-allocate two
45 * different operands of an instruction to the same physical register file
46 * even though instructions have only one field for the register file source
49 * In that case, we need to move one to a temporary that can be used in the
50 * instruction, instead.
53 fixup_raddr_conflict(uint64_t *insts
, uint32_t *ni
,
54 struct qpu_reg src0
, struct qpu_reg
*src1
)
56 if ((src0
.mux
== QPU_MUX_A
|| src0
.mux
== QPU_MUX_B
) &&
57 (src1
->mux
== QPU_MUX_A
|| src1
->mux
== QPU_MUX_B
) &&
58 src0
.addr
!= src1
->addr
) {
59 insts
[(*ni
)++] = qpu_inst(qpu_a_MOV(qpu_r5(), *src1
),
66 vc4_generate_code(struct qcompile
*c
)
68 uint64_t *insts
= malloc(sizeof(uint64_t) * 1024); /* XXX: sizing */
70 struct qpu_reg allocate_to_qpu_reg
[4 + 32 + 32];
71 bool reg_in_use
[ARRAY_SIZE(allocate_to_qpu_reg
)];
72 int *reg_allocated
= calloc(c
->num_temps
, sizeof(*reg_allocated
));
73 int *reg_uses_remaining
=
74 calloc(c
->num_temps
, sizeof(*reg_uses_remaining
));
76 for (int i
= 0; i
< ARRAY_SIZE(reg_in_use
); i
++)
77 reg_in_use
[i
] = false;
78 for (int i
= 0; i
< c
->num_temps
; i
++)
79 reg_allocated
[i
] = -1;
80 for (int i
= 0; i
< 4; i
++)
81 allocate_to_qpu_reg
[i
] = qpu_rn(i
);
82 for (int i
= 0; i
< 32; i
++)
83 allocate_to_qpu_reg
[i
+ 4] = qpu_ra(i
);
84 for (int i
= 0; i
< 32; i
++)
85 allocate_to_qpu_reg
[i
+ 4 + 32] = qpu_rb(i
);
87 struct simple_node
*node
;
88 foreach(node
, &c
->instructions
) {
89 struct qinst
*qinst
= (struct qinst
*)node
;
91 if (qinst
->dst
.file
== QFILE_TEMP
)
92 reg_uses_remaining
[qinst
->dst
.index
]++;
93 for (int i
= 0; i
< qir_get_op_nsrc(qinst
->op
); i
++) {
94 if (qinst
->src
[i
].file
== QFILE_TEMP
)
95 reg_uses_remaining
[qinst
->src
[i
].index
]++;
102 insts
[ni
++] = qpu_load_imm_ui(qpu_vrsetup(), 0x00401a00);
103 insts
[ni
++] = qpu_load_imm_ui(qpu_vwsetup(), 0x00001a00);
109 foreach(node
, &c
->instructions
) {
110 struct qinst
*qinst
= (struct qinst
*)node
;
113 fprintf(stderr
, "translating qinst to qpu: ");
114 qir_dump_inst(qinst
);
115 fprintf(stderr
, "\n");
118 static const struct {
122 #define A(name) [QOP_##name] = {QPU_A_##name, false}
123 #define M(name) [QOP_##name] = {QPU_M_##name, true}
135 static const uint32_t compareflags
[] = {
136 [QOP_SEQ
- QOP_SEQ
] = QPU_COND_ZS
,
137 [QOP_SNE
- QOP_SEQ
] = QPU_COND_ZC
,
138 [QOP_SLT
- QOP_SEQ
] = QPU_COND_NS
,
139 [QOP_SGE
- QOP_SEQ
] = QPU_COND_NC
,
142 struct qpu_reg src
[4];
143 for (int i
= 0; i
< qir_get_op_nsrc(qinst
->op
); i
++) {
144 int index
= qinst
->src
[i
].index
;
145 switch (qinst
->src
[i
].file
) {
150 assert(reg_allocated
[index
] != -1);
151 src
[i
] = allocate_to_qpu_reg
[reg_allocated
[index
]];
152 reg_uses_remaining
[index
]--;
153 if (reg_uses_remaining
[index
] == 0)
154 reg_in_use
[reg_allocated
[index
]] = false;
166 switch (qinst
->dst
.file
) {
168 dst
= qpu_ra(QPU_W_NOP
);
172 if (reg_allocated
[qinst
->dst
.index
] == -1) {
175 alloc
< ARRAY_SIZE(reg_in_use
);
177 /* The pack flags require an A-file register. */
178 if (qinst
->op
== QOP_PACK_SCALED
&&
179 allocate_to_qpu_reg
[alloc
].mux
!= QPU_MUX_A
) {
183 if (!reg_in_use
[alloc
])
186 assert(alloc
!= ARRAY_SIZE(reg_in_use
) && "need better reg alloc");
187 reg_in_use
[alloc
] = true;
188 reg_allocated
[qinst
->dst
.index
] = alloc
;
191 dst
= allocate_to_qpu_reg
[reg_allocated
[qinst
->dst
.index
]];
193 reg_uses_remaining
[qinst
->dst
.index
]--;
194 if (reg_uses_remaining
[qinst
->dst
.index
] == 0) {
195 reg_in_use
[reg_allocated
[qinst
->dst
.index
]] =
202 assert(!"not reached");
208 /* Skip emitting the MOV if it's a no-op. */
209 if (dst
.mux
== QPU_MUX_A
|| dst
.mux
== QPU_MUX_B
||
210 dst
.mux
!= src
[0].mux
|| dst
.addr
!= src
[0].addr
) {
211 insts
[ni
++] = qpu_inst(qpu_a_MOV(dst
, src
[0]),
220 fixup_raddr_conflict(insts
, &ni
, src
[0], &src
[1]);
221 insts
[ni
++] = qpu_inst(qpu_a_SUB(qpu_ra(QPU_W_NOP
),
224 insts
[ni
- 1] |= QPU_SF
;
226 insts
[ni
++] = qpu_load_imm_f(dst
, 0.0);
227 insts
[ni
++] = qpu_load_imm_f(dst
, 1.0);
228 insts
[ni
- 1] = ((insts
[ni
- 1] & ~QPU_COND_ADD_MASK
)
229 | QPU_SET_FIELD(compareflags
[qinst
->op
- QOP_SEQ
],
235 insts
[ni
++] = qpu_inst(qpu_a_MOV(qpu_ra(QPU_W_VPM
),
241 insts
[ni
++] = qpu_inst(qpu_a_MOV(dst
,
252 insts
[ni
++] = qpu_inst(qpu_a_MOV(qpu_rb(QPU_W_SFU_RECIP
),
257 insts
[ni
++] = qpu_inst(qpu_a_MOV(qpu_rb(QPU_W_SFU_RECIPSQRT
),
262 insts
[ni
++] = qpu_inst(qpu_a_MOV(qpu_rb(QPU_W_SFU_EXP
),
267 insts
[ni
++] = qpu_inst(qpu_a_MOV(qpu_rb(QPU_W_SFU_LOG
),
275 insts
[ni
++] = qpu_inst(qpu_a_NOP(), qpu_m_NOP());
276 insts
[ni
++] = qpu_inst(qpu_a_NOP(), qpu_m_NOP());
278 insts
[ni
++] = qpu_inst(qpu_a_MOV(dst
, qpu_r4()),
283 case QOP_PACK_COLORS
:
284 for (int i
= 0; i
< 4; i
++) {
285 insts
[ni
++] = qpu_inst(qpu_a_NOP(),
286 qpu_m_MOV(qpu_r5(), src
[i
]));
287 insts
[ni
- 1] |= QPU_PM
;
288 insts
[ni
- 1] |= QPU_SET_FIELD(QPU_PACK_MUL_8A
+ i
,
292 insts
[ni
++] = qpu_inst(qpu_a_MOV(dst
, qpu_r5()),
296 case QOP_TLB_COLOR_WRITE
:
297 insts
[ni
++] = qpu_inst(qpu_a_MOV(qpu_tlbc(),
302 case QOP_PACK_SCALED
:
303 insts
[ni
++] = qpu_inst(qpu_a_MOV(dst
, src
[0]),
305 insts
[ni
- 1] |= QPU_SET_FIELD(QPU_PACK_A_16A
, QPU_PACK
);
307 insts
[ni
++] = qpu_inst(qpu_a_MOV(dst
, src
[1]),
309 insts
[ni
- 1] |= QPU_SET_FIELD(QPU_PACK_A_16B
, QPU_PACK
);
314 assert(qinst
->op
< ARRAY_SIZE(translate
));
315 assert(translate
[qinst
->op
].op
!= 0); /* NOPs */
317 /* If we have only one source, put it in the second
318 * argument slot as well so that we don't take up
319 * another raddr just to get unused data.
321 if (qir_get_op_nsrc(qinst
->op
) == 1)
324 fixup_raddr_conflict(insts
, &ni
, src
[0], &src
[1]);
326 if (translate
[qinst
->op
].is_mul
) {
327 insts
[ni
++] = qpu_inst(qpu_a_NOP(),
328 qpu_m_alu2(translate
[qinst
->op
].op
,
329 dst
, src
[0], src
[1]));
331 insts
[ni
++] = qpu_inst(qpu_a_alu2(translate
[qinst
->op
].op
,
332 dst
, src
[0], src
[1]),
338 if ((dst
.mux
== QPU_MUX_A
|| dst
.mux
== QPU_MUX_B
) &&
340 insts
[ni
++] = qpu_inst(qpu_a_NOP(), qpu_m_NOP());
343 /* thread end can't have VPM write */
344 if (QPU_GET_FIELD(insts
[ni
- 1], QPU_WADDR_ADD
) == QPU_W_VPM
||
345 QPU_GET_FIELD(insts
[ni
- 1], QPU_WADDR_MUL
) == QPU_W_VPM
)
346 insts
[ni
++] = qpu_inst(qpu_a_NOP(), qpu_m_NOP());
348 insts
[ni
- 1] = qpu_set_sig(insts
[ni
- 1], QPU_SIG_PROG_END
);
349 insts
[ni
++] = qpu_inst(qpu_a_NOP(), qpu_m_NOP());
350 insts
[ni
++] = qpu_inst(qpu_a_NOP(), qpu_m_NOP());
357 insts
[2] = qpu_set_sig(insts
[2], QPU_SIG_WAIT_FOR_SCOREBOARD
);
358 insts
[ni
- 1] = qpu_set_sig(insts
[ni
- 1],
359 QPU_SIG_SCOREBOARD_UNLOCK
);
363 c
->qpu_insts
= insts
;
364 c
->num_qpu_insts
= ni
;
367 vc4_qpu_validate(insts
, ni
);