r600g/sb: initial commit of the optimizing shader backend
This commit is contained in:
@@ -0,0 +1,413 @@
|
||||
r600-sb
|
||||
=======
|
||||
|
||||
* * * * *
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
### Environment variables
|
||||
|
||||
- **R600\_DEBUG**
|
||||
|
||||
There are new flags:
|
||||
|
||||
- **sb** - Enable optimization of graphics shaders
|
||||
- **sbcl** - Enable optimization of compute shaders (experimental)
|
||||
- **sbdry** - Dry run, optimize but use source bytecode -
|
||||
useful if you only want to check shader dumps
|
||||
without the risk of lockups and other problems
|
||||
- **sbstat** - Print optimization statistics (only time so far)
|
||||
- **sbdump** - Print IR after some passes.
|
||||
|
||||
### Regression debugging
|
||||
|
||||
If there are any regressions as compared to the default backend
|
||||
(R600\_SB=0), it's possible to use the following environment variables
|
||||
to find the incorrectly optimized shader that causes the regression.
|
||||
|
||||
- **R600\_SB\_DSKIP\_MODE** - allows to skip optimization for some
|
||||
shaders
|
||||
- 0 - disabled (default)
|
||||
- 1 - skip optimization for the shaders in the range
|
||||
[R600\_SB\_DSKIP\_START; R600\_SB\_DSKIP\_END], that is,
|
||||
optimize only the shaders that are not in this range
|
||||
- 2 - optimize only the shaders in the range
|
||||
[R600\_SB\_DSKIP\_START; R600\_SB\_DSKIP\_END]
|
||||
|
||||
- **R600\_SB\_DSKIP\_START** - start of the range (1-based)
|
||||
|
||||
- **R600\_SB\_DSKIP\_END** - end of the range (1-based)
|
||||
|
||||
Example - optimize only the shaders 5, 6, and 7:
|
||||
|
||||
R600_SB_DSKIP_START=5 R600_SB_DSKIP_END=7 R600_SB_DSKIP_MODE=2
|
||||
|
||||
All shaders compiled by the application are numbered starting from 1,
|
||||
the number of shaders used by the application may be obtained by running
|
||||
it with "R600_DEBUG=sb,sbstat" - it will print "sb: shader \#index\#"
|
||||
for each compiled shader.
|
||||
|
||||
After figuring out the total number of shaders used by the application,
|
||||
the variables above allow to use bisection to find the shader that is
|
||||
the cause of regression. E.g. if the application uses 100 shaders, we
|
||||
can divide the range [1; 100] and run the application with the
|
||||
optimization enabled only for the first half of the shaders:
|
||||
|
||||
R600_SB_DSKIP_START=1 R600_SB_DSKIP_END=50 R600_SB_DSKIP_MODE=2 <app>
|
||||
|
||||
If the regression is reproduced with these parameters, then the failing
|
||||
shader is in the range [1; 50], if it's not reproduced - then it's in
|
||||
the range [51; 100]. Then we can divide the new range again and repeat
|
||||
the testing, until we'll reduce the range to a single failing shader.
|
||||
|
||||
*NOTE: This method relies on the assumption that the application
|
||||
produces the same sequence of the shaders on each run. It's not always
|
||||
true - some applications may produce different sequences of the shaders,
|
||||
in such cases the tools like apitrace may be used to record the trace
|
||||
with the application, then this method may be applied when replaying the
|
||||
trace - also this may be faster and/or more convenient than testing the
|
||||
application itself.*
|
||||
|
||||
* * * * *
|
||||
|
||||
Intermediate Representation
|
||||
---------------------------
|
||||
|
||||
### Values
|
||||
|
||||
All kinds of the operands (literal constants, references to kcache
|
||||
constants, references to GPRs, etc) are currently represented by the
|
||||
**value** class (possibly it makes sense to switch to hierarchy of
|
||||
classes derived from **value** instead, to save some memory).
|
||||
|
||||
All values (except some pseudo values like the exec\_mask or predicate
|
||||
register) represent 32bit scalar values - there are no vector values,
|
||||
CF/FETCH instructions use groups of 4 values for src and dst operands.
|
||||
|
||||
### Nodes
|
||||
|
||||
Shader programs are represented using the tree data structure, some
|
||||
nodes contain a list of subnodes.
|
||||
|
||||
#### Control flow nodes
|
||||
|
||||
Control flow information is represented using four special node types
|
||||
(based on the ideas from [[1]](#references) )
|
||||
|
||||
- **region\_node** - single-entry, single-exit region.
|
||||
|
||||
All loops and if's in the program are enclosed in region nodes.
|
||||
Region nodes have two containers for phi nodes -
|
||||
region\_node::loop\_phi contains the phi expressions to be executed
|
||||
at the region entry, region\_node::phi contains the phi expressions
|
||||
to be executed at the region exit. It's the only type of the node
|
||||
that contains associated phi expressions.
|
||||
|
||||
- **depart\_node** - "depart region \$id after { ... }"
|
||||
|
||||
Depart target region (jump to exit point) after executing contained
|
||||
code.
|
||||
|
||||
- **repeat\_node** - "repeat region \$id after { ... }"
|
||||
|
||||
Repeat target region (jump to entry point) after executing contained
|
||||
code.
|
||||
|
||||
- **if\_node** - "if (cond) { ... }"
|
||||
|
||||
Execute contained code if condition is true. The difference from
|
||||
[[1]](#references) is that we don't have associated phi expressions
|
||||
for the **if\_node**, we enclose **if\_node** in the
|
||||
**region\_node** and store corresponding phi's in the
|
||||
**region\_node**, this allows more uniform handling.
|
||||
|
||||
The target region of depart and repeat nodes is always the region where
|
||||
they are located (possibly in the nested region), there are no arbitrary
|
||||
jumps/goto's - control flow in the program is always structured.
|
||||
|
||||
Typical control flow constructs can be represented as in the following
|
||||
examples:
|
||||
|
||||
GLSL:
|
||||
|
||||
if (cond) {
|
||||
< 1 >
|
||||
} else {
|
||||
< 2 >
|
||||
}
|
||||
|
||||
IR:
|
||||
|
||||
region #0 {
|
||||
depart region #0 after {
|
||||
if (cond) {
|
||||
depart region #0 after {
|
||||
< 1 >
|
||||
}
|
||||
}
|
||||
< 2 >
|
||||
}
|
||||
<region #0 phi nodes >
|
||||
}
|
||||
|
||||
GLSL:
|
||||
|
||||
while (cond) {
|
||||
< 1 >
|
||||
}
|
||||
|
||||
IR:
|
||||
|
||||
region #0 {
|
||||
<region #0 loop_phi nodes>
|
||||
repeat region #0 after {
|
||||
region #1 {
|
||||
depart region #1 after {
|
||||
if (!cond) {
|
||||
depart region #0
|
||||
}
|
||||
}
|
||||
}
|
||||
< 1 >
|
||||
}
|
||||
<region #0 phi nodes>
|
||||
}
|
||||
|
||||
'Break' and 'continue' inside the loops are directly translated to the
|
||||
depart and repeat nodes for the corresponding loop region.
|
||||
|
||||
This may look a bit too complicated, but in fact this allows more simple
|
||||
and uniform handling of the control flow.
|
||||
|
||||
All loop\_phi and phi nodes for some region always have the same number
|
||||
of source operands. The number of source operands for
|
||||
region\_node::loop\_phi nodes is 1 + number of repeat nodes that
|
||||
reference this region as a target. The number of source operands for
|
||||
region\_node::phi nodes is equal to the number of depart nodes that
|
||||
reference this region as a target. All depart/repeat nodes for the
|
||||
region have unique indices equal to the index of source operand for
|
||||
phi/loop\_phi nodes.
|
||||
|
||||
First source operand for region\_node::loop\_phi nodes (src[0]) is an
|
||||
incoming value that enters the region from the outside. Each remaining
|
||||
source operand comes from the corresponding repeat node.
|
||||
|
||||
More complex example:
|
||||
|
||||
GLSL:
|
||||
|
||||
a = 1;
|
||||
while (a < 5) {
|
||||
a = a * 2;
|
||||
if (b == 3) {
|
||||
continue;
|
||||
} else {
|
||||
a = 6;
|
||||
}
|
||||
if (c == 4)
|
||||
break;
|
||||
a = a + 1;
|
||||
}
|
||||
|
||||
IR with SSA form:
|
||||
|
||||
a.1 = 1;
|
||||
region #0 {
|
||||
// loop phi values: src[0] - incoming, src[1] - from repeat_1, src[2] - from repeat_2
|
||||
region#0 loop_phi: a.2 = phi a.1, a.6, a.3
|
||||
|
||||
repeat_1 region #0 after {
|
||||
a.3 = a.2 * 2;
|
||||
cond1 = (b == 3);
|
||||
region #1 {
|
||||
depart_0 region #1 after {
|
||||
if (cond1) {
|
||||
repeat_2 region #0;
|
||||
}
|
||||
}
|
||||
a.4 = 6;
|
||||
|
||||
region #1 phi: a.5 = phi a.4; // src[0] - from depart_0
|
||||
}
|
||||
cond2 = (c == 4);
|
||||
region #2 {
|
||||
depart_0 region #2 after {
|
||||
if (cond2) {
|
||||
depart_0 region #0;
|
||||
}
|
||||
}
|
||||
}
|
||||
a.6 = a.5 + 1;
|
||||
}
|
||||
|
||||
region #0 phi: a.7 = phi a.5 // src[0] from depart_0
|
||||
}
|
||||
|
||||
Phi nodes with single source operand are just copies, they are not
|
||||
really necessary, but this allows to handle all **depart\_node**s in the
|
||||
uniform way.
|
||||
|
||||
#### Instruction nodes
|
||||
|
||||
Instruction nodes represent different kinds of instructions -
|
||||
**alu\_node**, **cf\_node**, **fetch\_node**, etc. Each of them contains
|
||||
the "bc" structure where all fields of the bytecode are stored (the type
|
||||
is **bc\_alu** for **alu\_node**, etc). The operands are represented
|
||||
using the vectors of pointers to **value** class (node::src, node::dst)
|
||||
|
||||
#### SSA-specific nodes
|
||||
|
||||
Phi nodes currently don't have special node class, they are stored as
|
||||
**node**. Destination vector contains a single destination value, source
|
||||
vector contains 1 or more source values.
|
||||
|
||||
Psi nodes [[5], [6]](#references) also don't have a special node class
|
||||
and stored as **node**. Source vector contains 3 values for each source
|
||||
operand - the **value** of predicate, **value** of corresponding
|
||||
PRED\_SEL field, and the source **value** itself.
|
||||
|
||||
### Indirect addressing
|
||||
|
||||
Special kind of values (VLK\_RELREG) is used to represent indirect
|
||||
operands. These values don't have SSA versions. The representation is
|
||||
mostly based on the [[2]](#references). Indirect operand contains the
|
||||
"offset/address" value (value::rel), (e.g. some SSA version of the AR
|
||||
register value, though after some passes it may be any value - constant,
|
||||
register, etc), also it contains the maydef and mayuse vectors of
|
||||
pointers to **value**s (similar to dst/src vectors in the **node**) to
|
||||
represent the effects of aliasing in the SSA form.
|
||||
|
||||
E.g. if we have the array R5.x ... R8.x and the following instruction :
|
||||
|
||||
MOV R0.x, R[5 + AR].x
|
||||
|
||||
then source indirect operand is represented with the VLK\_RELREG value,
|
||||
value::rel is AR, value::maydef is empty (in fact it always contain the
|
||||
same number of elements as mayuse to simplify the handling, but they are
|
||||
NULLs), value::mayuse contains [R5.x, R6.x, R7.x, R8.x] (or the
|
||||
corresponding SSA versions after ssa\_rename).
|
||||
|
||||
Additional "virtual variables" as in [HSSA [2]](#references) are not
|
||||
used, also there is no special handling for "zero versions". Typical
|
||||
programs in our case are small, indirect addressing is rare, array sizes
|
||||
are limited by max gpr number, so we don't really need to use special
|
||||
tricks to avoid the explosion of value versions. Also this allows more
|
||||
precise liveness computation for array elements without modifications to
|
||||
the algorithms.
|
||||
|
||||
With the following instruction:
|
||||
|
||||
MOV R[5+AR].x, R0.x
|
||||
|
||||
we'll have both maydef and mayuse vectors for dst operand filled with
|
||||
array values initially: [R5.x, R6.x, R7.x, R8.x]. After the ssa\_rename
|
||||
pass mayuse will contain previous versions, maydef will contain new
|
||||
potentially-defined versions.
|
||||
|
||||
* * * * *
|
||||
|
||||
Passes
|
||||
------
|
||||
|
||||
- **bc\_parser** - creates the IR from the source bytecode,
|
||||
initializes src and dst value vectors for instruction nodes. Most
|
||||
ALU nodes have one dst operand and the number of source operands is
|
||||
equal to the number of source operands for the ISA instruction.
|
||||
Nodes for PREDSETxx instructions have 3 dst operands - dst[0] is dst
|
||||
gpr as in the original instruction, other two are pseudo-operands
|
||||
that represent possibly updated predicate and exec\_mask. Predicate
|
||||
values are used in the predicated alu instructions (node::pred),
|
||||
exec\_mask values are used in the if\_nodes (if\_node::cond). Each
|
||||
vector operand in the CF/TEX/VTX instructions is represented with 4
|
||||
values - components of the vector.
|
||||
|
||||
- **ssa\_prepare** - creates phi expressions.
|
||||
|
||||
- **ssa\_rename** - renames the values (assigns versions).
|
||||
|
||||
- **liveness** - liveness computation, sets 'dead' flag for unused
|
||||
nodes and values, optionally computes interference information for
|
||||
the values.
|
||||
|
||||
- **dce\_cleanup** - eliminates 'dead' nodes, also removes some
|
||||
unnecessary nodes created by bc\_parser, e.g. the nodes for the JUMP
|
||||
instructions in the source, containers for ALU groups (they were
|
||||
only needed for the ssa\_rename pass)
|
||||
|
||||
- **if\_conversion** - converts control flow with if\_nodes to the
|
||||
data flow in cases where it can improve performance (small alu-only
|
||||
branches). Both branches are executed speculatively and the phi
|
||||
expressions are replaced with conditional moves (CNDxx) to select
|
||||
the final value using the same condition predicate as was used by
|
||||
the original if\_node. E.g. **if\_node** used dst[2] from PREDSETxx
|
||||
instruction, CNDxx now uses dst[0] from the same PREDSETxx
|
||||
instruction.
|
||||
|
||||
- **peephole** - peephole optimizations
|
||||
|
||||
- **gvn** - Global Value Numbering [[2]](#references),
|
||||
[[3]](#references)
|
||||
|
||||
- **gcm** - Global Code Motion [[3]](#references). Also performs
|
||||
grouping of the instructions of the same kind (CF/FETCH/ALU).
|
||||
|
||||
- register allocation passes, some ideas are used from
|
||||
[[4]](#references), but implementation is simplified to make it more
|
||||
efficient in terms of the compilation speed (e.g. no recursive
|
||||
recoloring) while achieving good enough results.
|
||||
|
||||
- **ra\_split** - prepares the program to register allocation.
|
||||
Splits live ranges for constrained values by inserting the
|
||||
copies to/from temporary values, so that the live range of the
|
||||
constrained values becomes minimal.
|
||||
|
||||
- **ra\_coalesce** - performs global allocation on registers used
|
||||
in CF/FETCH instructions. It's performed first to make sure they
|
||||
end up in the same GPR. Also tries to allocate all values
|
||||
involved in copies (inserted by the ra\_split pass) to the same
|
||||
register, so that the copies may be eliminated.
|
||||
|
||||
- **ra\_init** - allocates gpr arrays (if indirect addressing is
|
||||
used), and remaining values.
|
||||
|
||||
- **post\_scheduler** - ALU scheduler, handles VLIW packing and
|
||||
performs the final register allocation for local values inside ALU
|
||||
clauses. Eliminates all coalesced copies (if src and dst of the copy
|
||||
are allocated to the same register).
|
||||
|
||||
- **ra\_checker** - optional debugging pass that tries to catch basic
|
||||
errors of the scheduler or regalloc,
|
||||
|
||||
- **bc\_finalize** - propagates the regalloc information from values
|
||||
in node::src and node::dst vectors to the bytecode fields, converts
|
||||
control flow structure (region/depart/repeat) to the target
|
||||
instructions (JUMP/ELSE/POP,
|
||||
LOOP\_START/LOOP\_END/LOOP\_CONTINUE/LOOP\_BREAK).
|
||||
|
||||
- **bc\_builder** - builds final bytecode,
|
||||
|
||||
* * * * *
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
[1] ["Tree-Based Code Optimization. A Thesis Proposal", Carl
|
||||
McConnell](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.38.4210&rep=rep1&type=pdf)
|
||||
|
||||
[2] ["Effective Representation of Aliases and Indirect Memory Operations
|
||||
in SSA Form", Fred Chow, Sun Chan, Shin-Ming Liu, Raymond Lo, Mark
|
||||
Streich](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.33.6974&rep=rep1&type=pdf)
|
||||
|
||||
[3] ["Global Code Motion. Global Value Numbering.", Cliff
|
||||
Click](http://www.cs.washington.edu/education/courses/cse501/06wi/reading/click-pldi95.pdf)
|
||||
|
||||
[4] ["Register Allocation for Programs in SSA Form", Sebastian
|
||||
Hack](http://digbib.ubka.uni-karlsruhe.de/volltexte/documents/6532)
|
||||
|
||||
[5] ["An extension to the SSA representation for predicated code",
|
||||
Francois de
|
||||
Ferriere](http://www.cdl.uni-saarland.de/ssasem/talks/Francois.de.Ferriere.pdf)
|
||||
|
||||
[6] ["Improvements to the Psi-SSA Representation", F. de
|
||||
Ferriere](http://www.scopesconf.org/scopes-07/presentations/3_Presentation.pdf)
|
||||
@@ -0,0 +1,785 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#ifndef SB_BC_H_
|
||||
#define SB_BC_H_
|
||||
|
||||
extern "C" {
|
||||
#include <stdint.h>
|
||||
#include "r600_isa.h"
|
||||
}
|
||||
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
|
||||
struct r600_bytecode;
|
||||
struct r600_shader;
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
class hw_encoding_format;
|
||||
class alu_node;
|
||||
class cf_node;
|
||||
class fetch_node;
|
||||
class alu_group_node;
|
||||
class region_node;
|
||||
class shader;
|
||||
|
||||
enum shader_target
|
||||
{
|
||||
TARGET_UNKNOWN,
|
||||
TARGET_VS,
|
||||
TARGET_PS,
|
||||
TARGET_GS,
|
||||
TARGET_COMPUTE,
|
||||
TARGET_FETCH,
|
||||
|
||||
TARGET_NUM
|
||||
};
|
||||
|
||||
enum sb_hw_class_bits
|
||||
{
|
||||
HB_R6 = (1<<0),
|
||||
HB_R7 = (1<<1),
|
||||
HB_EG = (1<<2),
|
||||
HB_CM = (1<<3),
|
||||
|
||||
HB_R6R7 = (HB_R6 | HB_R7),
|
||||
HB_EGCM = (HB_EG | HB_CM),
|
||||
HB_R6R7EG = (HB_R6 | HB_R7 | HB_EG),
|
||||
HB_R7EGCM = (HB_R7 | HB_EG | HB_CM),
|
||||
|
||||
HB_ALL = (HB_R6 | HB_R7 | HB_EG | HB_CM)
|
||||
};
|
||||
|
||||
enum sb_hw_chip
|
||||
{
|
||||
HW_CHIP_UNKNOWN,
|
||||
HW_CHIP_R600,
|
||||
HW_CHIP_RV610,
|
||||
HW_CHIP_RV630,
|
||||
HW_CHIP_RV670,
|
||||
HW_CHIP_RV620,
|
||||
HW_CHIP_RV635,
|
||||
HW_CHIP_RS780,
|
||||
HW_CHIP_RS880,
|
||||
HW_CHIP_RV770,
|
||||
HW_CHIP_RV730,
|
||||
HW_CHIP_RV710,
|
||||
HW_CHIP_RV740,
|
||||
HW_CHIP_CEDAR,
|
||||
HW_CHIP_REDWOOD,
|
||||
HW_CHIP_JUNIPER,
|
||||
HW_CHIP_CYPRESS,
|
||||
HW_CHIP_HEMLOCK,
|
||||
HW_CHIP_PALM,
|
||||
HW_CHIP_SUMO,
|
||||
HW_CHIP_SUMO2,
|
||||
HW_CHIP_BARTS,
|
||||
HW_CHIP_TURKS,
|
||||
HW_CHIP_CAICOS,
|
||||
HW_CHIP_CAYMAN,
|
||||
HW_CHIP_ARUBA
|
||||
};
|
||||
|
||||
enum sb_hw_class
|
||||
{
|
||||
HW_CLASS_UNKNOWN,
|
||||
HW_CLASS_R600,
|
||||
HW_CLASS_R700,
|
||||
HW_CLASS_EVERGREEN,
|
||||
HW_CLASS_CAYMAN
|
||||
};
|
||||
|
||||
enum alu_slots {
|
||||
SLOT_X = 0,
|
||||
SLOT_Y = 1,
|
||||
SLOT_Z = 2,
|
||||
SLOT_W = 3,
|
||||
SLOT_TRANS = 4
|
||||
};
|
||||
|
||||
enum misc_consts {
|
||||
MAX_ALU_LITERALS = 4,
|
||||
MAX_ALU_SLOTS = 128,
|
||||
MAX_GPR = 128,
|
||||
MAX_CHAN = 4
|
||||
|
||||
};
|
||||
|
||||
enum alu_src_sel {
|
||||
|
||||
ALU_SRC_LDS_OQ_A = 219,
|
||||
ALU_SRC_LDS_OQ_B = 220,
|
||||
ALU_SRC_LDS_OQ_A_POP = 221,
|
||||
ALU_SRC_LDS_OQ_B_POP = 222,
|
||||
ALU_SRC_LDS_DIRECT_A = 223,
|
||||
ALU_SRC_LDS_DIRECT_B = 224,
|
||||
ALU_SRC_TIME_HI = 227,
|
||||
ALU_SRC_TIME_LO = 228,
|
||||
ALU_SRC_MASK_HI = 229,
|
||||
ALU_SRC_MASK_LO = 230,
|
||||
ALU_SRC_HW_WAVE_ID = 231,
|
||||
ALU_SRC_SIMD_ID = 232,
|
||||
ALU_SRC_SE_ID = 233,
|
||||
ALU_SRC_HW_THREADGRP_ID = 234,
|
||||
ALU_SRC_WAVE_ID_IN_GRP = 235,
|
||||
ALU_SRC_NUM_THREADGRP_WAVES = 236,
|
||||
ALU_SRC_HW_ALU_ODD = 237,
|
||||
ALU_SRC_LOOP_IDX = 238,
|
||||
ALU_SRC_PARAM_BASE_ADDR = 240,
|
||||
ALU_SRC_NEW_PRIM_MASK = 241,
|
||||
ALU_SRC_PRIM_MASK_HI = 242,
|
||||
ALU_SRC_PRIM_MASK_LO = 243,
|
||||
ALU_SRC_1_DBL_L = 244,
|
||||
ALU_SRC_1_DBL_M = 245,
|
||||
ALU_SRC_0_5_DBL_L = 246,
|
||||
ALU_SRC_0_5_DBL_M = 247,
|
||||
ALU_SRC_0 = 248,
|
||||
ALU_SRC_1 = 249,
|
||||
ALU_SRC_1_INT = 250,
|
||||
ALU_SRC_M_1_INT = 251,
|
||||
ALU_SRC_0_5 = 252,
|
||||
ALU_SRC_LITERAL = 253,
|
||||
ALU_SRC_PV = 254,
|
||||
ALU_SRC_PS = 255,
|
||||
|
||||
ALU_SRC_PARAM_OFFSET = 448
|
||||
};
|
||||
|
||||
enum alu_predicate_select
|
||||
{
|
||||
PRED_SEL_OFF = 0,
|
||||
// RESERVED = 1,
|
||||
PRED_SEL_0 = 2,
|
||||
PRED_SEL_1 = 3
|
||||
};
|
||||
|
||||
|
||||
enum alu_omod {
|
||||
OMOD_OFF = 0,
|
||||
OMOD_M2 = 1,
|
||||
OMOD_M4 = 2,
|
||||
OMOD_D2 = 3
|
||||
};
|
||||
|
||||
enum alu_index_mode {
|
||||
INDEX_AR_X = 0,
|
||||
INDEX_AR_Y_R600 = 1,
|
||||
INDEX_AR_Z_R600 = 2,
|
||||
INDEX_AR_W_R600 = 3,
|
||||
|
||||
INDEX_LOOP = 4,
|
||||
INDEX_GLOBAL = 5,
|
||||
INDEX_GLOBAL_AR_X = 6
|
||||
};
|
||||
|
||||
enum alu_cayman_mova_dst {
|
||||
CM_MOVADST_AR_X,
|
||||
CM_MOVADST_PC,
|
||||
CM_MOVADST_IDX0,
|
||||
CM_MOVADST_IDX1,
|
||||
CM_MOVADST_CG0, // clause-global byte 0
|
||||
CM_MOVADST_CG1,
|
||||
CM_MOVADST_CG2,
|
||||
CM_MOVADST_CG3
|
||||
};
|
||||
|
||||
enum alu_cayman_exec_mask_op {
|
||||
CM_EMO_DEACTIVATE,
|
||||
CM_EMO_BREAK,
|
||||
CM_EMO_CONTINUE,
|
||||
CM_EMO_KILL
|
||||
};
|
||||
|
||||
|
||||
enum cf_exp_type {
|
||||
EXP_PIXEL,
|
||||
EXP_POS,
|
||||
EXP_PARAM,
|
||||
|
||||
EXP_TYPE_COUNT
|
||||
};
|
||||
|
||||
enum cf_mem_type {
|
||||
MEM_WRITE,
|
||||
MEM_WRITE_IND,
|
||||
MEM_WRITE_ACK,
|
||||
MEM_WRITE_IND_ACK
|
||||
};
|
||||
|
||||
|
||||
enum alu_kcache_mode {
|
||||
KC_LOCK_NONE,
|
||||
KC_LOCK_1,
|
||||
KC_LOCK_2,
|
||||
KC_LOCK_LOOP
|
||||
};
|
||||
|
||||
enum alu_kcache_index_mode {
|
||||
KC_INDEX_NONE,
|
||||
KC_INDEX_0,
|
||||
KC_INDEX_1,
|
||||
KC_INDEX_INVALID
|
||||
};
|
||||
|
||||
enum chan_select {
|
||||
SEL_X = 0,
|
||||
SEL_Y = 1,
|
||||
SEL_Z = 2,
|
||||
SEL_W = 3,
|
||||
SEL_0 = 4,
|
||||
SEL_1 = 5,
|
||||
// RESERVED = 6,
|
||||
SEL_MASK = 7
|
||||
};
|
||||
|
||||
enum bank_swizzle {
|
||||
VEC_012 = 0,
|
||||
VEC_021 = 1,
|
||||
VEC_120 = 2,
|
||||
VEC_102 = 3,
|
||||
VEC_201 = 4,
|
||||
VEC_210 = 5,
|
||||
|
||||
VEC_NUM = 6,
|
||||
|
||||
SCL_210 = 0,
|
||||
SCL_122 = 1,
|
||||
SCL_212 = 2,
|
||||
SCL_221 = 3,
|
||||
|
||||
SCL_NUM = 4
|
||||
|
||||
};
|
||||
|
||||
enum sched_queue_id {
|
||||
SQ_CF,
|
||||
SQ_ALU,
|
||||
SQ_TEX,
|
||||
SQ_VTX,
|
||||
|
||||
SQ_NUM
|
||||
};
|
||||
|
||||
struct literal {
|
||||
union {
|
||||
int32_t i;
|
||||
uint32_t u;
|
||||
float f;
|
||||
};
|
||||
|
||||
literal(int32_t i = 0) : i(i) {}
|
||||
literal(uint32_t u) : u(u) {}
|
||||
literal(float f) : f(f) {}
|
||||
literal(double f) : f(f) {}
|
||||
operator uint32_t() const { return u; }
|
||||
bool operator ==(literal l) { return u == l.u; }
|
||||
bool operator ==(int v_int) { return i == v_int; }
|
||||
bool operator ==(unsigned v_uns) { return u == v_uns; }
|
||||
};
|
||||
|
||||
struct bc_kcache {
|
||||
unsigned mode;
|
||||
unsigned bank;
|
||||
unsigned addr;
|
||||
unsigned index_mode;
|
||||
} ;
|
||||
|
||||
// TODO optimize bc structures
|
||||
|
||||
struct bc_cf {
|
||||
|
||||
bc_kcache kc[4];
|
||||
|
||||
unsigned id;
|
||||
|
||||
|
||||
const cf_op_info * op_ptr;
|
||||
unsigned op;
|
||||
|
||||
unsigned addr:32;
|
||||
|
||||
unsigned alt_const:1;
|
||||
unsigned uses_waterfall:1;
|
||||
|
||||
unsigned barrier:1;
|
||||
unsigned count:7;
|
||||
unsigned pop_count:3;
|
||||
unsigned call_count:6;
|
||||
unsigned whole_quad_mode:1;
|
||||
unsigned valid_pixel_mode:1;
|
||||
|
||||
unsigned jumptable_sel:3;
|
||||
unsigned cf_const:5;
|
||||
unsigned cond:2;
|
||||
unsigned end_of_program:1;
|
||||
|
||||
unsigned array_base:13;
|
||||
unsigned elem_size:2;
|
||||
unsigned index_gpr:7;
|
||||
unsigned rw_gpr:7;
|
||||
unsigned rw_rel:1;
|
||||
unsigned type:2;
|
||||
|
||||
unsigned burst_count:4;
|
||||
unsigned mark:1;
|
||||
unsigned sel[4];
|
||||
|
||||
unsigned array_size:12;
|
||||
unsigned comp_mask:4;
|
||||
|
||||
unsigned rat_id:4;
|
||||
unsigned rat_inst:6;
|
||||
unsigned rat_index_mode:2;
|
||||
|
||||
void set_op(unsigned op) { this->op = op; op_ptr = r600_isa_cf(op); }
|
||||
|
||||
bool is_alu_extended() {
|
||||
assert(op_ptr->flags & CF_ALU);
|
||||
return kc[2].mode != KC_LOCK_NONE || kc[3].mode != KC_LOCK_NONE;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct bc_alu_src {
|
||||
unsigned sel:9;
|
||||
unsigned chan:2;
|
||||
unsigned neg:1;
|
||||
unsigned abs:1;
|
||||
unsigned rel:1;
|
||||
literal value;
|
||||
};
|
||||
|
||||
struct bc_alu {
|
||||
const alu_op_info * op_ptr;
|
||||
unsigned op;
|
||||
|
||||
bc_alu_src src[3];
|
||||
|
||||
unsigned dst_gpr:7;
|
||||
unsigned dst_chan:2;
|
||||
unsigned dst_rel:1;
|
||||
unsigned clamp:1;
|
||||
unsigned omod:2;
|
||||
unsigned bank_swizzle:3;
|
||||
|
||||
unsigned index_mode:3;
|
||||
unsigned last:1;
|
||||
unsigned pred_sel:2;
|
||||
|
||||
unsigned fog_merge:1;
|
||||
unsigned write_mask:1;
|
||||
unsigned update_exec_mask:1;
|
||||
unsigned update_pred:1;
|
||||
|
||||
unsigned slot:3;
|
||||
|
||||
alu_op_flags slot_flags;
|
||||
|
||||
void set_op(unsigned op) {
|
||||
this->op = op;
|
||||
op_ptr = r600_isa_alu(op);
|
||||
}
|
||||
};
|
||||
|
||||
struct bc_fetch {
|
||||
const fetch_op_info * op_ptr;
|
||||
unsigned op;
|
||||
|
||||
unsigned bc_frac_mode:1;
|
||||
unsigned fetch_whole_quad:1;
|
||||
unsigned resource_id:8;
|
||||
|
||||
unsigned src_gpr:7;
|
||||
unsigned src_rel:1;
|
||||
unsigned src_sel[4];
|
||||
|
||||
unsigned dst_gpr:7;
|
||||
unsigned dst_rel:1;
|
||||
unsigned dst_sel[4];
|
||||
|
||||
unsigned alt_const:1;
|
||||
|
||||
unsigned inst_mod:2;
|
||||
unsigned resource_index_mode:2;
|
||||
unsigned sampler_index_mode:2;
|
||||
|
||||
unsigned coord_type[4];
|
||||
unsigned lod_bias:7;
|
||||
|
||||
unsigned offset[3];
|
||||
|
||||
unsigned sampler_id:5;
|
||||
|
||||
|
||||
unsigned fetch_type:2;
|
||||
unsigned mega_fetch_count:6;
|
||||
unsigned coalesced_read:1;
|
||||
unsigned structured_read:2;
|
||||
unsigned lds_req:1;
|
||||
|
||||
unsigned data_format:6;
|
||||
unsigned format_comp_all:1;
|
||||
unsigned num_format_all:2;
|
||||
unsigned semantic_id:8;
|
||||
unsigned srf_mode_all:1;
|
||||
unsigned use_const_fields:1;
|
||||
|
||||
unsigned const_buf_no_stride:1;
|
||||
unsigned endian_swap:2;
|
||||
unsigned mega_fetch:1;
|
||||
|
||||
void set_op(unsigned op) { this->op = op; op_ptr = r600_isa_fetch(op); }
|
||||
};
|
||||
|
||||
class sb_context {
|
||||
|
||||
public:
|
||||
|
||||
r600_isa *isa;
|
||||
|
||||
sb_hw_chip hw_chip;
|
||||
sb_hw_class hw_class;
|
||||
|
||||
unsigned alu_temp_gprs;
|
||||
unsigned max_fetch;
|
||||
bool has_trans;
|
||||
unsigned vtx_src_num;
|
||||
unsigned num_slots;
|
||||
bool uses_mova_gpr;
|
||||
|
||||
unsigned stack_entry_size;
|
||||
|
||||
static unsigned dump_pass;
|
||||
static unsigned dump_stat;
|
||||
|
||||
static unsigned dry_run;
|
||||
|
||||
static unsigned dskip_start;
|
||||
static unsigned dskip_end;
|
||||
static unsigned dskip_mode;
|
||||
|
||||
sb_context()
|
||||
: isa(0), hw_chip(HW_CHIP_UNKNOWN), hw_class(HW_CLASS_UNKNOWN) {}
|
||||
|
||||
int init(r600_isa *isa, sb_hw_chip chip, sb_hw_class cclass);
|
||||
|
||||
bool is_r600() {return hw_class == HW_CLASS_R600;}
|
||||
bool is_r700() {return hw_class == HW_CLASS_R700;}
|
||||
bool is_evergreen() {return hw_class == HW_CLASS_EVERGREEN;}
|
||||
bool is_cayman() {return hw_class == HW_CLASS_CAYMAN;}
|
||||
bool is_egcm() {return hw_class >= HW_CLASS_EVERGREEN;}
|
||||
|
||||
sb_hw_class_bits hw_class_bit() {
|
||||
switch (hw_class) {
|
||||
case HW_CLASS_R600:return HB_R6;
|
||||
case HW_CLASS_R700:return HB_R7;
|
||||
case HW_CLASS_EVERGREEN:return HB_EG;
|
||||
case HW_CLASS_CAYMAN:return HB_CM;
|
||||
default: assert(!"unknown hw class"); return (sb_hw_class_bits)0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
unsigned cf_opcode(unsigned op) {
|
||||
return r600_isa_cf_opcode(isa->hw_class, op);
|
||||
}
|
||||
|
||||
unsigned alu_opcode(unsigned op) {
|
||||
return r600_isa_alu_opcode(isa->hw_class, op);
|
||||
}
|
||||
|
||||
unsigned alu_slots(unsigned op) {
|
||||
return r600_isa_alu_slots(isa->hw_class, op);
|
||||
}
|
||||
|
||||
unsigned alu_slots(const alu_op_info * op_ptr) {
|
||||
return op_ptr->slots[isa->hw_class];
|
||||
}
|
||||
|
||||
unsigned alu_slots_mask(const alu_op_info * op_ptr) {
|
||||
unsigned mask = 0;
|
||||
unsigned slot_flags = alu_slots(op_ptr);
|
||||
if (slot_flags & AF_V)
|
||||
mask = 0b01111;
|
||||
if (!is_cayman() && (slot_flags & AF_S))
|
||||
mask |= 0b10000;
|
||||
return mask;
|
||||
}
|
||||
|
||||
unsigned fetch_opcode(unsigned op) {
|
||||
return r600_isa_fetch_opcode(isa->hw_class, op);
|
||||
}
|
||||
|
||||
bool is_kcache_sel(unsigned sel) {
|
||||
return ((sel >= 128 && sel < 192) || (sel >= 256 && sel < 320));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#define SB_DUMP_STAT(a) do { if (sb_context::dump_stat) { a } } while (0)
|
||||
#define SB_DUMP_PASS(a) do { if (sb_context::dump_pass) { a } } while (0)
|
||||
|
||||
class bc_decoder {
|
||||
|
||||
sb_context &ctx;
|
||||
|
||||
uint32_t* dw;
|
||||
unsigned ndw;
|
||||
|
||||
public:
|
||||
|
||||
bc_decoder(sb_context &sctx, uint32_t *data, unsigned size)
|
||||
: ctx(sctx), dw(data), ndw(size) {}
|
||||
|
||||
int decode_cf(unsigned &i, bc_cf &bc);
|
||||
int decode_alu(unsigned &i, bc_alu &bc);
|
||||
int decode_fetch(unsigned &i, bc_fetch &bc);
|
||||
|
||||
private:
|
||||
int decode_cf_alu(unsigned &i, bc_cf &bc);
|
||||
int decode_cf_exp(unsigned &i, bc_cf &bc);
|
||||
int decode_cf_mem(unsigned &i, bc_cf &bc);
|
||||
|
||||
int decode_fetch_vtx(unsigned &i, bc_fetch &bc);
|
||||
};
|
||||
|
||||
// bytecode format definition
|
||||
|
||||
class hw_encoding_format {
|
||||
const sb_hw_class_bits hw_target; //FIXME: debug - remove after testing
|
||||
hw_encoding_format();
|
||||
protected:
|
||||
uint32_t value;
|
||||
public:
|
||||
hw_encoding_format(sb_hw_class_bits hw)
|
||||
: hw_target(hw), value(0) {}
|
||||
hw_encoding_format(uint32_t v, sb_hw_class_bits hw)
|
||||
: hw_target(hw), value(v) {}
|
||||
uint32_t get_value(sb_hw_class_bits hw) const {
|
||||
assert((hw & hw_target) == hw);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
#define BC_FORMAT_BEGIN_HW(fmt, hwset) \
|
||||
class fmt##_##hwset : public hw_encoding_format {\
|
||||
typedef fmt##_##hwset thistype; \
|
||||
public: \
|
||||
fmt##_##hwset() : hw_encoding_format(HB_##hwset) {}; \
|
||||
fmt##_##hwset(uint32_t v) : hw_encoding_format(v, HB_##hwset) {};
|
||||
|
||||
#define BC_FORMAT_BEGIN(fmt) BC_FORMAT_BEGIN_HW(fmt, ALL)
|
||||
|
||||
#define BC_FORMAT_END(fmt) };
|
||||
|
||||
// bytecode format field definition
|
||||
|
||||
#define BC_FIELD(fmt, name, shortname, last_bit, first_bit) \
|
||||
thistype & name(unsigned v) { \
|
||||
value |= ((v&((1ull<<((last_bit)-(first_bit)+1))-1))<<(first_bit)); \
|
||||
return *this; \
|
||||
} \
|
||||
unsigned get_##name() const { \
|
||||
return (value>>(first_bit))&((1ull<<((last_bit)-(first_bit)+1))-1); \
|
||||
} \
|
||||
|
||||
#define BC_RSRVD(fmt, last_bit, first_bit)
|
||||
|
||||
// CLAMP macro defined elsewhere interferes with bytecode field name
|
||||
#undef CLAMP
|
||||
|
||||
#include "sb_bc_fmt_def.inc"
|
||||
|
||||
#undef BC_FORMAT_BEGIN
|
||||
#undef BC_FORMAT_END
|
||||
#undef BC_FIELD
|
||||
#undef BC_RSRVD
|
||||
|
||||
class bc_parser {
|
||||
sb_context & ctx;
|
||||
|
||||
bc_decoder *dec;
|
||||
|
||||
r600_bytecode *bc;
|
||||
r600_shader *pshader;
|
||||
|
||||
uint32_t *dw;
|
||||
unsigned bc_ndw;
|
||||
|
||||
unsigned max_cf;
|
||||
|
||||
shader *sh;
|
||||
|
||||
int error;
|
||||
|
||||
alu_node *slots[2][5];
|
||||
unsigned cgroup;
|
||||
|
||||
typedef std::vector<cf_node*> id_cf_map;
|
||||
id_cf_map cf_map;
|
||||
|
||||
typedef std::stack<region_node*> region_stack;
|
||||
region_stack loop_stack;
|
||||
|
||||
int enable_dump;
|
||||
int optimize;
|
||||
|
||||
public:
|
||||
|
||||
bc_parser(sb_context &sctx, r600_bytecode *bc, r600_shader* pshader,
|
||||
int dump_source, int optimize) :
|
||||
ctx(sctx), dec(), bc(bc), pshader(pshader),
|
||||
dw(), bc_ndw(), max_cf(),
|
||||
sh(), error(), slots(), cgroup(),
|
||||
cf_map(), loop_stack(), enable_dump(dump_source),
|
||||
optimize(optimize) { }
|
||||
|
||||
int parse();
|
||||
|
||||
shader* get_shader() { assert(!error); return sh; }
|
||||
|
||||
private:
|
||||
|
||||
int parse_shader();
|
||||
|
||||
int parse_decls();
|
||||
|
||||
int parse_cf(unsigned &i, bool &eop);
|
||||
|
||||
int parse_alu_clause(cf_node *cf);
|
||||
int parse_alu_group(cf_node* cf, unsigned &i, unsigned &gcnt);
|
||||
|
||||
int parse_fetch_clause(cf_node *cf);
|
||||
|
||||
int prepare_ir();
|
||||
int prepare_loop(cf_node *c);
|
||||
int prepare_if(cf_node *c);
|
||||
int prepare_alu_clause(cf_node *c);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class bytecode {
|
||||
typedef std::vector<uint32_t> bc_vector;
|
||||
sb_hw_class_bits hw_class_bit;
|
||||
|
||||
bc_vector bc;
|
||||
|
||||
unsigned pos;
|
||||
|
||||
public:
|
||||
|
||||
bytecode(sb_hw_class_bits hw, unsigned rdw = 256)
|
||||
: hw_class_bit(hw), pos(0) { bc.reserve(rdw); }
|
||||
|
||||
unsigned ndw() { return bc.size(); }
|
||||
|
||||
void write_data(uint32_t* dst) {
|
||||
memcpy(dst, bc.data(), 4 * bc.size());
|
||||
}
|
||||
|
||||
void align(unsigned a) {
|
||||
unsigned size = bc.size();
|
||||
size = (size + a - 1) & ~(a-1);
|
||||
bc.resize(size);
|
||||
}
|
||||
|
||||
void set_size(unsigned sz) {
|
||||
assert(sz >= bc.size());
|
||||
bc.resize(sz);
|
||||
}
|
||||
|
||||
void seek(unsigned p) {
|
||||
if (p != pos) {
|
||||
if (p > bc.size()) {
|
||||
bc.resize(p);
|
||||
}
|
||||
pos = p;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned get_pos() { return pos; }
|
||||
uint32_t *data() { return bc.data(); }
|
||||
|
||||
bytecode & operator <<(uint32_t v) {
|
||||
if (pos == ndw()) {
|
||||
bc.push_back(v);
|
||||
} else
|
||||
bc.at(pos) = v;
|
||||
++pos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bytecode & operator <<(const hw_encoding_format &e) {
|
||||
*this << e.get_value(hw_class_bit);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bytecode & operator <<(const bytecode &b) {
|
||||
bc.insert(bc.end(), b.bc.begin(), b.bc.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint32_t at(unsigned dw_id) { return bc.at(dw_id); }
|
||||
};
|
||||
|
||||
|
||||
class bc_builder {
|
||||
shader &sh;
|
||||
sb_context &ctx;
|
||||
bytecode bb;
|
||||
int error;
|
||||
|
||||
public:
|
||||
|
||||
bc_builder(shader &s);
|
||||
int build();
|
||||
bytecode& get_bytecode() { assert(!error); return bb; }
|
||||
|
||||
private:
|
||||
|
||||
int build_cf(cf_node *n);
|
||||
|
||||
int build_cf_alu(cf_node *n);
|
||||
int build_cf_mem(cf_node *n);
|
||||
int build_cf_exp(cf_node *n);
|
||||
|
||||
int build_alu_clause(cf_node *n);
|
||||
int build_alu_group(alu_group_node *n);
|
||||
int build_alu(alu_node *n);
|
||||
|
||||
int build_fetch_clause(cf_node *n);
|
||||
int build_fetch_tex(fetch_node *n);
|
||||
int build_fetch_vtx(fetch_node *n);
|
||||
};
|
||||
|
||||
} // namespace r600_sb
|
||||
|
||||
#endif /* SB_BC_H_ */
|
||||
@@ -0,0 +1,638 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "sb_bc.h"
|
||||
#include "sb_shader.h"
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
bc_builder::bc_builder(shader &s)
|
||||
: sh(s), ctx(s.get_ctx()), bb(ctx.hw_class_bit()), error(0) {}
|
||||
|
||||
int bc_builder::build() {
|
||||
|
||||
container_node *root = sh.root;
|
||||
int cf_cnt = 0;
|
||||
|
||||
// FIXME reserve total size to avoid reallocs
|
||||
|
||||
for (node_iterator it = root->begin(), end = root->end();
|
||||
it != end; ++it) {
|
||||
|
||||
cf_node *cf = static_cast<cf_node*>(*it);
|
||||
assert(cf->is_cf_inst() || cf->is_alu_clause() || cf->is_fetch_clause());
|
||||
|
||||
cf_op_flags flags = (cf_op_flags)cf->bc.op_ptr->flags;
|
||||
|
||||
cf->bc.id = cf_cnt++;
|
||||
|
||||
if (flags & CF_ALU) {
|
||||
if (cf->bc.is_alu_extended())
|
||||
cf_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
bb.set_size(cf_cnt << 1);
|
||||
bb.seek(cf_cnt << 1);
|
||||
|
||||
unsigned cf_pos = 0;
|
||||
|
||||
for (node_iterator I = root->begin(), end = root->end();
|
||||
I != end; ++I) {
|
||||
|
||||
cf_node *cf = static_cast<cf_node*>(*I);
|
||||
cf_op_flags flags = (cf_op_flags)cf->bc.op_ptr->flags;
|
||||
|
||||
if (flags & CF_ALU) {
|
||||
bb.seek(bb.ndw());
|
||||
cf->bc.addr = bb.ndw() >> 1;
|
||||
build_alu_clause(cf);
|
||||
cf->bc.count = (bb.ndw() >> 1) - cf->bc.addr - 1;
|
||||
} else if (flags & CF_FETCH) {
|
||||
bb.align(4);
|
||||
bb.seek(bb.ndw());
|
||||
cf->bc.addr = bb.ndw() >> 1;
|
||||
build_fetch_clause(cf);
|
||||
cf->bc.count = (((bb.ndw() >> 1) - cf->bc.addr) >> 1) - 1;
|
||||
} else if (cf->jump_target) {
|
||||
cf->bc.addr = cf->jump_target->bc.id;
|
||||
if (cf->jump_after_target)
|
||||
cf->bc.addr += 1;
|
||||
}
|
||||
|
||||
bb.seek(cf_pos);
|
||||
build_cf(cf);
|
||||
cf_pos = bb.get_pos();
|
||||
}
|
||||
|
||||
if (sh.enable_dump) {
|
||||
bc_dump(sh, cerr, &bb).run();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_builder::build_alu_clause(cf_node* n) {
|
||||
for (node_iterator I = n->begin(), E = n->end();
|
||||
I != E; ++I) {
|
||||
|
||||
alu_group_node *g = static_cast<alu_group_node*>(*I);
|
||||
assert(g->is_valid());
|
||||
|
||||
build_alu_group(g);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_builder::build_alu_group(alu_group_node* n) {
|
||||
|
||||
for (node_iterator I = n->begin(), E = n->end();
|
||||
I != E; ++I) {
|
||||
|
||||
alu_node *a = static_cast<alu_node*>(*I);
|
||||
assert(a->is_valid());
|
||||
build_alu(a);
|
||||
}
|
||||
|
||||
for(int i = 0, ls = n->literals.size(); i < ls; ++i) {
|
||||
bb << n->literals.at(i).u;
|
||||
}
|
||||
|
||||
bb.align(2);
|
||||
bb.seek(bb.ndw());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_builder::build_fetch_clause(cf_node* n) {
|
||||
for (node_iterator I = n->begin(), E = n->end();
|
||||
I != E; ++I) {
|
||||
fetch_node *f = static_cast<fetch_node*>(*I);
|
||||
|
||||
if (f->bc.op_ptr->flags & FF_VTX)
|
||||
build_fetch_vtx(f);
|
||||
else
|
||||
build_fetch_tex(f);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int bc_builder::build_cf(cf_node* n) {
|
||||
const bc_cf &bc = n->bc;
|
||||
const cf_op_info *cfop = bc.op_ptr;
|
||||
|
||||
if (cfop->flags & CF_ALU)
|
||||
return build_cf_alu(n);
|
||||
if (cfop->flags & (CF_EXP | CF_MEM))
|
||||
return build_cf_exp(n);
|
||||
|
||||
if (ctx.is_egcm()) {
|
||||
bb << CF_WORD0_EGCM()
|
||||
.ADDR(bc.addr)
|
||||
.JUMPTABLE_SEL(bc.jumptable_sel);
|
||||
|
||||
if (ctx.is_evergreen())
|
||||
|
||||
bb << CF_WORD1_EG()
|
||||
.BARRIER(bc.barrier)
|
||||
.CF_CONST(bc.cf_const)
|
||||
.CF_INST(ctx.cf_opcode(bc.op))
|
||||
.COND(bc.cond)
|
||||
.COUNT(bc.count)
|
||||
.END_OF_PROGRAM(bc.end_of_program)
|
||||
.POP_COUNT(bc.pop_count)
|
||||
.VALID_PIXEL_MODE(bc.valid_pixel_mode)
|
||||
.WHOLE_QUAD_MODE(bc.whole_quad_mode);
|
||||
|
||||
else //cayman
|
||||
|
||||
bb << CF_WORD1_CM()
|
||||
.BARRIER(bc.barrier)
|
||||
.CF_CONST(bc.cf_const)
|
||||
.CF_INST(ctx.cf_opcode(bc.op))
|
||||
.COND(bc.cond)
|
||||
.COUNT(bc.count)
|
||||
.POP_COUNT(bc.pop_count)
|
||||
.VALID_PIXEL_MODE(bc.valid_pixel_mode);
|
||||
} else {
|
||||
bb << CF_WORD0_R6R7()
|
||||
.ADDR(bc.addr);
|
||||
|
||||
assert(bc.count < ctx.max_fetch);
|
||||
|
||||
bb << CF_WORD1_R6R7()
|
||||
.BARRIER(bc.barrier)
|
||||
.CALL_COUNT(bc.call_count)
|
||||
.CF_CONST(bc.cf_const)
|
||||
.CF_INST(ctx.cf_opcode(bc.op))
|
||||
.COND(bc.cond)
|
||||
.COUNT(bc.count & 7)
|
||||
.COUNT_3(bc.count >> 3)
|
||||
.END_OF_PROGRAM(bc.end_of_program)
|
||||
.POP_COUNT(bc.pop_count)
|
||||
.VALID_PIXEL_MODE(bc.valid_pixel_mode)
|
||||
.WHOLE_QUAD_MODE(bc.whole_quad_mode);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_builder::build_cf_alu(cf_node* n) {
|
||||
const bc_cf &bc = n->bc;
|
||||
|
||||
assert(bc.count < 128);
|
||||
|
||||
if (n->bc.is_alu_extended()) {
|
||||
assert(ctx.is_egcm());
|
||||
|
||||
bb << CF_ALU_WORD0_EXT_EGCM()
|
||||
.KCACHE_BANK2(bc.kc[2].bank)
|
||||
.KCACHE_BANK3(bc.kc[3].bank)
|
||||
.KCACHE_BANK_INDEX_MODE0(bc.kc[0].index_mode)
|
||||
.KCACHE_BANK_INDEX_MODE1(bc.kc[1].index_mode)
|
||||
.KCACHE_BANK_INDEX_MODE2(bc.kc[2].index_mode)
|
||||
.KCACHE_BANK_INDEX_MODE3(bc.kc[3].index_mode)
|
||||
.KCACHE_MODE2(bc.kc[2].mode);
|
||||
|
||||
bb << CF_ALU_WORD1_EXT_EGCM()
|
||||
.BARRIER(bc.barrier)
|
||||
.CF_INST(ctx.cf_opcode(CF_OP_ALU_EXT))
|
||||
.KCACHE_ADDR2(bc.kc[2].addr)
|
||||
.KCACHE_ADDR3(bc.kc[3].addr)
|
||||
.KCACHE_MODE3(bc.kc[3].mode);
|
||||
}
|
||||
|
||||
bb << CF_ALU_WORD0_ALL()
|
||||
.ADDR(bc.addr)
|
||||
.KCACHE_BANK0(bc.kc[0].bank)
|
||||
.KCACHE_BANK1(bc.kc[1].bank)
|
||||
.KCACHE_MODE0(bc.kc[0].mode);
|
||||
|
||||
assert(bc.count < 128);
|
||||
|
||||
if (ctx.is_r600())
|
||||
bb << CF_ALU_WORD1_R6()
|
||||
.BARRIER(bc.barrier)
|
||||
.CF_INST(ctx.cf_opcode(bc.op))
|
||||
.COUNT(bc.count)
|
||||
.KCACHE_ADDR0(bc.kc[0].addr)
|
||||
.KCACHE_ADDR1(bc.kc[1].addr)
|
||||
.KCACHE_MODE1(bc.kc[1].mode)
|
||||
.USES_WATERFALL(bc.uses_waterfall)
|
||||
.WHOLE_QUAD_MODE(bc.whole_quad_mode);
|
||||
else
|
||||
bb << CF_ALU_WORD1_R7EGCM()
|
||||
.ALT_CONST(bc.alt_const)
|
||||
.BARRIER(bc.barrier)
|
||||
.CF_INST(ctx.cf_opcode(bc.op))
|
||||
.COUNT(bc.count)
|
||||
.KCACHE_ADDR0(bc.kc[0].addr)
|
||||
.KCACHE_ADDR1(bc.kc[1].addr)
|
||||
.KCACHE_MODE1(bc.kc[1].mode)
|
||||
.WHOLE_QUAD_MODE(bc.whole_quad_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_builder::build_cf_exp(cf_node* n) {
|
||||
const bc_cf &bc = n->bc;
|
||||
const cf_op_info *cfop = bc.op_ptr;
|
||||
|
||||
if (cfop->flags & CF_RAT) {
|
||||
assert(ctx.is_egcm());
|
||||
|
||||
bb << CF_ALLOC_EXPORT_WORD0_RAT_EGCM()
|
||||
.ELEM_SIZE(bc.elem_size)
|
||||
.INDEX_GPR(bc.index_gpr)
|
||||
.RAT_ID(bc.rat_id)
|
||||
.RAT_INDEX_MODE(bc.rat_index_mode)
|
||||
.RAT_INST(bc.rat_inst)
|
||||
.RW_GPR(bc.rw_gpr)
|
||||
.RW_REL(bc.rw_rel)
|
||||
.TYPE(bc.type);
|
||||
} else {
|
||||
|
||||
bb << CF_ALLOC_EXPORT_WORD0_ALL()
|
||||
.ARRAY_BASE(bc.array_base)
|
||||
.ELEM_SIZE(bc.elem_size)
|
||||
.INDEX_GPR(bc.index_gpr)
|
||||
.RW_GPR(bc.rw_gpr)
|
||||
.RW_REL(bc.rw_rel)
|
||||
.TYPE(bc.type);
|
||||
}
|
||||
|
||||
if (cfop->flags & CF_EXP) {
|
||||
|
||||
if (!ctx.is_egcm())
|
||||
bb << CF_ALLOC_EXPORT_WORD1_SWIZ_R6R7()
|
||||
.BARRIER(bc.barrier)
|
||||
.BURST_COUNT(bc.burst_count)
|
||||
.CF_INST(ctx.cf_opcode(bc.op))
|
||||
.END_OF_PROGRAM(bc.end_of_program)
|
||||
.SEL_X(bc.sel[0])
|
||||
.SEL_Y(bc.sel[1])
|
||||
.SEL_Z(bc.sel[2])
|
||||
.SEL_W(bc.sel[3])
|
||||
.VALID_PIXEL_MODE(bc.valid_pixel_mode)
|
||||
.WHOLE_QUAD_MODE(bc.whole_quad_mode);
|
||||
|
||||
else if (ctx.is_evergreen())
|
||||
bb << CF_ALLOC_EXPORT_WORD1_SWIZ_EG()
|
||||
.BARRIER(bc.barrier)
|
||||
.BURST_COUNT(bc.burst_count)
|
||||
.CF_INST(ctx.cf_opcode(bc.op))
|
||||
.END_OF_PROGRAM(bc.end_of_program)
|
||||
.MARK(bc.mark)
|
||||
.SEL_X(bc.sel[0])
|
||||
.SEL_Y(bc.sel[1])
|
||||
.SEL_Z(bc.sel[2])
|
||||
.SEL_W(bc.sel[3])
|
||||
.VALID_PIXEL_MODE(bc.valid_pixel_mode);
|
||||
|
||||
else // cayman
|
||||
bb << CF_ALLOC_EXPORT_WORD1_SWIZ_CM()
|
||||
.BARRIER(bc.barrier)
|
||||
.BURST_COUNT(bc.burst_count)
|
||||
.CF_INST(ctx.cf_opcode(bc.op))
|
||||
.MARK(bc.mark)
|
||||
.SEL_X(bc.sel[0])
|
||||
.SEL_Y(bc.sel[1])
|
||||
.SEL_Z(bc.sel[2])
|
||||
.SEL_W(bc.sel[3])
|
||||
.VALID_PIXEL_MODE(bc.valid_pixel_mode);
|
||||
|
||||
} else if (cfop->flags & CF_MEM) {
|
||||
return build_cf_mem(n);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_builder::build_cf_mem(cf_node* n) {
|
||||
const bc_cf &bc = n->bc;
|
||||
|
||||
if (!ctx.is_egcm())
|
||||
bb << CF_ALLOC_EXPORT_WORD1_BUF_R6R7()
|
||||
.ARRAY_SIZE(bc.array_size)
|
||||
.BARRIER(bc.barrier)
|
||||
.BURST_COUNT(bc.burst_count)
|
||||
.CF_INST(ctx.cf_opcode(bc.op))
|
||||
.COMP_MASK(bc.comp_mask)
|
||||
.END_OF_PROGRAM(bc.end_of_program)
|
||||
.VALID_PIXEL_MODE(bc.valid_pixel_mode)
|
||||
.WHOLE_QUAD_MODE(bc.whole_quad_mode);
|
||||
|
||||
else if (ctx.is_evergreen())
|
||||
bb << CF_ALLOC_EXPORT_WORD1_BUF_EG()
|
||||
.ARRAY_SIZE(bc.array_size)
|
||||
.BARRIER(bc.barrier)
|
||||
.BURST_COUNT(bc.burst_count)
|
||||
.CF_INST(ctx.cf_opcode(bc.op))
|
||||
.COMP_MASK(bc.comp_mask)
|
||||
.END_OF_PROGRAM(bc.end_of_program)
|
||||
.MARK(bc.mark)
|
||||
.VALID_PIXEL_MODE(bc.valid_pixel_mode);
|
||||
|
||||
else // cayman
|
||||
bb << CF_ALLOC_EXPORT_WORD1_BUF_CM()
|
||||
.ARRAY_SIZE(bc.array_size)
|
||||
.BARRIER(bc.barrier)
|
||||
.BURST_COUNT(bc.burst_count)
|
||||
.CF_INST(ctx.cf_opcode(bc.op))
|
||||
.COMP_MASK(bc.comp_mask)
|
||||
.MARK(bc.mark)
|
||||
.VALID_PIXEL_MODE(bc.valid_pixel_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_builder::build_alu(alu_node* n) {
|
||||
const bc_alu &bc = n->bc;
|
||||
const alu_op_info *aop = bc.op_ptr;
|
||||
|
||||
bb << ALU_WORD0_ALL()
|
||||
.INDEX_MODE(bc.index_mode)
|
||||
.LAST(bc.last)
|
||||
.PRED_SEL(bc.pred_sel)
|
||||
.SRC0_SEL(bc.src[0].sel)
|
||||
.SRC0_CHAN(bc.src[0].chan)
|
||||
.SRC0_NEG(bc.src[0].neg)
|
||||
.SRC0_REL(bc.src[0].rel)
|
||||
.SRC1_SEL(bc.src[1].sel)
|
||||
.SRC1_CHAN(bc.src[1].chan)
|
||||
.SRC1_NEG(bc.src[1].neg)
|
||||
.SRC1_REL(bc.src[1].rel);
|
||||
|
||||
if (aop->src_count<3) {
|
||||
if (ctx.is_r600())
|
||||
bb << ALU_WORD1_OP2_R6()
|
||||
.ALU_INST(ctx.alu_opcode(bc.op))
|
||||
.BANK_SWIZZLE(bc.bank_swizzle)
|
||||
.CLAMP(bc.clamp)
|
||||
.DST_GPR(bc.dst_gpr)
|
||||
.DST_CHAN(bc.dst_chan)
|
||||
.DST_REL(bc.dst_rel)
|
||||
.FOG_MERGE(bc.fog_merge)
|
||||
.OMOD(bc.omod)
|
||||
.SRC0_ABS(bc.src[0].abs)
|
||||
.SRC1_ABS(bc.src[1].abs)
|
||||
.UPDATE_EXEC_MASK(bc.update_exec_mask)
|
||||
.UPDATE_PRED(bc.update_pred)
|
||||
.WRITE_MASK(bc.write_mask);
|
||||
else {
|
||||
|
||||
if (ctx.is_cayman() && (aop->flags & AF_MOVA)) {
|
||||
|
||||
bb << ALU_WORD1_OP2_MOVA_CM()
|
||||
.ALU_INST(ctx.alu_opcode(bc.op))
|
||||
.BANK_SWIZZLE(bc.bank_swizzle)
|
||||
.CLAMP(bc.clamp)
|
||||
.MOVA_DST(bc.dst_gpr)
|
||||
.DST_CHAN(bc.dst_chan)
|
||||
.DST_REL(bc.dst_rel)
|
||||
.OMOD(bc.omod)
|
||||
.UPDATE_EXEC_MASK(bc.update_exec_mask)
|
||||
.UPDATE_PRED(bc.update_pred)
|
||||
.WRITE_MASK(bc.write_mask)
|
||||
.SRC0_ABS(bc.src[0].abs)
|
||||
.SRC1_ABS(bc.src[1].abs);
|
||||
|
||||
} else if (ctx.is_cayman() && (aop->flags & (AF_PRED|AF_KILL))) {
|
||||
bb << ALU_WORD1_OP2_EXEC_MASK_CM()
|
||||
.ALU_INST(ctx.alu_opcode(bc.op))
|
||||
.BANK_SWIZZLE(bc.bank_swizzle)
|
||||
.CLAMP(bc.clamp)
|
||||
.DST_CHAN(bc.dst_chan)
|
||||
.DST_REL(bc.dst_rel)
|
||||
.EXECUTE_MASK_OP(bc.omod)
|
||||
.UPDATE_EXEC_MASK(bc.update_exec_mask)
|
||||
.UPDATE_PRED(bc.update_pred)
|
||||
.WRITE_MASK(bc.write_mask)
|
||||
.SRC0_ABS(bc.src[0].abs)
|
||||
.SRC1_ABS(bc.src[1].abs);
|
||||
|
||||
} else
|
||||
bb << ALU_WORD1_OP2_R7EGCM()
|
||||
.ALU_INST(ctx.alu_opcode(bc.op))
|
||||
.BANK_SWIZZLE(bc.bank_swizzle)
|
||||
.CLAMP(bc.clamp)
|
||||
.DST_GPR(bc.dst_gpr)
|
||||
.DST_CHAN(bc.dst_chan)
|
||||
.DST_REL(bc.dst_rel)
|
||||
.OMOD(bc.omod)
|
||||
.UPDATE_EXEC_MASK(bc.update_exec_mask)
|
||||
.UPDATE_PRED(bc.update_pred)
|
||||
.WRITE_MASK(bc.write_mask)
|
||||
.SRC0_ABS(bc.src[0].abs)
|
||||
.SRC1_ABS(bc.src[1].abs);
|
||||
|
||||
}
|
||||
} else
|
||||
bb << ALU_WORD1_OP3_ALL()
|
||||
.ALU_INST(ctx.alu_opcode(bc.op))
|
||||
.BANK_SWIZZLE(bc.bank_swizzle)
|
||||
.CLAMP(bc.clamp)
|
||||
.DST_GPR(bc.dst_gpr)
|
||||
.DST_CHAN(bc.dst_chan)
|
||||
.DST_REL(bc.dst_rel)
|
||||
.SRC2_SEL(bc.src[2].sel)
|
||||
.SRC2_CHAN(bc.src[2].chan)
|
||||
.SRC2_NEG(bc.src[2].neg)
|
||||
.SRC2_REL(bc.src[2].rel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_builder::build_fetch_tex(fetch_node* n) {
|
||||
const bc_fetch &bc = n->bc;
|
||||
const fetch_op_info *fop = bc.op_ptr;
|
||||
|
||||
assert(!(fop->flags & FF_VTX));
|
||||
|
||||
if (ctx.is_r600())
|
||||
bb << TEX_WORD0_R6()
|
||||
.BC_FRAC_MODE(bc.bc_frac_mode)
|
||||
.FETCH_WHOLE_QUAD(bc.fetch_whole_quad)
|
||||
.RESOURCE_ID(bc.resource_id)
|
||||
.SRC_GPR(bc.src_gpr)
|
||||
.SRC_REL(bc.src_rel)
|
||||
.TEX_INST(ctx.fetch_opcode(bc.op));
|
||||
|
||||
else if (ctx.is_r700())
|
||||
bb << TEX_WORD0_R7()
|
||||
.ALT_CONST(bc.alt_const)
|
||||
.BC_FRAC_MODE(bc.bc_frac_mode)
|
||||
.FETCH_WHOLE_QUAD(bc.fetch_whole_quad)
|
||||
.RESOURCE_ID(bc.resource_id)
|
||||
.SRC_GPR(bc.src_gpr)
|
||||
.SRC_REL(bc.src_rel)
|
||||
.TEX_INST(ctx.fetch_opcode(bc.op));
|
||||
|
||||
else
|
||||
bb << TEX_WORD0_EGCM()
|
||||
.ALT_CONST(bc.alt_const)
|
||||
.FETCH_WHOLE_QUAD(bc.fetch_whole_quad)
|
||||
.INST_MOD(bc.inst_mod)
|
||||
.RESOURCE_ID(bc.resource_id)
|
||||
.RESOURCE_INDEX_MODE(bc.resource_index_mode)
|
||||
.SAMPLER_INDEX_MODE(bc.sampler_index_mode)
|
||||
.SRC_GPR(bc.src_gpr)
|
||||
.SRC_REL(bc.src_rel)
|
||||
.TEX_INST(ctx.fetch_opcode(bc.op));
|
||||
|
||||
bb << TEX_WORD1_ALL()
|
||||
.COORD_TYPE_X(bc.coord_type[0])
|
||||
.COORD_TYPE_Y(bc.coord_type[1])
|
||||
.COORD_TYPE_Z(bc.coord_type[2])
|
||||
.COORD_TYPE_W(bc.coord_type[3])
|
||||
.DST_GPR(bc.dst_gpr)
|
||||
.DST_REL(bc.dst_rel)
|
||||
.DST_SEL_X(bc.dst_sel[0])
|
||||
.DST_SEL_Y(bc.dst_sel[1])
|
||||
.DST_SEL_Z(bc.dst_sel[2])
|
||||
.DST_SEL_W(bc.dst_sel[3])
|
||||
.LOD_BIAS(bc.lod_bias);
|
||||
|
||||
bb << TEX_WORD2_ALL()
|
||||
.OFFSET_X(bc.offset[0])
|
||||
.OFFSET_Y(bc.offset[1])
|
||||
.OFFSET_Z(bc.offset[2])
|
||||
.SAMPLER_ID(bc.sampler_id)
|
||||
.SRC_SEL_X(bc.src_sel[0])
|
||||
.SRC_SEL_Y(bc.src_sel[1])
|
||||
.SRC_SEL_Z(bc.src_sel[2])
|
||||
.SRC_SEL_W(bc.src_sel[3]);
|
||||
|
||||
bb << 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_builder::build_fetch_vtx(fetch_node* n) {
|
||||
const bc_fetch &bc = n->bc;
|
||||
const fetch_op_info *fop = bc.op_ptr;
|
||||
|
||||
assert(fop->flags & FF_VTX);
|
||||
|
||||
if (!ctx.is_cayman())
|
||||
bb << VTX_WORD0_R6R7EG()
|
||||
.BUFFER_ID(bc.resource_id)
|
||||
.FETCH_TYPE(bc.fetch_type)
|
||||
.FETCH_WHOLE_QUAD(bc.fetch_whole_quad)
|
||||
.MEGA_FETCH_COUNT(bc.mega_fetch_count)
|
||||
.SRC_GPR(bc.src_gpr)
|
||||
.SRC_REL(bc.src_rel)
|
||||
.SRC_SEL_X(bc.src_sel[0])
|
||||
.VC_INST(ctx.fetch_opcode(bc.op));
|
||||
|
||||
else
|
||||
bb << VTX_WORD0_CM()
|
||||
.BUFFER_ID(bc.resource_id)
|
||||
.COALESCED_READ(bc.coalesced_read)
|
||||
.FETCH_TYPE(bc.fetch_type)
|
||||
.FETCH_WHOLE_QUAD(bc.fetch_whole_quad)
|
||||
.LDS_REQ(bc.lds_req)
|
||||
.SRC_GPR(bc.src_gpr)
|
||||
.SRC_REL(bc.src_rel)
|
||||
.SRC_SEL_X(bc.src_sel[0])
|
||||
.SRC_SEL_Y(bc.src_sel[1])
|
||||
.STRUCTURED_READ(bc.structured_read)
|
||||
.VC_INST(ctx.fetch_opcode(bc.op));
|
||||
|
||||
if (bc.op == FETCH_OP_SEMFETCH)
|
||||
bb << VTX_WORD1_SEM_ALL()
|
||||
.DATA_FORMAT(bc.data_format)
|
||||
.DST_SEL_X(bc.dst_sel[0])
|
||||
.DST_SEL_Y(bc.dst_sel[1])
|
||||
.DST_SEL_Z(bc.dst_sel[2])
|
||||
.DST_SEL_W(bc.dst_sel[3])
|
||||
.FORMAT_COMP_ALL(bc.format_comp_all)
|
||||
.NUM_FORMAT_ALL(bc.num_format_all)
|
||||
.SEMANTIC_ID(bc.semantic_id)
|
||||
.SRF_MODE_ALL(bc.srf_mode_all)
|
||||
.USE_CONST_FIELDS(bc.use_const_fields);
|
||||
else
|
||||
bb << VTX_WORD1_GPR_ALL()
|
||||
.DATA_FORMAT(bc.data_format)
|
||||
.DST_GPR(bc.dst_gpr)
|
||||
.DST_REL(bc.dst_rel)
|
||||
.DST_SEL_X(bc.dst_sel[0])
|
||||
.DST_SEL_Y(bc.dst_sel[1])
|
||||
.DST_SEL_Z(bc.dst_sel[2])
|
||||
.DST_SEL_W(bc.dst_sel[3])
|
||||
.FORMAT_COMP_ALL(bc.format_comp_all)
|
||||
.NUM_FORMAT_ALL(bc.num_format_all)
|
||||
.SRF_MODE_ALL(bc.srf_mode_all)
|
||||
.USE_CONST_FIELDS(bc.use_const_fields);
|
||||
|
||||
switch (ctx.hw_class) {
|
||||
case HW_CLASS_R600:
|
||||
bb << VTX_WORD2_R6()
|
||||
.CONST_BUF_NO_STRIDE(bc.const_buf_no_stride)
|
||||
.ENDIAN_SWAP(bc.endian_swap)
|
||||
.MEGA_FETCH(bc.mega_fetch)
|
||||
.OFFSET(bc.offset[0]);
|
||||
break;
|
||||
case HW_CLASS_R700:
|
||||
bb << VTX_WORD2_R7()
|
||||
.ALT_CONST(bc.alt_const)
|
||||
.CONST_BUF_NO_STRIDE(bc.const_buf_no_stride)
|
||||
.ENDIAN_SWAP(bc.endian_swap)
|
||||
.MEGA_FETCH(bc.mega_fetch)
|
||||
.OFFSET(bc.offset[0]);
|
||||
break;
|
||||
case HW_CLASS_EVERGREEN:
|
||||
bb << VTX_WORD2_EG()
|
||||
.ALT_CONST(bc.alt_const)
|
||||
.BUFFER_INDEX_MODE(bc.resource_index_mode)
|
||||
.CONST_BUF_NO_STRIDE(bc.const_buf_no_stride)
|
||||
.ENDIAN_SWAP(bc.endian_swap)
|
||||
.MEGA_FETCH(bc.mega_fetch)
|
||||
.OFFSET(bc.offset[0]);
|
||||
break;
|
||||
case HW_CLASS_CAYMAN:
|
||||
bb << VTX_WORD2_CM()
|
||||
.ALT_CONST(bc.alt_const)
|
||||
.BUFFER_INDEX_MODE(bc.resource_index_mode)
|
||||
.CONST_BUF_NO_STRIDE(bc.const_buf_no_stride)
|
||||
.ENDIAN_SWAP(bc.endian_swap)
|
||||
.OFFSET(bc.offset[0]);
|
||||
break;
|
||||
default:
|
||||
assert(!"unknown hw class");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bb << 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include "sstream"
|
||||
|
||||
#include "sb_bc.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
int bc_decoder::decode_cf(unsigned &i, bc_cf& bc) {
|
||||
int r = 0;
|
||||
uint32_t dw0 = dw[i];
|
||||
uint32_t dw1 = dw[i+1];
|
||||
|
||||
if ((dw1 >> 29) & 1) { // CF_ALU
|
||||
return decode_cf_alu(i, bc);
|
||||
} else {
|
||||
// CF_INST field encoding on cayman is the same as on evergreen
|
||||
unsigned opcode = ctx.is_egcm() ?
|
||||
CF_WORD1_EG(dw1).get_CF_INST() :
|
||||
CF_WORD1_R6R7(dw1).get_CF_INST();
|
||||
|
||||
bc.set_op(r600_isa_cf_by_opcode(ctx.isa, opcode, 0));
|
||||
|
||||
if (bc.op_ptr->flags & CF_EXP) {
|
||||
return decode_cf_exp(i, bc);
|
||||
} else if (bc.op_ptr->flags & CF_MEM) {
|
||||
return decode_cf_mem(i, bc);
|
||||
}
|
||||
|
||||
if (ctx.is_egcm()) {
|
||||
CF_WORD0_EGCM w0(dw0);
|
||||
bc.addr = w0.get_ADDR();
|
||||
bc.jumptable_sel = w0.get_JUMPTABLE_SEL();
|
||||
|
||||
if (ctx.is_evergreen()) {
|
||||
CF_WORD1_EG w1(dw1);
|
||||
|
||||
bc.barrier = w1.get_BARRIER();
|
||||
bc.cf_const = w1.get_CF_CONST();
|
||||
bc.cond = w1.get_COND();
|
||||
bc.count = w1.get_COUNT();
|
||||
bc.end_of_program = w1.get_END_OF_PROGRAM();
|
||||
bc.pop_count = w1.get_POP_COUNT();
|
||||
bc.valid_pixel_mode = w1.get_VALID_PIXEL_MODE();
|
||||
bc.whole_quad_mode = w1.get_WHOLE_QUAD_MODE();
|
||||
|
||||
} else { // cayman
|
||||
CF_WORD1_CM w1(dw1);
|
||||
|
||||
bc.barrier = w1.get_BARRIER();
|
||||
bc.cf_const = w1.get_CF_CONST();
|
||||
bc.cond = w1.get_COND();
|
||||
bc.count = w1.get_COUNT();
|
||||
bc.pop_count = w1.get_POP_COUNT();
|
||||
bc.valid_pixel_mode = w1.get_VALID_PIXEL_MODE();
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
CF_WORD0_R6R7 w0(dw0);
|
||||
bc.addr = w0.get_ADDR();
|
||||
|
||||
CF_WORD1_R6R7 w1(dw1);
|
||||
bc.barrier = w1.get_BARRIER();
|
||||
bc.cf_const = w1.get_CF_CONST();
|
||||
bc.cond = w1.get_COND();
|
||||
|
||||
if (ctx.is_r600())
|
||||
bc.count = w1.get_COUNT();
|
||||
else
|
||||
bc.count = w1.get_COUNT() + (w1.get_COUNT_3() << 3);
|
||||
|
||||
bc.end_of_program = w1.get_END_OF_PROGRAM();
|
||||
bc.pop_count = w1.get_POP_COUNT();
|
||||
bc.valid_pixel_mode = w1.get_VALID_PIXEL_MODE();
|
||||
bc.whole_quad_mode = w1.get_WHOLE_QUAD_MODE();
|
||||
bc.call_count = w1.get_CALL_COUNT();
|
||||
}
|
||||
}
|
||||
|
||||
i += 2;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int bc_decoder::decode_cf_alu(unsigned & i, bc_cf& bc) {
|
||||
int r = 0;
|
||||
uint32_t dw0 = dw[i++];
|
||||
uint32_t dw1 = dw[i++];
|
||||
|
||||
assert(i <= ndw);
|
||||
|
||||
CF_ALU_WORD0_ALL w0(dw0);
|
||||
|
||||
bc.kc[0].bank = w0.get_KCACHE_BANK0();
|
||||
bc.kc[1].bank = w0.get_KCACHE_BANK1();
|
||||
bc.kc[0].mode = w0.get_KCACHE_MODE0();
|
||||
|
||||
bc.addr = w0.get_ADDR();
|
||||
|
||||
if (ctx.is_r600()) {
|
||||
CF_ALU_WORD1_R6 w1(dw1);
|
||||
|
||||
bc.set_op(r600_isa_cf_by_opcode(ctx.isa, w1.get_CF_INST(), 1));
|
||||
|
||||
bc.kc[0].addr = w1.get_KCACHE_ADDR0();
|
||||
bc.kc[1].mode = w1.get_KCACHE_MODE1();
|
||||
bc.kc[1].addr = w1.get_KCACHE_ADDR1();
|
||||
|
||||
bc.barrier = w1.get_BARRIER();
|
||||
bc.count = w1.get_COUNT();
|
||||
bc.whole_quad_mode = w1.get_WHOLE_QUAD_MODE();
|
||||
|
||||
bc.uses_waterfall = w1.get_USES_WATERFALL();
|
||||
} else {
|
||||
CF_ALU_WORD1_R7EGCM w1(dw1);
|
||||
|
||||
bc.set_op(r600_isa_cf_by_opcode(ctx.isa, w1.get_CF_INST(), 1));
|
||||
|
||||
if (bc.op == CF_OP_ALU_EXT) {
|
||||
CF_ALU_WORD0_EXT_EGCM w0(dw0);
|
||||
CF_ALU_WORD1_EXT_EGCM w1(dw1);
|
||||
|
||||
bc.kc[0].index_mode = w0.get_KCACHE_BANK_INDEX_MODE0();
|
||||
bc.kc[1].index_mode = w0.get_KCACHE_BANK_INDEX_MODE1();
|
||||
bc.kc[2].index_mode = w0.get_KCACHE_BANK_INDEX_MODE2();
|
||||
bc.kc[3].index_mode = w0.get_KCACHE_BANK_INDEX_MODE3();
|
||||
bc.kc[2].bank = w0.get_KCACHE_BANK2();
|
||||
bc.kc[3].bank = w0.get_KCACHE_BANK3();
|
||||
bc.kc[2].mode = w0.get_KCACHE_MODE2();
|
||||
bc.kc[3].mode = w1.get_KCACHE_MODE3();
|
||||
bc.kc[2].addr = w1.get_KCACHE_ADDR2();
|
||||
bc.kc[3].addr = w1.get_KCACHE_ADDR3();
|
||||
|
||||
r = decode_cf_alu(i, bc);
|
||||
|
||||
} else {
|
||||
|
||||
bc.kc[0].addr = w1.get_KCACHE_ADDR0();
|
||||
bc.kc[1].mode = w1.get_KCACHE_MODE1();
|
||||
bc.kc[1].addr = w1.get_KCACHE_ADDR1();
|
||||
bc.barrier = w1.get_BARRIER();
|
||||
bc.count = w1.get_COUNT();
|
||||
bc.whole_quad_mode = w1.get_WHOLE_QUAD_MODE();
|
||||
|
||||
bc.alt_const = w1.get_ALT_CONST();
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int bc_decoder::decode_cf_exp(unsigned & i, bc_cf& bc) {
|
||||
int r = 0;
|
||||
uint32_t dw0 = dw[i++];
|
||||
uint32_t dw1 = dw[i++];
|
||||
assert(i <= ndw);
|
||||
|
||||
CF_ALLOC_EXPORT_WORD0_ALL w0(dw0);
|
||||
bc.array_base = w0.get_ARRAY_BASE();
|
||||
bc.elem_size = w0.get_ELEM_SIZE();
|
||||
bc.index_gpr = w0.get_INDEX_GPR();
|
||||
bc.rw_gpr = w0.get_RW_GPR();
|
||||
bc.rw_rel = w0.get_RW_REL();
|
||||
bc.type = w0.get_TYPE();
|
||||
|
||||
if (ctx.is_evergreen()) {
|
||||
CF_ALLOC_EXPORT_WORD1_SWIZ_EG w1(dw1);
|
||||
bc.barrier = w1.get_BARRIER();
|
||||
bc.burst_count = w1.get_BURST_COUNT();
|
||||
bc.end_of_program = w1.get_END_OF_PROGRAM();
|
||||
bc.sel[0] = w1.get_SEL_X();
|
||||
bc.sel[1] = w1.get_SEL_Y();
|
||||
bc.sel[2] = w1.get_SEL_Z();
|
||||
bc.sel[3] = w1.get_SEL_W();
|
||||
bc.valid_pixel_mode = w1.get_VALID_PIXEL_MODE();
|
||||
bc.mark = w1.get_MARK();
|
||||
|
||||
} else if (ctx.is_cayman()) {
|
||||
CF_ALLOC_EXPORT_WORD1_SWIZ_CM w1(dw1);
|
||||
bc.barrier = w1.get_BARRIER();
|
||||
bc.burst_count = w1.get_BURST_COUNT();
|
||||
bc.mark = w1.get_MARK();
|
||||
bc.sel[0] = w1.get_SEL_X();
|
||||
bc.sel[1] = w1.get_SEL_Y();
|
||||
bc.sel[2] = w1.get_SEL_Z();
|
||||
bc.sel[3] = w1.get_SEL_W();
|
||||
bc.valid_pixel_mode = w1.get_VALID_PIXEL_MODE();
|
||||
|
||||
} else { // r67
|
||||
CF_ALLOC_EXPORT_WORD1_SWIZ_R6R7 w1(dw1);
|
||||
bc.barrier = w1.get_BARRIER();
|
||||
bc.burst_count = w1.get_BURST_COUNT();
|
||||
bc.end_of_program = w1.get_END_OF_PROGRAM();
|
||||
bc.sel[0] = w1.get_SEL_X();
|
||||
bc.sel[1] = w1.get_SEL_Y();
|
||||
bc.sel[2] = w1.get_SEL_Z();
|
||||
bc.sel[3] = w1.get_SEL_W();
|
||||
bc.valid_pixel_mode = w1.get_VALID_PIXEL_MODE();
|
||||
bc.whole_quad_mode = w1.get_WHOLE_QUAD_MODE();
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
int bc_decoder::decode_cf_mem(unsigned & i, bc_cf& bc) {
|
||||
int r = 0;
|
||||
uint32_t dw0 = dw[i++];
|
||||
uint32_t dw1 = dw[i++];
|
||||
assert(i <= ndw);
|
||||
|
||||
if (!(bc.op_ptr->flags & CF_RAT)) {
|
||||
CF_ALLOC_EXPORT_WORD0_ALL w0(dw0);
|
||||
bc.array_base = w0.get_ARRAY_BASE();
|
||||
bc.elem_size = w0.get_ELEM_SIZE();
|
||||
bc.index_gpr = w0.get_INDEX_GPR();
|
||||
bc.rw_gpr = w0.get_RW_GPR();
|
||||
bc.rw_rel = w0.get_RW_REL();
|
||||
bc.type = w0.get_TYPE();
|
||||
} else {
|
||||
assert(ctx.is_egcm());
|
||||
CF_ALLOC_EXPORT_WORD0_RAT_EGCM w0(dw0);
|
||||
bc.elem_size = w0.get_ELEM_SIZE();
|
||||
bc.index_gpr = w0.get_INDEX_GPR();
|
||||
bc.rw_gpr = w0.get_RW_GPR();
|
||||
bc.rw_rel = w0.get_RW_REL();
|
||||
bc.type = w0.get_TYPE();
|
||||
bc.rat_id = w0.get_RAT_ID();
|
||||
bc.rat_inst = w0.get_RAT_INST();
|
||||
bc.rat_index_mode = w0.get_RAT_INDEX_MODE();
|
||||
}
|
||||
|
||||
if (ctx.is_evergreen()) {
|
||||
CF_ALLOC_EXPORT_WORD1_BUF_EG w1(dw1);
|
||||
bc.barrier = w1.get_BARRIER();
|
||||
bc.burst_count = w1.get_BURST_COUNT();
|
||||
bc.end_of_program = w1.get_END_OF_PROGRAM();
|
||||
bc.valid_pixel_mode = w1.get_VALID_PIXEL_MODE();
|
||||
bc.mark = w1.get_MARK();
|
||||
bc.array_size = w1.get_ARRAY_SIZE();
|
||||
bc.comp_mask = w1.get_COMP_MASK();
|
||||
|
||||
} else if (ctx.is_cayman()) {
|
||||
CF_ALLOC_EXPORT_WORD1_BUF_CM w1(dw1);
|
||||
bc.barrier = w1.get_BARRIER();
|
||||
bc.burst_count = w1.get_BURST_COUNT();
|
||||
bc.mark = w1.get_MARK();
|
||||
bc.valid_pixel_mode = w1.get_VALID_PIXEL_MODE();
|
||||
bc.array_size = w1.get_ARRAY_SIZE();
|
||||
bc.comp_mask = w1.get_COMP_MASK();
|
||||
|
||||
} else { // r67
|
||||
CF_ALLOC_EXPORT_WORD1_BUF_R6R7 w1(dw1);
|
||||
bc.barrier = w1.get_BARRIER();
|
||||
bc.burst_count = w1.get_BURST_COUNT();
|
||||
bc.end_of_program = w1.get_END_OF_PROGRAM();
|
||||
bc.valid_pixel_mode = w1.get_VALID_PIXEL_MODE();
|
||||
bc.whole_quad_mode = w1.get_WHOLE_QUAD_MODE();
|
||||
bc.array_size = w1.get_ARRAY_SIZE();
|
||||
bc.comp_mask = w1.get_COMP_MASK();
|
||||
bc.whole_quad_mode = w1.get_WHOLE_QUAD_MODE();
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int bc_decoder::decode_alu(unsigned & i, bc_alu& bc) {
|
||||
int r = 0;
|
||||
uint32_t dw0 = dw[i++];
|
||||
uint32_t dw1 = dw[i++];
|
||||
assert(i <= ndw);
|
||||
|
||||
ALU_WORD0_ALL w0(dw0);
|
||||
bc.index_mode = w0.get_INDEX_MODE();
|
||||
bc.last = w0.get_LAST();
|
||||
bc.pred_sel = w0.get_PRED_SEL();
|
||||
bc.src[0].chan = w0.get_SRC0_CHAN();
|
||||
bc.src[0].sel = w0.get_SRC0_SEL();
|
||||
bc.src[0].neg = w0.get_SRC0_NEG();
|
||||
bc.src[0].rel = w0.get_SRC0_REL();
|
||||
bc.src[1].chan = w0.get_SRC1_CHAN();
|
||||
bc.src[1].sel = w0.get_SRC1_SEL();
|
||||
bc.src[1].neg = w0.get_SRC1_NEG();
|
||||
bc.src[1].rel = w0.get_SRC1_REL();
|
||||
|
||||
if ((dw1 >> 15) & 7) { // op3
|
||||
ALU_WORD1_OP3_ALL w1(dw1);
|
||||
bc.set_op(r600_isa_alu_by_opcode(ctx.isa, w1.get_ALU_INST(), 1));
|
||||
|
||||
bc.bank_swizzle = w1.get_BANK_SWIZZLE();
|
||||
bc.clamp = w1.get_CLAMP();
|
||||
bc.dst_chan = w1.get_DST_CHAN();
|
||||
bc.dst_gpr = w1.get_DST_GPR();
|
||||
bc.dst_rel = w1.get_DST_REL();
|
||||
|
||||
bc.src[2].chan = w1.get_SRC2_CHAN();
|
||||
bc.src[2].sel = w1.get_SRC2_SEL();
|
||||
bc.src[2].neg = w1.get_SRC2_NEG();
|
||||
bc.src[2].rel = w1.get_SRC2_REL();
|
||||
|
||||
} else { // op2
|
||||
if (ctx.is_r600()) {
|
||||
ALU_WORD1_OP2_R6 w1(dw1);
|
||||
bc.set_op(r600_isa_alu_by_opcode(ctx.isa, w1.get_ALU_INST(), 0));
|
||||
|
||||
bc.bank_swizzle = w1.get_BANK_SWIZZLE();
|
||||
bc.clamp = w1.get_CLAMP();
|
||||
bc.dst_chan = w1.get_DST_CHAN();
|
||||
bc.dst_gpr = w1.get_DST_GPR();
|
||||
bc.dst_rel = w1.get_DST_REL();
|
||||
|
||||
bc.omod = w1.get_OMOD();
|
||||
bc.src[0].abs = w1.get_SRC0_ABS();
|
||||
bc.src[1].abs = w1.get_SRC1_ABS();
|
||||
bc.write_mask = w1.get_WRITE_MASK();
|
||||
bc.update_exec_mask = w1.get_UPDATE_EXEC_MASK();
|
||||
bc.update_pred = w1.get_UPDATE_PRED();
|
||||
|
||||
bc.fog_merge = w1.get_FOG_MERGE();
|
||||
|
||||
} else {
|
||||
ALU_WORD1_OP2_R7EGCM w1(dw1);
|
||||
bc.set_op(r600_isa_alu_by_opcode(ctx.isa, w1.get_ALU_INST(), 0));
|
||||
|
||||
bc.bank_swizzle = w1.get_BANK_SWIZZLE();
|
||||
bc.clamp = w1.get_CLAMP();
|
||||
bc.dst_chan = w1.get_DST_CHAN();
|
||||
bc.dst_gpr = w1.get_DST_GPR();
|
||||
bc.dst_rel = w1.get_DST_REL();
|
||||
|
||||
bc.omod = w1.get_OMOD();
|
||||
bc.src[0].abs = w1.get_SRC0_ABS();
|
||||
bc.src[1].abs = w1.get_SRC1_ABS();
|
||||
bc.write_mask = w1.get_WRITE_MASK();
|
||||
bc.update_exec_mask = w1.get_UPDATE_EXEC_MASK();
|
||||
bc.update_pred = w1.get_UPDATE_PRED();
|
||||
}
|
||||
}
|
||||
|
||||
bc.slot_flags = (alu_op_flags)bc.op_ptr->slots[ctx.isa->hw_class];
|
||||
return r;
|
||||
}
|
||||
|
||||
int bc_decoder::decode_fetch(unsigned & i, bc_fetch& bc) {
|
||||
int r = 0;
|
||||
uint32_t dw0 = dw[i];
|
||||
uint32_t dw1 = dw[i+1];
|
||||
uint32_t dw2 = dw[i+2];
|
||||
assert(i + 4 <= ndw);
|
||||
|
||||
unsigned fetch_opcode = dw0 & 0x1F;
|
||||
|
||||
bc.set_op(r600_isa_fetch_by_opcode(ctx.isa, fetch_opcode));
|
||||
|
||||
if (bc.op_ptr->flags & FF_VTX)
|
||||
return decode_fetch_vtx(i, bc);
|
||||
|
||||
// tex
|
||||
|
||||
if (ctx.is_r600()) {
|
||||
TEX_WORD0_R6 w0(dw0);
|
||||
|
||||
bc.bc_frac_mode = w0.get_BC_FRAC_MODE();
|
||||
bc.fetch_whole_quad = w0.get_FETCH_WHOLE_QUAD();
|
||||
bc.resource_id = w0.get_RESOURCE_ID();
|
||||
bc.src_gpr = w0.get_SRC_GPR();
|
||||
bc.src_rel = w0.get_SRC_REL();
|
||||
|
||||
} else if (ctx.is_r600()) {
|
||||
TEX_WORD0_R7 w0(dw0);
|
||||
|
||||
bc.bc_frac_mode = w0.get_BC_FRAC_MODE();
|
||||
bc.fetch_whole_quad = w0.get_FETCH_WHOLE_QUAD();
|
||||
bc.resource_id = w0.get_RESOURCE_ID();
|
||||
bc.src_gpr = w0.get_SRC_GPR();
|
||||
bc.src_rel = w0.get_SRC_REL();
|
||||
bc.alt_const = w0.get_ALT_CONST();
|
||||
|
||||
} else { // eg/cm
|
||||
TEX_WORD0_EGCM w0(dw0);
|
||||
|
||||
bc.fetch_whole_quad = w0.get_FETCH_WHOLE_QUAD();
|
||||
bc.resource_id = w0.get_RESOURCE_ID();
|
||||
bc.src_gpr = w0.get_SRC_GPR();
|
||||
bc.src_rel = w0.get_SRC_REL();
|
||||
bc.alt_const = w0.get_ALT_CONST();
|
||||
bc.inst_mod = w0.get_INST_MOD();
|
||||
bc.resource_index_mode = w0.get_RESOURCE_INDEX_MODE();
|
||||
bc.sampler_index_mode = w0.get_SAMPLER_INDEX_MODE();
|
||||
}
|
||||
|
||||
TEX_WORD1_ALL w1(dw1);
|
||||
bc.coord_type[0] = w1.get_COORD_TYPE_X();
|
||||
bc.coord_type[1] = w1.get_COORD_TYPE_Y();
|
||||
bc.coord_type[2] = w1.get_COORD_TYPE_Z();
|
||||
bc.coord_type[3] = w1.get_COORD_TYPE_W();
|
||||
bc.dst_gpr = w1.get_DST_GPR();
|
||||
bc.dst_rel = w1.get_DST_REL();
|
||||
bc.dst_sel[0] = w1.get_DST_SEL_X();
|
||||
bc.dst_sel[1] = w1.get_DST_SEL_Y();
|
||||
bc.dst_sel[2] = w1.get_DST_SEL_Z();
|
||||
bc.dst_sel[3] = w1.get_DST_SEL_W();
|
||||
bc.lod_bias = w1.get_LOD_BIAS();
|
||||
|
||||
TEX_WORD2_ALL w2(dw2);
|
||||
bc.offset[0] = w2.get_OFFSET_X();
|
||||
bc.offset[1] = w2.get_OFFSET_Y();
|
||||
bc.offset[2] = w2.get_OFFSET_Z();
|
||||
bc.sampler_id = w2.get_SAMPLER_ID();
|
||||
bc.src_sel[0] = w2.get_SRC_SEL_X();
|
||||
bc.src_sel[1] = w2.get_SRC_SEL_Y();
|
||||
bc.src_sel[2] = w2.get_SRC_SEL_Z();
|
||||
bc.src_sel[3] = w2.get_SRC_SEL_W();
|
||||
|
||||
i += 4;
|
||||
return r;
|
||||
}
|
||||
|
||||
int bc_decoder::decode_fetch_vtx(unsigned & i, bc_fetch& bc) {
|
||||
int r = 0;
|
||||
uint32_t dw0 = dw[i];
|
||||
uint32_t dw1 = dw[i+1];
|
||||
uint32_t dw2 = dw[i+2];
|
||||
i+= 4;
|
||||
assert(i <= ndw);
|
||||
|
||||
if (ctx.is_cayman()) {
|
||||
VTX_WORD0_CM w0(dw0);
|
||||
bc.resource_id = w0.get_BUFFER_ID();
|
||||
bc.fetch_type = w0.get_FETCH_TYPE();
|
||||
bc.fetch_whole_quad = w0.get_FETCH_WHOLE_QUAD();
|
||||
bc.src_gpr = w0.get_SRC_GPR();
|
||||
bc.src_rel = w0.get_SRC_REL();
|
||||
bc.src_sel[0] = w0.get_SRC_SEL_X();
|
||||
bc.coalesced_read = w0.get_COALESCED_READ();
|
||||
bc.lds_req = w0.get_LDS_REQ();
|
||||
bc.structured_read = w0.get_STRUCTURED_READ();
|
||||
|
||||
} else {
|
||||
VTX_WORD0_R6R7EG w0(dw0);
|
||||
bc.resource_id = w0.get_BUFFER_ID();
|
||||
bc.fetch_type = w0.get_FETCH_TYPE();
|
||||
bc.fetch_whole_quad = w0.get_FETCH_WHOLE_QUAD();
|
||||
bc.mega_fetch_count = w0.get_MEGA_FETCH_COUNT();
|
||||
bc.src_gpr = w0.get_SRC_GPR();
|
||||
bc.src_rel = w0.get_SRC_REL();
|
||||
bc.src_sel[0] = w0.get_SRC_SEL_X();
|
||||
}
|
||||
|
||||
if (bc.op == FETCH_OP_SEMFETCH) {
|
||||
VTX_WORD1_SEM_ALL w1(dw1);
|
||||
bc.data_format = w1.get_DATA_FORMAT();
|
||||
bc.dst_sel[0] = w1.get_DST_SEL_X();
|
||||
bc.dst_sel[1] = w1.get_DST_SEL_Y();
|
||||
bc.dst_sel[2] = w1.get_DST_SEL_Z();
|
||||
bc.dst_sel[3] = w1.get_DST_SEL_W();
|
||||
bc.format_comp_all = w1.get_FORMAT_COMP_ALL();
|
||||
bc.num_format_all = w1.get_NUM_FORMAT_ALL();
|
||||
bc.srf_mode_all = w1.get_SRF_MODE_ALL();
|
||||
bc.use_const_fields = w1.get_USE_CONST_FIELDS();
|
||||
|
||||
bc.semantic_id = w1.get_SEMANTIC_ID();
|
||||
|
||||
} else {
|
||||
VTX_WORD1_GPR_ALL w1(dw1);
|
||||
bc.data_format = w1.get_DATA_FORMAT();
|
||||
bc.dst_sel[0] = w1.get_DST_SEL_X();
|
||||
bc.dst_sel[1] = w1.get_DST_SEL_Y();
|
||||
bc.dst_sel[2] = w1.get_DST_SEL_Z();
|
||||
bc.dst_sel[3] = w1.get_DST_SEL_W();
|
||||
bc.format_comp_all = w1.get_FORMAT_COMP_ALL();
|
||||
bc.num_format_all = w1.get_NUM_FORMAT_ALL();
|
||||
bc.srf_mode_all = w1.get_SRF_MODE_ALL();
|
||||
bc.use_const_fields = w1.get_USE_CONST_FIELDS();
|
||||
|
||||
bc.dst_gpr = w1.get_DST_GPR();
|
||||
bc.dst_rel = w1.get_DST_REL();
|
||||
}
|
||||
|
||||
switch (ctx.hw_class) {
|
||||
case HW_CLASS_R600:
|
||||
{
|
||||
VTX_WORD2_R6 w2(dw2);
|
||||
bc.const_buf_no_stride = w2.get_CONST_BUF_NO_STRIDE();
|
||||
bc.endian_swap = w2.get_ENDIAN_SWAP();
|
||||
bc.mega_fetch = w2.get_MEGA_FETCH();
|
||||
bc.offset[0] = w2.get_OFFSET();
|
||||
break;
|
||||
}
|
||||
case HW_CLASS_R700:
|
||||
{
|
||||
VTX_WORD2_R7 w2(dw2);
|
||||
bc.const_buf_no_stride = w2.get_CONST_BUF_NO_STRIDE();
|
||||
bc.endian_swap = w2.get_ENDIAN_SWAP();
|
||||
bc.mega_fetch = w2.get_MEGA_FETCH();
|
||||
bc.offset[0] = w2.get_OFFSET();
|
||||
bc.alt_const = w2.get_ALT_CONST();
|
||||
break;
|
||||
}
|
||||
case HW_CLASS_EVERGREEN:
|
||||
{
|
||||
VTX_WORD2_EG w2(dw2);
|
||||
bc.const_buf_no_stride = w2.get_CONST_BUF_NO_STRIDE();
|
||||
bc.endian_swap = w2.get_ENDIAN_SWAP();
|
||||
bc.mega_fetch = w2.get_MEGA_FETCH();
|
||||
bc.offset[0] = w2.get_OFFSET();
|
||||
bc.alt_const = w2.get_ALT_CONST();
|
||||
bc.resource_index_mode = w2.get_BUFFER_INDEX_MODE();
|
||||
break;
|
||||
}
|
||||
case HW_CLASS_CAYMAN:
|
||||
{
|
||||
VTX_WORD2_CM w2(dw2);
|
||||
bc.const_buf_no_stride = w2.get_CONST_BUF_NO_STRIDE();
|
||||
bc.endian_swap = w2.get_ENDIAN_SWAP();
|
||||
bc.offset[0] = w2.get_OFFSET();
|
||||
bc.alt_const = w2.get_ALT_CONST();
|
||||
bc.resource_index_mode = w2.get_BUFFER_INDEX_MODE();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(!"unknown hw class");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "sb_bc.h"
|
||||
#include "sb_shader.h"
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
static const char* chans = "xyzw01?_";
|
||||
|
||||
static const char* vec_bs[] = {
|
||||
"VEC_012", "VEC_021", "VEC_120", "VEC_102", "VEC_201", "VEC_210"
|
||||
};
|
||||
|
||||
static const char* scl_bs[] = {
|
||||
"SCL_210", "SCL_122", "SCL_212", "SCL_221"
|
||||
};
|
||||
|
||||
|
||||
bool bc_dump::visit(cf_node& n, bool enter) {
|
||||
if (enter) {
|
||||
|
||||
id = n.bc.id << 1;
|
||||
|
||||
if ((n.bc.op_ptr->flags & CF_ALU) && n.bc.is_alu_extended()) {
|
||||
dump_dw(id, 2);
|
||||
id += 2;
|
||||
o << "\n";
|
||||
}
|
||||
|
||||
dump_dw(id, 2);
|
||||
dump(n);
|
||||
|
||||
if (n.bc.op_ptr->flags & CF_CLAUSE) {
|
||||
id = n.bc.addr << 1;
|
||||
new_group = 1;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bc_dump::visit(alu_node& n, bool enter) {
|
||||
if (enter) {
|
||||
o << " ";
|
||||
dump_dw(id, 2);
|
||||
|
||||
if (new_group)
|
||||
o << std::setw(5) << ++group_index << " ";
|
||||
else
|
||||
o << " ";
|
||||
|
||||
dump(n);
|
||||
id += 2;
|
||||
|
||||
new_group = n.bc.last;
|
||||
} else {
|
||||
if (n.bc.last) {
|
||||
alu_group_node *g =
|
||||
static_cast<alu_group_node*>(n.get_alu_group_node());
|
||||
assert(g);
|
||||
for (unsigned k = 0; k < g->literals.size(); ++k) {
|
||||
o << " ";
|
||||
dump_dw(id, 1);
|
||||
id += 1;
|
||||
o << "\n";
|
||||
}
|
||||
|
||||
id = (id + 1) & ~1u;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bc_dump::visit(fetch_node& n, bool enter) {
|
||||
if (enter) {
|
||||
o << " ";
|
||||
dump_dw(id, 3);
|
||||
dump(n);
|
||||
id += 4;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void fill_to(std::ostringstream &s, int pos) {
|
||||
int l = s.str().length();
|
||||
if (l < pos)
|
||||
s << std::string(pos-l, ' ');
|
||||
}
|
||||
|
||||
void bc_dump::dump(cf_node& n) {
|
||||
std::ostringstream s;
|
||||
s << n.bc.op_ptr->name;
|
||||
|
||||
if (n.bc.op_ptr->flags & CF_EXP) {
|
||||
static const char *exp_type[] = {"PIXEL", "POS ", "PARAM"};
|
||||
|
||||
fill_to(s, 18);
|
||||
s << " " << exp_type[n.bc.type];
|
||||
|
||||
if (n.bc.burst_count) {
|
||||
std::ostringstream s2;
|
||||
s2 << n.bc.array_base << "-" << n.bc.array_base + n.bc.burst_count;
|
||||
s << " " << std::setw(5) << std::left << s2.str();
|
||||
s << "R" << n.bc.rw_gpr << "-" <<
|
||||
n.bc.rw_gpr + n.bc.burst_count << ".";
|
||||
} else {
|
||||
s << " " << std::setw(5) << std::left << n.bc.array_base;
|
||||
s << "R" << n.bc.rw_gpr << ".";
|
||||
}
|
||||
|
||||
for (int k = 0; k < 4; ++k)
|
||||
s << chans[n.bc.sel[k]];
|
||||
|
||||
} else if (n.bc.op_ptr->flags & (CF_STRM | CF_RAT)) {
|
||||
static const char *exp_type[] = {"WRITE", "WRITE_IND", "WRITE_ACK",
|
||||
"WRITE_IND_ACK"};
|
||||
fill_to(s, 18);
|
||||
s << " " << exp_type[n.bc.type];
|
||||
s << " " << std::setw(5) << std::left << n.bc.array_base;
|
||||
s << "R" << n.bc.rw_gpr << ".";
|
||||
for (int k = 0; k < 4; ++k)
|
||||
s << ((n.bc.comp_mask & (1 << k)) ? chans[k] : '_');
|
||||
|
||||
if ((n.bc.op_ptr->flags & CF_RAT) && (n.bc.type & 1)) {
|
||||
s << ", @R" << n.bc.index_gpr << ".xyz";
|
||||
}
|
||||
|
||||
s << " ES:" << n.bc.elem_size;
|
||||
|
||||
} else {
|
||||
|
||||
if (n.bc.op_ptr->flags & CF_CLAUSE) {
|
||||
s << " " << n.bc.count+1;
|
||||
}
|
||||
|
||||
s << " @" << (n.bc.addr << 1);
|
||||
|
||||
if (n.bc.op_ptr->flags & CF_ALU) {
|
||||
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
bc_kcache &kc = n.bc.kc[k];
|
||||
if (kc.mode) {
|
||||
s << " KC" << k << "[CB" << kc.bank << ":" <<
|
||||
(kc.addr << 4) << "-" <<
|
||||
(((kc.addr + kc.mode) << 4) - 1) << "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n.bc.cond)
|
||||
s << " CND:" << n.bc.pop_count;
|
||||
|
||||
if (n.bc.pop_count)
|
||||
s << " POP:" << n.bc.pop_count;
|
||||
}
|
||||
|
||||
if (!n.bc.barrier)
|
||||
s << " NO_BARRIER";
|
||||
|
||||
if (n.bc.valid_pixel_mode)
|
||||
s << " VPM";
|
||||
|
||||
if (n.bc.whole_quad_mode)
|
||||
s << " WQM";
|
||||
|
||||
if (n.bc.end_of_program)
|
||||
s << " EOP";
|
||||
|
||||
o << s.str() << "\n";
|
||||
}
|
||||
|
||||
|
||||
static void print_sel(std::ostream &s, int sel, int rel, int index_mode,
|
||||
int need_brackets) {
|
||||
if (rel && index_mode >= 5 && sel < 128)
|
||||
s << "G";
|
||||
if (rel || need_brackets) {
|
||||
s << "[";
|
||||
}
|
||||
s << sel;
|
||||
if (rel) {
|
||||
if (index_mode == 0 || index_mode == 6)
|
||||
s << "+AR";
|
||||
else if (index_mode == 4)
|
||||
s << "+AL";
|
||||
}
|
||||
if (rel || need_brackets) {
|
||||
s << "]";
|
||||
}
|
||||
}
|
||||
|
||||
static void print_dst(std::ostream &s, bc_alu &alu)
|
||||
{
|
||||
unsigned sel = alu.dst_gpr;
|
||||
char reg_char = 'R';
|
||||
if (sel >= 128 - 4) { // clause temporary gpr
|
||||
sel -= 128 - 4;
|
||||
reg_char = 'T';
|
||||
}
|
||||
|
||||
if (alu.write_mask || alu.op_ptr->src_count == 3) {
|
||||
s << reg_char;
|
||||
print_sel(s, sel, alu.dst_rel, alu.index_mode, 0);
|
||||
} else {
|
||||
s << "__";
|
||||
}
|
||||
s << ".";
|
||||
s << chans[alu.dst_chan];
|
||||
}
|
||||
|
||||
static void print_src(std::ostream &s, bc_alu &alu, unsigned idx)
|
||||
{
|
||||
bc_alu_src *src = &alu.src[idx];
|
||||
unsigned sel = src->sel, need_sel = 1, need_chan = 1, need_brackets = 0;
|
||||
|
||||
if (src->neg)
|
||||
s <<"-";
|
||||
if (src->abs)
|
||||
s <<"|";
|
||||
|
||||
if (sel < 128 - 4) {
|
||||
s << "R";
|
||||
} else if (sel < 128) {
|
||||
s << "T";
|
||||
sel -= 128 - 4;
|
||||
} else if (sel < 160) {
|
||||
s << "KC0";
|
||||
need_brackets = 1;
|
||||
sel -= 128;
|
||||
} else if (sel < 192) {
|
||||
s << "KC1";
|
||||
need_brackets = 1;
|
||||
sel -= 160;
|
||||
} else if (sel >= 448) {
|
||||
s << "Param";
|
||||
sel -= 448;
|
||||
} else if (sel >= 288) {
|
||||
s << "KC3";
|
||||
need_brackets = 1;
|
||||
sel -= 288;
|
||||
} else if (sel >= 256) {
|
||||
s << "KC2";
|
||||
need_brackets = 1;
|
||||
sel -= 256;
|
||||
} else {
|
||||
need_sel = 0;
|
||||
need_chan = 0;
|
||||
switch (sel) {
|
||||
case ALU_SRC_PS:
|
||||
s << "PS";
|
||||
break;
|
||||
case ALU_SRC_PV:
|
||||
s << "PV";
|
||||
need_chan = 1;
|
||||
break;
|
||||
case ALU_SRC_LITERAL:
|
||||
s << "[" << std::hex << std::setfill('0') << std::setw(8)
|
||||
<< std::showbase << src->value.u << " "
|
||||
<< std::noshowbase << std::setfill(' ') << std::dec
|
||||
<< src->value.f << "]";
|
||||
need_chan = 1;
|
||||
break;
|
||||
case ALU_SRC_0_5:
|
||||
s << "0.5";
|
||||
break;
|
||||
case ALU_SRC_M_1_INT:
|
||||
s << "-1";
|
||||
break;
|
||||
case ALU_SRC_1_INT:
|
||||
s << "1";
|
||||
break;
|
||||
case ALU_SRC_1:
|
||||
s << "1.0";
|
||||
break;
|
||||
case ALU_SRC_0:
|
||||
s << "0";
|
||||
break;
|
||||
default:
|
||||
s << "??IMM_" << sel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_sel)
|
||||
print_sel(s, sel, src->rel, alu.index_mode, need_brackets);
|
||||
|
||||
if (need_chan) {
|
||||
s << "." << chans[src->chan];
|
||||
}
|
||||
|
||||
if (src->abs)
|
||||
s << "|";
|
||||
}
|
||||
void bc_dump::dump(alu_node& n) {
|
||||
std::ostringstream s;
|
||||
static const char *omod_str[] = {"","*2","*4","/2"};
|
||||
static const char *slots = "xyzwt";
|
||||
|
||||
s << (n.bc.update_exec_mask ? 'M' : ' ');
|
||||
s << (n.bc.update_pred ? 'P' : ' ');
|
||||
s << " ";
|
||||
s << (n.bc.pred_sel>=2 ? (n.bc.pred_sel == 2 ? '0' : '1') : ' ');
|
||||
s << " ";
|
||||
|
||||
s << slots[n.bc.slot] << ": ";
|
||||
|
||||
s << n.bc.op_ptr->name << omod_str[n.bc.omod] << (n.bc.clamp ? "_sat" : "");
|
||||
fill_to(s, 26);
|
||||
s << " ";
|
||||
|
||||
print_dst(s, n.bc);
|
||||
for (int k = 0; k < n.bc.op_ptr->src_count; ++k) {
|
||||
s << (k ? ", " : ", ");
|
||||
print_src(s, n.bc, k);
|
||||
}
|
||||
|
||||
if (n.bc.bank_swizzle) {
|
||||
fill_to(s, 55);
|
||||
if (n.bc.slot == SLOT_TRANS)
|
||||
s << " " << scl_bs[n.bc.bank_swizzle];
|
||||
else
|
||||
s << " " << vec_bs[n.bc.bank_swizzle];
|
||||
}
|
||||
|
||||
o << s.str() << "\n";
|
||||
}
|
||||
|
||||
int bc_dump::init() {
|
||||
std::ostringstream s;
|
||||
s << "===== SHADER #" << sh.id;
|
||||
|
||||
if (sh.optimized)
|
||||
s << " OPT";
|
||||
|
||||
s << " ";
|
||||
|
||||
std::string target = std::string(" ") +
|
||||
sh.get_full_target_name() + " =====";
|
||||
|
||||
while (s.str().length() + target.length() < 80)
|
||||
s << "=";
|
||||
|
||||
s << target;
|
||||
|
||||
o << "\n" << s.str() << "\n";
|
||||
|
||||
s.str(std::string());
|
||||
|
||||
if (bc_data) {
|
||||
s << "===== " << ndw << " dw ===== " << sh.ngpr
|
||||
<< " gprs ===== " << sh.nstack << " stack ";
|
||||
}
|
||||
|
||||
while (s.str().length() < 80)
|
||||
s << "=";
|
||||
|
||||
o << s.str() << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_dump::done() {
|
||||
std::ostringstream s;
|
||||
s << "===== SHADER_END ";
|
||||
|
||||
while (s.str().length() < 80)
|
||||
s << "=";
|
||||
|
||||
o << s.str() << "\n\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bc_dump::bc_dump(shader& s, std::ostream& o, bytecode* bc) :
|
||||
vpass(s), o(o), bc_data(), ndw(), id(),
|
||||
new_group(), group_index() {
|
||||
|
||||
if (bc) {
|
||||
bc_data = bc->data();
|
||||
ndw = bc->ndw();
|
||||
}
|
||||
}
|
||||
|
||||
void bc_dump::dump(fetch_node& n) {
|
||||
std::ostringstream s;
|
||||
static const char * fetch_type[] = {"VERTEX", "INSTANCE", ""};
|
||||
|
||||
s << n.bc.op_ptr->name;
|
||||
fill_to(s, 20);
|
||||
|
||||
s << "R";
|
||||
print_sel(s, n.bc.dst_gpr, n.bc.dst_rel, INDEX_LOOP, 0);
|
||||
s << ".";
|
||||
for (int k = 0; k < 4; ++k)
|
||||
s << chans[n.bc.dst_sel[k]];
|
||||
s << ", ";
|
||||
|
||||
s << "R";
|
||||
print_sel(s, n.bc.src_gpr, n.bc.src_rel, INDEX_LOOP, 0);
|
||||
s << ".";
|
||||
|
||||
unsigned vtx = n.bc.op_ptr->flags & FF_VTX;
|
||||
unsigned num_src_comp = vtx ? ctx.is_cayman() ? 2 : 1 : 4;
|
||||
|
||||
for (unsigned k = 0; k < num_src_comp; ++k)
|
||||
s << chans[n.bc.src_sel[k]];
|
||||
|
||||
if (vtx && n.bc.offset[0]) {
|
||||
s << " + " << n.bc.offset[0] << "b ";
|
||||
}
|
||||
|
||||
s << ", RID:" << n.bc.resource_id;
|
||||
|
||||
if (vtx) {
|
||||
s << " " << fetch_type[n.bc.fetch_type];
|
||||
if (!ctx.is_cayman() && n.bc.mega_fetch_count)
|
||||
s << " MFC:" << n.bc.mega_fetch_count;
|
||||
if (n.bc.fetch_whole_quad)
|
||||
s << " FWQ";
|
||||
s << " UCF:" << n.bc.use_const_fields
|
||||
<< " FMT(DTA:" << n.bc.data_format
|
||||
<< " NUM:" << n.bc.num_format_all
|
||||
<< " COMP:" << n.bc.format_comp_all
|
||||
<< " MODE:" << n.bc.srf_mode_all << ")";
|
||||
} else {
|
||||
s << ", SID:" << n.bc.sampler_id;
|
||||
if (n.bc.lod_bias)
|
||||
s << " LB:" << n.bc.lod_bias;
|
||||
s << " CT:";
|
||||
for (unsigned k = 0; k < 4; ++k)
|
||||
s << (n.bc.coord_type[k] ? "N" : "U");
|
||||
for (unsigned k = 0; k < 3; ++k)
|
||||
if (n.bc.offset[k])
|
||||
s << " O" << chans[k] << ":" << n.bc.offset[k];
|
||||
}
|
||||
|
||||
o << s.str() << "\n";
|
||||
}
|
||||
|
||||
void bc_dump::dump_dw(unsigned dw_id, unsigned count) {
|
||||
if (!bc_data)
|
||||
return;
|
||||
|
||||
assert(dw_id + count <= ndw);
|
||||
|
||||
o << std::setfill('0') << std::setw(4) << dw_id << " ";
|
||||
while (count--) {
|
||||
o << std::setw(8) << std::hex << bc_data[dw_id++] << " ";
|
||||
}
|
||||
o << std::setfill(' ') << std::dec;
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,833 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#define FBC_DEBUG 0
|
||||
|
||||
#if FBC_DEBUG
|
||||
#define FBC_DUMP(q) do { q } while (0)
|
||||
#else
|
||||
#define FBC_DUMP(q)
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "sb_bc.h"
|
||||
|
||||
#include "sb_shader.h"
|
||||
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
int bc_finalizer::run() {
|
||||
|
||||
regions_vec &rv = sh.get_regions();
|
||||
|
||||
for (regions_vec::reverse_iterator I = rv.rbegin(), E = rv.rend(); I != E;
|
||||
++I) {
|
||||
region_node *r = *I;
|
||||
|
||||
assert(r);
|
||||
|
||||
bool loop = r->is_loop();
|
||||
|
||||
if (loop)
|
||||
finalize_loop(r);
|
||||
else
|
||||
finalize_if(r);
|
||||
|
||||
r->expand();
|
||||
}
|
||||
|
||||
run_on(sh.root);
|
||||
|
||||
cf_peephole();
|
||||
|
||||
// workaround for some problems on r6xx/7xx
|
||||
// add ALU NOP to each vertex shader
|
||||
if (!ctx.is_egcm() && sh.target == TARGET_VS) {
|
||||
cf_node *c = sh.create_clause(NST_ALU_CLAUSE);
|
||||
|
||||
alu_group_node *g = sh.create_alu_group();
|
||||
|
||||
alu_node *a = sh.create_alu();
|
||||
a->bc.set_op(ALU_OP0_NOP);
|
||||
a->bc.last = 1;
|
||||
|
||||
g->push_back(a);
|
||||
c->push_back(g);
|
||||
|
||||
sh.root->push_back(c);
|
||||
|
||||
c = sh.create_cf(CF_OP_NOP);
|
||||
sh.root->push_back(c);
|
||||
|
||||
last_cf = c;
|
||||
}
|
||||
|
||||
if (last_cf->bc.op_ptr->flags & CF_ALU) {
|
||||
last_cf = sh.create_cf(CF_OP_NOP);
|
||||
sh.root->push_back(last_cf);
|
||||
}
|
||||
|
||||
if (ctx.is_cayman())
|
||||
last_cf->insert_after(sh.create_cf(CF_OP_CF_END));
|
||||
else
|
||||
last_cf->bc.end_of_program = 1;
|
||||
|
||||
for (unsigned t = EXP_PIXEL; t < EXP_TYPE_COUNT; ++t) {
|
||||
cf_node *le = last_export[t];
|
||||
if (le)
|
||||
le->bc.set_op(CF_OP_EXPORT_DONE);
|
||||
}
|
||||
|
||||
sh.ngpr = ngpr;
|
||||
sh.nstack = nstack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bc_finalizer::finalize_loop(region_node* r) {
|
||||
|
||||
cf_node *loop_start = sh.create_cf(CF_OP_LOOP_START_DX10);
|
||||
cf_node *loop_end = sh.create_cf(CF_OP_LOOP_END);
|
||||
|
||||
loop_start->jump_after(loop_end);
|
||||
loop_end->jump_after(loop_start);
|
||||
|
||||
for (depart_vec::iterator I = r->departs.begin(), E = r->departs.end();
|
||||
I != E; ++I) {
|
||||
depart_node *dep = *I;
|
||||
cf_node *loop_break = sh.create_cf(CF_OP_LOOP_BREAK);
|
||||
loop_break->jump(loop_end);
|
||||
dep->push_back(loop_break);
|
||||
dep->expand();
|
||||
}
|
||||
|
||||
// FIXME produces unnecessary LOOP_CONTINUE
|
||||
for (repeat_vec::iterator I = r->repeats.begin(), E = r->repeats.end();
|
||||
I != E; ++I) {
|
||||
repeat_node *rep = *I;
|
||||
if (!(rep->parent == r && rep->prev == NULL)) {
|
||||
cf_node *loop_cont = sh.create_cf(CF_OP_LOOP_CONTINUE);
|
||||
loop_cont->jump(loop_end);
|
||||
rep->push_back(loop_cont);
|
||||
}
|
||||
rep->expand();
|
||||
}
|
||||
|
||||
r->push_front(loop_start);
|
||||
r->push_back(loop_end);
|
||||
}
|
||||
|
||||
void bc_finalizer::finalize_if(region_node* r) {
|
||||
|
||||
update_nstack(r);
|
||||
|
||||
// expecting the following control flow structure here:
|
||||
// - region
|
||||
// {
|
||||
// - depart/repeat 1 (it may be depart/repeat for some outer region)
|
||||
// {
|
||||
// - if
|
||||
// {
|
||||
// - depart/repeat 2 (possibly for outer region)
|
||||
// {
|
||||
// - some optional code
|
||||
// }
|
||||
// }
|
||||
// - optional <else> code> ...
|
||||
// }
|
||||
// }
|
||||
|
||||
container_node *repdep1 = static_cast<container_node*>(r->first);
|
||||
assert(repdep1->is_depart() || repdep1->is_repeat());
|
||||
|
||||
if_node *n_if = static_cast<if_node*>(repdep1->first);
|
||||
|
||||
if (n_if) {
|
||||
|
||||
|
||||
assert(n_if->is_if());
|
||||
|
||||
container_node *repdep2 = static_cast<container_node*>(n_if->first);
|
||||
assert(repdep2->is_depart() || repdep2->is_repeat());
|
||||
|
||||
cf_node *if_jump = sh.create_cf(CF_OP_JUMP);
|
||||
cf_node *if_pop = sh.create_cf(CF_OP_POP);
|
||||
|
||||
if_pop->bc.pop_count = 1;
|
||||
if_pop->jump_after(if_pop);
|
||||
|
||||
r->push_front(if_jump);
|
||||
r->push_back(if_pop);
|
||||
|
||||
bool has_else = n_if->next;
|
||||
|
||||
if (has_else) {
|
||||
cf_node *nelse = sh.create_cf(CF_OP_ELSE);
|
||||
n_if->insert_after(nelse);
|
||||
if_jump->jump(nelse);
|
||||
nelse->jump_after(if_pop);
|
||||
nelse->bc.pop_count = 1;
|
||||
|
||||
} else {
|
||||
if_jump->jump_after(if_pop);
|
||||
if_jump->bc.pop_count = 1;
|
||||
}
|
||||
|
||||
n_if->expand();
|
||||
}
|
||||
|
||||
for (depart_vec::iterator I = r->departs.begin(), E = r->departs.end();
|
||||
I != E; ++I) {
|
||||
(*I)->expand();
|
||||
}
|
||||
r->departs.clear();
|
||||
assert(r->repeats.empty());
|
||||
}
|
||||
|
||||
void bc_finalizer::run_on(container_node* c) {
|
||||
|
||||
for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
|
||||
if (n->is_alu_group()) {
|
||||
finalize_alu_group(static_cast<alu_group_node*>(n));
|
||||
} else {
|
||||
if (n->is_fetch_inst()) {
|
||||
finalize_fetch(static_cast<fetch_node*>(n));
|
||||
} else if (n->is_cf_inst()) {
|
||||
finalize_cf(static_cast<cf_node*>(n));
|
||||
} else if (n->is_alu_clause()) {
|
||||
|
||||
} else if (n->is_fetch_clause()) {
|
||||
|
||||
} else {
|
||||
assert(!"unexpected node");
|
||||
}
|
||||
|
||||
if (n->is_container())
|
||||
run_on(static_cast<container_node*>(n));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bc_finalizer::finalize_alu_group(alu_group_node* g) {
|
||||
|
||||
alu_node *last = NULL;
|
||||
|
||||
for (node_iterator I = g->begin(), E = g->end(); I != E; ++I) {
|
||||
alu_node *n = static_cast<alu_node*>(*I);
|
||||
unsigned slot = n->bc.slot;
|
||||
|
||||
value *d = n->dst.empty() ? NULL : n->dst[0];
|
||||
|
||||
if (d && d->is_special_reg()) {
|
||||
assert(n->bc.op_ptr->flags & AF_MOVA);
|
||||
d = NULL;
|
||||
}
|
||||
|
||||
sel_chan fdst = d ? d->get_final_gpr() : sel_chan(0, 0);
|
||||
|
||||
if (d) {
|
||||
assert(fdst.chan() == slot || slot == SLOT_TRANS);
|
||||
}
|
||||
|
||||
n->bc.dst_gpr = fdst.sel();
|
||||
n->bc.dst_chan = d ? fdst.chan() : slot < SLOT_TRANS ? slot : 0;
|
||||
|
||||
|
||||
if (d && d->is_rel() && d->rel && !d->rel->is_const()) {
|
||||
n->bc.dst_rel = 1;
|
||||
update_ngpr(d->array->gpr.sel() + d->array->array_size -1);
|
||||
} else {
|
||||
n->bc.dst_rel = 0;
|
||||
}
|
||||
|
||||
n->bc.write_mask = d != NULL;
|
||||
n->bc.last = 0;
|
||||
|
||||
if (n->bc.op_ptr->flags & AF_PRED) {
|
||||
n->bc.update_pred = (n->dst[1] != NULL);
|
||||
n->bc.update_exec_mask = (n->dst[2] != NULL);
|
||||
}
|
||||
|
||||
// FIXME handle predication here
|
||||
n->bc.pred_sel = PRED_SEL_OFF;
|
||||
|
||||
update_ngpr(n->bc.dst_gpr);
|
||||
|
||||
finalize_alu_src(g, n);
|
||||
|
||||
last = n;
|
||||
}
|
||||
|
||||
last->bc.last = 1;
|
||||
}
|
||||
|
||||
void bc_finalizer::finalize_alu_src(alu_group_node* g, alu_node* a) {
|
||||
vvec &sv = a->src;
|
||||
|
||||
FBC_DUMP(
|
||||
cerr << "finalize_alu_src: ";
|
||||
dump::dump_op(a);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
unsigned si = 0;
|
||||
|
||||
for (vvec::iterator I = sv.begin(), E = sv.end(); I != E; ++I, ++si) {
|
||||
value *v = *I;
|
||||
assert(v);
|
||||
|
||||
bc_alu_src &src = a->bc.src[si];
|
||||
sel_chan sc;
|
||||
src.rel = 0;
|
||||
|
||||
sel_chan gpr;
|
||||
|
||||
switch (v->kind) {
|
||||
case VLK_REL_REG:
|
||||
sc = v->get_final_gpr();
|
||||
src.sel = sc.sel();
|
||||
src.chan = sc.chan();
|
||||
if (!v->rel->is_const()) {
|
||||
src.rel = 1;
|
||||
update_ngpr(v->array->gpr.sel() + v->array->array_size -1);
|
||||
} else
|
||||
src.rel = 0;
|
||||
|
||||
break;
|
||||
case VLK_REG:
|
||||
gpr = v->get_final_gpr();
|
||||
src.sel = gpr.sel();
|
||||
src.chan = gpr.chan();
|
||||
update_ngpr(src.sel);
|
||||
break;
|
||||
case VLK_TEMP:
|
||||
src.sel = v->gpr.sel();
|
||||
src.chan = v->gpr.chan();
|
||||
update_ngpr(src.sel);
|
||||
break;
|
||||
case VLK_UNDEF:
|
||||
case VLK_CONST: {
|
||||
literal lv = v->literal_value;
|
||||
src.chan = 0;
|
||||
|
||||
if (lv == literal(0))
|
||||
src.sel = ALU_SRC_0;
|
||||
else if (lv == literal(0.5f))
|
||||
src.sel = ALU_SRC_0_5;
|
||||
else if (lv == literal(1.0f))
|
||||
src.sel = ALU_SRC_1;
|
||||
else if (lv == literal(1))
|
||||
src.sel = ALU_SRC_1_INT;
|
||||
else if (lv == literal(-1))
|
||||
src.sel = ALU_SRC_M_1_INT;
|
||||
else {
|
||||
src.sel = ALU_SRC_LITERAL;
|
||||
src.chan = g->literal_chan(lv);
|
||||
src.value = lv;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VLK_KCACHE: {
|
||||
cf_node *clause = static_cast<cf_node*>(g->parent);
|
||||
assert(clause->is_alu_clause());
|
||||
sel_chan k = translate_kcache(clause, v);
|
||||
|
||||
assert(k && "kcache translation failed");
|
||||
|
||||
src.sel = k.sel();
|
||||
src.chan = k.chan();
|
||||
break;
|
||||
}
|
||||
case VLK_PARAM:
|
||||
case VLK_SPECIAL_CONST:
|
||||
src.sel = v->select.sel();
|
||||
src.chan = v->select.chan();
|
||||
break;
|
||||
default:
|
||||
assert(!"unknown value kind");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (si < 3) {
|
||||
a->bc.src[si++].sel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void bc_finalizer::emit_set_grad(fetch_node* f) {
|
||||
|
||||
assert(f->src.size() == 12);
|
||||
unsigned ops[2] = { FETCH_OP_SET_GRADIENTS_V, FETCH_OP_SET_GRADIENTS_H };
|
||||
|
||||
unsigned arg_start = 0;
|
||||
|
||||
for (unsigned op = 0; op < 2; ++op) {
|
||||
fetch_node *n = sh.create_fetch();
|
||||
n->bc.set_op(ops[op]);
|
||||
|
||||
// FIXME extract this loop into a separate method and reuse it
|
||||
|
||||
int reg = -1;
|
||||
|
||||
arg_start += 4;
|
||||
|
||||
for (unsigned chan = 0; chan < 4; ++chan) {
|
||||
|
||||
n->bc.dst_sel[chan] = SEL_MASK;
|
||||
|
||||
unsigned sel = SEL_MASK;
|
||||
|
||||
value *v = f->src[arg_start + chan];
|
||||
|
||||
if (!v || v->is_undef()) {
|
||||
sel = SEL_MASK;
|
||||
} else if (v->is_const()) {
|
||||
literal l = v->literal_value;
|
||||
if (l == literal(0))
|
||||
sel = SEL_0;
|
||||
else if (l == literal(1.0f))
|
||||
sel = SEL_1;
|
||||
else {
|
||||
cerr << "invalid fetch constant operand " << chan << " ";
|
||||
dump::dump_op(f);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
} else if (v->is_any_gpr()) {
|
||||
unsigned vreg = v->gpr.sel();
|
||||
unsigned vchan = v->gpr.chan();
|
||||
|
||||
if (reg == -1)
|
||||
reg = vreg;
|
||||
else if ((unsigned)reg != vreg) {
|
||||
cerr << "invalid fetch source operand " << chan << " ";
|
||||
dump::dump_op(f);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
sel = vchan;
|
||||
|
||||
} else {
|
||||
cerr << "invalid fetch source operand " << chan << " ";
|
||||
dump::dump_op(f);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
n->bc.src_sel[chan] = sel;
|
||||
}
|
||||
|
||||
if (reg >= 0)
|
||||
update_ngpr(reg);
|
||||
|
||||
n->bc.src_gpr = reg >= 0 ? reg : 0;
|
||||
|
||||
f->insert_before(n);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void bc_finalizer::finalize_fetch(fetch_node* f) {
|
||||
|
||||
int reg = -1;
|
||||
|
||||
// src
|
||||
|
||||
unsigned src_count = 4;
|
||||
|
||||
unsigned flags = f->bc.op_ptr->flags;
|
||||
|
||||
if (flags & FF_VTX) {
|
||||
src_count = 1;
|
||||
} else if (flags & FF_USEGRAD) {
|
||||
emit_set_grad(f);
|
||||
}
|
||||
|
||||
for (unsigned chan = 0; chan < src_count; ++chan) {
|
||||
|
||||
unsigned sel = f->bc.src_sel[chan];
|
||||
|
||||
if (sel > SEL_W)
|
||||
continue;
|
||||
|
||||
value *v = f->src[chan];
|
||||
|
||||
if (v->is_undef()) {
|
||||
sel = SEL_MASK;
|
||||
} else if (v->is_const()) {
|
||||
literal l = v->literal_value;
|
||||
if (l == literal(0))
|
||||
sel = SEL_0;
|
||||
else if (l == literal(1.0f))
|
||||
sel = SEL_1;
|
||||
else {
|
||||
cerr << "invalid fetch constant operand " << chan << " ";
|
||||
dump::dump_op(f);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
} else if (v->is_any_gpr()) {
|
||||
unsigned vreg = v->gpr.sel();
|
||||
unsigned vchan = v->gpr.chan();
|
||||
|
||||
if (reg == -1)
|
||||
reg = vreg;
|
||||
else if ((unsigned)reg != vreg) {
|
||||
cerr << "invalid fetch source operand " << chan << " ";
|
||||
dump::dump_op(f);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
sel = vchan;
|
||||
|
||||
} else {
|
||||
cerr << "invalid fetch source operand " << chan << " ";
|
||||
dump::dump_op(f);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
f->bc.src_sel[chan] = sel;
|
||||
}
|
||||
|
||||
if (reg >= 0)
|
||||
update_ngpr(reg);
|
||||
|
||||
f->bc.src_gpr = reg >= 0 ? reg : 0;
|
||||
|
||||
// dst
|
||||
|
||||
reg = -1;
|
||||
|
||||
unsigned dst_swz[4] = {SEL_MASK, SEL_MASK, SEL_MASK, SEL_MASK};
|
||||
|
||||
for (unsigned chan = 0; chan < 4; ++chan) {
|
||||
|
||||
unsigned sel = f->bc.dst_sel[chan];
|
||||
|
||||
if (sel == SEL_MASK)
|
||||
continue;
|
||||
|
||||
value *v = f->dst[chan];
|
||||
if (!v)
|
||||
continue;
|
||||
|
||||
if (v->is_any_gpr()) {
|
||||
unsigned vreg = v->gpr.sel();
|
||||
unsigned vchan = v->gpr.chan();
|
||||
|
||||
if (reg == -1)
|
||||
reg = vreg;
|
||||
else if ((unsigned)reg != vreg) {
|
||||
cerr << "invalid fetch dst operand " << chan << " ";
|
||||
dump::dump_op(f);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
dst_swz[vchan] = sel;
|
||||
|
||||
} else {
|
||||
cerr << "invalid fetch dst operand " << chan << " ";
|
||||
dump::dump_op(f);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 4; ++i)
|
||||
f->bc.dst_sel[i] = dst_swz[i];
|
||||
|
||||
assert(reg >= 0);
|
||||
|
||||
if (reg >= 0)
|
||||
update_ngpr(reg);
|
||||
|
||||
f->bc.dst_gpr = reg >= 0 ? reg : 0;
|
||||
}
|
||||
|
||||
void bc_finalizer::finalize_cf(cf_node* c) {
|
||||
|
||||
unsigned flags = c->bc.op_ptr->flags;
|
||||
|
||||
if (flags & CF_CALL) {
|
||||
update_nstack(c->get_parent_region(), ctx.is_cayman() ? 1 : 2);
|
||||
}
|
||||
|
||||
c->bc.end_of_program = 0;
|
||||
last_cf = c;
|
||||
|
||||
if (flags & CF_EXP) {
|
||||
c->bc.set_op(CF_OP_EXPORT);
|
||||
last_export[c->bc.type] = c;
|
||||
|
||||
int reg = -1;
|
||||
|
||||
for (unsigned chan = 0; chan < 4; ++chan) {
|
||||
|
||||
unsigned sel = c->bc.sel[chan];
|
||||
|
||||
if (sel > SEL_W)
|
||||
continue;
|
||||
|
||||
value *v = c->src[chan];
|
||||
|
||||
if (v->is_undef()) {
|
||||
sel = SEL_MASK;
|
||||
} else if (v->is_const()) {
|
||||
literal l = v->literal_value;
|
||||
if (l == literal(0))
|
||||
sel = SEL_0;
|
||||
else if (l == literal(1.0f))
|
||||
sel = SEL_1;
|
||||
else {
|
||||
cerr << "invalid export constant operand " << chan << " ";
|
||||
dump::dump_op(c);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
} else if (v->is_any_gpr()) {
|
||||
unsigned vreg = v->gpr.sel();
|
||||
unsigned vchan = v->gpr.chan();
|
||||
|
||||
if (reg == -1)
|
||||
reg = vreg;
|
||||
else if ((unsigned)reg != vreg) {
|
||||
cerr << "invalid export source operand " << chan << " ";
|
||||
dump::dump_op(c);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
sel = vchan;
|
||||
|
||||
} else {
|
||||
cerr << "invalid export source operand " << chan << " ";
|
||||
dump::dump_op(c);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
c->bc.sel[chan] = sel;
|
||||
}
|
||||
|
||||
if (reg >= 0)
|
||||
update_ngpr(reg);
|
||||
|
||||
c->bc.rw_gpr = reg >= 0 ? reg : 0;
|
||||
|
||||
} else if (flags & CF_MEM) {
|
||||
|
||||
int reg = -1;
|
||||
unsigned mask = 0;
|
||||
|
||||
for (unsigned chan = 0; chan < 4; ++chan) {
|
||||
value *v = c->src[chan];
|
||||
if (!v || v->is_undef())
|
||||
continue;
|
||||
|
||||
if (!v->is_any_gpr() || v->gpr.chan() != chan) {
|
||||
cerr << "invalid source operand " << chan << " ";
|
||||
dump::dump_op(c);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
unsigned vreg = v->gpr.sel();
|
||||
if (reg == -1)
|
||||
reg = vreg;
|
||||
else if ((unsigned)reg != vreg) {
|
||||
cerr << "invalid source operand " << chan << " ";
|
||||
dump::dump_op(c);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
mask |= (1 << chan);
|
||||
}
|
||||
|
||||
assert(reg >= 0 && mask);
|
||||
|
||||
if (reg >= 0)
|
||||
update_ngpr(reg);
|
||||
|
||||
c->bc.rw_gpr = reg >= 0 ? reg : 0;
|
||||
c->bc.comp_mask = mask;
|
||||
|
||||
if ((flags & CF_RAT) && (c->bc.type & 1)) {
|
||||
|
||||
reg = -1;
|
||||
|
||||
for (unsigned chan = 0; chan < 4; ++chan) {
|
||||
value *v = c->src[4 + chan];
|
||||
if (!v || v->is_undef())
|
||||
continue;
|
||||
|
||||
if (!v->is_any_gpr() || v->gpr.chan() != chan) {
|
||||
cerr << "invalid source operand " << chan << " ";
|
||||
dump::dump_op(c);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
unsigned vreg = v->gpr.sel();
|
||||
if (reg == -1)
|
||||
reg = vreg;
|
||||
else if ((unsigned)reg != vreg) {
|
||||
cerr << "invalid source operand " << chan << " ";
|
||||
dump::dump_op(c);
|
||||
cerr << "\n";
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
assert(reg >= 0);
|
||||
|
||||
if (reg >= 0)
|
||||
update_ngpr(reg);
|
||||
|
||||
c->bc.index_gpr = reg >= 0 ? reg : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
#if 0
|
||||
if ((flags & (CF_BRANCH | CF_LOOP)) && !sh.uses_gradients) {
|
||||
c->bc.valid_pixel_mode = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
sel_chan bc_finalizer::translate_kcache(cf_node* alu, value* v) {
|
||||
unsigned sel = v->select.sel();
|
||||
unsigned bank = sel >> 12;
|
||||
unsigned chan = v->select.chan();
|
||||
static const unsigned kc_base[] = {128, 160, 256, 288};
|
||||
|
||||
sel &= 4095;
|
||||
|
||||
unsigned line = sel >> 4;
|
||||
|
||||
for (unsigned k = 0; k < 4; ++k) {
|
||||
bc_kcache &kc = alu->bc.kc[k];
|
||||
|
||||
if (kc.mode == KC_LOCK_NONE)
|
||||
break;
|
||||
|
||||
if (kc.bank == bank && (kc.addr == line ||
|
||||
(kc.mode == KC_LOCK_2 && kc.addr + 1 == line))) {
|
||||
|
||||
sel = kc_base[k] + (sel - (kc.addr << 4));
|
||||
|
||||
return sel_chan(sel, chan);
|
||||
}
|
||||
}
|
||||
|
||||
assert(!"kcache translation error");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bc_finalizer::update_ngpr(unsigned gpr) {
|
||||
if (gpr < MAX_GPR - ctx.alu_temp_gprs && gpr >= ngpr)
|
||||
ngpr = gpr + 1;
|
||||
}
|
||||
|
||||
void bc_finalizer::update_nstack(region_node* r, unsigned add) {
|
||||
unsigned loops = 0;
|
||||
unsigned ifs = 0;
|
||||
|
||||
while (r) {
|
||||
if (r->is_loop())
|
||||
++loops;
|
||||
else
|
||||
++ifs;
|
||||
|
||||
r = r->get_parent_region();
|
||||
}
|
||||
|
||||
unsigned stack_elements = (loops * ctx.stack_entry_size) + ifs + add;
|
||||
|
||||
// FIXME calculate more precisely
|
||||
if (ctx.is_evergreen()) {
|
||||
++stack_elements;
|
||||
} else {
|
||||
stack_elements += 2;
|
||||
if (ctx.is_cayman())
|
||||
++stack_elements;
|
||||
}
|
||||
|
||||
unsigned stack_entries = (stack_elements + 3) >> 2;
|
||||
|
||||
if (nstack < stack_entries)
|
||||
nstack = stack_entries;
|
||||
}
|
||||
|
||||
void bc_finalizer::cf_peephole() {
|
||||
|
||||
for (node_iterator N, I = sh.root->begin(), E = sh.root->end(); I != E;
|
||||
I = N) {
|
||||
N = I; ++N;
|
||||
|
||||
cf_node *c = static_cast<cf_node*>(*I);
|
||||
|
||||
if (c->jump_after_target) {
|
||||
c->jump_target = static_cast<cf_node*>(c->jump_target->next);
|
||||
c->jump_after_target = false;
|
||||
}
|
||||
|
||||
if (c->is_cf_op(CF_OP_POP)) {
|
||||
node *p = c->prev;
|
||||
if (p->is_alu_clause()) {
|
||||
cf_node *a = static_cast<cf_node*>(p);
|
||||
|
||||
if (a->bc.op == CF_OP_ALU) {
|
||||
a->bc.set_op(CF_OP_ALU_POP_AFTER);
|
||||
c->remove();
|
||||
}
|
||||
}
|
||||
} else if (c->is_cf_op(CF_OP_JUMP) && c->jump_target == c->next) {
|
||||
// if JUMP is immediately followed by its jump target,
|
||||
// then JUMP is useless and we can eliminate it
|
||||
c->remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,543 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
// TODO add all formats
|
||||
|
||||
// CF
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_WORD0, R6R7)
|
||||
BC_FIELD(CF_WORD0, ADDR, ADDR, 31, 0)
|
||||
BC_FORMAT_END(CF_WORD0)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_WORD0, EGCM)
|
||||
BC_FIELD(CF_WORD0, ADDR, ADDR, 23, 0)
|
||||
BC_FIELD(CF_WORD0, JUMPTABLE_SEL, JTS, 26, 24)
|
||||
BC_RSRVD(CF_WORD0, 31, 27)
|
||||
BC_FORMAT_END(CF_WORD0)
|
||||
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_GWS_WORD0, EGCM)
|
||||
BC_FIELD(CF_GWS_WORD0, VALUE, VAL, 9, 0)
|
||||
BC_RSRVD(CF_GWS_WORD0, 15, 10)
|
||||
BC_FIELD(CF_GWS_WORD0, RESOURCE, RSRC, 20, 16)
|
||||
BC_RSRVD(CF_GWS_WORD0, 24, 21)
|
||||
BC_FIELD(CF_GWS_WORD0, SIGN, SIGN, 25, 25)
|
||||
BC_FIELD(CF_GWS_WORD0, VAL_INDEX_MODE, VIM, 27, 26)
|
||||
BC_FIELD(CF_GWS_WORD0, RSRC_INDEX_MODE, RIM, 29, 28)
|
||||
BC_FIELD(CF_GWS_WORD0, GWS_OPCODE, GWS_OP, 31, 30)
|
||||
BC_FORMAT_END(CF_GWS_WORD0)
|
||||
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_WORD1, R6R7)
|
||||
BC_FIELD(CF_WORD1, POP_COUNT, PC, 2, 0)
|
||||
BC_FIELD(CF_WORD1, CF_CONST, CF_CONST, 7, 3)
|
||||
BC_FIELD(CF_WORD1, COND, COND, 9, 8)
|
||||
BC_FIELD(CF_WORD1, COUNT, COUNT, 12, 10)
|
||||
BC_FIELD(CF_WORD1, CALL_COUNT, CALL_CNT, 18, 13)
|
||||
BC_FIELD(CF_WORD1, COUNT_3, COUNT_3, 19, 19)
|
||||
BC_RSRVD(CF_WORD1, 20, 20)
|
||||
BC_FIELD(CF_WORD1, END_OF_PROGRAM, EOP, 21, 21)
|
||||
BC_FIELD(CF_WORD1, VALID_PIXEL_MODE, VPM, 22, 22)
|
||||
BC_FIELD(CF_WORD1, CF_INST, CF_INST, 29, 23)
|
||||
BC_FIELD(CF_WORD1, WHOLE_QUAD_MODE, WQM, 30, 30)
|
||||
BC_FIELD(CF_WORD1, BARRIER, B, 31, 31)
|
||||
BC_FORMAT_END(CF_WORD1)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_WORD1, EG)
|
||||
BC_FIELD(CF_WORD1, POP_COUNT, PC, 2, 0)
|
||||
BC_FIELD(CF_WORD1, CF_CONST, CF_CONST, 7, 3)
|
||||
BC_FIELD(CF_WORD1, COND, COND, 9, 8)
|
||||
BC_FIELD(CF_WORD1, COUNT, COUNT, 15, 10)
|
||||
BC_RSRVD(CF_WORD1, 19, 16)
|
||||
BC_FIELD(CF_WORD1, VALID_PIXEL_MODE, VPM, 20, 20)
|
||||
BC_FIELD(CF_WORD1, END_OF_PROGRAM, EOP, 21, 21)
|
||||
BC_FIELD(CF_WORD1, CF_INST, CF_INST, 29, 22)
|
||||
BC_FIELD(CF_WORD1, WHOLE_QUAD_MODE, WQM, 30, 30)
|
||||
BC_FIELD(CF_WORD1, BARRIER, B, 31, 31)
|
||||
BC_FORMAT_END(CF_WORD1)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_WORD1, CM)
|
||||
BC_FIELD(CF_WORD1, POP_COUNT, PC, 2, 0)
|
||||
BC_FIELD(CF_WORD1, CF_CONST, CF_CONST, 7, 3)
|
||||
BC_FIELD(CF_WORD1, COND, COND, 9, 8)
|
||||
BC_FIELD(CF_WORD1, COUNT, COUNT, 15, 10)
|
||||
BC_RSRVD(CF_WORD1, 19, 16)
|
||||
BC_FIELD(CF_WORD1, VALID_PIXEL_MODE, VPM, 20, 20)
|
||||
BC_RSRVD(CF_WORD1, 21, 21)
|
||||
BC_FIELD(CF_WORD1, CF_INST, CF_INST, 29, 22)
|
||||
BC_RSRVD(CF_WORD1, 30, 30)
|
||||
BC_FIELD(CF_WORD1, BARRIER, B, 31, 31)
|
||||
BC_FORMAT_END(CF_WORD1)
|
||||
|
||||
|
||||
BC_FORMAT_BEGIN(CF_ALU_WORD0)
|
||||
BC_FIELD(CF_ALU_WORD0, ADDR, ADDR, 21, 0)
|
||||
BC_FIELD(CF_ALU_WORD0, KCACHE_BANK0, KB0, 25, 22)
|
||||
BC_FIELD(CF_ALU_WORD0, KCACHE_BANK1, KB1, 29, 26)
|
||||
BC_FIELD(CF_ALU_WORD0, KCACHE_MODE0, KM0, 31, 30)
|
||||
BC_FORMAT_END(CF_ALU_WORD0)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_ALU_WORD1, R6)
|
||||
BC_FIELD(CF_ALU_WORD1, KCACHE_MODE1, KM1, 1, 0)
|
||||
BC_FIELD(CF_ALU_WORD1, KCACHE_ADDR0, KA0, 9, 2)
|
||||
BC_FIELD(CF_ALU_WORD1, KCACHE_ADDR1, KA1, 17, 10)
|
||||
BC_FIELD(CF_ALU_WORD1, COUNT, COUNT, 24, 18)
|
||||
BC_FIELD(CF_ALU_WORD1, USES_WATERFALL, UW, 25, 25)
|
||||
BC_FIELD(CF_ALU_WORD1, CF_INST, CF_INST, 29, 26)
|
||||
BC_FIELD(CF_ALU_WORD1, WHOLE_QUAD_MODE, WQM, 30, 30)
|
||||
BC_FIELD(CF_ALU_WORD1, BARRIER, B, 31, 31)
|
||||
BC_FORMAT_END(CF_ALU_WORD1)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_ALU_WORD1, R7EGCM)
|
||||
BC_FIELD(CF_ALU_WORD1, KCACHE_MODE1, KM1, 1, 0)
|
||||
BC_FIELD(CF_ALU_WORD1, KCACHE_ADDR0, KA0, 9, 2)
|
||||
BC_FIELD(CF_ALU_WORD1, KCACHE_ADDR1, KA1, 17, 10)
|
||||
BC_FIELD(CF_ALU_WORD1, COUNT, COUNT, 24, 18)
|
||||
BC_FIELD(CF_ALU_WORD1, ALT_CONST, ALT_C, 25, 25)
|
||||
BC_FIELD(CF_ALU_WORD1, CF_INST, CF_INST, 29, 26)
|
||||
BC_FIELD(CF_ALU_WORD1, WHOLE_QUAD_MODE, WQM, 30, 30)
|
||||
BC_FIELD(CF_ALU_WORD1, BARRIER, B, 31, 31)
|
||||
BC_FORMAT_END(CF_ALU_WORD1)
|
||||
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_ALU_WORD0_EXT, EGCM)
|
||||
BC_RSRVD(CF_ALU_WORD0_EXT, 3, 0)
|
||||
BC_FIELD(CF_ALU_WORD0_EXT, KCACHE_BANK_INDEX_MODE0, KBIM0, 5, 4)
|
||||
BC_FIELD(CF_ALU_WORD0_EXT, KCACHE_BANK_INDEX_MODE1, KBIM1, 7, 6)
|
||||
BC_FIELD(CF_ALU_WORD0_EXT, KCACHE_BANK_INDEX_MODE2, KBIM2, 9, 8)
|
||||
BC_FIELD(CF_ALU_WORD0_EXT, KCACHE_BANK_INDEX_MODE3, KBIM3, 11, 10)
|
||||
BC_RSRVD(CF_ALU_WORD0_EXT, 21, 12)
|
||||
BC_FIELD(CF_ALU_WORD0_EXT, KCACHE_BANK2, KB2, 25, 22)
|
||||
BC_FIELD(CF_ALU_WORD0_EXT, KCACHE_BANK3, KB3, 29, 26)
|
||||
BC_FIELD(CF_ALU_WORD0_EXT, KCACHE_MODE2, KM2, 31, 30)
|
||||
BC_FORMAT_END(CF_ALU_WORD0_EXT)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_ALU_WORD1_EXT, EGCM)
|
||||
BC_FIELD(CF_ALU_WORD1_EXT, KCACHE_MODE3, KM3, 1, 0)
|
||||
BC_FIELD(CF_ALU_WORD1_EXT, KCACHE_ADDR2, KA2, 9, 2)
|
||||
BC_FIELD(CF_ALU_WORD1_EXT, KCACHE_ADDR3, KA3, 17, 10)
|
||||
BC_RSRVD(CF_ALU_WORD1_EXT, 25, 18)
|
||||
BC_FIELD(CF_ALU_WORD1_EXT, CF_INST, CF_INST, 29, 26)
|
||||
BC_RSRVD(CF_ALU_WORD1_EXT, 30, 30)
|
||||
BC_FIELD(CF_ALU_WORD1_EXT, BARRIER, B, 31, 31)
|
||||
BC_FORMAT_END(CF_ALU_WORD1_EXT)
|
||||
|
||||
|
||||
BC_FORMAT_BEGIN(CF_ALLOC_EXPORT_WORD0)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0, ARRAY_BASE, ARR_BS, 12, 0)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0, TYPE, TYPE, 14, 13)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0, RW_GPR, RW_GPR, 21, 15)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0, RW_REL, RW_REL, 22, 22)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0, INDEX_GPR, IND_GPR, 29, 23)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0, ELEM_SIZE, ES, 31, 30)
|
||||
BC_FORMAT_END(CF_ALLOC_EXPORT_WORD0)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_ALLOC_EXPORT_WORD0_RAT, EGCM)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0_RAT, RAT_ID, R_ID, 3, 0)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0_RAT, RAT_INST, R_INST, 9, 4)
|
||||
BC_RSRVD(CF_ALLOC_EXPORT_WORD0_RAT, 10, 10)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0_RAT, RAT_INDEX_MODE, RIM, 12, 11)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0_RAT, TYPE, TYPE, 14, 13)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0_RAT, RW_GPR, RW_GPR, 21, 15)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0_RAT, RW_REL, RW_REL, 22, 22)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0_RAT, INDEX_GPR, IND_GPR, 29, 23)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD0_RAT, ELEM_SIZE, ES, 31, 30)
|
||||
BC_FORMAT_END(CF_ALLOC_EXPORT_WORD0_RAT)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_ALLOC_EXPORT_WORD1_BUF, R6R7)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, ARRAY_SIZE, ARR_SZ, 11, 0)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, COMP_MASK, MASK, 15, 12)
|
||||
BC_RSRVD(CF_ALLOC_EXPORT_WORD1_BUF, 16, 16)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, BURST_COUNT, BURST, 20, 17)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, END_OF_PROGRAM, EOP, 21, 21)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, VALID_PIXEL_MODE, VPM, 22, 22)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, CF_INST, CF_INST, 29, 23)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, WHOLE_QUAD_MODE, WQM, 30, 30)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, BARRIER, B, 31, 31)
|
||||
BC_FORMAT_END(CF_ALLOC_EXPORT_WORD1_BUF)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_ALLOC_EXPORT_WORD1_BUF, EG)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, ARRAY_SIZE, ARR_SZ, 11, 0)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, COMP_MASK, MASK, 15, 12)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, BURST_COUNT, BURST, 19, 16)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, VALID_PIXEL_MODE, VPM, 20, 20)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, END_OF_PROGRAM, EOP, 21, 21)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, CF_INST, CF_INST, 29, 22)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, MARK, MARK, 30, 30)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, BARRIER, B, 31, 31)
|
||||
BC_FORMAT_END(CF_ALLOC_EXPORT_WORD1_BUF)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_ALLOC_EXPORT_WORD1_BUF, CM)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, ARRAY_SIZE, ARR_SZ, 11, 0)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, COMP_MASK, MASK, 15, 12)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, BURST_COUNT, BURST, 19, 16)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, VALID_PIXEL_MODE, VPM, 20, 20)
|
||||
BC_RSRVD(CF_ALLOC_EXPORT_WORD1_BUF, 21, 21)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, CF_INST, CF_INST, 29, 22)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, MARK, MARK, 30, 30)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_BUF, BARRIER, B, 31, 31)
|
||||
BC_FORMAT_END(CF_ALLOC_EXPORT_WORD1_BUF)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_ALLOC_EXPORT_WORD1_SWIZ, R6R7)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, SEL_X, SEL_X, 2, 0)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, SEL_Y, SEL_Y, 5, 3)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, SEL_Z, SEL_Z, 8, 6)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, SEL_W, SEL_W, 11, 9)
|
||||
BC_RSRVD(CF_ALLOC_EXPORT_WORD1_SWIZ, 16, 12)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, BURST_COUNT, BURST, 20, 17)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, END_OF_PROGRAM, EOP, 21, 21)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, VALID_PIXEL_MODE, VPM, 22, 22)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, CF_INST, CF_INST, 29, 23)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, WHOLE_QUAD_MODE, WQM, 30, 30)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, BARRIER, B, 31, 31)
|
||||
BC_FORMAT_END(CF_ALLOC_EXPORT_WORD1_SWIZ)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_ALLOC_EXPORT_WORD1_SWIZ, EG)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, SEL_X, SEL_X, 2, 0)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, SEL_Y, SEL_Y, 5, 3)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, SEL_Z, SEL_Z, 8, 6)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, SEL_W, SEL_W, 11, 9)
|
||||
BC_RSRVD(CF_ALLOC_EXPORT_WORD1_SWIZ, 15, 12)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, BURST_COUNT, BURST, 19, 16)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, VALID_PIXEL_MODE, VPM, 20, 20)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, END_OF_PROGRAM, EOP, 21, 21)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, CF_INST, CF_INST, 29, 22)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, MARK, M, 30, 30)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, BARRIER, B, 31, 31)
|
||||
BC_FORMAT_END(CF_ALLOC_EXPORT_WORD1_SWIZ)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(CF_ALLOC_EXPORT_WORD1_SWIZ, CM)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, SEL_X, SEL_X, 2, 0)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, SEL_Y, SEL_Y, 5, 3)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, SEL_Z, SEL_Z, 8, 6)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, SEL_W, SEL_W, 11, 9)
|
||||
BC_RSRVD(CF_ALLOC_EXPORT_WORD1_SWIZ, 15, 12)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, BURST_COUNT, BURST, 19, 16)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, VALID_PIXEL_MODE, VPM, 20, 20)
|
||||
BC_RSRVD(CF_ALLOC_EXPORT_WORD1_SWIZ, 21, 21)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, CF_INST, CF_INST, 29, 22)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, MARK, M, 30, 30)
|
||||
BC_FIELD(CF_ALLOC_EXPORT_WORD1_SWIZ, BARRIER, B, 31, 31)
|
||||
BC_FORMAT_END(CF_ALLOC_EXPORT_WORD1_SWIZ)
|
||||
|
||||
// ALU
|
||||
|
||||
BC_FORMAT_BEGIN(ALU_WORD0)
|
||||
BC_FIELD(ALU_WORD0, SRC0_SEL, S0S, 8, 0)
|
||||
BC_FIELD(ALU_WORD0, SRC0_REL, S0R, 9, 9)
|
||||
BC_FIELD(ALU_WORD0, SRC0_CHAN, S0C, 11, 10)
|
||||
BC_FIELD(ALU_WORD0, SRC0_NEG, S0N, 12, 12)
|
||||
BC_FIELD(ALU_WORD0, SRC1_SEL, S1S, 21, 13)
|
||||
BC_FIELD(ALU_WORD0, SRC1_REL, S1R, 22, 22)
|
||||
BC_FIELD(ALU_WORD0, SRC1_CHAN, S1C, 24, 23)
|
||||
BC_FIELD(ALU_WORD0, SRC1_NEG, S1N, 25, 25)
|
||||
BC_FIELD(ALU_WORD0, INDEX_MODE, IM, 28, 26)
|
||||
BC_FIELD(ALU_WORD0, PRED_SEL, PS, 30, 29)
|
||||
BC_FIELD(ALU_WORD0, LAST, L, 31, 31)
|
||||
BC_FORMAT_END(ALU_WORD0)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(ALU_WORD1_OP2, R6)
|
||||
BC_FIELD(ALU_WORD1_OP2, SRC0_ABS, S0A, 0, 0)
|
||||
BC_FIELD(ALU_WORD1_OP2, SRC1_ABS, S1A, 1, 1)
|
||||
BC_FIELD(ALU_WORD1_OP2, UPDATE_EXEC_MASK, UEM, 2, 2)
|
||||
BC_FIELD(ALU_WORD1_OP2, UPDATE_PRED, UP, 3, 3)
|
||||
BC_FIELD(ALU_WORD1_OP2, WRITE_MASK, WM, 4, 4)
|
||||
BC_FIELD(ALU_WORD1_OP2, FOG_MERGE, FM, 5, 5)
|
||||
BC_FIELD(ALU_WORD1_OP2, OMOD, OMOD, 7, 6)
|
||||
BC_FIELD(ALU_WORD1_OP2, ALU_INST, INST, 17, 8)
|
||||
BC_FIELD(ALU_WORD1_OP2, BANK_SWIZZLE, BS, 20, 18)
|
||||
BC_FIELD(ALU_WORD1_OP2, DST_GPR, DGPR, 27, 21)
|
||||
BC_FIELD(ALU_WORD1_OP2, DST_REL, DR, 28, 28)
|
||||
BC_FIELD(ALU_WORD1_OP2, DST_CHAN, DC, 30, 29)
|
||||
BC_FIELD(ALU_WORD1_OP2, CLAMP, C, 31, 31)
|
||||
BC_FORMAT_END(ALU_WORD1_OP2)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(ALU_WORD1_OP2, R7EGCM)
|
||||
BC_FIELD(ALU_WORD1_OP2, SRC0_ABS, S0A, 0, 0)
|
||||
BC_FIELD(ALU_WORD1_OP2, SRC1_ABS, S1A, 1, 1)
|
||||
BC_FIELD(ALU_WORD1_OP2, UPDATE_EXEC_MASK, UEM, 2, 2)
|
||||
BC_FIELD(ALU_WORD1_OP2, UPDATE_PRED, UP, 3, 3)
|
||||
BC_FIELD(ALU_WORD1_OP2, WRITE_MASK, WM, 4, 4)
|
||||
BC_FIELD(ALU_WORD1_OP2, OMOD, OMOD, 6, 5)
|
||||
BC_FIELD(ALU_WORD1_OP2, ALU_INST, INST, 17, 7)
|
||||
BC_FIELD(ALU_WORD1_OP2, BANK_SWIZZLE, BS, 20, 18)
|
||||
BC_FIELD(ALU_WORD1_OP2, DST_GPR, DGPR, 27, 21)
|
||||
BC_FIELD(ALU_WORD1_OP2, DST_REL, DR, 28, 28)
|
||||
BC_FIELD(ALU_WORD1_OP2, DST_CHAN, DC, 30, 29)
|
||||
BC_FIELD(ALU_WORD1_OP2, CLAMP, C, 31, 31)
|
||||
BC_FORMAT_END(ALU_WORD1_OP2)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(ALU_WORD1_OP2_MOVA, CM)
|
||||
BC_FIELD(ALU_WORD1_OP2_MOVA, SRC0_ABS, S0A, 0, 0)
|
||||
BC_FIELD(ALU_WORD1_OP2_MOVA, SRC1_ABS, S1A, 1, 1)
|
||||
BC_FIELD(ALU_WORD1_OP2_MOVA, UPDATE_EXEC_MASK, UEM, 2, 2)
|
||||
BC_FIELD(ALU_WORD1_OP2_MOVA, UPDATE_PRED, UP, 3, 3)
|
||||
BC_FIELD(ALU_WORD1_OP2_MOVA, WRITE_MASK, WM, 4, 4)
|
||||
BC_FIELD(ALU_WORD1_OP2_MOVA, OMOD, OMOD, 6, 5)
|
||||
BC_FIELD(ALU_WORD1_OP2_MOVA, ALU_INST, INST, 17, 7)
|
||||
BC_FIELD(ALU_WORD1_OP2_MOVA, BANK_SWIZZLE, BS, 20, 18)
|
||||
BC_FIELD(ALU_WORD1_OP2_MOVA, MOVA_DST, MOVA_DST, 27, 21)
|
||||
BC_FIELD(ALU_WORD1_OP2_MOVA, DST_REL, DR, 28, 28)
|
||||
BC_FIELD(ALU_WORD1_OP2_MOVA, DST_CHAN, DC, 30, 29)
|
||||
BC_FIELD(ALU_WORD1_OP2_MOVA, CLAMP, C, 31, 31)
|
||||
BC_FORMAT_END(ALU_WORD1_OP2_MOVA)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(ALU_WORD1_OP2_EXEC_MASK, CM)
|
||||
BC_FIELD(ALU_WORD1_OP2_EXEC_MASK, SRC0_ABS, S0A, 0, 0)
|
||||
BC_FIELD(ALU_WORD1_OP2_EXEC_MASK, SRC1_ABS, S1A, 1, 1)
|
||||
BC_FIELD(ALU_WORD1_OP2_EXEC_MASK, UPDATE_EXEC_MASK, UEM, 2, 2)
|
||||
BC_FIELD(ALU_WORD1_OP2_EXEC_MASK, UPDATE_PRED, UP, 3, 3)
|
||||
BC_FIELD(ALU_WORD1_OP2_EXEC_MASK, WRITE_MASK, WM, 4, 4)
|
||||
BC_FIELD(ALU_WORD1_OP2_EXEC_MASK, EXECUTE_MASK_OP, EMO, 6, 5)
|
||||
BC_FIELD(ALU_WORD1_OP2_EXEC_MASK, ALU_INST, INST, 17, 7)
|
||||
BC_FIELD(ALU_WORD1_OP2_EXEC_MASK, BANK_SWIZZLE, BS, 20, 18)
|
||||
BC_FIELD(ALU_WORD1_OP2_EXEC_MASK, DST_GPR, DGPR, 27, 21)
|
||||
BC_FIELD(ALU_WORD1_OP2_EXEC_MASK, DST_REL, DR, 28, 28)
|
||||
BC_FIELD(ALU_WORD1_OP2_EXEC_MASK, DST_CHAN, DC, 30, 29)
|
||||
BC_FIELD(ALU_WORD1_OP2_EXEC_MASK, CLAMP, C, 31, 31)
|
||||
BC_FORMAT_END(ALU_WORD1_OP2_EXEC_MASK)
|
||||
|
||||
BC_FORMAT_BEGIN(ALU_WORD1_OP3)
|
||||
BC_FIELD(ALU_WORD1_OP3, SRC2_SEL, S2S, 8, 0)
|
||||
BC_FIELD(ALU_WORD1_OP3, SRC2_REL, S2R, 9, 9)
|
||||
BC_FIELD(ALU_WORD1_OP3, SRC2_CHAN, S2C, 11, 10)
|
||||
BC_FIELD(ALU_WORD1_OP3, SRC2_NEG, S2N, 12, 12)
|
||||
BC_FIELD(ALU_WORD1_OP3, ALU_INST, INST, 17, 13)
|
||||
BC_FIELD(ALU_WORD1_OP3, BANK_SWIZZLE, BS, 20, 18)
|
||||
BC_FIELD(ALU_WORD1_OP3, DST_GPR, DGPR, 27, 21)
|
||||
BC_FIELD(ALU_WORD1_OP3, DST_REL, DR, 28, 28)
|
||||
BC_FIELD(ALU_WORD1_OP3, DST_CHAN, DC, 30, 29)
|
||||
BC_FIELD(ALU_WORD1_OP3, CLAMP, C, 31, 31)
|
||||
BC_FORMAT_END(ALU_WORD1_OP3)
|
||||
|
||||
|
||||
BC_FORMAT_BEGIN_HW(ALU_WORD0_LDS_IDX_OP, EGCM)
|
||||
BC_FIELD(ALU_WORD0_LDS_IDX_OP, SRC0_SEL, S0S, 8, 0)
|
||||
BC_FIELD(ALU_WORD0_LDS_IDX_OP, SRC0_REL, S0R, 9, 9)
|
||||
BC_FIELD(ALU_WORD0_LDS_IDX_OP, SRC0_CHAN, S0C, 11, 10)
|
||||
BC_FIELD(ALU_WORD0_LDS_IDX_OP, IDX_OFFSET_4, IO4, 12, 12)
|
||||
BC_FIELD(ALU_WORD0_LDS_IDX_OP, SRC1_SEL, S1S, 21, 13)
|
||||
BC_FIELD(ALU_WORD0_LDS_IDX_OP, SRC1_REL, S1R, 22, 22)
|
||||
BC_FIELD(ALU_WORD0_LDS_IDX_OP, SRC1_CHAN, S1C, 24, 23)
|
||||
BC_FIELD(ALU_WORD0_LDS_IDX_OP, IDX_OFFSET_5, IO5, 25, 25)
|
||||
BC_FIELD(ALU_WORD0_LDS_IDX_OP, INDEX_MODE, IM, 28, 26)
|
||||
BC_FIELD(ALU_WORD0_LDS_IDX_OP, PRED_SEL, PS, 30, 29)
|
||||
BC_FIELD(ALU_WORD0_LDS_IDX_OP, LAST, L, 31, 31)
|
||||
BC_FORMAT_END(ALU_WORD0_LDS_IDX_OP)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(ALU_WORD1_LDS_IDX_OP, EGCM)
|
||||
BC_FIELD(ALU_WORD1_LDS_IDX_OP, SRC2_SEL, S2S, 8, 0)
|
||||
BC_FIELD(ALU_WORD1_LDS_IDX_OP, SRC2_REL, S2R, 9, 9)
|
||||
BC_FIELD(ALU_WORD1_LDS_IDX_OP, SRC2_CHAN, S2C, 11, 10)
|
||||
BC_FIELD(ALU_WORD1_LDS_IDX_OP, IDX_OFFSET_1, IO1, 12, 12)
|
||||
BC_FIELD(ALU_WORD1_LDS_IDX_OP, ALU_INST, INST, 17, 13)
|
||||
BC_FIELD(ALU_WORD1_LDS_IDX_OP, BANK_SWIZZLE, BS, 20, 18)
|
||||
BC_FIELD(ALU_WORD1_LDS_IDX_OP, LDS_OP, LDS_OP, 26, 21)
|
||||
BC_FIELD(ALU_WORD1_LDS_IDX_OP, IDX_OFFSET_0, IO0, 27, 27)
|
||||
BC_FIELD(ALU_WORD1_LDS_IDX_OP, IDX_OFFSET_2, IO2, 28, 28)
|
||||
BC_FIELD(ALU_WORD1_LDS_IDX_OP, DST_CHAN, DC, 30, 29)
|
||||
BC_FIELD(ALU_WORD1_LDS_IDX_OP, IDX_OFFSET_3, IO3, 31, 31)
|
||||
BC_FORMAT_END(ALU_WORD1_LDS_IDX_OP)
|
||||
|
||||
|
||||
BC_FORMAT_BEGIN_HW(ALU_WORD1_LDS_DIRECT_LITERAL_LO, EGCM)
|
||||
BC_FIELD(ALU_WORD1_LDS_DIRECT_LITERAL_LO, OFFSET_A, OFS_A, 12, 0)
|
||||
BC_FIELD(ALU_WORD1_LDS_DIRECT_LITERAL_LO, STRIDE_A, STR_A, 19, 13)
|
||||
BC_RSRVD(ALU_WORD1_LDS_DIRECT_LITERAL_LO, 21, 20)
|
||||
BC_FIELD(ALU_WORD1_LDS_DIRECT_LITERAL_LO, THREAD_REL_A, THR_A, 22, 22)
|
||||
BC_RSRVD(ALU_WORD1_LDS_DIRECT_LITERAL_LO, 31, 22)
|
||||
BC_FORMAT_END(ALU_WORD1_LDS_DIRECT_LITERAL_LO)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(ALU_WORD1_LDS_DIRECT_LITERAL_HI, EGCM)
|
||||
BC_FIELD(ALU_WORD1_LDS_DIRECT_LITERAL_HI, OFFSET_B, OFS_B, 12, 0)
|
||||
BC_FIELD(ALU_WORD1_LDS_DIRECT_LITERAL_HI, STRIDE_B, STR_B, 19, 13)
|
||||
BC_RSRVD(ALU_WORD1_LDS_DIRECT_LITERAL_HI, 21, 20)
|
||||
BC_FIELD(ALU_WORD1_LDS_DIRECT_LITERAL_HI, THREAD_REL_B, THR_B, 22, 22)
|
||||
BC_RSRVD(ALU_WORD1_LDS_DIRECT_LITERAL_HI, 30, 22)
|
||||
BC_FIELD(ALU_WORD1_LDS_DIRECT_LITERAL_HI, DIRECT_READ_32, DR32, 31, 31)
|
||||
BC_FORMAT_END(ALU_WORD1_LDS_DIRECT_LITERAL_HI)
|
||||
|
||||
|
||||
// VTX
|
||||
|
||||
BC_FORMAT_BEGIN_HW(VTX_WORD0, R6R7EG)
|
||||
BC_FIELD(VTX_WORD0, VC_INST, INST, 4, 0)
|
||||
BC_FIELD(VTX_WORD0, FETCH_TYPE, FT, 6, 5)
|
||||
BC_FIELD(VTX_WORD0, FETCH_WHOLE_QUAD, FWQ, 7, 7)
|
||||
BC_FIELD(VTX_WORD0, BUFFER_ID, BUF_ID, 15, 8)
|
||||
BC_FIELD(VTX_WORD0, SRC_GPR, S_GPR, 22, 16)
|
||||
BC_FIELD(VTX_WORD0, SRC_REL, SR, 23, 23)
|
||||
BC_FIELD(VTX_WORD0, SRC_SEL_X, SSX, 25, 24)
|
||||
BC_FIELD(VTX_WORD0, MEGA_FETCH_COUNT, MFC, 31, 26)
|
||||
BC_FORMAT_END(VTX_WORD0)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(VTX_WORD0, CM)
|
||||
BC_FIELD(VTX_WORD0, VC_INST, INST, 4, 0)
|
||||
BC_FIELD(VTX_WORD0, FETCH_TYPE, FT, 6, 5)
|
||||
BC_FIELD(VTX_WORD0, FETCH_WHOLE_QUAD, FWQ, 7, 7)
|
||||
BC_FIELD(VTX_WORD0, BUFFER_ID, BUF_ID, 15, 8)
|
||||
BC_FIELD(VTX_WORD0, SRC_GPR, S_GPR, 22, 16)
|
||||
BC_FIELD(VTX_WORD0, SRC_REL, SR, 23, 23)
|
||||
BC_FIELD(VTX_WORD0, SRC_SEL_X, SSX, 25, 24)
|
||||
BC_FIELD(VTX_WORD0, SRC_SEL_Y, SSY, 27, 26)
|
||||
BC_FIELD(VTX_WORD0, STRUCTURED_READ, SR, 29, 28)
|
||||
BC_FIELD(VTX_WORD0, LDS_REQ, LR, 30, 30)
|
||||
BC_FIELD(VTX_WORD0, COALESCED_READ, CR, 31, 31)
|
||||
BC_FORMAT_END(VTX_WORD0)
|
||||
|
||||
|
||||
BC_FORMAT_BEGIN(VTX_WORD1_GPR)
|
||||
BC_FIELD(VTX_WORD1_GPR, DST_GPR, D_GPR, 6, 0)
|
||||
BC_FIELD(VTX_WORD1_GPR, DST_REL, DR, 7, 7)
|
||||
BC_RSRVD(VTX_WORD1_GPR, 8, 8)
|
||||
BC_FIELD(VTX_WORD1_GPR, DST_SEL_X, DSX, 11, 9)
|
||||
BC_FIELD(VTX_WORD1_GPR, DST_SEL_Y, DSY, 14, 12)
|
||||
BC_FIELD(VTX_WORD1_GPR, DST_SEL_Z, DSZ, 17, 15)
|
||||
BC_FIELD(VTX_WORD1_GPR, DST_SEL_W, DSW, 20, 18)
|
||||
BC_FIELD(VTX_WORD1_GPR, USE_CONST_FIELDS, UCF, 21, 21)
|
||||
BC_FIELD(VTX_WORD1_GPR, DATA_FORMAT, DFMT, 27, 22)
|
||||
BC_FIELD(VTX_WORD1_GPR, NUM_FORMAT_ALL, NFA, 29, 28)
|
||||
BC_FIELD(VTX_WORD1_GPR, FORMAT_COMP_ALL, FCA, 30, 30)
|
||||
BC_FIELD(VTX_WORD1_GPR, SRF_MODE_ALL, SMA, 31, 31)
|
||||
BC_FORMAT_END(VTX_WORD1_GPR)
|
||||
|
||||
BC_FORMAT_BEGIN(VTX_WORD1_SEM)
|
||||
BC_FIELD(VTX_WORD1_SEM, SEMANTIC_ID, SID, 7, 0)
|
||||
BC_RSRVD(VTX_WORD1_SEM, 8, 8)
|
||||
BC_FIELD(VTX_WORD1_SEM, DST_SEL_X, DSX, 11, 9)
|
||||
BC_FIELD(VTX_WORD1_SEM, DST_SEL_Y, DSY, 14, 12)
|
||||
BC_FIELD(VTX_WORD1_SEM, DST_SEL_Z, DSZ, 17, 15)
|
||||
BC_FIELD(VTX_WORD1_SEM, DST_SEL_W, DSW, 20, 18)
|
||||
BC_FIELD(VTX_WORD1_SEM, USE_CONST_FIELDS, UCF, 21, 21)
|
||||
BC_FIELD(VTX_WORD1_SEM, DATA_FORMAT, DFMT, 27, 22)
|
||||
BC_FIELD(VTX_WORD1_SEM, NUM_FORMAT_ALL, NFA, 29, 28)
|
||||
BC_FIELD(VTX_WORD1_SEM, FORMAT_COMP_ALL, FCA, 30, 30)
|
||||
BC_FIELD(VTX_WORD1_SEM, SRF_MODE_ALL, SMA, 31, 31)
|
||||
BC_FORMAT_END(VTX_WORD1_SEM)
|
||||
|
||||
|
||||
BC_FORMAT_BEGIN_HW(VTX_WORD2, R6)
|
||||
BC_FIELD(VTX_WORD2, OFFSET, OFS, 15, 0)
|
||||
BC_FIELD(VTX_WORD2, ENDIAN_SWAP, ES, 17, 16)
|
||||
BC_FIELD(VTX_WORD2, CONST_BUF_NO_STRIDE, CBNS, 18, 18)
|
||||
BC_FIELD(VTX_WORD2, MEGA_FETCH, MF, 19, 19)
|
||||
BC_RSRVD(VTX_WORD2, 31, 20)
|
||||
BC_FORMAT_END(VTX_WORD2)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(VTX_WORD2, R7)
|
||||
BC_FIELD(VTX_WORD2, OFFSET, OFS, 15, 0)
|
||||
BC_FIELD(VTX_WORD2, ENDIAN_SWAP, ES, 17, 16)
|
||||
BC_FIELD(VTX_WORD2, CONST_BUF_NO_STRIDE, CBNS, 18, 18)
|
||||
BC_FIELD(VTX_WORD2, MEGA_FETCH, MF, 19, 19)
|
||||
BC_FIELD(VTX_WORD2, ALT_CONST, ALT_C, 20, 20)
|
||||
BC_RSRVD(VTX_WORD2, 31, 21)
|
||||
BC_FORMAT_END(VTX_WORD2)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(VTX_WORD2, EG)
|
||||
BC_FIELD(VTX_WORD2, OFFSET, OFS, 15, 0)
|
||||
BC_FIELD(VTX_WORD2, ENDIAN_SWAP, ES, 17, 16)
|
||||
BC_FIELD(VTX_WORD2, CONST_BUF_NO_STRIDE, CBNS, 18, 18)
|
||||
BC_FIELD(VTX_WORD2, MEGA_FETCH, MF, 19, 19)
|
||||
BC_FIELD(VTX_WORD2, ALT_CONST, ALT_C, 20, 20)
|
||||
BC_FIELD(VTX_WORD2, BUFFER_INDEX_MODE, BIM, 22, 21)
|
||||
BC_RSRVD(VTX_WORD2, 31, 23)
|
||||
BC_FORMAT_END(VTX_WORD2)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(VTX_WORD2, CM)
|
||||
BC_FIELD(VTX_WORD2, OFFSET, OFS, 15, 0)
|
||||
BC_FIELD(VTX_WORD2, ENDIAN_SWAP, ES, 17, 16)
|
||||
BC_FIELD(VTX_WORD2, CONST_BUF_NO_STRIDE, CBNS, 18, 18)
|
||||
BC_RSRVD(VTX_WORD2, 19, 19)
|
||||
BC_FIELD(VTX_WORD2, ALT_CONST, ALT_C, 20, 20)
|
||||
BC_FIELD(VTX_WORD2, BUFFER_INDEX_MODE, BIM, 22, 21)
|
||||
BC_RSRVD(VTX_WORD2, 31, 23)
|
||||
BC_FORMAT_END(VTX_WORD2)
|
||||
|
||||
// TEX
|
||||
|
||||
BC_FORMAT_BEGIN_HW(TEX_WORD0, R6)
|
||||
BC_FIELD(TEX_WORD0, TEX_INST, T_INST, 4, 0)
|
||||
BC_FIELD(TEX_WORD0, BC_FRAC_MODE, BFM, 5, 5)
|
||||
BC_RSRVD(TEX_WORD0, 6, 6)
|
||||
BC_FIELD(TEX_WORD0, FETCH_WHOLE_QUAD, FWQ, 7, 7)
|
||||
BC_FIELD(TEX_WORD0, RESOURCE_ID, RSRC_ID, 15, 8)
|
||||
BC_FIELD(TEX_WORD0, SRC_GPR, S_GPR, 22, 16)
|
||||
BC_FIELD(TEX_WORD0, SRC_REL, SR, 23, 23)
|
||||
BC_RSRVD(TEX_WORD0, 31, 24)
|
||||
BC_FORMAT_END(TEX_WORD0)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(TEX_WORD0, R7)
|
||||
BC_FIELD(TEX_WORD0, TEX_INST, T_INST, 4, 0)
|
||||
BC_FIELD(TEX_WORD0, BC_FRAC_MODE, BFM, 5, 5)
|
||||
BC_RSRVD(TEX_WORD0, 6, 6)
|
||||
BC_FIELD(TEX_WORD0, FETCH_WHOLE_QUAD, FWQ, 7, 7)
|
||||
BC_FIELD(TEX_WORD0, RESOURCE_ID, RSRC_ID, 15, 8)
|
||||
BC_FIELD(TEX_WORD0, SRC_GPR, S_GPR, 22, 16)
|
||||
BC_FIELD(TEX_WORD0, SRC_REL, SR, 23, 23)
|
||||
BC_FIELD(TEX_WORD0, ALT_CONST, ALT_C, 24, 24)
|
||||
BC_RSRVD(TEX_WORD0, 31, 25)
|
||||
BC_FORMAT_END(TEX_WORD0)
|
||||
|
||||
BC_FORMAT_BEGIN_HW(TEX_WORD0, EGCM)
|
||||
BC_FIELD(TEX_WORD0, TEX_INST, T_INST, 4, 0)
|
||||
BC_FIELD(TEX_WORD0, INST_MOD, IMOD, 6, 5)
|
||||
BC_FIELD(TEX_WORD0, FETCH_WHOLE_QUAD, FWQ, 7, 7)
|
||||
BC_FIELD(TEX_WORD0, RESOURCE_ID, RSRC_ID, 15, 8)
|
||||
BC_FIELD(TEX_WORD0, SRC_GPR, S_GPR, 22, 16)
|
||||
BC_FIELD(TEX_WORD0, SRC_REL, SR, 23, 23)
|
||||
BC_FIELD(TEX_WORD0, ALT_CONST, ALT_C, 24, 24)
|
||||
BC_FIELD(TEX_WORD0, RESOURCE_INDEX_MODE, RIM, 26, 25)
|
||||
BC_FIELD(TEX_WORD0, SAMPLER_INDEX_MODE, SIM, 28, 27)
|
||||
BC_RSRVD(TEX_WORD0, 31, 29)
|
||||
BC_FORMAT_END(TEX_WORD0)
|
||||
|
||||
|
||||
BC_FORMAT_BEGIN(TEX_WORD1)
|
||||
BC_FIELD(TEX_WORD1, DST_GPR, D_GPR, 6, 0)
|
||||
BC_FIELD(TEX_WORD1, DST_REL, DR, 7, 7)
|
||||
BC_RSRVD(TEX_WORD1, 8, 8)
|
||||
BC_FIELD(TEX_WORD1, DST_SEL_X, DSX, 11, 9)
|
||||
BC_FIELD(TEX_WORD1, DST_SEL_Y, DSY, 14, 12)
|
||||
BC_FIELD(TEX_WORD1, DST_SEL_Z, DSZ, 17, 15)
|
||||
BC_FIELD(TEX_WORD1, DST_SEL_W, DSW, 20, 18)
|
||||
BC_FIELD(TEX_WORD1, LOD_BIAS, LBIAS, 27, 21)
|
||||
BC_FIELD(TEX_WORD1, COORD_TYPE_X, CTX, 28, 28)
|
||||
BC_FIELD(TEX_WORD1, COORD_TYPE_Y, CTY, 29, 29)
|
||||
BC_FIELD(TEX_WORD1, COORD_TYPE_Z, CTZ, 30, 30)
|
||||
BC_FIELD(TEX_WORD1, COORD_TYPE_W, CTW, 31, 31)
|
||||
BC_FORMAT_END(TEX_WORD1)
|
||||
|
||||
|
||||
BC_FORMAT_BEGIN(TEX_WORD2)
|
||||
BC_FIELD(TEX_WORD2, OFFSET_X, OFS_X, 4, 0)
|
||||
BC_FIELD(TEX_WORD2, OFFSET_Y, OFS_Y, 9, 5)
|
||||
BC_FIELD(TEX_WORD2, OFFSET_Z, OFS_Z, 14, 10)
|
||||
BC_FIELD(TEX_WORD2, SAMPLER_ID, SAMP_ID, 19, 15)
|
||||
BC_FIELD(TEX_WORD2, SRC_SEL_X, SSX, 22, 20)
|
||||
BC_FIELD(TEX_WORD2, SRC_SEL_Y, SSY, 25, 23)
|
||||
BC_FIELD(TEX_WORD2, SRC_SEL_Z, SSZ, 28, 26)
|
||||
BC_FIELD(TEX_WORD2, SRC_SEL_W, SSW, 31, 29)
|
||||
BC_FORMAT_END(TEX_WORD2)
|
||||
@@ -0,0 +1,763 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#define BCP_DEBUG 0
|
||||
|
||||
#if BCP_DEBUG
|
||||
#define BCP_DUMP(q) do { q } while (0)
|
||||
#else
|
||||
#define BCP_DUMP(q)
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include "r600_pipe.h"
|
||||
#include "r600_shader.h"
|
||||
}
|
||||
|
||||
#include <stack>
|
||||
|
||||
#include "sb_bc.h"
|
||||
#include "sb_shader.h"
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
int bc_parser::parse() {
|
||||
|
||||
dw = bc->bytecode;
|
||||
bc_ndw = bc->ndw;
|
||||
max_cf = 0;
|
||||
|
||||
dec = new bc_decoder(ctx, dw, bc_ndw);
|
||||
|
||||
shader_target t = TARGET_UNKNOWN;
|
||||
|
||||
if (pshader) {
|
||||
switch (bc->type) {
|
||||
case TGSI_PROCESSOR_FRAGMENT: t = TARGET_PS; break;
|
||||
case TGSI_PROCESSOR_VERTEX: t = TARGET_VS; break;
|
||||
case TGSI_PROCESSOR_COMPUTE: t = TARGET_COMPUTE; break;
|
||||
default: assert(!"unknown shader target"); return -1; break;
|
||||
}
|
||||
} else {
|
||||
if (bc->type == TGSI_PROCESSOR_COMPUTE)
|
||||
t = TARGET_COMPUTE;
|
||||
else
|
||||
t = TARGET_FETCH;
|
||||
}
|
||||
|
||||
sh = new shader(ctx, t, bc->debug_id, enable_dump);
|
||||
int r = parse_shader();
|
||||
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (enable_dump) {
|
||||
sh->ngpr = bc->ngpr;
|
||||
sh->nstack = bc->nstack;
|
||||
bc_dump(*sh, cerr, bc->bytecode, bc_ndw).run();
|
||||
}
|
||||
|
||||
if (!optimize)
|
||||
return 0;
|
||||
|
||||
prepare_ir();
|
||||
|
||||
delete dec;
|
||||
return r;
|
||||
}
|
||||
|
||||
int bc_parser::parse_shader() {
|
||||
int r = 0;
|
||||
unsigned i = 0;
|
||||
bool eop = false;
|
||||
|
||||
sh->init();
|
||||
|
||||
if (pshader)
|
||||
parse_decls();
|
||||
|
||||
do {
|
||||
eop = false;
|
||||
if ((r = parse_cf(i, eop)))
|
||||
return r;
|
||||
|
||||
} while (!eop || (i >> 1) <= max_cf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_parser::parse_decls() {
|
||||
|
||||
// sh->prepare_regs(rs.bc.ngpr);
|
||||
|
||||
if (pshader->indirect_files & ~(1 << TGSI_FILE_CONSTANT)) {
|
||||
|
||||
#if SB_NO_ARRAY_INFO
|
||||
|
||||
sh->add_gpr_array(0, pshader->bc.ngpr, 0b1111);
|
||||
|
||||
#else
|
||||
|
||||
assert(pshader->num_arrays);
|
||||
|
||||
if (pshader->num_arrays) {
|
||||
|
||||
for (unsigned i = 0; i < pshader->num_arrays; ++i) {
|
||||
r600_shader_array &a = pshader->arrays[i];
|
||||
sh->add_gpr_array(a.gpr_start, a.gpr_count, a.comp_mask);
|
||||
}
|
||||
|
||||
} else {
|
||||
sh->add_gpr_array(0, pshader->bc.ngpr, 0b1111);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
if (sh->target == TARGET_VS)
|
||||
sh->add_input(0, 1, 0b1111);
|
||||
|
||||
bool ps_interp = ctx.hw_class >= HW_CLASS_EVERGREEN
|
||||
&& sh->target == TARGET_PS;
|
||||
|
||||
unsigned linear = 0, persp = 0, centroid = 1;
|
||||
|
||||
for (unsigned i = 0; i < pshader->ninput; ++i) {
|
||||
r600_shader_io & in = pshader->input[i];
|
||||
bool preloaded = sh->target == TARGET_PS && !(ps_interp && in.spi_sid);
|
||||
sh->add_input(in.gpr, preloaded, /*in.write_mask*/ 0b1111);
|
||||
if (ps_interp && in.spi_sid) {
|
||||
if (in.interpolate == TGSI_INTERPOLATE_LINEAR ||
|
||||
in.interpolate == TGSI_INTERPOLATE_COLOR)
|
||||
linear = 1;
|
||||
else if (in.interpolate == TGSI_INTERPOLATE_PERSPECTIVE)
|
||||
persp = 1;
|
||||
if (in.centroid)
|
||||
centroid = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (ps_interp) {
|
||||
unsigned mask = (1 << (2 * (linear + persp) * centroid)) - 1;
|
||||
unsigned gpr = 0;
|
||||
|
||||
while (mask) {
|
||||
sh->add_input(gpr, true, mask & 0b1111);
|
||||
++gpr;
|
||||
mask >>= 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int bc_parser::parse_cf(unsigned &i, bool &eop) {
|
||||
|
||||
int r;
|
||||
|
||||
cf_node *cf = sh->create_cf();
|
||||
sh->root->push_back(cf);
|
||||
|
||||
unsigned id = i >> 1;
|
||||
|
||||
cf->bc.id = id;
|
||||
|
||||
if (cf_map.size() < id + 1)
|
||||
cf_map.resize(id + 1);
|
||||
|
||||
cf_map[id] = cf;
|
||||
|
||||
if ((r = dec->decode_cf(i, cf->bc)))
|
||||
return r;
|
||||
|
||||
cf_op_flags flags = (cf_op_flags)cf->bc.op_ptr->flags;
|
||||
|
||||
if (flags & CF_ALU) {
|
||||
if ((r = parse_alu_clause(cf)))
|
||||
return r;
|
||||
} else if (flags & CF_FETCH) {
|
||||
if ((r = parse_fetch_clause(cf)))
|
||||
return r;;
|
||||
} else if (flags & CF_EXP) {
|
||||
assert(!cf->bc.rw_rel);
|
||||
} else if (flags & (CF_STRM | CF_RAT)) {
|
||||
assert(!cf->bc.rw_rel);
|
||||
} else if (cf->bc.op == CF_OP_CALL_FS) {
|
||||
sh->init_call_fs(cf);
|
||||
cf->flags |= NF_SCHEDULE_EARLY | NF_DONT_MOVE;
|
||||
} else if (flags & CF_BRANCH) {
|
||||
if (cf->bc.addr > max_cf)
|
||||
max_cf = cf->bc.addr;
|
||||
}
|
||||
|
||||
eop = cf->bc.end_of_program || cf->bc.op == CF_OP_CF_END ||
|
||||
cf->bc.op == CF_OP_RET;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_parser::parse_alu_clause(cf_node* cf) {
|
||||
unsigned i = cf->bc.addr << 1, cnt = cf->bc.count + 1, gcnt;
|
||||
|
||||
cgroup = 0;
|
||||
memset(slots[0], 0, 5*sizeof(slots[0][0]));
|
||||
|
||||
unsigned ng = 0;
|
||||
|
||||
do {
|
||||
parse_alu_group(cf, i, gcnt);
|
||||
assert(gcnt <= cnt);
|
||||
cnt -= gcnt;
|
||||
ng++;
|
||||
} while (cnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_parser::parse_alu_group(cf_node* cf, unsigned &i, unsigned &gcnt) {
|
||||
int r;
|
||||
alu_node *n;
|
||||
alu_group_node *g = sh->create_alu_group();
|
||||
|
||||
cgroup = !cgroup;
|
||||
memset(slots[cgroup], 0, 5*sizeof(slots[0][0]));
|
||||
|
||||
gcnt = 0;
|
||||
|
||||
do {
|
||||
n = sh->create_alu();
|
||||
g->push_back(n);
|
||||
|
||||
if ((r = dec->decode_alu(i, n->bc)))
|
||||
return r;
|
||||
|
||||
if (!sh->assign_slot(n, slots[cgroup])) {
|
||||
assert(!"alu slot assignment failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gcnt++;
|
||||
|
||||
} while (gcnt <= 5 && !n->bc.last);
|
||||
|
||||
assert(n->bc.last);
|
||||
|
||||
unsigned literal_mask = 0;
|
||||
|
||||
for (node_iterator I = g->begin(), E = g->end();
|
||||
I != E; ++I) {
|
||||
n = static_cast<alu_node*>(*I);
|
||||
unsigned src_count = n->bc.op_ptr->src_count;
|
||||
|
||||
if (ctx.alu_slots(n->bc.op) & AF_4SLOT)
|
||||
n->flags |= NF_ALU_4SLOT;
|
||||
|
||||
n->src.resize(src_count);
|
||||
|
||||
unsigned flags = n->bc.op_ptr->flags;
|
||||
|
||||
if (flags & AF_PRED) {
|
||||
n->dst.resize(3);
|
||||
if (n->bc.update_pred)
|
||||
n->dst[1] = sh->get_special_value(SV_ALU_PRED);
|
||||
if (n->bc.update_exec_mask)
|
||||
n->dst[2] = sh->get_special_value(SV_EXEC_MASK);
|
||||
|
||||
n->flags |= NF_DONT_HOIST;
|
||||
|
||||
} else if (flags & AF_KILL) {
|
||||
|
||||
n->dst.resize(2);
|
||||
n->dst[1] = sh->get_special_value(SV_VALID_MASK);
|
||||
sh->set_uses_kill();
|
||||
|
||||
n->flags |= NF_DONT_HOIST | NF_DONT_MOVE |
|
||||
NF_DONT_KILL | NF_SCHEDULE_EARLY;
|
||||
|
||||
} else {
|
||||
n->dst.resize(1);
|
||||
}
|
||||
|
||||
if (flags & AF_MOVA) {
|
||||
|
||||
n->dst[0] = sh->get_special_value(SV_AR_INDEX);
|
||||
|
||||
n->flags |= NF_DONT_HOIST;
|
||||
|
||||
} else if (n->bc.op_ptr->src_count == 3 || n->bc.write_mask) {
|
||||
assert(!n->bc.dst_rel || n->bc.index_mode == INDEX_AR_X);
|
||||
|
||||
value *v = sh->get_gpr_value(false, n->bc.dst_gpr, n->bc.dst_chan,
|
||||
n->bc.dst_rel);
|
||||
|
||||
n->dst[0] = v;
|
||||
}
|
||||
|
||||
if (n->bc.pred_sel) {
|
||||
sh->has_alu_predication = true;
|
||||
n->pred = sh->get_special_value(SV_ALU_PRED);
|
||||
}
|
||||
|
||||
for (unsigned s = 0; s < src_count; ++s) {
|
||||
bc_alu_src &src = n->bc.src[s];
|
||||
|
||||
if (src.sel == ALU_SRC_LITERAL) {
|
||||
unsigned chan = src.chan;
|
||||
|
||||
literal_mask |= (1 << chan);
|
||||
src.value.u = dw[i+chan];
|
||||
n->src[s] = sh->get_const_value(src.value);
|
||||
} else if (src.sel == ALU_SRC_PS || src.sel == ALU_SRC_PV) {
|
||||
unsigned pgroup = !cgroup, prev_slot = src.sel == ALU_SRC_PS ?
|
||||
SLOT_TRANS : src.chan;
|
||||
alu_node *prev_alu = slots[pgroup][prev_slot];
|
||||
|
||||
assert(prev_alu);
|
||||
|
||||
if (!prev_alu->dst[0]) {
|
||||
value * t = sh->create_temp_value();
|
||||
prev_alu->dst[0] = t;
|
||||
}
|
||||
|
||||
value *d = prev_alu->dst[0];
|
||||
|
||||
if (d->is_rel()) {
|
||||
d = sh->get_gpr_value(true, prev_alu->bc.dst_gpr,
|
||||
prev_alu->bc.dst_chan,
|
||||
prev_alu->bc.dst_rel);
|
||||
}
|
||||
|
||||
n->src[s] = d;
|
||||
} else if (ctx.is_kcache_sel(src.sel)) {
|
||||
unsigned sel = src.sel, kc_addr;
|
||||
unsigned kc_set = ((sel >> 7) & 2) + ((sel >> 5) & 1);
|
||||
|
||||
bc_kcache &kc = cf->bc.kc[kc_set];
|
||||
kc_addr = (kc.addr << 4) + (sel & 0x1F);
|
||||
n->src[s] = sh->get_kcache_value(kc.bank, kc_addr, src.chan);
|
||||
} else if (src.sel < MAX_GPR) {
|
||||
value *v = sh->get_gpr_value(true, src.sel, src.chan, src.rel);
|
||||
|
||||
n->src[s] = v;
|
||||
|
||||
} else if (src.sel >= ALU_SRC_PARAM_OFFSET) {
|
||||
// using slot for value channel because in fact the slot
|
||||
// determines the channel that is loaded by INTERP_LOAD_P0
|
||||
// (and maybe some others).
|
||||
// otherwise GVN will consider INTERP_LOAD_P0s with the same
|
||||
// param index as equal instructions and leave only one of them
|
||||
n->src[s] = sh->get_special_ro_value(sel_chan(src.sel,
|
||||
n->bc.slot));
|
||||
} else {
|
||||
switch (src.sel) {
|
||||
case ALU_SRC_0:
|
||||
n->src[s] = sh->get_const_value(0);
|
||||
break;
|
||||
case ALU_SRC_0_5:
|
||||
n->src[s] = sh->get_const_value(0.5f);
|
||||
break;
|
||||
case ALU_SRC_1:
|
||||
n->src[s] = sh->get_const_value(1.0f);
|
||||
break;
|
||||
case ALU_SRC_1_INT:
|
||||
n->src[s] = sh->get_const_value(1);
|
||||
break;
|
||||
case ALU_SRC_M_1_INT:
|
||||
n->src[s] = sh->get_const_value(-1);
|
||||
break;
|
||||
default:
|
||||
n->src[s] = sh->get_special_ro_value(src.sel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pack multislot instructions into alu_packed_node
|
||||
|
||||
alu_packed_node *p = NULL;
|
||||
for (node_iterator N, I = g->begin(), E = g->end(); I != E; I = N) {
|
||||
N = I + 1;
|
||||
alu_node *a = static_cast<alu_node*>(*I);
|
||||
unsigned sflags = a->bc.slot_flags;
|
||||
|
||||
if (sflags == AF_4V || (ctx.is_cayman() && sflags == AF_S)) {
|
||||
if (!p)
|
||||
p = sh->create_alu_packed();
|
||||
|
||||
a->remove();
|
||||
p->push_back(a);
|
||||
}
|
||||
}
|
||||
|
||||
if (p) {
|
||||
g->push_front(p);
|
||||
}
|
||||
|
||||
unsigned literal_ndw = 0;
|
||||
while (literal_mask) {
|
||||
g->literals.push_back(dw[i + literal_ndw]);
|
||||
literal_ndw += 1;
|
||||
literal_mask >>= 1;
|
||||
}
|
||||
|
||||
literal_ndw = (literal_ndw + 1) & ~1u;
|
||||
|
||||
i += literal_ndw;
|
||||
gcnt += literal_ndw >> 1;
|
||||
|
||||
cf->push_back(g);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_parser::parse_fetch_clause(cf_node* cf) {
|
||||
int r;
|
||||
unsigned i = cf->bc.addr << 1, cnt = cf->bc.count + 1;
|
||||
|
||||
vvec grad_v, grad_h;
|
||||
|
||||
while (cnt--) {
|
||||
fetch_node *n = sh->create_fetch();
|
||||
cf->push_back(n);
|
||||
if ((r = dec->decode_fetch(i, n->bc)))
|
||||
return r;
|
||||
|
||||
unsigned flags = n->bc.op_ptr->flags;
|
||||
|
||||
unsigned vtx = flags & FF_VTX;
|
||||
unsigned num_src = vtx ? ctx.vtx_src_num : 4;
|
||||
|
||||
n->dst.resize(4);
|
||||
|
||||
if (flags & (FF_SETGRAD | FF_USEGRAD | FF_GETGRAD)) {
|
||||
sh->uses_gradients = true;
|
||||
}
|
||||
|
||||
if (flags & FF_SETGRAD) {
|
||||
|
||||
vvec *grad = NULL;
|
||||
|
||||
switch (n->bc.op) {
|
||||
case FETCH_OP_SET_GRADIENTS_V:
|
||||
grad = &grad_v;
|
||||
break;
|
||||
case FETCH_OP_SET_GRADIENTS_H:
|
||||
grad = &grad_h;
|
||||
break;
|
||||
default:
|
||||
assert(!"unexpected SET_GRAD instruction");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (grad->empty())
|
||||
grad->resize(4);
|
||||
|
||||
for(unsigned s = 0; s < 4; ++s) {
|
||||
unsigned sw = n->bc.src_sel[s];
|
||||
if (sw <= SEL_W)
|
||||
(*grad)[s] = sh->get_gpr_value(true, n->bc.src_gpr,
|
||||
sw, false);
|
||||
else if (sw == SEL_0)
|
||||
(*grad)[s] = sh->get_const_value(0.0f);
|
||||
else if (sw == SEL_1)
|
||||
(*grad)[s] = sh->get_const_value(1.0f);
|
||||
}
|
||||
} else {
|
||||
|
||||
if (flags & FF_USEGRAD) {
|
||||
n->src.resize(12);
|
||||
std::copy(grad_v.begin(), grad_v.end(), n->src.begin() + 4);
|
||||
std::copy(grad_h.begin(), grad_h.end(), n->src.begin() + 8);
|
||||
} else {
|
||||
n->src.resize(4);
|
||||
}
|
||||
|
||||
for(int s = 0; s < 4; ++s) {
|
||||
if (n->bc.dst_sel[s] != SEL_MASK)
|
||||
n->dst[s] = sh->get_gpr_value(false, n->bc.dst_gpr, s, false);
|
||||
// NOTE: it doesn't matter here which components of the result we
|
||||
// are using, but original n->bc.dst_sel should be taken into
|
||||
// account when building the bytecode
|
||||
}
|
||||
for(unsigned s = 0; s < num_src; ++s) {
|
||||
if (n->bc.src_sel[s] <= SEL_W)
|
||||
n->src[s] = sh->get_gpr_value(true, n->bc.src_gpr,
|
||||
n->bc.src_sel[s], false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_parser::prepare_ir() {
|
||||
|
||||
for(id_cf_map::iterator I = cf_map.begin(), E = cf_map.end(); I != E; ++I) {
|
||||
cf_node *c = *I;
|
||||
|
||||
if (!c)
|
||||
continue;
|
||||
|
||||
unsigned flags = c->bc.op_ptr->flags;
|
||||
|
||||
if (flags & CF_LOOP_START) {
|
||||
prepare_loop(c);
|
||||
} else if (c->bc.op == CF_OP_JUMP) {
|
||||
prepare_if(c);
|
||||
} else if (c->bc.op == CF_OP_LOOP_END) {
|
||||
loop_stack.pop();
|
||||
} else if (c->bc.op == CF_OP_LOOP_CONTINUE) {
|
||||
assert(!loop_stack.empty());
|
||||
repeat_node *rep = sh->create_repeat(loop_stack.top());
|
||||
if (c->parent->first != c)
|
||||
rep->move(c->parent->first, c);
|
||||
c->replace_with(rep);
|
||||
sh->simplify_dep_rep(rep);
|
||||
} else if (c->bc.op == CF_OP_LOOP_BREAK) {
|
||||
assert(!loop_stack.empty());
|
||||
depart_node *dep = sh->create_depart(loop_stack.top());
|
||||
if (c->parent->first != c)
|
||||
dep->move(c->parent->first, c);
|
||||
c->replace_with(dep);
|
||||
sh->simplify_dep_rep(dep);
|
||||
} else if (flags & CF_ALU && ctx.is_cayman()) {
|
||||
// postprocess cayman's 3-slot instructions (ex-trans-only)
|
||||
// FIXME it shouldn't be required with proper handling
|
||||
prepare_alu_clause(c);
|
||||
} else if (flags & CF_EXP) {
|
||||
|
||||
// unroll burst exports
|
||||
|
||||
assert(c->bc.op == CF_OP_EXPORT || c->bc.op == CF_OP_EXPORT_DONE);
|
||||
|
||||
c->bc.set_op(CF_OP_EXPORT);
|
||||
|
||||
unsigned burst_count = c->bc.burst_count;
|
||||
unsigned eop = c->bc.end_of_program;
|
||||
|
||||
c->bc.end_of_program = 0;
|
||||
c->bc.burst_count = 0;
|
||||
|
||||
do {
|
||||
c->src.resize(4);
|
||||
|
||||
for(int s = 0; s < 4; ++s) {
|
||||
switch (c->bc.sel[s]) {
|
||||
case SEL_0:
|
||||
c->src[s] = sh->get_const_value(0.0f);
|
||||
break;
|
||||
case SEL_1:
|
||||
c->src[s] = sh->get_const_value(1.0f);
|
||||
break;
|
||||
case SEL_MASK:
|
||||
break;
|
||||
default:
|
||||
if (c->bc.sel[s] <= SEL_W)
|
||||
c->src[s] = sh->get_gpr_value(true, c->bc.rw_gpr,
|
||||
c->bc.sel[s], false);
|
||||
else
|
||||
assert(!"invalid src_sel for export");
|
||||
}
|
||||
}
|
||||
|
||||
if (!burst_count--)
|
||||
break;
|
||||
|
||||
cf_node *cf_next = sh->create_cf();
|
||||
cf_next->bc = c->bc;
|
||||
++cf_next->bc.rw_gpr;
|
||||
++cf_next->bc.array_base;
|
||||
|
||||
c->insert_after(cf_next);
|
||||
c = cf_next;
|
||||
|
||||
} while (1);
|
||||
|
||||
c->bc.end_of_program = eop;
|
||||
} else if (flags & (CF_STRM | CF_RAT)) {
|
||||
|
||||
unsigned burst_count = c->bc.burst_count;
|
||||
unsigned eop = c->bc.end_of_program;
|
||||
|
||||
c->bc.end_of_program = 0;
|
||||
c->bc.burst_count = 0;
|
||||
|
||||
do {
|
||||
|
||||
c->src.resize(4);
|
||||
|
||||
for(int s = 0; s < 4; ++s) {
|
||||
if (c->bc.comp_mask & (1 << s))
|
||||
c->src[s] =
|
||||
sh->get_gpr_value(true, c->bc.rw_gpr, s, false);
|
||||
}
|
||||
|
||||
if ((flags & CF_RAT) && (c->bc.type & 1)) { // indexed write
|
||||
c->src.resize(8);
|
||||
for(int s = 0; s < 3; ++s) {
|
||||
c->src[4 + s] =
|
||||
sh->get_gpr_value(true, c->bc.index_gpr, s, false);
|
||||
}
|
||||
|
||||
// FIXME probably we can relax it a bit
|
||||
c->flags |= NF_DONT_HOIST | NF_DONT_MOVE;
|
||||
}
|
||||
|
||||
if (!burst_count--)
|
||||
break;
|
||||
|
||||
cf_node *cf_next = sh->create_cf();
|
||||
cf_next->bc = c->bc;
|
||||
++cf_next->bc.rw_gpr;
|
||||
|
||||
// FIXME is it correct?
|
||||
cf_next->bc.array_base += cf_next->bc.elem_size + 1;
|
||||
|
||||
c->insert_after(cf_next);
|
||||
c = cf_next;
|
||||
} while (1);
|
||||
|
||||
c->bc.end_of_program = eop;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
assert(loop_stack.empty());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_parser::prepare_loop(cf_node* c) {
|
||||
|
||||
cf_node *end = cf_map[c->bc.addr - 1];
|
||||
assert(end->bc.op == CF_OP_LOOP_END);
|
||||
assert(c->parent == end->parent);
|
||||
|
||||
region_node *reg = sh->create_region();
|
||||
repeat_node *rep = sh->create_repeat(reg);
|
||||
|
||||
reg->push_back(rep);
|
||||
c->insert_before(reg);
|
||||
rep->move(c, end->next);
|
||||
|
||||
loop_stack.push(reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_parser::prepare_if(cf_node* c) {
|
||||
cf_node *c_else = NULL, *end = cf_map[c->bc.addr];
|
||||
|
||||
BCP_DUMP(
|
||||
cerr << "parsing JUMP @" << c->bc.id;
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
if (end->bc.op == CF_OP_ELSE) {
|
||||
BCP_DUMP(
|
||||
cerr << " found ELSE : ";
|
||||
dump::dump_op(end);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
c_else = end;
|
||||
end = cf_map[c_else->bc.addr];
|
||||
} else {
|
||||
BCP_DUMP(
|
||||
cerr << " no else\n";
|
||||
);
|
||||
|
||||
c_else = end;
|
||||
}
|
||||
|
||||
if (c_else->parent != c->parent)
|
||||
c_else = NULL;
|
||||
|
||||
if (end->parent != c->parent)
|
||||
end = NULL;
|
||||
|
||||
region_node *reg = sh->create_region();
|
||||
|
||||
depart_node *dep2 = sh->create_depart(reg);
|
||||
depart_node *dep = sh->create_depart(reg);
|
||||
if_node *n_if = sh->create_if();
|
||||
|
||||
c->insert_before(reg);
|
||||
|
||||
if (c_else != end)
|
||||
dep->move(c_else, end);
|
||||
dep2->move(c, end);
|
||||
|
||||
reg->push_back(dep);
|
||||
dep->push_front(n_if);
|
||||
n_if->push_back(dep2);
|
||||
|
||||
n_if->cond = sh->get_special_value(SV_EXEC_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bc_parser::prepare_alu_clause(cf_node* c) {
|
||||
|
||||
// loop over alu groups
|
||||
for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
|
||||
assert(I->subtype == NST_ALU_GROUP);
|
||||
|
||||
alu_group_node *g = static_cast<alu_group_node*>(*I);
|
||||
|
||||
// loop over alu_group items
|
||||
for (node_iterator I2 = g->begin(), E2 = g->end(); I2 != E2; ++I2) {
|
||||
if (I2->subtype != NST_ALU_PACKED_INST)
|
||||
continue;
|
||||
|
||||
alu_packed_node *p = static_cast<alu_packed_node*>(*I2);
|
||||
|
||||
if (p->count() == 3) {
|
||||
// cayman's scalar instruction that takes 3 or 4 slots
|
||||
|
||||
// FIXME for simplicity we'll always add 4th slot,
|
||||
// but probably we might want to always remove 4th slot and make
|
||||
// sure that regalloc won't choose w component for dst
|
||||
|
||||
alu_node *f = static_cast<alu_node*>(p->first);
|
||||
alu_node *a = sh->create_alu();
|
||||
a->src = f->src;
|
||||
a->dst.resize(f->dst.size());
|
||||
a->bc = f->bc;
|
||||
a->bc.slot = SLOT_W;
|
||||
p->push_back(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include "sb_bc.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
unsigned sb_context::dump_pass = 0;
|
||||
unsigned sb_context::dump_stat = 0;
|
||||
unsigned sb_context::dry_run = 0;
|
||||
|
||||
unsigned sb_context::dskip_start = 0;
|
||||
unsigned sb_context::dskip_end = 0;
|
||||
unsigned sb_context::dskip_mode = 0;
|
||||
|
||||
int sb_context::init(r600_isa *isa, sb_hw_chip chip, sb_hw_class cclass) {
|
||||
if (chip == HW_CHIP_UNKNOWN || cclass == HW_CLASS_UNKNOWN)
|
||||
return -1;
|
||||
|
||||
this->isa = isa;
|
||||
|
||||
hw_chip = chip;
|
||||
hw_class = cclass;
|
||||
|
||||
alu_temp_gprs = 4;
|
||||
|
||||
max_fetch = is_r600() ? 8 : 16;
|
||||
|
||||
has_trans = !is_cayman();
|
||||
|
||||
vtx_src_num = 1;
|
||||
|
||||
num_slots = has_trans ? 5 : 4;
|
||||
|
||||
uses_mova_gpr = is_r600() && chip != HW_CHIP_RV670;
|
||||
|
||||
switch (chip) {
|
||||
case HW_CHIP_RV610:
|
||||
case HW_CHIP_RS780:
|
||||
case HW_CHIP_RV620:
|
||||
case HW_CHIP_RS880:
|
||||
|
||||
case HW_CHIP_RV630:
|
||||
case HW_CHIP_RV635:
|
||||
case HW_CHIP_RV730:
|
||||
case HW_CHIP_RV710:
|
||||
case HW_CHIP_PALM:
|
||||
case HW_CHIP_CEDAR:
|
||||
stack_entry_size = 8;
|
||||
break;
|
||||
default:
|
||||
stack_entry_size = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#define SB_RA_SCHED_CHECK DEBUG
|
||||
|
||||
extern "C" {
|
||||
#include "os/os_time.h"
|
||||
#include "r600_pipe.h"
|
||||
#include "r600_shader.h"
|
||||
|
||||
#include "sb_public.h"
|
||||
}
|
||||
|
||||
#include <stack>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
#include "sb_bc.h"
|
||||
#include "sb_shader.h"
|
||||
#include "sb_pass.h"
|
||||
#include "sb_sched.h"
|
||||
|
||||
using namespace r600_sb;
|
||||
|
||||
using std::cerr;
|
||||
|
||||
static sb_hw_class translate_chip_class(enum chip_class cc);
|
||||
static sb_hw_chip translate_chip(enum radeon_family rf);
|
||||
|
||||
sb_context *r600_sb_context_create(struct r600_context *rctx) {
|
||||
|
||||
sb_context *sctx = new sb_context();
|
||||
|
||||
if (sctx->init(rctx->isa, translate_chip(rctx->family),
|
||||
translate_chip_class(rctx->chip_class))) {
|
||||
delete sctx;
|
||||
sctx = NULL;
|
||||
}
|
||||
|
||||
unsigned df = rctx->screen->debug_flags;
|
||||
|
||||
sb_context::dump_pass = df & DBG_SB_DUMP;
|
||||
sb_context::dump_stat = df & DBG_SB_STAT;
|
||||
sb_context::dry_run = df & DBG_SB_DRY_RUN;
|
||||
|
||||
sb_context::dskip_start = debug_get_num_option("R600_SB_DSKIP_START", 0);
|
||||
sb_context::dskip_end = debug_get_num_option("R600_SB_DSKIP_END", 0);
|
||||
sb_context::dskip_mode = debug_get_num_option("R600_SB_DSKIP_MODE", 0);
|
||||
|
||||
return sctx;
|
||||
}
|
||||
|
||||
void r600_sb_context_destroy(void * sctx) {
|
||||
if (sctx)
|
||||
delete (sb_context*)sctx;
|
||||
}
|
||||
|
||||
int r600_sb_bytecode_process(struct r600_context *rctx,
|
||||
struct r600_bytecode *bc,
|
||||
struct r600_shader *pshader,
|
||||
int dump_source_bytecode,
|
||||
int optimize) {
|
||||
int r = 0;
|
||||
unsigned shader_id = bc->debug_id;
|
||||
|
||||
sb_context *ctx = (sb_context *)rctx->sb_context;
|
||||
if (!ctx) {
|
||||
rctx->sb_context = ctx = r600_sb_context_create(rctx);
|
||||
}
|
||||
|
||||
int64_t time_start = 0;
|
||||
if (sb_context::dump_stat) {
|
||||
time_start = os_time_get_nano();
|
||||
}
|
||||
|
||||
/* skip some shaders (use shaders from default backend)
|
||||
* dskip_start - range start, dskip_end - range_end,
|
||||
* e.g. start = 5, end = 6 means shaders 5 & 6
|
||||
*
|
||||
* dskip_mode == 0 - disabled,
|
||||
* dskip_mode == 1 - don't process the shaders from the [start;end] range
|
||||
* dskip_mode == 2 - process only the shaders from the range
|
||||
*/
|
||||
if (sb_context::dskip_mode) {
|
||||
if ((sb_context::dskip_start <= shader_id &&
|
||||
shader_id <= sb_context::dskip_end) ==
|
||||
(sb_context::dskip_mode == 1)) {
|
||||
cerr << "sb: skipped shader " << shader_id << " : " << "["
|
||||
<< sb_context::dskip_start << "; "
|
||||
<< sb_context::dskip_end << "] mode "
|
||||
<< sb_context::dskip_mode << "\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
SB_DUMP_STAT( cerr << "\nsb: shader " << shader_id << "\n"; );
|
||||
|
||||
bc_parser parser(*ctx, bc, pshader, dump_source_bytecode, optimize);
|
||||
|
||||
if ((r = parser.parse())) {
|
||||
assert(0);
|
||||
return r;
|
||||
}
|
||||
|
||||
shader *sh = parser.get_shader();
|
||||
SB_DUMP_PASS( cerr << "\n\n###### after parse\n"; sh->dump_ir(); );
|
||||
|
||||
if (!optimize) {
|
||||
delete sh;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SB_RUN_PASS(n, dump) \
|
||||
do { \
|
||||
r = n(*sh).run(); \
|
||||
if (dump) { \
|
||||
SB_DUMP_PASS( cerr << "\n\n###### after " << #n << "\n"; sh->dump_ir();); \
|
||||
} \
|
||||
assert(!r); \
|
||||
} while (0)
|
||||
|
||||
SB_RUN_PASS(ssa_prepare, 0);
|
||||
SB_RUN_PASS(ssa_rename, 1);
|
||||
|
||||
if (sh->has_alu_predication)
|
||||
SB_RUN_PASS(psi_ops, 1);
|
||||
|
||||
SB_RUN_PASS(liveness, 0);
|
||||
SB_RUN_PASS(dce_cleanup, 0);
|
||||
SB_RUN_PASS(def_use, 0);
|
||||
|
||||
sh->set_undef(sh->root->live_before);
|
||||
|
||||
SB_RUN_PASS(peephole, 1);
|
||||
SB_RUN_PASS(if_conversion, 1);
|
||||
|
||||
SB_RUN_PASS(def_use, 0);
|
||||
|
||||
SB_RUN_PASS(gvn, 1);
|
||||
|
||||
SB_RUN_PASS(liveness, 0);
|
||||
SB_RUN_PASS(dce_cleanup, 1);
|
||||
SB_RUN_PASS(def_use, 0);
|
||||
|
||||
SB_RUN_PASS(liveness, 0);
|
||||
SB_RUN_PASS(dce_cleanup, 0);
|
||||
|
||||
SB_RUN_PASS(ra_split, 0);
|
||||
SB_RUN_PASS(def_use, 0);
|
||||
|
||||
// create 'basic blocks'. it's not like we build CFG, they are just
|
||||
// container nodes in the correct locations for code placement
|
||||
sh->create_bbs();
|
||||
|
||||
SB_RUN_PASS(gcm, 0);
|
||||
|
||||
sh->compute_interferences = true;
|
||||
SB_RUN_PASS(liveness, 0);
|
||||
|
||||
SB_RUN_PASS(ra_coalesce, 1);
|
||||
SB_RUN_PASS(ra_init, 1);
|
||||
|
||||
SB_RUN_PASS(post_scheduler, 1);
|
||||
|
||||
sh->expand_bbs();
|
||||
|
||||
#if SB_RA_SCHED_CHECK
|
||||
// check code correctness after regalloc/scheduler
|
||||
SB_RUN_PASS(ra_checker, 0);
|
||||
#endif
|
||||
|
||||
SB_RUN_PASS(bc_finalizer, 0);
|
||||
|
||||
sh->optimized = true;
|
||||
|
||||
bc_builder builder(*sh);
|
||||
|
||||
if ((r = builder.build())) {
|
||||
assert(0);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!sb_context::dry_run) {
|
||||
bytecode &nbc = builder.get_bytecode();
|
||||
|
||||
free(bc->bytecode);
|
||||
bc->ndw = nbc.ndw();
|
||||
bc->bytecode = (uint32_t*) malloc(bc->ndw << 2);
|
||||
nbc.write_data(bc->bytecode);
|
||||
|
||||
bc->ngpr = sh->ngpr;
|
||||
bc->nstack = sh->nstack;
|
||||
} else {
|
||||
SB_DUMP_STAT( cerr << "SB_USE_NEW_BYTECODE is not enabled\n"; );
|
||||
}
|
||||
|
||||
delete sh;
|
||||
|
||||
if (sb_context::dump_stat) {
|
||||
int64_t t = os_time_get_nano() - time_start;
|
||||
|
||||
cerr << "sb: processing shader " << shader_id << " done ( "
|
||||
<< ((double)t)/1000000.0 << " ms ).\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sb_hw_chip translate_chip(enum radeon_family rf) {
|
||||
switch (rf) {
|
||||
|
||||
#define TRANSLATE_CHIP(c) case CHIP_##c: return HW_CHIP_##c
|
||||
TRANSLATE_CHIP(R600);
|
||||
TRANSLATE_CHIP(RV610);
|
||||
TRANSLATE_CHIP(RV630);
|
||||
TRANSLATE_CHIP(RV670);
|
||||
TRANSLATE_CHIP(RV620);
|
||||
TRANSLATE_CHIP(RV635);
|
||||
TRANSLATE_CHIP(RS780);
|
||||
TRANSLATE_CHIP(RS880);
|
||||
TRANSLATE_CHIP(RV770);
|
||||
TRANSLATE_CHIP(RV730);
|
||||
TRANSLATE_CHIP(RV710);
|
||||
TRANSLATE_CHIP(RV740);
|
||||
TRANSLATE_CHIP(CEDAR);
|
||||
TRANSLATE_CHIP(REDWOOD);
|
||||
TRANSLATE_CHIP(JUNIPER);
|
||||
TRANSLATE_CHIP(CYPRESS);
|
||||
TRANSLATE_CHIP(HEMLOCK);
|
||||
TRANSLATE_CHIP(PALM);
|
||||
TRANSLATE_CHIP(SUMO);
|
||||
TRANSLATE_CHIP(SUMO2);
|
||||
TRANSLATE_CHIP(BARTS);
|
||||
TRANSLATE_CHIP(TURKS);
|
||||
TRANSLATE_CHIP(CAICOS);
|
||||
TRANSLATE_CHIP(CAYMAN);
|
||||
#undef TRANSLATE_CHIP
|
||||
|
||||
default:
|
||||
assert(!"unknown chip");
|
||||
return HW_CHIP_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static sb_hw_class translate_chip_class(enum chip_class cc) {
|
||||
switch(cc) {
|
||||
case R600: return HW_CLASS_R600;
|
||||
case R700: return HW_CLASS_R700;
|
||||
case EVERGREEN: return HW_CLASS_EVERGREEN;
|
||||
case CAYMAN: return HW_CLASS_CAYMAN;
|
||||
|
||||
default:
|
||||
assert(!"unknown chip class");
|
||||
return HW_CLASS_UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include "sb_shader.h"
|
||||
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
bool dce_cleanup::visit(node& n, bool enter) {
|
||||
if (enter) {
|
||||
} else {
|
||||
if (n.flags & NF_DEAD)
|
||||
n.remove();
|
||||
else
|
||||
cleanup_dst(n);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dce_cleanup::visit(alu_group_node& n, bool enter) {
|
||||
if (enter) {
|
||||
} else {
|
||||
n.expand();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dce_cleanup::visit(cf_node& n, bool enter) {
|
||||
if (enter) {
|
||||
if (n.flags & NF_DEAD)
|
||||
n.remove();
|
||||
else
|
||||
cleanup_dst(n);
|
||||
} else {
|
||||
if (n.bc.op_ptr->flags & (CF_CLAUSE | CF_BRANCH | CF_LOOP))
|
||||
n.expand();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dce_cleanup::visit(alu_node& n, bool enter) {
|
||||
if (enter) {
|
||||
} else {
|
||||
if (n.flags & NF_DEAD)
|
||||
n.remove();
|
||||
else
|
||||
cleanup_dst(n);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dce_cleanup::visit(alu_packed_node& n, bool enter) {
|
||||
if (enter) {
|
||||
} else {
|
||||
if (n.flags & NF_DEAD)
|
||||
n.remove();
|
||||
else
|
||||
cleanup_dst(n);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dce_cleanup::visit(fetch_node& n, bool enter) {
|
||||
if (enter) {
|
||||
} else {
|
||||
if (n.flags & NF_DEAD)
|
||||
n.remove();
|
||||
else
|
||||
cleanup_dst(n);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dce_cleanup::visit(region_node& n, bool enter) {
|
||||
if (enter) {
|
||||
if (n.loop_phi)
|
||||
run_on(*n.loop_phi);
|
||||
} else {
|
||||
if (n.phi)
|
||||
run_on(*n.phi);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void dce_cleanup::cleanup_dst(node& n) {
|
||||
cleanup_dst_vec(n.dst);
|
||||
}
|
||||
|
||||
bool dce_cleanup::visit(container_node& n, bool enter) {
|
||||
if (enter) {
|
||||
cleanup_dst(n);
|
||||
} else {
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void dce_cleanup::cleanup_dst_vec(vvec& vv) {
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
value* &v = *I;
|
||||
if (!v)
|
||||
continue;
|
||||
|
||||
if (v->is_dead())
|
||||
v = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include "sb_shader.h"
|
||||
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
int def_use::run() {
|
||||
run_on(sh.root, true);
|
||||
run_on(sh.root, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void def_use::process_phi(container_node *c, bool defs, bool uses) {
|
||||
|
||||
for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
if (uses)
|
||||
process_uses(n);
|
||||
if (defs)
|
||||
process_defs(n, n->dst, false);
|
||||
}
|
||||
}
|
||||
|
||||
void def_use::run_on(node* n, bool defs) {
|
||||
|
||||
bool is_region = (n->type == NT_REGION);
|
||||
bool is_op = (n->type == NT_OP || n->type == NT_IF);
|
||||
|
||||
if (is_op) {
|
||||
|
||||
if (0) {
|
||||
cerr << "def_use processing op ";
|
||||
dump::dump_op(n);
|
||||
cerr << "\n";
|
||||
}
|
||||
|
||||
if (defs)
|
||||
process_defs(n, n->dst, false);
|
||||
else
|
||||
process_uses(n);
|
||||
} else if (is_region & defs) {
|
||||
region_node *r = static_cast<region_node*>(n);
|
||||
if (r->loop_phi)
|
||||
process_phi(r->loop_phi, true, false);
|
||||
}
|
||||
|
||||
if (n->is_container() && n->subtype != NST_ALU_PACKED_INST) {
|
||||
container_node *c = static_cast<container_node*>(n);
|
||||
for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
|
||||
run_on(*I, defs);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_region) {
|
||||
region_node *r = static_cast<region_node*>(n);
|
||||
if (r->phi)
|
||||
process_phi(r->phi, defs, !defs);
|
||||
if (r->loop_phi && !defs)
|
||||
process_phi(r->loop_phi, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
void def_use::process_defs(node *n, vvec &vv, bool arr_def) {
|
||||
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (!v)
|
||||
continue;
|
||||
|
||||
if (arr_def)
|
||||
v->adef = n;
|
||||
else
|
||||
v->def = n;
|
||||
|
||||
v->delete_uses();
|
||||
|
||||
if (v->is_rel()) {
|
||||
process_defs(n, v->mdef, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void def_use::process_uses(node* n) {
|
||||
unsigned k = 0;
|
||||
|
||||
for (vvec::iterator I = n->src.begin(), E = n->src.end(); I != E;
|
||||
++I, ++k) {
|
||||
value *v = *I;
|
||||
if (!v || v->is_readonly())
|
||||
continue;
|
||||
|
||||
if (v->is_rel()) {
|
||||
if (!v->rel->is_readonly())
|
||||
v->rel->add_use(n, UK_SRC_REL, k);
|
||||
|
||||
unsigned k2 = 0;
|
||||
for (vvec::iterator I = v->muse.begin(), E = v->muse.end();
|
||||
I != E; ++I, ++k2) {
|
||||
value *v = *I;
|
||||
if (!v)
|
||||
continue;
|
||||
|
||||
v->add_use(n, UK_MAYUSE, k2);
|
||||
}
|
||||
} else
|
||||
v->add_use(n, UK_SRC, k);
|
||||
}
|
||||
|
||||
k = 0;
|
||||
for (vvec::iterator I = n->dst.begin(), E = n->dst.end(); I != E;
|
||||
++I, ++k) {
|
||||
value *v = *I;
|
||||
if (!v || !v->is_rel())
|
||||
continue;
|
||||
|
||||
if (!v->rel->is_readonly())
|
||||
v->rel->add_use(n, UK_DST_REL, k);
|
||||
unsigned k2 = 0;
|
||||
for (vvec::iterator I = v->muse.begin(), E = v->muse.end();
|
||||
I != E; ++I, ++k2) {
|
||||
value *v = *I;
|
||||
if (!v)
|
||||
continue;
|
||||
|
||||
v->add_use(n, UK_MAYDEF, k2);
|
||||
}
|
||||
}
|
||||
|
||||
if (n->pred)
|
||||
n->pred->add_use(n, UK_PRED, 0);
|
||||
|
||||
if (n->type == NT_IF) {
|
||||
if_node *i = static_cast<if_node*>(n);
|
||||
if (i->cond)
|
||||
i->cond->add_use(i, UK_COND, 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "sb_shader.h"
|
||||
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
bool dump::visit(node& n, bool enter) {
|
||||
if (enter) {
|
||||
indent();
|
||||
dump_flags(n);
|
||||
|
||||
switch (n.subtype) {
|
||||
case NST_PHI:
|
||||
dump_op(n, "* phi");
|
||||
break;
|
||||
case NST_PSI:
|
||||
dump_op(n, "* psi");
|
||||
break;
|
||||
case NST_COPY:
|
||||
dump_op(n, "* copy");
|
||||
break;
|
||||
default:
|
||||
assert(!"invalid node subtype");
|
||||
break;
|
||||
}
|
||||
cerr << "\n";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dump::visit(container_node& n, bool enter) {
|
||||
if (enter) {
|
||||
if (!n.empty()) {
|
||||
indent();
|
||||
dump_flags(n);
|
||||
cerr << "{ ";
|
||||
if (!n.dst.empty()) {
|
||||
cerr << " preloaded inputs [";
|
||||
dump_vec(n.dst);
|
||||
cerr << "] ";
|
||||
}
|
||||
dump_live_values(n, true);
|
||||
}
|
||||
++level;
|
||||
} else {
|
||||
--level;
|
||||
if (!n.empty()) {
|
||||
indent();
|
||||
cerr << "} ";
|
||||
if (!n.src.empty()) {
|
||||
cerr << " results [";
|
||||
dump_vec(n.src);
|
||||
cerr << "] ";
|
||||
}
|
||||
dump_live_values(n, false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dump::visit(bb_node& n, bool enter) {
|
||||
if (enter) {
|
||||
indent();
|
||||
dump_flags(n);
|
||||
cerr << "{ BB_" << n.id << " loop_level = " << n.loop_level << " ";
|
||||
dump_live_values(n, true);
|
||||
++level;
|
||||
} else {
|
||||
--level;
|
||||
indent();
|
||||
cerr << "} end BB_" << n.id << " ";
|
||||
dump_live_values(n, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dump::visit(alu_group_node& n, bool enter) {
|
||||
if (enter) {
|
||||
indent();
|
||||
dump_flags(n);
|
||||
cerr << "[ ";
|
||||
dump_live_values(n, true);
|
||||
|
||||
++level;
|
||||
} else {
|
||||
--level;
|
||||
|
||||
indent();
|
||||
cerr << "] ";
|
||||
dump_live_values(n, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dump::visit(cf_node& n, bool enter) {
|
||||
if (enter) {
|
||||
indent();
|
||||
dump_flags(n);
|
||||
dump_op(n, n.bc.op_ptr->name);
|
||||
|
||||
if (n.bc.op_ptr->flags & CF_BRANCH) {
|
||||
cerr << " @" << (n.bc.addr << 1);
|
||||
}
|
||||
|
||||
dump_common(n);
|
||||
cerr << "\n";
|
||||
|
||||
if (!n.empty()) {
|
||||
indent();
|
||||
cerr << "< ";
|
||||
dump_live_values(n, true);
|
||||
}
|
||||
|
||||
++level;
|
||||
} else {
|
||||
--level;
|
||||
if (!n.empty()) {
|
||||
indent();
|
||||
cerr << "> ";
|
||||
dump_live_values(n, false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dump::visit(alu_node& n, bool enter) {
|
||||
if (enter) {
|
||||
indent();
|
||||
dump_flags(n);
|
||||
dump_alu(&n);
|
||||
dump_common(n);
|
||||
cerr << "\n";
|
||||
|
||||
++level;
|
||||
} else {
|
||||
--level;
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dump::visit(alu_packed_node& n, bool enter) {
|
||||
if (enter) {
|
||||
indent();
|
||||
dump_flags(n);
|
||||
dump_op(n, n.op_ptr()->name);
|
||||
cerr << " ";
|
||||
dump_live_values(n, true);
|
||||
|
||||
++level;
|
||||
} else {
|
||||
--level;
|
||||
if (!n.live_after.empty()) {
|
||||
indent();
|
||||
dump_live_values(n, false);
|
||||
}
|
||||
|
||||
}
|
||||
// proccess children only if their src/dst aren't moved to this node yet
|
||||
return n.src.empty();
|
||||
}
|
||||
|
||||
bool dump::visit(fetch_node& n, bool enter) {
|
||||
if (enter) {
|
||||
indent();
|
||||
dump_flags(n);
|
||||
dump_op(n, n.bc.op_ptr->name);
|
||||
cerr << "\n";
|
||||
|
||||
++level;
|
||||
} else {
|
||||
--level;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dump::visit(region_node& n, bool enter) {
|
||||
if (enter) {
|
||||
indent();
|
||||
dump_flags(n);
|
||||
cerr << "region #" << n.region_id << " ";
|
||||
dump_common(n);
|
||||
|
||||
if (!n.vars_defined.empty()) {
|
||||
cerr << "vars_defined: ";
|
||||
dump_set(sh, n.vars_defined);
|
||||
}
|
||||
|
||||
dump_live_values(n, true);
|
||||
|
||||
++level;
|
||||
|
||||
if (n.loop_phi)
|
||||
run_on(*n.loop_phi);
|
||||
} else {
|
||||
--level;
|
||||
|
||||
if (n.phi)
|
||||
run_on(*n.phi);
|
||||
|
||||
indent();
|
||||
dump_live_values(n, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dump::visit(repeat_node& n, bool enter) {
|
||||
if (enter) {
|
||||
indent();
|
||||
dump_flags(n);
|
||||
cerr << "repeat region #" << n.target->region_id;
|
||||
cerr << (n.empty() ? " " : " after { ");
|
||||
dump_common(n);
|
||||
cerr << " ";
|
||||
dump_live_values(n, true);
|
||||
|
||||
++level;
|
||||
} else {
|
||||
--level;
|
||||
|
||||
if (!n.empty()) {
|
||||
indent();
|
||||
cerr << "} end_repeat ";
|
||||
dump_live_values(n, false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dump::visit(depart_node& n, bool enter) {
|
||||
if (enter) {
|
||||
indent();
|
||||
dump_flags(n);
|
||||
cerr << "depart region #" << n.target->region_id;
|
||||
cerr << (n.empty() ? " " : " after { ");
|
||||
dump_common(n);
|
||||
cerr << " ";
|
||||
dump_live_values(n, true);
|
||||
|
||||
++level;
|
||||
} else {
|
||||
--level;
|
||||
if (!n.empty()) {
|
||||
indent();
|
||||
cerr << "} end_depart ";
|
||||
dump_live_values(n, false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dump::visit(if_node& n, bool enter) {
|
||||
if (enter) {
|
||||
indent();
|
||||
dump_flags(n);
|
||||
cerr << "if " << *n.cond << " ";
|
||||
dump_common(n);
|
||||
cerr << " ";
|
||||
dump_live_values(n, true);
|
||||
|
||||
indent();
|
||||
cerr <<"{\n";
|
||||
|
||||
++level;
|
||||
} else {
|
||||
--level;
|
||||
indent();
|
||||
cerr << "} endif ";
|
||||
dump_live_values(n, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void dump::indent() {
|
||||
cerr << std::setw(level * 4) << "";
|
||||
}
|
||||
|
||||
void dump::dump_vec(const vvec & vv) {
|
||||
bool first = true;
|
||||
for(vvec::const_iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (!first)
|
||||
cerr << ", ";
|
||||
else
|
||||
first = false;
|
||||
|
||||
if (v) {
|
||||
cerr << *v;
|
||||
} else {
|
||||
cerr << "__";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump::dump_rels(vvec & vv) {
|
||||
for(vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
|
||||
if (!v || !v->is_rel())
|
||||
continue;
|
||||
|
||||
cerr << "\n\t\t\t\t\t";
|
||||
cerr << " rels: " << *v << " : ";
|
||||
dump_vec(v->mdef);
|
||||
cerr << " <= ";
|
||||
dump_vec(v->muse);
|
||||
}
|
||||
}
|
||||
|
||||
void dump::dump_op(node &n, const char *name) {
|
||||
|
||||
if (n.pred) {
|
||||
alu_node &a = static_cast<alu_node&>(n);
|
||||
cerr << (a.bc.pred_sel-2) << " [" << *a.pred << "] ";
|
||||
}
|
||||
|
||||
cerr << name;
|
||||
|
||||
bool has_dst = !n.dst.empty();
|
||||
|
||||
if (n.subtype == NST_CF_INST) {
|
||||
cf_node *c = static_cast<cf_node*>(&n);
|
||||
if (c->bc.op_ptr->flags & CF_EXP) {
|
||||
static const char *exp_type[] = {"PIXEL", "POS ", "PARAM"};
|
||||
cerr << " " << exp_type[c->bc.type] << " " << c->bc.array_base;
|
||||
has_dst = false;
|
||||
} else if (c->bc.op_ptr->flags & CF_STRM) {
|
||||
static const char *exp_type[] = {"WRITE", "WRITE_IND", "WRITE_ACK",
|
||||
"WRITE_IND_ACK"};
|
||||
cerr << " " << exp_type[c->bc.type] << " " << c->bc.array_base
|
||||
<< " ES:" << c->bc.elem_size;
|
||||
has_dst = false;
|
||||
}
|
||||
}
|
||||
|
||||
cerr << " ";
|
||||
|
||||
if (has_dst) {
|
||||
dump_vec(n.dst);
|
||||
cerr << ", ";
|
||||
}
|
||||
|
||||
dump_vec(n.src);
|
||||
}
|
||||
|
||||
void dump::dump_set(shader &sh, val_set& v) {
|
||||
cerr << "[";
|
||||
for(val_set::iterator I = v.begin(sh), E = v.end(sh); I != E; ++I) {
|
||||
value *val = *I;
|
||||
cerr << *val << " ";
|
||||
}
|
||||
cerr << "]";
|
||||
}
|
||||
|
||||
void dump::dump_common(node& n) {
|
||||
}
|
||||
|
||||
void dump::dump_flags(node &n) {
|
||||
if (n.flags & NF_DEAD)
|
||||
cerr << "### DEAD ";
|
||||
if (n.flags & NF_REG_CONSTRAINT)
|
||||
cerr << "R_CONS ";
|
||||
if (n.flags & NF_CHAN_CONSTRAINT)
|
||||
cerr << "CH_CONS ";
|
||||
if (n.flags & NF_ALU_4SLOT)
|
||||
cerr << "4S ";
|
||||
}
|
||||
|
||||
void dump::dump_val(value* v) {
|
||||
cerr << *v;
|
||||
}
|
||||
|
||||
void dump::dump_alu(alu_node *n) {
|
||||
|
||||
if (n->is_copy_mov())
|
||||
cerr << "(copy) ";
|
||||
|
||||
if (n->pred) {
|
||||
cerr << (n->bc.pred_sel-2) << " [" << n->pred << "] ";
|
||||
}
|
||||
|
||||
cerr << n->bc.op_ptr->name;
|
||||
|
||||
if (n->bc.omod) {
|
||||
static const char *omod_str[] = {"", "*2", "*4", "/2"};
|
||||
cerr << omod_str[n->bc.omod];
|
||||
}
|
||||
|
||||
if (n->bc.clamp) {
|
||||
cerr << "_sat";
|
||||
}
|
||||
|
||||
bool has_dst = !n->dst.empty();
|
||||
|
||||
cerr << " ";
|
||||
|
||||
if (has_dst) {
|
||||
dump_vec(n->dst);
|
||||
cerr << ", ";
|
||||
}
|
||||
|
||||
unsigned s = 0;
|
||||
for (vvec::iterator I = n->src.begin(), E = n->src.end(); I != E;
|
||||
++I, ++s) {
|
||||
|
||||
bc_alu_src &src = n->bc.src[s];
|
||||
|
||||
if (src.neg)
|
||||
cerr << "-";
|
||||
|
||||
if (src.abs)
|
||||
cerr << "|";
|
||||
|
||||
dump_val(*I);
|
||||
|
||||
if (src.abs)
|
||||
cerr << "|";
|
||||
|
||||
if (I + 1 != E)
|
||||
cerr << ", ";
|
||||
}
|
||||
|
||||
dump_rels(n->dst);
|
||||
dump_rels(n->src);
|
||||
|
||||
}
|
||||
|
||||
void dump::dump_op(node* n) {
|
||||
if (n->type == NT_IF) {
|
||||
dump_op(*n, "IF ");
|
||||
return;
|
||||
}
|
||||
|
||||
switch(n->subtype) {
|
||||
case NST_ALU_INST:
|
||||
dump_alu(static_cast<alu_node*>(n));
|
||||
break;
|
||||
case NST_FETCH_INST:
|
||||
dump_op(*n, static_cast<fetch_node*>(n)->bc.op_ptr->name);
|
||||
break;
|
||||
case NST_CF_INST:
|
||||
case NST_ALU_CLAUSE:
|
||||
case NST_TEX_CLAUSE:
|
||||
case NST_VTX_CLAUSE:
|
||||
dump_op(*n, static_cast<cf_node*>(n)->bc.op_ptr->name);
|
||||
break;
|
||||
case NST_ALU_PACKED_INST:
|
||||
dump_op(*n, static_cast<alu_packed_node*>(n)->op_ptr()->name);
|
||||
break;
|
||||
case NST_PHI:
|
||||
dump_op(*n, "PHI");
|
||||
break;
|
||||
case NST_PSI:
|
||||
dump_op(*n, "PSI");
|
||||
break;
|
||||
case NST_COPY:
|
||||
dump_op(*n, "COPY");
|
||||
break;
|
||||
default:
|
||||
dump_op(*n, "??unknown_op");
|
||||
}
|
||||
}
|
||||
|
||||
void dump::dump_op_list(container_node* c) {
|
||||
for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
|
||||
dump_op(*I);
|
||||
cerr << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void dump::dump_queue(sched_queue& q) {
|
||||
for (sched_queue::iterator I = q.begin(), E = q.end(); I != E; ++I) {
|
||||
dump_op(*I);
|
||||
cerr << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void dump::dump_live_values(container_node &n, bool before) {
|
||||
if (before) {
|
||||
if (!n.live_before.empty()) {
|
||||
cerr << "live_before: ";
|
||||
dump_set(sh, n.live_before);
|
||||
}
|
||||
} else {
|
||||
if (!n.live_after.empty()) {
|
||||
cerr << "live_after: ";
|
||||
dump_set(sh, n.live_after);
|
||||
}
|
||||
}
|
||||
cerr << "\n";
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,611 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "sb_shader.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
value* get_select_value_for_em(shader& sh, value* em) {
|
||||
if (!em->def)
|
||||
return NULL;
|
||||
|
||||
node *predset = em->def;
|
||||
if (!predset->is_pred_set())
|
||||
return NULL;
|
||||
|
||||
alu_node *s = sh.clone(static_cast<alu_node*>(predset));
|
||||
convert_predset_to_set(sh, s);
|
||||
|
||||
predset->insert_after(s);
|
||||
|
||||
value* &d0 = s->dst[0];
|
||||
d0 = sh.create_temp_value();
|
||||
d0->def = s;
|
||||
return d0;
|
||||
}
|
||||
|
||||
expr_handler::expr_handler(shader& sh) : sh(sh), vt(sh.vt) {}
|
||||
|
||||
value * expr_handler::get_const(const literal &l) {
|
||||
value *v = sh.get_const_value(l);
|
||||
if (!v->gvn_source)
|
||||
vt.add_value(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
void expr_handler::assign_source(value *dst, value *src) {
|
||||
dst->gvn_source = src->gvn_source;
|
||||
}
|
||||
|
||||
bool expr_handler::equal(value *l, value *r) {
|
||||
|
||||
assert(l != r);
|
||||
|
||||
if (l->gvalue() == r->gvalue())
|
||||
return true;
|
||||
|
||||
if (l->def && r->def)
|
||||
return defs_equal(l, r);
|
||||
|
||||
if (l->is_rel() && r->is_rel())
|
||||
return ivars_equal(l, r);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool expr_handler::ivars_equal(value* l, value* r) {
|
||||
if (l->rel->gvalue() == r->rel->gvalue()
|
||||
&& l->select == r->select) {
|
||||
|
||||
vvec &lv = l->mdef.empty() ? l->muse : l->mdef;
|
||||
vvec &rv = r->mdef.empty() ? r->muse : r->mdef;
|
||||
|
||||
// FIXME: replace this with more precise aliasing test
|
||||
return lv == rv;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool expr_handler::defs_equal(value* l, value* r) {
|
||||
|
||||
node *d1 = l->def;
|
||||
node *d2 = r->def;
|
||||
|
||||
if (d1->type != d2->type || d1->subtype != d2->subtype)
|
||||
return false;
|
||||
|
||||
if (d1->is_pred_set() || d2->is_pred_set())
|
||||
return false;
|
||||
|
||||
if (d1->type == NT_OP) {
|
||||
switch (d1->subtype) {
|
||||
case NST_ALU_INST:
|
||||
return ops_equal(
|
||||
static_cast<alu_node*>(d1),
|
||||
static_cast<alu_node*>(d2));
|
||||
// case NST_FETCH_INST: return ops_equal(static_cast<fetch_node*>(d1),
|
||||
// static_cast<fetch_node*>(d2);
|
||||
// case NST_CF_INST: return ops_equal(static_cast<cf_node*>(d1),
|
||||
// static_cast<cf_node*>(d2);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool expr_handler::try_fold(value* v) {
|
||||
assert(!v->gvn_source);
|
||||
|
||||
if (v->def)
|
||||
try_fold(v->def);
|
||||
|
||||
if (v->gvn_source)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool expr_handler::try_fold(node* n) {
|
||||
return n->fold_dispatch(this);
|
||||
}
|
||||
|
||||
bool expr_handler::fold(node& n) {
|
||||
if (n.subtype == NST_PHI) {
|
||||
|
||||
value *s = n.src[0];
|
||||
|
||||
// FIXME disabling phi folding for registers for now, otherwise we lose
|
||||
// control flow information in some cases
|
||||
// (GCM fails on tests/shaders/glsl-fs-if-nested-loop.shader_test)
|
||||
// probably control flow transformation is required to enable it
|
||||
if (s->is_sgpr())
|
||||
return false;
|
||||
|
||||
for(vvec::iterator I = n.src.begin() + 1, E = n.src.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (!s->v_equal(v))
|
||||
return false;
|
||||
}
|
||||
|
||||
assign_source(n.dst[0], s);
|
||||
} else {
|
||||
assert(n.subtype == NST_PSI);
|
||||
assert(n.src.size() >= 6);
|
||||
|
||||
value *s = n.src[2];
|
||||
assert(s->gvn_source);
|
||||
|
||||
for(vvec::iterator I = n.src.begin() + 3, E = n.src.end(); I != E; I += 3) {
|
||||
value *v = *(I+2);
|
||||
if (!s->v_equal(v))
|
||||
return false;
|
||||
}
|
||||
assign_source(n.dst[0], s);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool expr_handler::fold(container_node& n) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool expr_handler::fold_setcc(alu_node &n) {
|
||||
|
||||
// TODO
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool expr_handler::fold(alu_node& n) {
|
||||
|
||||
if (n.bc.op_ptr->flags & (AF_PRED | AF_KILL)) {
|
||||
fold_setcc(n);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (n.bc.op_ptr->src_count) {
|
||||
case 1: return fold_alu_op1(n);
|
||||
case 2: return fold_alu_op2(n);
|
||||
case 3: return fold_alu_op3(n);
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool expr_handler::fold(fetch_node& n) {
|
||||
|
||||
unsigned chan = 0;
|
||||
for (vvec::iterator I = n.dst.begin(), E = n.dst.end(); I != E; ++I) {
|
||||
value* &v = *I;
|
||||
if (v) {
|
||||
if (n.bc.dst_sel[chan] == SEL_0)
|
||||
assign_source(*I, get_const(0.0f));
|
||||
else if (n.bc.dst_sel[chan] == SEL_1)
|
||||
assign_source(*I, get_const(1.0f));
|
||||
}
|
||||
++chan;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool expr_handler::fold(cf_node& n) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void expr_handler::apply_alu_src_mod(const bc_alu &bc, unsigned src,
|
||||
literal &v) {
|
||||
const bc_alu_src &s = bc.src[src];
|
||||
|
||||
if (s.abs)
|
||||
v = fabs(v.f);
|
||||
if (s.neg)
|
||||
v = -v.f;
|
||||
}
|
||||
|
||||
void expr_handler::apply_alu_dst_mod(const bc_alu &bc, literal &v) {
|
||||
float omod_coeff[] = {2.0f, 4.0, 0.5f};
|
||||
|
||||
if (bc.omod)
|
||||
v = v.f * omod_coeff[bc.omod - 1];
|
||||
if (bc.clamp)
|
||||
v = float_clamp(v.f);
|
||||
}
|
||||
|
||||
bool expr_handler::args_equal(const vvec &l, const vvec &r) {
|
||||
|
||||
assert(l.size() == r.size());
|
||||
|
||||
int s = l.size();
|
||||
|
||||
for (int k = 0; k < s; ++k) {
|
||||
if (!l[k]->v_equal(r[k]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool expr_handler::ops_equal(const alu_node *l, const alu_node* r) {
|
||||
const bc_alu &b0 = l->bc;
|
||||
const bc_alu &b1 = r->bc;
|
||||
|
||||
if (b0.op != b1.op)
|
||||
return false;
|
||||
|
||||
unsigned src_count = b0.op_ptr->src_count;
|
||||
|
||||
if (b0.index_mode != b1.index_mode)
|
||||
return false;
|
||||
|
||||
if (b0.clamp != b1.clamp || b0.omod != b1.omod)
|
||||
return false;
|
||||
|
||||
for (unsigned s = 0; s < src_count; ++s) {
|
||||
const bc_alu_src &s0 = b0.src[s];
|
||||
const bc_alu_src &s1 = b1.src[s];
|
||||
|
||||
if (s0.abs != s1.abs || s0.neg != s1.neg)
|
||||
return false;
|
||||
}
|
||||
return args_equal(l->src, r->src);
|
||||
}
|
||||
|
||||
bool expr_handler::fold_alu_op1(alu_node& n) {
|
||||
|
||||
assert(!n.src.empty());
|
||||
if (n.src.empty())
|
||||
return false;
|
||||
|
||||
value* v0 = n.src[0];
|
||||
|
||||
assert(v0 && n.dst[0]);
|
||||
|
||||
if (!v0->is_const()) {
|
||||
if ((n.bc.op == ALU_OP1_MOV || n.bc.op == ALU_OP1_MOVA_INT ||
|
||||
n.bc.op == ALU_OP1_MOVA_GPR_INT)
|
||||
&& n.bc.clamp == 0 && n.bc.omod == 0
|
||||
&& n.bc.src[0].abs == 0 && n.bc.src[0].neg == 0) {
|
||||
assign_source(n.dst[0], v0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
literal dv, cv = v0->get_const_value();
|
||||
apply_alu_src_mod(n.bc, 0, cv);
|
||||
|
||||
switch (n.bc.op) {
|
||||
case ALU_OP1_CEIL: dv = ceil(cv.f); break;
|
||||
case ALU_OP1_COS: dv = cos(cv.f * 2.0f * M_PI); break;
|
||||
case ALU_OP1_EXP_IEEE: dv = exp2(cv.f); break;
|
||||
case ALU_OP1_FLOOR: dv = floor(cv.f); break;
|
||||
case ALU_OP1_FLT_TO_INT: dv = (int)cv.f; break; // FIXME: round modes ????
|
||||
case ALU_OP1_FLT_TO_INT_FLOOR: dv = (int32_t)floor(cv.f); break;
|
||||
case ALU_OP1_FLT_TO_INT_RPI: dv = (int32_t)floor(cv.f + 0.5f); break;
|
||||
case ALU_OP1_FLT_TO_INT_TRUNC: dv = (int32_t)trunc(cv.f); break;
|
||||
case ALU_OP1_FLT_TO_UINT: dv = (uint32_t)cv.f; break;
|
||||
case ALU_OP1_FRACT: dv = cv.f - floor(cv.f); break;
|
||||
case ALU_OP1_INT_TO_FLT: dv = (float)cv.i; break;
|
||||
case ALU_OP1_LOG_CLAMPED:
|
||||
case ALU_OP1_LOG_IEEE:
|
||||
if (cv.f != 0.0f)
|
||||
dv = log2(cv.f);
|
||||
else
|
||||
// don't fold to NAN, let the GPU handle it for now
|
||||
// (prevents degenerate LIT tests from failing)
|
||||
return false;
|
||||
break;
|
||||
case ALU_OP1_MOV: dv = cv; break;
|
||||
case ALU_OP1_MOVA_INT: dv = cv; break; // FIXME ???
|
||||
// case ALU_OP1_MOVA_FLOOR: dv = (int32_t)floor(cv.f); break;
|
||||
// case ALU_OP1_MOVA_GPR_INT:
|
||||
case ALU_OP1_NOT_INT: dv = ~cv.i; break;
|
||||
case ALU_OP1_PRED_SET_INV:
|
||||
dv = cv.f == 0.0f ? 1.0f : (cv.f == 1.0f ? 0.0f : cv.f); break;
|
||||
case ALU_OP1_PRED_SET_RESTORE: dv = cv; break;
|
||||
case ALU_OP1_RECIPSQRT_CLAMPED:
|
||||
case ALU_OP1_RECIPSQRT_FF:
|
||||
case ALU_OP1_RECIPSQRT_IEEE: dv = 1.0f / sqrt(cv.f); break;
|
||||
case ALU_OP1_RECIP_CLAMPED:
|
||||
case ALU_OP1_RECIP_FF:
|
||||
case ALU_OP1_RECIP_IEEE: dv = 1.0f / cv.f; break;
|
||||
// case ALU_OP1_RECIP_INT:
|
||||
// case ALU_OP1_RECIP_UINT:
|
||||
// case ALU_OP1_RNDNE: dv = floor(cv.f + 0.5f); break;
|
||||
case ALU_OP1_SIN: dv = sin(cv.f * 2.0f * M_PI); break;
|
||||
case ALU_OP1_SQRT_IEEE: dv = sqrt(cv.f); break;
|
||||
case ALU_OP1_TRUNC: dv = trunc(cv.f); break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
apply_alu_dst_mod(n.bc, dv);
|
||||
assign_source(n.dst[0], get_const(dv));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool expr_handler::fold_alu_op2(alu_node& n) {
|
||||
|
||||
if (n.src.size() < 2)
|
||||
return false;
|
||||
|
||||
value* v0 = n.src[0];
|
||||
value* v1 = n.src[1];
|
||||
|
||||
assert(v0 && v1 && n.dst[0]);
|
||||
|
||||
bool isc0 = v0->is_const();
|
||||
bool isc1 = v1->is_const();
|
||||
|
||||
if (!isc0 && !isc1)
|
||||
return false;
|
||||
|
||||
literal dv, cv0, cv1;
|
||||
|
||||
if (isc0) {
|
||||
cv0 = v0->get_const_value();
|
||||
apply_alu_src_mod(n.bc, 0, cv0);
|
||||
}
|
||||
|
||||
if (isc1) {
|
||||
cv1 = v1->get_const_value();
|
||||
apply_alu_src_mod(n.bc, 1, cv1);
|
||||
}
|
||||
|
||||
if (isc0 && isc1) {
|
||||
switch (n.bc.op) {
|
||||
case ALU_OP2_ADD: dv = cv0.f + cv1.f; break;
|
||||
case ALU_OP2_ADDC_UINT:
|
||||
dv = (uint32_t)(((uint64_t)cv0.u + cv1.u)>>32); break;
|
||||
case ALU_OP2_ADD_INT: dv = cv0.i + cv1.i; break;
|
||||
case ALU_OP2_AND_INT: dv = cv0.i & cv1.i; break;
|
||||
case ALU_OP2_ASHR_INT: dv = cv0.i >> (cv1.i & 0x1F); break;
|
||||
case ALU_OP2_BFM_INT:
|
||||
dv = (((1 << (cv0.i & 0x1F)) - 1) << (cv1.i & 0x1F)); break;
|
||||
case ALU_OP2_LSHL_INT: dv = cv0.i << cv1.i; break;
|
||||
case ALU_OP2_LSHR_INT: dv = cv0.u >> cv1.u; break;
|
||||
case ALU_OP2_MAX:
|
||||
case ALU_OP2_MAX_DX10: dv = cv0.f > cv1.f ? cv0.f : cv1.f; break;
|
||||
case ALU_OP2_MAX_INT: dv = cv0.i > cv1.i ? cv0.i : cv1.i; break;
|
||||
case ALU_OP2_MAX_UINT: dv = cv0.u > cv1.u ? cv0.u : cv1.u; break;
|
||||
case ALU_OP2_MIN:
|
||||
case ALU_OP2_MIN_DX10: dv = cv0.f < cv1.f ? cv0.f : cv1.f; break;
|
||||
case ALU_OP2_MIN_INT: dv = cv0.i < cv1.i ? cv0.i : cv1.i; break;
|
||||
case ALU_OP2_MIN_UINT: dv = cv0.u < cv1.u ? cv0.u : cv1.u; break;
|
||||
case ALU_OP2_MUL:
|
||||
case ALU_OP2_MUL_IEEE: dv = cv0.f * cv1.f; break;
|
||||
case ALU_OP2_MULHI_INT:
|
||||
dv = (int32_t)(((int64_t)cv0.u * cv1.u)>>32); break;
|
||||
case ALU_OP2_MULHI_UINT:
|
||||
dv = (uint32_t)(((uint64_t)cv0.u * cv1.u)>>32); break;
|
||||
case ALU_OP2_MULLO_INT:
|
||||
dv = (int32_t)(((int64_t)cv0.u * cv1.u) & 0xFFFFFFFF); break;
|
||||
case ALU_OP2_MULLO_UINT:
|
||||
dv = (uint32_t)(((uint64_t)cv0.u * cv1.u) & 0xFFFFFFFF); break;
|
||||
case ALU_OP2_OR_INT: dv = cv0.i | cv1.i; break;
|
||||
case ALU_OP2_SUB_INT: dv = cv0.i - cv1.i; break;
|
||||
case ALU_OP2_XOR_INT: dv = cv0.i ^ cv1.i; break;
|
||||
|
||||
case ALU_OP2_SETE: dv = cv0.f == cv1.f ? 1.0f : 0.0f; break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
} else { // one source is const
|
||||
|
||||
// TODO handle 1 * anything, 0 * anything, 0 + anything, etc
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
apply_alu_dst_mod(n.bc, dv);
|
||||
assign_source(n.dst[0], get_const(dv));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool expr_handler::fold_alu_op3(alu_node& n) {
|
||||
|
||||
if (n.src.size() < 3)
|
||||
return false;
|
||||
|
||||
// TODO handle CNDxx by some common path
|
||||
|
||||
value* v0 = n.src[0];
|
||||
value* v1 = n.src[1];
|
||||
value* v2 = n.src[2];
|
||||
|
||||
assert(v0 && v1 && v2 && n.dst[0]);
|
||||
|
||||
bool isc0 = v0->is_const();
|
||||
bool isc1 = v1->is_const();
|
||||
bool isc2 = v2->is_const();
|
||||
|
||||
if (!isc0 && !isc1 && !isc2)
|
||||
return false;
|
||||
|
||||
literal dv, cv0, cv1, cv2;
|
||||
|
||||
if (isc0) {
|
||||
cv0 = v0->get_const_value();
|
||||
apply_alu_src_mod(n.bc, 0, cv0);
|
||||
}
|
||||
|
||||
if (isc1) {
|
||||
cv1 = v1->get_const_value();
|
||||
apply_alu_src_mod(n.bc, 1, cv1);
|
||||
}
|
||||
|
||||
if (isc2) {
|
||||
cv2 = v2->get_const_value();
|
||||
apply_alu_src_mod(n.bc, 2, cv2);
|
||||
}
|
||||
|
||||
if (isc0 && isc1 && isc2) {
|
||||
switch (n.bc.op) {
|
||||
case ALU_OP3_MULADD: dv = cv0.f * cv1.f + cv2.f; break;
|
||||
|
||||
// TODO
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// TODO
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
apply_alu_dst_mod(n.bc, dv);
|
||||
assign_source(n.dst[0], get_const(dv));
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned invert_setcc_condition(unsigned cc, bool &swap_args) {
|
||||
unsigned ncc = 0;
|
||||
|
||||
switch (cc) {
|
||||
case AF_CC_E: ncc = AF_CC_NE; break;
|
||||
case AF_CC_NE: ncc = AF_CC_E; break;
|
||||
case AF_CC_GE: ncc = AF_CC_GT; swap_args = true; break;
|
||||
case AF_CC_GT: ncc = AF_CC_GE; swap_args = true; break;
|
||||
default:
|
||||
assert(!"unexpected condition code");
|
||||
break;
|
||||
}
|
||||
return ncc;
|
||||
}
|
||||
|
||||
unsigned get_setcc_opcode(unsigned cc, unsigned cmp_type, bool int_dst) {
|
||||
|
||||
if (int_dst && cmp_type == AF_FLOAT_CMP) {
|
||||
switch (cc) {
|
||||
case AF_CC_E: return ALU_OP2_SETE_DX10;
|
||||
case AF_CC_NE: return ALU_OP2_SETNE_DX10;
|
||||
case AF_CC_GT: return ALU_OP2_SETGT_DX10;
|
||||
case AF_CC_GE: return ALU_OP2_SETGE_DX10;
|
||||
}
|
||||
} else {
|
||||
|
||||
switch(cmp_type) {
|
||||
case AF_FLOAT_CMP: {
|
||||
switch (cc) {
|
||||
case AF_CC_E: return ALU_OP2_SETE;
|
||||
case AF_CC_NE: return ALU_OP2_SETNE;
|
||||
case AF_CC_GT: return ALU_OP2_SETGT;
|
||||
case AF_CC_GE: return ALU_OP2_SETGE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AF_INT_CMP: {
|
||||
switch (cc) {
|
||||
case AF_CC_E: return ALU_OP2_SETE_INT;
|
||||
case AF_CC_NE: return ALU_OP2_SETNE_INT;
|
||||
case AF_CC_GT: return ALU_OP2_SETGT_INT;
|
||||
case AF_CC_GE: return ALU_OP2_SETGE_INT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AF_UINT_CMP: {
|
||||
switch (cc) {
|
||||
case AF_CC_GT: return ALU_OP2_SETGT_UINT;
|
||||
case AF_CC_GE: return ALU_OP2_SETGE_UINT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(!"unexpected cc&cmp_type combination");
|
||||
return ~0u;
|
||||
}
|
||||
|
||||
unsigned get_predsetcc_opcode(unsigned cc, unsigned cmp_type) {
|
||||
|
||||
switch(cmp_type) {
|
||||
case AF_FLOAT_CMP: {
|
||||
switch (cc) {
|
||||
case AF_CC_E: return ALU_OP2_PRED_SETE;
|
||||
case AF_CC_NE: return ALU_OP2_PRED_SETNE;
|
||||
case AF_CC_GT: return ALU_OP2_PRED_SETGT;
|
||||
case AF_CC_GE: return ALU_OP2_PRED_SETGE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AF_INT_CMP: {
|
||||
switch (cc) {
|
||||
case AF_CC_E: return ALU_OP2_PRED_SETE_INT;
|
||||
case AF_CC_NE: return ALU_OP2_PRED_SETNE_INT;
|
||||
case AF_CC_GT: return ALU_OP2_PRED_SETGT_INT;
|
||||
case AF_CC_GE: return ALU_OP2_PRED_SETGE_INT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AF_UINT_CMP: {
|
||||
switch (cc) {
|
||||
case AF_CC_GT: return ALU_OP2_PRED_SETGT_UINT;
|
||||
case AF_CC_GE: return ALU_OP2_PRED_SETGE_UINT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(!"unexpected cc&cmp_type combination");
|
||||
return ~0u;
|
||||
}
|
||||
|
||||
void convert_predset_to_set(shader& sh, alu_node* a) {
|
||||
|
||||
unsigned flags = a->bc.op_ptr->flags;
|
||||
unsigned cc = flags & AF_CC_MASK;
|
||||
unsigned cmp_type = flags & AF_CMP_TYPE_MASK;
|
||||
|
||||
bool swap_args = false;
|
||||
|
||||
cc = invert_setcc_condition(cc, swap_args);
|
||||
|
||||
unsigned newop = get_setcc_opcode(cc, cmp_type, true);
|
||||
|
||||
a->dst.resize(1);
|
||||
a->bc.set_op(newop);
|
||||
|
||||
if (swap_args) {
|
||||
std::swap(a->src[0], a->src[1]);
|
||||
std::swap(a->bc.src[0], a->bc.src[1]);
|
||||
}
|
||||
|
||||
a->bc.update_exec_mask = 0;
|
||||
a->bc.update_pred = 0;
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#ifndef SB_EXPR_H_
|
||||
#define SB_EXPR_H_
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
inline float float_clamp(float v) {
|
||||
return v < 0.0f ? 0.0f : (v > 1.0f ? 1.0f : v);
|
||||
}
|
||||
|
||||
value* get_select_value_for_em(shader &sh, value *em);
|
||||
|
||||
void convert_predset_to_set(shader &sh, alu_node *a);
|
||||
unsigned invert_setcc_condition(unsigned cc, bool &swap_args);
|
||||
unsigned get_setcc_opcode(unsigned cc, unsigned cmp_type, bool int_dst);
|
||||
unsigned get_predsetcc_opcode(unsigned cc, unsigned cmp_type);
|
||||
|
||||
class expr_handler {
|
||||
|
||||
shader &sh;
|
||||
value_table &vt;
|
||||
|
||||
public:
|
||||
|
||||
expr_handler(shader &sh);
|
||||
|
||||
bool equal(value *l, value *r);
|
||||
bool defs_equal(value *l, value *r);
|
||||
bool args_equal(const vvec &l, const vvec &r);
|
||||
bool ops_equal(const alu_node *l, const alu_node *r);
|
||||
bool ivars_equal(value *l, value *r);
|
||||
|
||||
value* get_const(const literal &l);
|
||||
|
||||
bool try_fold(value *v);
|
||||
bool try_fold(node *n);
|
||||
|
||||
bool fold(node &n);
|
||||
bool fold(container_node &n);
|
||||
bool fold(alu_node &n);
|
||||
bool fold(fetch_node &n);
|
||||
bool fold(cf_node &n);
|
||||
|
||||
bool fold_setcc(alu_node &n);
|
||||
|
||||
bool fold_alu_op1(alu_node &n);
|
||||
bool fold_alu_op2(alu_node &n);
|
||||
bool fold_alu_op3(alu_node &n);
|
||||
|
||||
void apply_alu_src_mod(const bc_alu &bc, unsigned src, literal &v);
|
||||
void apply_alu_dst_mod(const bc_alu &bc, literal &v);
|
||||
|
||||
void assign_source(value *dst, value *src);
|
||||
};
|
||||
|
||||
} // namespace r600_sb
|
||||
|
||||
#endif /* SB_EXPR_H_ */
|
||||
@@ -0,0 +1,745 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#define GCM_DEBUG 0
|
||||
|
||||
#if GCM_DEBUG
|
||||
#define GCM_DUMP(a) do { a } while(0);
|
||||
#else
|
||||
#define GCM_DUMP(a)
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
#include "sb_bc.h"
|
||||
#include "sb_shader.h"
|
||||
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
int gcm::run() {
|
||||
|
||||
GCM_DUMP( cerr << "==== GCM ==== \n"; sh.dump_ir(); );
|
||||
|
||||
collect_instructions(sh.root, true);
|
||||
|
||||
init_def_count(uses, pending);
|
||||
|
||||
for (node_iterator N, I = pending.begin(), E = pending.end();
|
||||
I != E; I = N) {
|
||||
N = I;
|
||||
++N;
|
||||
node *o = *I;
|
||||
|
||||
GCM_DUMP(
|
||||
cerr << "pending : ";
|
||||
dump::dump_op(o);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
if (td_is_ready(o)) {
|
||||
|
||||
GCM_DUMP(
|
||||
cerr << " ready: ";
|
||||
dump::dump_op(o);
|
||||
cerr << "\n";
|
||||
);
|
||||
pending.remove_node(o);
|
||||
ready.push_back(o);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
sched_early(sh.root);
|
||||
|
||||
if (!pending.empty()) {
|
||||
cerr << "##### gcm_sched_early_pass: unscheduled ops:\n";
|
||||
dump::dump_op(pending.front());
|
||||
}
|
||||
|
||||
assert(pending.empty());
|
||||
|
||||
GCM_DUMP( sh.dump_ir(); );
|
||||
|
||||
GCM_DUMP( cerr << "\n\n ############## gcm late\n\n"; );
|
||||
|
||||
collect_instructions(sh.root, false);
|
||||
|
||||
init_use_count(uses, pending);
|
||||
|
||||
sched_late(sh.root);
|
||||
if (!pending.empty()) {
|
||||
cerr << "##### gcm_sched_late_pass: unscheduled ops:\n";
|
||||
dump::dump_op(pending.front());
|
||||
}
|
||||
|
||||
assert(ucs_level == 0);
|
||||
assert(pending.empty());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void gcm::collect_instructions(container_node *c, bool early_pass) {
|
||||
if (c->is_bb()) {
|
||||
|
||||
if (early_pass) {
|
||||
for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
if (n->flags & NF_DONT_MOVE) {
|
||||
op_info &o = op_map[n];
|
||||
o.top_bb = o.bottom_bb = static_cast<bb_node*>(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pending.append_from(c);
|
||||
return;
|
||||
}
|
||||
|
||||
for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
|
||||
if (I->is_container()) {
|
||||
collect_instructions(static_cast<container_node*>(*I), early_pass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gcm::sched_early(container_node *n) {
|
||||
|
||||
region_node *r =
|
||||
(n->type == NT_REGION) ? static_cast<region_node*>(n) : NULL;
|
||||
|
||||
if (r && r->loop_phi) {
|
||||
sched_early(r->loop_phi);
|
||||
}
|
||||
|
||||
for (node_iterator I = n->begin(), E = n->end(); I != E; ++I) {
|
||||
if (I->type == NT_OP) {
|
||||
node *op = *I;
|
||||
if (op->subtype == NST_PHI) {
|
||||
td_release_uses(op->dst);
|
||||
}
|
||||
} else if (I->is_container()) {
|
||||
if (I->subtype == NST_BB) {
|
||||
bb_node* bb = static_cast<bb_node*>(*I);
|
||||
td_sched_bb(bb);
|
||||
} else {
|
||||
sched_early(static_cast<container_node*>(*I));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r && r->phi) {
|
||||
sched_early(r->phi);
|
||||
}
|
||||
}
|
||||
|
||||
void gcm::td_schedule(bb_node *bb, node *n) {
|
||||
GCM_DUMP(
|
||||
cerr << "scheduling : ";
|
||||
dump::dump_op(n);
|
||||
cerr << "\n";
|
||||
);
|
||||
td_release_uses(n->dst);
|
||||
|
||||
bb->push_back(n);
|
||||
|
||||
op_map[n].top_bb = bb;
|
||||
|
||||
}
|
||||
|
||||
void gcm::td_sched_bb(bb_node* bb) {
|
||||
GCM_DUMP(
|
||||
cerr << "td scheduling BB_" << bb->id << "\n";
|
||||
);
|
||||
|
||||
while (!ready.empty()) {
|
||||
for (sq_iterator N, I = ready.begin(), E = ready.end(); I != E;
|
||||
I = N) {
|
||||
N = I; ++N;
|
||||
td_schedule(bb, *I);
|
||||
ready.erase(I);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool gcm::td_is_ready(node* n) {
|
||||
return uses[n] == 0;
|
||||
}
|
||||
|
||||
void gcm::td_release_val(value *v) {
|
||||
|
||||
GCM_DUMP(
|
||||
cerr << "td checking uses: ";
|
||||
dump::dump_val(v);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
use_info *u = v->uses;
|
||||
while (u) {
|
||||
if (u->op->parent != &pending) {
|
||||
u = u->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
GCM_DUMP(
|
||||
cerr << "td used in ";
|
||||
dump::dump_op(u->op);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
if (--uses[u->op] == 0) {
|
||||
GCM_DUMP(
|
||||
cerr << "td released : ";
|
||||
dump::dump_op(u->op);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
pending.remove_node(u->op);
|
||||
ready.push_back(u->op);
|
||||
}
|
||||
u = u->next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void gcm::td_release_uses(vvec& v) {
|
||||
for (vvec::iterator I = v.begin(), E = v.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (!v)
|
||||
continue;
|
||||
|
||||
if (v->is_rel())
|
||||
td_release_uses(v->mdef);
|
||||
else
|
||||
td_release_val(v);
|
||||
}
|
||||
}
|
||||
|
||||
void gcm::sched_late(container_node *n) {
|
||||
|
||||
bool stack_pushed = false;
|
||||
|
||||
if (n->is_depart()) {
|
||||
depart_node *d = static_cast<depart_node*>(n);
|
||||
push_uc_stack();
|
||||
stack_pushed = true;
|
||||
bu_release_phi_defs(d->target->phi, d->dep_id);
|
||||
} else if (n->is_repeat()) {
|
||||
repeat_node *r = static_cast<repeat_node*>(n);
|
||||
assert(r->target->loop_phi);
|
||||
push_uc_stack();
|
||||
stack_pushed = true;
|
||||
bu_release_phi_defs(r->target->loop_phi, r->rep_id);
|
||||
}
|
||||
|
||||
for (node_riterator I = n->rbegin(), E = n->rend(); I != E; ++I) {
|
||||
if (I->is_container()) {
|
||||
if (I->subtype == NST_BB) {
|
||||
bb_node* bb = static_cast<bb_node*>(*I);
|
||||
bu_sched_bb(bb);
|
||||
} else {
|
||||
sched_late(static_cast<container_node*>(*I));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n->type == NT_IF) {
|
||||
if_node *f = static_cast<if_node*>(n);
|
||||
if (f->cond)
|
||||
pending_defs.push_back(f->cond);
|
||||
} else if (n->type == NT_REGION) {
|
||||
region_node *r = static_cast<region_node*>(n);
|
||||
if (r->loop_phi)
|
||||
bu_release_phi_defs(r->loop_phi, 0);
|
||||
}
|
||||
|
||||
if (stack_pushed)
|
||||
pop_uc_stack();
|
||||
|
||||
}
|
||||
|
||||
void gcm::bu_sched_bb(bb_node* bb) {
|
||||
GCM_DUMP(
|
||||
cerr << "bu scheduling BB_" << bb->id << "\n";
|
||||
);
|
||||
|
||||
bu_bb = bb;
|
||||
|
||||
if (!pending_nodes.empty()) {
|
||||
GCM_DUMP(
|
||||
cerr << "pending nodes:\n";
|
||||
);
|
||||
|
||||
// TODO consider sorting the exports by array_base,
|
||||
// possibly it can improve performance
|
||||
|
||||
for (node_list::iterator I = pending_nodes.begin(),
|
||||
E = pending_nodes.end(); I != E; ++I) {
|
||||
bu_release_op(*I);
|
||||
}
|
||||
pending_nodes.clear();
|
||||
GCM_DUMP(
|
||||
cerr << "pending nodes processed...\n";
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (!pending_defs.empty()) {
|
||||
for (vvec::iterator I = pending_defs.begin(), E = pending_defs.end();
|
||||
I != E; ++I) {
|
||||
bu_release_val(*I);
|
||||
}
|
||||
pending_defs.clear();
|
||||
}
|
||||
|
||||
for (sched_queue::iterator N, I = ready_above.begin(), E = ready_above.end();
|
||||
I != E; I = N) {
|
||||
N = I;
|
||||
++N;
|
||||
node *n = *I;
|
||||
if (op_map[n].bottom_bb == bb) {
|
||||
add_ready(*I);
|
||||
ready_above.erase(I);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned cnt_ready[SQ_NUM];
|
||||
|
||||
container_node *clause = NULL;
|
||||
unsigned last_inst_type = ~0;
|
||||
unsigned last_count = 0;
|
||||
|
||||
bool s = true;
|
||||
while (s) {
|
||||
node *n;
|
||||
|
||||
s = false;
|
||||
|
||||
unsigned ready_mask = 0;
|
||||
|
||||
for (unsigned sq = SQ_CF; sq < SQ_NUM; ++sq) {
|
||||
if (!bu_ready[sq].empty() || !bu_ready_next[sq].empty())
|
||||
ready_mask |= (1 << sq);
|
||||
}
|
||||
|
||||
if (!ready_mask) {
|
||||
for (unsigned sq = SQ_CF; sq < SQ_NUM; ++sq) {
|
||||
if (!bu_ready_early[sq].empty()) {
|
||||
node *n = bu_ready_early[sq].front();
|
||||
bu_ready_early[sq].pop_front();
|
||||
bu_ready[sq].push_back(n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned sq = SQ_CF; sq < SQ_NUM; ++sq) {
|
||||
|
||||
if (!bu_ready_next[sq].empty())
|
||||
bu_ready[sq].splice(bu_ready[sq].end(), bu_ready_next[sq]);
|
||||
|
||||
cnt_ready[sq] = bu_ready[sq].size();
|
||||
|
||||
if ((sq == SQ_TEX || sq == SQ_VTX) &&
|
||||
cnt_ready[sq] < ctx.max_fetch/2 &&
|
||||
!bu_ready_next[SQ_ALU].empty()) {
|
||||
sq = SQ_ALU;
|
||||
--sq;
|
||||
continue;
|
||||
}
|
||||
|
||||
while (!bu_ready[sq].empty()) {
|
||||
|
||||
if (last_inst_type != sq) {
|
||||
clause = NULL;
|
||||
last_count = 0;
|
||||
last_inst_type = sq;
|
||||
}
|
||||
|
||||
n = bu_ready[sq].front();
|
||||
|
||||
// real count (e.g. SAMPLE_G will be expanded to 3 instructions,
|
||||
// 2 SET_GRAD_ + 1 SAMPLE_G
|
||||
unsigned ncnt = 1;
|
||||
if (n->is_fetch_inst() && n->src.size() == 12) {
|
||||
ncnt = 3;
|
||||
}
|
||||
|
||||
if ((sq == SQ_TEX || sq == SQ_VTX) &&
|
||||
((last_count >= ctx.max_fetch/2 &&
|
||||
check_alu_ready_count(24)) ||
|
||||
last_count + ncnt > ctx.max_fetch))
|
||||
break;
|
||||
else if (sq == SQ_CF && last_count > 4 &&
|
||||
check_alu_ready_count(24))
|
||||
break;
|
||||
|
||||
bu_ready[sq].pop_front();
|
||||
|
||||
if (sq != SQ_CF) {
|
||||
if (!clause) {
|
||||
clause = sh.create_clause(sq == SQ_ALU ?
|
||||
NST_ALU_CLAUSE :
|
||||
sq == SQ_TEX ? NST_TEX_CLAUSE :
|
||||
NST_VTX_CLAUSE);
|
||||
bb->push_front(clause);
|
||||
}
|
||||
} else {
|
||||
clause = bb;
|
||||
}
|
||||
|
||||
bu_schedule(clause, n);
|
||||
s = true;
|
||||
last_count += ncnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bu_bb = NULL;
|
||||
|
||||
GCM_DUMP(
|
||||
cerr << "bu finished scheduling BB_" << bb->id << "\n";
|
||||
);
|
||||
}
|
||||
|
||||
void gcm::bu_release_defs(vvec& v, bool src) {
|
||||
for (vvec::reverse_iterator I = v.rbegin(), E = v.rend(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (!v || v->is_readonly())
|
||||
continue;
|
||||
|
||||
if (v->is_rel()) {
|
||||
if (!v->rel->is_readonly())
|
||||
bu_release_val(v->rel);
|
||||
bu_release_defs(v->muse, true);
|
||||
} else if (src)
|
||||
bu_release_val(v);
|
||||
}
|
||||
}
|
||||
|
||||
void gcm::push_uc_stack() {
|
||||
GCM_DUMP(
|
||||
cerr << "pushing use count stack prev_level " << ucs_level
|
||||
<< " new level " << (ucs_level + 1) << "\n";
|
||||
);
|
||||
++ucs_level;
|
||||
if (ucs_level == nuc_stk.size()) {
|
||||
nuc_stk.resize(ucs_level + 1);
|
||||
}
|
||||
else {
|
||||
nuc_stk[ucs_level].clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool gcm::bu_is_ready(node* n) {
|
||||
nuc_map &cm = nuc_stk[ucs_level];
|
||||
nuc_map::iterator F = cm.find(n);
|
||||
unsigned uc = (F == cm.end() ? 0 : F->second);
|
||||
return uc == uses[n];
|
||||
}
|
||||
|
||||
void gcm::bu_schedule(container_node* c, node* n) {
|
||||
GCM_DUMP(
|
||||
cerr << "bu scheduling : ";
|
||||
dump::dump_op(n);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
assert(op_map[n].bottom_bb == bu_bb);
|
||||
|
||||
bu_release_defs(n->src, true);
|
||||
bu_release_defs(n->dst, false);
|
||||
|
||||
c->push_front(n);
|
||||
}
|
||||
|
||||
void gcm::dump_uc_stack() {
|
||||
cerr << "##### uc_stk start ####\n";
|
||||
for (unsigned l = 0; l <= ucs_level; ++l) {
|
||||
nuc_map &m = nuc_stk[l];
|
||||
|
||||
cerr << "nuc_stk[" << l << "] : @" << &m << "\n";
|
||||
|
||||
for (nuc_map::iterator I = m.begin(), E = m.end(); I != E; ++I) {
|
||||
cerr << " uc " << I->second << " for ";
|
||||
dump::dump_op(I->first);
|
||||
cerr << "\n";
|
||||
}
|
||||
}
|
||||
cerr << "##### uc_stk end ####\n";
|
||||
}
|
||||
|
||||
void gcm::pop_uc_stack() {
|
||||
nuc_map &pm = nuc_stk[ucs_level];
|
||||
--ucs_level;
|
||||
nuc_map &cm = nuc_stk[ucs_level];
|
||||
|
||||
GCM_DUMP(
|
||||
cerr << "merging use stack from level " << (ucs_level+1)
|
||||
<< " to " << ucs_level << "\n";
|
||||
);
|
||||
|
||||
for (nuc_map::iterator N, I = pm.begin(), E = pm.end(); I != E; ++I) {
|
||||
node *n = I->first;
|
||||
|
||||
GCM_DUMP(
|
||||
cerr << " " << cm[n] << " += " << I->second << " for ";
|
||||
dump::dump_op(n);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
unsigned uc = cm[n] += I->second;
|
||||
|
||||
if (n->parent == &pending && uc == uses[n]) {
|
||||
cm.erase(n);
|
||||
pending_nodes.push_back(n);
|
||||
GCM_DUMP(
|
||||
cerr << "pushed pending_node due to stack pop ";
|
||||
dump::dump_op(n);
|
||||
cerr << "\n";
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gcm::bu_find_best_bb(node *n, op_info &oi) {
|
||||
|
||||
GCM_DUMP(
|
||||
cerr << " find best bb : ";
|
||||
dump::dump_op(n);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
if (oi.bottom_bb)
|
||||
return;
|
||||
|
||||
// don't hoist generated copies
|
||||
if (n->flags & NF_DONT_HOIST) {
|
||||
oi.bottom_bb = bu_bb;
|
||||
return;
|
||||
}
|
||||
|
||||
bb_node* best_bb = bu_bb;
|
||||
bb_node* top_bb = oi.top_bb;
|
||||
assert(oi.top_bb && !oi.bottom_bb);
|
||||
|
||||
node *c = best_bb;
|
||||
|
||||
// FIXME top_bb may be located inside the loop so we'll never enter it
|
||||
// in the loop below, and the instruction will be incorrectly placed at the
|
||||
// beginning of the shader.
|
||||
// For now just check if top_bb's loop_level is higher than of
|
||||
// current bb and abort the search for better bb in such case,
|
||||
// but this problem may require more complete (and more expensive) fix
|
||||
if (top_bb->loop_level <= best_bb->loop_level) {
|
||||
while (c && c != top_bb) {
|
||||
|
||||
if (c->prev) {
|
||||
c = c->prev;
|
||||
} else {
|
||||
c = c->parent;
|
||||
if (!c)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c->subtype == NST_BB) {
|
||||
bb_node *bb = static_cast<bb_node*>(c);
|
||||
if (bb->loop_level < best_bb->loop_level)
|
||||
best_bb = bb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
oi.bottom_bb = best_bb;
|
||||
}
|
||||
|
||||
void gcm::add_ready(node *n) {
|
||||
sched_queue_id sq = sh.get_queue_id(n);
|
||||
if (n->flags & NF_SCHEDULE_EARLY)
|
||||
bu_ready_early[sq].push_back(n);
|
||||
else
|
||||
bu_ready_next[sq].push_back(n);
|
||||
}
|
||||
|
||||
void gcm::bu_release_op(node * n) {
|
||||
op_info &oi = op_map[n];
|
||||
|
||||
GCM_DUMP(
|
||||
cerr << " bu release op ";
|
||||
dump::dump_op(n);
|
||||
);
|
||||
|
||||
nuc_stk[ucs_level].erase(n);
|
||||
pending.remove_node(n);
|
||||
|
||||
bu_find_best_bb(n, oi);
|
||||
|
||||
if (oi.bottom_bb == bu_bb) {
|
||||
GCM_DUMP( cerr << " ready\n";);
|
||||
add_ready(n);
|
||||
} else {
|
||||
GCM_DUMP( cerr << " ready_above\n";);
|
||||
ready_above.push_back(n);
|
||||
}
|
||||
}
|
||||
|
||||
void gcm::bu_release_phi_defs(container_node* p, unsigned op)
|
||||
{
|
||||
for (node_riterator I = p->rbegin(), E = p->rend(); I != E; ++I) {
|
||||
node *o = *I;
|
||||
value *v = o->src[op];
|
||||
if (v && !v->is_readonly())
|
||||
pending_defs.push_back(o->src[op]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
unsigned gcm::get_uc_vec(vvec &vv) {
|
||||
unsigned c = 0;
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (!v)
|
||||
continue;
|
||||
|
||||
if (v->is_rel())
|
||||
c += get_uc_vec(v->mdef);
|
||||
else
|
||||
c += v->use_count();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void gcm::init_use_count(nuc_map& m, container_node &s) {
|
||||
m.clear();
|
||||
for (node_iterator I = s.begin(), E = s.end(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
unsigned uc = get_uc_vec(n->dst);
|
||||
GCM_DUMP(
|
||||
cerr << "uc " << uc << " ";
|
||||
dump::dump_op(n);
|
||||
cerr << "\n";
|
||||
);
|
||||
if (!uc) {
|
||||
pending_nodes.push_back(n);
|
||||
GCM_DUMP(
|
||||
cerr << "pushed pending_node in init ";
|
||||
dump::dump_op(n);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
} else
|
||||
m[n] = uc;
|
||||
}
|
||||
}
|
||||
|
||||
void gcm::bu_release_val(value* v) {
|
||||
node *n = v->any_def();
|
||||
|
||||
if (n && n->parent == &pending) {
|
||||
unsigned uc = ++nuc_stk[ucs_level][n];
|
||||
unsigned uc2 = uses[n];
|
||||
|
||||
GCM_DUMP(
|
||||
cerr << "release val ";
|
||||
dump::dump_val(v);
|
||||
cerr << " for node ";
|
||||
dump::dump_op(n);
|
||||
cerr << " new uc=" << uc << ", total " << uc2 << "\n";
|
||||
);
|
||||
|
||||
if (uc == uc2)
|
||||
bu_release_op(n);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void gcm::init_def_count(nuc_map& m, container_node& s) {
|
||||
m.clear();
|
||||
for (node_iterator I = s.begin(), E = s.end(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
unsigned dc = get_dc_vec(n->src, true) + get_dc_vec(n->dst, false);
|
||||
m[n] = dc;
|
||||
|
||||
GCM_DUMP(
|
||||
cerr << "dc " << dc << " ";
|
||||
dump::dump_op(n);
|
||||
cerr << "\n";
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned gcm::get_dc_vec(vvec& vv, bool src) {
|
||||
unsigned c = 0;
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (!v || v->is_readonly())
|
||||
continue;
|
||||
|
||||
if (v->is_rel()) {
|
||||
c += v->rel->def != NULL;
|
||||
c += get_dc_vec(v->muse, true);
|
||||
}
|
||||
else if (src) {
|
||||
c += v->def != NULL;
|
||||
c += v->adef != NULL;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
unsigned gcm::real_alu_count(sched_queue& q, unsigned max) {
|
||||
sq_iterator I(q.begin()), E(q.end());
|
||||
unsigned c = 0;
|
||||
|
||||
while (I != E && c < max) {
|
||||
node *n = *I;
|
||||
if (n->is_alu_inst()) {
|
||||
if (!n->is_copy_mov() || !n->src[0]->is_any_gpr())
|
||||
++c;
|
||||
} else if (n->is_alu_packed()) {
|
||||
c += static_cast<container_node*>(n)->count();
|
||||
}
|
||||
++I;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
bool gcm::check_alu_ready_count(unsigned threshold) {
|
||||
unsigned r = real_alu_count(bu_ready[SQ_ALU], threshold);
|
||||
if (r >= threshold)
|
||||
return true;
|
||||
r += real_alu_count(bu_ready_next[SQ_ALU], threshold - r);
|
||||
return r >= threshold;
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#define GVN_DEBUG 0
|
||||
|
||||
#if GVN_DEBUG
|
||||
#define GVN_DUMP(q) do { q } while (0)
|
||||
#else
|
||||
#define GVN_DUMP(q)
|
||||
#endif
|
||||
|
||||
#include "sb_shader.h"
|
||||
|
||||
#include "sb_pass.h"
|
||||
|
||||
#include "sb_sched.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
bool gvn::visit(node& n, bool enter) {
|
||||
if (enter) {
|
||||
|
||||
|
||||
bool rewrite = true;
|
||||
|
||||
if (n.dst[0]->is_agpr()) {
|
||||
rewrite = false;
|
||||
}
|
||||
|
||||
|
||||
process_op(n, rewrite);
|
||||
|
||||
assert(n.parent);
|
||||
|
||||
if (n.parent->subtype == NST_LOOP_PHI_CONTAINER) {
|
||||
// There is a problem - sometimes with nested loops
|
||||
// loop counter initialization for inner loop is incorrectly hoisted
|
||||
// out of the outer loop
|
||||
|
||||
// FIXME not sure if this is enough to fix a problem completely,
|
||||
// possibly more complete fix is needed (anyway, the
|
||||
// problem was seen only in relatively complex
|
||||
// case involving nested loops and
|
||||
// indirect access to loop counters (without proper array info
|
||||
// loop counters may be considered as array elements too),
|
||||
// was not seen in any tests
|
||||
// or real apps when proper array information is available in TGSI).
|
||||
|
||||
// For now just mark the instructions that initialize loop counters
|
||||
// with DONT_HOIST flag to prevent the insts like MOV r, 0
|
||||
// (initialization of inner loop's counter with const)
|
||||
// from being hoisted out of the outer loop
|
||||
|
||||
assert(!n.src.empty());
|
||||
value *v = n.src[0];
|
||||
|
||||
if (v->is_any_gpr() && v->def)
|
||||
v->def->flags |= NF_DONT_HOIST;
|
||||
}
|
||||
|
||||
} else {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gvn::visit(cf_node& n, bool enter) {
|
||||
if (enter) {
|
||||
process_op(n);
|
||||
} else {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gvn::visit(alu_node& n, bool enter) {
|
||||
if (enter) {
|
||||
process_op(n);
|
||||
} else {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gvn::visit(alu_packed_node& n, bool enter) {
|
||||
if (enter) {
|
||||
process_op(n);
|
||||
} else {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gvn::visit(fetch_node& n, bool enter) {
|
||||
if (enter) {
|
||||
process_op(n);
|
||||
} else {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gvn::visit(region_node& n, bool enter) {
|
||||
if (enter) {
|
||||
// FIXME: loop_phi sources are undefined yet (except theone from the preceding
|
||||
// code), can we handle that somehow?
|
||||
// if (n.loop_phi)
|
||||
// run_on(*n.loop_phi);
|
||||
} else {
|
||||
if (n.loop_phi)
|
||||
run_on(*n.loop_phi);
|
||||
|
||||
if (n.phi)
|
||||
run_on(*n.phi);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gvn::process_src(value* &v, bool rewrite) {
|
||||
if (!v->gvn_source)
|
||||
sh.vt.add_value(v);
|
||||
|
||||
if (rewrite && !v->gvn_source->is_rel()) {
|
||||
v = v->gvn_source;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: maybe handle it in the scheduler?
|
||||
void gvn::process_alu_src_constants(node &n, value* &v) {
|
||||
if (n.src.size() < 3) {
|
||||
process_src(v, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!v->gvn_source)
|
||||
sh.vt.add_value(v);
|
||||
|
||||
rp_kcache_tracker kc(sh);
|
||||
|
||||
kc.try_reserve(v->gvn_source->select);
|
||||
|
||||
// don't propagate 3rd constant to the trans-only instruction
|
||||
if (!n.is_alu_packed()) {
|
||||
alu_node *a = static_cast<alu_node*>(&n);
|
||||
if (a->bc.op_ptr->src_count == 3 && !(a->bc.slot_flags & AF_V)) {
|
||||
unsigned const_count = 0;
|
||||
for (vvec::iterator I = n.src.begin(), E = n.src.end(); I != E;
|
||||
++I) {
|
||||
value *c = (*I);
|
||||
if (c && c->is_readonly() && ++const_count == 2) {
|
||||
process_src(v, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (vvec::iterator I = n.src.begin(), E = n.src.end(); I != E; ++I) {
|
||||
value *c = (*I);
|
||||
|
||||
if (c->is_kcache() && !kc.try_reserve(c->select)) {
|
||||
process_src(v, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
process_src(v, true);
|
||||
}
|
||||
|
||||
void gvn::process_op(node& n, bool rewrite) {
|
||||
|
||||
for(vvec::iterator I = n.src.begin(), E = n.src.end(); I != E; ++I) {
|
||||
value* &v = *I;
|
||||
if (v) {
|
||||
if (v->rel) {
|
||||
process_src(v->rel, rewrite);
|
||||
}
|
||||
|
||||
if (rewrite && v->gvn_source && v->gvn_source->is_readonly()
|
||||
&& n.is_any_alu()) {
|
||||
process_alu_src_constants(n, v);
|
||||
} else {
|
||||
if (rewrite && (n.is_fetch_op(FETCH_OP_VFETCH) ||
|
||||
n.is_fetch_op(FETCH_OP_SEMFETCH)))
|
||||
process_src(v, false);
|
||||
else
|
||||
process_src(v, rewrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (n.pred)
|
||||
process_src(n.pred, false);
|
||||
|
||||
if (n.type == NT_IF) {
|
||||
if_node &i = (if_node&)n;
|
||||
if (i.cond)
|
||||
process_src(i.cond, false);
|
||||
}
|
||||
|
||||
for(vvec::iterator I = n.dst.begin(), E = n.dst.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (v) {
|
||||
if (v->rel)
|
||||
process_src(v->rel, rewrite);
|
||||
sh.vt.add_value(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#define IFC_DEBUG 0
|
||||
|
||||
#if IFC_DEBUG
|
||||
#define IFC_DUMP(q) do { q } while (0)
|
||||
#else
|
||||
#define IFC_DUMP(q)
|
||||
#endif
|
||||
|
||||
#include "sb_shader.h"
|
||||
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
int if_conversion::run() {
|
||||
|
||||
regions_vec &rv = sh.get_regions();
|
||||
|
||||
unsigned converted = 0;
|
||||
|
||||
for (regions_vec::reverse_iterator N, I = rv.rbegin(), E = rv.rend();
|
||||
I != E; I = N) {
|
||||
N = I; ++N;
|
||||
|
||||
region_node *r = *I;
|
||||
if (run_on(r)) {
|
||||
rv.erase(I.base() - 1);
|
||||
++converted;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned if_conversion::try_convert_kills(region_node* r) {
|
||||
|
||||
// handling the simplest (and probably most frequent) case only -
|
||||
// if - 4 kills - endif
|
||||
|
||||
// TODO handle more complex cases
|
||||
|
||||
depart_node *d1 = static_cast<depart_node*>(r->front());
|
||||
if (!d1->is_depart())
|
||||
return 0;
|
||||
|
||||
if_node *f = static_cast<if_node*>(d1->front());
|
||||
if (!f->is_if())
|
||||
return 0;
|
||||
|
||||
depart_node *d2 = static_cast<depart_node*>(f->front());
|
||||
if (!d2->is_depart())
|
||||
return 0;
|
||||
|
||||
unsigned cnt = 0;
|
||||
|
||||
for (node_iterator I = d2->begin(), E = d2->end(); I != E; ++I) {
|
||||
alu_node *n = static_cast<alu_node*>(*I);
|
||||
if (!n->is_alu_inst())
|
||||
return 0;
|
||||
|
||||
if (!(n->bc.op_ptr->flags & AF_KILL))
|
||||
return 0;
|
||||
|
||||
if (n->bc.op_ptr->src_count != 2 || n->src.size() != 2)
|
||||
return 0;
|
||||
|
||||
value *s1 = n->src[0], *s2 = n->src[1];
|
||||
|
||||
// assuming that the KILL with constant operands is "always kill"
|
||||
|
||||
if (!s1 || !s2 || !s1->is_const() || !s2->is_const())
|
||||
return 0;
|
||||
|
||||
++cnt;
|
||||
}
|
||||
|
||||
if (cnt > 4)
|
||||
return 0;
|
||||
|
||||
value *cond = f->cond;
|
||||
value *pred = get_select_value_for_em(sh, cond);
|
||||
|
||||
if (!pred)
|
||||
return 0;
|
||||
|
||||
for (node_iterator N, I = d2->begin(), E = d2->end(); I != E; I = N) {
|
||||
N = I; ++N;
|
||||
|
||||
alu_node *n = static_cast<alu_node*>(*I);
|
||||
|
||||
IFC_DUMP(
|
||||
cerr << "converting ";
|
||||
dump::dump_op(n);
|
||||
cerr << " " << n << "\n";
|
||||
);
|
||||
|
||||
n->remove();
|
||||
|
||||
n->bc.set_op(ALU_OP2_KILLE_INT);
|
||||
n->src[0] = pred;
|
||||
n->src[1] = sh.get_const_value(0);
|
||||
// reset src modifiers
|
||||
memset(&n->bc.src[0], 0, sizeof(bc_alu_src));
|
||||
memset(&n->bc.src[1], 0, sizeof(bc_alu_src));
|
||||
|
||||
r->insert_before(n);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool if_conversion::run_on(region_node* r) {
|
||||
|
||||
if (r->dep_count() != 2 || r->rep_count() != 1)
|
||||
return false;
|
||||
|
||||
node_stats s;
|
||||
|
||||
r->collect_stats(s);
|
||||
|
||||
IFC_DUMP(
|
||||
cerr << "ifcvt: region " << r->region_id << " :\n";
|
||||
s.dump();
|
||||
);
|
||||
|
||||
if (s.region_count || s.fetch_count ||
|
||||
s.if_count != 1 || s.repeat_count)
|
||||
return false;
|
||||
|
||||
unsigned real_alu_count = s.alu_count - s.alu_copy_mov_count;
|
||||
|
||||
// if_conversion allows to eliminate JUMP-ALU_POP_AFTER or
|
||||
// JUMP-ALU-ELSE-ALU_POP_AFTER, for now let's assume that 3 CF instructions
|
||||
// are eliminated. According to the docs, cost of CF instruction is
|
||||
// equal to ~40 ALU VLIW instructions (instruction groups),
|
||||
// so we have eliminated cost equal to ~120 groups in total.
|
||||
// Let's also assume that we have avg 3 ALU instructions per group,
|
||||
// This means that potential eliminated cost is about 360 single alu inst.
|
||||
// On the other hand, we are speculatively executing conditional code now,
|
||||
// so we are increasing the cost in some cases. In the worst case, we'll
|
||||
// have to execute real_alu_count additional alu instructions instead of
|
||||
// jumping over them. Let's assume for now that average added cost is
|
||||
//
|
||||
// (0.9 * real_alu_count)
|
||||
//
|
||||
// So we should perform if_conversion if
|
||||
//
|
||||
// (0.9 * real_alu_count) < 360, or
|
||||
//
|
||||
// real_alu_count < 400
|
||||
//
|
||||
// So if real_alu_count is more than 400, than we think that if_conversion
|
||||
// doesn't make sense.
|
||||
|
||||
// FIXME: We can use more precise heuristic, taking into account sizes of
|
||||
// the branches and their probability instead of total size.
|
||||
// Another way to improve this is to consider the number of the groups
|
||||
// instead of the number of instructions (taking into account actual VLIW
|
||||
// packing).
|
||||
// (Currently we don't know anything about packing at this stage, but
|
||||
// probably we can make some more precise estimations anyway)
|
||||
|
||||
if (real_alu_count > 400)
|
||||
return false;
|
||||
|
||||
if (s.alu_kill_count) {
|
||||
unsigned kcnt = try_convert_kills(r);
|
||||
if (kcnt < s.alu_kill_count)
|
||||
return false;
|
||||
}
|
||||
|
||||
IFC_DUMP( cerr << "if_cvt: processing...\n"; );
|
||||
|
||||
depart_node *nd1 = static_cast<depart_node*>(r->first);
|
||||
if (!nd1->is_depart())
|
||||
return false;
|
||||
if_node *nif = static_cast<if_node*>(nd1->first);
|
||||
if (!nif->is_if())
|
||||
return false;
|
||||
depart_node *nd2 = static_cast<depart_node*>(nif->first);
|
||||
if (!nd2->is_depart())
|
||||
return false;
|
||||
|
||||
value *em = nif->cond;
|
||||
value *select = get_select_value_for_em(sh, em);
|
||||
|
||||
if (!select)
|
||||
return false;
|
||||
|
||||
for (node_iterator I = r->phi->begin(), E = r->phi->end(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
|
||||
alu_node *ns = convert_phi(select, n);
|
||||
|
||||
if (ns)
|
||||
r->insert_after(ns);
|
||||
}
|
||||
|
||||
nd2->expand();
|
||||
nif->expand();
|
||||
nd1->expand();
|
||||
r->expand();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
alu_node* if_conversion::convert_phi(value* select, node* phi) {
|
||||
assert(phi->dst.size() == 1 || phi->src.size() == 2);
|
||||
|
||||
value *d = phi->dst[0];
|
||||
value *v1 = phi->src[0];
|
||||
value *v2 = phi->src[1];
|
||||
|
||||
assert(d);
|
||||
|
||||
if (!d->is_any_gpr())
|
||||
return NULL;
|
||||
|
||||
if (v1->is_undef()) {
|
||||
if (v2->is_undef()) {
|
||||
return NULL;
|
||||
} else {
|
||||
return sh.create_mov(d, v2);
|
||||
}
|
||||
} else if (v2->is_undef())
|
||||
return sh.create_mov(d, v1);
|
||||
|
||||
alu_node* n = sh.create_alu();
|
||||
|
||||
n->bc.set_op(ALU_OP3_CNDE_INT);
|
||||
n->dst.push_back(d);
|
||||
n->src.push_back(select);
|
||||
n->src.push_back(v1);
|
||||
n->src.push_back(v2);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include "sb_bc.h"
|
||||
#include "sb_shader.h"
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
bool node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
||||
bool container_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
||||
bool alu_group_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
||||
bool alu_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
||||
bool cf_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
||||
bool fetch_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
||||
bool region_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
||||
|
||||
bool repeat_node::accept(vpass& p, bool enter) {
|
||||
return p.visit(*this, enter);
|
||||
}
|
||||
|
||||
bool depart_node::accept(vpass& p, bool enter) {
|
||||
return p.visit(*this, enter);
|
||||
}
|
||||
bool if_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
||||
bool bb_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
||||
bool alu_packed_node::accept(vpass& p, bool enter) {
|
||||
return p.visit(*this, enter);
|
||||
}
|
||||
|
||||
void alu_packed_node::init_args() {
|
||||
alu_node *p = static_cast<alu_node*>(first);
|
||||
assert(p->is_valid());
|
||||
while (p) {
|
||||
dst.insert(dst.end(), p->dst.begin(), p->dst.end());
|
||||
src.insert(src.end(), p->src.begin(), p->src.end());
|
||||
p = static_cast<alu_node*>(p->next);
|
||||
}
|
||||
|
||||
// if it's packed then it's always multislot, no need to check slot flags
|
||||
bool repl = (op_ptr()->flags & AF_REPL);
|
||||
value *replicated_value = NULL;
|
||||
|
||||
for (vvec::iterator I = dst.begin(), E = dst.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (v) {
|
||||
if (repl) {
|
||||
if (replicated_value)
|
||||
v->assign_source(replicated_value);
|
||||
else
|
||||
replicated_value = v;
|
||||
}
|
||||
|
||||
v->def = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void container_node::insert_node_before(node* s, node* n) {
|
||||
if (s->prev) {
|
||||
node *sp = s->prev;
|
||||
sp->next = n;
|
||||
n->prev = sp;
|
||||
n->next = s;
|
||||
s->prev = n;
|
||||
} else {
|
||||
n->next = s;
|
||||
s->prev = n;
|
||||
first = n;
|
||||
}
|
||||
n->parent = this;
|
||||
}
|
||||
|
||||
void container_node::insert_node_after(node* s, node* n) {
|
||||
if (s->next) {
|
||||
node *sn = s->next;
|
||||
sn->prev = n;
|
||||
n->next = sn;
|
||||
n->prev = s;
|
||||
s->next = n;
|
||||
} else {
|
||||
n->prev = s;
|
||||
s->next = n;
|
||||
last = n;
|
||||
}
|
||||
n->parent = this;
|
||||
}
|
||||
|
||||
void container_node::move(iterator b, iterator e) {
|
||||
assert(b != e);
|
||||
|
||||
container_node *source_container = b->parent;
|
||||
node *l = source_container->cut(b, e);
|
||||
|
||||
first = last = l;
|
||||
first->parent = this;
|
||||
|
||||
while (last->next) {
|
||||
last = last->next;
|
||||
last->parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
node* container_node::cut(iterator b, iterator e) {
|
||||
assert(!*b || b->parent == this);
|
||||
assert(!*e || e->parent == this);
|
||||
assert(b != e);
|
||||
|
||||
if (b->prev) {
|
||||
b->prev->next = *e;
|
||||
} else {
|
||||
first = *e;
|
||||
}
|
||||
|
||||
if (*e) {
|
||||
e->prev->next = NULL;
|
||||
e->prev = b->prev;
|
||||
} else {
|
||||
last->next = NULL;
|
||||
last = b->prev;
|
||||
}
|
||||
|
||||
b->prev = NULL;
|
||||
|
||||
return *b;
|
||||
}
|
||||
|
||||
unsigned container_node::count() {
|
||||
unsigned c = 0;
|
||||
node *t = first;
|
||||
while (t) {
|
||||
t = t->next;
|
||||
c++;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void container_node::remove_node(node *n) {
|
||||
if (n->prev)
|
||||
n->prev->next = n->next;
|
||||
else
|
||||
first = n->next;
|
||||
if (n->next)
|
||||
n->next->prev = n->prev;
|
||||
else
|
||||
last = n->prev;
|
||||
n->parent = NULL;
|
||||
}
|
||||
|
||||
void container_node::expand(container_node *n) {
|
||||
if (!n->empty()) {
|
||||
node *e0 = n->first;
|
||||
node *e1 = n->last;
|
||||
|
||||
e0->prev = n->prev;
|
||||
if (e0->prev) {
|
||||
e0->prev->next = e0;
|
||||
} else {
|
||||
first = e0;
|
||||
}
|
||||
|
||||
e1->next = n->next;
|
||||
if (e1->next)
|
||||
e1->next->prev = e1;
|
||||
else
|
||||
last = e1;
|
||||
|
||||
do {
|
||||
e0->parent = this;
|
||||
e0 = e0->next;
|
||||
} while (e0 != e1->next);
|
||||
} else
|
||||
remove_node(n);
|
||||
}
|
||||
|
||||
void container_node::push_back(node *n) {
|
||||
if (last) {
|
||||
last->next = n;
|
||||
n->next = NULL;
|
||||
n->prev = last;
|
||||
last = n;
|
||||
} else {
|
||||
assert(!first);
|
||||
first = last = n;
|
||||
n->prev = n->next = NULL;
|
||||
}
|
||||
n->parent = this;
|
||||
}
|
||||
void container_node::push_front(node *n) {
|
||||
if (first) {
|
||||
first->prev = n;
|
||||
n->prev = NULL;
|
||||
n->next = first;
|
||||
first = n;
|
||||
} else {
|
||||
assert(!last);
|
||||
first = last = n;
|
||||
n->prev = n->next = NULL;
|
||||
}
|
||||
n->parent = this;
|
||||
}
|
||||
|
||||
void node::insert_before(node* n) {
|
||||
parent->insert_node_before(this, n);
|
||||
}
|
||||
|
||||
void node::insert_after(node* n) {
|
||||
parent->insert_node_after(this, n);
|
||||
}
|
||||
|
||||
void node::replace_with(node* n) {
|
||||
n->prev = prev;
|
||||
n->next = next;
|
||||
n->parent = parent;
|
||||
if (prev)
|
||||
prev->next = n;
|
||||
if (next)
|
||||
next->prev = n;
|
||||
|
||||
if (parent->first == this)
|
||||
parent->first = n;
|
||||
|
||||
if (parent->last == this)
|
||||
parent->last = n;
|
||||
|
||||
parent = NULL;
|
||||
next = prev = NULL;
|
||||
}
|
||||
|
||||
void container_node::expand() {
|
||||
parent->expand(this);
|
||||
}
|
||||
|
||||
void node::remove() {parent->remove_node(this);
|
||||
}
|
||||
|
||||
value_hash node::hash_src() {
|
||||
|
||||
value_hash h = 12345;
|
||||
|
||||
for (int k = 0, e = src.size(); k < e; ++k) {
|
||||
value *s = src[k];
|
||||
if (s)
|
||||
h ^= (s->hash());
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
value_hash node::hash() {
|
||||
|
||||
if (parent && parent->subtype == NST_LOOP_PHI_CONTAINER)
|
||||
return 47451;
|
||||
|
||||
return hash_src() ^ (subtype << 13) ^ (type << 3);
|
||||
}
|
||||
|
||||
void r600_sb::container_node::append_from(container_node* c) {
|
||||
if (!c->first)
|
||||
return;
|
||||
|
||||
node *b = c->first;
|
||||
|
||||
if (last) {
|
||||
last->next = c->first;
|
||||
last->next->prev = last;
|
||||
} else {
|
||||
first = c->first;
|
||||
}
|
||||
|
||||
last = c->last;
|
||||
c->first = NULL;
|
||||
c->last = NULL;
|
||||
|
||||
while (b) {
|
||||
b->parent = this;
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
|
||||
bool node::fold_dispatch(expr_handler* ex) { return ex->fold(*this); }
|
||||
bool container_node::fold_dispatch(expr_handler* ex) { return ex->fold(*this); }
|
||||
bool alu_node::fold_dispatch(expr_handler* ex) { return ex->fold(*this); }
|
||||
bool alu_packed_node::fold_dispatch(expr_handler* ex) { return ex->fold(*this); }
|
||||
bool fetch_node::fold_dispatch(expr_handler* ex) { return ex->fold(*this); }
|
||||
bool cf_node::fold_dispatch(expr_handler* ex) { return ex->fold(*this); }
|
||||
|
||||
unsigned alu_packed_node::get_slot_mask() {
|
||||
unsigned mask = 0;
|
||||
for (node_iterator I = begin(), E = end(); I != E; ++I)
|
||||
mask |= 1 << static_cast<alu_node*>(*I)->bc.slot;
|
||||
return mask;
|
||||
}
|
||||
|
||||
void alu_packed_node::update_packed_items(sb_context &ctx) {
|
||||
|
||||
vvec::iterator SI(src.begin()), DI(dst.begin());
|
||||
|
||||
assert(first);
|
||||
|
||||
alu_node *c = static_cast<alu_node*>(first);
|
||||
unsigned flags = c->bc.op_ptr->flags;
|
||||
unsigned slot_flags = c->bc.slot_flags;
|
||||
|
||||
// fixup dst for instructions that replicate output
|
||||
if (((flags & AF_REPL) && slot_flags == AF_4V) ||
|
||||
(ctx.is_cayman() && slot_flags == AF_S)) {
|
||||
|
||||
value *swp[4] = {};
|
||||
|
||||
unsigned chan;
|
||||
|
||||
for (vvec::iterator I2 = dst.begin(), E2 = dst.end();
|
||||
I2 != E2; ++I2) {
|
||||
value *v = *I2;
|
||||
if (v) {
|
||||
chan = v->get_final_chan();
|
||||
assert(!swp[chan] || swp[chan] == v);
|
||||
swp[chan] = v;
|
||||
}
|
||||
}
|
||||
|
||||
chan = 0;
|
||||
for (vvec::iterator I2 = dst.begin(), E2 = dst.end();
|
||||
I2 != E2; ++I2, ++chan) {
|
||||
*I2 = swp[chan];
|
||||
}
|
||||
}
|
||||
|
||||
for (node_iterator I = begin(), E = end(); I != E; ++I) {
|
||||
alu_node *n = static_cast<alu_node*>(*I);
|
||||
assert(n);
|
||||
|
||||
for (vvec::iterator I2 = n->src.begin(), E2 = n->src.end();
|
||||
I2 != E2; ++I2, ++SI) {
|
||||
*I2 = *SI;
|
||||
}
|
||||
for (vvec::iterator I2 = n->dst.begin(), E2 = n->dst.end();
|
||||
I2 != E2; ++I2, ++DI) {
|
||||
*I2 = *DI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool node::is_cf_op(unsigned op) {
|
||||
if (!is_cf_inst())
|
||||
return false;
|
||||
cf_node *c = static_cast<cf_node*>(this);
|
||||
return c->bc.op == op;
|
||||
}
|
||||
|
||||
bool node::is_alu_op(unsigned op) {
|
||||
if (!is_alu_inst())
|
||||
return false;
|
||||
alu_node *c = static_cast<alu_node*>(this);
|
||||
return c->bc.op == op;
|
||||
}
|
||||
|
||||
bool node::is_fetch_op(unsigned op) {
|
||||
if (!is_fetch_inst())
|
||||
return false;
|
||||
fetch_node *c = static_cast<fetch_node*>(this);
|
||||
return c->bc.op == op;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool node::is_mova() {
|
||||
if (!is_alu_inst())
|
||||
return false;
|
||||
alu_node *a = static_cast<alu_node*>(this);
|
||||
return (a->bc.op_ptr->flags & AF_MOVA);
|
||||
}
|
||||
|
||||
bool node::is_pred_set() {
|
||||
if (!is_alu_inst())
|
||||
return false;
|
||||
alu_node *a = static_cast<alu_node*>(this);
|
||||
return (a->bc.op_ptr->flags & AF_ANY_PRED);
|
||||
}
|
||||
|
||||
unsigned node::cf_op_flags() {
|
||||
assert(is_cf_inst());
|
||||
cf_node *c = static_cast<cf_node*>(this);
|
||||
return c->bc.op_ptr->flags;
|
||||
}
|
||||
|
||||
unsigned node::alu_op_flags() {
|
||||
assert(is_alu_inst());
|
||||
alu_node *c = static_cast<alu_node*>(this);
|
||||
return c->bc.op_ptr->flags;
|
||||
}
|
||||
|
||||
unsigned node::fetch_op_flags() {
|
||||
assert(is_fetch_inst());
|
||||
fetch_node *c = static_cast<fetch_node*>(this);
|
||||
return c->bc.op_ptr->flags;
|
||||
}
|
||||
|
||||
unsigned node::alu_op_slot_flags() {
|
||||
assert(is_alu_inst());
|
||||
alu_node *c = static_cast<alu_node*>(this);
|
||||
return c->bc.slot_flags;
|
||||
}
|
||||
|
||||
region_node* node::get_parent_region() {
|
||||
node *p = this;
|
||||
while ((p = p->parent))
|
||||
if (p->is_region())
|
||||
return static_cast<region_node*>(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned container_node::real_alu_count() {
|
||||
unsigned c = 0;
|
||||
node *t = first;
|
||||
while (t) {
|
||||
if (t->is_alu_inst())
|
||||
++c;
|
||||
else if (t->is_alu_packed())
|
||||
c += static_cast<container_node*>(t)->count();
|
||||
t = t->next;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void container_node::collect_stats(node_stats& s) {
|
||||
|
||||
for (node_iterator I = begin(), E = end(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
if (n->is_container()) {
|
||||
static_cast<container_node*>(n)->collect_stats(s);
|
||||
}
|
||||
|
||||
if (n->is_alu_inst()) {
|
||||
++s.alu_count;
|
||||
alu_node *a = static_cast<alu_node*>(n);
|
||||
if (a->bc.op_ptr->flags & AF_KILL)
|
||||
++s.alu_kill_count;
|
||||
else if (a->is_copy_mov())
|
||||
++s.alu_copy_mov_count;
|
||||
} else if (n->is_fetch_inst())
|
||||
++s.fetch_count;
|
||||
else if (n->is_cf_inst())
|
||||
++s.cf_count;
|
||||
else if (n->is_region()) {
|
||||
++s.region_count;
|
||||
region_node *r = static_cast<region_node*>(n);
|
||||
if(r->is_loop())
|
||||
++s.loop_count;
|
||||
|
||||
if (r->phi)
|
||||
s.phi_count += r->phi->count();
|
||||
if (r->loop_phi)
|
||||
s.loop_phi_count += r->loop_phi->count();
|
||||
}
|
||||
else if (n->is_depart())
|
||||
++s.depart_count;
|
||||
else if (n->is_repeat())
|
||||
++s.repeat_count;
|
||||
else if (n->is_if())
|
||||
++s.if_count;
|
||||
}
|
||||
}
|
||||
|
||||
void region_node::expand_depart(depart_node *d) {
|
||||
depart_vec::iterator I = departs.begin() + d->dep_id, E;
|
||||
I = departs.erase(I);
|
||||
E = departs.end();
|
||||
while (I != E) {
|
||||
--(*I)->dep_id;
|
||||
++I;
|
||||
}
|
||||
d->expand();
|
||||
}
|
||||
|
||||
void region_node::expand_repeat(repeat_node *r) {
|
||||
repeat_vec::iterator I = repeats.begin() + r->rep_id - 1, E;
|
||||
I = repeats.erase(I);
|
||||
E = repeats.end();
|
||||
while (I != E) {
|
||||
--(*I)->rep_id;
|
||||
++I;
|
||||
}
|
||||
r->expand();
|
||||
}
|
||||
|
||||
void node_stats::dump() {
|
||||
cerr << " alu_count : " << alu_count << "\n";
|
||||
cerr << " alu_kill_count : " << alu_kill_count << "\n";
|
||||
cerr << " alu_copy_mov_count : " << alu_copy_mov_count << "\n";
|
||||
cerr << " cf_count : " << cf_count << "\n";
|
||||
cerr << " fetch_count : " << fetch_count << "\n";
|
||||
cerr << " region_count : " << region_count << "\n";
|
||||
cerr << " loop_count : " << loop_count << "\n";
|
||||
cerr << " phi_count : " << phi_count << "\n";
|
||||
cerr << " loop_phi_count : " << loop_phi_count << "\n";
|
||||
cerr << " depart_count : " << depart_count << "\n";
|
||||
cerr << " repeat_count : " << repeat_count << "\n";
|
||||
cerr << " if_count : " << if_count << "\n";
|
||||
}
|
||||
|
||||
unsigned alu_node::interp_param() {
|
||||
if (!(bc.op_ptr->flags & AF_INTERP))
|
||||
return 0;
|
||||
unsigned param;
|
||||
if (bc.op_ptr->src_count == 2) {
|
||||
param = src[1]->select.sel();
|
||||
} else {
|
||||
param = src[0]->select.sel();
|
||||
}
|
||||
return param + 1;
|
||||
}
|
||||
|
||||
alu_group_node* alu_node::get_alu_group_node() {
|
||||
node *p = parent;
|
||||
if (p) {
|
||||
if (p->subtype == NST_ALU_PACKED_INST) {
|
||||
assert(p->parent && p->parent->subtype == NST_ALU_GROUP);
|
||||
p = p->parent;
|
||||
}
|
||||
return static_cast<alu_group_node*>(p);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include "sb_shader.h"
|
||||
|
||||
#include "sb_pass.h"
|
||||
|
||||
#define LIV_DEBUG 0
|
||||
|
||||
#if LIV_DEBUG
|
||||
#define LIV_DUMP(a) do { a } while (0)
|
||||
#else
|
||||
#define LIV_DUMP(a)
|
||||
#endif
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
bool liveness::visit(container_node& n, bool enter) {
|
||||
if (enter) {
|
||||
n.live_after = live;
|
||||
process_ins(n);
|
||||
} else {
|
||||
process_outs(n);
|
||||
n.live_before = live;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool liveness::visit(bb_node& n, bool enter) {
|
||||
if (enter) {
|
||||
n.live_after = live;
|
||||
} else {
|
||||
n.live_before = live;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool liveness::visit(alu_group_node& n, bool enter) {
|
||||
if (enter) {
|
||||
} else {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool liveness::visit(cf_node& n, bool enter) {
|
||||
if (enter) {
|
||||
if (n.bc.op == CF_OP_CF_END) {
|
||||
n.flags |= NF_DEAD;
|
||||
return false;
|
||||
}
|
||||
n.live_after = live;
|
||||
update_interferences();
|
||||
process_op(n);
|
||||
} else {
|
||||
n.live_before = live;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool liveness::visit(alu_node& n, bool enter) {
|
||||
if (enter) {
|
||||
update_interferences();
|
||||
process_op(n);
|
||||
} else {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool liveness::visit(alu_packed_node& n, bool enter) {
|
||||
if (enter) {
|
||||
update_interferences();
|
||||
process_op(n);
|
||||
|
||||
} else {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool liveness::visit(fetch_node& n, bool enter) {
|
||||
if (enter) {
|
||||
update_interferences();
|
||||
process_op(n);
|
||||
} else {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool liveness::visit(region_node& n, bool enter) {
|
||||
if (enter) {
|
||||
val_set s = live;
|
||||
|
||||
update_interferences();
|
||||
|
||||
if (n.phi)
|
||||
process_phi_outs(n.phi);
|
||||
|
||||
n.live_after = live;
|
||||
|
||||
live.clear();
|
||||
|
||||
if (n.loop_phi) {
|
||||
n.live_before.clear();
|
||||
}
|
||||
|
||||
assert(n.count() == 1);
|
||||
run_on(*static_cast<container_node*>(*n.begin()));
|
||||
|
||||
// second pass for loops
|
||||
if (n.loop_phi) {
|
||||
process_phi_outs(n.loop_phi);
|
||||
n.live_before = live;
|
||||
|
||||
run_on(*static_cast<container_node*>(*n.begin()));
|
||||
|
||||
update_interferences(); // FIXME is it required
|
||||
|
||||
process_phi_outs(n.loop_phi);
|
||||
process_phi_branch(n.loop_phi, 0);
|
||||
}
|
||||
|
||||
update_interferences(); // FIXME is it required
|
||||
|
||||
n.live_after = s;
|
||||
n.live_before = live;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool liveness::visit(repeat_node& n, bool enter) {
|
||||
if (enter) {
|
||||
live = n.target->live_before;
|
||||
process_phi_branch(n.target->loop_phi, n.rep_id);
|
||||
} else {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool liveness::visit(depart_node& n, bool enter) {
|
||||
if (enter) {
|
||||
live = n.target->live_after;
|
||||
if(n.target->phi)
|
||||
process_phi_branch(n.target->phi, n.dep_id);
|
||||
} else {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool liveness::visit(if_node& n, bool enter) {
|
||||
if (enter) {
|
||||
assert(n.count() == 1);
|
||||
n.live_after = live;
|
||||
|
||||
run_on(*static_cast<container_node*>(*n.begin()));
|
||||
|
||||
process_op(n);
|
||||
live.add_set(n.live_after);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void liveness::update_interferences() {
|
||||
if (!sh.compute_interferences)
|
||||
return;
|
||||
|
||||
if (!live_changed)
|
||||
return;
|
||||
|
||||
LIV_DUMP(
|
||||
cerr << "interf ";
|
||||
dump::dump_set(sh, live);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
val_set& s = live;
|
||||
for(val_set::iterator I = s.begin(sh), E = s.end(sh); I != E; ++I) {
|
||||
value *v = *I;
|
||||
assert(v);
|
||||
|
||||
if (v->array) {
|
||||
v->array->interferences.add_set(s);
|
||||
}
|
||||
|
||||
v->interferences.add_set(s);
|
||||
v->interferences.remove_val(v);
|
||||
|
||||
LIV_DUMP(
|
||||
cerr << "interferences updated for ";
|
||||
dump::dump_val(v);
|
||||
cerr << " : ";
|
||||
dump::dump_set(sh, v->interferences);
|
||||
cerr << "\n";
|
||||
);
|
||||
}
|
||||
live_changed = false;
|
||||
}
|
||||
|
||||
bool liveness::remove_val(value *v) {
|
||||
if (live.remove_val(v)) {
|
||||
v->flags &= ~VLF_DEAD;
|
||||
return true;
|
||||
}
|
||||
v->flags |= VLF_DEAD;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool liveness::process_maydef(value *v) {
|
||||
bool r = false;
|
||||
vvec::iterator S(v->muse.begin());
|
||||
|
||||
for (vvec::iterator I = v->mdef.begin(), E = v->mdef.end(); I != E;
|
||||
++I, ++S) {
|
||||
value *&d = *I, *&u = *S;
|
||||
if (!d) {
|
||||
assert(!u);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool alive = remove_val(d);
|
||||
if (alive) {
|
||||
r = true;
|
||||
} else {
|
||||
d = NULL;
|
||||
u = NULL;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
bool liveness::remove_vec(vvec &vv) {
|
||||
bool r = false;
|
||||
for (vvec::reverse_iterator I = vv.rbegin(), E = vv.rend(); I != E; ++I) {
|
||||
value* &v = *I;
|
||||
if (!v)
|
||||
continue;
|
||||
|
||||
if (v->is_rel()) {
|
||||
r |= process_maydef(v);
|
||||
} else
|
||||
r |= remove_val(v);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
bool r600_sb::liveness::visit(node& n, bool enter) {
|
||||
if (enter) {
|
||||
update_interferences();
|
||||
process_op(n);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool liveness::process_outs(node& n) {
|
||||
bool alive = remove_vec(n.dst);
|
||||
if (alive)
|
||||
live_changed = true;
|
||||
return alive;
|
||||
}
|
||||
|
||||
bool liveness::add_vec(vvec &vv, bool src) {
|
||||
bool r = false;
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (!v || v->is_readonly())
|
||||
continue;
|
||||
|
||||
if (v->is_rel()) {
|
||||
r |= add_vec(v->muse, true);
|
||||
if (v->rel->is_any_reg())
|
||||
r |= live.add_val(v->rel);
|
||||
|
||||
} else if (src) {
|
||||
r |= live.add_val(v);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void liveness::process_ins(node& n) {
|
||||
if (!(n.flags & NF_DEAD)) {
|
||||
|
||||
live_changed |= add_vec(n.src, true);
|
||||
live_changed |= add_vec(n.dst, false);
|
||||
|
||||
if (n.type == NT_IF) {
|
||||
if_node &in = (if_node&)n;
|
||||
if (in.cond)
|
||||
live_changed |= live.add_val(in.cond);
|
||||
}
|
||||
if (n.pred)
|
||||
live_changed |= live.add_val(n.pred);
|
||||
}
|
||||
}
|
||||
|
||||
void liveness::process_op(node& n) {
|
||||
|
||||
LIV_DUMP(
|
||||
cerr << "process_op: ";
|
||||
dump::dump_op(&n);
|
||||
cerr << "\n";
|
||||
cerr << "process_op: live_after:";
|
||||
dump::dump_set(sh, live);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
if(!n.dst.empty() || n.is_cf_op(CF_OP_CALL_FS)) {
|
||||
if (!process_outs(n)) {
|
||||
if (!(n.flags & NF_DONT_KILL))
|
||||
n.flags |= NF_DEAD;
|
||||
} else {
|
||||
n.flags &= ~NF_DEAD;
|
||||
}
|
||||
}
|
||||
process_ins(n);
|
||||
|
||||
LIV_DUMP(
|
||||
cerr << "process_op: live_before:";
|
||||
dump::dump_set(sh, live);
|
||||
cerr << "\n";
|
||||
);
|
||||
}
|
||||
|
||||
int liveness::init() {
|
||||
|
||||
if (sh.compute_interferences) {
|
||||
gpr_array_vec &vv = sh.arrays();
|
||||
for (gpr_array_vec::iterator I = vv.begin(), E = vv.end(); I != E;
|
||||
++I) {
|
||||
gpr_array *a = *I;
|
||||
a->interferences.clear();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void liveness::update_src_vec(vvec &vv, bool src) {
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
|
||||
if (!v || !v->is_sgpr())
|
||||
continue;
|
||||
|
||||
if (v->rel && v->rel->is_dead())
|
||||
v->rel->flags &= ~VLF_DEAD;
|
||||
|
||||
if (src && v->is_dead()) {
|
||||
v->flags &= ~VLF_DEAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void liveness::process_phi_outs(container_node *phi) {
|
||||
for (node_iterator I = phi->begin(), E = phi->end(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
if (!process_outs(*n)) {
|
||||
n->flags |= NF_DEAD;
|
||||
} else {
|
||||
n->flags &= ~NF_DEAD;
|
||||
update_src_vec(n->src, true);
|
||||
update_src_vec(n->dst, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void liveness::process_phi_branch(container_node* phi, unsigned id) {
|
||||
val_set &s = live;
|
||||
for (node_iterator I = phi->begin(), E = phi->end(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
if (n->is_dead())
|
||||
continue;
|
||||
|
||||
value *v = n->src[id];
|
||||
|
||||
if (!v->is_readonly()) {
|
||||
live_changed |= s.add_val(v);
|
||||
v->flags &= ~VLF_DEAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace r600_sb
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include "sb_shader.h"
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
pass::pass(shader &s) : ctx(s.get_ctx()), sh(s) {}
|
||||
|
||||
int pass::run() { return -1; }
|
||||
|
||||
int vpass::init() { return 0; }
|
||||
int vpass::done() { return 0; }
|
||||
|
||||
int vpass::run() {
|
||||
int r;
|
||||
if ((r = init()))
|
||||
return r;
|
||||
|
||||
run_on(*sh.root);
|
||||
|
||||
if ((r = done()))
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vpass::run_on(container_node &n) {
|
||||
if (n.accept(*this, true)) {
|
||||
|
||||
for (node_iterator N, I = n.begin(), E = n.end(); I != E; I = N) {
|
||||
N = I;
|
||||
++N;
|
||||
|
||||
if (I->is_container()) {
|
||||
container_node *c = static_cast<container_node*>(*I);
|
||||
run_on(*c);
|
||||
} else {
|
||||
I->accept(*this, true);
|
||||
I->accept(*this, false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
n.accept(*this, false);
|
||||
}
|
||||
|
||||
bool vpass::visit(node& n, bool enter) { return true; }
|
||||
bool vpass::visit(container_node& n, bool enter) { return true; }
|
||||
bool vpass::visit(alu_group_node& n, bool enter) { return true; }
|
||||
bool vpass::visit(cf_node& n, bool enter) { return true; }
|
||||
bool vpass::visit(alu_node& n, bool enter) { return true; }
|
||||
bool vpass::visit(alu_packed_node& n, bool enter) { return true; }
|
||||
bool vpass::visit(fetch_node& n, bool enter) { return true; }
|
||||
bool vpass::visit(region_node& n, bool enter) { return true; }
|
||||
bool vpass::visit(repeat_node& n, bool enter) { return true; }
|
||||
bool vpass::visit(depart_node& n, bool enter) { return true; }
|
||||
bool vpass::visit(if_node& n, bool enter) { return true; }
|
||||
bool vpass::visit(bb_node& n, bool enter) { return true; }
|
||||
|
||||
void rev_vpass::run_on(container_node& n) {
|
||||
if (n.accept(*this, true)) {
|
||||
|
||||
for (node_riterator N, I = n.rbegin(), E = n.rend(); I != E; I = N) {
|
||||
N = I;
|
||||
++N;
|
||||
|
||||
if (I->is_container()) {
|
||||
container_node *c = static_cast<container_node*>(*I);
|
||||
run_on(*c);
|
||||
} else {
|
||||
I->accept(*this, true);
|
||||
I->accept(*this, false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
n.accept(*this, false);
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,681 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#ifndef SB_PASS_H_
|
||||
#define SB_PASS_H_
|
||||
|
||||
#include <stack>
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
class pass {
|
||||
protected:
|
||||
sb_context &ctx;
|
||||
shader &sh;
|
||||
|
||||
public:
|
||||
pass(shader &s);
|
||||
|
||||
virtual int run();
|
||||
|
||||
virtual ~pass() {}
|
||||
};
|
||||
|
||||
class vpass : public pass {
|
||||
|
||||
public:
|
||||
|
||||
vpass(shader &s) : pass(s) {}
|
||||
|
||||
virtual int init();
|
||||
virtual int done();
|
||||
|
||||
virtual int run();
|
||||
virtual void run_on(container_node &n);
|
||||
|
||||
virtual bool visit(node &n, bool enter);
|
||||
virtual bool visit(container_node &n, bool enter);
|
||||
virtual bool visit(alu_group_node &n, bool enter);
|
||||
virtual bool visit(cf_node &n, bool enter);
|
||||
virtual bool visit(alu_node &n, bool enter);
|
||||
virtual bool visit(alu_packed_node &n, bool enter);
|
||||
virtual bool visit(fetch_node &n, bool enter);
|
||||
virtual bool visit(region_node &n, bool enter);
|
||||
virtual bool visit(repeat_node &n, bool enter);
|
||||
virtual bool visit(depart_node &n, bool enter);
|
||||
virtual bool visit(if_node &n, bool enter);
|
||||
virtual bool visit(bb_node &n, bool enter);
|
||||
|
||||
};
|
||||
|
||||
class rev_vpass : public vpass {
|
||||
|
||||
public:
|
||||
rev_vpass(shader &s) : vpass(s) {}
|
||||
|
||||
virtual void run_on(container_node &n);
|
||||
};
|
||||
|
||||
|
||||
// =================== PASSES
|
||||
|
||||
class bytecode;
|
||||
|
||||
class bc_dump : public vpass {
|
||||
using vpass::visit;
|
||||
|
||||
std::ostream &o;
|
||||
|
||||
uint32_t *bc_data;
|
||||
unsigned ndw;
|
||||
|
||||
unsigned id;
|
||||
|
||||
unsigned new_group, group_index;
|
||||
|
||||
public:
|
||||
|
||||
bc_dump(shader &s, std::ostream &o, bytecode *bc = NULL);
|
||||
|
||||
bc_dump(shader &s, std::ostream &o, uint32_t *bc_ptr, unsigned ndw) :
|
||||
vpass(s), o(o), bc_data(bc_ptr), ndw(ndw), id(), new_group(), group_index() {}
|
||||
|
||||
virtual int init();
|
||||
virtual int done();
|
||||
|
||||
virtual bool visit(cf_node &n, bool enter);
|
||||
virtual bool visit(alu_node &n, bool enter);
|
||||
virtual bool visit(fetch_node &n, bool enter);
|
||||
|
||||
void dump_dw(unsigned dw_id, unsigned count = 2);
|
||||
|
||||
void dump(cf_node& n);
|
||||
void dump(alu_node& n);
|
||||
void dump(fetch_node& n);
|
||||
};
|
||||
|
||||
|
||||
class dce_cleanup : public vpass {
|
||||
using vpass::visit;
|
||||
|
||||
public:
|
||||
|
||||
dce_cleanup(shader &s) : vpass(s) {}
|
||||
|
||||
virtual bool visit(node &n, bool enter);
|
||||
virtual bool visit(alu_group_node &n, bool enter);
|
||||
virtual bool visit(cf_node &n, bool enter);
|
||||
virtual bool visit(alu_node &n, bool enter);
|
||||
virtual bool visit(alu_packed_node &n, bool enter);
|
||||
virtual bool visit(fetch_node &n, bool enter);
|
||||
virtual bool visit(region_node &n, bool enter);
|
||||
virtual bool visit(container_node &n, bool enter);
|
||||
|
||||
private:
|
||||
|
||||
void cleanup_dst(node &n);
|
||||
void cleanup_dst_vec(vvec &vv);
|
||||
|
||||
};
|
||||
|
||||
|
||||
class def_use : public pass {
|
||||
|
||||
public:
|
||||
|
||||
def_use(shader &sh) : pass(sh) {}
|
||||
|
||||
virtual int run();
|
||||
void run_on(node *n, bool defs);
|
||||
|
||||
private:
|
||||
|
||||
void process_uses(node *n);
|
||||
void process_defs(node *n, vvec &vv, bool arr_def);
|
||||
void process_phi(container_node *c, bool defs, bool uses);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class dump : public vpass {
|
||||
using vpass::visit;
|
||||
|
||||
int level;
|
||||
|
||||
public:
|
||||
|
||||
dump(shader &s) : vpass(s), level(0) {}
|
||||
|
||||
virtual bool visit(node &n, bool enter);
|
||||
virtual bool visit(container_node &n, bool enter);
|
||||
virtual bool visit(alu_group_node &n, bool enter);
|
||||
virtual bool visit(cf_node &n, bool enter);
|
||||
virtual bool visit(alu_node &n, bool enter);
|
||||
virtual bool visit(alu_packed_node &n, bool enter);
|
||||
virtual bool visit(fetch_node &n, bool enter);
|
||||
virtual bool visit(region_node &n, bool enter);
|
||||
virtual bool visit(repeat_node &n, bool enter);
|
||||
virtual bool visit(depart_node &n, bool enter);
|
||||
virtual bool visit(if_node &n, bool enter);
|
||||
virtual bool visit(bb_node &n, bool enter);
|
||||
|
||||
|
||||
static void dump_op(node &n, const char *name);
|
||||
static void dump_vec(const vvec & vv);
|
||||
static void dump_set(shader &sh, val_set & v);
|
||||
|
||||
static void dump_rels(vvec & vv);
|
||||
|
||||
static void dump_val(value *v);
|
||||
static void dump_op(node *n);
|
||||
|
||||
static void dump_op_list(container_node *c);
|
||||
static void dump_queue(sched_queue &q);
|
||||
|
||||
static void dump_alu(alu_node *n);
|
||||
|
||||
private:
|
||||
|
||||
void indent();
|
||||
|
||||
void dump_common(node &n);
|
||||
void dump_flags(node &n);
|
||||
|
||||
void dump_live_values(container_node &n, bool before);
|
||||
};
|
||||
|
||||
|
||||
// Global Code Motion
|
||||
|
||||
class gcm : public pass {
|
||||
|
||||
sched_queue bu_ready[SQ_NUM];
|
||||
sched_queue bu_ready_next[SQ_NUM];
|
||||
sched_queue bu_ready_early[SQ_NUM];
|
||||
sched_queue ready;
|
||||
sched_queue ready_above;
|
||||
|
||||
container_node pending;
|
||||
|
||||
struct op_info {
|
||||
bb_node* top_bb;
|
||||
bb_node* bottom_bb;
|
||||
op_info() : top_bb(), bottom_bb() {}
|
||||
};
|
||||
|
||||
typedef std::map<node*, op_info> op_info_map;
|
||||
|
||||
typedef std::map<node*, unsigned> nuc_map;
|
||||
|
||||
op_info_map op_map;
|
||||
nuc_map uses;
|
||||
|
||||
typedef std::vector<nuc_map> nuc_stack;
|
||||
|
||||
nuc_stack nuc_stk;
|
||||
unsigned ucs_level;
|
||||
|
||||
bb_node * bu_bb;
|
||||
|
||||
vvec pending_defs;
|
||||
|
||||
node_list pending_nodes;
|
||||
|
||||
unsigned cur_sq;
|
||||
|
||||
public:
|
||||
|
||||
gcm(shader &sh) : pass(sh),
|
||||
bu_ready(), bu_ready_next(), bu_ready_early(),
|
||||
ready(), op_map(), uses(), nuc_stk(1), ucs_level(),
|
||||
bu_bb(), pending_defs(), pending_nodes() {}
|
||||
|
||||
virtual int run();
|
||||
|
||||
private:
|
||||
|
||||
void collect_instructions(container_node *c, bool early_pass);
|
||||
|
||||
void sched_early(container_node *n);
|
||||
void td_sched_bb(bb_node *bb);
|
||||
bool td_is_ready(node *n);
|
||||
void td_release_uses(vvec &v);
|
||||
void td_release_val(value *v);
|
||||
void td_schedule(bb_node *bb, node *n);
|
||||
|
||||
void sched_late(container_node *n);
|
||||
void bu_sched_bb(bb_node *bb);
|
||||
void bu_release_defs(vvec &v, bool src);
|
||||
void bu_release_phi_defs(container_node *p, unsigned op);
|
||||
bool bu_is_ready(node *n);
|
||||
void bu_release_val(value *v);
|
||||
void bu_release_op(node * n);
|
||||
void bu_find_best_bb(node *n, op_info &oi);
|
||||
void bu_schedule(container_node *bb, node *n);
|
||||
|
||||
void push_uc_stack();
|
||||
void pop_uc_stack();
|
||||
|
||||
void init_def_count(nuc_map &m, container_node &s);
|
||||
void init_use_count(nuc_map &m, container_node &s);
|
||||
unsigned get_uc_vec(vvec &vv);
|
||||
unsigned get_dc_vec(vvec &vv, bool src);
|
||||
|
||||
void add_ready(node *n);
|
||||
|
||||
void dump_uc_stack();
|
||||
|
||||
unsigned real_alu_count(sched_queue &q, unsigned max);
|
||||
|
||||
// check if we have not less than threshold ready alu instructions
|
||||
bool check_alu_ready_count(unsigned threshold);
|
||||
};
|
||||
|
||||
|
||||
class gvn : public vpass {
|
||||
using vpass::visit;
|
||||
|
||||
public:
|
||||
|
||||
gvn(shader &sh) : vpass(sh) {}
|
||||
|
||||
virtual bool visit(node &n, bool enter);
|
||||
virtual bool visit(cf_node &n, bool enter);
|
||||
virtual bool visit(alu_node &n, bool enter);
|
||||
virtual bool visit(alu_packed_node &n, bool enter);
|
||||
virtual bool visit(fetch_node &n, bool enter);
|
||||
virtual bool visit(region_node &n, bool enter);
|
||||
|
||||
private:
|
||||
|
||||
void process_op(node &n, bool rewrite = true);
|
||||
|
||||
// returns true if the value was rewritten
|
||||
bool process_src(value* &v, bool rewrite);
|
||||
|
||||
|
||||
void process_alu_src_constants(node &n, value* &v);
|
||||
};
|
||||
|
||||
|
||||
class if_conversion : public pass {
|
||||
|
||||
public:
|
||||
|
||||
if_conversion(shader &sh) : pass(sh) {}
|
||||
|
||||
virtual int run();
|
||||
|
||||
bool run_on(region_node *r);
|
||||
|
||||
alu_node* convert_phi(value *select, node *phi);
|
||||
|
||||
unsigned try_convert_kills(region_node* r);
|
||||
|
||||
};
|
||||
|
||||
|
||||
class liveness : public rev_vpass {
|
||||
using vpass::visit;
|
||||
|
||||
val_set live;
|
||||
bool live_changed;
|
||||
|
||||
public:
|
||||
|
||||
liveness(shader &s) : rev_vpass(s), live_changed(false) {}
|
||||
|
||||
virtual int init();
|
||||
|
||||
virtual bool visit(node &n, bool enter);
|
||||
virtual bool visit(bb_node &n, bool enter);
|
||||
virtual bool visit(container_node &n, bool enter);
|
||||
virtual bool visit(alu_group_node &n, bool enter);
|
||||
virtual bool visit(cf_node &n, bool enter);
|
||||
virtual bool visit(alu_node &n, bool enter);
|
||||
virtual bool visit(alu_packed_node &n, bool enter);
|
||||
virtual bool visit(fetch_node &n, bool enter);
|
||||
virtual bool visit(region_node &n, bool enter);
|
||||
virtual bool visit(repeat_node &n, bool enter);
|
||||
virtual bool visit(depart_node &n, bool enter);
|
||||
virtual bool visit(if_node &n, bool enter);
|
||||
|
||||
private:
|
||||
|
||||
void update_interferences();
|
||||
void process_op(node &n);
|
||||
|
||||
bool remove_val(value *v);
|
||||
bool remove_vec(vvec &v);
|
||||
bool process_outs(node& n);
|
||||
void process_ins(node& n);
|
||||
|
||||
void process_phi_outs(container_node *phi);
|
||||
void process_phi_branch(container_node *phi, unsigned id);
|
||||
|
||||
bool process_maydef(value *v);
|
||||
|
||||
bool add_vec(vvec &vv, bool src);
|
||||
|
||||
void update_src_vec(vvec &vv, bool src);
|
||||
};
|
||||
|
||||
|
||||
struct bool_op_info {
|
||||
bool invert;
|
||||
unsigned int_cvt;
|
||||
|
||||
alu_node *n;
|
||||
};
|
||||
|
||||
class peephole : public pass {
|
||||
|
||||
public:
|
||||
|
||||
peephole(shader &sh) : pass(sh) {}
|
||||
|
||||
virtual int run();
|
||||
|
||||
void run_on(container_node *c);
|
||||
|
||||
void optimize_cc_op(alu_node *a);
|
||||
|
||||
void optimize_SETcc_op(alu_node *a);
|
||||
void optimize_CNDcc_op(alu_node *a);
|
||||
|
||||
bool get_bool_op_info(value *b, bool_op_info& bop);
|
||||
bool get_bool_flt_to_int_source(alu_node* &a);
|
||||
void convert_float_setcc(alu_node *f2i, alu_node *s);
|
||||
};
|
||||
|
||||
|
||||
class psi_ops : public rev_vpass {
|
||||
using rev_vpass::visit;
|
||||
|
||||
public:
|
||||
|
||||
psi_ops(shader &s) : rev_vpass(s) {}
|
||||
|
||||
virtual bool visit(node &n, bool enter);
|
||||
virtual bool visit(alu_node &n, bool enter);
|
||||
|
||||
bool try_inline(node &n);
|
||||
bool try_reduce(node &n);
|
||||
bool eliminate(node &n);
|
||||
|
||||
void unpredicate(node *n);
|
||||
};
|
||||
|
||||
|
||||
// check correctness of the generated code, e.g.:
|
||||
// - expected source operand value is the last value written to its gpr,
|
||||
// - all arguments of phi node should be allocated to the same gpr,
|
||||
// TODO other tests
|
||||
class ra_checker : public pass {
|
||||
|
||||
typedef std::map<sel_chan, value *> reg_value_map;
|
||||
|
||||
typedef std::vector<reg_value_map> regmap_stack;
|
||||
|
||||
regmap_stack rm_stack;
|
||||
unsigned rm_stk_level;
|
||||
|
||||
value* prev_dst[5];
|
||||
|
||||
public:
|
||||
|
||||
ra_checker(shader &sh) : pass(sh) {}
|
||||
|
||||
virtual int run();
|
||||
|
||||
void run_on(container_node *c);
|
||||
|
||||
void dump_error(const error_info &e);
|
||||
void dump_all_errors();
|
||||
|
||||
private:
|
||||
|
||||
reg_value_map& rmap() { return rm_stack[rm_stk_level]; }
|
||||
|
||||
void push_stack();
|
||||
void pop_stack();
|
||||
|
||||
// when going out of the alu clause, values in the clause temporary gprs,
|
||||
// AR, predicate values, PS/PV are destroyed
|
||||
void kill_alu_only_regs();
|
||||
void error(node *n, unsigned id, std::string msg);
|
||||
|
||||
void check_phi_src(container_node *p, unsigned id);
|
||||
void process_phi_dst(container_node *p);
|
||||
void check_alu_group(alu_group_node *g);
|
||||
void process_op_dst(node *n);
|
||||
void check_op_src(node *n);
|
||||
void check_src_vec(node *n, unsigned id, vvec &vv, bool src);
|
||||
void check_value_gpr(node *n, unsigned id, value *v);
|
||||
};
|
||||
|
||||
// =======================================
|
||||
|
||||
|
||||
class ra_coalesce : public pass {
|
||||
|
||||
public:
|
||||
|
||||
ra_coalesce(shader &sh) : pass(sh) {}
|
||||
|
||||
virtual int run();
|
||||
};
|
||||
|
||||
|
||||
|
||||
// =======================================
|
||||
|
||||
class ra_init : public pass {
|
||||
|
||||
public:
|
||||
|
||||
ra_init(shader &sh) : pass(sh) {}
|
||||
|
||||
virtual int run();
|
||||
|
||||
private:
|
||||
|
||||
void ra_node(container_node *c);
|
||||
void process_op(node *n);
|
||||
|
||||
void color(value *v);
|
||||
|
||||
void color_bs_constraint(ra_constraint *c);
|
||||
|
||||
void assign_color(value *v, sel_chan c);
|
||||
void alloc_arrays();
|
||||
};
|
||||
|
||||
// =======================================
|
||||
|
||||
class ra_split : public pass {
|
||||
|
||||
public:
|
||||
|
||||
ra_split(shader &sh) : pass(sh) {}
|
||||
|
||||
virtual int run();
|
||||
|
||||
void split(container_node *n);
|
||||
void split_op(node *n);
|
||||
void split_alu_packed(alu_packed_node *n);
|
||||
void split_vector_inst(node *n);
|
||||
|
||||
void split_packed_ins(alu_packed_node *n);
|
||||
|
||||
#if 0
|
||||
void split_pinned_outs(node *n);
|
||||
#endif
|
||||
|
||||
void split_vec(vvec &vv, vvec &v1, vvec &v2, bool allow_swz);
|
||||
|
||||
void split_phi_src(container_node *loc, container_node *c, unsigned id,
|
||||
bool loop);
|
||||
void split_phi_dst(node *loc, container_node *c, bool loop);
|
||||
void init_phi_constraints(container_node *c);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ssa_prepare : public vpass {
|
||||
using vpass::visit;
|
||||
|
||||
typedef std::vector<val_set> vd_stk;
|
||||
vd_stk stk;
|
||||
|
||||
unsigned level;
|
||||
|
||||
public:
|
||||
ssa_prepare(shader &s) : vpass(s), level(0) {}
|
||||
|
||||
virtual bool visit(cf_node &n, bool enter);
|
||||
virtual bool visit(alu_node &n, bool enter);
|
||||
virtual bool visit(fetch_node &n, bool enter);
|
||||
virtual bool visit(region_node &n, bool enter);
|
||||
virtual bool visit(repeat_node &n, bool enter);
|
||||
virtual bool visit(depart_node &n, bool enter);
|
||||
|
||||
private:
|
||||
|
||||
void push_stk() {
|
||||
++level;
|
||||
if (level + 1 > stk.size())
|
||||
stk.resize(level+1);
|
||||
else
|
||||
stk[level].clear();
|
||||
}
|
||||
void pop_stk() {
|
||||
assert(level);
|
||||
--level;
|
||||
stk[level].add_set(stk[level + 1]);
|
||||
}
|
||||
|
||||
void add_defs(node &n);
|
||||
|
||||
val_set & cur_set() { return stk[level]; }
|
||||
|
||||
container_node* create_phi_nodes(int count);
|
||||
};
|
||||
|
||||
class ssa_rename : public vpass {
|
||||
using vpass::visit;
|
||||
|
||||
typedef sb_map<value*, unsigned> def_map;
|
||||
|
||||
def_map def_count;
|
||||
std::stack<def_map> rename_stack;
|
||||
|
||||
typedef std::map<uint32_t, value*> val_map;
|
||||
val_map values;
|
||||
|
||||
public:
|
||||
|
||||
ssa_rename(shader &s) : vpass(s) {}
|
||||
|
||||
virtual int init();
|
||||
|
||||
virtual bool visit(container_node &n, bool enter);
|
||||
virtual bool visit(node &n, bool enter);
|
||||
virtual bool visit(alu_group_node &n, bool enter);
|
||||
virtual bool visit(cf_node &n, bool enter);
|
||||
virtual bool visit(alu_node &n, bool enter);
|
||||
virtual bool visit(alu_packed_node &n, bool enter);
|
||||
virtual bool visit(fetch_node &n, bool enter);
|
||||
virtual bool visit(region_node &n, bool enter);
|
||||
virtual bool visit(repeat_node &n, bool enter);
|
||||
virtual bool visit(depart_node &n, bool enter);
|
||||
virtual bool visit(if_node &n, bool enter);
|
||||
|
||||
private:
|
||||
|
||||
void push(node *phi);
|
||||
void pop();
|
||||
|
||||
unsigned get_index(def_map& m, value* v);
|
||||
void set_index(def_map& m, value* v, unsigned index);
|
||||
unsigned new_index(def_map& m, value* v);
|
||||
|
||||
value* rename_use(node *n, value* v);
|
||||
value* rename_def(node *def, value* v);
|
||||
|
||||
void rename_src_vec(node *n, vvec &vv, bool src);
|
||||
void rename_dst_vec(node *def, vvec &vv, bool set_def);
|
||||
|
||||
void rename_src(node *n);
|
||||
void rename_dst(node *n);
|
||||
|
||||
void rename_phi_args(container_node *phi, unsigned op, bool def);
|
||||
|
||||
void rename_virt(node *n);
|
||||
void rename_virt_val(node *n, value *v);
|
||||
};
|
||||
|
||||
class bc_finalizer : public pass {
|
||||
|
||||
cf_node *last_export[EXP_TYPE_COUNT];
|
||||
cf_node *last_cf;
|
||||
|
||||
unsigned ngpr;
|
||||
unsigned nstack;
|
||||
|
||||
public:
|
||||
|
||||
bc_finalizer(shader &sh) : pass(sh), last_export(), last_cf(), ngpr(),
|
||||
nstack() {}
|
||||
|
||||
virtual int run();
|
||||
|
||||
void finalize_loop(region_node *r);
|
||||
void finalize_if(region_node *r);
|
||||
|
||||
void run_on(container_node *c);
|
||||
|
||||
void finalize_alu_group(alu_group_node *g);
|
||||
void finalize_alu_src(alu_group_node *g, alu_node *a);
|
||||
|
||||
void emit_set_grad(fetch_node* f);
|
||||
void finalize_fetch(fetch_node *f);
|
||||
|
||||
void finalize_cf(cf_node *c);
|
||||
|
||||
sel_chan translate_kcache(cf_node *alu, value *v);
|
||||
|
||||
void update_ngpr(unsigned gpr);
|
||||
void update_nstack(region_node *r, unsigned add = 0);
|
||||
|
||||
void cf_peephole();
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace r600_sb
|
||||
|
||||
#endif /* SB_PASS_H_ */
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#define PPH_DEBUG 0
|
||||
|
||||
#if PPH_DEBUG
|
||||
#define PPH_DUMP(q) do { q } while (0)
|
||||
#else
|
||||
#define PPH_DUMP(q)
|
||||
#endif
|
||||
|
||||
#include "sb_shader.h"
|
||||
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
int peephole::run() {
|
||||
|
||||
run_on(sh.root);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void peephole::run_on(container_node* c) {
|
||||
|
||||
for (node_riterator I = c->rbegin(), E = c->rend(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
|
||||
if (n->is_container())
|
||||
run_on(static_cast<container_node*>(n));
|
||||
else {
|
||||
|
||||
if (n->is_alu_inst()) {
|
||||
alu_node *a = static_cast<alu_node*>(n);
|
||||
|
||||
if (a->bc.op_ptr->flags & AF_CC_MASK) {
|
||||
optimize_cc_op(a);
|
||||
} else if (a->bc.op == ALU_OP1_FLT_TO_INT) {
|
||||
|
||||
alu_node *s = a;
|
||||
if (get_bool_flt_to_int_source(s)) {
|
||||
convert_float_setcc(a, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void peephole::optimize_cc_op(alu_node* a) {
|
||||
unsigned aflags = a->bc.op_ptr->flags;
|
||||
|
||||
if (aflags & (AF_PRED | AF_SET)) {
|
||||
optimize_SETcc_op(a);
|
||||
} else if (aflags & AF_CMOV) {
|
||||
optimize_CNDcc_op(a);
|
||||
}
|
||||
}
|
||||
|
||||
void peephole::convert_float_setcc(alu_node *f2i, alu_node *s) {
|
||||
alu_node *ns = sh.clone(s);
|
||||
|
||||
ns->dst[0] = f2i->dst[0];
|
||||
ns->dst[0]->def = ns;
|
||||
ns->bc.set_op(ns->bc.op + (ALU_OP2_SETE_DX10 - ALU_OP2_SETE));
|
||||
f2i->insert_after(ns);
|
||||
f2i->remove();
|
||||
}
|
||||
|
||||
void peephole::optimize_SETcc_op(alu_node* a) {
|
||||
|
||||
unsigned flags = a->bc.op_ptr->flags;
|
||||
unsigned cc = flags & AF_CC_MASK;
|
||||
unsigned cmp_type = flags & AF_CMP_TYPE_MASK;
|
||||
unsigned dst_type = flags & AF_DST_TYPE_MASK;
|
||||
bool is_pred = flags & AF_PRED;
|
||||
|
||||
// TODO handle other cases
|
||||
|
||||
if (a->src[1]->is_const() && (cc == AF_CC_E || cc == AF_CC_NE) &&
|
||||
a->src[1]->literal_value == literal(0) &&
|
||||
a->bc.src[0].neg == 0 && a->bc.src[0].abs == 0) {
|
||||
|
||||
value *s = a->src[0];
|
||||
|
||||
bool_op_info bop = {};
|
||||
|
||||
PPH_DUMP(
|
||||
cerr << "optSETcc ";
|
||||
dump::dump_op(a);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
if (!get_bool_op_info(s, bop))
|
||||
return;
|
||||
|
||||
if (cc == AF_CC_E)
|
||||
bop.invert = !bop.invert;
|
||||
|
||||
bool swap_args = false;
|
||||
|
||||
cc = bop.n->bc.op_ptr->flags & AF_CC_MASK;
|
||||
|
||||
if (bop.invert)
|
||||
cc = invert_setcc_condition(cc, swap_args);
|
||||
|
||||
if (bop.int_cvt) {
|
||||
assert(cmp_type != AF_FLOAT_CMP);
|
||||
cmp_type = AF_FLOAT_CMP;
|
||||
}
|
||||
|
||||
PPH_DUMP(
|
||||
cerr << "boi node: ";
|
||||
dump::dump_op(bop.n);
|
||||
cerr << " invert: " << bop.invert << " int_cvt: " << bop.int_cvt;
|
||||
cerr <<"\n";
|
||||
);
|
||||
|
||||
unsigned newop = is_pred ? get_predsetcc_opcode(cc, cmp_type) :
|
||||
get_setcc_opcode(cc, cmp_type, dst_type != AF_FLOAT_DST);
|
||||
|
||||
a->bc.set_op(newop);
|
||||
|
||||
if (swap_args) {
|
||||
a->src[0] = bop.n->src[1];
|
||||
a->src[1] = bop.n->src[0];
|
||||
a->bc.src[0] = bop.n->bc.src[1];
|
||||
a->bc.src[1] = bop.n->bc.src[0];
|
||||
|
||||
} else {
|
||||
a->src[0] = bop.n->src[0];
|
||||
a->src[1] = bop.n->src[1];
|
||||
a->bc.src[0] = bop.n->bc.src[0];
|
||||
a->bc.src[1] = bop.n->bc.src[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void peephole::optimize_CNDcc_op(alu_node* a) {
|
||||
|
||||
//TODO
|
||||
}
|
||||
|
||||
bool peephole::get_bool_flt_to_int_source(alu_node* &a) {
|
||||
|
||||
if (a->bc.op == ALU_OP1_FLT_TO_INT) {
|
||||
|
||||
if (a->bc.src[0].neg || a->bc.src[0].abs || a->bc.src[0].rel)
|
||||
return false;
|
||||
|
||||
value *s = a->src[0];
|
||||
if (!s || !s->def || !s->def->is_alu_inst())
|
||||
return false;
|
||||
|
||||
alu_node *dn = static_cast<alu_node*>(s->def);
|
||||
|
||||
if (dn->is_alu_op(ALU_OP1_TRUNC)) {
|
||||
s = dn->src[0];
|
||||
if (!s || !s->def || !s->def->is_alu_inst())
|
||||
return false;
|
||||
|
||||
if (dn->bc.src[0].neg != 1 || dn->bc.src[0].abs != 0 ||
|
||||
dn->bc.src[0].rel != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dn = static_cast<alu_node*>(s->def);
|
||||
|
||||
}
|
||||
|
||||
if (dn->bc.op_ptr->flags & AF_SET) {
|
||||
a = dn;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool peephole::get_bool_op_info(value* b, bool_op_info& bop) {
|
||||
|
||||
node *d = b->def;
|
||||
|
||||
if (!d || !d->is_alu_inst())
|
||||
return false;
|
||||
|
||||
alu_node *dn = static_cast<alu_node*>(d);
|
||||
|
||||
if (dn->bc.op_ptr->flags & AF_SET) {
|
||||
bop.n = dn;
|
||||
|
||||
if (dn->bc.op_ptr->flags & AF_DX10)
|
||||
bop.int_cvt = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (get_bool_flt_to_int_source(dn)) {
|
||||
bop.n = dn;
|
||||
bop.int_cvt = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include "sb_shader.h"
|
||||
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
bool r600_sb::psi_ops::visit(alu_node& n, bool enter) {
|
||||
if (enter) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool psi_ops::visit(node& n, bool enter) {
|
||||
if (enter) {
|
||||
assert(n.subtype == NST_PSI);
|
||||
|
||||
try_inline(n);
|
||||
|
||||
// TODO eliminate predication until there is full support in all passes
|
||||
// unpredicate instructions and replace psi-nodes with conditional moves
|
||||
eliminate(n);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
value* get_pred_val(node &n) {
|
||||
value *pred_val = NULL;
|
||||
|
||||
for (vvec::iterator I = n.src.begin(), E = n.src.end(); I != E; I += 3) {
|
||||
value* &pred = *I;
|
||||
if (pred) {
|
||||
if (!pred_val)
|
||||
pred_val = pred;
|
||||
else {
|
||||
assert(pred == pred_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pred_val;
|
||||
}
|
||||
|
||||
// for now we'll never inline psi's with different predicate values,
|
||||
// so psi node may only contain the refs to one predicate value.
|
||||
bool psi_ops::try_inline(node& n) {
|
||||
assert(n.subtype == NST_PSI);
|
||||
|
||||
vvec &ns = n.src;
|
||||
|
||||
int sz = ns.size();
|
||||
assert(sz && (sz % 3 == 0));
|
||||
|
||||
value *pred_val = get_pred_val(n);
|
||||
|
||||
int ps_mask = 0;
|
||||
|
||||
bool r = false;
|
||||
|
||||
for (int i = sz - 1; i >= 0; i -= 3) {
|
||||
|
||||
if (ps_mask == 3) {
|
||||
ns.erase(ns.begin(), ns.begin() + i + 1);
|
||||
return r;
|
||||
}
|
||||
|
||||
value* val = ns[i];
|
||||
value* predsel = ns[i-1];
|
||||
int ps = !predsel ? 3 : predsel == sh.get_pred_sel(0) ? 1 : 2;
|
||||
|
||||
assert(val->def);
|
||||
|
||||
if (val->def->subtype == NST_PSI && ps == 3) {
|
||||
if (get_pred_val(*val->def) != pred_val)
|
||||
continue;
|
||||
|
||||
vvec &ds = val->def->src;
|
||||
|
||||
ns.insert(ns.begin() + i + 1, ds.begin(), ds.end());
|
||||
ns.erase(ns.begin() + i - 2, ns.begin() + i + 1);
|
||||
i += ds.size();
|
||||
r = true;
|
||||
|
||||
} else {
|
||||
if ((ps_mask & ps) == ps) {
|
||||
// this predicate select is subsumed by already handled ops
|
||||
ns.erase(ns.begin() + i - 2, ns.begin() + i + 1);
|
||||
} else {
|
||||
ps_mask |= ps;
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
bool psi_ops::try_reduce(node& n) {
|
||||
assert(n.subtype == NST_PSI);
|
||||
assert(n.src.size() % 3 == 0);
|
||||
|
||||
// TODO
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void psi_ops::unpredicate(node *n) {
|
||||
|
||||
if (!n->is_alu_inst())
|
||||
return;
|
||||
|
||||
alu_node *a = static_cast<alu_node*>(n);
|
||||
a->pred = NULL;
|
||||
}
|
||||
|
||||
bool psi_ops::eliminate(node& n) {
|
||||
assert(n.subtype == NST_PSI);
|
||||
assert(n.src.size() == 6);
|
||||
|
||||
value *d = n.dst[0];
|
||||
|
||||
value *s1 = n.src[2];
|
||||
value *s2 = n.src[5];
|
||||
|
||||
value *pred = n.src[3];
|
||||
|
||||
bool psel = n.src[4] == sh.get_pred_sel(0);
|
||||
|
||||
value *sel = get_select_value_for_em(sh, pred);
|
||||
|
||||
if (s1->is_undef()) {
|
||||
if (s2->is_undef()) {
|
||||
|
||||
} else {
|
||||
n.insert_after(sh.create_mov(d, s2));
|
||||
}
|
||||
} else if (s2->is_undef()) {
|
||||
n.insert_after(sh.create_mov(d, s1));
|
||||
} else {
|
||||
alu_node *a = sh.create_alu();
|
||||
a->bc.set_op(ALU_OP3_CNDE_INT);
|
||||
|
||||
a->dst.push_back(d);
|
||||
a->src.push_back(sel);
|
||||
|
||||
if (psel) {
|
||||
a->src.push_back(s1);
|
||||
a->src.push_back(s2);
|
||||
} else {
|
||||
a->src.push_back(s2);
|
||||
a->src.push_back(s1);
|
||||
}
|
||||
|
||||
n.insert_after(a);
|
||||
}
|
||||
|
||||
n.remove();
|
||||
|
||||
if (s1->is_any_gpr() && !s1->is_undef() && s1->def)
|
||||
unpredicate(s1->def);
|
||||
if (s2->is_any_gpr() && !s2->is_undef() && s2->def)
|
||||
unpredicate(s2->def);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#ifndef R600_SB_H_
|
||||
#define R600_SB_H_
|
||||
|
||||
struct r600_shader;
|
||||
|
||||
void r600_sb_context_destroy(void *sctx);
|
||||
|
||||
int r600_sb_bytecode_process(struct r600_context *rctx,
|
||||
struct r600_bytecode *bc,
|
||||
struct r600_shader *pshader,
|
||||
int dump_source_bytecode,
|
||||
int optimize);
|
||||
|
||||
#endif //R600_SB_H_
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "sb_shader.h"
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
int ra_checker::run() {
|
||||
|
||||
rm_stack.clear();
|
||||
rm_stack.resize(1);
|
||||
rm_stk_level = 0;
|
||||
|
||||
process_op_dst(sh.root);
|
||||
|
||||
run_on(sh.root);
|
||||
|
||||
assert(rm_stk_level == 0);
|
||||
|
||||
dump_all_errors();
|
||||
|
||||
assert(sh.errors.empty());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ra_checker::dump_error(const error_info &e) {
|
||||
|
||||
cerr << "error at : ";
|
||||
dump::dump_op(e.n);
|
||||
|
||||
cerr << "\n";
|
||||
cerr << " : " << e.message << "\n";
|
||||
}
|
||||
|
||||
void ra_checker::dump_all_errors() {
|
||||
for (error_map::iterator I = sh.errors.begin(), E = sh.errors.end();
|
||||
I != E; ++I) {
|
||||
dump_error(I->second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ra_checker::error(node *n, unsigned id, std::string msg) {
|
||||
error_info e;
|
||||
e.n = n;
|
||||
e.arg_index = id;
|
||||
e.message = msg;
|
||||
sh.errors.insert(std::make_pair(n, e));
|
||||
}
|
||||
|
||||
void ra_checker::push_stack() {
|
||||
++rm_stk_level;
|
||||
if (rm_stack.size() == rm_stk_level)
|
||||
rm_stack.push_back(rm_stack.back());
|
||||
else
|
||||
rm_stack[rm_stk_level] = rm_stack[rm_stk_level - 1];
|
||||
}
|
||||
|
||||
void ra_checker::pop_stack() {
|
||||
--rm_stk_level;
|
||||
}
|
||||
|
||||
void ra_checker::kill_alu_only_regs() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void ra_checker::check_value_gpr(node *n, unsigned id, value *v) {
|
||||
sel_chan gpr = v->gpr;
|
||||
if (!gpr) {
|
||||
std::ostringstream o;
|
||||
o << "operand value " << *v << " is not allocated";
|
||||
error(n, id, o.str());
|
||||
return;
|
||||
}
|
||||
reg_value_map::iterator F = rmap().find(v->gpr);
|
||||
if (F == rmap().end()) {
|
||||
std::ostringstream o;
|
||||
o << "operand value " << *v << " was not previously written to its gpr";
|
||||
error(n, id, o.str());
|
||||
return;
|
||||
}
|
||||
if (!F->second->v_equal(v)) {
|
||||
std::ostringstream o;
|
||||
o << "expected operand value " << *v
|
||||
<< ", gpr contains " << *(F->second);
|
||||
error(n, id, o.str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void ra_checker::check_src_vec(node *n, unsigned id, vvec &vv, bool src) {
|
||||
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (!v || !v->is_sgpr())
|
||||
continue;
|
||||
|
||||
if (v->is_rel()) {
|
||||
if (!v->rel) {
|
||||
std::ostringstream o;
|
||||
o << "expected relative offset in " << *v;
|
||||
error(n, id, o.str());
|
||||
return;
|
||||
}
|
||||
} else if (src) {
|
||||
check_value_gpr(n, id, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ra_checker::check_op_src(node *n) {
|
||||
check_src_vec(n, 0, n->dst, false);
|
||||
check_src_vec(n, 100, n->src, true);
|
||||
}
|
||||
|
||||
void ra_checker::process_op_dst(node *n) {
|
||||
|
||||
unsigned id = 0;
|
||||
|
||||
for (vvec::iterator I = n->dst.begin(), E = n->dst.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
|
||||
++id;
|
||||
|
||||
if (!v)
|
||||
continue;
|
||||
|
||||
if (v->is_sgpr()) {
|
||||
|
||||
if (!v->gpr) {
|
||||
std::ostringstream o;
|
||||
o << "destination operand " << *v << " is not allocated";
|
||||
error(n, id, o.str());
|
||||
return;
|
||||
}
|
||||
|
||||
rmap()[v->gpr] = v;
|
||||
} else if (v->is_rel()) {
|
||||
if (v->rel->is_const()) {
|
||||
rmap()[v->get_final_gpr()] = v;
|
||||
} else {
|
||||
unsigned sz = v->array->array_size;
|
||||
unsigned start = v->array->gpr;
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
rmap()[start + (i << 2)] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ra_checker::check_phi_src(container_node *p, unsigned id) {
|
||||
for (node_iterator I = p->begin(), E = p->end(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
value *s = n->src[id];
|
||||
if (s->is_sgpr())
|
||||
check_value_gpr(n, id, s);
|
||||
}
|
||||
}
|
||||
|
||||
void ra_checker::process_phi_dst(container_node *p) {
|
||||
for (node_iterator I = p->begin(), E = p->end(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
process_op_dst(n);
|
||||
}
|
||||
}
|
||||
|
||||
void ra_checker::check_alu_group(alu_group_node *g) {
|
||||
|
||||
for (node_iterator I = g->begin(), E = g->end(); I != E; ++I) {
|
||||
node *a = *I;
|
||||
if (!a->is_alu_inst()) {
|
||||
std::ostringstream o;
|
||||
o << "non-alu node inside alu group";
|
||||
error(a, 0, o.str());
|
||||
return;
|
||||
}
|
||||
|
||||
check_op_src(a);
|
||||
}
|
||||
|
||||
std::fill(prev_dst, prev_dst + 5, (value*)NULL);
|
||||
|
||||
for (node_iterator I = g->begin(), E = g->end(); I != E; ++I) {
|
||||
alu_node *a = static_cast<alu_node*>(*I);
|
||||
|
||||
process_op_dst(a);
|
||||
|
||||
unsigned slot = a->bc.slot;
|
||||
prev_dst[slot] = a->dst[0];
|
||||
}
|
||||
}
|
||||
|
||||
void ra_checker::run_on(container_node* c) {
|
||||
|
||||
if (c->is_region()) {
|
||||
region_node *r = static_cast<region_node*>(c);
|
||||
if (r->loop_phi) {
|
||||
check_phi_src(r->loop_phi, 0);
|
||||
process_phi_dst(r->loop_phi);
|
||||
}
|
||||
} else if (c->is_depart()) {
|
||||
|
||||
push_stack();
|
||||
|
||||
} else if (c->is_repeat()) {
|
||||
|
||||
push_stack();
|
||||
|
||||
}
|
||||
|
||||
for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
|
||||
if(n->is_cf_inst() || n->is_fetch_inst()) {
|
||||
check_op_src(n);
|
||||
process_op_dst(n);
|
||||
}
|
||||
|
||||
if (n->is_container()) {
|
||||
if (n->is_alu_group()) {
|
||||
check_alu_group(static_cast<alu_group_node*>(n));
|
||||
} else {
|
||||
container_node *nc = static_cast<container_node*>(n);
|
||||
run_on(nc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c->is_depart()) {
|
||||
depart_node *r = static_cast<depart_node*>(c);
|
||||
check_phi_src(r->target->phi, r->dep_id);
|
||||
pop_stack();
|
||||
} else if (c->is_repeat()) {
|
||||
repeat_node *r = static_cast<repeat_node*>(c);
|
||||
assert (r->target->loop_phi);
|
||||
|
||||
pop_stack();
|
||||
} else if (c->is_region()) {
|
||||
region_node *r = static_cast<region_node*>(c);
|
||||
if (r->phi)
|
||||
process_phi_dst(r->phi);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,608 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#define RA_DEBUG 0
|
||||
|
||||
#if RA_DEBUG
|
||||
#define RA_DUMP(q) do { q } while (0)
|
||||
#else
|
||||
#define RA_DUMP(q)
|
||||
#endif
|
||||
|
||||
#include "sb_shader.h"
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
int ra_coalesce::run() {
|
||||
|
||||
sh.coal.run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void coalescer::add_edge(value* a, value* b, unsigned cost) {
|
||||
assert(a->is_sgpr() && b->is_sgpr());
|
||||
edges.insert(new ra_edge(a,b, cost));
|
||||
}
|
||||
|
||||
void coalescer::create_chunk(value *v) {
|
||||
|
||||
assert(v->is_sgpr());
|
||||
|
||||
ra_chunk *c = new ra_chunk();
|
||||
|
||||
c->values.push_back(v);
|
||||
|
||||
if (v->is_chan_pinned())
|
||||
c->flags |= RCF_PIN_CHAN;
|
||||
if (v->is_reg_pinned()) {
|
||||
c->flags |= RCF_PIN_REG;
|
||||
}
|
||||
|
||||
c->pin = v->pin_gpr;
|
||||
|
||||
RA_DUMP(
|
||||
cerr << "create_chunk: ";
|
||||
dump_chunk(c);
|
||||
);
|
||||
|
||||
all_chunks.push_back(c);
|
||||
v->chunk = c;
|
||||
|
||||
}
|
||||
|
||||
void coalescer::unify_chunks(ra_edge *e) {
|
||||
ra_chunk *c1 = e->a->chunk, *c2 = e->b->chunk;
|
||||
|
||||
RA_DUMP(
|
||||
cerr << "unify_chunks: ";
|
||||
dump_chunk(c1);
|
||||
dump_chunk(c2);
|
||||
);
|
||||
|
||||
if (c2->is_chan_pinned() && !c1->is_chan_pinned()) {
|
||||
c1->flags |= RCF_PIN_CHAN;
|
||||
c1->pin = sel_chan(c1->pin.sel(), c2->pin.chan());
|
||||
}
|
||||
|
||||
if (c2->is_reg_pinned() && !c1->is_reg_pinned()) {
|
||||
c1->flags |= RCF_PIN_REG;
|
||||
c1->pin = sel_chan(c2->pin.sel(), c1->pin.chan());
|
||||
}
|
||||
|
||||
c1->values.reserve(c1->values.size() + c2->values.size());
|
||||
|
||||
for (vvec::iterator I = c2->values.begin(), E = c2->values.end(); I != E;
|
||||
++I) {
|
||||
(*I)->chunk = c1;
|
||||
c1->values.push_back(*I);
|
||||
}
|
||||
|
||||
chunk_vec::iterator F = std::find(all_chunks.begin(), all_chunks.end(), c2);
|
||||
assert(F != all_chunks.end());
|
||||
|
||||
all_chunks.erase(F);
|
||||
|
||||
c1->cost += c2->cost + e->cost;
|
||||
delete c2;
|
||||
}
|
||||
|
||||
bool coalescer::chunks_interference(ra_chunk *c1, ra_chunk *c2) {
|
||||
unsigned pin_flags = (c1->flags & c2->flags) &
|
||||
(RCF_PIN_CHAN | RCF_PIN_REG);
|
||||
|
||||
if ((pin_flags & RCF_PIN_CHAN) &&
|
||||
c1->pin.chan() != c2->pin.chan())
|
||||
return true;
|
||||
|
||||
if ((pin_flags & RCF_PIN_REG) &&
|
||||
c1->pin.sel() != c2->pin.sel())
|
||||
return true;
|
||||
|
||||
for (vvec::iterator I = c1->values.begin(), E = c1->values.end(); I != E;
|
||||
++I) {
|
||||
value *v1 = *I;
|
||||
|
||||
for (vvec::iterator I = c2->values.begin(), E = c2->values.end(); I != E;
|
||||
++I) {
|
||||
value *v2 = *I;
|
||||
|
||||
if (!v1->v_equal(v2) && v1->interferences.contains(v2))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void coalescer::build_chunks() {
|
||||
|
||||
for (edge_queue::iterator I = edges.begin(), E = edges.end();
|
||||
I != E; ++I) {
|
||||
|
||||
ra_edge *e = *I;
|
||||
|
||||
if (!e->a->chunk)
|
||||
create_chunk(e->a);
|
||||
|
||||
if (!e->b->chunk)
|
||||
create_chunk(e->b);
|
||||
|
||||
ra_chunk *c1 = e->a->chunk, *c2 = e->b->chunk;
|
||||
|
||||
if (c1 == c2) {
|
||||
c1->cost += e->cost;
|
||||
} else if (!chunks_interference(c1, c2))
|
||||
unify_chunks(e);
|
||||
}
|
||||
}
|
||||
|
||||
ra_constraint* coalescer::create_constraint(constraint_kind kind) {
|
||||
ra_constraint *c = new ra_constraint(kind);
|
||||
all_constraints.push_back(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
void coalescer::dump_edges() {
|
||||
cerr << "######## affinity edges\n";
|
||||
|
||||
for (edge_queue::iterator I = edges.begin(), E = edges.end();
|
||||
I != E; ++I) {
|
||||
ra_edge* e = *I;
|
||||
cerr << " ra_edge ";
|
||||
dump::dump_val(e->a);
|
||||
cerr << " <-> ";
|
||||
dump::dump_val(e->b);
|
||||
cerr << " cost = " << e->cost << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void coalescer::dump_chunks() {
|
||||
cerr << "######## chunks\n";
|
||||
|
||||
for (chunk_vec::iterator I = all_chunks.begin(), E = all_chunks.end();
|
||||
I != E; ++I) {
|
||||
ra_chunk* c = *I;
|
||||
dump_chunk(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void coalescer::dump_constraint_queue() {
|
||||
cerr << "######## constraints\n";
|
||||
|
||||
for (constraint_queue::iterator I = constraints.begin(),
|
||||
E = constraints.end(); I != E; ++I) {
|
||||
ra_constraint* c = *I;
|
||||
dump_constraint(c);
|
||||
}
|
||||
}
|
||||
|
||||
void coalescer::dump_chunk(ra_chunk* c) {
|
||||
cerr << " ra_chunk cost = " << c->cost << " : ";
|
||||
dump::dump_vec(c->values);
|
||||
|
||||
if (c->flags & RCF_PIN_REG)
|
||||
cerr << " REG = " << c->pin.sel();
|
||||
|
||||
if (c->flags & RCF_PIN_CHAN)
|
||||
cerr << " CHAN = " << c->pin.chan();
|
||||
|
||||
cerr << (c->flags & RCF_GLOBAL ? " GLOBAL" : "");
|
||||
|
||||
cerr << "\n";
|
||||
}
|
||||
|
||||
void coalescer::dump_constraint(ra_constraint* c) {
|
||||
cerr << " ra_constraint: ";
|
||||
switch (c->kind) {
|
||||
case CK_PACKED_BS: cerr << "PACKED_BS"; break;
|
||||
case CK_PHI: cerr << "PHI"; break;
|
||||
case CK_SAME_REG: cerr << "SAME_REG"; break;
|
||||
default: cerr << "UNKNOWN_KIND"; assert(0); break;
|
||||
}
|
||||
|
||||
cerr << " cost = " << c->cost << " : ";
|
||||
dump::dump_vec(c->values);
|
||||
|
||||
cerr << "\n";
|
||||
}
|
||||
|
||||
void coalescer::get_chunk_interferences(ra_chunk *c, val_set &s) {
|
||||
|
||||
for (vvec::iterator I = c->values.begin(), E = c->values.end(); I != E;
|
||||
++I) {
|
||||
value *v = *I;
|
||||
s.add_set(v->interferences);
|
||||
}
|
||||
s.remove_vec(c->values);
|
||||
}
|
||||
|
||||
void coalescer::build_chunk_queue() {
|
||||
for (chunk_vec::iterator I = all_chunks.begin(),
|
||||
E = all_chunks.end(); I != E; ++I) {
|
||||
ra_chunk *c = *I;
|
||||
|
||||
if (!c->is_fixed())
|
||||
chunks.insert(c);
|
||||
}
|
||||
}
|
||||
|
||||
void coalescer::build_constraint_queue() {
|
||||
for (constraint_vec::iterator I = all_constraints.begin(),
|
||||
E = all_constraints.end(); I != E; ++I) {
|
||||
ra_constraint *c = *I;
|
||||
unsigned cost = 0;
|
||||
|
||||
if (c->values.empty() || !c->values.front()->is_sgpr())
|
||||
continue;
|
||||
|
||||
if (c->kind != CK_SAME_REG)
|
||||
continue;
|
||||
|
||||
for (vvec::iterator I = c->values.begin(), E = c->values.end();
|
||||
I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (!v->chunk)
|
||||
create_chunk(v);
|
||||
else
|
||||
cost += v->chunk->cost;
|
||||
}
|
||||
c->cost = cost;
|
||||
constraints.insert(c);
|
||||
}
|
||||
}
|
||||
|
||||
void coalescer::color_chunks() {
|
||||
|
||||
for (chunk_queue::iterator I = chunks.begin(), E = chunks.end();
|
||||
I != E; ++I) {
|
||||
ra_chunk *c = *I;
|
||||
if (c->is_fixed() || c->values.size() == 1)
|
||||
continue;
|
||||
|
||||
sb_bitset rb;
|
||||
val_set interf;
|
||||
|
||||
get_chunk_interferences(c, interf);
|
||||
|
||||
RA_DUMP(
|
||||
cerr << "color_chunks: ";
|
||||
dump_chunk(c);
|
||||
cerr << "\n interferences: ";
|
||||
dump::dump_set(sh,interf);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
init_reg_bitset(rb, interf);
|
||||
|
||||
unsigned pass = c->is_reg_pinned() ? 0 : 1;
|
||||
|
||||
unsigned cs = c->is_chan_pinned() ? c->pin.chan() : 0;
|
||||
unsigned ce = c->is_chan_pinned() ? cs + 1 : 4;
|
||||
|
||||
unsigned color = 0;
|
||||
|
||||
while (pass < 2) {
|
||||
|
||||
unsigned rs, re;
|
||||
|
||||
if (pass == 0) {
|
||||
rs = c->pin.sel();
|
||||
re = rs + 1;
|
||||
} else {
|
||||
rs = 0;
|
||||
re = sh.num_nontemp_gpr();
|
||||
}
|
||||
|
||||
for (unsigned reg = rs; reg < re; ++reg) {
|
||||
for (unsigned chan = cs; chan < ce; ++chan) {
|
||||
unsigned bit = sel_chan(reg, chan);
|
||||
if (bit >= rb.size() || !rb.get(bit)) {
|
||||
color = bit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (color)
|
||||
break;
|
||||
}
|
||||
|
||||
if (color)
|
||||
break;
|
||||
|
||||
++pass;
|
||||
}
|
||||
|
||||
assert(color);
|
||||
color_chunk(c, color);
|
||||
}
|
||||
}
|
||||
|
||||
void coalescer::init_reg_bitset(sb_bitset &bs, val_set &vs) {
|
||||
|
||||
for (val_set::iterator I = vs.begin(sh), E = vs.end(sh); I != E; ++I) {
|
||||
value *v = *I;
|
||||
|
||||
if (!v->is_sgpr())
|
||||
continue;
|
||||
|
||||
if (v->gpr) {
|
||||
if (v->gpr >= bs.size())
|
||||
bs.resize(v->gpr + 64);
|
||||
bs.set(v->gpr, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void coalescer::color_chunk(ra_chunk *c, sel_chan color) {
|
||||
|
||||
vvec vv = c->values;
|
||||
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E;
|
||||
++I) {
|
||||
value *v = *I;
|
||||
|
||||
if (v->is_reg_pinned() && v->pin_gpr.sel() != color.sel()) {
|
||||
detach_value(v);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (v->is_chan_pinned() && v->pin_gpr.chan() != color.chan()) {
|
||||
detach_value(v);
|
||||
continue;
|
||||
}
|
||||
|
||||
v->gpr = color;
|
||||
|
||||
if (v->constraint && v->constraint->kind == CK_PHI)
|
||||
v->fix();
|
||||
|
||||
|
||||
RA_DUMP(
|
||||
cerr << " assigned " << color << " to ";
|
||||
dump::dump_val(v);
|
||||
cerr << "\n";
|
||||
);
|
||||
}
|
||||
|
||||
c->pin = color;
|
||||
|
||||
if (c->is_reg_pinned()) {
|
||||
c->fix();
|
||||
}
|
||||
}
|
||||
|
||||
coalescer::~coalescer() {
|
||||
|
||||
// FIXME use pool allocator ??
|
||||
|
||||
for (constraint_vec::iterator I = all_constraints.begin(),
|
||||
E = all_constraints.end(); I != E; ++I) {
|
||||
delete (*I);
|
||||
}
|
||||
|
||||
for (chunk_vec::iterator I = all_chunks.begin(),
|
||||
E = all_chunks.end(); I != E; ++I) {
|
||||
delete (*I);
|
||||
}
|
||||
|
||||
for (edge_queue::iterator I = edges.begin(), E = edges.end();
|
||||
I != E; ++I) {
|
||||
delete (*I);
|
||||
}
|
||||
}
|
||||
|
||||
void coalescer::run() {
|
||||
RA_DUMP( dump_edges(); );
|
||||
|
||||
build_chunks();
|
||||
RA_DUMP( dump_chunks(); );
|
||||
|
||||
build_constraint_queue();
|
||||
RA_DUMP( dump_constraint_queue(); );
|
||||
|
||||
color_constraints();
|
||||
|
||||
build_chunk_queue();
|
||||
color_chunks();
|
||||
}
|
||||
|
||||
void coalescer::color_phi_constraint(ra_constraint* c) {
|
||||
}
|
||||
|
||||
ra_chunk* coalescer::detach_value(value *v) {
|
||||
|
||||
vvec::iterator F = std::find(v->chunk->values.begin(),
|
||||
v->chunk->values.end(), v);
|
||||
|
||||
assert(F != v->chunk->values.end());
|
||||
v->chunk->values.erase(F);
|
||||
create_chunk(v);
|
||||
|
||||
if (v->is_reg_pinned()) {
|
||||
v->chunk->fix();
|
||||
}
|
||||
|
||||
RA_DUMP(
|
||||
cerr << " detached : ";
|
||||
dump_chunk(v->chunk);
|
||||
);
|
||||
|
||||
return v->chunk;
|
||||
|
||||
}
|
||||
|
||||
void coalescer::color_reg_constraint(ra_constraint *c) {
|
||||
unsigned k, cnt = c->values.size();
|
||||
vvec & cv = c->values;
|
||||
|
||||
ra_chunk *ch[4];
|
||||
unsigned swz[4] = {0, 1, 2, 3};
|
||||
val_set interf[4];
|
||||
sb_bitset rb[4];
|
||||
|
||||
bool reg_pinned = false;
|
||||
unsigned pin_reg = ~0;
|
||||
|
||||
unsigned chan_mask = 0;
|
||||
|
||||
k = 0;
|
||||
for (vvec::iterator I = cv.begin(), E = cv.end(); I != E; ++I, ++k) {
|
||||
value *v = *I;
|
||||
|
||||
if (!v->chunk)
|
||||
create_chunk(v);
|
||||
|
||||
ch[k] = v->chunk;
|
||||
|
||||
if (v->chunk->is_chan_pinned()) {
|
||||
unsigned chan = 1 << v->chunk->pin.chan();
|
||||
|
||||
if (chan & chan_mask) { // channel already in use
|
||||
ch[k] = detach_value(v);
|
||||
assert(!ch[k]->is_chan_pinned());
|
||||
} else {
|
||||
chan_mask |= chan;
|
||||
}
|
||||
}
|
||||
|
||||
if (v->chunk->is_reg_pinned()) {
|
||||
if (!reg_pinned) {
|
||||
reg_pinned = true;
|
||||
pin_reg = v->chunk->pin.sel();
|
||||
}
|
||||
}
|
||||
|
||||
get_chunk_interferences(ch[k], interf[k]);
|
||||
init_reg_bitset(rb[k], interf[k]);
|
||||
}
|
||||
|
||||
unsigned start_reg, end_reg;
|
||||
|
||||
start_reg = 0;
|
||||
end_reg = sh.num_nontemp_gpr();
|
||||
|
||||
unsigned min_reg = end_reg;
|
||||
unsigned min_swz[4];
|
||||
unsigned i, pass = reg_pinned ? 0 : 1;
|
||||
|
||||
bool done = false;
|
||||
|
||||
while (pass < 2) {
|
||||
|
||||
unsigned rs, re;
|
||||
|
||||
if (pass == 0) {
|
||||
re = pin_reg + 1;
|
||||
rs = pin_reg;
|
||||
} else {
|
||||
re = end_reg;
|
||||
rs = start_reg;
|
||||
}
|
||||
|
||||
min_reg = re;
|
||||
|
||||
// cycle on swizzle combinations
|
||||
do {
|
||||
for (i = 0; i < cnt; ++i) {
|
||||
if (ch[i]->flags & RCF_PIN_CHAN)
|
||||
if (ch[i]->pin.chan() != swz[i])
|
||||
break;
|
||||
}
|
||||
if (i != cnt)
|
||||
continue;
|
||||
|
||||
// looking for minimal reg number such that the constrained chunks
|
||||
// may be colored with the current swizzle combination
|
||||
for (unsigned reg = rs; reg < min_reg; ++reg) {
|
||||
for (i = 0; i < cnt; ++i) {
|
||||
unsigned bit = sel_chan(reg, swz[i]);
|
||||
if (bit < rb[i].size() && rb[i].get(bit))
|
||||
break;
|
||||
}
|
||||
if (i == cnt) {
|
||||
done = true;
|
||||
min_reg = reg;
|
||||
std::copy(swz, swz + 4, min_swz);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pass == 0 && done)
|
||||
break;
|
||||
|
||||
} while (std::next_permutation(swz, swz + 4));
|
||||
|
||||
if (pass == 0 && done)
|
||||
break;
|
||||
|
||||
++pass;
|
||||
};
|
||||
|
||||
assert(done);
|
||||
|
||||
RA_DUMP(
|
||||
cerr << "min reg = " << min_reg << " min_swz = "
|
||||
<< min_swz[0] << min_swz[1] << min_swz[2] << min_swz[3] << "\n";
|
||||
);
|
||||
|
||||
for (i = 0; i < cnt; ++i) {
|
||||
sel_chan color(min_reg, min_swz[i]);
|
||||
ra_chunk *cc = ch[i];
|
||||
|
||||
if (cc->is_fixed()) {
|
||||
if (cc->pin != color)
|
||||
cc = detach_value(cv[i]);
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
color_chunk(cc, color);
|
||||
cc->fix();
|
||||
}
|
||||
}
|
||||
|
||||
void coalescer::color_constraints() {
|
||||
for (constraint_queue::iterator I = constraints.begin(),
|
||||
E = constraints.end(); I != E; ++I) {
|
||||
|
||||
ra_constraint *c = *I;
|
||||
|
||||
RA_DUMP(
|
||||
cerr << "color_constraints: ";
|
||||
dump_constraint(c);
|
||||
);
|
||||
|
||||
if (c->kind == CK_SAME_REG)
|
||||
color_reg_constraint(c);
|
||||
else if (c->kind == CK_PHI)
|
||||
color_phi_constraint(c);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,793 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#define RA_DEBUG 0
|
||||
|
||||
#if RA_DEBUG
|
||||
#define RA_DUMP(q) do { q } while (0)
|
||||
#else
|
||||
#define RA_DUMP(q)
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "sb_bc.h"
|
||||
#include "sb_shader.h"
|
||||
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
class regbits {
|
||||
typedef uint32_t basetype;
|
||||
static const unsigned bt_bytes = sizeof(basetype);
|
||||
static const unsigned bt_index_shift = 5;
|
||||
static const unsigned bt_index_mask = (1u << bt_index_shift) - 1;
|
||||
static const unsigned bt_bits = bt_bytes << 3;
|
||||
static const unsigned size = MAX_GPR * 4 / bt_bits;
|
||||
|
||||
basetype dta[size];
|
||||
|
||||
unsigned num_temps;
|
||||
|
||||
public:
|
||||
|
||||
regbits(unsigned num_temps) : dta(), num_temps(num_temps) {}
|
||||
regbits(unsigned num_temps, unsigned value) : num_temps(num_temps)
|
||||
{ set_all(value); }
|
||||
|
||||
regbits(shader &sh, val_set &vs) : num_temps(sh.get_ctx().alu_temp_gprs)
|
||||
{ set_all(1); from_val_set(sh, vs); }
|
||||
|
||||
void set_all(unsigned val);
|
||||
void from_val_set(shader &sh, val_set &vs);
|
||||
|
||||
void set(unsigned index);
|
||||
void clear(unsigned index);
|
||||
bool get(unsigned index);
|
||||
|
||||
void set(unsigned index, unsigned val);
|
||||
|
||||
sel_chan find_free_bit(unsigned start);
|
||||
sel_chan find_free_chans(unsigned mask);
|
||||
sel_chan find_free_array(unsigned size, unsigned mask);
|
||||
|
||||
void dump();
|
||||
};
|
||||
|
||||
// =======================================
|
||||
|
||||
void regbits::dump() {
|
||||
for (unsigned i = 0; i < size * bt_bits; ++i) {
|
||||
|
||||
if (!(i & 31))
|
||||
cerr << "\n";
|
||||
|
||||
if (!(i & 3))
|
||||
cerr << " " << std::setw(3) << (i / 4) << " ";
|
||||
|
||||
cerr << (get(i) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void regbits::set_all(unsigned v) {
|
||||
memset(&dta, v ? 0xFF : 0x00, size * bt_bytes);
|
||||
}
|
||||
|
||||
void regbits::from_val_set(shader &sh, val_set& vs) {
|
||||
val_set &s = vs;
|
||||
unsigned g;
|
||||
for (val_set::iterator I = s.begin(sh), E = s.end(sh); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (v->is_any_gpr()) {
|
||||
g = v->get_final_gpr();
|
||||
if (!g)
|
||||
continue;
|
||||
} else
|
||||
continue;
|
||||
|
||||
assert(g);
|
||||
--g;
|
||||
assert(g < 512);
|
||||
clear(g);
|
||||
}
|
||||
}
|
||||
|
||||
void regbits::set(unsigned index) {
|
||||
unsigned ih = index >> bt_index_shift;
|
||||
unsigned il = index & bt_index_mask;
|
||||
dta[ih] |= ((basetype)1u << il);
|
||||
}
|
||||
|
||||
void regbits::clear(unsigned index) {
|
||||
unsigned ih = index >> bt_index_shift;
|
||||
unsigned il = index & bt_index_mask;
|
||||
assert(ih < size);
|
||||
dta[ih] &= ~((basetype)1u << il);
|
||||
}
|
||||
|
||||
bool regbits::get(unsigned index) {
|
||||
unsigned ih = index >> bt_index_shift;
|
||||
unsigned il = index & bt_index_mask;
|
||||
return dta[ih] & ((basetype)1u << il);
|
||||
}
|
||||
|
||||
void regbits::set(unsigned index, unsigned val) {
|
||||
unsigned ih = index >> bt_index_shift;
|
||||
unsigned il = index & bt_index_mask;
|
||||
basetype bm = 1u << il;
|
||||
dta[ih] = (dta[ih] & ~bm) | (val << il);
|
||||
}
|
||||
|
||||
// free register for ra means the bit is set
|
||||
sel_chan regbits::find_free_bit(unsigned start) {
|
||||
unsigned elt = start >> bt_index_shift;
|
||||
unsigned bit = start & bt_index_mask;
|
||||
|
||||
unsigned end = start < MAX_GPR - num_temps ? MAX_GPR - num_temps : MAX_GPR;
|
||||
|
||||
while (elt < end && !dta[elt]) {
|
||||
++elt;
|
||||
bit = 0;
|
||||
}
|
||||
|
||||
if (elt >= end)
|
||||
return 0;
|
||||
|
||||
// FIXME this seems broken when not starting from 0
|
||||
|
||||
bit += __builtin_ctz(dta[elt]);
|
||||
return ((elt << bt_index_shift) | bit) + 1;
|
||||
}
|
||||
|
||||
// find free gpr component to use as indirectly addressable array
|
||||
sel_chan regbits::find_free_array(unsigned length, unsigned mask) {
|
||||
unsigned cc[4] = {};
|
||||
|
||||
// FIXME optimize this. though hopefully we won't have a lot of arrays
|
||||
for (unsigned a = 0; a < MAX_GPR - num_temps; ++a) {
|
||||
for(unsigned c = 0; c < MAX_CHAN; ++c) {
|
||||
if (mask & (1 << c)) {
|
||||
if (get((a << 2) | c)) {
|
||||
if (++cc[c] == length)
|
||||
return sel_chan(a - length + 1, c);
|
||||
} else {
|
||||
cc[c] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sel_chan regbits::find_free_chans(unsigned mask) {
|
||||
unsigned elt = 0;
|
||||
unsigned bit = 0;
|
||||
|
||||
basetype cd = dta[elt] >> bit;
|
||||
|
||||
do {
|
||||
|
||||
if (!cd) {
|
||||
if (++elt < size)
|
||||
cd = dta[elt];
|
||||
else
|
||||
return 0;
|
||||
|
||||
bit = 0;
|
||||
}
|
||||
|
||||
unsigned p = __builtin_ctz(cd) & ~(basetype)3u;
|
||||
|
||||
if (p > bt_bits - bit) {
|
||||
if (++elt < size)
|
||||
cd = dta[elt];
|
||||
else
|
||||
return 0;
|
||||
bit = 0;
|
||||
}
|
||||
|
||||
bit += p;
|
||||
cd >>= p;
|
||||
|
||||
if ((cd & mask) == mask) {
|
||||
return ((elt << bt_index_shift) | bit) + 1;
|
||||
}
|
||||
|
||||
bit += 4;
|
||||
cd >>= 4;
|
||||
|
||||
} while (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ================================
|
||||
|
||||
void ra_init::alloc_arrays() {
|
||||
|
||||
gpr_array_vec &ga = sh.arrays();
|
||||
|
||||
for(gpr_array_vec::iterator I = ga.begin(), E = ga.end(); I != E; ++I) {
|
||||
gpr_array *a = *I;
|
||||
|
||||
RA_DUMP(
|
||||
cerr << "array [" << a->array_size << "] at " << a->base_gpr << "\n";
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
bool dead = a->is_dead();
|
||||
|
||||
if (dead) {
|
||||
RA_DUMP( cerr << " DEAD\n"; );
|
||||
continue;
|
||||
}
|
||||
|
||||
val_set &s = a->interferences;
|
||||
|
||||
|
||||
for (val_set::iterator I = s.begin(sh), E = s.end(sh); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (v->array == a)
|
||||
s.remove_val(v);
|
||||
}
|
||||
|
||||
RA_DUMP(
|
||||
cerr << " interf: ";
|
||||
dump::dump_set(sh, s);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
regbits rb(sh, s);
|
||||
|
||||
sel_chan base = rb.find_free_array(a->array_size,
|
||||
(1 << a->base_gpr.chan()));
|
||||
|
||||
RA_DUMP( cerr << " found base: " << base << "\n"; );
|
||||
|
||||
a->gpr = base;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int ra_init::run() {
|
||||
|
||||
alloc_arrays();
|
||||
|
||||
ra_node(sh.root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ra_init::ra_node(container_node* c) {
|
||||
|
||||
for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
|
||||
node *n = *I;
|
||||
if (n->type == NT_OP) {
|
||||
process_op(n);
|
||||
}
|
||||
if (n->is_container() && !n->is_alu_packed()) {
|
||||
ra_node(static_cast<container_node*>(n));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ra_init::process_op(node* n) {
|
||||
|
||||
bool copy = n->is_copy_mov();
|
||||
|
||||
RA_DUMP(
|
||||
cerr << "ra_init: process_op : ";
|
||||
dump::dump_op(n);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
if (n->is_alu_packed()) {
|
||||
for (vvec::iterator I = n->src.begin(), E = n->src.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (v && v->is_sgpr() && v->constraint &&
|
||||
v->constraint->kind == CK_PACKED_BS) {
|
||||
color_bs_constraint(v->constraint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n->is_fetch_inst() || n->is_cf_inst()) {
|
||||
for (vvec::iterator I = n->src.begin(), E = n->src.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (v && v->is_sgpr())
|
||||
color(v);
|
||||
}
|
||||
}
|
||||
|
||||
for (vvec::iterator I = n->dst.begin(), E = n->dst.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (!v)
|
||||
continue;
|
||||
if (v->is_sgpr()) {
|
||||
if (!v->gpr) {
|
||||
if (copy && !v->constraint) {
|
||||
value *s = *(n->src.begin() + (I - n->dst.begin()));
|
||||
assert(s);
|
||||
if (s->is_sgpr()) {
|
||||
assign_color(v, s->gpr);
|
||||
}
|
||||
} else
|
||||
color(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ra_init::color_bs_constraint(ra_constraint* c) {
|
||||
vvec &vv = c->values;
|
||||
assert(vv.size() <= 8);
|
||||
|
||||
RA_DUMP(
|
||||
cerr << "color_bs_constraint: ";
|
||||
dump::dump_vec(vv);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
regbits rb(ctx.alu_temp_gprs);
|
||||
|
||||
unsigned chan_count[4] = {};
|
||||
unsigned allowed_chans = 0b1111;
|
||||
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
sel_chan gpr = v->get_final_gpr();
|
||||
|
||||
if (!v || v->is_dead())
|
||||
continue;
|
||||
|
||||
val_set interf;
|
||||
|
||||
if (v->chunk)
|
||||
sh.coal.get_chunk_interferences(v->chunk, interf);
|
||||
else
|
||||
interf = v->interferences;
|
||||
|
||||
RA_DUMP(
|
||||
cerr << " processing " << *v << " interferences : ";
|
||||
dump::dump_set(sh, interf);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
if (gpr) {
|
||||
unsigned chan = gpr.chan();
|
||||
if (chan_count[chan] < 3) {
|
||||
++chan_count[chan];
|
||||
continue;
|
||||
} else {
|
||||
v->flags &= ~VLF_FIXED;
|
||||
allowed_chans &= ~(1 << chan);
|
||||
assert(allowed_chans);
|
||||
}
|
||||
}
|
||||
|
||||
v->gpr = 0;
|
||||
|
||||
gpr = 1;
|
||||
rb.set_all(1);
|
||||
|
||||
|
||||
rb.from_val_set(sh, interf);
|
||||
|
||||
RA_DUMP(
|
||||
cerr << " regbits : ";
|
||||
rb.dump();
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
while (allowed_chans && gpr.sel() < sh.num_nontemp_gpr()) {
|
||||
|
||||
while (rb.get(gpr - 1) == 0)
|
||||
gpr = gpr + 1;
|
||||
|
||||
RA_DUMP(
|
||||
cerr << " trying " << gpr << "\n";
|
||||
);
|
||||
|
||||
unsigned chan = gpr.chan();
|
||||
if (chan_count[chan] < 3) {
|
||||
++chan_count[chan];
|
||||
|
||||
if (v->chunk) {
|
||||
vvec::iterator F = std::find(v->chunk->values.begin(),
|
||||
v->chunk->values.end(),
|
||||
v);
|
||||
v->chunk->values.erase(F);
|
||||
v->chunk = NULL;
|
||||
}
|
||||
|
||||
assign_color(v, gpr);
|
||||
break;
|
||||
} else {
|
||||
allowed_chans &= ~(1 << chan);
|
||||
}
|
||||
gpr = gpr + 1;
|
||||
}
|
||||
|
||||
if (!gpr) {
|
||||
cerr << "color_bs_constraint: failed...\n";
|
||||
assert(!"coloring failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ra_init::color(value* v) {
|
||||
|
||||
if (v->constraint && v->constraint->kind == CK_PACKED_BS) {
|
||||
color_bs_constraint(v->constraint);
|
||||
return;
|
||||
}
|
||||
|
||||
if (v->chunk && v->chunk->is_fixed())
|
||||
return;
|
||||
|
||||
RA_DUMP(
|
||||
cerr << "coloring ";
|
||||
dump::dump_val(v);
|
||||
cerr << " interferences ";
|
||||
dump::dump_set(sh, v->interferences);
|
||||
cerr << "\n";
|
||||
);
|
||||
|
||||
if (v->is_reg_pinned()) {
|
||||
assert(v->is_chan_pinned());
|
||||
assign_color(v, v->pin_gpr);
|
||||
return;
|
||||
}
|
||||
|
||||
regbits rb(sh, v->interferences);
|
||||
sel_chan c;
|
||||
|
||||
if (v->is_chan_pinned()) {
|
||||
RA_DUMP( cerr << "chan_pinned = " << v->pin_gpr.chan() << " "; );
|
||||
unsigned mask = 1 << v->pin_gpr.chan();
|
||||
c = rb.find_free_chans(mask) + v->pin_gpr.chan();
|
||||
} else {
|
||||
c = rb.find_free_bit(0);
|
||||
}
|
||||
|
||||
assert(c && c.sel() < 128 - ctx.alu_temp_gprs && "color failed");
|
||||
assign_color(v, c);
|
||||
}
|
||||
|
||||
void ra_init::assign_color(value* v, sel_chan c) {
|
||||
v->gpr = c;
|
||||
RA_DUMP(
|
||||
cerr << "colored ";
|
||||
dump::dump_val(v);
|
||||
cerr << " to " << c << "\n";
|
||||
);
|
||||
}
|
||||
|
||||
// ===================================================
|
||||
|
||||
int ra_split::run() {
|
||||
split(sh.root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ra_split::split_phi_src(container_node *loc, container_node *c,
|
||||
unsigned id, bool loop) {
|
||||
for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
|
||||
node *p = *I;
|
||||
value* &v = p->src[id], *d = p->dst[0];
|
||||
assert(v);
|
||||
|
||||
if (!d->is_sgpr() || v->is_undef())
|
||||
continue;
|
||||
|
||||
value *t = sh.create_temp_value();
|
||||
if (loop && id == 0)
|
||||
loc->insert_before(sh.create_copy_mov(t, v));
|
||||
else
|
||||
loc->push_back(sh.create_copy_mov(t, v));
|
||||
v = t;
|
||||
|
||||
sh.coal.add_edge(v, d, coalescer::phi_cost);
|
||||
}
|
||||
}
|
||||
|
||||
void ra_split::split_phi_dst(node* loc, container_node *c, bool loop) {
|
||||
for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
|
||||
node *p = *I;
|
||||
value* &v = p->dst[0];
|
||||
assert(v);
|
||||
|
||||
if (!v->is_sgpr())
|
||||
continue;
|
||||
|
||||
value *t = sh.create_temp_value();
|
||||
node *cp = sh.create_copy_mov(v, t);
|
||||
if (loop)
|
||||
static_cast<container_node*>(loc)->push_front(cp);
|
||||
else
|
||||
loc->insert_after(cp);
|
||||
v = t;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ra_split::init_phi_constraints(container_node *c) {
|
||||
for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
|
||||
node *p = *I;
|
||||
ra_constraint *cc = sh.coal.create_constraint(CK_PHI);
|
||||
cc->values.push_back(p->dst[0]);
|
||||
|
||||
for (vvec::iterator I = p->src.begin(), E = p->src.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (v->is_sgpr())
|
||||
cc->values.push_back(v);
|
||||
}
|
||||
|
||||
cc->update_values();
|
||||
}
|
||||
}
|
||||
|
||||
void ra_split::split(container_node* n) {
|
||||
|
||||
if (n->type == NT_DEPART) {
|
||||
depart_node *d = static_cast<depart_node*>(n);
|
||||
if (d->target->phi)
|
||||
split_phi_src(d, d->target->phi, d->dep_id, false);
|
||||
} else if (n->type == NT_REPEAT) {
|
||||
repeat_node *r = static_cast<repeat_node*>(n);
|
||||
if (r->target->loop_phi)
|
||||
split_phi_src(r, r->target->loop_phi, r->rep_id, true);
|
||||
} else if (n->type == NT_REGION) {
|
||||
region_node *r = static_cast<region_node*>(n);
|
||||
if (r->phi) {
|
||||
split_phi_dst(r, r->phi, false);
|
||||
}
|
||||
if (r->loop_phi) {
|
||||
split_phi_dst(r->get_entry_code_location(), r->loop_phi,
|
||||
true);
|
||||
split_phi_src(r, r->loop_phi, 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
for (node_riterator N, I = n->rbegin(), E = n->rend(); I != E; I = N) {
|
||||
N = I;
|
||||
++N;
|
||||
node *o = *I;
|
||||
if (o->type == NT_OP) {
|
||||
split_op(o);
|
||||
} else if (o->is_container()) {
|
||||
split(static_cast<container_node*>(o));
|
||||
}
|
||||
}
|
||||
|
||||
if (n->type == NT_REGION) {
|
||||
region_node *r = static_cast<region_node*>(n);
|
||||
if (r->phi)
|
||||
init_phi_constraints(r->phi);
|
||||
if (r->loop_phi)
|
||||
init_phi_constraints(r->loop_phi);
|
||||
}
|
||||
}
|
||||
|
||||
void ra_split::split_op(node* n) {
|
||||
switch(n->subtype) {
|
||||
case NST_ALU_PACKED_INST:
|
||||
split_alu_packed(static_cast<alu_packed_node*>(n));
|
||||
break;
|
||||
case NST_FETCH_INST:
|
||||
case NST_CF_INST:
|
||||
split_vector_inst(n);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ra_split::split_packed_ins(alu_packed_node *n) {
|
||||
vvec vv = n->src;
|
||||
vvec sv, dv;
|
||||
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
|
||||
value *&v = *I;
|
||||
|
||||
if (v && v->is_any_gpr() && !v->is_undef()) {
|
||||
|
||||
vvec::iterator F = std::find(sv.begin(), sv.end(), v);
|
||||
value *t;
|
||||
|
||||
if (F != sv.end()) {
|
||||
t = *(dv.begin() + (F - sv.begin()));
|
||||
} else {
|
||||
t = sh.create_temp_value();
|
||||
sv.push_back(v);
|
||||
dv.push_back(t);
|
||||
}
|
||||
v = t;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned cnt = sv.size();
|
||||
|
||||
if (cnt > 0) {
|
||||
n->src = vv;
|
||||
for (vvec::iterator SI = sv.begin(), DI = dv.begin(), SE = sv.end();
|
||||
SI != SE; ++SI, ++DI) {
|
||||
n->insert_before(sh.create_copy_mov(*DI, *SI));
|
||||
}
|
||||
|
||||
ra_constraint *c = sh.coal.create_constraint(CK_PACKED_BS);
|
||||
c->values = dv;
|
||||
c->update_values();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO handle other packed ops for cayman
|
||||
void ra_split::split_alu_packed(alu_packed_node* n) {
|
||||
switch (n->op()) {
|
||||
case ALU_OP2_DOT4:
|
||||
case ALU_OP2_CUBE:
|
||||
split_packed_ins(n);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ra_split::split_vec(vvec &vv, vvec &v1, vvec &v2, bool allow_swz) {
|
||||
unsigned ch = 0;
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I, ++ch) {
|
||||
|
||||
value* &o = *I;
|
||||
|
||||
if (o) {
|
||||
|
||||
assert(!o->is_dead());
|
||||
|
||||
if (o->is_undef())
|
||||
continue;
|
||||
|
||||
if (allow_swz && o->is_float_0_or_1())
|
||||
continue;
|
||||
|
||||
value *t;
|
||||
vvec::iterator F =
|
||||
allow_swz ? find(v2.begin(), v2.end(), o) : v2.end();
|
||||
|
||||
if (F != v2.end()) {
|
||||
t = *(v1.begin() + (F - v2.begin()));
|
||||
} else {
|
||||
t = sh.create_temp_value();
|
||||
|
||||
if (!allow_swz) {
|
||||
t->flags |= VLF_PIN_CHAN;
|
||||
t->pin_gpr = sel_chan(0, ch);
|
||||
}
|
||||
|
||||
v2.push_back(o);
|
||||
v1.push_back(t);
|
||||
}
|
||||
o = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ra_split::split_vector_inst(node* n) {
|
||||
ra_constraint *c;
|
||||
|
||||
bool call_fs = n->is_cf_op(CF_OP_CALL_FS);
|
||||
bool no_src_swizzle = n->is_cf_inst() && (n->cf_op_flags() & CF_MEM);
|
||||
|
||||
no_src_swizzle |= n->is_fetch_op(FETCH_OP_VFETCH) ||
|
||||
n->is_fetch_op(FETCH_OP_SEMFETCH);
|
||||
|
||||
if (!n->src.empty() && !call_fs) {
|
||||
|
||||
// we may have more than one source vector -
|
||||
// fetch instructions with FF_USEGRAD have gradient values in
|
||||
// src vectors 1 (src[4-7] and 2 (src[8-11])
|
||||
|
||||
unsigned nvec = n->src.size() >> 2;
|
||||
assert(nvec << 2 == n->src.size());
|
||||
|
||||
for (unsigned nv = 0; nv < nvec; ++nv) {
|
||||
vvec sv, tv, nsrc(4);
|
||||
unsigned arg_start = nv << 2;
|
||||
|
||||
std::copy(n->src.begin() + arg_start,
|
||||
n->src.begin() + arg_start + 4,
|
||||
nsrc.begin());
|
||||
|
||||
split_vec(nsrc, tv, sv, !no_src_swizzle);
|
||||
|
||||
unsigned cnt = sv.size();
|
||||
|
||||
if (no_src_swizzle || cnt) {
|
||||
|
||||
std::copy(nsrc.begin(), nsrc.end(), n->src.begin() + arg_start);
|
||||
|
||||
for(unsigned i = 0, s = tv.size(); i < s; ++i) {
|
||||
n->insert_before(sh.create_copy_mov(tv[i], sv[i]));
|
||||
}
|
||||
|
||||
c = sh.coal.create_constraint(CK_SAME_REG);
|
||||
c->values = tv;
|
||||
c->update_values();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!n->dst.empty()) {
|
||||
vvec sv, tv, ndst = n->dst;
|
||||
|
||||
split_vec(ndst, tv, sv, true);
|
||||
|
||||
if (sv.size()) {
|
||||
n->dst = ndst;
|
||||
|
||||
node *lp = n;
|
||||
for(unsigned i = 0, s = tv.size(); i < s; ++i) {
|
||||
lp->insert_after(sh.create_copy_mov(sv[i], tv[i]));
|
||||
lp = lp->next;
|
||||
}
|
||||
|
||||
if (call_fs) {
|
||||
for (unsigned i = 0, cnt = tv.size(); i < cnt; ++i) {
|
||||
value *v = tv[i];
|
||||
value *s = sv[i];
|
||||
if (!v)
|
||||
continue;
|
||||
|
||||
v->flags |= VLF_PIN_REG | VLF_PIN_CHAN;
|
||||
s->flags &= ~(VLF_PIN_REG | VLF_PIN_CHAN);
|
||||
sel_chan sel;
|
||||
|
||||
if (s->is_rel()) {
|
||||
assert(s->rel->is_const());
|
||||
sel = sel_chan(s->select.sel() +
|
||||
s->rel->get_const_value().u,
|
||||
s->select.chan());
|
||||
} else
|
||||
sel = s->select;
|
||||
|
||||
v->gpr = v->pin_gpr = sel;
|
||||
v->fix();
|
||||
}
|
||||
} else {
|
||||
c = sh.coal.create_constraint(CK_SAME_REG);
|
||||
c->values = tv;
|
||||
c->update_values();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#ifndef SB_SCHED_H_
|
||||
#define SB_SCHED_H_
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
typedef sb_map<node*, unsigned> uc_map;
|
||||
|
||||
// resource trackers for scheduler
|
||||
|
||||
typedef sb_set<unsigned> kc_lines;
|
||||
|
||||
class rp_kcache_tracker {
|
||||
unsigned rp[4];
|
||||
unsigned uc[4];
|
||||
const unsigned sel_count;
|
||||
|
||||
unsigned kc_sel(sel_chan r) {
|
||||
return sel_count == 4 ? (unsigned)r : ((r - 1) >> 1) + 1;
|
||||
}
|
||||
|
||||
public:
|
||||
rp_kcache_tracker(shader &sh);
|
||||
|
||||
bool try_reserve(node *n);
|
||||
void unreserve(node *n);
|
||||
|
||||
|
||||
bool try_reserve(sel_chan r);
|
||||
void unreserve(sel_chan r);
|
||||
|
||||
void reset();
|
||||
|
||||
unsigned num_sels() { return !!rp[0] + !!rp[1] + !!rp[2] + !!rp[3]; }
|
||||
|
||||
unsigned get_lines(kc_lines &lines);
|
||||
};
|
||||
|
||||
class literal_tracker {
|
||||
literal lt[4];
|
||||
unsigned uc[4];
|
||||
public:
|
||||
literal_tracker() : lt(), uc() {}
|
||||
|
||||
bool try_reserve(alu_node *n);
|
||||
void unreserve(alu_node *n);
|
||||
|
||||
bool try_reserve(literal l);
|
||||
void unreserve(literal l);
|
||||
|
||||
void reset();
|
||||
|
||||
unsigned count() { return !!uc[0] + !!uc[1] + !!uc[2] + !!uc[3]; }
|
||||
|
||||
void init_group_literals(alu_group_node *g);
|
||||
|
||||
};
|
||||
|
||||
class rp_gpr_tracker {
|
||||
// rp[cycle][elem]
|
||||
unsigned rp[3][4];
|
||||
unsigned uc[3][4];
|
||||
|
||||
public:
|
||||
rp_gpr_tracker() : rp(), uc() {}
|
||||
|
||||
bool try_reserve(alu_node *n);
|
||||
void unreserve(alu_node *n);
|
||||
|
||||
bool try_reserve(unsigned cycle, unsigned sel, unsigned chan);
|
||||
void unreserve(unsigned cycle, unsigned sel, unsigned chan);
|
||||
|
||||
void reset();
|
||||
|
||||
void dump();
|
||||
};
|
||||
|
||||
class alu_group_tracker {
|
||||
|
||||
shader &sh;
|
||||
|
||||
rp_kcache_tracker kc;
|
||||
rp_gpr_tracker gpr;
|
||||
literal_tracker lt;
|
||||
|
||||
alu_node * slots[5];
|
||||
|
||||
unsigned available_slots;
|
||||
|
||||
unsigned max_slots;
|
||||
|
||||
typedef std::map<value*, unsigned> value_index_map;
|
||||
|
||||
value_index_map vmap;
|
||||
|
||||
bool has_mova;
|
||||
bool uses_ar;
|
||||
bool has_predset;
|
||||
bool has_kill;
|
||||
bool updates_exec_mask;
|
||||
|
||||
unsigned chan_count[4];
|
||||
|
||||
// param index + 1 (0 means that group doesn't refer to Params)
|
||||
// we can't use more than one param index in a group
|
||||
unsigned interp_param;
|
||||
|
||||
unsigned next_id;
|
||||
|
||||
node_vec packed_ops;
|
||||
|
||||
void assign_slot(unsigned slot, alu_node *n);
|
||||
|
||||
public:
|
||||
alu_group_tracker(shader &sh);
|
||||
|
||||
// FIXME use fast bs correctness check (values for same chan <= 3) ??
|
||||
bool try_reserve(alu_node *n);
|
||||
bool try_reserve(alu_packed_node *p);
|
||||
|
||||
void reinit();
|
||||
void reset(bool keep_packed = false);
|
||||
|
||||
sel_chan get_value_id(value *v);
|
||||
void update_flags(alu_node *n);
|
||||
|
||||
alu_node* slot(unsigned i) { return slots[i]; }
|
||||
|
||||
unsigned used_slots() {
|
||||
return (~available_slots) & ((1 << max_slots) - 1);
|
||||
}
|
||||
|
||||
unsigned inst_count() {
|
||||
return __builtin_popcount(used_slots());
|
||||
}
|
||||
|
||||
unsigned literal_count() { return lt.count(); }
|
||||
unsigned literal_slot_count() { return (literal_count() + 1) >> 1; };
|
||||
unsigned slot_count() { return inst_count() + literal_slot_count(); }
|
||||
|
||||
alu_group_node* emit();
|
||||
|
||||
rp_kcache_tracker& kcache() { return kc; }
|
||||
|
||||
bool has_update_exec_mask() { return updates_exec_mask; }
|
||||
unsigned avail_slots() { return available_slots; }
|
||||
|
||||
void discard_all_slots(container_node &removed_nodes);
|
||||
void discard_slots(unsigned slot_mask, container_node &removed_nodes);
|
||||
|
||||
bool has_ar_load() { return has_mova; }
|
||||
};
|
||||
|
||||
class alu_kcache_tracker {
|
||||
bc_kcache kc[4];
|
||||
sb_set<unsigned> lines;
|
||||
unsigned max_kcs;
|
||||
|
||||
public:
|
||||
|
||||
alu_kcache_tracker(sb_hw_class hc)
|
||||
: kc(), lines(), max_kcs(hc >= HW_CLASS_EVERGREEN ? 4 : 2) {}
|
||||
|
||||
void reset();
|
||||
bool try_reserve(alu_group_tracker >);
|
||||
bool update_kc();
|
||||
void init_clause(bc_cf &bc) {
|
||||
memcpy(bc.kc, kc, sizeof(kc));
|
||||
}
|
||||
};
|
||||
|
||||
class alu_clause_tracker {
|
||||
shader &sh;
|
||||
|
||||
alu_kcache_tracker kt;
|
||||
unsigned slot_count;
|
||||
|
||||
alu_group_tracker grp0;
|
||||
alu_group_tracker grp1;
|
||||
|
||||
unsigned group;
|
||||
|
||||
cf_node *clause;
|
||||
|
||||
bool push_exec_mask;
|
||||
|
||||
public:
|
||||
container_node conflict_nodes;
|
||||
|
||||
// current values of AR and PR registers that we have to preload
|
||||
// till the end of clause (in fact, beginning, because we're scheduling
|
||||
// bottom-up)
|
||||
value *current_ar;
|
||||
value *current_pr;
|
||||
|
||||
alu_clause_tracker(shader &sh);
|
||||
|
||||
void reset();
|
||||
|
||||
// current group
|
||||
alu_group_tracker& grp() { return group ? grp1 : grp0; }
|
||||
// previous group
|
||||
alu_group_tracker& prev_grp() { return group ? grp0 : grp1; }
|
||||
|
||||
void emit_group();
|
||||
void emit_clause(container_node *c);
|
||||
bool check_clause_limits();
|
||||
void new_group();
|
||||
bool is_empty();
|
||||
|
||||
alu_node* create_ar_load();
|
||||
|
||||
void discard_current_group();
|
||||
|
||||
unsigned total_slots() { return slot_count; }
|
||||
};
|
||||
|
||||
class post_scheduler : public pass {
|
||||
|
||||
container_node ready, ready_copies; // alu only
|
||||
container_node pending, bb_pending;
|
||||
bb_node *cur_bb;
|
||||
val_set live; // values live at the end of the alu clause
|
||||
uc_map ucm;
|
||||
alu_clause_tracker alu;
|
||||
|
||||
typedef std::map<sel_chan, value*> rv_map;
|
||||
rv_map regmap, prev_regmap;
|
||||
|
||||
val_set cleared_interf;
|
||||
|
||||
public:
|
||||
|
||||
post_scheduler(shader &sh) : pass(sh),
|
||||
ready(), ready_copies(), pending(), cur_bb(),
|
||||
live(), ucm(), alu(sh), regmap(), cleared_interf() {}
|
||||
|
||||
virtual int run();
|
||||
void run_on(container_node *n);
|
||||
void schedule_bb(bb_node *bb);
|
||||
|
||||
void process_alu(container_node *c);
|
||||
void schedule_alu(container_node *c);
|
||||
bool prepare_alu_group();
|
||||
|
||||
void release_op(node *n);
|
||||
|
||||
void release_src_values(node *n);
|
||||
void release_src_vec(vvec &vv, bool src);
|
||||
void release_src_val(value *v);
|
||||
|
||||
void init_uc_val(container_node *c, value *v);
|
||||
void init_uc_vec(container_node *c, vvec &vv, bool src);
|
||||
unsigned init_ucm(container_node *c, node *n);
|
||||
|
||||
void init_regmap();
|
||||
|
||||
bool check_interferences();
|
||||
|
||||
unsigned try_add_instruction(node *n);
|
||||
|
||||
bool check_copy(node *n);
|
||||
void dump_group(alu_group_tracker &rt);
|
||||
|
||||
bool unmap_dst(alu_node *n);
|
||||
bool unmap_dst_val(value *d);
|
||||
|
||||
bool map_src(alu_node *n);
|
||||
bool map_src_vec(vvec &vv, bool src);
|
||||
bool map_src_val(value *v);
|
||||
|
||||
bool recolor_local(value *v);
|
||||
|
||||
void update_local_interferences();
|
||||
void update_live_src_vec(vvec &vv, val_set &born, bool src);
|
||||
void update_live_dst_vec(vvec &vv);
|
||||
void update_live(node *n, val_set &born);
|
||||
void process_group();
|
||||
|
||||
void set_color_local_val(value *v, sel_chan color);
|
||||
void set_color_local(value *v, sel_chan color);
|
||||
|
||||
void add_interferences(value *v, sb_bitset &rb, val_set &vs);
|
||||
|
||||
void init_globals(val_set &s, bool prealloc);
|
||||
|
||||
void recolor_locals();
|
||||
|
||||
void dump_regmap();
|
||||
|
||||
void emit_load_ar();
|
||||
void emit_clause();
|
||||
|
||||
void process_ready_copies();
|
||||
};
|
||||
|
||||
} // namespace r600_sb
|
||||
|
||||
#endif /* SB_SCHED_H_ */
|
||||
@@ -0,0 +1,660 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include "sb_bc.h"
|
||||
#include "sb_shader.h"
|
||||
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
shader::shader(sb_context &sctx, shader_target t, unsigned id, bool dump)
|
||||
: ctx(sctx), next_temp_value_index(temp_regid_offset),
|
||||
prep_regs_count(), pred_sels(),
|
||||
regions(), inputs(), undef(), val_pool(sizeof(value)),
|
||||
pool(), all_nodes(), errors(), enable_dump(dump),
|
||||
optimized(), id(id),
|
||||
coal(*this), bbs(),
|
||||
target(t), vt(ex), ex(*this), root(),
|
||||
compute_interferences(),
|
||||
has_alu_predication(), uses_gradients(), ngpr(), nstack() {}
|
||||
|
||||
bool shader::assign_slot(alu_node* n, alu_node *slots[5]) {
|
||||
|
||||
unsigned slot_flags = ctx.alu_slots(n->bc.op);
|
||||
unsigned slot = n->bc.dst_chan;
|
||||
|
||||
if (!ctx.is_cayman() && (!(slot_flags & AF_V) || slots[slot]) &&
|
||||
(slot_flags & AF_S))
|
||||
slot = SLOT_TRANS;
|
||||
|
||||
if (slots[slot])
|
||||
return false;
|
||||
|
||||
n->bc.slot = slot;
|
||||
slots[slot] = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
void shader::add_gpr_values(vvec& vec, unsigned gpr, unsigned comp_mask,
|
||||
bool src) {
|
||||
unsigned chan = 0;
|
||||
while (comp_mask) {
|
||||
if (comp_mask & 1) {
|
||||
value *v = get_gpr_value(src, gpr, chan, false);
|
||||
v->flags |= (VLF_PIN_REG | VLF_PIN_CHAN);
|
||||
if (!v->is_rel()) {
|
||||
v->gpr = v->pin_gpr = v->select;
|
||||
v->fix();
|
||||
}
|
||||
vec.push_back(v);
|
||||
}
|
||||
comp_mask >>= 1;
|
||||
++chan;
|
||||
}
|
||||
}
|
||||
|
||||
cf_node* shader::create_clause(node_subtype nst) {
|
||||
cf_node *n = create_cf();
|
||||
|
||||
n->subtype = nst;
|
||||
|
||||
switch (nst) {
|
||||
case NST_ALU_CLAUSE: n->bc.set_op(CF_OP_ALU); break;
|
||||
case NST_TEX_CLAUSE: n->bc.set_op(CF_OP_TEX); break;
|
||||
case NST_VTX_CLAUSE: n->bc.set_op(CF_OP_VTX); break;
|
||||
default: assert(!"invalid clause type"); break;
|
||||
}
|
||||
|
||||
n->bc.barrier = 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
void shader::create_bbs() {
|
||||
create_bbs(root, bbs);
|
||||
}
|
||||
|
||||
void shader::expand_bbs() {
|
||||
expand_bbs(bbs);
|
||||
}
|
||||
|
||||
alu_node* shader::create_mov(value* dst, value* src) {
|
||||
alu_node *n = create_alu();
|
||||
n->bc.set_op(ALU_OP1_MOV);
|
||||
n->dst.push_back(dst);
|
||||
n->src.push_back(src);
|
||||
dst->def = n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
alu_node* shader::create_copy_mov(value* dst, value* src, unsigned affcost) {
|
||||
alu_node *n = create_mov(dst, src);
|
||||
|
||||
dst->assign_source(src);
|
||||
n->flags |= NF_COPY_MOV | NF_DONT_HOIST;
|
||||
|
||||
if (affcost && dst->is_sgpr() && src->is_sgpr())
|
||||
coal.add_edge(src, dst, affcost);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
value* shader::get_value(value_kind kind, sel_chan id,
|
||||
unsigned version) {
|
||||
if (version == 0 && kind == VLK_REG && id.sel() < prep_regs_count)
|
||||
return val_pool[id - 1];
|
||||
|
||||
|
||||
unsigned key = (kind << 28) | (version << 16) | id;
|
||||
value_map::iterator i = reg_values.find(key);
|
||||
if (i != reg_values.end()) {
|
||||
return i->second;
|
||||
}
|
||||
value *v = create_value(kind, id, version);
|
||||
reg_values.insert(std::make_pair(key, v));
|
||||
return v;
|
||||
}
|
||||
|
||||
value* shader::get_special_value(unsigned sv_id, unsigned version) {
|
||||
sel_chan id(sv_id, 0);
|
||||
return get_value(VLK_SPECIAL_REG, id, version);
|
||||
}
|
||||
|
||||
void shader::fill_array_values(gpr_array *a, vvec &vv) {
|
||||
unsigned sz = a->array_size;
|
||||
vv.resize(sz);
|
||||
for (unsigned i = 0; i < a->array_size; ++i) {
|
||||
vv[i] = get_gpr_value(true, a->base_gpr.sel() + i, a->base_gpr.chan(),
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
value* shader::get_gpr_value(bool src, unsigned reg, unsigned chan, bool rel,
|
||||
unsigned version) {
|
||||
sel_chan id(reg, chan);
|
||||
value *v;
|
||||
gpr_array *a = get_gpr_array(reg, chan);
|
||||
if (rel) {
|
||||
assert(a);
|
||||
v = create_value(VLK_REL_REG, id, 0);
|
||||
v->rel = get_special_value(SV_AR_INDEX);
|
||||
fill_array_values(a, v->muse);
|
||||
if (!src)
|
||||
fill_array_values(a, v->mdef);
|
||||
} else {
|
||||
if (version == 0 && reg < prep_regs_count)
|
||||
return (val_pool[id - 1]);
|
||||
|
||||
v = get_value(VLK_REG, id, version);
|
||||
}
|
||||
|
||||
v->array = a;
|
||||
v->pin_gpr = v->select;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
value* shader::create_temp_value() {
|
||||
sel_chan id(++next_temp_value_index, 0);
|
||||
return get_value(VLK_TEMP, id, 0);
|
||||
}
|
||||
|
||||
value* shader::get_kcache_value(unsigned bank, unsigned index, unsigned chan) {
|
||||
return get_ro_value(kcache_values, VLK_KCACHE,
|
||||
sel_chan((bank << 12) | index, chan));
|
||||
}
|
||||
|
||||
void shader::add_input(unsigned gpr, bool preloaded, unsigned comp_mask) {
|
||||
if (inputs.size() <= gpr)
|
||||
inputs.resize(gpr+1);
|
||||
|
||||
shader_input &i = inputs[gpr];
|
||||
i.preloaded = preloaded;
|
||||
i.comp_mask = comp_mask;
|
||||
|
||||
if (preloaded) {
|
||||
add_gpr_values(root->dst, gpr, comp_mask, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void shader::init() {
|
||||
assert(!root);
|
||||
root = create_container();
|
||||
}
|
||||
|
||||
void shader::init_call_fs(cf_node* cf) {
|
||||
unsigned gpr = 0;
|
||||
|
||||
assert(target == TARGET_VS);
|
||||
|
||||
for(inputs_vec::const_iterator I = inputs.begin(),
|
||||
E = inputs.end(); I != E; ++I, ++gpr) {
|
||||
if (!I->preloaded)
|
||||
add_gpr_values(cf->dst, gpr, I->comp_mask, false);
|
||||
else
|
||||
add_gpr_values(cf->src, gpr, I->comp_mask, true);
|
||||
}
|
||||
}
|
||||
|
||||
void shader::set_undef(val_set& s) {
|
||||
value *undefined = get_undef_value();
|
||||
if (!undefined->gvn_source)
|
||||
vt.add_value(undefined);
|
||||
|
||||
val_set &vs = s;
|
||||
|
||||
for (val_set::iterator I = vs.begin(*this), E = vs.end(*this); I != E; ++I) {
|
||||
value *v = *I;
|
||||
|
||||
assert(!v->is_readonly() && !v->is_rel());
|
||||
|
||||
v->gvn_source = undefined->gvn_source;
|
||||
}
|
||||
}
|
||||
/*
|
||||
void shader::transfer_pins(vvec& vv, vvec &sv) {
|
||||
vvec::iterator SI = sv.begin();
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I, ++SI) {
|
||||
value *v = *I;
|
||||
value *sv = *SI;
|
||||
|
||||
v->pin_gpr = sv->pin_gpr;
|
||||
|
||||
if (sv->is_chan_pinned()) {
|
||||
v->flags |= VLF_PIN_CHAN;
|
||||
sv->flags &= ~VLF_PIN_CHAN;
|
||||
}
|
||||
if (sv->is_reg_pinned()) {
|
||||
v->flags |= VLF_PIN_REG;
|
||||
sv->flags &= ~VLF_PIN_REG;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
value* shader::create_value(value_kind k, sel_chan regid, unsigned ver) {
|
||||
value *v = val_pool.create(k, regid, ver);
|
||||
return v;
|
||||
}
|
||||
|
||||
value* shader::get_undef_value() {
|
||||
if (!undef)
|
||||
undef = create_value(VLK_UNDEF, 0, 0);
|
||||
return undef;
|
||||
}
|
||||
|
||||
node* shader::create_node(node_type nt, node_subtype nst, node_flags flags) {
|
||||
node *n = new (pool.allocate(sizeof(node))) node(nt, nst, flags);
|
||||
all_nodes.push_back(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
alu_node* shader::create_alu() {
|
||||
alu_node* n = new (pool.allocate(sizeof(alu_node))) alu_node();
|
||||
all_nodes.push_back(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
alu_group_node* shader::create_alu_group() {
|
||||
alu_group_node* n =
|
||||
new (pool.allocate(sizeof(alu_group_node))) alu_group_node();
|
||||
all_nodes.push_back(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
alu_packed_node* shader::create_alu_packed() {
|
||||
alu_packed_node* n =
|
||||
new (pool.allocate(sizeof(alu_packed_node))) alu_packed_node();
|
||||
all_nodes.push_back(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
cf_node* shader::create_cf() {
|
||||
cf_node* n = new (pool.allocate(sizeof(cf_node))) cf_node();
|
||||
n->bc.barrier = 1;
|
||||
all_nodes.push_back(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
fetch_node* shader::create_fetch() {
|
||||
fetch_node* n = new (pool.allocate(sizeof(fetch_node))) fetch_node();
|
||||
all_nodes.push_back(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
region_node* shader::create_region() {
|
||||
region_node *n = new (pool.allocate(sizeof(region_node)))
|
||||
region_node(regions.size());
|
||||
regions.push_back(n);
|
||||
all_nodes.push_back(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
depart_node* shader::create_depart(region_node* target) {
|
||||
depart_node* n = new (pool.allocate(sizeof(depart_node)))
|
||||
depart_node(target, target->departs.size());
|
||||
target->departs.push_back(n);
|
||||
all_nodes.push_back(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
repeat_node* shader::create_repeat(region_node* target) {
|
||||
repeat_node* n = new (pool.allocate(sizeof(repeat_node)))
|
||||
repeat_node(target, target->repeats.size() + 1);
|
||||
target->repeats.push_back(n);
|
||||
all_nodes.push_back(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
container_node* shader::create_container(node_type nt, node_subtype nst,
|
||||
node_flags flags) {
|
||||
container_node *n = new (pool.allocate(sizeof(container_node)))
|
||||
container_node(nt, nst, flags);
|
||||
all_nodes.push_back(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
if_node* shader::create_if() {
|
||||
if_node* n = new (pool.allocate(sizeof(if_node))) if_node();
|
||||
all_nodes.push_back(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
bb_node* shader::create_bb(unsigned id, unsigned loop_level) {
|
||||
bb_node* n = new (pool.allocate(sizeof(bb_node))) bb_node(id, loop_level);
|
||||
all_nodes.push_back(n);
|
||||
return n;
|
||||
}
|
||||
/*
|
||||
void shader::prepare_regs(unsigned cnt) {
|
||||
assert(!prep_regs_count);
|
||||
|
||||
for (unsigned i = 0; i < cnt*4; ++i) {
|
||||
value *v = create_value(VLK_REG, i + 1, 0);
|
||||
assert (v->uid == i + 1);
|
||||
}
|
||||
|
||||
prep_regs_count = cnt;
|
||||
}
|
||||
*/
|
||||
value* shader::get_special_ro_value(unsigned sel) {
|
||||
return get_ro_value(special_ro_values, VLK_PARAM, sel);
|
||||
}
|
||||
|
||||
value* shader::get_const_value(const literal &v) {
|
||||
value *val = get_ro_value(const_values, VLK_CONST, v);
|
||||
val->literal_value = v;
|
||||
return val;
|
||||
}
|
||||
|
||||
shader::~shader() {
|
||||
for (node_vec::iterator I = all_nodes.begin(), E = all_nodes.end();
|
||||
I != E; ++I)
|
||||
(*I)->~node();
|
||||
}
|
||||
|
||||
void shader::dump_ir() {
|
||||
if (ctx.dump_pass)
|
||||
dump(*this).run();
|
||||
}
|
||||
|
||||
value* shader::get_value_version(value* v, unsigned ver) {
|
||||
assert(!v->is_readonly() && !v->is_rel());
|
||||
value *vv = get_value(v->kind, v->select, ver);
|
||||
assert(vv);
|
||||
|
||||
if (v->array) {
|
||||
vv->array = v->array;
|
||||
}
|
||||
|
||||
return vv;
|
||||
}
|
||||
|
||||
gpr_array* shader::get_gpr_array(unsigned reg, unsigned chan) {
|
||||
|
||||
for (regarray_vec::iterator I = gpr_arrays.begin(),
|
||||
E = gpr_arrays.end(); I != E; ++I) {
|
||||
gpr_array* a = *I;
|
||||
unsigned achan = a->base_gpr.chan();
|
||||
unsigned areg = a->base_gpr.sel();
|
||||
if (achan == chan && (reg >= areg && reg < areg+a->array_size))
|
||||
return a;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void shader::add_gpr_array(unsigned gpr_start, unsigned gpr_count,
|
||||
unsigned comp_mask) {
|
||||
unsigned chan = 0;
|
||||
while (comp_mask) {
|
||||
if (comp_mask & 1) {
|
||||
gpr_array *a = new gpr_array(
|
||||
sel_chan(gpr_start, chan), gpr_count);
|
||||
|
||||
SB_DUMP_PASS( cerr << "add_gpr_array: @" << a->base_gpr
|
||||
<< " [" << a->array_size << "]\n";
|
||||
);
|
||||
|
||||
gpr_arrays.push_back(a);
|
||||
}
|
||||
comp_mask >>= 1;
|
||||
++chan;
|
||||
}
|
||||
}
|
||||
|
||||
value* shader::get_pred_sel(int sel) {
|
||||
assert(sel == 0 || sel == 1);
|
||||
if (!pred_sels[sel])
|
||||
pred_sels[sel] = get_const_value(sel);
|
||||
|
||||
return pred_sels[sel];
|
||||
}
|
||||
|
||||
cf_node* shader::create_cf(unsigned op) {
|
||||
cf_node *c = create_cf();
|
||||
c->bc.set_op(op);
|
||||
c->bc.barrier = 1;
|
||||
return c;
|
||||
}
|
||||
|
||||
std::string shader::get_full_target_name() {
|
||||
std::string s = get_shader_target_name();
|
||||
s += "/";
|
||||
s += get_hw_chip_name();
|
||||
s += "/";
|
||||
s += get_hw_class_name();
|
||||
return s;
|
||||
}
|
||||
|
||||
const char* shader::get_hw_class_name() {
|
||||
switch (ctx.hw_class) {
|
||||
#define TRANSLATE_HW_CLASS(c) case HW_CLASS_##c: return #c
|
||||
TRANSLATE_HW_CLASS(R600);
|
||||
TRANSLATE_HW_CLASS(R700);
|
||||
TRANSLATE_HW_CLASS(EVERGREEN);
|
||||
TRANSLATE_HW_CLASS(CAYMAN);
|
||||
#undef TRANSLATE_HW_CLASS
|
||||
default:
|
||||
return "INVALID_CHIP_CLASS";
|
||||
}
|
||||
}
|
||||
|
||||
const char* shader::get_hw_chip_name() {
|
||||
switch (ctx.hw_chip) {
|
||||
#define TRANSLATE_CHIP(c) case HW_CHIP_##c: return #c
|
||||
TRANSLATE_CHIP(R600);
|
||||
TRANSLATE_CHIP(RV610);
|
||||
TRANSLATE_CHIP(RV630);
|
||||
TRANSLATE_CHIP(RV670);
|
||||
TRANSLATE_CHIP(RV620);
|
||||
TRANSLATE_CHIP(RV635);
|
||||
TRANSLATE_CHIP(RS780);
|
||||
TRANSLATE_CHIP(RS880);
|
||||
TRANSLATE_CHIP(RV770);
|
||||
TRANSLATE_CHIP(RV730);
|
||||
TRANSLATE_CHIP(RV710);
|
||||
TRANSLATE_CHIP(RV740);
|
||||
TRANSLATE_CHIP(CEDAR);
|
||||
TRANSLATE_CHIP(REDWOOD);
|
||||
TRANSLATE_CHIP(JUNIPER);
|
||||
TRANSLATE_CHIP(CYPRESS);
|
||||
TRANSLATE_CHIP(HEMLOCK);
|
||||
TRANSLATE_CHIP(PALM);
|
||||
TRANSLATE_CHIP(SUMO);
|
||||
TRANSLATE_CHIP(SUMO2);
|
||||
TRANSLATE_CHIP(BARTS);
|
||||
TRANSLATE_CHIP(TURKS);
|
||||
TRANSLATE_CHIP(CAICOS);
|
||||
TRANSLATE_CHIP(CAYMAN);
|
||||
#undef TRANSLATE_CHIP
|
||||
|
||||
default:
|
||||
assert(!"unknown chip");
|
||||
return "INVALID_CHIP";
|
||||
}
|
||||
}
|
||||
|
||||
const char* shader::get_shader_target_name() {
|
||||
switch (target) {
|
||||
case TARGET_VS: return "VS";
|
||||
case TARGET_PS: return "PS";
|
||||
case TARGET_GS: return "GS";
|
||||
case TARGET_COMPUTE: return "COMPUTE";
|
||||
case TARGET_FETCH: return "FETCH";
|
||||
default:
|
||||
return "INVALID_TARGET";
|
||||
}
|
||||
}
|
||||
|
||||
void shader::simplify_dep_rep(node* dr) {
|
||||
container_node *p = dr->parent;
|
||||
if (p->is_repeat()) {
|
||||
repeat_node *r = static_cast<repeat_node*>(p);
|
||||
r->target->expand_repeat(r);
|
||||
} else if (p->is_depart()) {
|
||||
depart_node *d = static_cast<depart_node*>(p);
|
||||
d->target->expand_depart(d);
|
||||
}
|
||||
if (dr->next)
|
||||
dr->parent->cut(dr->next, NULL);
|
||||
}
|
||||
|
||||
|
||||
// FIXME this is used in some places as the max non-temp gpr,
|
||||
// (MAX_GPR - 2 * ctx.alu_temp_gprs) should be used for that instead.
|
||||
unsigned shader::first_temp_gpr() {
|
||||
return MAX_GPR - ctx.alu_temp_gprs;
|
||||
}
|
||||
|
||||
unsigned shader::num_nontemp_gpr() {
|
||||
return MAX_GPR - 2 * ctx.alu_temp_gprs;
|
||||
}
|
||||
|
||||
void shader::set_uses_kill() {
|
||||
if (root->src.empty())
|
||||
root->src.resize(1);
|
||||
|
||||
if (!root->src[0])
|
||||
root->src[0] = get_special_value(SV_VALID_MASK);
|
||||
}
|
||||
|
||||
alu_node* shader::clone(alu_node* n) {
|
||||
alu_node *c = create_alu();
|
||||
|
||||
// FIXME: this may be wrong with indirect operands
|
||||
c->src = n->src;
|
||||
c->dst = n->dst;
|
||||
|
||||
c->bc = n->bc;
|
||||
c->pred = n->pred;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
value* shader::get_ro_value(value_map& vm, value_kind vk, unsigned key) {
|
||||
value_map::iterator I = vm.find(key);
|
||||
if (I != vm.end())
|
||||
return I->second;
|
||||
value *v = create_value(vk, key, 0);
|
||||
v->flags = VLF_READONLY;
|
||||
vm.insert(std::make_pair(key, v));
|
||||
return v;
|
||||
}
|
||||
|
||||
void shader::create_bbs(container_node* n, bbs_vec &bbs, int loop_level) {
|
||||
|
||||
bool inside_bb = false;
|
||||
bool last_inside_bb = true;
|
||||
node_iterator bb_start(n->begin()), I(bb_start), E(n->end());
|
||||
|
||||
for (; I != E; ++I) {
|
||||
node *k = *I;
|
||||
inside_bb = k->type == NT_OP;
|
||||
|
||||
if (inside_bb && !last_inside_bb)
|
||||
bb_start = I;
|
||||
else if (!inside_bb) {
|
||||
if (last_inside_bb
|
||||
&& I->type != NT_REPEAT
|
||||
&& I->type != NT_DEPART
|
||||
&& I->type != NT_IF) {
|
||||
bb_node *bb = create_bb(bbs.size(), loop_level);
|
||||
bbs.push_back(bb);
|
||||
n->insert_node_before(*bb_start, bb);
|
||||
if (bb_start != I)
|
||||
bb->move(bb_start, I);
|
||||
}
|
||||
|
||||
if (k->is_container()) {
|
||||
|
||||
bool loop = false;
|
||||
if (k->type == NT_REGION) {
|
||||
loop = static_cast<region_node*>(k)->is_loop();
|
||||
}
|
||||
|
||||
create_bbs(static_cast<container_node*>(k), bbs,
|
||||
loop_level + loop);
|
||||
}
|
||||
}
|
||||
|
||||
if (k->type == NT_DEPART)
|
||||
return;
|
||||
|
||||
last_inside_bb = inside_bb;
|
||||
}
|
||||
|
||||
if (last_inside_bb) {
|
||||
bb_node *bb = create_bb(bbs.size(), loop_level);
|
||||
bbs.push_back(bb);
|
||||
if (n->empty())
|
||||
n->push_back(bb);
|
||||
else {
|
||||
n->insert_node_before(*bb_start, bb);
|
||||
if (bb_start != n->end())
|
||||
bb->move(bb_start, n->end());
|
||||
}
|
||||
} else {
|
||||
if (n->last && n->last->type == NT_IF) {
|
||||
bb_node *bb = create_bb(bbs.size(), loop_level);
|
||||
bbs.push_back(bb);
|
||||
n->push_back(bb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void shader::expand_bbs(bbs_vec &bbs) {
|
||||
|
||||
for (bbs_vec::iterator I = bbs.begin(), E = bbs.end(); I != E; ++I) {
|
||||
bb_node *b = *I;
|
||||
b->expand();
|
||||
}
|
||||
}
|
||||
|
||||
sched_queue_id shader::get_queue_id(node* n) {
|
||||
switch (n->subtype) {
|
||||
case NST_ALU_INST:
|
||||
case NST_ALU_PACKED_INST:
|
||||
case NST_COPY:
|
||||
case NST_PSI:
|
||||
return SQ_ALU;
|
||||
case NST_FETCH_INST: {
|
||||
fetch_node *f = static_cast<fetch_node*>(n);
|
||||
if (ctx.is_r600() && (f->bc.op_ptr->flags & FF_VTX))
|
||||
return SQ_VTX;
|
||||
return SQ_TEX;
|
||||
}
|
||||
case NST_CF_INST:
|
||||
return SQ_CF;
|
||||
default:
|
||||
assert(0);
|
||||
return SQ_NUM;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#ifndef SB_SHADER_H_
|
||||
#define SB_SHADER_H_
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "sb_ir.h"
|
||||
#include "sb_expr.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
struct shader_input {
|
||||
unsigned comp_mask;
|
||||
unsigned preloaded;
|
||||
};
|
||||
|
||||
struct error_info {
|
||||
node *n;
|
||||
unsigned arg_index;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
typedef std::multimap<node*, error_info> error_map;
|
||||
|
||||
class sb_context;
|
||||
|
||||
typedef std::vector<shader_input> inputs_vec;
|
||||
typedef std::vector<gpr_array*> gpr_array_vec;
|
||||
|
||||
struct ra_edge {
|
||||
value *a, *b;
|
||||
unsigned cost;
|
||||
|
||||
ra_edge(value *a, value *b, unsigned cost) : a(a), b(b), cost(cost) {}
|
||||
};
|
||||
|
||||
enum chunk_flags {
|
||||
RCF_GLOBAL = (1 << 0),
|
||||
RCF_PIN_CHAN = (1 << 1),
|
||||
RCF_PIN_REG = (1 << 2),
|
||||
|
||||
RCF_FIXED = (1 << 3),
|
||||
|
||||
RCF_PREALLOC = (1 << 4)
|
||||
};
|
||||
|
||||
inline chunk_flags operator |(chunk_flags l, chunk_flags r) {
|
||||
return (chunk_flags)((unsigned)l|(unsigned)r);
|
||||
}
|
||||
inline chunk_flags& operator |=(chunk_flags &l, chunk_flags r) {
|
||||
l = l | r;
|
||||
return l;
|
||||
}
|
||||
|
||||
inline chunk_flags& operator &=(chunk_flags &l, chunk_flags r) {
|
||||
l = (chunk_flags)((unsigned)l & (unsigned)r);
|
||||
return l;
|
||||
}
|
||||
|
||||
inline chunk_flags operator ~(chunk_flags r) {
|
||||
return (chunk_flags)~(unsigned)r;
|
||||
}
|
||||
|
||||
struct ra_chunk {
|
||||
vvec values;
|
||||
chunk_flags flags;
|
||||
unsigned cost;
|
||||
sel_chan pin;
|
||||
|
||||
ra_chunk() : values(), flags(), cost(), pin() {}
|
||||
|
||||
bool is_fixed() { return flags & RCF_FIXED; }
|
||||
void fix() { flags |= RCF_FIXED; }
|
||||
|
||||
bool is_global() { return flags & RCF_GLOBAL; }
|
||||
void set_global() { flags |= RCF_GLOBAL; }
|
||||
|
||||
bool is_reg_pinned() { return flags & RCF_PIN_REG; }
|
||||
bool is_chan_pinned() { return flags & RCF_PIN_CHAN; }
|
||||
|
||||
bool is_prealloc() { return flags & RCF_PREALLOC; }
|
||||
void set_prealloc() { flags |= RCF_PREALLOC; }
|
||||
};
|
||||
|
||||
typedef std::vector<ra_chunk*> chunk_vector;
|
||||
|
||||
class ra_constraint {
|
||||
public:
|
||||
ra_constraint(constraint_kind kind) : kind(kind) {}
|
||||
|
||||
constraint_kind kind;
|
||||
vvec values;
|
||||
unsigned cost;
|
||||
|
||||
void update_values();
|
||||
bool check();
|
||||
};
|
||||
|
||||
typedef std::vector<ra_constraint*> constraint_vec;
|
||||
typedef std::vector<ra_chunk*> chunk_vec;
|
||||
|
||||
// priority queue
|
||||
// FIXME use something more suitale or custom class ?
|
||||
|
||||
template <class T>
|
||||
struct cost_compare {
|
||||
bool operator ()(const T& t1, const T& t2) {
|
||||
return t1->cost > t2->cost;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class Comp>
|
||||
class queue {
|
||||
typedef std::vector<T> container;
|
||||
container cont;
|
||||
|
||||
public:
|
||||
queue() : cont() {}
|
||||
|
||||
typedef typename container::iterator iterator;
|
||||
|
||||
iterator begin() { return cont.begin(); }
|
||||
iterator end() { return cont.end(); }
|
||||
|
||||
iterator insert(const T& t) {
|
||||
iterator I = std::upper_bound(begin(), end(), t, Comp());
|
||||
if (I == end())
|
||||
cont.push_back(t);
|
||||
else
|
||||
cont.insert(I, t);
|
||||
|
||||
return I;
|
||||
}
|
||||
|
||||
void erase(const T& t) {
|
||||
std::pair<iterator, iterator> R =
|
||||
std::equal_range(begin(), end(), t, Comp());
|
||||
iterator F = std::find(R.first, R.second, t);
|
||||
if (F != R.second)
|
||||
cont.erase(F);
|
||||
}
|
||||
};
|
||||
|
||||
typedef queue<ra_chunk*, cost_compare<ra_chunk*> > chunk_queue;
|
||||
typedef queue<ra_edge*, cost_compare<ra_edge*> > edge_queue;
|
||||
typedef queue<ra_constraint*, cost_compare<ra_constraint*> > constraint_queue;
|
||||
|
||||
typedef std::set<ra_chunk*> chunk_set;
|
||||
|
||||
class shader;
|
||||
|
||||
class coalescer {
|
||||
|
||||
shader &sh;
|
||||
|
||||
edge_queue edges;
|
||||
chunk_queue chunks;
|
||||
constraint_queue constraints;
|
||||
|
||||
constraint_vec all_constraints;
|
||||
chunk_vec all_chunks;
|
||||
|
||||
public:
|
||||
|
||||
coalescer(shader &sh) : sh(sh), edges(), chunks(), constraints() {}
|
||||
~coalescer();
|
||||
|
||||
void run();
|
||||
|
||||
void add_edge(value *a, value *b, unsigned cost);
|
||||
void build_chunks();
|
||||
void build_constraint_queue();
|
||||
void build_chunk_queue();
|
||||
void color_constraints();
|
||||
void color_chunks();
|
||||
|
||||
ra_constraint* create_constraint(constraint_kind kind);
|
||||
|
||||
enum ac_cost {
|
||||
phi_cost = 10000,
|
||||
copy_cost = 1,
|
||||
};
|
||||
|
||||
void dump_edges();
|
||||
void dump_chunks();
|
||||
void dump_constraint_queue();
|
||||
|
||||
static void dump_chunk(ra_chunk *c);
|
||||
static void dump_constraint(ra_constraint* c);
|
||||
|
||||
void get_chunk_interferences(ra_chunk *c, val_set &s);
|
||||
|
||||
private:
|
||||
|
||||
void create_chunk(value *v);
|
||||
void unify_chunks(ra_edge *e);
|
||||
bool chunks_interference(ra_chunk *c1, ra_chunk *c2);
|
||||
|
||||
void color_reg_constraint(ra_constraint *c);
|
||||
void color_phi_constraint(ra_constraint *c);
|
||||
|
||||
|
||||
void init_reg_bitset(sb_bitset &bs, val_set &vs);
|
||||
|
||||
void color_chunk(ra_chunk *c, sel_chan color);
|
||||
|
||||
ra_chunk* detach_value(value *v);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class shader {
|
||||
|
||||
sb_context &ctx;
|
||||
|
||||
typedef sb_map<uint32_t, value*> value_map;
|
||||
value_map reg_values;
|
||||
|
||||
// read-only values
|
||||
value_map const_values; // immediate constants key -const value (uint32_t)
|
||||
value_map special_ro_values; // key - hw alu_sel & chan
|
||||
value_map kcache_values;
|
||||
|
||||
gpr_array_vec gpr_arrays;
|
||||
|
||||
unsigned next_temp_value_index;
|
||||
|
||||
unsigned prep_regs_count;
|
||||
|
||||
value* pred_sels[2];
|
||||
|
||||
regions_vec regions;
|
||||
inputs_vec inputs;
|
||||
|
||||
value *undef;
|
||||
|
||||
sb_value_pool val_pool;
|
||||
sb_pool pool;
|
||||
|
||||
std::vector<node*> all_nodes;
|
||||
|
||||
public:
|
||||
error_map errors;
|
||||
|
||||
bool enable_dump;
|
||||
bool optimized;
|
||||
|
||||
unsigned id;
|
||||
|
||||
coalescer coal;
|
||||
|
||||
static const unsigned temp_regid_offset = 512;
|
||||
|
||||
bbs_vec bbs;
|
||||
|
||||
const shader_target target;
|
||||
|
||||
value_table vt;
|
||||
expr_handler ex;
|
||||
|
||||
container_node *root;
|
||||
|
||||
bool compute_interferences;
|
||||
|
||||
bool has_alu_predication;
|
||||
bool uses_gradients;
|
||||
|
||||
unsigned ngpr, nstack;
|
||||
|
||||
shader(sb_context &sctx, shader_target t, unsigned id, bool dump);
|
||||
|
||||
~shader();
|
||||
|
||||
sb_context &get_ctx() const { return ctx; }
|
||||
|
||||
value* get_const_value(const literal & v);
|
||||
value* get_special_value(unsigned sv_id, unsigned version = 0);
|
||||
value* create_temp_value();
|
||||
value* get_gpr_value(bool src, unsigned reg, unsigned chan, bool rel,
|
||||
unsigned version = 0);
|
||||
|
||||
|
||||
value* get_special_ro_value(unsigned sel);
|
||||
value* get_kcache_value(unsigned bank, unsigned index, unsigned chan);
|
||||
|
||||
value* get_value_version(value* v, unsigned ver);
|
||||
|
||||
void init();
|
||||
void add_gpr_values(vvec& vec, unsigned gpr, unsigned comp_mask, bool src);
|
||||
|
||||
void dump_ir();
|
||||
|
||||
void add_gpr_array(unsigned gpr_start, unsigned gpr_count,
|
||||
unsigned comp_mask);
|
||||
|
||||
value* get_pred_sel(int sel);
|
||||
bool assign_slot(alu_node *n, alu_node *slots[5]);
|
||||
|
||||
gpr_array* get_gpr_array(unsigned reg, unsigned chan);
|
||||
|
||||
void add_input(unsigned gpr, bool preloaded = false,
|
||||
unsigned comp_mask = 0xF);
|
||||
|
||||
const inputs_vec & get_inputs() {return inputs; }
|
||||
|
||||
regions_vec & get_regions() { return regions; }
|
||||
|
||||
void init_call_fs(cf_node *cf);
|
||||
|
||||
value *get_undef_value();
|
||||
void set_undef(val_set &s);
|
||||
|
||||
void transfer_pins(vvec& vv, vvec &sv);
|
||||
|
||||
node* create_node(node_type nt, node_subtype nst,
|
||||
node_flags flags = NF_EMPTY);
|
||||
alu_node* create_alu();
|
||||
alu_group_node* create_alu_group();
|
||||
alu_packed_node* create_alu_packed();
|
||||
cf_node* create_cf();
|
||||
cf_node* create_cf(unsigned op);
|
||||
fetch_node* create_fetch();
|
||||
region_node* create_region();
|
||||
depart_node* create_depart(region_node *target);
|
||||
repeat_node* create_repeat(region_node *target);
|
||||
container_node* create_container(node_type nt = NT_LIST,
|
||||
node_subtype nst = NST_LIST,
|
||||
node_flags flags = NF_EMPTY);
|
||||
if_node* create_if();
|
||||
bb_node* create_bb(unsigned id, unsigned loop_level);
|
||||
|
||||
void prepare_regs(unsigned cnt);
|
||||
|
||||
value* get_value_by_uid(unsigned id) { return val_pool[id - 1]; }
|
||||
|
||||
cf_node* create_clause(node_subtype nst);
|
||||
|
||||
void create_bbs();
|
||||
void expand_bbs();
|
||||
|
||||
alu_node* create_mov(value* dst, value* src);
|
||||
alu_node* create_copy_mov(value *dst, value *src, unsigned affcost = 1);
|
||||
|
||||
const char * get_hw_class_name();
|
||||
const char * get_hw_chip_name();
|
||||
const char * get_shader_target_name();
|
||||
|
||||
std::string get_full_target_name();
|
||||
|
||||
void create_bbs(container_node* n, bbs_vec &bbs, int loop_level = 0);
|
||||
void expand_bbs(bbs_vec &bbs);
|
||||
|
||||
sched_queue_id get_queue_id(node* n);
|
||||
|
||||
void simplify_dep_rep(node *dr);
|
||||
|
||||
unsigned first_temp_gpr();
|
||||
unsigned num_nontemp_gpr();
|
||||
|
||||
gpr_array_vec& arrays() { return gpr_arrays; }
|
||||
|
||||
void set_uses_kill();
|
||||
|
||||
void fill_array_values(gpr_array *a, vvec &vv);
|
||||
|
||||
alu_node* clone(alu_node *n);
|
||||
|
||||
sb_value_pool& get_value_pool() { return val_pool; }
|
||||
|
||||
private:
|
||||
value* create_value(value_kind k, sel_chan regid, unsigned ver);
|
||||
value* get_value(value_kind kind, sel_chan id,
|
||||
unsigned version = 0);
|
||||
value* get_ro_value(value_map &vm, value_kind vk, unsigned key);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SHADER_H_ */
|
||||
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#include <stack>
|
||||
#include <map>
|
||||
|
||||
#include "sb_shader.h"
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
container_node* ssa_prepare::create_phi_nodes(int count) {
|
||||
container_node *p = sh.create_container();
|
||||
val_set &vars = cur_set();
|
||||
node *nn;
|
||||
|
||||
for (val_set::iterator I = vars.begin(sh), E = vars.end(sh); I != E; ++I) {
|
||||
nn = sh.create_node(NT_OP, NST_PHI);
|
||||
nn->dst.assign(1, *I);
|
||||
nn->src.assign(count, *I);
|
||||
p->push_back(nn);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void ssa_prepare::add_defs(node &n) {
|
||||
val_set &s = cur_set();
|
||||
for (vvec::iterator I = n.dst.begin(), E = n.dst.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (!v)
|
||||
continue;
|
||||
|
||||
if (v->is_rel()) {
|
||||
s.add_vec(v->mdef);
|
||||
} else
|
||||
s.add_val(v);
|
||||
}
|
||||
}
|
||||
|
||||
bool ssa_prepare::visit(cf_node& n, bool enter) {
|
||||
if (enter) {
|
||||
push_stk();
|
||||
} else {
|
||||
add_defs(n);
|
||||
pop_stk();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ssa_prepare::visit(alu_node& n, bool enter) {
|
||||
if (enter) {
|
||||
} else {
|
||||
add_defs(n);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ssa_prepare::visit(fetch_node& n, bool enter) {
|
||||
if (enter) {
|
||||
} else {
|
||||
add_defs(n);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ssa_prepare::visit(region_node& n, bool enter) {
|
||||
if (enter) {
|
||||
|
||||
push_stk();
|
||||
} else {
|
||||
cur_set().add_set(n.vars_defined);
|
||||
if (n.dep_count() > 0)
|
||||
n.phi = create_phi_nodes(n.dep_count());
|
||||
if (n.rep_count() > 1) {
|
||||
n.loop_phi = create_phi_nodes(n.rep_count());
|
||||
n.loop_phi->subtype = NST_LOOP_PHI_CONTAINER;
|
||||
}
|
||||
n.vars_defined.clear();
|
||||
pop_stk();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ssa_prepare::visit(repeat_node& n, bool enter) {
|
||||
if (enter) {
|
||||
push_stk();
|
||||
} else {
|
||||
assert(n.target);
|
||||
n.target->vars_defined.add_set(cur_set());
|
||||
cur_set().clear();
|
||||
pop_stk();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ssa_prepare::visit(depart_node& n, bool enter) {
|
||||
if (enter) {
|
||||
push_stk();
|
||||
} else {
|
||||
assert(n.target);
|
||||
n.target->vars_defined.add_set(cur_set());
|
||||
cur_set().clear();
|
||||
pop_stk();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ===============================
|
||||
|
||||
int ssa_rename::init() {
|
||||
rename_stack.push(def_map());
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ssa_rename::visit(alu_group_node& n, bool enter) {
|
||||
// taking into account parallel execution of the alu group
|
||||
if (enter) {
|
||||
for (node_iterator I = n.begin(), E = n.end(); I != E; ++I) {
|
||||
I->accept(*this, true);
|
||||
}
|
||||
} else {
|
||||
for (node_iterator I = n.begin(), E = n.end(); I != E; ++I) {
|
||||
I->accept(*this, false);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ssa_rename::visit(cf_node& n, bool enter) {
|
||||
if (enter) {
|
||||
rename_src(&n);
|
||||
} else {
|
||||
rename_dst(&n);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ssa_rename::visit(alu_node& n, bool enter) {
|
||||
if (enter) {
|
||||
rename_src(&n);
|
||||
} else {
|
||||
|
||||
node *psi = NULL;
|
||||
|
||||
if (n.pred && n.dst[0]) {
|
||||
|
||||
value *d = n.dst[0];
|
||||
unsigned index = get_index(rename_stack.top(), d);
|
||||
value *p = sh.get_value_version(d, index);
|
||||
|
||||
psi = sh.create_node(NT_OP, NST_PSI);
|
||||
|
||||
container_node *parent;
|
||||
if (n.parent->subtype == NST_ALU_GROUP)
|
||||
parent = n.parent;
|
||||
else {
|
||||
assert (n.parent->parent->subtype == NST_ALU_GROUP);
|
||||
parent = n.parent->parent;
|
||||
}
|
||||
parent->insert_after(psi);
|
||||
|
||||
assert(n.bc.pred_sel);
|
||||
|
||||
psi->src.resize(6);
|
||||
psi->src[2] = p;
|
||||
psi->src[3] = n.pred;
|
||||
psi->src[4] = sh.get_pred_sel(n.bc.pred_sel - PRED_SEL_0);
|
||||
psi->src[5] = d;
|
||||
psi->dst.push_back(d);
|
||||
}
|
||||
|
||||
rename_dst(&n);
|
||||
|
||||
if (psi) {
|
||||
rename_src(psi);
|
||||
rename_dst(psi);
|
||||
}
|
||||
|
||||
if (!n.dst.empty() && n.dst[0]) {
|
||||
// FIXME probably use separate pass for such things
|
||||
if ((n.bc.op_ptr->flags & AF_INTERP) || n.bc.op == ALU_OP2_CUBE)
|
||||
n.dst[0]->flags |= VLF_PIN_CHAN;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ssa_rename::visit(alu_packed_node& n, bool enter) {
|
||||
if (enter) {
|
||||
for (node_iterator I = n.begin(), E = n.end(); I != E; ++I) {
|
||||
I->accept(*this, true);
|
||||
}
|
||||
} else {
|
||||
for (node_iterator I = n.begin(), E = n.end(); I != E; ++I) {
|
||||
I->accept(*this, false);
|
||||
}
|
||||
|
||||
n.init_args();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ssa_rename::visit(fetch_node& n, bool enter) {
|
||||
if (enter) {
|
||||
rename_src(&n);
|
||||
rename_dst(&n);
|
||||
} else {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ssa_rename::visit(region_node& n, bool enter) {
|
||||
if (enter) {
|
||||
if (n.loop_phi)
|
||||
rename_phi_args(n.loop_phi, 0, true);
|
||||
} else {
|
||||
if (n.phi)
|
||||
rename_phi_args(n.phi, ~0u, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ssa_rename::visit(repeat_node& n, bool enter) {
|
||||
if (enter) {
|
||||
push(n.target->loop_phi);
|
||||
} else {
|
||||
if (n.target->loop_phi)
|
||||
rename_phi_args(n.target->loop_phi, n.rep_id, false);
|
||||
pop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ssa_rename::visit(depart_node& n, bool enter) {
|
||||
if (enter) {
|
||||
push(n.target->phi);
|
||||
} else {
|
||||
if (n.target->phi)
|
||||
rename_phi_args(n.target->phi, n.dep_id, false);
|
||||
pop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ssa_rename::visit(if_node& n, bool enter) {
|
||||
if (enter) {
|
||||
} else {
|
||||
n.cond = rename_use(&n, n.cond);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ssa_rename::push(node* phi) {
|
||||
rename_stack.push(rename_stack.top());
|
||||
}
|
||||
|
||||
void ssa_rename::pop() {
|
||||
rename_stack.pop();
|
||||
}
|
||||
|
||||
value* ssa_rename::rename_use(node *n, value* v) {
|
||||
if (v->version)
|
||||
return v;
|
||||
|
||||
unsigned index = get_index(rename_stack.top(), v);
|
||||
v = sh.get_value_version(v, index);
|
||||
|
||||
// if (alu) instruction is predicated and source arg comes from psi node
|
||||
// (that is, from another predicated instruction through its psi node),
|
||||
// we can try to select the corresponding source value directly
|
||||
if (n->pred && v->def && v->def->subtype == NST_PSI) {
|
||||
assert(n->subtype == NST_ALU_INST);
|
||||
alu_node *an = static_cast<alu_node*>(n);
|
||||
node *pn = v->def;
|
||||
// FIXME make it more generic ???
|
||||
if (pn->src.size() == 6) {
|
||||
if (pn->src[3] == n->pred) {
|
||||
value* ps = sh.get_pred_sel(an->bc.pred_sel - PRED_SEL_0);
|
||||
if (pn->src[4] == ps)
|
||||
return pn->src[5];
|
||||
else
|
||||
return pn->src[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
value* ssa_rename::rename_def(node *n, value* v) {
|
||||
unsigned index = new_index(def_count, v);
|
||||
set_index(rename_stack.top(), v, index);
|
||||
value *r = sh.get_value_version(v, index);
|
||||
return r;
|
||||
}
|
||||
|
||||
void ssa_rename::rename_src_vec(node *n, vvec &vv, bool src) {
|
||||
for(vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
value* &v = *I;
|
||||
if (!v || v->is_readonly())
|
||||
continue;
|
||||
|
||||
if (v->is_rel()) {
|
||||
if (!v->rel->is_readonly())
|
||||
v->rel = rename_use(n, v->rel);
|
||||
rename_src_vec(n, v->muse, true);
|
||||
} else if (src)
|
||||
v = rename_use(n, v);
|
||||
}
|
||||
}
|
||||
|
||||
void ssa_rename::rename_src(node* n) {
|
||||
if (n->pred)
|
||||
n->pred = rename_use(n, n->pred);
|
||||
|
||||
rename_src_vec(n, n->src, true);
|
||||
rename_src_vec(n, n->dst, false);
|
||||
|
||||
}
|
||||
|
||||
void ssa_rename::rename_dst_vec(node *n, vvec &vv, bool set_def) {
|
||||
|
||||
for(vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
value* &v = *I;
|
||||
if (!v)
|
||||
continue;
|
||||
|
||||
if (v->is_rel()) {
|
||||
rename_dst_vec(n, v->mdef, false);
|
||||
} else {
|
||||
v = rename_def(n, v);
|
||||
if (set_def)
|
||||
v->def = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ssa_rename::rename_dst(node* n) {
|
||||
rename_dst_vec(n, n->dst, true);
|
||||
}
|
||||
|
||||
unsigned ssa_rename::get_index(def_map& m, value* v) {
|
||||
def_map::iterator I = m.find(v);
|
||||
if (I != m.end())
|
||||
return I->second;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ssa_rename::set_index(def_map& m, value* v, unsigned index) {
|
||||
std::pair<def_map::iterator,bool> r = m.insert(std::make_pair(v, index));
|
||||
if (!r.second)
|
||||
r.first->second = index;
|
||||
}
|
||||
|
||||
unsigned ssa_rename::new_index(def_map& m, value* v) {
|
||||
unsigned index = 1;
|
||||
def_map::iterator I = m.find(v);
|
||||
if (I != m.end())
|
||||
index = ++I->second;
|
||||
else
|
||||
m.insert(std::make_pair(v, index));
|
||||
return index;
|
||||
}
|
||||
|
||||
bool ssa_rename::visit(node& n, bool enter) {
|
||||
if (enter) {
|
||||
assert(n.subtype == NST_PSI);
|
||||
rename_src(&n);
|
||||
rename_dst(&n);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ssa_rename::visit(container_node& n, bool enter) {
|
||||
if (enter) {
|
||||
} else {
|
||||
// should be root container node
|
||||
assert(n.parent == NULL);
|
||||
rename_src_vec(&n, n.src, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ssa_rename::rename_phi_args(container_node* phi, unsigned op, bool def) {
|
||||
for (node_iterator I = phi->begin(), E = phi->end(); I != E; ++I) {
|
||||
node *o = *I;
|
||||
if (op != ~0u)
|
||||
o->src[op] = rename_use(o, o->src[op]);
|
||||
if (def) {
|
||||
o->dst[0] = rename_def(o, o->dst[0]);
|
||||
o->dst[0]->def = o;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
||||
* license, and/or sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Vadim Girlin
|
||||
*/
|
||||
|
||||
#define VT_DEBUG 0
|
||||
|
||||
#if VT_DEBUG
|
||||
#define VT_DUMP(q) do { q } while (0)
|
||||
#else
|
||||
#define VT_DUMP(q)
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#include "sb_shader.h"
|
||||
#include "sb_pass.h"
|
||||
|
||||
namespace r600_sb {
|
||||
|
||||
using std::cerr;
|
||||
|
||||
static const char * chans = "xyzw01?_";
|
||||
|
||||
std::ostream& operator << (std::ostream &o, value &v) {
|
||||
|
||||
bool dead = v.flags & VLF_DEAD;
|
||||
|
||||
if (dead)
|
||||
o << "{";
|
||||
|
||||
switch (v.kind) {
|
||||
case VLK_SPECIAL_REG: {
|
||||
switch (v.select.sel()) {
|
||||
case SV_AR_INDEX: o << "AR"; break;
|
||||
case SV_ALU_PRED: o << "PR"; break;
|
||||
case SV_EXEC_MASK: o << "EM"; break;
|
||||
case SV_VALID_MASK: o << "VM"; break;
|
||||
default: o << "???specialreg"; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case VLK_REG:
|
||||
o << "R" << v.select.sel() << "."
|
||||
<< chans[v.select.chan()];
|
||||
|
||||
break;
|
||||
case VLK_KCACHE: {
|
||||
o << "C" << v.select.sel() << "." << chans[v.select.chan()];
|
||||
}
|
||||
break;
|
||||
case VLK_CONST:
|
||||
o << v.literal_value.f << "|" << std::hex
|
||||
<< std::showbase << v.literal_value.u
|
||||
<< std::dec << std::noshowbase;
|
||||
break;
|
||||
case VLK_PARAM:
|
||||
o << "Param" << (v.select.sel() - ALU_SRC_PARAM_OFFSET)
|
||||
<< chans[v.select.chan()];
|
||||
break;
|
||||
case VLK_TEMP:
|
||||
o << "t" << v.select.sel() - shader::temp_regid_offset;
|
||||
break;
|
||||
case VLK_REL_REG:
|
||||
|
||||
o << "A" << v.select;
|
||||
o << "[";
|
||||
o << *v.rel;
|
||||
o << "]";
|
||||
|
||||
o << "_" << v.uid;
|
||||
|
||||
break;
|
||||
case VLK_UNDEF:
|
||||
o << "undef";
|
||||
break;
|
||||
default:
|
||||
o << v.kind << "?????";
|
||||
break;
|
||||
}
|
||||
|
||||
if (v.version)
|
||||
o << "." << v.version;
|
||||
|
||||
if (dead)
|
||||
o << "}";
|
||||
|
||||
if (v.is_global())
|
||||
o << "||";
|
||||
if (v.is_fixed())
|
||||
o << "F";
|
||||
if (v.is_prealloc())
|
||||
o << "P";
|
||||
|
||||
sel_chan g;
|
||||
|
||||
if (v.is_rel()) {
|
||||
g = v.array->gpr;
|
||||
} else {
|
||||
g = v.gpr;
|
||||
}
|
||||
|
||||
if (g) {
|
||||
o << "@R" << g.sel() << "." << chans[g.chan()];
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
void value_table::add_value(value* v) {
|
||||
|
||||
if (v->gvn_source) {
|
||||
return;
|
||||
}
|
||||
|
||||
VT_DUMP(
|
||||
cerr << "gvn add_value ";
|
||||
dump::dump_val(v);
|
||||
);
|
||||
|
||||
value_hash hash = v->hash();
|
||||
vt_item & vti = hashtable[hash & size_mask];
|
||||
vti.push_back(v);
|
||||
++cnt;
|
||||
|
||||
if (v->def && ex.try_fold(v)) {
|
||||
VT_DUMP(
|
||||
cerr << " folded: ";
|
||||
dump::dump_val(v->gvn_source);
|
||||
cerr << "\n";
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
for (vt_item::iterator I = vti.begin(), E = vti.end(); I != E; ++I, ++n) {
|
||||
value *c = *I;
|
||||
|
||||
if (c == v)
|
||||
break;
|
||||
|
||||
if (expr_equal(c, v)) {
|
||||
v->gvn_source = c->gvn_source;
|
||||
|
||||
VT_DUMP(
|
||||
cerr << " found : equal to ";
|
||||
dump::dump_val(v->gvn_source);
|
||||
cerr << "\n";
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
v->gvn_source = v;
|
||||
VT_DUMP(
|
||||
cerr << " added new\n";
|
||||
);
|
||||
}
|
||||
|
||||
value_hash value::hash() {
|
||||
if (ghash)
|
||||
return ghash;
|
||||
if (is_rel())
|
||||
ghash = rel_hash();
|
||||
else if (def)
|
||||
ghash = def->hash();
|
||||
else
|
||||
ghash = ((uintptr_t)this) | 1;
|
||||
|
||||
return ghash;
|
||||
}
|
||||
|
||||
value_hash value::rel_hash() {
|
||||
value_hash h = rel ? rel->hash() : 0;
|
||||
h |= select << 10;
|
||||
h |= array->hash();
|
||||
return h;
|
||||
}
|
||||
|
||||
bool value_table::expr_equal(value* l, value* r) {
|
||||
return ex.equal(l, r);
|
||||
}
|
||||
|
||||
void value_table::get_values(vvec& v) {
|
||||
v.resize(cnt);
|
||||
|
||||
vvec::iterator T = v.begin();
|
||||
|
||||
for(vt_table::iterator I = hashtable.begin(), E = hashtable.end();
|
||||
I != E; ++I) {
|
||||
T = copy(I->begin(), I->end(), T);
|
||||
}
|
||||
}
|
||||
|
||||
void value::add_use(node* n, use_kind kind, int arg) {
|
||||
if (0) {
|
||||
cerr << "add_use ";
|
||||
dump::dump_val(this);
|
||||
cerr << " => ";
|
||||
dump::dump_op(n);
|
||||
cerr << " kind " << kind << " arg " << arg << "\n";
|
||||
}
|
||||
uses = new use_info(n, kind, arg, uses);
|
||||
}
|
||||
|
||||
unsigned value::use_count() {
|
||||
use_info *u = uses;
|
||||
unsigned c = 0;
|
||||
while (u) {
|
||||
++c;
|
||||
u = u->next;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
bool value::is_global() {
|
||||
if (chunk)
|
||||
return chunk->is_global();
|
||||
return flags & VLF_GLOBAL;
|
||||
}
|
||||
|
||||
void value::set_global() {
|
||||
assert(is_sgpr());
|
||||
flags |= VLF_GLOBAL;
|
||||
if (chunk)
|
||||
chunk->set_global();
|
||||
}
|
||||
|
||||
void value::set_prealloc() {
|
||||
assert(is_sgpr());
|
||||
flags |= VLF_PREALLOC;
|
||||
if (chunk)
|
||||
chunk->set_prealloc();
|
||||
}
|
||||
|
||||
bool value::is_fixed() {
|
||||
if (array && array->gpr)
|
||||
return true;
|
||||
if (chunk)
|
||||
return chunk->is_fixed();
|
||||
return flags & VLF_FIXED;
|
||||
}
|
||||
|
||||
void value::fix() {
|
||||
if (chunk)
|
||||
chunk->fix();
|
||||
flags |= VLF_FIXED;
|
||||
}
|
||||
|
||||
bool value::is_prealloc() {
|
||||
if (chunk)
|
||||
return chunk->is_prealloc();
|
||||
return flags & VLF_PREALLOC;
|
||||
}
|
||||
|
||||
void value::delete_uses() {
|
||||
use_info *u, *c = uses;
|
||||
while (c) {
|
||||
u = c->next;
|
||||
delete c;
|
||||
c = u;
|
||||
}
|
||||
uses = NULL;
|
||||
}
|
||||
|
||||
void ra_constraint::update_values() {
|
||||
for (vvec::iterator I = values.begin(), E = values.end(); I != E; ++I) {
|
||||
assert(!(*I)->constraint);
|
||||
(*I)->constraint = this;
|
||||
}
|
||||
}
|
||||
|
||||
void* sb_pool::allocate(unsigned sz) {
|
||||
sz = (sz + SB_POOL_ALIGN - 1) & ~(SB_POOL_ALIGN - 1);
|
||||
assert (sz < (block_size >> 6) && "too big allocation size for sb_pool");
|
||||
|
||||
unsigned offset = total_size % block_size;
|
||||
unsigned capacity = block_size * blocks.size();
|
||||
|
||||
if (total_size + sz > capacity) {
|
||||
total_size = capacity;
|
||||
void * nb = malloc(block_size);
|
||||
blocks.push_back(nb);
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
total_size += sz;
|
||||
return ((char*)blocks.back() + offset);
|
||||
}
|
||||
|
||||
void sb_pool::free_all() {
|
||||
for (block_vector::iterator I = blocks.begin(), E = blocks.end(); I != E;
|
||||
++I) {
|
||||
free(*I);
|
||||
}
|
||||
}
|
||||
|
||||
value* sb_value_pool::create(value_kind k, sel_chan regid,
|
||||
unsigned ver) {
|
||||
void* np = allocate(aligned_elt_size);
|
||||
value *v = new (np) value(size(), k, regid, ver);
|
||||
return v;
|
||||
}
|
||||
|
||||
void sb_value_pool::delete_all() {
|
||||
unsigned bcnt = blocks.size();
|
||||
unsigned toffset = 0;
|
||||
for (unsigned b = 0; b < bcnt; ++b) {
|
||||
char *bstart = (char*)blocks[b];
|
||||
for (unsigned offset = 0; offset < block_size;
|
||||
offset += aligned_elt_size) {
|
||||
((value*)(bstart + offset))->~value();
|
||||
toffset += aligned_elt_size;
|
||||
if (toffset >= total_size)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool sb_bitset::get(unsigned id) {
|
||||
assert(id < bit_size);
|
||||
unsigned w = id / bt_bits;
|
||||
unsigned b = id % bt_bits;
|
||||
return (data[w] >> b) & 1;
|
||||
}
|
||||
|
||||
void sb_bitset::set(unsigned id, bool bit) {
|
||||
assert(id < bit_size);
|
||||
unsigned w = id / bt_bits;
|
||||
unsigned b = id % bt_bits;
|
||||
if (w >= data.size())
|
||||
data.resize(w + 1);
|
||||
|
||||
if (bit)
|
||||
data[w] |= (1 << b);
|
||||
else
|
||||
data[w] &= ~(1 << b);
|
||||
}
|
||||
|
||||
inline bool sb_bitset::set_chk(unsigned id, bool bit) {
|
||||
assert(id < bit_size);
|
||||
unsigned w = id / bt_bits;
|
||||
unsigned b = id % bt_bits;
|
||||
basetype d = data[w];
|
||||
basetype dn = (d & ~(1 << b)) | (bit << b);
|
||||
bool r = (d != dn);
|
||||
data[w] = r ? dn : data[w];
|
||||
return r;
|
||||
}
|
||||
|
||||
void sb_bitset::clear() {
|
||||
memset(data.data(), 0, sizeof(basetype) * data.size());
|
||||
}
|
||||
|
||||
void sb_bitset::resize(unsigned size) {
|
||||
unsigned cur_data_size = data.size();
|
||||
unsigned new_data_size = (size + bt_bits - 1) / bt_bits;
|
||||
|
||||
|
||||
if (new_data_size != cur_data_size)
|
||||
data.resize(new_data_size);
|
||||
|
||||
// make sure that new bits in the existing word are cleared
|
||||
if (cur_data_size && size > bit_size && bit_size % bt_bits) {
|
||||
basetype clear_mask = (~(basetype)0u) << (bit_size % bt_bits);
|
||||
data[cur_data_size - 1] &= ~clear_mask;
|
||||
}
|
||||
|
||||
bit_size = size;
|
||||
}
|
||||
|
||||
unsigned sb_bitset::find_bit(unsigned start) {
|
||||
assert(start < bit_size);
|
||||
unsigned w = start / bt_bits;
|
||||
unsigned b = start % bt_bits;
|
||||
unsigned sz = data.size();
|
||||
|
||||
while (w < sz) {
|
||||
basetype d = data[w] >> b;
|
||||
if (d != 0) {
|
||||
unsigned pos = __builtin_ctz(d) + b + w * bt_bits;
|
||||
return pos;
|
||||
}
|
||||
|
||||
b = 0;
|
||||
++w;
|
||||
}
|
||||
|
||||
return bit_size;
|
||||
}
|
||||
|
||||
sb_value_set::iterator::iterator(shader& sh, sb_value_set* s, unsigned nb)
|
||||
: vp(sh.get_value_pool()), s(s), nb(nb) {}
|
||||
|
||||
bool sb_value_set::add_set_checked(sb_value_set& s2) {
|
||||
if (bs.size() < s2.bs.size())
|
||||
bs.resize(s2.bs.size());
|
||||
sb_bitset nbs = bs | s2.bs;
|
||||
if (bs != nbs) {
|
||||
bs.swap(nbs);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void r600_sb::sb_value_set::remove_set(sb_value_set& s2) {
|
||||
bs.mask(s2.bs);
|
||||
}
|
||||
|
||||
bool sb_value_set::add_val(value* v) {
|
||||
assert(v);
|
||||
if (bs.size() < v->uid)
|
||||
bs.resize(v->uid + 32);
|
||||
|
||||
return bs.set_chk(v->uid - 1, 1);
|
||||
}
|
||||
|
||||
bool sb_value_set::remove_vec(vvec& vv) {
|
||||
bool modified = false;
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
if (*I)
|
||||
modified |= remove_val(*I);
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
void sb_value_set::clear() {
|
||||
bs.clear();
|
||||
}
|
||||
|
||||
bool sb_value_set::remove_val(value* v) {
|
||||
assert(v);
|
||||
if (bs.size() < v->uid)
|
||||
return false;
|
||||
return bs.set_chk(v->uid - 1, 0);
|
||||
}
|
||||
|
||||
bool r600_sb::sb_value_set::add_vec(vvec& vv) {
|
||||
bool modified = false;
|
||||
for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (v)
|
||||
modified |= add_val(v);
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool r600_sb::sb_value_set::contains(value* v) {
|
||||
unsigned b = v->uid - 1;
|
||||
if (b < bs.size())
|
||||
return bs.get(v->uid - 1);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sb_value_set::empty() {
|
||||
return bs.size() == 0 || bs.find_bit(0) == bs.size();
|
||||
}
|
||||
|
||||
void sb_bitset::swap(sb_bitset& bs2) {
|
||||
std::swap(data, bs2.data);
|
||||
std::swap(bit_size, bs2.bit_size);
|
||||
}
|
||||
|
||||
bool sb_bitset::operator ==(const sb_bitset& bs2) {
|
||||
if (bit_size != bs2.bit_size)
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0, c = data.size(); i < c; ++i) {
|
||||
if (data[i] != bs2.data[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
sb_bitset& sb_bitset::operator &=(const sb_bitset& bs2) {
|
||||
if (bit_size > bs2.bit_size) {
|
||||
resize(bs2.bit_size);
|
||||
}
|
||||
|
||||
for (unsigned i = 0, c = std::min(data.size(), bs2.data.size()); i < c;
|
||||
++i) {
|
||||
data[i] &= bs2.data[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
sb_bitset& sb_bitset::mask(const sb_bitset& bs2) {
|
||||
if (bit_size < bs2.bit_size) {
|
||||
resize(bs2.bit_size);
|
||||
}
|
||||
|
||||
for (unsigned i = 0, c = data.size(); i < c;
|
||||
++i) {
|
||||
data[i] &= ~bs2.data[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool ra_constraint::check() {
|
||||
assert(kind == CK_SAME_REG);
|
||||
|
||||
unsigned reg = 0;
|
||||
|
||||
for (vvec::iterator I = values.begin(), E = values.end(); I != E; ++I) {
|
||||
value *v = *I;
|
||||
if (!v)
|
||||
continue;
|
||||
|
||||
if (!v->gpr)
|
||||
return false;
|
||||
|
||||
if (reg == 0)
|
||||
reg = v->gpr.sel() + 1;
|
||||
else if (reg != v->gpr.sel() + 1)
|
||||
return false;
|
||||
|
||||
if (v->is_chan_pinned()) {
|
||||
if (v->pin_gpr.chan() != v->gpr.chan())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gpr_array::is_dead() {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace r600_sb
|
||||
Reference in New Issue
Block a user