2011-08-16 15:09:48 -07:00
|
|
|
/*
|
|
|
|
|
* Copyright © 2011 Intel Corporation
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
|
* 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 NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS 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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "brw_vec4.h"
|
2014-10-20 23:29:41 -07:00
|
|
|
#include "brw_fs.h"
|
2012-11-30 18:29:34 -08:00
|
|
|
#include "brw_cfg.h"
|
2015-04-07 15:15:09 -07:00
|
|
|
#include "brw_nir.h"
|
2016-02-11 12:27:02 -08:00
|
|
|
#include "brw_vec4_builder.h"
|
2015-03-18 20:49:43 +02:00
|
|
|
#include "brw_vec4_live_variables.h"
|
2017-02-28 19:00:45 -08:00
|
|
|
#include "brw_vec4_vs.h"
|
2013-10-30 10:32:12 -07:00
|
|
|
#include "brw_dead_control_flow.h"
|
2017-03-01 11:20:25 -08:00
|
|
|
#include "common/gen_debug.h"
|
2011-08-16 15:09:48 -07:00
|
|
|
#include "program/prog_parameter.h"
|
|
|
|
|
|
|
|
|
|
#define MAX_INSTRUCTION (1 << 30)
|
|
|
|
|
|
2012-11-26 21:46:27 -08:00
|
|
|
using namespace brw;
|
|
|
|
|
|
2011-08-16 15:09:48 -07:00
|
|
|
namespace brw {
|
|
|
|
|
|
2012-07-04 13:25:27 -07:00
|
|
|
void
|
|
|
|
|
src_reg::init()
|
|
|
|
|
{
|
|
|
|
|
memset(this, 0, sizeof(*this));
|
|
|
|
|
|
|
|
|
|
this->file = BAD_FILE;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-26 18:41:27 -07:00
|
|
|
src_reg::src_reg(enum brw_reg_file file, int nr, const glsl_type *type)
|
2012-07-04 13:25:27 -07:00
|
|
|
{
|
|
|
|
|
init();
|
|
|
|
|
|
|
|
|
|
this->file = file;
|
2015-10-26 04:35:14 -07:00
|
|
|
this->nr = nr;
|
2012-07-04 13:25:27 -07:00
|
|
|
if (type && (type->is_scalar() || type->is_vector() || type->is_matrix()))
|
2015-03-18 21:18:08 +02:00
|
|
|
this->swizzle = brw_swizzle_for_size(type->vector_elements);
|
2012-07-04 13:25:27 -07:00
|
|
|
else
|
2013-11-27 22:21:45 -08:00
|
|
|
this->swizzle = BRW_SWIZZLE_XYZW;
|
2015-09-01 17:02:20 +02:00
|
|
|
if (type)
|
|
|
|
|
this->type = brw_type_for_base_type(type);
|
2012-07-04 13:25:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Generic unset register constructor. */
|
|
|
|
|
src_reg::src_reg()
|
|
|
|
|
{
|
|
|
|
|
init();
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-23 16:17:28 -08:00
|
|
|
src_reg::src_reg(struct ::brw_reg reg) :
|
2015-10-24 15:29:03 -07:00
|
|
|
backend_reg(reg)
|
2013-11-22 20:22:03 -08:00
|
|
|
{
|
2016-09-01 13:10:36 -07:00
|
|
|
this->offset = 0;
|
2015-10-24 15:29:03 -07:00
|
|
|
this->reladdr = NULL;
|
2013-11-22 20:22:03 -08:00
|
|
|
}
|
|
|
|
|
|
2015-10-24 15:29:03 -07:00
|
|
|
src_reg::src_reg(const dst_reg ®) :
|
2015-11-20 21:18:26 -08:00
|
|
|
backend_reg(reg)
|
2012-07-04 13:31:46 -07:00
|
|
|
{
|
|
|
|
|
this->reladdr = reg.reladdr;
|
i965/vec4: Improve src_reg/dst_reg conversion constructors.
This simplifies the src_reg/dst_reg conversion constructors using the
swizzle utils introduced in a previous patch. It also makes them more
useful by changing their semantics slightly: dst_reg(src_reg) used to
set the writemask to XYZW if the src_reg swizzle was anything other
than XXXX, which was almost certainly not what the caller intended if
the swizzle was non-trivial. After this patch the same components
that are present in the swizzle will be enabled in the resulting
writemask.
src_reg(dst_reg) used to set the first components of the swizzle to
the enabled components of the writemask and then replicate the last
enabled component to fill the swizzle, which, in cases where the
writemask didn't have exactly the first n components set, would in
general not be compatible with the original dst_reg. E.g.:
| ADD(tmp, src_reg(tmp), src_reg(1));
would *not* do what one would expect (add one to each of the enabled
components of tmp) if tmp didn't have a writemask of the described
form (e.g. YZ, YW, XZW would all fail). This pattern actually occurs
in many different places in the VEC4 back-end, it's a wonder that it
hasn't caused piglit failures until now. After this patch
src_reg(dst_reg) will construct a swizzle with each enabled component
at its natural position (e.g. Y at the second position, Z at the
third, and so on). The resulting swizzle will behave like the
identity when used in any instruction with the original writemask.
I've manually verified that *none* of the callers of both conversion
constructors were relying on the previous broken semantics. There are
no piglit regressions on any generation.
Reviewed-by: Matt Turner <mattst88@gmail.com>
2015-03-18 15:27:58 +02:00
|
|
|
this->swizzle = brw_swizzle_for_mask(reg.writemask);
|
2012-07-04 13:31:46 -07:00
|
|
|
}
|
|
|
|
|
|
2012-07-04 13:25:27 -07:00
|
|
|
void
|
|
|
|
|
dst_reg::init()
|
|
|
|
|
{
|
|
|
|
|
memset(this, 0, sizeof(*this));
|
|
|
|
|
this->file = BAD_FILE;
|
|
|
|
|
this->writemask = WRITEMASK_XYZW;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dst_reg::dst_reg()
|
|
|
|
|
{
|
|
|
|
|
init();
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-26 18:41:27 -07:00
|
|
|
dst_reg::dst_reg(enum brw_reg_file file, int nr)
|
2012-07-04 13:25:27 -07:00
|
|
|
{
|
|
|
|
|
init();
|
|
|
|
|
|
|
|
|
|
this->file = file;
|
2015-10-26 04:35:14 -07:00
|
|
|
this->nr = nr;
|
2012-07-04 13:25:27 -07:00
|
|
|
}
|
|
|
|
|
|
2015-10-26 18:41:27 -07:00
|
|
|
dst_reg::dst_reg(enum brw_reg_file file, int nr, const glsl_type *type,
|
2015-03-18 15:24:06 +02:00
|
|
|
unsigned writemask)
|
2012-07-04 13:25:27 -07:00
|
|
|
{
|
|
|
|
|
init();
|
|
|
|
|
|
2015-10-26 18:41:27 -07:00
|
|
|
this->file = file;
|
2015-10-26 04:35:14 -07:00
|
|
|
this->nr = nr;
|
2012-07-04 13:25:27 -07:00
|
|
|
this->type = brw_type_for_base_type(type);
|
|
|
|
|
this->writemask = writemask;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-26 18:41:27 -07:00
|
|
|
dst_reg::dst_reg(enum brw_reg_file file, int nr, brw_reg_type type,
|
2015-05-23 23:42:58 +02:00
|
|
|
unsigned writemask)
|
|
|
|
|
{
|
|
|
|
|
init();
|
|
|
|
|
|
|
|
|
|
this->file = file;
|
2015-10-26 04:35:14 -07:00
|
|
|
this->nr = nr;
|
2015-05-23 23:42:58 +02:00
|
|
|
this->type = type;
|
|
|
|
|
this->writemask = writemask;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-23 16:17:28 -08:00
|
|
|
dst_reg::dst_reg(struct ::brw_reg reg) :
|
2015-10-24 15:29:03 -07:00
|
|
|
backend_reg(reg)
|
2012-07-04 13:25:27 -07:00
|
|
|
{
|
2016-09-01 13:10:36 -07:00
|
|
|
this->offset = 0;
|
2015-10-24 15:29:03 -07:00
|
|
|
this->reladdr = NULL;
|
2012-07-04 13:25:27 -07:00
|
|
|
}
|
|
|
|
|
|
2015-10-24 15:29:03 -07:00
|
|
|
dst_reg::dst_reg(const src_reg ®) :
|
2015-11-20 21:18:26 -08:00
|
|
|
backend_reg(reg)
|
2012-07-04 13:31:46 -07:00
|
|
|
{
|
i965/vec4: Improve src_reg/dst_reg conversion constructors.
This simplifies the src_reg/dst_reg conversion constructors using the
swizzle utils introduced in a previous patch. It also makes them more
useful by changing their semantics slightly: dst_reg(src_reg) used to
set the writemask to XYZW if the src_reg swizzle was anything other
than XXXX, which was almost certainly not what the caller intended if
the swizzle was non-trivial. After this patch the same components
that are present in the swizzle will be enabled in the resulting
writemask.
src_reg(dst_reg) used to set the first components of the swizzle to
the enabled components of the writemask and then replicate the last
enabled component to fill the swizzle, which, in cases where the
writemask didn't have exactly the first n components set, would in
general not be compatible with the original dst_reg. E.g.:
| ADD(tmp, src_reg(tmp), src_reg(1));
would *not* do what one would expect (add one to each of the enabled
components of tmp) if tmp didn't have a writemask of the described
form (e.g. YZ, YW, XZW would all fail). This pattern actually occurs
in many different places in the VEC4 back-end, it's a wonder that it
hasn't caused piglit failures until now. After this patch
src_reg(dst_reg) will construct a swizzle with each enabled component
at its natural position (e.g. Y at the second position, Z at the
third, and so on). The resulting swizzle will behave like the
identity when used in any instruction with the original writemask.
I've manually verified that *none* of the callers of both conversion
constructors were relying on the previous broken semantics. There are
no piglit regressions on any generation.
Reviewed-by: Matt Turner <mattst88@gmail.com>
2015-03-18 15:27:58 +02:00
|
|
|
this->writemask = brw_mask_for_swizzle(reg.swizzle);
|
2012-07-04 13:31:46 -07:00
|
|
|
this->reladdr = reg.reladdr;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 22:40:07 +02:00
|
|
|
bool
|
|
|
|
|
dst_reg::equals(const dst_reg &r) const
|
|
|
|
|
{
|
2015-11-22 13:25:05 -08:00
|
|
|
return (this->backend_reg::equals(r) &&
|
2015-02-05 22:40:07 +02:00
|
|
|
(reladdr == r.reladdr ||
|
2015-10-26 17:52:57 -07:00
|
|
|
(reladdr && r.reladdr && reladdr->equals(*r.reladdr))));
|
2015-02-05 22:40:07 +02:00
|
|
|
}
|
|
|
|
|
|
2012-12-17 16:48:20 -08:00
|
|
|
bool
|
|
|
|
|
vec4_instruction::is_send_from_grf()
|
|
|
|
|
{
|
2013-04-04 14:10:18 -07:00
|
|
|
switch (opcode) {
|
|
|
|
|
case SHADER_OPCODE_SHADER_TIME_ADD:
|
|
|
|
|
case VS_OPCODE_PULL_CONSTANT_LOAD_GEN7:
|
2015-02-26 17:42:47 +02:00
|
|
|
case SHADER_OPCODE_UNTYPED_ATOMIC:
|
|
|
|
|
case SHADER_OPCODE_UNTYPED_SURFACE_READ:
|
2015-04-23 14:24:14 +03:00
|
|
|
case SHADER_OPCODE_UNTYPED_SURFACE_WRITE:
|
2015-04-23 14:28:25 +03:00
|
|
|
case SHADER_OPCODE_TYPED_ATOMIC:
|
|
|
|
|
case SHADER_OPCODE_TYPED_SURFACE_READ:
|
|
|
|
|
case SHADER_OPCODE_TYPED_SURFACE_WRITE:
|
2015-11-17 01:07:39 -08:00
|
|
|
case VEC4_OPCODE_URB_READ:
|
|
|
|
|
case TCS_OPCODE_URB_WRITE:
|
2015-11-25 17:54:22 -08:00
|
|
|
case TCS_OPCODE_RELEASE_INPUT:
|
2015-11-17 01:07:39 -08:00
|
|
|
case SHADER_OPCODE_BARRIER:
|
2013-04-04 14:10:18 -07:00
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2012-12-17 16:48:20 -08:00
|
|
|
}
|
|
|
|
|
|
i965: Add src/dst interference for certain instructions with hazards.
When working on tessellation shaders, I created some vec4 virtual
opcodes for creating message headers through a sequence like:
mov(8) g7<1>UD 0x00000000UD { align1 WE_all 1Q compacted };
mov(1) g7.5<1>UD 0x00000100UD { align1 WE_all };
mov(1) g7<1>UD g0<0,1,0>UD { align1 WE_all compacted };
mov(1) g7.3<1>UD g8<0,1,0>UD { align1 WE_all };
This is done in the generator since the vec4 backend can't handle align1
regioning. From the visitor's point of view, this is a single opcode:
hs_set_output_urb_offsets vgrf7.0:UD, 1U, vgrf8.xxxx:UD
Normally, there's no hazard between sources and destinations - an
instruction (naturally) reads its sources, then writes the result to the
destination. However, when the virtual instruction generates multiple
hardware instructions, we can get into trouble.
In the above example, if the register allocator assigned vgrf7 and vgrf8
to the same hardware register, then we'd clobber the source with 0 in
the first instruction, and read back the wrong value in the last one.
It occured to me that this is exactly the same problem we have with
SIMD16 instructions that use W/UW or B/UB types with 0 stride. The
hardware implicitly decodes them as two SIMD8 instructions, and with
the overlapping regions, the first would clobber the second.
Previously, we handled that by incrementing the live range end IP by 1,
which works, but is excessive: the next instruction doesn't actually
care about that. It might also be the end of control flow. This might
keep values alive too long. What we really want is to say "my source
and destinations interfere".
This patch creates new infrastructure for doing just that, and teaches
the register allocator to add interference when there's a hazard. For
my vec4 case, we can determine this by switching on opcodes. For the
SIMD16 case, we just move the existing code there.
I audited our existing virtual opcodes that generate multiple
instructions; I believe FS_OPCODE_PACK_HALF_2x16_SPLIT needs this
treatment as well, but no others.
v2: Rebased by mattst88.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Matt Turner <mattst88@gmail.com>
2015-11-19 16:00:18 -08:00
|
|
|
/**
|
|
|
|
|
* Returns true if this instruction's sources and destinations cannot
|
|
|
|
|
* safely be the same register.
|
|
|
|
|
*
|
|
|
|
|
* In most cases, a register can be written over safely by the same
|
|
|
|
|
* instruction that is its last use. For a single instruction, the
|
|
|
|
|
* sources are dereferenced before writing of the destination starts
|
|
|
|
|
* (naturally).
|
|
|
|
|
*
|
|
|
|
|
* However, there are a few cases where this can be problematic:
|
|
|
|
|
*
|
|
|
|
|
* - Virtual opcodes that translate to multiple instructions in the
|
|
|
|
|
* code generator: if src == dst and one instruction writes the
|
|
|
|
|
* destination before a later instruction reads the source, then
|
|
|
|
|
* src will have been clobbered.
|
|
|
|
|
*
|
|
|
|
|
* The register allocator uses this information to set up conflicts between
|
|
|
|
|
* GRF sources and the destination.
|
|
|
|
|
*/
|
|
|
|
|
bool
|
|
|
|
|
vec4_instruction::has_source_and_destination_hazard() const
|
|
|
|
|
{
|
|
|
|
|
switch (opcode) {
|
2015-11-17 01:07:39 -08:00
|
|
|
case TCS_OPCODE_SET_INPUT_URB_OFFSETS:
|
|
|
|
|
case TCS_OPCODE_SET_OUTPUT_URB_OFFSETS:
|
2015-11-17 01:30:35 -08:00
|
|
|
case TES_OPCODE_ADD_INDIRECT_URB_OFFSET:
|
2015-11-17 01:07:39 -08:00
|
|
|
return true;
|
i965: Add src/dst interference for certain instructions with hazards.
When working on tessellation shaders, I created some vec4 virtual
opcodes for creating message headers through a sequence like:
mov(8) g7<1>UD 0x00000000UD { align1 WE_all 1Q compacted };
mov(1) g7.5<1>UD 0x00000100UD { align1 WE_all };
mov(1) g7<1>UD g0<0,1,0>UD { align1 WE_all compacted };
mov(1) g7.3<1>UD g8<0,1,0>UD { align1 WE_all };
This is done in the generator since the vec4 backend can't handle align1
regioning. From the visitor's point of view, this is a single opcode:
hs_set_output_urb_offsets vgrf7.0:UD, 1U, vgrf8.xxxx:UD
Normally, there's no hazard between sources and destinations - an
instruction (naturally) reads its sources, then writes the result to the
destination. However, when the virtual instruction generates multiple
hardware instructions, we can get into trouble.
In the above example, if the register allocator assigned vgrf7 and vgrf8
to the same hardware register, then we'd clobber the source with 0 in
the first instruction, and read back the wrong value in the last one.
It occured to me that this is exactly the same problem we have with
SIMD16 instructions that use W/UW or B/UB types with 0 stride. The
hardware implicitly decodes them as two SIMD8 instructions, and with
the overlapping regions, the first would clobber the second.
Previously, we handled that by incrementing the live range end IP by 1,
which works, but is excessive: the next instruction doesn't actually
care about that. It might also be the end of control flow. This might
keep values alive too long. What we really want is to say "my source
and destinations interfere".
This patch creates new infrastructure for doing just that, and teaches
the register allocator to add interference when there's a hazard. For
my vec4 case, we can determine this by switching on opcodes. For the
SIMD16 case, we just move the existing code there.
I audited our existing virtual opcodes that generate multiple
instructions; I believe FS_OPCODE_PACK_HALF_2x16_SPLIT needs this
treatment as well, but no others.
v2: Rebased by mattst88.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Matt Turner <mattst88@gmail.com>
2015-11-19 16:00:18 -08:00
|
|
|
default:
|
2016-09-01 14:23:26 +02:00
|
|
|
/* 8-wide compressed DF operations are executed as two 4-wide operations,
|
|
|
|
|
* so we have a src/dst hazard if the first half of the instruction
|
|
|
|
|
* overwrites the source of the second half. Prevent this by marking
|
|
|
|
|
* compressed instructions as having src/dst hazards, so the register
|
|
|
|
|
* allocator assigns safe register regions for dst and srcs.
|
|
|
|
|
*/
|
|
|
|
|
return size_written > REG_SIZE;
|
i965: Add src/dst interference for certain instructions with hazards.
When working on tessellation shaders, I created some vec4 virtual
opcodes for creating message headers through a sequence like:
mov(8) g7<1>UD 0x00000000UD { align1 WE_all 1Q compacted };
mov(1) g7.5<1>UD 0x00000100UD { align1 WE_all };
mov(1) g7<1>UD g0<0,1,0>UD { align1 WE_all compacted };
mov(1) g7.3<1>UD g8<0,1,0>UD { align1 WE_all };
This is done in the generator since the vec4 backend can't handle align1
regioning. From the visitor's point of view, this is a single opcode:
hs_set_output_urb_offsets vgrf7.0:UD, 1U, vgrf8.xxxx:UD
Normally, there's no hazard between sources and destinations - an
instruction (naturally) reads its sources, then writes the result to the
destination. However, when the virtual instruction generates multiple
hardware instructions, we can get into trouble.
In the above example, if the register allocator assigned vgrf7 and vgrf8
to the same hardware register, then we'd clobber the source with 0 in
the first instruction, and read back the wrong value in the last one.
It occured to me that this is exactly the same problem we have with
SIMD16 instructions that use W/UW or B/UB types with 0 stride. The
hardware implicitly decodes them as two SIMD8 instructions, and with
the overlapping regions, the first would clobber the second.
Previously, we handled that by incrementing the live range end IP by 1,
which works, but is excessive: the next instruction doesn't actually
care about that. It might also be the end of control flow. This might
keep values alive too long. What we really want is to say "my source
and destinations interfere".
This patch creates new infrastructure for doing just that, and teaches
the register allocator to add interference when there's a hazard. For
my vec4 case, we can determine this by switching on opcodes. For the
SIMD16 case, we just move the existing code there.
I audited our existing virtual opcodes that generate multiple
instructions; I believe FS_OPCODE_PACK_HALF_2x16_SPLIT needs this
treatment as well, but no others.
v2: Rebased by mattst88.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Matt Turner <mattst88@gmail.com>
2015-11-19 16:00:18 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-05 22:39:33 +02:00
|
|
|
unsigned
|
2016-09-07 17:00:30 -07:00
|
|
|
vec4_instruction::size_read(unsigned arg) const
|
2015-02-05 22:39:33 +02:00
|
|
|
{
|
|
|
|
|
switch (opcode) {
|
|
|
|
|
case SHADER_OPCODE_SHADER_TIME_ADD:
|
2015-02-26 17:42:47 +02:00
|
|
|
case SHADER_OPCODE_UNTYPED_ATOMIC:
|
|
|
|
|
case SHADER_OPCODE_UNTYPED_SURFACE_READ:
|
2015-04-23 14:24:14 +03:00
|
|
|
case SHADER_OPCODE_UNTYPED_SURFACE_WRITE:
|
2015-04-23 14:28:25 +03:00
|
|
|
case SHADER_OPCODE_TYPED_ATOMIC:
|
|
|
|
|
case SHADER_OPCODE_TYPED_SURFACE_READ:
|
|
|
|
|
case SHADER_OPCODE_TYPED_SURFACE_WRITE:
|
2015-11-17 01:07:39 -08:00
|
|
|
case TCS_OPCODE_URB_WRITE:
|
2016-09-07 17:00:30 -07:00
|
|
|
if (arg == 0)
|
|
|
|
|
return mlen * REG_SIZE;
|
|
|
|
|
break;
|
2015-02-05 22:39:33 +02:00
|
|
|
case VS_OPCODE_PULL_CONSTANT_LOAD_GEN7:
|
2016-09-07 17:00:30 -07:00
|
|
|
if (arg == 1)
|
|
|
|
|
return mlen * REG_SIZE;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-02-05 22:39:33 +02:00
|
|
|
|
2016-09-07 17:00:30 -07:00
|
|
|
switch (src[arg].file) {
|
|
|
|
|
case BAD_FILE:
|
|
|
|
|
return 0;
|
|
|
|
|
case IMM:
|
|
|
|
|
case UNIFORM:
|
|
|
|
|
return 4 * type_sz(src[arg].type);
|
2015-02-05 22:39:33 +02:00
|
|
|
default:
|
2016-05-30 13:20:31 +02:00
|
|
|
/* XXX - Represent actual vertical stride. */
|
|
|
|
|
return exec_size * type_sz(src[arg].type);
|
2015-02-05 22:39:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-17 17:03:02 -08:00
|
|
|
bool
|
2016-08-22 15:01:08 -07:00
|
|
|
vec4_instruction::can_do_source_mods(const struct gen_device_info *devinfo)
|
2012-12-17 17:03:02 -08:00
|
|
|
{
|
2015-04-15 18:00:05 -07:00
|
|
|
if (devinfo->gen == 6 && is_math())
|
2012-12-17 17:03:02 -08:00
|
|
|
return false;
|
|
|
|
|
|
2014-06-23 21:57:31 -07:00
|
|
|
if (is_send_from_grf())
|
2012-12-17 17:03:02 -08:00
|
|
|
return false;
|
|
|
|
|
|
2014-06-23 21:57:31 -07:00
|
|
|
if (!backend_instruction::can_do_source_mods())
|
2013-09-19 19:48:22 -07:00
|
|
|
return false;
|
|
|
|
|
|
2012-12-17 17:03:02 -08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-05 14:06:10 -07:00
|
|
|
bool
|
2016-08-22 15:01:08 -07:00
|
|
|
vec4_instruction::can_do_writemask(const struct gen_device_info *devinfo)
|
2016-04-05 14:06:10 -07:00
|
|
|
{
|
|
|
|
|
switch (opcode) {
|
|
|
|
|
case SHADER_OPCODE_GEN4_SCRATCH_READ:
|
2017-03-24 08:46:13 +01:00
|
|
|
case VEC4_OPCODE_DOUBLE_TO_F32:
|
|
|
|
|
case VEC4_OPCODE_DOUBLE_TO_D32:
|
|
|
|
|
case VEC4_OPCODE_DOUBLE_TO_U32:
|
2016-02-17 10:32:05 +01:00
|
|
|
case VEC4_OPCODE_TO_DOUBLE:
|
2016-05-31 10:17:37 +02:00
|
|
|
case VEC4_OPCODE_PICK_LOW_32BIT:
|
|
|
|
|
case VEC4_OPCODE_PICK_HIGH_32BIT:
|
2016-06-17 12:19:35 +02:00
|
|
|
case VEC4_OPCODE_SET_LOW_32BIT:
|
|
|
|
|
case VEC4_OPCODE_SET_HIGH_32BIT:
|
2016-04-05 14:06:10 -07:00
|
|
|
case VS_OPCODE_PULL_CONSTANT_LOAD:
|
|
|
|
|
case VS_OPCODE_PULL_CONSTANT_LOAD_GEN7:
|
|
|
|
|
case VS_OPCODE_SET_SIMD4X2_HEADER_GEN9:
|
|
|
|
|
case TCS_OPCODE_SET_INPUT_URB_OFFSETS:
|
|
|
|
|
case TCS_OPCODE_SET_OUTPUT_URB_OFFSETS:
|
|
|
|
|
case TES_OPCODE_CREATE_INPUT_READ_HEADER:
|
|
|
|
|
case TES_OPCODE_ADD_INDIRECT_URB_OFFSET:
|
|
|
|
|
case VEC4_OPCODE_URB_READ:
|
2015-11-23 21:39:15 -08:00
|
|
|
case SHADER_OPCODE_MOV_INDIRECT:
|
2016-04-05 14:06:10 -07:00
|
|
|
return false;
|
|
|
|
|
default:
|
|
|
|
|
/* The MATH instruction on Gen6 only executes in align1 mode, which does
|
|
|
|
|
* not support writemasking.
|
|
|
|
|
*/
|
|
|
|
|
if (devinfo->gen == 6 && is_math())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (is_tex())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-14 02:12:09 -07:00
|
|
|
bool
|
|
|
|
|
vec4_instruction::can_change_types() const
|
|
|
|
|
{
|
|
|
|
|
return dst.type == src[0].type &&
|
|
|
|
|
!src[0].abs && !src[0].negate && !saturate &&
|
|
|
|
|
(opcode == BRW_OPCODE_MOV ||
|
|
|
|
|
(opcode == BRW_OPCODE_SEL &&
|
|
|
|
|
dst.type == src[1].type &&
|
|
|
|
|
predicate != BRW_PREDICATE_NONE &&
|
|
|
|
|
!src[1].abs && !src[1].negate));
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-06 12:26:42 -07:00
|
|
|
/**
|
|
|
|
|
* Returns how many MRFs an opcode will write over.
|
|
|
|
|
*
|
|
|
|
|
* Note that this is not the 0 or 1 implied writes in an actual gen
|
|
|
|
|
* instruction -- the generate_* functions generate additional MOVs
|
|
|
|
|
* for setup.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
vec4_visitor::implied_mrf_writes(vec4_instruction *inst)
|
|
|
|
|
{
|
2015-02-05 22:58:03 +02:00
|
|
|
if (inst->mlen == 0 || inst->is_send_from_grf())
|
2011-09-06 12:26:42 -07:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
switch (inst->opcode) {
|
|
|
|
|
case SHADER_OPCODE_RCP:
|
|
|
|
|
case SHADER_OPCODE_RSQ:
|
|
|
|
|
case SHADER_OPCODE_SQRT:
|
|
|
|
|
case SHADER_OPCODE_EXP2:
|
|
|
|
|
case SHADER_OPCODE_LOG2:
|
|
|
|
|
case SHADER_OPCODE_SIN:
|
|
|
|
|
case SHADER_OPCODE_COS:
|
|
|
|
|
return 1;
|
2013-05-28 14:35:19 -07:00
|
|
|
case SHADER_OPCODE_INT_QUOTIENT:
|
|
|
|
|
case SHADER_OPCODE_INT_REMAINDER:
|
2011-09-06 12:26:42 -07:00
|
|
|
case SHADER_OPCODE_POW:
|
2016-01-05 01:53:57 -08:00
|
|
|
case TCS_OPCODE_THREAD_END:
|
2011-09-06 12:26:42 -07:00
|
|
|
return 2;
|
|
|
|
|
case VS_OPCODE_URB_WRITE:
|
|
|
|
|
return 1;
|
|
|
|
|
case VS_OPCODE_PULL_CONSTANT_LOAD:
|
|
|
|
|
return 2;
|
2013-10-16 11:45:06 -07:00
|
|
|
case SHADER_OPCODE_GEN4_SCRATCH_READ:
|
2011-09-06 12:26:42 -07:00
|
|
|
return 2;
|
2013-10-16 11:45:06 -07:00
|
|
|
case SHADER_OPCODE_GEN4_SCRATCH_WRITE:
|
2011-09-06 12:26:42 -07:00
|
|
|
return 3;
|
2013-03-21 09:11:12 -07:00
|
|
|
case GS_OPCODE_URB_WRITE:
|
2014-07-09 16:28:30 +02:00
|
|
|
case GS_OPCODE_URB_WRITE_ALLOCATE:
|
2013-03-23 07:42:32 -07:00
|
|
|
case GS_OPCODE_THREAD_END:
|
2013-03-21 09:11:12 -07:00
|
|
|
return 0;
|
2014-07-09 08:46:17 +02:00
|
|
|
case GS_OPCODE_FF_SYNC:
|
|
|
|
|
return 1;
|
2015-11-17 01:07:39 -08:00
|
|
|
case TCS_OPCODE_URB_WRITE:
|
|
|
|
|
return 0;
|
2012-11-27 14:10:52 -08:00
|
|
|
case SHADER_OPCODE_SHADER_TIME_ADD:
|
|
|
|
|
return 0;
|
2012-11-30 16:13:34 -08:00
|
|
|
case SHADER_OPCODE_TEX:
|
|
|
|
|
case SHADER_OPCODE_TXL:
|
|
|
|
|
case SHADER_OPCODE_TXD:
|
|
|
|
|
case SHADER_OPCODE_TXF:
|
2013-12-10 16:36:31 +02:00
|
|
|
case SHADER_OPCODE_TXF_CMS:
|
2015-09-09 15:59:36 +01:00
|
|
|
case SHADER_OPCODE_TXF_CMS_W:
|
2013-11-30 10:32:16 +13:00
|
|
|
case SHADER_OPCODE_TXF_MCS:
|
2012-11-30 16:13:34 -08:00
|
|
|
case SHADER_OPCODE_TXS:
|
2013-03-31 21:31:12 +13:00
|
|
|
case SHADER_OPCODE_TG4:
|
2013-10-08 21:42:10 +13:00
|
|
|
case SHADER_OPCODE_TG4_OFFSET:
|
2015-08-11 20:37:32 -04:00
|
|
|
case SHADER_OPCODE_SAMPLEINFO:
|
2015-08-28 09:39:49 +02:00
|
|
|
case VS_OPCODE_GET_BUFFER_SIZE:
|
2015-03-24 10:17:32 -07:00
|
|
|
return inst->header_size;
|
2011-09-06 12:26:42 -07:00
|
|
|
default:
|
2014-06-29 14:54:01 -07:00
|
|
|
unreachable("not reached");
|
2011-09-06 12:26:42 -07:00
|
|
|
}
|
|
|
|
|
}
|
2011-09-01 16:21:14 -07:00
|
|
|
|
2011-08-30 13:25:15 -07:00
|
|
|
bool
|
2013-03-06 08:51:44 -08:00
|
|
|
src_reg::equals(const src_reg &r) const
|
2011-08-30 13:25:15 -07:00
|
|
|
{
|
2015-11-22 13:25:05 -08:00
|
|
|
return (this->backend_reg::equals(r) &&
|
2015-10-26 17:52:57 -07:00
|
|
|
!reladdr && !r.reladdr);
|
2011-08-30 13:25:15 -07:00
|
|
|
}
|
|
|
|
|
|
2014-12-20 11:50:31 -08:00
|
|
|
bool
|
|
|
|
|
vec4_visitor::opt_vector_float()
|
|
|
|
|
{
|
|
|
|
|
bool progress = false;
|
|
|
|
|
|
2016-07-20 12:21:41 -07:00
|
|
|
foreach_block(block, cfg) {
|
2016-09-01 21:51:07 -07:00
|
|
|
int last_reg = -1, last_offset = -1;
|
2016-07-20 12:21:41 -07:00
|
|
|
enum brw_reg_file last_reg_file = BAD_FILE;
|
|
|
|
|
|
|
|
|
|
uint8_t imm[4] = { 0 };
|
|
|
|
|
int inst_count = 0;
|
|
|
|
|
vec4_instruction *imm_inst[4];
|
|
|
|
|
unsigned writemask = 0;
|
|
|
|
|
enum brw_reg_type dest_type = BRW_REGISTER_TYPE_F;
|
|
|
|
|
|
|
|
|
|
foreach_inst_in_block_safe(vec4_instruction, inst, block) {
|
|
|
|
|
int vf = -1;
|
|
|
|
|
enum brw_reg_type need_type;
|
|
|
|
|
|
|
|
|
|
/* Look for unconditional MOVs from an immediate with a partial
|
|
|
|
|
* writemask. Skip type-conversion MOVs other than integer 0,
|
|
|
|
|
* where the type doesn't matter. See if the immediate can be
|
|
|
|
|
* represented as a VF.
|
|
|
|
|
*/
|
|
|
|
|
if (inst->opcode == BRW_OPCODE_MOV &&
|
|
|
|
|
inst->src[0].file == IMM &&
|
|
|
|
|
inst->predicate == BRW_PREDICATE_NONE &&
|
|
|
|
|
inst->dst.writemask != WRITEMASK_XYZW &&
|
2016-02-12 13:10:06 +01:00
|
|
|
type_sz(inst->src[0].type) < 8 &&
|
2016-07-20 12:21:41 -07:00
|
|
|
(inst->src[0].type == inst->dst.type || inst->src[0].d == 0)) {
|
|
|
|
|
|
|
|
|
|
vf = brw_float_to_vf(inst->src[0].d);
|
|
|
|
|
need_type = BRW_REGISTER_TYPE_D;
|
|
|
|
|
|
|
|
|
|
if (vf == -1) {
|
|
|
|
|
vf = brw_float_to_vf(inst->src[0].f);
|
|
|
|
|
need_type = BRW_REGISTER_TYPE_F;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
last_reg = -1;
|
i965: Properly handle integer types in opt_vector_float().
Previously, opt_vector_float() always interpreted MOV sources as
floating point, and always created a MOV with a F-type destination.
This meant that we could mess up sequences of integer loads, such as:
mov vgrf6.0.x:D, 0D
mov vgrf6.0.y:D, 1D
mov vgrf6.0.z:D, 2D
mov vgrf6.0.w:D, 3D
Here, integer 0/1/2/3 become approximately 0.0f, so we generated:
mov vgrf6.0:F, [0F, 0F, 0F, 0F]
which is clearly wrong. We can properly handle this by converting
integer values to float (rather than bitcasting), and emitting a type
converting MOV:
mov vgrf6.0:D, [0F, 1F, 2F, 3F]
To do this, see first see if the integer values (converted to float)
are representable. If so, we use a D-type MOV. If not, we then try
the floating point values and an F-type MOV. We make zero not impose
type restrictions. This is important because 0D would imply a D-type
MOV, but is often used in sequences such as MOV 0D, MOV 0x3f800000D,
where we want to use an F-type MOV.
Fixes about 54 dEQP-GLES2 failures with the vec4 VS backend. This
recently became visible due to changes in opt_vector_float() which
made it optimize more cases, but it was a pre-existing bug.
Apparently it also manages to turn more integer loads into VFs,
producing the following shader-db statistics on Haswell:
total instructions in shared programs: 7084195 -> 7082191 (-0.03%)
instructions in affected programs: 246027 -> 244023 (-0.81%)
helped: 1937
total cycles in shared programs: 65669642 -> 65651968 (-0.03%)
cycles in affected programs: 531064 -> 513390 (-3.33%)
helped: 1177
v2: Handle the type of zero better.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Iago Toral Quiroga <itoral@igalia.com>
Reviewed-by: Matt Turner <mattst88@gmail.com>
2016-04-13 16:58:10 -07:00
|
|
|
}
|
2016-04-13 15:56:07 -07:00
|
|
|
|
2016-07-20 12:21:41 -07:00
|
|
|
/* If this wasn't a MOV, or the destination register doesn't match,
|
|
|
|
|
* or we have to switch destination types, then this breaks our
|
|
|
|
|
* sequence. Combine anything we've accumulated so far.
|
|
|
|
|
*/
|
|
|
|
|
if (last_reg != inst->dst.nr ||
|
2016-09-01 21:51:07 -07:00
|
|
|
last_offset != inst->dst.offset ||
|
2016-07-20 12:21:41 -07:00
|
|
|
last_reg_file != inst->dst.file ||
|
|
|
|
|
(vf > 0 && dest_type != need_type)) {
|
|
|
|
|
|
|
|
|
|
if (inst_count > 1) {
|
|
|
|
|
unsigned vf;
|
|
|
|
|
memcpy(&vf, imm, sizeof(vf));
|
|
|
|
|
vec4_instruction *mov = MOV(imm_inst[0]->dst, brw_imm_vf(vf));
|
|
|
|
|
mov->dst.type = dest_type;
|
|
|
|
|
mov->dst.writemask = writemask;
|
|
|
|
|
inst->insert_before(block, mov);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < inst_count; i++) {
|
|
|
|
|
imm_inst[i]->remove(block);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
progress = true;
|
2016-04-13 16:04:04 -07:00
|
|
|
}
|
|
|
|
|
|
2016-07-20 12:21:41 -07:00
|
|
|
inst_count = 0;
|
|
|
|
|
last_reg = -1;
|
|
|
|
|
writemask = 0;
|
|
|
|
|
dest_type = BRW_REGISTER_TYPE_F;
|
2016-04-13 16:04:04 -07:00
|
|
|
|
2016-07-20 12:21:41 -07:00
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
imm[i] = 0;
|
|
|
|
|
}
|
i965/vec4: add opportunistic behaviour to opt_vector_float()
opt_vector_float() transforms several scalar MOV operations to a single
vectorial MOV.
This is done when those MOV covers all the components of the destination
register. So something like:
mov vgrf3.0.xy:D, 0D
mov vgrf3.0.w:D, 1065353216D
mov vgrf3.0.z:D, 0D
is transformed in:
mov vgrf3.0:F, [0F, 0F, 0F, 1F]
But there are cases where not all the components are written. For
example, in:
mov vgrf2.0.x:D, 1073741824D
mov vgrf3.0.xy:D, 0D
mov vgrf3.0.w:D, 1065353216D
mov vgrf4.0.xy:D, 1065353216D
mov vgrf4.0.w:D, 0D
mov vgrf6.0:UD, u4.xyzw:UD
Nor vgrf3 nor vgrf4 .z components are written, so the optimization is
not applied.
But it could be applied anyway with the components covered, using a
writemask to select the ones written. So we could transform it in:
mov vgrf2.0.x:D, 1073741824D
mov vgrf3.0.xyw:F, [0F, 0F, 0F, 1F]
mov vgrf4.0.xyw:F, [1F, 1F, 0F, 0F]
mov vgrf6.0:UD, u4.xyzw:UD
This commit does precisely that: opportunistically apply
opt_vector_float() when possible.
total instructions in shared programs: 7124660 -> 7114784 (-0.14%)
instructions in affected programs: 443078 -> 433202 (-2.23%)
helped: 4998
HURT: 0
total cycles in shared programs: 64757760 -> 64728016 (-0.05%)
cycles in affected programs: 1401686 -> 1371942 (-2.12%)
helped: 3243
HURT: 38
v2: change vectorize_mov() signature (Matt).
v3: take in account predicates (Juan).
v4 [mattst88]: Update shader-db numbers. Fix some whitespace issues.
Reviewed-by: Matt Turner <mattst88@gmail.com>
Signed-off-by: Juan A. Suarez Romero <jasuarez@igalia.com>
2016-03-02 13:21:02 +01:00
|
|
|
}
|
2014-12-20 11:50:31 -08:00
|
|
|
|
2016-07-20 12:21:41 -07:00
|
|
|
/* Record this instruction's value (if it was representable). */
|
|
|
|
|
if (vf != -1) {
|
|
|
|
|
if ((inst->dst.writemask & WRITEMASK_X) != 0)
|
|
|
|
|
imm[0] = vf;
|
|
|
|
|
if ((inst->dst.writemask & WRITEMASK_Y) != 0)
|
|
|
|
|
imm[1] = vf;
|
|
|
|
|
if ((inst->dst.writemask & WRITEMASK_Z) != 0)
|
|
|
|
|
imm[2] = vf;
|
|
|
|
|
if ((inst->dst.writemask & WRITEMASK_W) != 0)
|
|
|
|
|
imm[3] = vf;
|
|
|
|
|
|
|
|
|
|
writemask |= inst->dst.writemask;
|
|
|
|
|
imm_inst[inst_count++] = inst;
|
|
|
|
|
|
|
|
|
|
last_reg = inst->dst.nr;
|
2016-09-01 21:51:07 -07:00
|
|
|
last_offset = inst->dst.offset;
|
2016-07-20 12:21:41 -07:00
|
|
|
last_reg_file = inst->dst.file;
|
|
|
|
|
if (vf > 0)
|
|
|
|
|
dest_type = need_type;
|
|
|
|
|
}
|
2016-04-13 15:56:07 -07:00
|
|
|
}
|
2014-12-20 11:50:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (progress)
|
|
|
|
|
invalidate_live_intervals();
|
|
|
|
|
|
|
|
|
|
return progress;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-17 15:13:54 -07:00
|
|
|
/* Replaces unused channels of a swizzle with channels that are used.
|
|
|
|
|
*
|
|
|
|
|
* For instance, this pass transforms
|
|
|
|
|
*
|
|
|
|
|
* mov vgrf4.yz, vgrf5.wxzy
|
|
|
|
|
*
|
|
|
|
|
* into
|
|
|
|
|
*
|
|
|
|
|
* mov vgrf4.yz, vgrf5.xxzx
|
|
|
|
|
*
|
|
|
|
|
* This eliminates false uses of some channels, letting dead code elimination
|
|
|
|
|
* remove the instructions that wrote them.
|
|
|
|
|
*/
|
|
|
|
|
bool
|
|
|
|
|
vec4_visitor::opt_reduce_swizzle()
|
|
|
|
|
{
|
|
|
|
|
bool progress = false;
|
|
|
|
|
|
2014-09-01 13:42:51 -07:00
|
|
|
foreach_block_and_inst_safe(block, vec4_instruction, inst, cfg) {
|
2015-10-26 17:52:57 -07:00
|
|
|
if (inst->dst.file == BAD_FILE ||
|
|
|
|
|
inst->dst.file == ARF ||
|
|
|
|
|
inst->dst.file == FIXED_GRF ||
|
2015-02-03 20:34:17 +02:00
|
|
|
inst->is_send_from_grf())
|
2014-08-17 15:13:54 -07:00
|
|
|
continue;
|
|
|
|
|
|
2015-03-18 21:08:52 +02:00
|
|
|
unsigned swizzle;
|
2014-08-17 15:13:54 -07:00
|
|
|
|
|
|
|
|
/* Determine which channels of the sources are read. */
|
|
|
|
|
switch (inst->opcode) {
|
2014-03-10 13:26:30 -07:00
|
|
|
case VEC4_OPCODE_PACK_BYTES:
|
2014-08-17 15:13:54 -07:00
|
|
|
case BRW_OPCODE_DP4:
|
|
|
|
|
case BRW_OPCODE_DPH: /* FINISHME: DPH reads only three channels of src0,
|
|
|
|
|
* but all four of src1.
|
|
|
|
|
*/
|
2015-03-18 21:08:52 +02:00
|
|
|
swizzle = brw_swizzle_for_size(4);
|
2014-08-17 15:13:54 -07:00
|
|
|
break;
|
|
|
|
|
case BRW_OPCODE_DP3:
|
2015-03-18 21:08:52 +02:00
|
|
|
swizzle = brw_swizzle_for_size(3);
|
2014-08-17 15:13:54 -07:00
|
|
|
break;
|
|
|
|
|
case BRW_OPCODE_DP2:
|
2015-03-18 21:08:52 +02:00
|
|
|
swizzle = brw_swizzle_for_size(2);
|
2014-08-17 15:13:54 -07:00
|
|
|
break;
|
2015-08-13 15:36:05 -07:00
|
|
|
|
2016-02-17 10:32:05 +01:00
|
|
|
case VEC4_OPCODE_TO_DOUBLE:
|
2017-03-24 08:46:13 +01:00
|
|
|
case VEC4_OPCODE_DOUBLE_TO_F32:
|
|
|
|
|
case VEC4_OPCODE_DOUBLE_TO_D32:
|
|
|
|
|
case VEC4_OPCODE_DOUBLE_TO_U32:
|
2016-05-31 10:17:37 +02:00
|
|
|
case VEC4_OPCODE_PICK_LOW_32BIT:
|
|
|
|
|
case VEC4_OPCODE_PICK_HIGH_32BIT:
|
2016-06-17 12:19:35 +02:00
|
|
|
case VEC4_OPCODE_SET_LOW_32BIT:
|
|
|
|
|
case VEC4_OPCODE_SET_HIGH_32BIT:
|
2015-08-13 15:36:05 -07:00
|
|
|
swizzle = brw_swizzle_for_size(4);
|
|
|
|
|
break;
|
|
|
|
|
|
2014-08-17 15:13:54 -07:00
|
|
|
default:
|
2015-03-18 21:08:52 +02:00
|
|
|
swizzle = brw_swizzle_for_mask(inst->dst.writemask);
|
2014-08-17 15:13:54 -07:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update sources' swizzles. */
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
2015-10-26 17:09:25 -07:00
|
|
|
if (inst->src[i].file != VGRF &&
|
2014-08-17 15:13:54 -07:00
|
|
|
inst->src[i].file != ATTR &&
|
|
|
|
|
inst->src[i].file != UNIFORM)
|
|
|
|
|
continue;
|
|
|
|
|
|
2015-03-18 21:08:52 +02:00
|
|
|
const unsigned new_swizzle =
|
|
|
|
|
brw_compose_swizzle(swizzle, inst->src[i].swizzle);
|
2014-08-17 15:13:54 -07:00
|
|
|
if (inst->src[i].swizzle != new_swizzle) {
|
|
|
|
|
inst->src[i].swizzle = new_swizzle;
|
|
|
|
|
progress = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (progress)
|
2014-09-01 10:54:00 -07:00
|
|
|
invalidate_live_intervals();
|
2014-08-17 15:13:54 -07:00
|
|
|
|
|
|
|
|
return progress;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-23 10:22:50 -07:00
|
|
|
void
|
|
|
|
|
vec4_visitor::split_uniform_registers()
|
|
|
|
|
{
|
|
|
|
|
/* Prior to this, uniforms have been in an array sized according to
|
|
|
|
|
* the number of vector uniforms present, sparsely filled (so an
|
|
|
|
|
* aggregate results in reg indices being skipped over). Now we're
|
2015-10-26 04:35:14 -07:00
|
|
|
* going to cut those aggregates up so each .nr index is one
|
2011-08-23 10:22:50 -07:00
|
|
|
* vector. The goal is to make elimination of unused uniform
|
|
|
|
|
* components easier later.
|
|
|
|
|
*/
|
2014-09-01 13:42:51 -07:00
|
|
|
foreach_block_and_inst(block, vec4_instruction, inst, cfg) {
|
2011-08-23 10:22:50 -07:00
|
|
|
for (int i = 0 ; i < 3; i++) {
|
|
|
|
|
if (inst->src[i].file != UNIFORM)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
assert(!inst->src[i].reladdr);
|
|
|
|
|
|
2016-09-01 13:10:36 -07:00
|
|
|
inst->src[i].nr += inst->src[i].offset / 16;
|
|
|
|
|
inst->src[i].offset %= 16;
|
2011-08-23 10:22:50 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-19 11:18:35 +02:00
|
|
|
/* This function returns the register number where we placed the uniform */
|
|
|
|
|
static int
|
|
|
|
|
set_push_constant_loc(const int nr_uniforms, int *new_uniform_count,
|
|
|
|
|
const int src, const int size, const int channel_size,
|
|
|
|
|
int *new_loc, int *new_chan,
|
|
|
|
|
int *new_chans_used)
|
|
|
|
|
{
|
|
|
|
|
int dst;
|
|
|
|
|
/* Find the lowest place we can slot this uniform in. */
|
|
|
|
|
for (dst = 0; dst < nr_uniforms; dst++) {
|
|
|
|
|
if (ALIGN(new_chans_used[dst], channel_size) + size <= 4)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(dst < nr_uniforms);
|
|
|
|
|
|
|
|
|
|
new_loc[src] = dst;
|
|
|
|
|
new_chan[src] = ALIGN(new_chans_used[dst], channel_size);
|
|
|
|
|
new_chans_used[dst] = ALIGN(new_chans_used[dst], channel_size) + size;
|
|
|
|
|
|
|
|
|
|
*new_uniform_count = MAX2(*new_uniform_count, dst + 1);
|
|
|
|
|
return dst;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-23 12:13:14 -07:00
|
|
|
void
|
|
|
|
|
vec4_visitor::pack_uniform_registers()
|
|
|
|
|
{
|
2015-09-30 10:31:03 -07:00
|
|
|
uint8_t chans_used[this->uniforms];
|
2011-08-23 12:13:14 -07:00
|
|
|
int new_loc[this->uniforms];
|
|
|
|
|
int new_chan[this->uniforms];
|
2017-04-19 11:18:35 +02:00
|
|
|
bool is_aligned_to_dvec4[this->uniforms];
|
|
|
|
|
int new_chans_used[this->uniforms];
|
|
|
|
|
int channel_sizes[this->uniforms];
|
2011-08-23 12:13:14 -07:00
|
|
|
|
2015-09-30 10:31:03 -07:00
|
|
|
memset(chans_used, 0, sizeof(chans_used));
|
2011-08-23 12:13:14 -07:00
|
|
|
memset(new_loc, 0, sizeof(new_loc));
|
|
|
|
|
memset(new_chan, 0, sizeof(new_chan));
|
2017-04-19 11:18:35 +02:00
|
|
|
memset(new_chans_used, 0, sizeof(new_chans_used));
|
|
|
|
|
memset(is_aligned_to_dvec4, 0, sizeof(is_aligned_to_dvec4));
|
|
|
|
|
memset(channel_sizes, 0, sizeof(channel_sizes));
|
2011-08-23 12:13:14 -07:00
|
|
|
|
|
|
|
|
/* Find which uniform vectors are actually used by the program. We
|
|
|
|
|
* expect unused vector elements when we've moved array access out
|
|
|
|
|
* to pull constants, and from some GLSL code generators like wine.
|
|
|
|
|
*/
|
2014-09-01 13:42:51 -07:00
|
|
|
foreach_block_and_inst(block, vec4_instruction, inst, cfg) {
|
2015-09-30 10:31:03 -07:00
|
|
|
unsigned readmask;
|
|
|
|
|
switch (inst->opcode) {
|
|
|
|
|
case VEC4_OPCODE_PACK_BYTES:
|
|
|
|
|
case BRW_OPCODE_DP4:
|
|
|
|
|
case BRW_OPCODE_DPH:
|
|
|
|
|
readmask = 0xf;
|
|
|
|
|
break;
|
|
|
|
|
case BRW_OPCODE_DP3:
|
|
|
|
|
readmask = 0x7;
|
|
|
|
|
break;
|
|
|
|
|
case BRW_OPCODE_DP2:
|
|
|
|
|
readmask = 0x3;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
readmask = inst->dst.writemask;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-23 12:13:14 -07:00
|
|
|
for (int i = 0 ; i < 3; i++) {
|
2015-09-30 10:31:03 -07:00
|
|
|
if (inst->src[i].file != UNIFORM)
|
|
|
|
|
continue;
|
2011-08-23 12:13:14 -07:00
|
|
|
|
2016-06-29 10:49:47 +02:00
|
|
|
assert(type_sz(inst->src[i].type) % 4 == 0);
|
2017-04-19 11:18:35 +02:00
|
|
|
int channel_size = type_sz(inst->src[i].type) / 4;
|
2016-06-29 10:49:47 +02:00
|
|
|
|
2015-10-26 04:35:14 -07:00
|
|
|
int reg = inst->src[i].nr;
|
2015-09-30 10:31:03 -07:00
|
|
|
for (int c = 0; c < 4; c++) {
|
|
|
|
|
if (!(readmask & (1 << c)))
|
|
|
|
|
continue;
|
|
|
|
|
|
2016-06-29 10:49:47 +02:00
|
|
|
unsigned channel = BRW_GET_SWZ(inst->src[i].swizzle, c) + 1;
|
|
|
|
|
unsigned used = MAX2(chans_used[reg], channel * channel_size);
|
2017-04-19 11:18:35 +02:00
|
|
|
if (used <= 4) {
|
2016-06-29 10:49:47 +02:00
|
|
|
chans_used[reg] = used;
|
2017-04-19 11:18:35 +02:00
|
|
|
channel_sizes[reg] = MAX2(channel_sizes[reg], channel_size);
|
|
|
|
|
} else {
|
|
|
|
|
is_aligned_to_dvec4[reg] = true;
|
|
|
|
|
is_aligned_to_dvec4[reg + 1] = true;
|
2016-06-29 10:49:47 +02:00
|
|
|
chans_used[reg + 1] = used - 4;
|
2017-04-19 11:18:35 +02:00
|
|
|
channel_sizes[reg + 1] = MAX2(channel_sizes[reg + 1], channel_size);
|
|
|
|
|
}
|
2015-09-30 10:31:03 -07:00
|
|
|
}
|
2011-08-23 12:13:14 -07:00
|
|
|
}
|
2016-04-05 15:43:48 -07:00
|
|
|
|
|
|
|
|
if (inst->opcode == SHADER_OPCODE_MOV_INDIRECT &&
|
|
|
|
|
inst->src[0].file == UNIFORM) {
|
|
|
|
|
assert(inst->src[2].file == BRW_IMMEDIATE_VALUE);
|
|
|
|
|
assert(inst->src[0].subnr == 0);
|
|
|
|
|
|
|
|
|
|
unsigned bytes_read = inst->src[2].ud;
|
|
|
|
|
assert(bytes_read % 4 == 0);
|
|
|
|
|
unsigned vec4s_read = DIV_ROUND_UP(bytes_read, 16);
|
|
|
|
|
|
|
|
|
|
/* We just mark every register touched by a MOV_INDIRECT as being
|
|
|
|
|
* fully used. This ensures that it doesn't broken up piecewise by
|
|
|
|
|
* the next part of our packing algorithm.
|
|
|
|
|
*/
|
|
|
|
|
int reg = inst->src[0].nr;
|
|
|
|
|
for (unsigned i = 0; i < vec4s_read; i++)
|
|
|
|
|
chans_used[reg + i] = 4;
|
|
|
|
|
}
|
2011-08-23 12:13:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int new_uniform_count = 0;
|
|
|
|
|
|
2017-04-19 11:18:35 +02:00
|
|
|
/* As the uniforms are going to be reordered, take the data from a temporary
|
|
|
|
|
* copy of the original param[].
|
|
|
|
|
*/
|
|
|
|
|
gl_constant_value **param = ralloc_array(NULL, gl_constant_value*,
|
|
|
|
|
stage_prog_data->nr_params);
|
|
|
|
|
memcpy(param, stage_prog_data->param,
|
|
|
|
|
sizeof(gl_constant_value*) * stage_prog_data->nr_params);
|
|
|
|
|
|
2011-08-23 12:13:14 -07:00
|
|
|
/* Now, figure out a packing of the live uniform vectors into our
|
2017-04-19 11:18:35 +02:00
|
|
|
* push constants. Start with dvec{3,4} because they are aligned to
|
|
|
|
|
* dvec4 size (2 vec4).
|
2011-08-23 12:13:14 -07:00
|
|
|
*/
|
|
|
|
|
for (int src = 0; src < uniforms; src++) {
|
2015-09-30 10:31:03 -07:00
|
|
|
int size = chans_used[src];
|
2011-08-23 12:13:14 -07:00
|
|
|
|
2017-04-19 11:18:35 +02:00
|
|
|
if (size == 0 || !is_aligned_to_dvec4[src])
|
2015-09-30 10:31:03 -07:00
|
|
|
continue;
|
2011-08-23 12:13:14 -07:00
|
|
|
|
2017-04-19 11:18:35 +02:00
|
|
|
/* dvec3 are aligned to dvec4 size, apply the alignment of the size
|
|
|
|
|
* to 4 to avoid moving last component of a dvec3 to the available
|
|
|
|
|
* location at the end of a previous dvec3. These available locations
|
|
|
|
|
* could be filled by smaller variables in next loop.
|
|
|
|
|
*/
|
|
|
|
|
size = ALIGN(size, 4);
|
|
|
|
|
int dst = set_push_constant_loc(uniforms, &new_uniform_count,
|
|
|
|
|
src, size, channel_sizes[src],
|
|
|
|
|
new_loc, new_chan,
|
|
|
|
|
new_chans_used);
|
|
|
|
|
/* Move the references to the data */
|
|
|
|
|
for (int j = 0; j < size; j++) {
|
|
|
|
|
stage_prog_data->param[dst * 4 + new_chan[src] + j] =
|
|
|
|
|
param[src * 4 + j];
|
2011-08-23 12:13:14 -07:00
|
|
|
}
|
2017-04-19 11:18:35 +02:00
|
|
|
}
|
2011-08-23 12:13:14 -07:00
|
|
|
|
2017-04-19 11:18:35 +02:00
|
|
|
/* Continue with the rest of data, which is aligned to vec4. */
|
|
|
|
|
for (int src = 0; src < uniforms; src++) {
|
|
|
|
|
int size = chans_used[src];
|
2011-08-23 12:13:14 -07:00
|
|
|
|
2017-04-19 11:18:35 +02:00
|
|
|
if (size == 0 || is_aligned_to_dvec4[src])
|
|
|
|
|
continue;
|
2011-08-23 12:13:14 -07:00
|
|
|
|
2017-04-19 11:18:35 +02:00
|
|
|
int dst = set_push_constant_loc(uniforms, &new_uniform_count,
|
|
|
|
|
src, size, channel_sizes[src],
|
|
|
|
|
new_loc, new_chan,
|
|
|
|
|
new_chans_used);
|
|
|
|
|
/* Move the references to the data */
|
|
|
|
|
for (int j = 0; j < size; j++) {
|
|
|
|
|
stage_prog_data->param[dst * 4 + new_chan[src] + j] =
|
|
|
|
|
param[src * 4 + j];
|
|
|
|
|
}
|
2011-08-23 12:13:14 -07:00
|
|
|
}
|
|
|
|
|
|
2017-04-19 11:18:35 +02:00
|
|
|
ralloc_free(param);
|
2011-08-23 12:13:14 -07:00
|
|
|
this->uniforms = new_uniform_count;
|
|
|
|
|
|
|
|
|
|
/* Now, update the instructions for our repacked uniforms. */
|
2014-09-01 13:42:51 -07:00
|
|
|
foreach_block_and_inst(block, vec4_instruction, inst, cfg) {
|
2011-08-23 12:13:14 -07:00
|
|
|
for (int i = 0 ; i < 3; i++) {
|
2015-10-26 04:35:14 -07:00
|
|
|
int src = inst->src[i].nr;
|
2011-08-23 12:13:14 -07:00
|
|
|
|
2016-05-30 09:08:04 +02:00
|
|
|
if (inst->src[i].file != UNIFORM)
|
|
|
|
|
continue;
|
2011-08-23 12:13:14 -07:00
|
|
|
|
2017-04-19 11:18:35 +02:00
|
|
|
int chan = new_chan[src] / channel_sizes[src];
|
2015-10-26 04:35:14 -07:00
|
|
|
inst->src[i].nr = new_loc[src];
|
2017-04-19 11:18:35 +02:00
|
|
|
inst->src[i].swizzle += BRW_SWIZZLE4(chan, chan, chan, chan);
|
2011-08-23 12:13:14 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-02 15:18:29 -07:00
|
|
|
/**
|
|
|
|
|
* Does algebraic optimizations (0 * a = 0, 1 * a = a, a + 0 = a).
|
|
|
|
|
*
|
|
|
|
|
* While GLSL IR also performs this optimization, we end up with it in
|
|
|
|
|
* our instruction stream for a couple of reasons. One is that we
|
|
|
|
|
* sometimes generate silly instructions, for example in array access
|
|
|
|
|
* where we'll generate "ADD offset, index, base" even if base is 0.
|
|
|
|
|
* The other is that GLSL IR's constant propagation doesn't track the
|
|
|
|
|
* components of aggregates, so some VS patterns (initialize matrix to
|
|
|
|
|
* 0, accumulate in vertex blending factors) end up breaking down to
|
|
|
|
|
* instructions involving 0.
|
|
|
|
|
*/
|
|
|
|
|
bool
|
|
|
|
|
vec4_visitor::opt_algebraic()
|
|
|
|
|
{
|
|
|
|
|
bool progress = false;
|
|
|
|
|
|
2014-08-24 19:27:09 -07:00
|
|
|
foreach_block_and_inst(block, vec4_instruction, inst, cfg) {
|
2011-09-02 15:18:29 -07:00
|
|
|
switch (inst->opcode) {
|
2014-12-21 06:56:54 -08:00
|
|
|
case BRW_OPCODE_MOV:
|
|
|
|
|
if (inst->src[0].file != IMM)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (inst->saturate) {
|
|
|
|
|
if (inst->dst.type != inst->src[0].type)
|
|
|
|
|
assert(!"unimplemented: saturate mixed types");
|
|
|
|
|
|
2015-11-19 21:51:37 -08:00
|
|
|
if (brw_saturate_immediate(inst->dst.type,
|
|
|
|
|
&inst->src[0].as_brw_reg())) {
|
2014-12-21 06:56:54 -08:00
|
|
|
inst->saturate = false;
|
|
|
|
|
progress = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2014-10-23 23:22:09 -07:00
|
|
|
case VEC4_OPCODE_UNPACK_UNIFORM:
|
|
|
|
|
if (inst->src[0].file != UNIFORM) {
|
|
|
|
|
inst->opcode = BRW_OPCODE_MOV;
|
|
|
|
|
progress = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2011-09-02 15:18:29 -07:00
|
|
|
case BRW_OPCODE_ADD:
|
|
|
|
|
if (inst->src[1].is_zero()) {
|
|
|
|
|
inst->opcode = BRW_OPCODE_MOV;
|
|
|
|
|
inst->src[1] = src_reg();
|
|
|
|
|
progress = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BRW_OPCODE_MUL:
|
|
|
|
|
if (inst->src[1].is_zero()) {
|
|
|
|
|
inst->opcode = BRW_OPCODE_MOV;
|
|
|
|
|
switch (inst->src[0].type) {
|
|
|
|
|
case BRW_REGISTER_TYPE_F:
|
2015-11-02 12:12:44 -08:00
|
|
|
inst->src[0] = brw_imm_f(0.0f);
|
2011-09-02 15:18:29 -07:00
|
|
|
break;
|
|
|
|
|
case BRW_REGISTER_TYPE_D:
|
2015-11-02 12:12:44 -08:00
|
|
|
inst->src[0] = brw_imm_d(0);
|
2011-09-02 15:18:29 -07:00
|
|
|
break;
|
|
|
|
|
case BRW_REGISTER_TYPE_UD:
|
2015-11-02 12:12:44 -08:00
|
|
|
inst->src[0] = brw_imm_ud(0u);
|
2011-09-02 15:18:29 -07:00
|
|
|
break;
|
|
|
|
|
default:
|
2014-06-29 14:54:01 -07:00
|
|
|
unreachable("not reached");
|
2011-09-02 15:18:29 -07:00
|
|
|
}
|
|
|
|
|
inst->src[1] = src_reg();
|
|
|
|
|
progress = true;
|
|
|
|
|
} else if (inst->src[1].is_one()) {
|
|
|
|
|
inst->opcode = BRW_OPCODE_MOV;
|
|
|
|
|
inst->src[1] = src_reg();
|
|
|
|
|
progress = true;
|
2015-02-04 18:08:30 -08:00
|
|
|
} else if (inst->src[1].is_negative_one()) {
|
|
|
|
|
inst->opcode = BRW_OPCODE_MOV;
|
|
|
|
|
inst->src[0].negate = !inst->src[0].negate;
|
|
|
|
|
inst->src[1] = src_reg();
|
|
|
|
|
progress = true;
|
2011-09-02 15:18:29 -07:00
|
|
|
}
|
|
|
|
|
break;
|
2015-01-05 13:51:03 -08:00
|
|
|
case BRW_OPCODE_CMP:
|
|
|
|
|
if (inst->conditional_mod == BRW_CONDITIONAL_GE &&
|
|
|
|
|
inst->src[0].abs &&
|
|
|
|
|
inst->src[0].negate &&
|
|
|
|
|
inst->src[1].is_zero()) {
|
|
|
|
|
inst->src[0].abs = false;
|
|
|
|
|
inst->src[0].negate = false;
|
|
|
|
|
inst->conditional_mod = BRW_CONDITIONAL_Z;
|
|
|
|
|
progress = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2015-02-19 14:52:24 +02:00
|
|
|
case SHADER_OPCODE_BROADCAST:
|
|
|
|
|
if (is_uniform(inst->src[0]) ||
|
|
|
|
|
inst->src[1].is_zero()) {
|
|
|
|
|
inst->opcode = BRW_OPCODE_MOV;
|
|
|
|
|
inst->src[1] = src_reg();
|
|
|
|
|
inst->force_writemask_all = true;
|
|
|
|
|
progress = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2011-09-02 15:18:29 -07:00
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (progress)
|
2014-09-01 10:54:00 -07:00
|
|
|
invalidate_live_intervals();
|
2011-09-02 15:18:29 -07:00
|
|
|
|
|
|
|
|
return progress;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-06 22:32:33 -07:00
|
|
|
/**
|
|
|
|
|
* Only a limited number of hardware registers may be used for push
|
|
|
|
|
* constants, so this turns access to the overflowed constants into
|
|
|
|
|
* pull constants.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
vec4_visitor::move_push_constants_to_pull_constants()
|
|
|
|
|
{
|
|
|
|
|
int pull_constant_loc[this->uniforms];
|
|
|
|
|
|
|
|
|
|
/* Only allow 32 registers (256 uniform components) as push constants,
|
|
|
|
|
* which is the limit on gen6.
|
2014-05-19 08:51:12 -07:00
|
|
|
*
|
|
|
|
|
* If changing this value, note the limitation about total_regs in
|
|
|
|
|
* brw_curbe.c.
|
2011-09-06 22:32:33 -07:00
|
|
|
*/
|
|
|
|
|
int max_uniform_components = 32 * 8;
|
|
|
|
|
if (this->uniforms * 4 <= max_uniform_components)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Make some sort of choice as to which uniforms get sent to pull
|
|
|
|
|
* constants. We could potentially do something clever here like
|
|
|
|
|
* look for the most infrequently used uniform vec4s, but leave
|
|
|
|
|
* that for later.
|
|
|
|
|
*/
|
|
|
|
|
for (int i = 0; i < this->uniforms * 4; i += 4) {
|
|
|
|
|
pull_constant_loc[i / 4] = -1;
|
|
|
|
|
|
|
|
|
|
if (i >= max_uniform_components) {
|
2016-07-13 10:21:18 +02:00
|
|
|
const gl_constant_value **values = &stage_prog_data->param[i];
|
2011-09-06 22:32:33 -07:00
|
|
|
|
2016-07-13 10:21:18 +02:00
|
|
|
/* Try to find an existing copy of this uniform in the pull
|
|
|
|
|
* constants if it was part of an array access already.
|
|
|
|
|
*/
|
|
|
|
|
for (unsigned int j = 0; j < stage_prog_data->nr_pull_params; j += 4) {
|
|
|
|
|
int matches;
|
2011-09-06 22:32:33 -07:00
|
|
|
|
2016-07-13 10:21:18 +02:00
|
|
|
for (matches = 0; matches < 4; matches++) {
|
|
|
|
|
if (stage_prog_data->pull_param[j + matches] != values[matches])
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-09-06 22:32:33 -07:00
|
|
|
|
2016-07-13 10:21:18 +02:00
|
|
|
if (matches == 4) {
|
|
|
|
|
pull_constant_loc[i / 4] = j / 4;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-06 22:32:33 -07:00
|
|
|
|
2016-07-13 10:21:18 +02:00
|
|
|
if (pull_constant_loc[i / 4] == -1) {
|
|
|
|
|
assert(stage_prog_data->nr_pull_params % 4 == 0);
|
|
|
|
|
pull_constant_loc[i / 4] = stage_prog_data->nr_pull_params / 4;
|
2011-09-06 22:32:33 -07:00
|
|
|
|
2016-07-13 10:21:18 +02:00
|
|
|
for (int j = 0; j < 4; j++) {
|
|
|
|
|
stage_prog_data->pull_param[stage_prog_data->nr_pull_params++] =
|
2014-02-19 15:14:02 +01:00
|
|
|
values[j];
|
2016-07-13 10:21:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
2011-09-06 22:32:33 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now actually rewrite usage of the things we've moved to pull
|
|
|
|
|
* constants.
|
|
|
|
|
*/
|
2014-08-24 19:38:21 -07:00
|
|
|
foreach_block_and_inst_safe(block, vec4_instruction, inst, cfg) {
|
2011-09-06 22:32:33 -07:00
|
|
|
for (int i = 0 ; i < 3; i++) {
|
2016-07-13 10:21:18 +02:00
|
|
|
if (inst->src[i].file != UNIFORM ||
|
2015-10-26 04:35:14 -07:00
|
|
|
pull_constant_loc[inst->src[i].nr] == -1)
|
2016-07-13 10:21:18 +02:00
|
|
|
continue;
|
2011-09-06 22:32:33 -07:00
|
|
|
|
2015-10-26 04:35:14 -07:00
|
|
|
int uniform = inst->src[i].nr;
|
2011-09-06 22:32:33 -07:00
|
|
|
|
2016-07-15 13:02:27 +02:00
|
|
|
const glsl_type *temp_type = type_sz(inst->src[i].type) == 8 ?
|
|
|
|
|
glsl_type::dvec4_type : glsl_type::vec4_type;
|
|
|
|
|
dst_reg temp = dst_reg(this, temp_type);
|
|
|
|
|
|
|
|
|
|
emit_pull_constant_load(block, inst, temp, inst->src[i],
|
|
|
|
|
pull_constant_loc[uniform], src_reg());
|
2011-09-06 22:32:33 -07:00
|
|
|
|
2016-07-13 10:21:18 +02:00
|
|
|
inst->src[i].file = temp.file;
|
2015-10-26 04:35:14 -07:00
|
|
|
inst->src[i].nr = temp.nr;
|
2016-07-13 10:21:18 +02:00
|
|
|
inst->src[i].offset %= 16;
|
|
|
|
|
inst->src[i].reladdr = NULL;
|
2011-09-06 22:32:33 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Repack push constants to remove the now-unused ones. */
|
|
|
|
|
pack_uniform_registers();
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-21 10:47:37 -08:00
|
|
|
/* Conditions for which we want to avoid setting the dependency control bits */
|
2014-11-21 10:47:41 -08:00
|
|
|
bool
|
|
|
|
|
vec4_visitor::is_dep_ctrl_unsafe(const vec4_instruction *inst)
|
2014-11-21 10:47:37 -08:00
|
|
|
{
|
2014-11-21 10:47:41 -08:00
|
|
|
#define IS_DWORD(reg) \
|
|
|
|
|
(reg.type == BRW_REGISTER_TYPE_UD || \
|
|
|
|
|
reg.type == BRW_REGISTER_TYPE_D)
|
|
|
|
|
|
2016-06-23 10:40:47 +02:00
|
|
|
#define IS_64BIT(reg) (reg.file != BAD_FILE && type_sz(reg.type) == 8)
|
|
|
|
|
|
2016-06-23 10:35:50 +02:00
|
|
|
/* From the Cherryview and Broadwell PRMs:
|
|
|
|
|
*
|
|
|
|
|
* "When source or destination datatype is 64b or operation is integer DWord
|
2014-12-02 17:30:49 -08:00
|
|
|
* multiply, DepCtrl must not be used."
|
2016-06-23 10:35:50 +02:00
|
|
|
*
|
2016-06-23 10:40:47 +02:00
|
|
|
* SKL PRMs don't include this restriction, however, gen7 seems to be
|
|
|
|
|
* affected, at least by the 64b restriction, since DepCtrl with double
|
|
|
|
|
* precision instructions seems to produce GPU hangs in some cases.
|
2014-11-21 10:47:41 -08:00
|
|
|
*/
|
2017-06-20 11:06:24 +01:00
|
|
|
if (devinfo->gen == 8 || gen_device_info_is_9lp(devinfo)) {
|
2014-11-21 10:47:41 -08:00
|
|
|
if (inst->opcode == BRW_OPCODE_MUL &&
|
|
|
|
|
IS_DWORD(inst->src[0]) &&
|
|
|
|
|
IS_DWORD(inst->src[1]))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2016-06-23 10:40:47 +02:00
|
|
|
|
|
|
|
|
if (devinfo->gen >= 7 && devinfo->gen <= 8) {
|
|
|
|
|
if (IS_64BIT(inst->dst) || IS_64BIT(inst->src[0]) ||
|
|
|
|
|
IS_64BIT(inst->src[1]) || IS_64BIT(inst->src[2]))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef IS_64BIT
|
2014-11-21 10:47:41 -08:00
|
|
|
#undef IS_DWORD
|
|
|
|
|
|
2015-04-15 18:00:05 -07:00
|
|
|
if (devinfo->gen >= 8) {
|
2015-02-04 18:37:46 +02:00
|
|
|
if (inst->opcode == BRW_OPCODE_F32TO16)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-21 10:47:37 -08:00
|
|
|
/*
|
|
|
|
|
* mlen:
|
|
|
|
|
* In the presence of send messages, totally interrupt dependency
|
|
|
|
|
* control. They're long enough that the chance of dependency
|
|
|
|
|
* control around them just doesn't matter.
|
|
|
|
|
*
|
|
|
|
|
* predicate:
|
|
|
|
|
* From the Ivy Bridge PRM, volume 4 part 3.7, page 80:
|
|
|
|
|
* When a sequence of NoDDChk and NoDDClr are used, the last instruction that
|
|
|
|
|
* completes the scoreboard clear must have a non-zero execution mask. This
|
|
|
|
|
* means, if any kind of predication can change the execution mask or channel
|
|
|
|
|
* enable of the last instruction, the optimization must be avoided. This is
|
|
|
|
|
* to avoid instructions being shot down the pipeline when no writes are
|
|
|
|
|
* required.
|
|
|
|
|
*
|
|
|
|
|
* math:
|
|
|
|
|
* Dependency control does not work well over math instructions.
|
|
|
|
|
* NB: Discovered empirically
|
|
|
|
|
*/
|
|
|
|
|
return (inst->mlen || inst->predicate || inst->is_math());
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-30 18:29:34 -08:00
|
|
|
/**
|
|
|
|
|
* Sets the dependency control fields on instructions after register
|
|
|
|
|
* allocation and before the generator is run.
|
|
|
|
|
*
|
|
|
|
|
* When you have a sequence of instructions like:
|
|
|
|
|
*
|
|
|
|
|
* DP4 temp.x vertex uniform[0]
|
|
|
|
|
* DP4 temp.y vertex uniform[0]
|
|
|
|
|
* DP4 temp.z vertex uniform[0]
|
|
|
|
|
* DP4 temp.w vertex uniform[0]
|
|
|
|
|
*
|
|
|
|
|
* The hardware doesn't know that it can actually run the later instructions
|
|
|
|
|
* while the previous ones are in flight, producing stalls. However, we have
|
|
|
|
|
* manual fields we can set in the instructions that let it do so.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
vec4_visitor::opt_set_dependency_control()
|
|
|
|
|
{
|
|
|
|
|
vec4_instruction *last_grf_write[BRW_MAX_GRF];
|
|
|
|
|
uint8_t grf_channels_written[BRW_MAX_GRF];
|
|
|
|
|
vec4_instruction *last_mrf_write[BRW_MAX_GRF];
|
|
|
|
|
uint8_t mrf_channels_written[BRW_MAX_GRF];
|
|
|
|
|
|
2013-02-17 09:09:45 -08:00
|
|
|
assert(prog_data->total_grf ||
|
2013-02-17 07:48:21 -08:00
|
|
|
!"Must be called after register allocation");
|
2012-11-30 18:29:34 -08:00
|
|
|
|
2014-07-11 22:31:39 -07:00
|
|
|
foreach_block (block, cfg) {
|
2012-11-30 18:29:34 -08:00
|
|
|
memset(last_grf_write, 0, sizeof(last_grf_write));
|
|
|
|
|
memset(last_mrf_write, 0, sizeof(last_mrf_write));
|
|
|
|
|
|
2014-07-11 22:31:39 -07:00
|
|
|
foreach_inst_in_block (vec4_instruction, inst, block) {
|
2012-11-30 18:29:34 -08:00
|
|
|
/* If we read from a register that we were doing dependency control
|
|
|
|
|
* on, don't do dependency control across the read.
|
|
|
|
|
*/
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
2016-09-01 13:10:36 -07:00
|
|
|
int reg = inst->src[i].nr + inst->src[i].offset / REG_SIZE;
|
2015-10-26 17:09:25 -07:00
|
|
|
if (inst->src[i].file == VGRF) {
|
2012-11-30 18:29:34 -08:00
|
|
|
last_grf_write[reg] = NULL;
|
2015-10-26 17:52:57 -07:00
|
|
|
} else if (inst->src[i].file == FIXED_GRF) {
|
2012-11-30 18:29:34 -08:00
|
|
|
memset(last_grf_write, 0, sizeof(last_grf_write));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
assert(inst->src[i].file != MRF);
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-21 10:47:37 -08:00
|
|
|
if (is_dep_ctrl_unsafe(inst)) {
|
2014-02-12 17:24:55 -07:00
|
|
|
memset(last_grf_write, 0, sizeof(last_grf_write));
|
|
|
|
|
memset(last_mrf_write, 0, sizeof(last_mrf_write));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-30 18:29:34 -08:00
|
|
|
/* Now, see if we can do dependency control for this instruction
|
|
|
|
|
* against a previous one writing to its destination.
|
|
|
|
|
*/
|
2016-09-01 13:10:36 -07:00
|
|
|
int reg = inst->dst.nr + inst->dst.offset / REG_SIZE;
|
2015-10-26 17:52:57 -07:00
|
|
|
if (inst->dst.file == VGRF || inst->dst.file == FIXED_GRF) {
|
2012-11-30 18:29:34 -08:00
|
|
|
if (last_grf_write[reg] &&
|
2016-09-01 22:02:00 -07:00
|
|
|
last_grf_write[reg]->dst.offset == inst->dst.offset &&
|
2012-11-30 18:29:34 -08:00
|
|
|
!(inst->dst.writemask & grf_channels_written[reg])) {
|
|
|
|
|
last_grf_write[reg]->no_dd_clear = true;
|
|
|
|
|
inst->no_dd_check = true;
|
|
|
|
|
} else {
|
|
|
|
|
grf_channels_written[reg] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_grf_write[reg] = inst;
|
|
|
|
|
grf_channels_written[reg] |= inst->dst.writemask;
|
|
|
|
|
} else if (inst->dst.file == MRF) {
|
|
|
|
|
if (last_mrf_write[reg] &&
|
2016-09-01 22:02:00 -07:00
|
|
|
last_mrf_write[reg]->dst.offset == inst->dst.offset &&
|
2012-11-30 18:29:34 -08:00
|
|
|
!(inst->dst.writemask & mrf_channels_written[reg])) {
|
|
|
|
|
last_mrf_write[reg]->no_dd_clear = true;
|
|
|
|
|
inst->no_dd_check = true;
|
|
|
|
|
} else {
|
|
|
|
|
mrf_channels_written[reg] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_mrf_write[reg] = inst;
|
|
|
|
|
mrf_channels_written[reg] |= inst->dst.writemask;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-30 22:29:26 -08:00
|
|
|
bool
|
2016-08-22 15:01:08 -07:00
|
|
|
vec4_instruction::can_reswizzle(const struct gen_device_info *devinfo,
|
2015-09-22 18:17:45 +02:00
|
|
|
int dst_writemask,
|
2014-08-31 11:10:02 -07:00
|
|
|
int swizzle,
|
|
|
|
|
int swizzle_mask)
|
2012-11-30 22:29:26 -08:00
|
|
|
{
|
2015-09-22 18:17:45 +02:00
|
|
|
/* Gen6 MATH instructions can not execute in align16 mode, so swizzles
|
2016-04-05 14:10:04 -07:00
|
|
|
* are not allowed.
|
2015-09-22 18:17:45 +02:00
|
|
|
*/
|
2016-04-05 14:10:04 -07:00
|
|
|
if (devinfo->gen == 6 && is_math() && swizzle != BRW_SWIZZLE_XYZW)
|
|
|
|
|
return false;
|
|
|
|
|
|
2017-04-22 15:02:08 -07:00
|
|
|
/* We can't swizzle implicit accumulator access. We'd have to
|
|
|
|
|
* reswizzle the producer of the accumulator value in addition
|
|
|
|
|
* to the consumer (i.e. both MUL and MACH). Just skip this.
|
i965/vec4: Avoid reswizzling MACH instructions in opt_register_coalesce().
opt_register_coalesce() was optimizing sequences such as:
mul(8) acc0:D, attr18.xyyy:D, attr19.xyyy:D
mach(8) vgrf5.xy:D, attr18.xyyy:D, attr19.xyyy:D
mov(8) m4.zw:F, vgrf5.xxxy:F
into:
mul(8) acc0:D, attr18.xyyy:D, attr19.xyyy:D
mach(8) m4.zw:D, attr18.xxxy:D, attr19.xxxy:D
This doesn't work - if we're going to reswizzle MACH, we'd need to
reswizzle the MUL as well. Here, the MUL fills the accumulator's .zw
components with attr18.yy * attr19.yy. But the MACH instruction expects
.z to contain attr18.x * attr19.x. Bogus results ensue.
No change in shader-db on Haswell. Prevents regressions in Timothy's
patches to use enhanced layouts for varying packing (which rearrange
code just enough to trigger this pre-existing bug, but were fine
themselves).
Acked-by: Timothy Arceri <tarceri@itsqueeze.com>
Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
2017-04-21 01:28:13 -07:00
|
|
|
*/
|
2017-04-22 15:02:08 -07:00
|
|
|
if (reads_accumulator_implicitly())
|
i965/vec4: Avoid reswizzling MACH instructions in opt_register_coalesce().
opt_register_coalesce() was optimizing sequences such as:
mul(8) acc0:D, attr18.xyyy:D, attr19.xyyy:D
mach(8) vgrf5.xy:D, attr18.xyyy:D, attr19.xyyy:D
mov(8) m4.zw:F, vgrf5.xxxy:F
into:
mul(8) acc0:D, attr18.xyyy:D, attr19.xyyy:D
mach(8) m4.zw:D, attr18.xxxy:D, attr19.xxxy:D
This doesn't work - if we're going to reswizzle MACH, we'd need to
reswizzle the MUL as well. Here, the MUL fills the accumulator's .zw
components with attr18.yy * attr19.yy. But the MACH instruction expects
.z to contain attr18.x * attr19.x. Bogus results ensue.
No change in shader-db on Haswell. Prevents regressions in Timothy's
patches to use enhanced layouts for varying packing (which rearrange
code just enough to trigger this pre-existing bug, but were fine
themselves).
Acked-by: Timothy Arceri <tarceri@itsqueeze.com>
Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
2017-04-21 01:28:13 -07:00
|
|
|
return false;
|
|
|
|
|
|
2016-04-05 14:10:04 -07:00
|
|
|
if (!can_do_writemask(devinfo) && dst_writemask != WRITEMASK_XYZW)
|
2015-09-22 18:17:45 +02:00
|
|
|
return false;
|
|
|
|
|
|
2012-11-30 22:29:26 -08:00
|
|
|
/* If this instruction sets anything not referenced by swizzle, then we'd
|
|
|
|
|
* totally break it when we reswizzle.
|
|
|
|
|
*/
|
|
|
|
|
if (dst.writemask & ~swizzle_mask)
|
|
|
|
|
return false;
|
|
|
|
|
|
2014-09-05 10:29:38 -07:00
|
|
|
if (mlen > 0)
|
2014-08-31 11:27:40 -07:00
|
|
|
return false;
|
2012-11-30 22:29:26 -08:00
|
|
|
|
2015-09-10 16:19:42 -07:00
|
|
|
for (int i = 0; i < 3; i++) {
|
2015-10-26 17:52:57 -07:00
|
|
|
if (src[i].is_accumulator())
|
2015-09-10 16:19:42 -07:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-31 11:27:40 -07:00
|
|
|
return true;
|
2012-11-30 22:29:26 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* For any channels in the swizzle's source that were populated by this
|
|
|
|
|
* instruction, rewrite the instruction to put the appropriate result directly
|
|
|
|
|
* in those channels.
|
|
|
|
|
*
|
|
|
|
|
* e.g. for swizzle=yywx, MUL a.xy b c -> MUL a.yy_x b.yy z.yy_x
|
|
|
|
|
*/
|
|
|
|
|
void
|
2014-08-31 11:10:02 -07:00
|
|
|
vec4_instruction::reswizzle(int dst_writemask, int swizzle)
|
2012-11-30 22:29:26 -08:00
|
|
|
{
|
2015-03-18 21:09:51 +02:00
|
|
|
/* Destination write mask doesn't correspond to source swizzle for the dot
|
|
|
|
|
* product and pack_bytes instructions.
|
|
|
|
|
*/
|
2014-08-31 11:27:40 -07:00
|
|
|
if (opcode != BRW_OPCODE_DP4 && opcode != BRW_OPCODE_DPH &&
|
2015-03-18 21:09:51 +02:00
|
|
|
opcode != BRW_OPCODE_DP3 && opcode != BRW_OPCODE_DP2 &&
|
|
|
|
|
opcode != VEC4_OPCODE_PACK_BYTES) {
|
2014-08-31 11:10:02 -07:00
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
|
if (src[i].file == BAD_FILE || src[i].file == IMM)
|
|
|
|
|
continue;
|
|
|
|
|
|
2015-03-18 21:09:51 +02:00
|
|
|
src[i].swizzle = brw_compose_swizzle(swizzle, src[i].swizzle);
|
2014-08-31 11:10:02 -07:00
|
|
|
}
|
2014-08-31 11:27:40 -07:00
|
|
|
}
|
2014-08-31 11:10:02 -07:00
|
|
|
|
2015-03-18 21:09:51 +02:00
|
|
|
/* Apply the specified swizzle and writemask to the original mask of
|
|
|
|
|
* written components.
|
|
|
|
|
*/
|
|
|
|
|
dst.writemask = dst_writemask &
|
|
|
|
|
brw_apply_swizzle_to_mask(swizzle, dst.writemask);
|
2012-11-30 22:29:26 -08:00
|
|
|
}
|
|
|
|
|
|
2011-09-06 13:21:24 -07:00
|
|
|
/*
|
2012-08-01 19:35:18 -07:00
|
|
|
* Tries to reduce extra MOV instructions by taking temporary GRFs that get
|
|
|
|
|
* just written and then MOVed into another reg and making the original write
|
|
|
|
|
* of the GRF write directly to the final destination instead.
|
2011-09-06 13:21:24 -07:00
|
|
|
*/
|
|
|
|
|
bool
|
2012-08-01 19:35:18 -07:00
|
|
|
vec4_visitor::opt_register_coalesce()
|
2011-09-06 13:21:24 -07:00
|
|
|
{
|
|
|
|
|
bool progress = false;
|
|
|
|
|
int next_ip = 0;
|
|
|
|
|
|
|
|
|
|
calculate_live_intervals();
|
|
|
|
|
|
2014-07-12 21:18:39 -07:00
|
|
|
foreach_block_and_inst_safe (block, vec4_instruction, inst, cfg) {
|
2011-09-06 13:21:24 -07:00
|
|
|
int ip = next_ip;
|
|
|
|
|
next_ip++;
|
|
|
|
|
|
|
|
|
|
if (inst->opcode != BRW_OPCODE_MOV ||
|
2015-10-26 17:09:25 -07:00
|
|
|
(inst->dst.file != VGRF && inst->dst.file != MRF) ||
|
2011-09-06 13:21:24 -07:00
|
|
|
inst->predicate ||
|
2015-10-26 17:09:25 -07:00
|
|
|
inst->src[0].file != VGRF ||
|
2011-09-06 13:21:24 -07:00
|
|
|
inst->dst.type != inst->src[0].type ||
|
|
|
|
|
inst->src[0].abs || inst->src[0].negate || inst->src[0].reladdr)
|
|
|
|
|
continue;
|
|
|
|
|
|
2015-09-21 13:58:19 -07:00
|
|
|
/* Remove no-op MOVs */
|
|
|
|
|
if (inst->dst.file == inst->src[0].file &&
|
2015-10-26 04:35:14 -07:00
|
|
|
inst->dst.nr == inst->src[0].nr &&
|
2016-09-01 22:04:02 -07:00
|
|
|
inst->dst.offset == inst->src[0].offset) {
|
2015-09-21 13:58:19 -07:00
|
|
|
bool is_nop_mov = true;
|
|
|
|
|
|
|
|
|
|
for (unsigned c = 0; c < 4; c++) {
|
|
|
|
|
if ((inst->dst.writemask & (1 << c)) == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (BRW_GET_SWZ(inst->src[0].swizzle, c) != c) {
|
|
|
|
|
is_nop_mov = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_nop_mov) {
|
|
|
|
|
inst->remove(block);
|
2016-03-13 19:15:45 -07:00
|
|
|
progress = true;
|
2015-09-21 13:58:19 -07:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-01 19:35:18 -07:00
|
|
|
bool to_mrf = (inst->dst.file == MRF);
|
2011-09-06 13:21:24 -07:00
|
|
|
|
2012-08-01 19:35:18 -07:00
|
|
|
/* Can't coalesce this GRF if someone else was going to
|
2011-09-06 13:21:24 -07:00
|
|
|
* read it later.
|
|
|
|
|
*/
|
2016-06-10 13:55:00 +02:00
|
|
|
if (var_range_end(var_from_reg(alloc, dst_reg(inst->src[0])), 8) > ip)
|
2011-09-06 13:21:24 -07:00
|
|
|
continue;
|
|
|
|
|
|
2012-08-01 19:35:18 -07:00
|
|
|
/* We need to check interference with the final destination between this
|
|
|
|
|
* instruction and the earliest instruction involved in writing the GRF
|
|
|
|
|
* we're eliminating. To do that, keep track of which of our source
|
|
|
|
|
* channels we've seen initialized.
|
2011-09-06 13:21:24 -07:00
|
|
|
*/
|
2015-03-18 21:11:20 +02:00
|
|
|
const unsigned chans_needed =
|
|
|
|
|
brw_apply_inv_swizzle_to_mask(inst->src[0].swizzle,
|
|
|
|
|
inst->dst.writemask);
|
|
|
|
|
unsigned chans_remaining = chans_needed;
|
2011-09-06 13:21:24 -07:00
|
|
|
|
2012-08-01 19:35:18 -07:00
|
|
|
/* Now walk up the instruction stream trying to see if we can rewrite
|
|
|
|
|
* everything writing to the temporary to write into the destination
|
|
|
|
|
* instead.
|
2011-09-06 13:21:24 -07:00
|
|
|
*/
|
2014-09-02 14:43:43 -07:00
|
|
|
vec4_instruction *_scan_inst = (vec4_instruction *)inst->prev;
|
|
|
|
|
foreach_inst_in_block_reverse_starting_from(vec4_instruction, scan_inst,
|
2015-10-20 11:16:00 +02:00
|
|
|
inst) {
|
2014-09-02 14:43:43 -07:00
|
|
|
_scan_inst = scan_inst;
|
|
|
|
|
|
2016-09-01 19:42:40 -07:00
|
|
|
if (regions_overlap(inst->src[0], inst->size_read(0),
|
|
|
|
|
scan_inst->dst, scan_inst->size_written)) {
|
2012-08-01 19:35:18 -07:00
|
|
|
/* Found something writing to the reg we want to coalesce away. */
|
|
|
|
|
if (to_mrf) {
|
|
|
|
|
/* SEND instructions can't have MRF as a destination. */
|
|
|
|
|
if (scan_inst->mlen)
|
|
|
|
|
break;
|
|
|
|
|
|
2015-04-15 18:00:05 -07:00
|
|
|
if (devinfo->gen == 6) {
|
2012-08-01 19:35:18 -07:00
|
|
|
/* gen6 math instructions must have the destination be
|
2015-10-26 17:09:25 -07:00
|
|
|
* VGRF, so no compute-to-MRF for them.
|
2012-08-01 19:35:18 -07:00
|
|
|
*/
|
|
|
|
|
if (scan_inst->is_math()) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-06 13:21:24 -07:00
|
|
|
|
2015-08-05 15:57:33 +02:00
|
|
|
/* This doesn't handle saturation on the instruction we
|
|
|
|
|
* want to coalesce away if the register types do not match.
|
|
|
|
|
* But if scan_inst is a non type-converting 'mov', we can fix
|
|
|
|
|
* the types later.
|
|
|
|
|
*/
|
|
|
|
|
if (inst->saturate &&
|
|
|
|
|
inst->dst.type != scan_inst->dst.type &&
|
|
|
|
|
!(scan_inst->opcode == BRW_OPCODE_MOV &&
|
|
|
|
|
scan_inst->dst.type == scan_inst->src[0].type))
|
|
|
|
|
break;
|
|
|
|
|
|
i965/vec4: teach register coalescing about 64-bit
Specifically, at least for now, we don't want to deal with the fact that
channel sizes for fp64 instructions are twice the size, so prevent
coalescing from instructions with a different type size.
Also, we should check that if we are coalescing a register from another
MOV we should be writing the same amount of data in both operations, otherwise
we end up wiring more or less than the original instruction. This can happen,
for example, when we have split fp64 MOVs with an exec size of 4 that only
write one register each and then a MOV with exec size of 8 that reads both.
We want to avoid the pass to think that it can coalesce from the first split
MOV alone. Ideally we would like the pass to see that it can coalesce from both
split MOVs instead, but for now we keep it simple.
Finally, the pass doesn't support coalescing of multiple registers but in the
case of normal SIMD4x2 double-precision instructions they naturally write two
registers (one per vertex) and there is no reason why we should not allow
coalescing in this case. Change the restriction to bail if we see instructions
that write more than 8 channels, where the channels can be 32-bit or 64-bit.
v2:
- Make sure that scan_inst and inst write the same amount of data.
Reviewed-by: Matt Turner <mattst88@gmail.com>
2016-07-18 12:04:13 +02:00
|
|
|
/* Only allow coalescing between registers of the same type size.
|
|
|
|
|
* Otherwise we would need to make the pass aware of the fact that
|
|
|
|
|
* channel sizes are different for single and double precision.
|
|
|
|
|
*/
|
|
|
|
|
if (type_sz(inst->src[0].type) != type_sz(scan_inst->src[0].type))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Check that scan_inst writes the same amount of data as the
|
|
|
|
|
* instruction, otherwise coalescing would lead to writing a
|
|
|
|
|
* different (larger or smaller) region of the destination
|
|
|
|
|
*/
|
|
|
|
|
if (scan_inst->size_written != inst->size_written)
|
|
|
|
|
break;
|
|
|
|
|
|
2012-11-30 22:29:26 -08:00
|
|
|
/* If we can't handle the swizzle, bail. */
|
2015-09-22 18:17:45 +02:00
|
|
|
if (!scan_inst->can_reswizzle(devinfo, inst->dst.writemask,
|
2014-08-31 11:10:02 -07:00
|
|
|
inst->src[0].swizzle,
|
2015-03-18 21:11:20 +02:00
|
|
|
chans_needed)) {
|
2012-11-30 22:29:26 -08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
i965/vec4: teach register coalescing about 64-bit
Specifically, at least for now, we don't want to deal with the fact that
channel sizes for fp64 instructions are twice the size, so prevent
coalescing from instructions with a different type size.
Also, we should check that if we are coalescing a register from another
MOV we should be writing the same amount of data in both operations, otherwise
we end up wiring more or less than the original instruction. This can happen,
for example, when we have split fp64 MOVs with an exec size of 4 that only
write one register each and then a MOV with exec size of 8 that reads both.
We want to avoid the pass to think that it can coalesce from the first split
MOV alone. Ideally we would like the pass to see that it can coalesce from both
split MOVs instead, but for now we keep it simple.
Finally, the pass doesn't support coalescing of multiple registers but in the
case of normal SIMD4x2 double-precision instructions they naturally write two
registers (one per vertex) and there is no reason why we should not allow
coalescing in this case. Change the restriction to bail if we see instructions
that write more than 8 channels, where the channels can be 32-bit or 64-bit.
v2:
- Make sure that scan_inst and inst write the same amount of data.
Reviewed-by: Matt Turner <mattst88@gmail.com>
2016-07-18 12:04:13 +02:00
|
|
|
/* This only handles coalescing writes of 8 channels (1 register
|
|
|
|
|
* for single-precision and 2 registers for double-precision)
|
|
|
|
|
* starting at the source offset of the copy instruction.
|
2016-09-01 22:08:29 -07:00
|
|
|
*/
|
i965/vec4: teach register coalescing about 64-bit
Specifically, at least for now, we don't want to deal with the fact that
channel sizes for fp64 instructions are twice the size, so prevent
coalescing from instructions with a different type size.
Also, we should check that if we are coalescing a register from another
MOV we should be writing the same amount of data in both operations, otherwise
we end up wiring more or less than the original instruction. This can happen,
for example, when we have split fp64 MOVs with an exec size of 4 that only
write one register each and then a MOV with exec size of 8 that reads both.
We want to avoid the pass to think that it can coalesce from the first split
MOV alone. Ideally we would like the pass to see that it can coalesce from both
split MOVs instead, but for now we keep it simple.
Finally, the pass doesn't support coalescing of multiple registers but in the
case of normal SIMD4x2 double-precision instructions they naturally write two
registers (one per vertex) and there is no reason why we should not allow
coalescing in this case. Change the restriction to bail if we see instructions
that write more than 8 channels, where the channels can be 32-bit or 64-bit.
v2:
- Make sure that scan_inst and inst write the same amount of data.
Reviewed-by: Matt Turner <mattst88@gmail.com>
2016-07-18 12:04:13 +02:00
|
|
|
if (DIV_ROUND_UP(scan_inst->size_written,
|
|
|
|
|
type_sz(scan_inst->dst.type)) > 8 ||
|
2016-09-01 22:08:29 -07:00
|
|
|
scan_inst->dst.offset != inst->src[0].offset)
|
2015-03-18 19:43:44 +02:00
|
|
|
break;
|
|
|
|
|
|
2011-09-06 13:21:24 -07:00
|
|
|
/* Mark which channels we found unconditional writes for. */
|
2015-03-18 21:11:20 +02:00
|
|
|
if (!scan_inst->predicate)
|
|
|
|
|
chans_remaining &= ~scan_inst->dst.writemask;
|
2011-09-06 13:21:24 -07:00
|
|
|
|
|
|
|
|
if (chans_remaining == 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-01 19:35:18 -07:00
|
|
|
/* You can't read from an MRF, so if someone else reads our MRF's
|
|
|
|
|
* source GRF that we wanted to rewrite, that stops us. If it's a
|
|
|
|
|
* GRF we're trying to coalesce to, we don't actually handle
|
|
|
|
|
* rewriting sources so bail in that case as well.
|
|
|
|
|
*/
|
2011-09-06 13:21:24 -07:00
|
|
|
bool interfered = false;
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
2016-09-01 19:42:40 -07:00
|
|
|
if (regions_overlap(inst->src[0], inst->size_read(0),
|
|
|
|
|
scan_inst->src[i], scan_inst->size_read(i)))
|
2011-09-06 13:21:24 -07:00
|
|
|
interfered = true;
|
|
|
|
|
}
|
|
|
|
|
if (interfered)
|
|
|
|
|
break;
|
|
|
|
|
|
2015-09-11 12:21:13 +02:00
|
|
|
/* If somebody else writes the same channels of our destination here,
|
|
|
|
|
* we can't coalesce before that.
|
2012-08-01 19:35:18 -07:00
|
|
|
*/
|
2016-09-01 19:42:40 -07:00
|
|
|
if (regions_overlap(inst->dst, inst->size_written,
|
|
|
|
|
scan_inst->dst, scan_inst->size_written) &&
|
2015-09-11 12:21:13 +02:00
|
|
|
(inst->dst.writemask & scan_inst->dst.writemask) != 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-09-06 13:21:24 -07:00
|
|
|
|
2012-08-01 19:35:18 -07:00
|
|
|
/* Check for reads of the register we're trying to coalesce into. We
|
|
|
|
|
* can't go rewriting instructions above that to put some other value
|
|
|
|
|
* in the register instead.
|
|
|
|
|
*/
|
|
|
|
|
if (to_mrf && scan_inst->mlen > 0) {
|
2015-10-26 04:35:14 -07:00
|
|
|
if (inst->dst.nr >= scan_inst->base_mrf &&
|
|
|
|
|
inst->dst.nr < scan_inst->base_mrf + scan_inst->mlen) {
|
2012-08-01 19:35:18 -07:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
2016-09-01 19:42:40 -07:00
|
|
|
if (regions_overlap(inst->dst, inst->size_written,
|
|
|
|
|
scan_inst->src[i], scan_inst->size_read(i)))
|
2012-08-01 19:35:18 -07:00
|
|
|
interfered = true;
|
|
|
|
|
}
|
|
|
|
|
if (interfered)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-09-06 13:21:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (chans_remaining == 0) {
|
2012-08-01 19:35:18 -07:00
|
|
|
/* If we've made it here, we have an MOV we want to coalesce out, and
|
|
|
|
|
* a scan_inst pointing to the earliest instruction involved in
|
|
|
|
|
* computing the value. Now go rewrite the instruction stream
|
|
|
|
|
* between the two.
|
2011-09-06 13:21:24 -07:00
|
|
|
*/
|
2014-09-02 14:43:43 -07:00
|
|
|
vec4_instruction *scan_inst = _scan_inst;
|
2011-09-06 13:21:24 -07:00
|
|
|
while (scan_inst != inst) {
|
2015-10-26 17:09:25 -07:00
|
|
|
if (scan_inst->dst.file == VGRF &&
|
2015-10-26 04:35:14 -07:00
|
|
|
scan_inst->dst.nr == inst->src[0].nr &&
|
2016-09-01 22:08:29 -07:00
|
|
|
scan_inst->dst.offset == inst->src[0].offset) {
|
2014-08-31 11:10:02 -07:00
|
|
|
scan_inst->reswizzle(inst->dst.writemask,
|
|
|
|
|
inst->src[0].swizzle);
|
2012-08-01 19:35:18 -07:00
|
|
|
scan_inst->dst.file = inst->dst.file;
|
2015-10-26 04:35:14 -07:00
|
|
|
scan_inst->dst.nr = inst->dst.nr;
|
2016-09-01 22:12:04 -07:00
|
|
|
scan_inst->dst.offset = inst->dst.offset;
|
2015-08-05 15:57:33 +02:00
|
|
|
if (inst->saturate &&
|
|
|
|
|
inst->dst.type != scan_inst->dst.type) {
|
|
|
|
|
/* If we have reached this point, scan_inst is a non
|
|
|
|
|
* type-converting 'mov' and we can modify its register types
|
|
|
|
|
* to match the ones in inst. Otherwise, we could have an
|
|
|
|
|
* incorrect saturation result.
|
|
|
|
|
*/
|
|
|
|
|
scan_inst->dst.type = inst->dst.type;
|
|
|
|
|
scan_inst->src[0].type = inst->src[0].type;
|
|
|
|
|
}
|
2011-09-06 13:21:24 -07:00
|
|
|
scan_inst->saturate |= inst->saturate;
|
|
|
|
|
}
|
|
|
|
|
scan_inst = (vec4_instruction *)scan_inst->next;
|
|
|
|
|
}
|
2014-07-12 21:18:39 -07:00
|
|
|
inst->remove(block);
|
2011-09-06 13:21:24 -07:00
|
|
|
progress = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (progress)
|
2014-09-01 10:54:00 -07:00
|
|
|
invalidate_live_intervals();
|
2011-09-06 13:21:24 -07:00
|
|
|
|
|
|
|
|
return progress;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-20 20:25:04 +02:00
|
|
|
/**
|
|
|
|
|
* Eliminate FIND_LIVE_CHANNEL instructions occurring outside any control
|
|
|
|
|
* flow. We could probably do better here with some form of divergence
|
|
|
|
|
* analysis.
|
|
|
|
|
*/
|
|
|
|
|
bool
|
|
|
|
|
vec4_visitor::eliminate_find_live_channel()
|
|
|
|
|
{
|
|
|
|
|
bool progress = false;
|
|
|
|
|
unsigned depth = 0;
|
|
|
|
|
|
2016-09-15 17:20:23 -07:00
|
|
|
if (!brw_stage_has_packed_dispatch(devinfo, stage, stage_prog_data)) {
|
|
|
|
|
/* The optimization below assumes that channel zero is live on thread
|
|
|
|
|
* dispatch, which may not be the case if the fixed function dispatches
|
|
|
|
|
* threads sparsely.
|
|
|
|
|
*/
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-20 20:25:04 +02:00
|
|
|
foreach_block_and_inst_safe(block, vec4_instruction, inst, cfg) {
|
|
|
|
|
switch (inst->opcode) {
|
|
|
|
|
case BRW_OPCODE_IF:
|
|
|
|
|
case BRW_OPCODE_DO:
|
|
|
|
|
depth++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BRW_OPCODE_ENDIF:
|
|
|
|
|
case BRW_OPCODE_WHILE:
|
|
|
|
|
depth--;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SHADER_OPCODE_FIND_LIVE_CHANNEL:
|
|
|
|
|
if (depth == 0) {
|
|
|
|
|
inst->opcode = BRW_OPCODE_MOV;
|
2015-11-02 12:12:44 -08:00
|
|
|
inst->src[0] = brw_imm_d(0);
|
2015-02-20 20:25:04 +02:00
|
|
|
inst->force_writemask_all = true;
|
|
|
|
|
progress = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return progress;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-03 10:04:22 -07:00
|
|
|
/**
|
|
|
|
|
* Splits virtual GRFs requesting more than one contiguous physical register.
|
|
|
|
|
*
|
|
|
|
|
* We initially create large virtual GRFs for temporary structures, arrays,
|
2016-09-02 13:53:13 -07:00
|
|
|
* and matrices, so that the visitor functions can add offsets to work their
|
|
|
|
|
* way down to the actual member being accessed. But when it comes to
|
|
|
|
|
* optimization, we'd like to treat each register as individual storage if
|
|
|
|
|
* possible.
|
2012-10-03 10:04:22 -07:00
|
|
|
*
|
2012-12-17 16:48:20 -08:00
|
|
|
* So far, the only thing that might prevent splitting is a send message from
|
|
|
|
|
* a GRF on IVB.
|
2012-10-03 10:04:22 -07:00
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
vec4_visitor::split_virtual_grfs()
|
|
|
|
|
{
|
2015-02-10 15:51:34 +02:00
|
|
|
int num_vars = this->alloc.count;
|
2012-10-03 10:04:22 -07:00
|
|
|
int new_virtual_grf[num_vars];
|
2012-12-17 16:48:20 -08:00
|
|
|
bool split_grf[num_vars];
|
2012-10-03 10:04:22 -07:00
|
|
|
|
|
|
|
|
memset(new_virtual_grf, 0, sizeof(new_virtual_grf));
|
|
|
|
|
|
2012-12-17 16:48:20 -08:00
|
|
|
/* Try to split anything > 0 sized. */
|
|
|
|
|
for (int i = 0; i < num_vars; i++) {
|
2015-02-10 15:51:34 +02:00
|
|
|
split_grf[i] = this->alloc.sizes[i] != 1;
|
2012-12-17 16:48:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check that the instructions are compatible with the registers we're trying
|
|
|
|
|
* to split.
|
|
|
|
|
*/
|
2014-09-01 13:42:51 -07:00
|
|
|
foreach_block_and_inst(block, vec4_instruction, inst, cfg) {
|
2016-09-01 16:55:46 -07:00
|
|
|
if (inst->dst.file == VGRF && regs_written(inst) > 1)
|
2015-10-26 04:35:14 -07:00
|
|
|
split_grf[inst->dst.nr] = false;
|
2015-03-18 19:45:40 +02:00
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
2016-09-01 16:55:46 -07:00
|
|
|
if (inst->src[i].file == VGRF && regs_read(inst, i) > 1)
|
2015-10-26 04:35:14 -07:00
|
|
|
split_grf[inst->src[i].nr] = false;
|
2012-12-17 16:48:20 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-03 10:04:22 -07:00
|
|
|
/* Allocate new space for split regs. Note that the virtual
|
|
|
|
|
* numbers will be contiguous.
|
|
|
|
|
*/
|
|
|
|
|
for (int i = 0; i < num_vars; i++) {
|
2012-12-17 16:48:20 -08:00
|
|
|
if (!split_grf[i])
|
2012-10-03 10:04:22 -07:00
|
|
|
continue;
|
|
|
|
|
|
2015-02-10 15:51:34 +02:00
|
|
|
new_virtual_grf[i] = alloc.allocate(1);
|
|
|
|
|
for (unsigned j = 2; j < this->alloc.sizes[i]; j++) {
|
|
|
|
|
unsigned reg = alloc.allocate(1);
|
2012-10-03 10:04:22 -07:00
|
|
|
assert(reg == new_virtual_grf[i] + j - 1);
|
|
|
|
|
(void) reg;
|
|
|
|
|
}
|
2015-02-10 15:51:34 +02:00
|
|
|
this->alloc.sizes[i] = 1;
|
2012-10-03 10:04:22 -07:00
|
|
|
}
|
|
|
|
|
|
2014-09-01 13:42:51 -07:00
|
|
|
foreach_block_and_inst(block, vec4_instruction, inst, cfg) {
|
2015-10-26 17:09:25 -07:00
|
|
|
if (inst->dst.file == VGRF && split_grf[inst->dst.nr] &&
|
2016-09-01 13:10:36 -07:00
|
|
|
inst->dst.offset / REG_SIZE != 0) {
|
2015-10-26 04:35:14 -07:00
|
|
|
inst->dst.nr = (new_virtual_grf[inst->dst.nr] +
|
2016-09-01 13:10:36 -07:00
|
|
|
inst->dst.offset / REG_SIZE - 1);
|
|
|
|
|
inst->dst.offset %= REG_SIZE;
|
2012-10-03 10:04:22 -07:00
|
|
|
}
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
2015-10-26 17:09:25 -07:00
|
|
|
if (inst->src[i].file == VGRF && split_grf[inst->src[i].nr] &&
|
2016-09-01 13:10:36 -07:00
|
|
|
inst->src[i].offset / REG_SIZE != 0) {
|
2015-10-26 04:35:14 -07:00
|
|
|
inst->src[i].nr = (new_virtual_grf[inst->src[i].nr] +
|
2016-09-01 13:10:36 -07:00
|
|
|
inst->src[i].offset / REG_SIZE - 1);
|
|
|
|
|
inst->src[i].offset %= REG_SIZE;
|
2012-12-17 16:48:20 -08:00
|
|
|
}
|
2012-10-03 10:04:22 -07:00
|
|
|
}
|
|
|
|
|
}
|
2014-09-01 10:54:00 -07:00
|
|
|
invalidate_live_intervals();
|
2012-10-03 10:04:22 -07:00
|
|
|
}
|
|
|
|
|
|
2012-10-03 16:11:26 -07:00
|
|
|
void
|
2013-04-29 14:21:14 -07:00
|
|
|
vec4_visitor::dump_instruction(backend_instruction *be_inst)
|
2014-05-29 11:45:15 -07:00
|
|
|
{
|
|
|
|
|
dump_instruction(be_inst, stderr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
vec4_visitor::dump_instruction(backend_instruction *be_inst, FILE *file)
|
2012-10-03 16:11:26 -07:00
|
|
|
{
|
2013-04-29 14:21:14 -07:00
|
|
|
vec4_instruction *inst = (vec4_instruction *)be_inst;
|
|
|
|
|
|
2014-03-13 11:22:08 -07:00
|
|
|
if (inst->predicate) {
|
2015-10-09 18:39:42 +02:00
|
|
|
fprintf(file, "(%cf0.%d%s) ",
|
2015-02-06 01:28:12 +02:00
|
|
|
inst->predicate_inverse ? '-' : '+',
|
2015-10-09 18:39:42 +02:00
|
|
|
inst->flag_subreg,
|
|
|
|
|
pred_ctrl_align16[inst->predicate]);
|
2014-03-13 11:22:08 -07:00
|
|
|
}
|
|
|
|
|
|
2016-05-30 13:36:30 +02:00
|
|
|
fprintf(file, "%s(%d)", brw_instruction_name(devinfo, inst->opcode),
|
|
|
|
|
inst->exec_size);
|
2015-03-05 11:16:07 -08:00
|
|
|
if (inst->saturate)
|
|
|
|
|
fprintf(file, ".sat");
|
2013-10-20 15:48:14 -07:00
|
|
|
if (inst->conditional_mod) {
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, "%s", conditional_modifier[inst->conditional_mod]);
|
2015-02-06 01:28:12 +02:00
|
|
|
if (!inst->predicate &&
|
2015-04-15 18:00:05 -07:00
|
|
|
(devinfo->gen < 5 || (inst->opcode != BRW_OPCODE_SEL &&
|
|
|
|
|
inst->opcode != BRW_OPCODE_IF &&
|
|
|
|
|
inst->opcode != BRW_OPCODE_WHILE))) {
|
2015-02-06 01:28:12 +02:00
|
|
|
fprintf(file, ".f0.%d", inst->flag_subreg);
|
|
|
|
|
}
|
2013-10-20 15:48:14 -07:00
|
|
|
}
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, " ");
|
2012-10-03 16:11:26 -07:00
|
|
|
|
|
|
|
|
switch (inst->dst.file) {
|
2015-10-26 17:09:25 -07:00
|
|
|
case VGRF:
|
2016-09-02 13:52:54 -07:00
|
|
|
fprintf(file, "vgrf%d", inst->dst.nr);
|
2012-10-03 16:11:26 -07:00
|
|
|
break;
|
2015-10-26 17:52:57 -07:00
|
|
|
case FIXED_GRF:
|
|
|
|
|
fprintf(file, "g%d", inst->dst.nr);
|
|
|
|
|
break;
|
2012-10-03 16:11:26 -07:00
|
|
|
case MRF:
|
2015-10-26 04:35:14 -07:00
|
|
|
fprintf(file, "m%d", inst->dst.nr);
|
2012-10-03 16:11:26 -07:00
|
|
|
break;
|
2015-10-26 17:52:57 -07:00
|
|
|
case ARF:
|
|
|
|
|
switch (inst->dst.nr) {
|
|
|
|
|
case BRW_ARF_NULL:
|
|
|
|
|
fprintf(file, "null");
|
|
|
|
|
break;
|
|
|
|
|
case BRW_ARF_ADDRESS:
|
|
|
|
|
fprintf(file, "a0.%d", inst->dst.subnr);
|
|
|
|
|
break;
|
|
|
|
|
case BRW_ARF_ACCUMULATOR:
|
|
|
|
|
fprintf(file, "acc%d", inst->dst.subnr);
|
|
|
|
|
break;
|
|
|
|
|
case BRW_ARF_FLAG:
|
|
|
|
|
fprintf(file, "f%d.%d", inst->dst.nr & 0xf, inst->dst.subnr);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fprintf(file, "arf%d.%d", inst->dst.nr & 0xf, inst->dst.subnr);
|
|
|
|
|
break;
|
2013-11-25 22:17:29 -08:00
|
|
|
}
|
|
|
|
|
break;
|
2012-10-03 16:11:26 -07:00
|
|
|
case BAD_FILE:
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, "(null)");
|
2012-10-03 16:11:26 -07:00
|
|
|
break;
|
2015-10-26 06:58:56 -07:00
|
|
|
case IMM:
|
|
|
|
|
case ATTR:
|
|
|
|
|
case UNIFORM:
|
|
|
|
|
unreachable("not reached");
|
2012-10-03 16:11:26 -07:00
|
|
|
}
|
2016-09-02 13:52:54 -07:00
|
|
|
if (inst->dst.offset ||
|
|
|
|
|
(inst->dst.file == VGRF &&
|
|
|
|
|
alloc.sizes[inst->dst.nr] * REG_SIZE != inst->size_written)) {
|
|
|
|
|
const unsigned reg_size = (inst->dst.file == UNIFORM ? 16 : REG_SIZE);
|
|
|
|
|
fprintf(file, "+%d.%d", inst->dst.offset / reg_size,
|
|
|
|
|
inst->dst.offset % reg_size);
|
|
|
|
|
}
|
2012-10-03 16:11:26 -07:00
|
|
|
if (inst->dst.writemask != WRITEMASK_XYZW) {
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, ".");
|
2012-10-03 16:11:26 -07:00
|
|
|
if (inst->dst.writemask & 1)
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, "x");
|
2012-10-03 16:11:26 -07:00
|
|
|
if (inst->dst.writemask & 2)
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, "y");
|
2012-10-03 16:11:26 -07:00
|
|
|
if (inst->dst.writemask & 4)
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, "z");
|
2012-10-03 16:11:26 -07:00
|
|
|
if (inst->dst.writemask & 8)
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, "w");
|
2012-10-03 16:11:26 -07:00
|
|
|
}
|
2014-09-09 19:55:28 +12:00
|
|
|
fprintf(file, ":%s", brw_reg_type_letters(inst->dst.type));
|
|
|
|
|
|
|
|
|
|
if (inst->src[0].file != BAD_FILE)
|
|
|
|
|
fprintf(file, ", ");
|
2012-10-03 16:11:26 -07:00
|
|
|
|
2013-11-25 15:15:25 -08:00
|
|
|
for (int i = 0; i < 3 && inst->src[i].file != BAD_FILE; i++) {
|
2013-12-02 12:41:16 -08:00
|
|
|
if (inst->src[i].negate)
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, "-");
|
2013-12-02 12:41:16 -08:00
|
|
|
if (inst->src[i].abs)
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, "|");
|
2012-10-03 16:11:26 -07:00
|
|
|
switch (inst->src[i].file) {
|
2015-10-26 17:09:25 -07:00
|
|
|
case VGRF:
|
2015-10-26 04:35:14 -07:00
|
|
|
fprintf(file, "vgrf%d", inst->src[i].nr);
|
2012-10-03 16:11:26 -07:00
|
|
|
break;
|
2015-10-26 17:52:57 -07:00
|
|
|
case FIXED_GRF:
|
2016-07-01 09:28:31 +02:00
|
|
|
fprintf(file, "g%d.%d", inst->src[i].nr, inst->src[i].subnr);
|
2015-10-26 17:52:57 -07:00
|
|
|
break;
|
2012-10-03 16:11:26 -07:00
|
|
|
case ATTR:
|
2015-10-26 04:35:14 -07:00
|
|
|
fprintf(file, "attr%d", inst->src[i].nr);
|
2012-10-03 16:11:26 -07:00
|
|
|
break;
|
|
|
|
|
case UNIFORM:
|
2015-10-26 04:35:14 -07:00
|
|
|
fprintf(file, "u%d", inst->src[i].nr);
|
2012-10-03 16:11:26 -07:00
|
|
|
break;
|
2013-03-11 11:10:34 -07:00
|
|
|
case IMM:
|
|
|
|
|
switch (inst->src[i].type) {
|
|
|
|
|
case BRW_REGISTER_TYPE_F:
|
2015-10-24 14:55:57 -07:00
|
|
|
fprintf(file, "%fF", inst->src[i].f);
|
2013-03-11 11:10:34 -07:00
|
|
|
break;
|
2015-08-13 14:18:04 -07:00
|
|
|
case BRW_REGISTER_TYPE_DF:
|
|
|
|
|
fprintf(file, "%fDF", inst->src[i].df);
|
|
|
|
|
break;
|
2013-03-11 11:10:34 -07:00
|
|
|
case BRW_REGISTER_TYPE_D:
|
2015-10-24 14:55:57 -07:00
|
|
|
fprintf(file, "%dD", inst->src[i].d);
|
2013-03-11 11:10:34 -07:00
|
|
|
break;
|
|
|
|
|
case BRW_REGISTER_TYPE_UD:
|
2015-10-24 14:55:57 -07:00
|
|
|
fprintf(file, "%uU", inst->src[i].ud);
|
2013-03-11 11:10:34 -07:00
|
|
|
break;
|
2014-03-08 17:22:22 -08:00
|
|
|
case BRW_REGISTER_TYPE_VF:
|
2014-12-31 16:54:44 -08:00
|
|
|
fprintf(file, "[%-gF, %-gF, %-gF, %-gF]",
|
2015-10-24 14:55:57 -07:00
|
|
|
brw_vf_to_float((inst->src[i].ud >> 0) & 0xff),
|
|
|
|
|
brw_vf_to_float((inst->src[i].ud >> 8) & 0xff),
|
|
|
|
|
brw_vf_to_float((inst->src[i].ud >> 16) & 0xff),
|
|
|
|
|
brw_vf_to_float((inst->src[i].ud >> 24) & 0xff));
|
2014-03-08 17:22:22 -08:00
|
|
|
break;
|
2013-03-11 11:10:34 -07:00
|
|
|
default:
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, "???");
|
2013-03-11 11:10:34 -07:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2015-10-26 17:52:57 -07:00
|
|
|
case ARF:
|
|
|
|
|
switch (inst->src[i].nr) {
|
|
|
|
|
case BRW_ARF_NULL:
|
|
|
|
|
fprintf(file, "null");
|
|
|
|
|
break;
|
|
|
|
|
case BRW_ARF_ADDRESS:
|
|
|
|
|
fprintf(file, "a0.%d", inst->src[i].subnr);
|
|
|
|
|
break;
|
|
|
|
|
case BRW_ARF_ACCUMULATOR:
|
|
|
|
|
fprintf(file, "acc%d", inst->src[i].subnr);
|
|
|
|
|
break;
|
|
|
|
|
case BRW_ARF_FLAG:
|
|
|
|
|
fprintf(file, "f%d.%d", inst->src[i].nr & 0xf, inst->src[i].subnr);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fprintf(file, "arf%d.%d", inst->src[i].nr & 0xf, inst->src[i].subnr);
|
|
|
|
|
break;
|
2013-11-25 22:17:29 -08:00
|
|
|
}
|
|
|
|
|
break;
|
2012-10-03 16:11:26 -07:00
|
|
|
case BAD_FILE:
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, "(null)");
|
2012-10-03 16:11:26 -07:00
|
|
|
break;
|
2015-10-26 06:58:56 -07:00
|
|
|
case MRF:
|
|
|
|
|
unreachable("not reached");
|
2012-10-03 16:11:26 -07:00
|
|
|
}
|
|
|
|
|
|
2016-09-02 13:52:54 -07:00
|
|
|
if (inst->src[i].offset ||
|
|
|
|
|
(inst->src[i].file == VGRF &&
|
|
|
|
|
alloc.sizes[inst->src[i].nr] * REG_SIZE != inst->size_read(i))) {
|
|
|
|
|
const unsigned reg_size = (inst->src[i].file == UNIFORM ? 16 : REG_SIZE);
|
|
|
|
|
fprintf(file, "+%d.%d", inst->src[i].offset / reg_size,
|
|
|
|
|
inst->src[i].offset % reg_size);
|
|
|
|
|
}
|
2012-10-03 16:11:26 -07:00
|
|
|
|
2013-12-02 12:43:50 -08:00
|
|
|
if (inst->src[i].file != IMM) {
|
|
|
|
|
static const char *chans[4] = {"x", "y", "z", "w"};
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, ".");
|
2013-12-02 12:43:50 -08:00
|
|
|
for (int c = 0; c < 4; c++) {
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, "%s", chans[BRW_GET_SWZ(inst->src[i].swizzle, c)]);
|
2013-12-02 12:43:50 -08:00
|
|
|
}
|
2012-10-03 16:11:26 -07:00
|
|
|
}
|
|
|
|
|
|
2013-12-02 12:41:16 -08:00
|
|
|
if (inst->src[i].abs)
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, "|");
|
2013-12-02 12:41:16 -08:00
|
|
|
|
2013-12-02 13:10:29 -08:00
|
|
|
if (inst->src[i].file != IMM) {
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, ":%s", brw_reg_type_letters(inst->src[i].type));
|
2013-12-02 13:10:29 -08:00
|
|
|
}
|
|
|
|
|
|
2013-11-25 15:15:25 -08:00
|
|
|
if (i < 2 && inst->src[i + 1].file != BAD_FILE)
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, ", ");
|
2012-10-03 16:11:26 -07:00
|
|
|
}
|
|
|
|
|
|
2015-11-09 23:55:58 -08:00
|
|
|
if (inst->force_writemask_all)
|
|
|
|
|
fprintf(file, " NoMask");
|
|
|
|
|
|
2016-06-29 08:40:27 +02:00
|
|
|
if (inst->exec_size != 8)
|
|
|
|
|
fprintf(file, " group%d", inst->group);
|
|
|
|
|
|
2014-05-29 11:45:15 -07:00
|
|
|
fprintf(file, "\n");
|
2012-10-03 16:11:26 -07:00
|
|
|
}
|
|
|
|
|
|
2013-10-16 11:40:41 -07:00
|
|
|
|
2013-03-22 08:47:49 -07:00
|
|
|
int
|
|
|
|
|
vec4_vs_visitor::setup_attributes(int payload_reg)
|
|
|
|
|
{
|
2017-05-03 17:40:54 -07:00
|
|
|
foreach_block_and_inst(block, vec4_instruction, inst, cfg) {
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
|
if (inst->src[i].file == ATTR) {
|
|
|
|
|
assert(inst->src[i].offset % REG_SIZE == 0);
|
|
|
|
|
int grf = payload_reg + inst->src[i].nr +
|
|
|
|
|
inst->src[i].offset / REG_SIZE;
|
|
|
|
|
|
|
|
|
|
struct brw_reg reg = brw_vec8_grf(grf, 0);
|
|
|
|
|
reg.swizzle = inst->src[i].swizzle;
|
|
|
|
|
reg.type = inst->src[i].type;
|
|
|
|
|
reg.abs = inst->src[i].abs;
|
|
|
|
|
reg.negate = inst->src[i].negate;
|
|
|
|
|
inst->src[i] = reg;
|
|
|
|
|
}
|
2013-03-22 08:47:49 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-21 12:23:47 +02:00
|
|
|
return payload_reg + vs_prog_data->nr_attribute_slots;
|
2012-11-26 21:46:27 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
vec4_visitor::setup_uniforms(int reg)
|
|
|
|
|
{
|
2014-05-19 10:10:01 -07:00
|
|
|
prog_data->base.dispatch_grf_start_reg = reg;
|
2013-07-13 07:09:54 -07:00
|
|
|
|
2012-11-26 21:46:27 -08:00
|
|
|
/* The pre-gen6 VS requires that some push constants get loaded no
|
|
|
|
|
* matter what, or the GPU would hang.
|
|
|
|
|
*/
|
2015-04-15 18:00:05 -07:00
|
|
|
if (devinfo->gen < 6 && this->uniforms == 0) {
|
2014-02-19 15:14:02 +01:00
|
|
|
stage_prog_data->param =
|
2014-08-11 12:21:44 +01:00
|
|
|
reralloc(NULL, stage_prog_data->param, const gl_constant_value *, 4);
|
2012-11-26 21:46:27 -08:00
|
|
|
for (unsigned int i = 0; i < 4; i++) {
|
|
|
|
|
unsigned int slot = this->uniforms * 4 + i;
|
2014-08-14 21:17:04 +03:00
|
|
|
static gl_constant_value zero = { 0.0 };
|
2014-02-19 15:14:02 +01:00
|
|
|
stage_prog_data->param[slot] = &zero;
|
2012-11-26 21:46:27 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->uniforms++;
|
|
|
|
|
reg++;
|
|
|
|
|
} else {
|
|
|
|
|
reg += ALIGN(uniforms, 2) / 2;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-19 15:14:02 +01:00
|
|
|
stage_prog_data->nr_params = this->uniforms * 4;
|
2012-11-26 21:46:27 -08:00
|
|
|
|
2014-09-02 11:38:29 -07:00
|
|
|
prog_data->base.curb_read_length =
|
|
|
|
|
reg - prog_data->base.dispatch_grf_start_reg;
|
2012-11-26 21:46:27 -08:00
|
|
|
|
|
|
|
|
return reg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2013-07-13 09:03:18 -07:00
|
|
|
vec4_vs_visitor::setup_payload(void)
|
2012-11-26 21:46:27 -08:00
|
|
|
{
|
|
|
|
|
int reg = 0;
|
|
|
|
|
|
|
|
|
|
/* The payload always contains important data in g0, which contains
|
|
|
|
|
* the URB handles that are passed on to the URB write at the end
|
|
|
|
|
* of the thread. So, we always start push constants at g1.
|
|
|
|
|
*/
|
|
|
|
|
reg++;
|
|
|
|
|
|
|
|
|
|
reg = setup_uniforms(reg);
|
|
|
|
|
|
|
|
|
|
reg = setup_attributes(reg);
|
|
|
|
|
|
|
|
|
|
this->first_non_payload_grf = reg;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-11 12:27:02 -08:00
|
|
|
bool
|
|
|
|
|
vec4_visitor::lower_minmax()
|
|
|
|
|
{
|
|
|
|
|
assert(devinfo->gen < 6);
|
|
|
|
|
|
|
|
|
|
bool progress = false;
|
|
|
|
|
|
|
|
|
|
foreach_block_and_inst_safe(block, vec4_instruction, inst, cfg) {
|
|
|
|
|
const vec4_builder ibld(this, block, inst);
|
|
|
|
|
|
|
|
|
|
if (inst->opcode == BRW_OPCODE_SEL &&
|
|
|
|
|
inst->predicate == BRW_PREDICATE_NONE) {
|
|
|
|
|
/* FIXME: Using CMP doesn't preserve the NaN propagation semantics of
|
|
|
|
|
* the original SEL.L/GE instruction
|
|
|
|
|
*/
|
|
|
|
|
ibld.CMP(ibld.null_reg_d(), inst->src[0], inst->src[1],
|
|
|
|
|
inst->conditional_mod);
|
|
|
|
|
inst->predicate = BRW_PREDICATE_NORMAL;
|
|
|
|
|
inst->conditional_mod = BRW_CONDITIONAL_NONE;
|
|
|
|
|
|
|
|
|
|
progress = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (progress)
|
|
|
|
|
invalidate_live_intervals();
|
|
|
|
|
|
|
|
|
|
return progress;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-27 14:10:52 -08:00
|
|
|
src_reg
|
|
|
|
|
vec4_visitor::get_timestamp()
|
|
|
|
|
{
|
2015-04-15 18:00:05 -07:00
|
|
|
assert(devinfo->gen >= 7);
|
2012-11-27 14:10:52 -08:00
|
|
|
|
|
|
|
|
src_reg ts = src_reg(brw_reg(BRW_ARCHITECTURE_REGISTER_FILE,
|
|
|
|
|
BRW_ARF_TIMESTAMP,
|
|
|
|
|
0,
|
2014-12-12 17:19:07 +01:00
|
|
|
0,
|
|
|
|
|
0,
|
2012-11-27 14:10:52 -08:00
|
|
|
BRW_REGISTER_TYPE_UD,
|
|
|
|
|
BRW_VERTICAL_STRIDE_0,
|
|
|
|
|
BRW_WIDTH_4,
|
|
|
|
|
BRW_HORIZONTAL_STRIDE_4,
|
|
|
|
|
BRW_SWIZZLE_XYZW,
|
|
|
|
|
WRITEMASK_XYZW));
|
|
|
|
|
|
|
|
|
|
dst_reg dst = dst_reg(this, glsl_type::uvec4_type);
|
|
|
|
|
|
|
|
|
|
vec4_instruction *mov = emit(MOV(dst, ts));
|
|
|
|
|
/* We want to read the 3 fields we care about (mostly field 0, but also 2)
|
|
|
|
|
* even if it's not enabled in the dispatch.
|
|
|
|
|
*/
|
|
|
|
|
mov->force_writemask_all = true;
|
|
|
|
|
|
|
|
|
|
return src_reg(dst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
vec4_visitor::emit_shader_time_begin()
|
|
|
|
|
{
|
|
|
|
|
current_annotation = "shader time start";
|
|
|
|
|
shader_start_time = get_timestamp();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
vec4_visitor::emit_shader_time_end()
|
|
|
|
|
{
|
|
|
|
|
current_annotation = "shader time end";
|
|
|
|
|
src_reg shader_end_time = get_timestamp();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Check that there weren't any timestamp reset events (assuming these
|
|
|
|
|
* were the only two timestamp reads that happened).
|
|
|
|
|
*/
|
2012-12-10 09:21:34 -08:00
|
|
|
src_reg reset_end = shader_end_time;
|
2012-11-27 14:10:52 -08:00
|
|
|
reset_end.swizzle = BRW_SWIZZLE_ZZZZ;
|
2015-11-02 12:12:44 -08:00
|
|
|
vec4_instruction *test = emit(AND(dst_null_ud(), reset_end, brw_imm_ud(1u)));
|
2012-11-27 14:10:52 -08:00
|
|
|
test->conditional_mod = BRW_CONDITIONAL_Z;
|
|
|
|
|
|
|
|
|
|
emit(IF(BRW_PREDICATE_NORMAL));
|
|
|
|
|
|
|
|
|
|
/* Take the current timestamp and get the delta. */
|
2012-12-10 09:21:34 -08:00
|
|
|
shader_start_time.negate = true;
|
2012-11-27 14:10:52 -08:00
|
|
|
dst_reg diff = dst_reg(this, glsl_type::uint_type);
|
2012-12-10 09:21:34 -08:00
|
|
|
emit(ADD(diff, shader_start_time, shader_end_time));
|
2012-11-27 14:10:52 -08:00
|
|
|
|
|
|
|
|
/* If there were no instructions between the two timestamp gets, the diff
|
|
|
|
|
* is 2 cycles. Remove that overhead, so I can forget about that when
|
|
|
|
|
* trying to determine the time taken for single instructions.
|
|
|
|
|
*/
|
2015-11-02 12:12:44 -08:00
|
|
|
emit(ADD(diff, src_reg(diff), brw_imm_ud(-2u)));
|
2012-11-27 14:10:52 -08:00
|
|
|
|
2015-06-19 15:40:09 -07:00
|
|
|
emit_shader_time_write(0, src_reg(diff));
|
2015-11-02 12:12:44 -08:00
|
|
|
emit_shader_time_write(1, brw_imm_ud(1u));
|
2012-12-10 09:44:19 -08:00
|
|
|
emit(BRW_OPCODE_ELSE);
|
2015-11-02 12:12:44 -08:00
|
|
|
emit_shader_time_write(2, brw_imm_ud(1u));
|
2012-12-10 09:21:34 -08:00
|
|
|
emit(BRW_OPCODE_ENDIF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2015-06-19 15:40:09 -07:00
|
|
|
vec4_visitor::emit_shader_time_write(int shader_time_subindex, src_reg value)
|
2012-12-10 09:21:34 -08:00
|
|
|
{
|
2012-12-17 17:11:21 -08:00
|
|
|
dst_reg dst =
|
|
|
|
|
dst_reg(this, glsl_type::get_array_instance(glsl_type::vec4_type, 2));
|
2012-11-27 14:10:52 -08:00
|
|
|
|
2012-12-17 17:11:21 -08:00
|
|
|
dst_reg offset = dst;
|
|
|
|
|
dst_reg time = dst;
|
2016-09-01 13:10:36 -07:00
|
|
|
time.offset += REG_SIZE;
|
2012-11-27 14:10:52 -08:00
|
|
|
|
2012-12-17 17:11:21 -08:00
|
|
|
offset.type = BRW_REGISTER_TYPE_UD;
|
2015-06-19 14:46:03 -07:00
|
|
|
int index = shader_time_index * 3 + shader_time_subindex;
|
2017-02-28 18:10:53 -08:00
|
|
|
emit(MOV(offset, brw_imm_d(index * BRW_SHADER_TIME_STRIDE)));
|
2012-11-27 14:10:52 -08:00
|
|
|
|
2012-12-17 17:11:21 -08:00
|
|
|
time.type = BRW_REGISTER_TYPE_UD;
|
2015-10-07 12:38:12 +01:00
|
|
|
emit(MOV(time, value));
|
2012-12-17 17:11:21 -08:00
|
|
|
|
2015-02-03 22:52:37 +02:00
|
|
|
vec4_instruction *inst =
|
|
|
|
|
emit(SHADER_OPCODE_SHADER_TIME_ADD, dst_reg(), src_reg(dst));
|
|
|
|
|
inst->mlen = 2;
|
2012-11-27 14:10:52 -08:00
|
|
|
}
|
|
|
|
|
|
i965/vec4: fix vertical stride to avoid breaking region parameter rule
From IVB PRM, vol4, part3, "General Restrictions on Regioning
Parameters":
"If ExecSize = Width and HorzStride ≠ 0, VertStride must
be set to Width * HorzStride."
In next patch, we are going to modify the region parameter for
uniforms and vgrf. For uniforms that are the source of
DF align1 instructions, they will have <0, 4, 1> regioning and
the execsize for those instructions will be 4, so they will break
the regioning rule. This will be the same for VGRF sources where
we use the vstride == 0 exploit.
As we know we are not going to cross the GRF boundary with that
execsize and parameters (not even with the exploit), we just fix
the vstride here.
v2:
- Move is_align1_df() (Curro)
- Refactor exec_size == width calculation (Curro)
Signed-off-by: Samuel Iglesias Gonsálvez <siglesias@igalia.com>
Cc: "17.1" <mesa-stable@lists.freedesktop.org>
Reviewed-by: Francisco Jerez <currojerez@riseup.net>
2017-04-26 10:12:03 +02:00
|
|
|
static bool
|
|
|
|
|
is_align1_df(vec4_instruction *inst)
|
|
|
|
|
{
|
|
|
|
|
switch (inst->opcode) {
|
|
|
|
|
case VEC4_OPCODE_DOUBLE_TO_F32:
|
|
|
|
|
case VEC4_OPCODE_DOUBLE_TO_D32:
|
|
|
|
|
case VEC4_OPCODE_DOUBLE_TO_U32:
|
|
|
|
|
case VEC4_OPCODE_TO_DOUBLE:
|
|
|
|
|
case VEC4_OPCODE_PICK_LOW_32BIT:
|
|
|
|
|
case VEC4_OPCODE_PICK_HIGH_32BIT:
|
|
|
|
|
case VEC4_OPCODE_SET_LOW_32BIT:
|
|
|
|
|
case VEC4_OPCODE_SET_HIGH_32BIT:
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-22 16:04:15 -07:00
|
|
|
void
|
|
|
|
|
vec4_visitor::convert_to_hw_regs()
|
|
|
|
|
{
|
|
|
|
|
foreach_block_and_inst(block, vec4_instruction, inst, cfg) {
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
|
struct src_reg &src = inst->src[i];
|
|
|
|
|
struct brw_reg reg;
|
|
|
|
|
switch (src.file) {
|
2015-11-18 14:00:58 +01:00
|
|
|
case VGRF: {
|
2017-04-25 12:18:17 +02:00
|
|
|
reg = byte_offset(brw_vecn_grf(4, src.nr, 0), src.offset);
|
2015-10-22 16:04:15 -07:00
|
|
|
reg.type = src.type;
|
|
|
|
|
reg.abs = src.abs;
|
|
|
|
|
reg.negate = src.negate;
|
|
|
|
|
break;
|
2015-11-18 14:00:58 +01:00
|
|
|
}
|
2015-10-22 16:04:15 -07:00
|
|
|
|
2015-11-18 14:00:58 +01:00
|
|
|
case UNIFORM: {
|
2016-09-08 18:00:11 -07:00
|
|
|
reg = stride(byte_offset(brw_vec4_grf(
|
|
|
|
|
prog_data->base.dispatch_grf_start_reg +
|
|
|
|
|
src.nr / 2, src.nr % 2 * 4),
|
|
|
|
|
src.offset),
|
2017-04-25 12:18:17 +02:00
|
|
|
0, 4, 1);
|
2015-10-22 16:04:15 -07:00
|
|
|
reg.type = src.type;
|
|
|
|
|
reg.abs = src.abs;
|
|
|
|
|
reg.negate = src.negate;
|
|
|
|
|
|
|
|
|
|
/* This should have been moved to pull constants. */
|
|
|
|
|
assert(!src.reladdr);
|
|
|
|
|
break;
|
2015-11-18 14:00:58 +01:00
|
|
|
}
|
2015-10-22 16:04:15 -07:00
|
|
|
|
2015-10-26 17:52:57 -07:00
|
|
|
case FIXED_GRF:
|
2016-05-24 11:01:27 +02:00
|
|
|
if (type_sz(src.type) == 8) {
|
|
|
|
|
reg = src.as_brw_reg();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* fallthrough */
|
|
|
|
|
case ARF:
|
2015-11-02 12:25:24 -08:00
|
|
|
case IMM:
|
2015-10-22 16:04:15 -07:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
case BAD_FILE:
|
|
|
|
|
/* Probably unused. */
|
|
|
|
|
reg = brw_null_reg();
|
2016-09-12 16:06:22 +00:00
|
|
|
reg = retype(reg, src.type);
|
2015-10-22 16:04:15 -07:00
|
|
|
break;
|
|
|
|
|
|
2015-10-26 06:58:56 -07:00
|
|
|
case MRF:
|
|
|
|
|
case ATTR:
|
2015-10-22 16:04:15 -07:00
|
|
|
unreachable("not reached");
|
|
|
|
|
}
|
i965: Move 3-src subnr swizzle handling into the vec4 backend.
While most align16 instructions only support a SubRegNum of 0 or 4
(using swizzling to control the other channels), 3-src instructions
actually support arbitrary SubRegNums. When the RepCtrl bit is set,
we believe it ignores the swizzle and uses the equivalent of a <0,1,0>
region from the subnr.
In the past, we adopted a vec4-centric approach of specifying subnr of
0 or 4 and a swizzle, then having brw_eu_emit.c convert that to a proper
SubRegNum. This isn't a great fit for the scalar backend, where we
don't set swizzles at all, and happily set subnrs in the range [0, 7].
This patch changes brw_eu_emit.c to use subnr and swizzle directly,
relying on the higher levels to set them sensibly.
This should fix problems where scalar sources get copy propagated into
3-src instructions in the FS backend. I've only observed this with
TES push model inputs, but I suppose it could happen in other cases.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Matt Turner <mattst88@gmail.com>
2015-12-31 12:47:19 -08:00
|
|
|
|
2016-05-24 11:01:27 +02:00
|
|
|
apply_logical_swizzle(®, inst, i);
|
2015-10-24 15:29:03 -07:00
|
|
|
src = reg;
|
i965/vec4: fix vertical stride to avoid breaking region parameter rule
From IVB PRM, vol4, part3, "General Restrictions on Regioning
Parameters":
"If ExecSize = Width and HorzStride ≠ 0, VertStride must
be set to Width * HorzStride."
In next patch, we are going to modify the region parameter for
uniforms and vgrf. For uniforms that are the source of
DF align1 instructions, they will have <0, 4, 1> regioning and
the execsize for those instructions will be 4, so they will break
the regioning rule. This will be the same for VGRF sources where
we use the vstride == 0 exploit.
As we know we are not going to cross the GRF boundary with that
execsize and parameters (not even with the exploit), we just fix
the vstride here.
v2:
- Move is_align1_df() (Curro)
- Refactor exec_size == width calculation (Curro)
Signed-off-by: Samuel Iglesias Gonsálvez <siglesias@igalia.com>
Cc: "17.1" <mesa-stable@lists.freedesktop.org>
Reviewed-by: Francisco Jerez <currojerez@riseup.net>
2017-04-26 10:12:03 +02:00
|
|
|
|
|
|
|
|
/* From IVB PRM, vol4, part3, "General Restrictions on Regioning
|
|
|
|
|
* Parameters":
|
|
|
|
|
*
|
|
|
|
|
* "If ExecSize = Width and HorzStride ≠ 0, VertStride must be set
|
|
|
|
|
* to Width * HorzStride."
|
|
|
|
|
*
|
|
|
|
|
* We can break this rule with DF sources on DF align1
|
|
|
|
|
* instructions, because the exec_size would be 4 and width is 4.
|
|
|
|
|
* As we know we are not accessing to next GRF, it is safe to
|
|
|
|
|
* set vstride to the formula given by the rule itself.
|
|
|
|
|
*/
|
|
|
|
|
if (is_align1_df(inst) && (cvt(inst->exec_size) - 1) == src.width)
|
|
|
|
|
src.vstride = src.width + src.hstride;
|
2015-10-22 16:04:15 -07:00
|
|
|
}
|
|
|
|
|
|
2016-04-28 00:19:13 -07:00
|
|
|
if (inst->is_3src(devinfo)) {
|
i965: Move 3-src subnr swizzle handling into the vec4 backend.
While most align16 instructions only support a SubRegNum of 0 or 4
(using swizzling to control the other channels), 3-src instructions
actually support arbitrary SubRegNums. When the RepCtrl bit is set,
we believe it ignores the swizzle and uses the equivalent of a <0,1,0>
region from the subnr.
In the past, we adopted a vec4-centric approach of specifying subnr of
0 or 4 and a swizzle, then having brw_eu_emit.c convert that to a proper
SubRegNum. This isn't a great fit for the scalar backend, where we
don't set swizzles at all, and happily set subnrs in the range [0, 7].
This patch changes brw_eu_emit.c to use subnr and swizzle directly,
relying on the higher levels to set them sensibly.
This should fix problems where scalar sources get copy propagated into
3-src instructions in the FS backend. I've only observed this with
TES push model inputs, but I suppose it could happen in other cases.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Matt Turner <mattst88@gmail.com>
2015-12-31 12:47:19 -08:00
|
|
|
/* 3-src instructions with scalar sources support arbitrary subnr,
|
|
|
|
|
* but don't actually use swizzles. Convert swizzle into subnr.
|
2016-06-01 08:35:37 +02:00
|
|
|
* Skip this for double-precision instructions: RepCtrl=1 is not
|
|
|
|
|
* allowed for them and needs special handling.
|
i965: Move 3-src subnr swizzle handling into the vec4 backend.
While most align16 instructions only support a SubRegNum of 0 or 4
(using swizzling to control the other channels), 3-src instructions
actually support arbitrary SubRegNums. When the RepCtrl bit is set,
we believe it ignores the swizzle and uses the equivalent of a <0,1,0>
region from the subnr.
In the past, we adopted a vec4-centric approach of specifying subnr of
0 or 4 and a swizzle, then having brw_eu_emit.c convert that to a proper
SubRegNum. This isn't a great fit for the scalar backend, where we
don't set swizzles at all, and happily set subnrs in the range [0, 7].
This patch changes brw_eu_emit.c to use subnr and swizzle directly,
relying on the higher levels to set them sensibly.
This should fix problems where scalar sources get copy propagated into
3-src instructions in the FS backend. I've only observed this with
TES push model inputs, but I suppose it could happen in other cases.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Matt Turner <mattst88@gmail.com>
2015-12-31 12:47:19 -08:00
|
|
|
*/
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
2016-06-01 08:35:37 +02:00
|
|
|
if (inst->src[i].vstride == BRW_VERTICAL_STRIDE_0 &&
|
|
|
|
|
type_sz(inst->src[i].type) < 8) {
|
i965: Move 3-src subnr swizzle handling into the vec4 backend.
While most align16 instructions only support a SubRegNum of 0 or 4
(using swizzling to control the other channels), 3-src instructions
actually support arbitrary SubRegNums. When the RepCtrl bit is set,
we believe it ignores the swizzle and uses the equivalent of a <0,1,0>
region from the subnr.
In the past, we adopted a vec4-centric approach of specifying subnr of
0 or 4 and a swizzle, then having brw_eu_emit.c convert that to a proper
SubRegNum. This isn't a great fit for the scalar backend, where we
don't set swizzles at all, and happily set subnrs in the range [0, 7].
This patch changes brw_eu_emit.c to use subnr and swizzle directly,
relying on the higher levels to set them sensibly.
This should fix problems where scalar sources get copy propagated into
3-src instructions in the FS backend. I've only observed this with
TES push model inputs, but I suppose it could happen in other cases.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Matt Turner <mattst88@gmail.com>
2015-12-31 12:47:19 -08:00
|
|
|
assert(brw_is_single_value_swizzle(inst->src[i].swizzle));
|
|
|
|
|
inst->src[i].subnr += 4 * BRW_GET_SWZ(inst->src[i].swizzle, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-22 16:04:15 -07:00
|
|
|
dst_reg &dst = inst->dst;
|
|
|
|
|
struct brw_reg reg;
|
|
|
|
|
|
|
|
|
|
switch (inst->dst.file) {
|
2015-10-26 17:09:25 -07:00
|
|
|
case VGRF:
|
2016-09-08 18:00:11 -07:00
|
|
|
reg = byte_offset(brw_vec8_grf(dst.nr, 0), dst.offset);
|
2015-10-22 16:04:15 -07:00
|
|
|
reg.type = dst.type;
|
2015-10-22 19:41:30 -07:00
|
|
|
reg.writemask = dst.writemask;
|
2015-10-22 16:04:15 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MRF:
|
2016-09-08 18:00:11 -07:00
|
|
|
reg = byte_offset(brw_message_reg(dst.nr), dst.offset);
|
|
|
|
|
assert((reg.nr & ~BRW_MRF_COMPR4) < BRW_MAX_MRF(devinfo->gen));
|
2015-10-22 16:04:15 -07:00
|
|
|
reg.type = dst.type;
|
2015-10-22 19:41:30 -07:00
|
|
|
reg.writemask = dst.writemask;
|
2015-10-22 16:04:15 -07:00
|
|
|
break;
|
|
|
|
|
|
2015-10-26 17:52:57 -07:00
|
|
|
case ARF:
|
|
|
|
|
case FIXED_GRF:
|
2015-11-19 21:51:37 -08:00
|
|
|
reg = dst.as_brw_reg();
|
2015-10-22 16:04:15 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BAD_FILE:
|
|
|
|
|
reg = brw_null_reg();
|
2016-09-12 16:06:22 +00:00
|
|
|
reg = retype(reg, dst.type);
|
2015-10-22 16:04:15 -07:00
|
|
|
break;
|
|
|
|
|
|
2015-10-26 06:58:56 -07:00
|
|
|
case IMM:
|
|
|
|
|
case ATTR:
|
|
|
|
|
case UNIFORM:
|
2015-10-22 16:04:15 -07:00
|
|
|
unreachable("not reached");
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-24 15:29:03 -07:00
|
|
|
dst = reg;
|
2015-10-22 16:04:15 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-01 09:26:32 +02:00
|
|
|
static bool
|
|
|
|
|
stage_uses_interleaved_attributes(unsigned stage,
|
|
|
|
|
enum shader_dispatch_mode dispatch_mode)
|
|
|
|
|
{
|
|
|
|
|
switch (stage) {
|
|
|
|
|
case MESA_SHADER_TESS_EVAL:
|
|
|
|
|
return true;
|
|
|
|
|
case MESA_SHADER_GEOMETRY:
|
|
|
|
|
return dispatch_mode != DISPATCH_MODE_4X2_DUAL_OBJECT;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-29 10:41:45 +02:00
|
|
|
/**
|
|
|
|
|
* Get the closest native SIMD width supported by the hardware for instruction
|
|
|
|
|
* \p inst. The instruction will be left untouched by
|
|
|
|
|
* vec4_visitor::lower_simd_width() if the returned value matches the
|
|
|
|
|
* instruction's original execution size.
|
|
|
|
|
*/
|
|
|
|
|
static unsigned
|
|
|
|
|
get_lowered_simd_width(const struct gen_device_info *devinfo,
|
2016-07-01 09:26:32 +02:00
|
|
|
enum shader_dispatch_mode dispatch_mode,
|
|
|
|
|
unsigned stage, const vec4_instruction *inst)
|
2016-08-29 10:41:45 +02:00
|
|
|
{
|
2016-06-28 11:54:07 +02:00
|
|
|
/* Do not split some instructions that require special handling */
|
|
|
|
|
switch (inst->opcode) {
|
|
|
|
|
case SHADER_OPCODE_GEN4_SCRATCH_READ:
|
|
|
|
|
case SHADER_OPCODE_GEN4_SCRATCH_WRITE:
|
|
|
|
|
return inst->exec_size;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-29 10:41:45 +02:00
|
|
|
unsigned lowered_width = MIN2(16, inst->exec_size);
|
|
|
|
|
|
|
|
|
|
/* We need to split some cases of double-precision instructions that write
|
|
|
|
|
* 2 registers. We only need to care about this in gen7 because that is the
|
|
|
|
|
* only hardware that implements fp64 in Align16.
|
|
|
|
|
*/
|
|
|
|
|
if (devinfo->gen == 7 && inst->size_written > REG_SIZE) {
|
2016-06-17 08:47:29 +02:00
|
|
|
/* Align16 8-wide double-precision SEL does not work well. Verified
|
|
|
|
|
* empirically.
|
|
|
|
|
*/
|
|
|
|
|
if (inst->opcode == BRW_OPCODE_SEL && type_sz(inst->dst.type) == 8)
|
|
|
|
|
lowered_width = MIN2(lowered_width, 4);
|
|
|
|
|
|
2016-08-29 10:41:45 +02:00
|
|
|
/* HSW PRM, 3D Media GPGPU Engine, Region Alignment Rules for Direct
|
|
|
|
|
* Register Addressing:
|
|
|
|
|
*
|
|
|
|
|
* "When destination spans two registers, the source MUST span two
|
|
|
|
|
* registers."
|
|
|
|
|
*/
|
|
|
|
|
for (unsigned i = 0; i < 3; i++) {
|
|
|
|
|
if (inst->src[i].file == BAD_FILE)
|
|
|
|
|
continue;
|
|
|
|
|
if (inst->size_read(i) <= REG_SIZE)
|
|
|
|
|
lowered_width = MIN2(lowered_width, 4);
|
2016-07-01 09:26:32 +02:00
|
|
|
|
|
|
|
|
/* Interleaved attribute setups use a vertical stride of 0, which
|
|
|
|
|
* makes them hit the associated instruction decompression bug in gen7.
|
|
|
|
|
* Split them to prevent this.
|
|
|
|
|
*/
|
|
|
|
|
if (inst->src[i].file == ATTR &&
|
|
|
|
|
stage_uses_interleaved_attributes(stage, dispatch_mode))
|
|
|
|
|
lowered_width = MIN2(lowered_width, 4);
|
2016-08-29 10:41:45 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-29 10:10:30 +02:00
|
|
|
/* IvyBridge can manage a maximum of 4 DFs per SIMD4x2 instruction, since
|
|
|
|
|
* it doesn't support compression in Align16 mode, no matter if it has
|
|
|
|
|
* force_writemask_all enabled or disabled (the latter is affected by the
|
|
|
|
|
* compressed instruction bug in gen7, which is another reason to enforce
|
|
|
|
|
* this limit).
|
|
|
|
|
*/
|
|
|
|
|
if (devinfo->gen == 7 && !devinfo->is_haswell &&
|
|
|
|
|
(get_exec_type_size(inst) == 8 || type_sz(inst->dst.type) == 8))
|
|
|
|
|
lowered_width = MIN2(lowered_width, 4);
|
|
|
|
|
|
2016-08-29 10:41:45 +02:00
|
|
|
return lowered_width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
dst_src_regions_overlap(vec4_instruction *inst)
|
|
|
|
|
{
|
|
|
|
|
if (inst->size_written == 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
unsigned dst_start = inst->dst.offset;
|
|
|
|
|
unsigned dst_end = dst_start + inst->size_written - 1;
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
|
if (inst->src[i].file == BAD_FILE)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (inst->dst.file != inst->src[i].file ||
|
|
|
|
|
inst->dst.nr != inst->src[i].nr)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
unsigned src_start = inst->src[i].offset;
|
|
|
|
|
unsigned src_end = src_start + inst->size_read(i) - 1;
|
|
|
|
|
|
|
|
|
|
if ((dst_start >= src_start && dst_start <= src_end) ||
|
|
|
|
|
(dst_end >= src_start && dst_end <= src_end) ||
|
|
|
|
|
(dst_start <= src_start && dst_end >= src_end)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
vec4_visitor::lower_simd_width()
|
|
|
|
|
{
|
|
|
|
|
bool progress = false;
|
|
|
|
|
|
|
|
|
|
foreach_block_and_inst_safe(block, vec4_instruction, inst, cfg) {
|
2016-07-01 09:26:32 +02:00
|
|
|
const unsigned lowered_width =
|
|
|
|
|
get_lowered_simd_width(devinfo, prog_data->dispatch_mode, stage, inst);
|
2016-08-29 10:41:45 +02:00
|
|
|
assert(lowered_width <= inst->exec_size);
|
|
|
|
|
if (lowered_width == inst->exec_size)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* We need to deal with source / destination overlaps when splitting.
|
|
|
|
|
* The hardware supports reading from and writing to the same register
|
|
|
|
|
* in the same instruction, but we need to be careful that each split
|
|
|
|
|
* instruction we produce does not corrupt the source of the next.
|
|
|
|
|
*
|
|
|
|
|
* The easiest way to handle this is to make the split instructions write
|
|
|
|
|
* to temporaries if there is an src/dst overlap and then move from the
|
|
|
|
|
* temporaries to the original destination. We also need to consider
|
|
|
|
|
* instructions that do partial writes via align1 opcodes, in which case
|
|
|
|
|
* we need to make sure that the we initialize the temporary with the
|
|
|
|
|
* value of the instruction's dst.
|
|
|
|
|
*/
|
|
|
|
|
bool needs_temp = dst_src_regions_overlap(inst);
|
|
|
|
|
for (unsigned n = 0; n < inst->exec_size / lowered_width; n++) {
|
|
|
|
|
unsigned channel_offset = lowered_width * n;
|
|
|
|
|
|
|
|
|
|
unsigned size_written = lowered_width * type_sz(inst->dst.type);
|
|
|
|
|
|
|
|
|
|
/* Create the split instruction from the original so that we copy all
|
|
|
|
|
* relevant instruction fields, then set the width and calculate the
|
|
|
|
|
* new dst/src regions.
|
|
|
|
|
*/
|
|
|
|
|
vec4_instruction *linst = new(mem_ctx) vec4_instruction(*inst);
|
|
|
|
|
linst->exec_size = lowered_width;
|
|
|
|
|
linst->group = channel_offset;
|
|
|
|
|
linst->size_written = size_written;
|
|
|
|
|
|
|
|
|
|
/* Compute split dst region */
|
|
|
|
|
dst_reg dst;
|
|
|
|
|
if (needs_temp) {
|
|
|
|
|
unsigned num_regs = DIV_ROUND_UP(size_written, REG_SIZE);
|
|
|
|
|
dst = retype(dst_reg(VGRF, alloc.allocate(num_regs)),
|
|
|
|
|
inst->dst.type);
|
|
|
|
|
if (inst->is_align1_partial_write()) {
|
|
|
|
|
vec4_instruction *copy = MOV(dst, src_reg(inst->dst));
|
|
|
|
|
copy->exec_size = lowered_width;
|
|
|
|
|
copy->group = channel_offset;
|
|
|
|
|
copy->size_written = size_written;
|
|
|
|
|
inst->insert_before(block, copy);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
dst = horiz_offset(inst->dst, channel_offset);
|
|
|
|
|
}
|
|
|
|
|
linst->dst = dst;
|
|
|
|
|
|
|
|
|
|
/* Compute split source regions */
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
|
if (linst->src[i].file == BAD_FILE)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!is_uniform(linst->src[i]))
|
|
|
|
|
linst->src[i] = horiz_offset(linst->src[i], channel_offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inst->insert_before(block, linst);
|
|
|
|
|
|
|
|
|
|
/* If we used a temporary to store the result of the split
|
|
|
|
|
* instruction, copy the result to the original destination
|
|
|
|
|
*/
|
|
|
|
|
if (needs_temp) {
|
|
|
|
|
vec4_instruction *mov =
|
|
|
|
|
MOV(offset(inst->dst, lowered_width, n), src_reg(dst));
|
|
|
|
|
mov->exec_size = lowered_width;
|
|
|
|
|
mov->group = channel_offset;
|
|
|
|
|
mov->size_written = size_written;
|
|
|
|
|
mov->predicate = inst->predicate;
|
|
|
|
|
inst->insert_before(block, mov);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inst->remove(block);
|
|
|
|
|
progress = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (progress)
|
|
|
|
|
invalidate_live_intervals();
|
|
|
|
|
|
|
|
|
|
return progress;
|
|
|
|
|
}
|
|
|
|
|
|
i965/vec4: add a scalarization pass for double-precision instructions
The hardware only supports 32-bit swizzles, which means that we can
only access directly channels XY of a DF making access to channels ZW
more difficult, specially considering the various regioning restrictions
imposed by the hardware. The combination of both things makes handling
ramdom swizzles on DF operands rather difficult, as there are many
combinations that can't be represented at all, at least not without
some work and some level of instruction splitting depending on the case.
Writemasks are 64-bit in general, however XY and ZW writemasks also work
in 32-bit, which means these writemasks can't be represented natively,
adding to the complexity.
For now, we decided to try and simplify things as much as possible to
avoid dealing with all this from the get go by adding a scalarization
pass that runs after the main optimization loop. By fully scalarizing
DF instructions in align16 we avoid most of the complexity introduced
by the aforementioned hardware restrictions and we have an easier path
to an initial fully functional version for the vector backend in Haswell
and IvyBridge.
Later, we can improve the implementation so we don't necessarily
scalarize everything, iteratively adding more complexity and building
on top of a framework that is already working. Curro drafted some ideas
for how this could be done here:
https://bugs.freedesktop.org/show_bug.cgi?id=92760#c82
v2:
- Use a copy constructor for the scalar instructions so we copy all
relevant instructions fields from the original instruction.
v3: Fix indention in one switch (Matt)
Reviewed-by: Matt Turner <mattst88@gmail.com>
2016-05-24 09:20:51 +02:00
|
|
|
static brw_predicate
|
|
|
|
|
scalarize_predicate(brw_predicate predicate, unsigned writemask)
|
|
|
|
|
{
|
|
|
|
|
if (predicate != BRW_PREDICATE_NORMAL)
|
|
|
|
|
return predicate;
|
|
|
|
|
|
|
|
|
|
switch (writemask) {
|
|
|
|
|
case WRITEMASK_X:
|
|
|
|
|
return BRW_PREDICATE_ALIGN16_REPLICATE_X;
|
|
|
|
|
case WRITEMASK_Y:
|
|
|
|
|
return BRW_PREDICATE_ALIGN16_REPLICATE_Y;
|
|
|
|
|
case WRITEMASK_Z:
|
|
|
|
|
return BRW_PREDICATE_ALIGN16_REPLICATE_Z;
|
|
|
|
|
case WRITEMASK_W:
|
|
|
|
|
return BRW_PREDICATE_ALIGN16_REPLICATE_W;
|
|
|
|
|
default:
|
|
|
|
|
unreachable("invalid writemask");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-18 11:15:56 +02:00
|
|
|
/* Gen7 has a hardware decompression bug that we can exploit to represent
|
|
|
|
|
* handful of additional swizzles natively.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
is_gen7_supported_64bit_swizzle(vec4_instruction *inst, unsigned arg)
|
|
|
|
|
{
|
|
|
|
|
switch (inst->src[arg].swizzle) {
|
|
|
|
|
case BRW_SWIZZLE_XXXX:
|
|
|
|
|
case BRW_SWIZZLE_YYYY:
|
|
|
|
|
case BRW_SWIZZLE_ZZZZ:
|
|
|
|
|
case BRW_SWIZZLE_WWWW:
|
|
|
|
|
case BRW_SWIZZLE_XYXY:
|
|
|
|
|
case BRW_SWIZZLE_YXYX:
|
|
|
|
|
case BRW_SWIZZLE_ZWZW:
|
|
|
|
|
case BRW_SWIZZLE_WZWZ:
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-19 09:28:04 +02:00
|
|
|
/* 64-bit sources use regions with a width of 2. These 2 elements in each row
|
|
|
|
|
* can be addressed using 32-bit swizzles (which is what the hardware supports)
|
|
|
|
|
* but it also means that the swizzle we apply on the first two components of a
|
|
|
|
|
* dvec4 is coupled with the swizzle we use for the last 2. In other words,
|
|
|
|
|
* only some specific swizzle combinations can be natively supported.
|
|
|
|
|
*
|
|
|
|
|
* FIXME: we can go an step further and implement even more swizzle
|
|
|
|
|
* variations using only partial scalarization.
|
|
|
|
|
*
|
|
|
|
|
* For more details see:
|
|
|
|
|
* https://bugs.freedesktop.org/show_bug.cgi?id=92760#c82
|
|
|
|
|
*/
|
|
|
|
|
bool
|
2016-08-18 11:15:56 +02:00
|
|
|
vec4_visitor::is_supported_64bit_region(vec4_instruction *inst, unsigned arg)
|
2016-07-19 09:28:04 +02:00
|
|
|
{
|
2016-08-18 11:15:56 +02:00
|
|
|
const src_reg &src = inst->src[arg];
|
2016-07-19 09:28:04 +02:00
|
|
|
assert(type_sz(src.type) == 8);
|
|
|
|
|
|
|
|
|
|
/* Uniform regions have a vstride=0. Because we use 2-wide rows with
|
|
|
|
|
* 64-bit regions it means that we cannot access components Z/W, so
|
|
|
|
|
* return false for any such case. Interleaved attributes will also be
|
|
|
|
|
* mapped to GRF registers with a vstride of 0, so apply the same
|
|
|
|
|
* treatment.
|
|
|
|
|
*/
|
|
|
|
|
if ((is_uniform(src) ||
|
|
|
|
|
(stage_uses_interleaved_attributes(stage, prog_data->dispatch_mode) &&
|
|
|
|
|
src.file == ATTR)) &&
|
|
|
|
|
(brw_mask_for_swizzle(src.swizzle) & 12))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
switch (src.swizzle) {
|
|
|
|
|
case BRW_SWIZZLE_XYZW:
|
|
|
|
|
case BRW_SWIZZLE_XXZZ:
|
|
|
|
|
case BRW_SWIZZLE_YYWW:
|
|
|
|
|
case BRW_SWIZZLE_YXWZ:
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
2016-08-18 11:15:56 +02:00
|
|
|
return devinfo->gen == 7 && is_gen7_supported_64bit_swizzle(inst, arg);
|
2016-07-19 09:28:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
i965/vec4: add a scalarization pass for double-precision instructions
The hardware only supports 32-bit swizzles, which means that we can
only access directly channels XY of a DF making access to channels ZW
more difficult, specially considering the various regioning restrictions
imposed by the hardware. The combination of both things makes handling
ramdom swizzles on DF operands rather difficult, as there are many
combinations that can't be represented at all, at least not without
some work and some level of instruction splitting depending on the case.
Writemasks are 64-bit in general, however XY and ZW writemasks also work
in 32-bit, which means these writemasks can't be represented natively,
adding to the complexity.
For now, we decided to try and simplify things as much as possible to
avoid dealing with all this from the get go by adding a scalarization
pass that runs after the main optimization loop. By fully scalarizing
DF instructions in align16 we avoid most of the complexity introduced
by the aforementioned hardware restrictions and we have an easier path
to an initial fully functional version for the vector backend in Haswell
and IvyBridge.
Later, we can improve the implementation so we don't necessarily
scalarize everything, iteratively adding more complexity and building
on top of a framework that is already working. Curro drafted some ideas
for how this could be done here:
https://bugs.freedesktop.org/show_bug.cgi?id=92760#c82
v2:
- Use a copy constructor for the scalar instructions so we copy all
relevant instructions fields from the original instruction.
v3: Fix indention in one switch (Matt)
Reviewed-by: Matt Turner <mattst88@gmail.com>
2016-05-24 09:20:51 +02:00
|
|
|
bool
|
|
|
|
|
vec4_visitor::scalarize_df()
|
|
|
|
|
{
|
|
|
|
|
bool progress = false;
|
|
|
|
|
|
|
|
|
|
foreach_block_and_inst_safe(block, vec4_instruction, inst, cfg) {
|
|
|
|
|
/* Skip DF instructions that operate in Align1 mode */
|
|
|
|
|
if (is_align1_df(inst))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Check if this is a double-precision instruction */
|
|
|
|
|
bool is_double = type_sz(inst->dst.type) == 8;
|
|
|
|
|
for (int arg = 0; !is_double && arg < 3; arg++) {
|
|
|
|
|
is_double = inst->src[arg].file != BAD_FILE &&
|
|
|
|
|
type_sz(inst->src[arg].type) == 8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_double)
|
|
|
|
|
continue;
|
|
|
|
|
|
2016-07-19 09:28:04 +02:00
|
|
|
/* Skip the lowering for specific regioning scenarios that we can
|
|
|
|
|
* support natively.
|
|
|
|
|
*/
|
|
|
|
|
bool skip_lowering = true;
|
|
|
|
|
|
|
|
|
|
/* XY and ZW writemasks operate in 32-bit, which means that they don't
|
|
|
|
|
* have a native 64-bit representation and they should always be split.
|
|
|
|
|
*/
|
|
|
|
|
if (inst->dst.writemask == WRITEMASK_XY ||
|
|
|
|
|
inst->dst.writemask == WRITEMASK_ZW) {
|
|
|
|
|
skip_lowering = false;
|
|
|
|
|
} else {
|
|
|
|
|
for (unsigned i = 0; i < 3; i++) {
|
|
|
|
|
if (inst->src[i].file == BAD_FILE || type_sz(inst->src[i].type) < 8)
|
|
|
|
|
continue;
|
2016-08-18 11:15:56 +02:00
|
|
|
skip_lowering = skip_lowering && is_supported_64bit_region(inst, i);
|
2016-07-19 09:28:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (skip_lowering)
|
|
|
|
|
continue;
|
|
|
|
|
|
i965/vec4: add a scalarization pass for double-precision instructions
The hardware only supports 32-bit swizzles, which means that we can
only access directly channels XY of a DF making access to channels ZW
more difficult, specially considering the various regioning restrictions
imposed by the hardware. The combination of both things makes handling
ramdom swizzles on DF operands rather difficult, as there are many
combinations that can't be represented at all, at least not without
some work and some level of instruction splitting depending on the case.
Writemasks are 64-bit in general, however XY and ZW writemasks also work
in 32-bit, which means these writemasks can't be represented natively,
adding to the complexity.
For now, we decided to try and simplify things as much as possible to
avoid dealing with all this from the get go by adding a scalarization
pass that runs after the main optimization loop. By fully scalarizing
DF instructions in align16 we avoid most of the complexity introduced
by the aforementioned hardware restrictions and we have an easier path
to an initial fully functional version for the vector backend in Haswell
and IvyBridge.
Later, we can improve the implementation so we don't necessarily
scalarize everything, iteratively adding more complexity and building
on top of a framework that is already working. Curro drafted some ideas
for how this could be done here:
https://bugs.freedesktop.org/show_bug.cgi?id=92760#c82
v2:
- Use a copy constructor for the scalar instructions so we copy all
relevant instructions fields from the original instruction.
v3: Fix indention in one switch (Matt)
Reviewed-by: Matt Turner <mattst88@gmail.com>
2016-05-24 09:20:51 +02:00
|
|
|
/* Generate scalar instructions for each enabled channel */
|
|
|
|
|
for (unsigned chan = 0; chan < 4; chan++) {
|
|
|
|
|
unsigned chan_mask = 1 << chan;
|
|
|
|
|
if (!(inst->dst.writemask & chan_mask))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
vec4_instruction *scalar_inst = new(mem_ctx) vec4_instruction(*inst);
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < 3; i++) {
|
|
|
|
|
unsigned swz = BRW_GET_SWZ(inst->src[i].swizzle, chan);
|
|
|
|
|
scalar_inst->src[i].swizzle = BRW_SWIZZLE4(swz, swz, swz, swz);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scalar_inst->dst.writemask = chan_mask;
|
|
|
|
|
|
|
|
|
|
if (inst->predicate != BRW_PREDICATE_NONE) {
|
|
|
|
|
scalar_inst->predicate =
|
|
|
|
|
scalarize_predicate(inst->predicate, chan_mask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inst->insert_before(block, scalar_inst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inst->remove(block);
|
|
|
|
|
progress = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (progress)
|
|
|
|
|
invalidate_live_intervals();
|
|
|
|
|
|
|
|
|
|
return progress;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-08 11:04:34 +02:00
|
|
|
bool
|
|
|
|
|
vec4_visitor::lower_64bit_mad_to_mul_add()
|
|
|
|
|
{
|
|
|
|
|
bool progress = false;
|
|
|
|
|
|
|
|
|
|
foreach_block_and_inst_safe(block, vec4_instruction, inst, cfg) {
|
|
|
|
|
if (inst->opcode != BRW_OPCODE_MAD)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (type_sz(inst->dst.type) != 8)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
dst_reg mul_dst = dst_reg(this, glsl_type::dvec4_type);
|
|
|
|
|
|
|
|
|
|
/* Use the copy constructor so we copy all relevant instruction fields
|
|
|
|
|
* from the original mad into the add and mul instructions
|
|
|
|
|
*/
|
|
|
|
|
vec4_instruction *mul = new(mem_ctx) vec4_instruction(*inst);
|
|
|
|
|
mul->opcode = BRW_OPCODE_MUL;
|
|
|
|
|
mul->dst = mul_dst;
|
|
|
|
|
mul->src[0] = inst->src[1];
|
|
|
|
|
mul->src[1] = inst->src[2];
|
|
|
|
|
mul->src[2].file = BAD_FILE;
|
|
|
|
|
|
|
|
|
|
vec4_instruction *add = new(mem_ctx) vec4_instruction(*inst);
|
|
|
|
|
add->opcode = BRW_OPCODE_ADD;
|
|
|
|
|
add->src[0] = src_reg(mul_dst);
|
|
|
|
|
add->src[1] = inst->src[0];
|
|
|
|
|
add->src[2].file = BAD_FILE;
|
|
|
|
|
|
|
|
|
|
inst->insert_before(block, mul);
|
|
|
|
|
inst->insert_before(block, add);
|
|
|
|
|
inst->remove(block);
|
|
|
|
|
|
|
|
|
|
progress = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (progress)
|
|
|
|
|
invalidate_live_intervals();
|
|
|
|
|
|
|
|
|
|
return progress;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-24 11:01:27 +02:00
|
|
|
/* The align16 hardware can only do 32-bit swizzle channels, so we need to
|
|
|
|
|
* translate the logical 64-bit swizzle channels that we use in the Vec4 IR
|
|
|
|
|
* to 32-bit swizzle channels in hardware registers.
|
|
|
|
|
*
|
|
|
|
|
* @inst and @arg identify the original vec4 IR source operand we need to
|
|
|
|
|
* translate the swizzle for and @hw_reg is the hardware register where we
|
|
|
|
|
* will write the hardware swizzle to use.
|
|
|
|
|
*
|
|
|
|
|
* This pass assumes that Align16/DF instructions have been fully scalarized
|
|
|
|
|
* previously so there is just one 64-bit swizzle channel to deal with for any
|
|
|
|
|
* given Vec4 IR source.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
vec4_visitor::apply_logical_swizzle(struct brw_reg *hw_reg,
|
|
|
|
|
vec4_instruction *inst, int arg)
|
|
|
|
|
{
|
|
|
|
|
src_reg reg = inst->src[arg];
|
|
|
|
|
|
|
|
|
|
if (reg.file == BAD_FILE || reg.file == BRW_IMMEDIATE_VALUE)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* If this is not a 64-bit operand or this is a scalar instruction we don't
|
|
|
|
|
* need to do anything about the swizzles.
|
|
|
|
|
*/
|
|
|
|
|
if(type_sz(reg.type) < 8 || is_align1_df(inst)) {
|
|
|
|
|
hw_reg->swizzle = reg.swizzle;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-19 09:28:04 +02:00
|
|
|
/* Take the 64-bit logical swizzle channel and translate it to 32-bit */
|
|
|
|
|
assert(brw_is_single_value_swizzle(reg.swizzle) ||
|
2016-08-18 11:15:56 +02:00
|
|
|
is_supported_64bit_region(inst, arg));
|
2016-05-24 11:01:27 +02:00
|
|
|
|
2017-04-25 12:18:17 +02:00
|
|
|
/* Apply the region <2, 2, 1> for GRF or <0, 2, 1> for uniforms, as align16
|
|
|
|
|
* HW can only do 32-bit swizzle channels.
|
|
|
|
|
*/
|
|
|
|
|
hw_reg->width = BRW_WIDTH_2;
|
|
|
|
|
|
2016-08-18 11:15:56 +02:00
|
|
|
if (is_supported_64bit_region(inst, arg) &&
|
|
|
|
|
!is_gen7_supported_64bit_swizzle(inst, arg)) {
|
2016-07-19 09:28:04 +02:00
|
|
|
/* Supported 64-bit swizzles are those such that their first two
|
|
|
|
|
* components, when expanded to 32-bit swizzles, match the semantics
|
|
|
|
|
* of the original 64-bit swizzle with 2-wide row regioning.
|
|
|
|
|
*/
|
|
|
|
|
unsigned swizzle0 = BRW_GET_SWZ(reg.swizzle, 0);
|
|
|
|
|
unsigned swizzle1 = BRW_GET_SWZ(reg.swizzle, 1);
|
|
|
|
|
hw_reg->swizzle = BRW_SWIZZLE4(swizzle0 * 2, swizzle0 * 2 + 1,
|
|
|
|
|
swizzle1 * 2, swizzle1 * 2 + 1);
|
|
|
|
|
} else {
|
2016-08-18 11:15:56 +02:00
|
|
|
/* If we got here then we have one of the following:
|
|
|
|
|
*
|
|
|
|
|
* 1. An unsupported swizzle, which should be single-value thanks to the
|
|
|
|
|
* scalarization pass.
|
|
|
|
|
*
|
|
|
|
|
* 2. A gen7 supported swizzle. These can be single-value or double-value
|
|
|
|
|
* swizzles. If the latter, they are never cross-dvec2 channels. For
|
|
|
|
|
* these we always need to activate the gen7 vstride=0 exploit.
|
2016-07-19 09:28:04 +02:00
|
|
|
*/
|
2016-08-18 11:15:56 +02:00
|
|
|
unsigned swizzle0 = BRW_GET_SWZ(reg.swizzle, 0);
|
|
|
|
|
unsigned swizzle1 = BRW_GET_SWZ(reg.swizzle, 1);
|
|
|
|
|
assert((swizzle0 < 2) == (swizzle1 < 2));
|
i965/vec4: implement access to DF source components Z/W
The general idea is that with 32-bit swizzles we cannot address DF
components Z/W directly, so instead we select the region that starts
at the the 16B offset into the register and use X/Y swizzles.
The above, however, has the caveat that we can't do that without
violating register region restrictions unless we probably do some
sort of SIMD splitting.
Alternatively, we can accomplish what we need without SIMD splitting
by exploiting the gen7 hardware decompression bug for instructions
with a vstride=0. For example, an instruction like this:
mov(8) r2.x:DF r0.2<0>xyzw:DF
Activates the hardware bug and produces this region:
Component: x0 y0 z0 w0 x1 y1 z1 w1
Register: r0.2 r0.3 r0.2 r0.3 r1.2 r1.3 r1.2 r1.3
Where r0.2 and r0.3 are r0.z:DF for the first vertex of the SIMD4x2
execution and r1.2 and r1.3 are the same for the second vertex.
Using this to our advantage we can select r0.z:DF by doing
r0.2<0,2,1>.xyxy and r0.w by doing r0.2<0,2,1>.zwzw without needing
to split the instruction.
Of course, this only works for gen7, but that is the only hardware
platform were we implement align16/fp64 at the moment.
v2: Adapted to the fact that we now do this after converting to
hardware registers (Iago)
Reviewed-by: Matt Turner <mattst88@gmail.com>
2016-07-18 13:43:00 +02:00
|
|
|
|
2016-07-19 09:28:04 +02:00
|
|
|
/* To gain access to Z/W components we need to select the second half
|
|
|
|
|
* of the register and then use a X/Y swizzle to select Z/W respectively.
|
|
|
|
|
*/
|
2016-08-18 11:15:56 +02:00
|
|
|
if (swizzle0 >= 2) {
|
2016-07-19 09:28:04 +02:00
|
|
|
*hw_reg = suboffset(*hw_reg, 2);
|
2016-08-18 11:15:56 +02:00
|
|
|
swizzle0 -= 2;
|
|
|
|
|
swizzle1 -= 2;
|
2016-07-19 09:28:04 +02:00
|
|
|
}
|
i965/vec4: implement access to DF source components Z/W
The general idea is that with 32-bit swizzles we cannot address DF
components Z/W directly, so instead we select the region that starts
at the the 16B offset into the register and use X/Y swizzles.
The above, however, has the caveat that we can't do that without
violating register region restrictions unless we probably do some
sort of SIMD splitting.
Alternatively, we can accomplish what we need without SIMD splitting
by exploiting the gen7 hardware decompression bug for instructions
with a vstride=0. For example, an instruction like this:
mov(8) r2.x:DF r0.2<0>xyzw:DF
Activates the hardware bug and produces this region:
Component: x0 y0 z0 w0 x1 y1 z1 w1
Register: r0.2 r0.3 r0.2 r0.3 r1.2 r1.3 r1.2 r1.3
Where r0.2 and r0.3 are r0.z:DF for the first vertex of the SIMD4x2
execution and r1.2 and r1.3 are the same for the second vertex.
Using this to our advantage we can select r0.z:DF by doing
r0.2<0,2,1>.xyxy and r0.w by doing r0.2<0,2,1>.zwzw without needing
to split the instruction.
Of course, this only works for gen7, but that is the only hardware
platform were we implement align16/fp64 at the moment.
v2: Adapted to the fact that we now do this after converting to
hardware registers (Iago)
Reviewed-by: Matt Turner <mattst88@gmail.com>
2016-07-18 13:43:00 +02:00
|
|
|
|
2016-08-18 11:15:56 +02:00
|
|
|
/* All gen7-specific supported swizzles require the vstride=0 exploit */
|
|
|
|
|
if (devinfo->gen == 7 && is_gen7_supported_64bit_swizzle(inst, arg))
|
|
|
|
|
hw_reg->vstride = BRW_VERTICAL_STRIDE_0;
|
|
|
|
|
|
2016-07-19 09:28:04 +02:00
|
|
|
/* Any 64-bit source with an offset at 16B is intended to address the
|
|
|
|
|
* second half of a register and needs a vertical stride of 0 so we:
|
|
|
|
|
*
|
|
|
|
|
* 1. Don't violate register region restrictions.
|
|
|
|
|
* 2. Activate the gen7 instruction decompresion bug exploit when
|
|
|
|
|
* execsize > 4
|
|
|
|
|
*/
|
|
|
|
|
if (hw_reg->subnr % REG_SIZE == 16) {
|
|
|
|
|
assert(devinfo->gen == 7);
|
|
|
|
|
hw_reg->vstride = BRW_VERTICAL_STRIDE_0;
|
|
|
|
|
}
|
i965/vec4: implement access to DF source components Z/W
The general idea is that with 32-bit swizzles we cannot address DF
components Z/W directly, so instead we select the region that starts
at the the 16B offset into the register and use X/Y swizzles.
The above, however, has the caveat that we can't do that without
violating register region restrictions unless we probably do some
sort of SIMD splitting.
Alternatively, we can accomplish what we need without SIMD splitting
by exploiting the gen7 hardware decompression bug for instructions
with a vstride=0. For example, an instruction like this:
mov(8) r2.x:DF r0.2<0>xyzw:DF
Activates the hardware bug and produces this region:
Component: x0 y0 z0 w0 x1 y1 z1 w1
Register: r0.2 r0.3 r0.2 r0.3 r1.2 r1.3 r1.2 r1.3
Where r0.2 and r0.3 are r0.z:DF for the first vertex of the SIMD4x2
execution and r1.2 and r1.3 are the same for the second vertex.
Using this to our advantage we can select r0.z:DF by doing
r0.2<0,2,1>.xyxy and r0.w by doing r0.2<0,2,1>.zwzw without needing
to split the instruction.
Of course, this only works for gen7, but that is the only hardware
platform were we implement align16/fp64 at the moment.
v2: Adapted to the fact that we now do this after converting to
hardware registers (Iago)
Reviewed-by: Matt Turner <mattst88@gmail.com>
2016-07-18 13:43:00 +02:00
|
|
|
|
2016-08-18 11:15:56 +02:00
|
|
|
hw_reg->swizzle = BRW_SWIZZLE4(swizzle0 * 2, swizzle0 * 2 + 1,
|
|
|
|
|
swizzle1 * 2, swizzle1 * 2 + 1);
|
2016-07-19 09:28:04 +02:00
|
|
|
}
|
2016-05-24 11:01:27 +02:00
|
|
|
}
|
|
|
|
|
|
2012-11-26 21:46:27 -08:00
|
|
|
bool
|
2015-08-27 23:49:03 -07:00
|
|
|
vec4_visitor::run()
|
2012-11-26 21:46:27 -08:00
|
|
|
{
|
2015-06-19 15:40:09 -07:00
|
|
|
if (shader_time_index >= 0)
|
2012-11-27 14:10:52 -08:00
|
|
|
emit_shader_time_begin();
|
|
|
|
|
|
2013-02-17 08:05:52 -08:00
|
|
|
emit_prolog();
|
2012-11-26 21:46:27 -08:00
|
|
|
|
2015-09-21 10:42:19 -07:00
|
|
|
emit_nir_code();
|
|
|
|
|
if (failed)
|
|
|
|
|
return false;
|
2012-11-29 16:51:13 -08:00
|
|
|
base_ir = NULL;
|
2012-11-26 21:46:27 -08:00
|
|
|
|
2013-02-17 08:05:52 -08:00
|
|
|
emit_thread_end();
|
2012-11-26 21:46:27 -08:00
|
|
|
|
2014-09-01 10:58:11 -07:00
|
|
|
calculate_cfg();
|
|
|
|
|
|
2012-11-26 21:46:27 -08:00
|
|
|
/* Before any optimization, push array accesses out to scratch
|
|
|
|
|
* space where we need them to be. This pass may allocate new
|
|
|
|
|
* virtual GRFs, so we want to do it early. It also makes sure
|
|
|
|
|
* that we have reladdr computations available for CSE, since we'll
|
|
|
|
|
* often do repeated subexpressions for those.
|
|
|
|
|
*/
|
2015-09-21 10:42:19 -07:00
|
|
|
move_grf_array_access_to_scratch();
|
|
|
|
|
move_uniform_array_access_to_pull_constants();
|
|
|
|
|
|
2012-11-26 21:46:27 -08:00
|
|
|
pack_uniform_registers();
|
|
|
|
|
move_push_constants_to_pull_constants();
|
|
|
|
|
split_virtual_grfs();
|
|
|
|
|
|
2014-12-31 16:47:25 -08:00
|
|
|
#define OPT(pass, args...) ({ \
|
2014-07-21 20:06:23 -07:00
|
|
|
pass_num++; \
|
|
|
|
|
bool this_progress = pass(args); \
|
|
|
|
|
\
|
|
|
|
|
if (unlikely(INTEL_DEBUG & DEBUG_OPTIMIZER) && this_progress) { \
|
|
|
|
|
char filename[64]; \
|
2015-10-01 15:22:23 -07:00
|
|
|
snprintf(filename, 64, "%s-%s-%02d-%02d-" #pass, \
|
2017-05-08 09:20:21 -07:00
|
|
|
stage_abbrev, nir->info.name, iteration, pass_num); \
|
2014-07-21 20:06:23 -07:00
|
|
|
\
|
2015-05-20 09:44:01 -07:00
|
|
|
backend_shader::dump_instructions(filename); \
|
2014-07-21 20:06:23 -07:00
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
progress = progress || this_progress; \
|
2014-12-31 16:47:25 -08:00
|
|
|
this_progress; \
|
|
|
|
|
})
|
2014-07-21 20:06:23 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if (unlikely(INTEL_DEBUG & DEBUG_OPTIMIZER)) {
|
|
|
|
|
char filename[64];
|
2016-02-11 10:55:48 -08:00
|
|
|
snprintf(filename, 64, "%s-%s-00-00-start",
|
2017-05-08 09:20:21 -07:00
|
|
|
stage_abbrev, nir->info.name);
|
2014-07-21 20:06:23 -07:00
|
|
|
|
2015-05-20 09:44:01 -07:00
|
|
|
backend_shader::dump_instructions(filename);
|
2014-07-21 20:06:23 -07:00
|
|
|
}
|
|
|
|
|
|
2012-11-26 21:46:27 -08:00
|
|
|
bool progress;
|
2014-07-21 20:06:23 -07:00
|
|
|
int iteration = 0;
|
2014-12-31 16:47:25 -08:00
|
|
|
int pass_num = 0;
|
2012-11-26 21:46:27 -08:00
|
|
|
do {
|
|
|
|
|
progress = false;
|
2014-12-31 16:47:25 -08:00
|
|
|
pass_num = 0;
|
2014-07-21 20:06:23 -07:00
|
|
|
iteration++;
|
|
|
|
|
|
2015-10-02 20:30:41 -07:00
|
|
|
OPT(opt_predicated_break, this);
|
2014-08-17 15:13:54 -07:00
|
|
|
OPT(opt_reduce_swizzle);
|
2014-07-21 20:06:23 -07:00
|
|
|
OPT(dead_code_eliminate);
|
|
|
|
|
OPT(dead_control_flow_eliminate, this);
|
|
|
|
|
OPT(opt_copy_propagation);
|
2015-09-28 17:00:19 +02:00
|
|
|
OPT(opt_cmod_propagation);
|
2014-07-21 20:06:23 -07:00
|
|
|
OPT(opt_cse);
|
i965/vec4: Call opt_algebraic after opt_cse.
The next patch adds an algebraic optimization for the pattern
sqrt a, b
rcp c, a
and turns it into
sqrt a, b
rsq c, b
but many vertex shaders do
a = sqrt(b);
var1 /= a;
var2 /= a;
which generates
sqrt a, b
rcp c, a
rcp d, a
If we apply the algebraic optimization before CSE, we'll end up with
sqrt a, b
rsq c, b
rcp d, a
Applying CSE combines the RCP instructions, preventing this from
happening.
No shader-db changes.
Reviewed-by: Anuj Phogat <anuj.phogat@gmail.com>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2014-09-27 10:34:07 -07:00
|
|
|
OPT(opt_algebraic);
|
2014-07-21 20:06:23 -07:00
|
|
|
OPT(opt_register_coalesce);
|
2015-02-20 20:25:04 +02:00
|
|
|
OPT(eliminate_find_live_channel);
|
2012-11-26 21:46:27 -08:00
|
|
|
} while (progress);
|
|
|
|
|
|
2014-12-31 16:47:25 -08:00
|
|
|
pass_num = 0;
|
|
|
|
|
|
|
|
|
|
if (OPT(opt_vector_float)) {
|
|
|
|
|
OPT(opt_cse);
|
|
|
|
|
OPT(opt_copy_propagation, false);
|
|
|
|
|
OPT(opt_copy_propagation, true);
|
|
|
|
|
OPT(dead_code_eliminate);
|
2014-12-20 13:17:00 -08:00
|
|
|
}
|
2012-11-26 21:46:27 -08:00
|
|
|
|
2016-02-11 12:27:02 -08:00
|
|
|
if (devinfo->gen <= 5 && OPT(lower_minmax)) {
|
|
|
|
|
OPT(opt_cmod_propagation);
|
|
|
|
|
OPT(opt_cse);
|
|
|
|
|
OPT(opt_copy_propagation);
|
|
|
|
|
OPT(dead_code_eliminate);
|
2016-08-29 10:41:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (OPT(lower_simd_width)) {
|
|
|
|
|
OPT(opt_copy_propagation);
|
|
|
|
|
OPT(dead_code_eliminate);
|
2016-02-11 12:27:02 -08:00
|
|
|
}
|
|
|
|
|
|
2012-11-26 21:46:27 -08:00
|
|
|
if (failed)
|
|
|
|
|
return false;
|
|
|
|
|
|
2016-06-08 11:04:34 +02:00
|
|
|
OPT(lower_64bit_mad_to_mul_add);
|
2016-09-01 12:01:02 +02:00
|
|
|
|
|
|
|
|
/* Run this before payload setup because tesselation shaders
|
|
|
|
|
* rely on it to prevent cross dvec2 regioning on DF attributes
|
|
|
|
|
* that are setup so that XY are on the second half of register and
|
|
|
|
|
* ZW are in the first half of the next.
|
|
|
|
|
*/
|
i965/vec4: add a scalarization pass for double-precision instructions
The hardware only supports 32-bit swizzles, which means that we can
only access directly channels XY of a DF making access to channels ZW
more difficult, specially considering the various regioning restrictions
imposed by the hardware. The combination of both things makes handling
ramdom swizzles on DF operands rather difficult, as there are many
combinations that can't be represented at all, at least not without
some work and some level of instruction splitting depending on the case.
Writemasks are 64-bit in general, however XY and ZW writemasks also work
in 32-bit, which means these writemasks can't be represented natively,
adding to the complexity.
For now, we decided to try and simplify things as much as possible to
avoid dealing with all this from the get go by adding a scalarization
pass that runs after the main optimization loop. By fully scalarizing
DF instructions in align16 we avoid most of the complexity introduced
by the aforementioned hardware restrictions and we have an easier path
to an initial fully functional version for the vector backend in Haswell
and IvyBridge.
Later, we can improve the implementation so we don't necessarily
scalarize everything, iteratively adding more complexity and building
on top of a framework that is already working. Curro drafted some ideas
for how this could be done here:
https://bugs.freedesktop.org/show_bug.cgi?id=92760#c82
v2:
- Use a copy constructor for the scalar instructions so we copy all
relevant instructions fields from the original instruction.
v3: Fix indention in one switch (Matt)
Reviewed-by: Matt Turner <mattst88@gmail.com>
2016-05-24 09:20:51 +02:00
|
|
|
OPT(scalarize_df);
|
|
|
|
|
|
2012-11-26 21:46:27 -08:00
|
|
|
setup_payload();
|
|
|
|
|
|
2015-07-23 11:11:53 +02:00
|
|
|
if (unlikely(INTEL_DEBUG & DEBUG_SPILL_VEC4)) {
|
2012-11-26 21:46:27 -08:00
|
|
|
/* Debug of register spilling: Go spill everything. */
|
2015-02-10 15:51:34 +02:00
|
|
|
const int grf_count = alloc.count;
|
|
|
|
|
float spill_costs[alloc.count];
|
|
|
|
|
bool no_spill[alloc.count];
|
2012-11-26 21:46:27 -08:00
|
|
|
evaluate_spill_costs(spill_costs, no_spill);
|
|
|
|
|
for (int i = 0; i < grf_count; i++) {
|
|
|
|
|
if (no_spill[i])
|
|
|
|
|
continue;
|
|
|
|
|
spill_reg(i);
|
|
|
|
|
}
|
2016-09-01 12:01:02 +02:00
|
|
|
|
|
|
|
|
/* We want to run this after spilling because 64-bit (un)spills need to
|
|
|
|
|
* emit code to shuffle 64-bit data for the 32-bit scratch read/write
|
|
|
|
|
* messages that can produce unsupported 64-bit swizzle regions.
|
|
|
|
|
*/
|
|
|
|
|
OPT(scalarize_df);
|
2012-11-26 21:46:27 -08:00
|
|
|
}
|
|
|
|
|
|
2015-06-28 20:45:47 -07:00
|
|
|
bool allocated_without_spills = reg_allocate();
|
|
|
|
|
|
|
|
|
|
if (!allocated_without_spills) {
|
|
|
|
|
compiler->shader_perf_log(log_data,
|
|
|
|
|
"%s shader triggered register spilling. "
|
|
|
|
|
"Try reducing the number of live vec4 values "
|
|
|
|
|
"to improve performance.\n",
|
|
|
|
|
stage_name);
|
|
|
|
|
|
|
|
|
|
while (!reg_allocate()) {
|
|
|
|
|
if (failed)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2016-09-01 12:01:02 +02:00
|
|
|
|
|
|
|
|
/* We want to run this after spilling because 64-bit (un)spills need to
|
|
|
|
|
* emit code to shuffle 64-bit data for the 32-bit scratch read/write
|
|
|
|
|
* messages that can produce unsupported 64-bit swizzle regions.
|
|
|
|
|
*/
|
|
|
|
|
OPT(scalarize_df);
|
2012-11-26 21:46:27 -08:00
|
|
|
}
|
|
|
|
|
|
2012-11-30 16:13:34 -08:00
|
|
|
opt_schedule_instructions();
|
|
|
|
|
|
2012-11-30 18:29:34 -08:00
|
|
|
opt_set_dependency_control();
|
|
|
|
|
|
2015-10-22 16:04:15 -07:00
|
|
|
convert_to_hw_regs();
|
|
|
|
|
|
2015-06-28 21:02:15 -07:00
|
|
|
if (last_scratch > 0) {
|
2015-06-28 20:55:25 -07:00
|
|
|
prog_data->base.total_scratch =
|
2015-06-28 21:02:15 -07:00
|
|
|
brw_get_scratch_size(last_scratch * REG_SIZE);
|
2015-06-28 20:55:25 -07:00
|
|
|
}
|
|
|
|
|
|
2012-11-26 21:46:27 -08:00
|
|
|
return !failed;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-16 15:09:48 -07:00
|
|
|
} /* namespace brw */
|
2012-11-26 21:46:27 -08:00
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
|
2012-11-26 23:59:32 -08:00
|
|
|
/**
|
|
|
|
|
* Compile a vertex shader.
|
|
|
|
|
*
|
|
|
|
|
* Returns the final assembly and the program's size.
|
|
|
|
|
*/
|
|
|
|
|
const unsigned *
|
2015-10-08 16:20:34 -07:00
|
|
|
brw_compile_vs(const struct brw_compiler *compiler, void *log_data,
|
|
|
|
|
void *mem_ctx,
|
|
|
|
|
const struct brw_vs_prog_key *key,
|
|
|
|
|
struct brw_vs_prog_data *prog_data,
|
2015-11-11 10:04:43 -08:00
|
|
|
const nir_shader *src_shader,
|
2015-10-08 16:20:34 -07:00
|
|
|
gl_clip_plane *clip_planes,
|
|
|
|
|
bool use_legacy_snorm_formula,
|
|
|
|
|
int shader_time_index,
|
|
|
|
|
unsigned *final_assembly_size,
|
|
|
|
|
char **error_str)
|
2012-11-26 21:46:27 -08:00
|
|
|
{
|
2016-01-13 15:23:48 -08:00
|
|
|
const bool is_scalar = compiler->scalar_stage[MESA_SHADER_VERTEX];
|
2015-11-11 10:04:43 -08:00
|
|
|
nir_shader *shader = nir_shader_clone(mem_ctx, src_shader);
|
2016-09-15 12:20:38 +10:00
|
|
|
shader = brw_nir_apply_sampler_key(shader, compiler, &key->tex, is_scalar);
|
2015-11-11 10:04:43 -08:00
|
|
|
|
2014-10-20 23:29:41 -07:00
|
|
|
const unsigned *assembly = NULL;
|
2012-11-26 21:46:27 -08:00
|
|
|
|
2017-05-09 12:34:10 -07:00
|
|
|
if (prog_data->base.vue_map.varying_to_slot[VARYING_SLOT_EDGE] != -1) {
|
|
|
|
|
/* If the output VUE map contains VARYING_SLOT_EDGE then we need to copy
|
|
|
|
|
* the edge flag from VERT_ATTRIB_EDGEFLAG. This will be done
|
|
|
|
|
* automatically by brw_vec4_visitor::emit_urb_slot but we need to
|
|
|
|
|
* ensure that prog_data->inputs_read is accurate.
|
2017-05-03 17:40:54 -07:00
|
|
|
*
|
|
|
|
|
* In order to make late NIR passes aware of the change, we actually
|
|
|
|
|
* whack shader->info.inputs_read instead. This is safe because we just
|
|
|
|
|
* made a copy of the shader.
|
2017-05-09 12:34:10 -07:00
|
|
|
*/
|
|
|
|
|
assert(!is_scalar);
|
|
|
|
|
assert(key->copy_edgeflag);
|
2017-05-03 17:40:54 -07:00
|
|
|
shader->info.inputs_read |= VERT_BIT_EDGEFLAG;
|
2017-05-09 12:34:10 -07:00
|
|
|
}
|
|
|
|
|
|
2017-05-04 14:50:20 -07:00
|
|
|
prog_data->inputs_read = shader->info.inputs_read;
|
|
|
|
|
prog_data->double_inputs_read = shader->info.double_inputs_read;
|
|
|
|
|
|
2017-05-03 17:40:54 -07:00
|
|
|
brw_nir_lower_vs_inputs(shader, use_legacy_snorm_formula,
|
|
|
|
|
key->gl_attrib_wa_flags);
|
|
|
|
|
brw_nir_lower_vue_outputs(shader, is_scalar);
|
|
|
|
|
shader = brw_postprocess_nir(shader, compiler, is_scalar);
|
|
|
|
|
|
2016-10-03 23:41:09 -07:00
|
|
|
prog_data->base.clip_distance_mask =
|
2017-05-08 09:20:21 -07:00
|
|
|
((1 << shader->info.clip_distance_array_size) - 1);
|
2016-10-03 23:36:30 -07:00
|
|
|
prog_data->base.cull_distance_mask =
|
2017-05-08 09:20:21 -07:00
|
|
|
((1 << shader->info.cull_distance_array_size) - 1) <<
|
|
|
|
|
shader->info.clip_distance_array_size;
|
2016-10-03 23:36:30 -07:00
|
|
|
|
nir/i965: use two slots from inputs_read for dvec3/dvec4 vertex input attributes
So far, input_reads was a bitmap tracking which vertex input locations
were being used.
In OpenGL, an attribute bigger than a vec4 (like a dvec3 or dvec4)
consumes just one location, any other small attribute. So we mark the
proper bit in inputs_read, and also the same bit in double_inputs_read
if the attribute is a dvec3/dvec4.
But in Vulkan, this is slightly different: a dvec3/dvec4 attribute
consumes two locations, not just one. And hence two bits would be marked
in inputs_read for the same vertex input attribute.
To avoid handling two different situations in NIR, we just choose the
latest one: in OpenGL, when creating NIR from GLSL/IR, any dvec3/dvec4
vertex input attribute is marked with two bits in the inputs_read bitmap
(and also in the double_inputs_read), and following attributes are
adjusted accordingly.
As example, if in our GLSL/IR shader we have three attributes:
layout(location = 0) vec3 attr0;
layout(location = 1) dvec4 attr1;
layout(location = 2) dvec3 attr2;
then in our NIR shader we put attr0 in location 0, attr1 in locations 1
and 2, and attr2 in location 3 and 4.
Checking carefully, basically we are using slots rather than locations
in NIR.
When emitting the vertices, we do a inverse map to know the
corresponding location for each slot.
v2 (Jason):
- use two slots from inputs_read for dvec3/dvec4 NIR from GLSL/IR.
v3 (Jason):
- Fix commit log error.
- Use ladder ifs and fix braces.
- elements_double is divisible by 2, don't need DIV_ROUND_UP().
- Use if ladder instead of a switch.
- Add comment about hardware restriction in 64bit vertex attributes.
Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
2016-12-16 10:24:43 +01:00
|
|
|
unsigned nr_attribute_slots = _mesa_bitcount_64(prog_data->inputs_read);
|
2015-10-15 11:39:06 -07:00
|
|
|
|
|
|
|
|
/* gl_VertexID and gl_InstanceID are system values, but arrive via an
|
|
|
|
|
* incoming vertex attribute. So, add an extra slot.
|
|
|
|
|
*/
|
2017-05-08 09:20:21 -07:00
|
|
|
if (shader->info.system_values_read &
|
2015-12-10 12:24:50 -08:00
|
|
|
(BITFIELD64_BIT(SYSTEM_VALUE_BASE_VERTEX) |
|
|
|
|
|
BITFIELD64_BIT(SYSTEM_VALUE_BASE_INSTANCE) |
|
|
|
|
|
BITFIELD64_BIT(SYSTEM_VALUE_VERTEX_ID_ZERO_BASE) |
|
2015-10-15 11:39:06 -07:00
|
|
|
BITFIELD64_BIT(SYSTEM_VALUE_INSTANCE_ID))) {
|
nir/i965: use two slots from inputs_read for dvec3/dvec4 vertex input attributes
So far, input_reads was a bitmap tracking which vertex input locations
were being used.
In OpenGL, an attribute bigger than a vec4 (like a dvec3 or dvec4)
consumes just one location, any other small attribute. So we mark the
proper bit in inputs_read, and also the same bit in double_inputs_read
if the attribute is a dvec3/dvec4.
But in Vulkan, this is slightly different: a dvec3/dvec4 attribute
consumes two locations, not just one. And hence two bits would be marked
in inputs_read for the same vertex input attribute.
To avoid handling two different situations in NIR, we just choose the
latest one: in OpenGL, when creating NIR from GLSL/IR, any dvec3/dvec4
vertex input attribute is marked with two bits in the inputs_read bitmap
(and also in the double_inputs_read), and following attributes are
adjusted accordingly.
As example, if in our GLSL/IR shader we have three attributes:
layout(location = 0) vec3 attr0;
layout(location = 1) dvec4 attr1;
layout(location = 2) dvec3 attr2;
then in our NIR shader we put attr0 in location 0, attr1 in locations 1
and 2, and attr2 in location 3 and 4.
Checking carefully, basically we are using slots rather than locations
in NIR.
When emitting the vertices, we do a inverse map to know the
corresponding location for each slot.
v2 (Jason):
- use two slots from inputs_read for dvec3/dvec4 NIR from GLSL/IR.
v3 (Jason):
- Fix commit log error.
- Use ladder ifs and fix braces.
- elements_double is divisible by 2, don't need DIV_ROUND_UP().
- Use if ladder instead of a switch.
- Add comment about hardware restriction in 64bit vertex attributes.
Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
2016-12-16 10:24:43 +01:00
|
|
|
nr_attribute_slots++;
|
2015-10-15 11:39:06 -07:00
|
|
|
}
|
|
|
|
|
|
2017-05-03 17:24:43 -07:00
|
|
|
if (shader->info.system_values_read &
|
|
|
|
|
BITFIELD64_BIT(SYSTEM_VALUE_BASE_VERTEX))
|
|
|
|
|
prog_data->uses_basevertex = true;
|
|
|
|
|
|
|
|
|
|
if (shader->info.system_values_read &
|
|
|
|
|
BITFIELD64_BIT(SYSTEM_VALUE_BASE_INSTANCE))
|
|
|
|
|
prog_data->uses_baseinstance = true;
|
|
|
|
|
|
|
|
|
|
if (shader->info.system_values_read &
|
|
|
|
|
BITFIELD64_BIT(SYSTEM_VALUE_VERTEX_ID_ZERO_BASE))
|
|
|
|
|
prog_data->uses_vertexid = true;
|
|
|
|
|
|
|
|
|
|
if (shader->info.system_values_read &
|
|
|
|
|
BITFIELD64_BIT(SYSTEM_VALUE_INSTANCE_ID))
|
|
|
|
|
prog_data->uses_instanceid = true;
|
|
|
|
|
|
2015-12-10 12:27:38 -08:00
|
|
|
/* gl_DrawID has its very own vec4 */
|
2017-05-08 09:20:21 -07:00
|
|
|
if (shader->info.system_values_read &
|
2016-10-13 11:41:23 +11:00
|
|
|
BITFIELD64_BIT(SYSTEM_VALUE_DRAW_ID)) {
|
2017-05-03 17:24:43 -07:00
|
|
|
prog_data->uses_drawid = true;
|
nir/i965: use two slots from inputs_read for dvec3/dvec4 vertex input attributes
So far, input_reads was a bitmap tracking which vertex input locations
were being used.
In OpenGL, an attribute bigger than a vec4 (like a dvec3 or dvec4)
consumes just one location, any other small attribute. So we mark the
proper bit in inputs_read, and also the same bit in double_inputs_read
if the attribute is a dvec3/dvec4.
But in Vulkan, this is slightly different: a dvec3/dvec4 attribute
consumes two locations, not just one. And hence two bits would be marked
in inputs_read for the same vertex input attribute.
To avoid handling two different situations in NIR, we just choose the
latest one: in OpenGL, when creating NIR from GLSL/IR, any dvec3/dvec4
vertex input attribute is marked with two bits in the inputs_read bitmap
(and also in the double_inputs_read), and following attributes are
adjusted accordingly.
As example, if in our GLSL/IR shader we have three attributes:
layout(location = 0) vec3 attr0;
layout(location = 1) dvec4 attr1;
layout(location = 2) dvec3 attr2;
then in our NIR shader we put attr0 in location 0, attr1 in locations 1
and 2, and attr2 in location 3 and 4.
Checking carefully, basically we are using slots rather than locations
in NIR.
When emitting the vertices, we do a inverse map to know the
corresponding location for each slot.
v2 (Jason):
- use two slots from inputs_read for dvec3/dvec4 NIR from GLSL/IR.
v3 (Jason):
- Fix commit log error.
- Use ladder ifs and fix braces.
- elements_double is divisible by 2, don't need DIV_ROUND_UP().
- Use if ladder instead of a switch.
- Add comment about hardware restriction in 64bit vertex attributes.
Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
2016-12-16 10:24:43 +01:00
|
|
|
nr_attribute_slots++;
|
2015-12-10 12:27:38 -08:00
|
|
|
}
|
|
|
|
|
|
nir/i965: use two slots from inputs_read for dvec3/dvec4 vertex input attributes
So far, input_reads was a bitmap tracking which vertex input locations
were being used.
In OpenGL, an attribute bigger than a vec4 (like a dvec3 or dvec4)
consumes just one location, any other small attribute. So we mark the
proper bit in inputs_read, and also the same bit in double_inputs_read
if the attribute is a dvec3/dvec4.
But in Vulkan, this is slightly different: a dvec3/dvec4 attribute
consumes two locations, not just one. And hence two bits would be marked
in inputs_read for the same vertex input attribute.
To avoid handling two different situations in NIR, we just choose the
latest one: in OpenGL, when creating NIR from GLSL/IR, any dvec3/dvec4
vertex input attribute is marked with two bits in the inputs_read bitmap
(and also in the double_inputs_read), and following attributes are
adjusted accordingly.
As example, if in our GLSL/IR shader we have three attributes:
layout(location = 0) vec3 attr0;
layout(location = 1) dvec4 attr1;
layout(location = 2) dvec3 attr2;
then in our NIR shader we put attr0 in location 0, attr1 in locations 1
and 2, and attr2 in location 3 and 4.
Checking carefully, basically we are using slots rather than locations
in NIR.
When emitting the vertices, we do a inverse map to know the
corresponding location for each slot.
v2 (Jason):
- use two slots from inputs_read for dvec3/dvec4 NIR from GLSL/IR.
v3 (Jason):
- Fix commit log error.
- Use ladder ifs and fix braces.
- elements_double is divisible by 2, don't need DIV_ROUND_UP().
- Use if ladder instead of a switch.
- Add comment about hardware restriction in 64bit vertex attributes.
Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
2016-12-16 10:24:43 +01:00
|
|
|
unsigned nr_attributes = nr_attribute_slots -
|
2017-05-08 09:20:21 -07:00
|
|
|
DIV_ROUND_UP(_mesa_bitcount_64(shader->info.double_inputs_read), 2);
|
2016-04-04 12:47:57 +02:00
|
|
|
|
2015-10-15 11:39:06 -07:00
|
|
|
/* The 3DSTATE_VS documentation lists the lower bound on "Vertex URB Entry
|
|
|
|
|
* Read Length" as 1 in vec4 mode, and 0 in SIMD8 mode. Empirically, in
|
|
|
|
|
* vec4 mode, the hardware appears to wedge unless we read something.
|
|
|
|
|
*/
|
2016-01-13 15:23:48 -08:00
|
|
|
if (is_scalar)
|
2016-04-04 12:47:57 +02:00
|
|
|
prog_data->base.urb_read_length =
|
|
|
|
|
DIV_ROUND_UP(nr_attribute_slots, 2);
|
2015-10-15 11:39:06 -07:00
|
|
|
else
|
2016-04-04 12:47:57 +02:00
|
|
|
prog_data->base.urb_read_length =
|
|
|
|
|
DIV_ROUND_UP(MAX2(nr_attribute_slots, 1), 2);
|
2015-10-15 11:39:06 -07:00
|
|
|
|
|
|
|
|
prog_data->nr_attributes = nr_attributes;
|
2016-04-04 12:47:57 +02:00
|
|
|
prog_data->nr_attribute_slots = nr_attribute_slots;
|
2015-10-15 11:39:06 -07:00
|
|
|
|
|
|
|
|
/* Since vertex shaders reuse the same VUE entry for inputs and outputs
|
|
|
|
|
* (overwriting the original contents), we need to make sure the size is
|
|
|
|
|
* the larger of the two.
|
|
|
|
|
*/
|
|
|
|
|
const unsigned vue_entries =
|
2016-04-04 12:47:57 +02:00
|
|
|
MAX2(nr_attribute_slots, (unsigned)prog_data->base.vue_map.num_slots);
|
2015-10-15 11:39:06 -07:00
|
|
|
|
2016-01-05 08:41:39 -08:00
|
|
|
if (compiler->devinfo->gen == 6) {
|
2015-10-15 11:39:06 -07:00
|
|
|
prog_data->base.urb_entry_size = DIV_ROUND_UP(vue_entries, 8);
|
2016-01-05 08:41:39 -08:00
|
|
|
} else {
|
2015-10-15 11:39:06 -07:00
|
|
|
prog_data->base.urb_entry_size = DIV_ROUND_UP(vue_entries, 4);
|
2016-01-05 08:41:39 -08:00
|
|
|
/* On Cannonlake software shall not program an allocation size that
|
|
|
|
|
* specifies a size that is a multiple of 3 64B (512-bit) cachelines.
|
|
|
|
|
*/
|
|
|
|
|
if (compiler->devinfo->gen == 10 &&
|
|
|
|
|
prog_data->base.urb_entry_size % 3 == 0)
|
|
|
|
|
prog_data->base.urb_entry_size++;
|
|
|
|
|
}
|
2015-10-15 11:39:06 -07:00
|
|
|
|
2017-01-04 17:52:38 -08:00
|
|
|
if (INTEL_DEBUG & DEBUG_VS) {
|
|
|
|
|
fprintf(stderr, "VS Output ");
|
|
|
|
|
brw_print_vue_map(stderr, &prog_data->base.vue_map);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-13 15:23:48 -08:00
|
|
|
if (is_scalar) {
|
2015-03-11 21:18:42 -07:00
|
|
|
prog_data->base.dispatch_mode = DISPATCH_MODE_SIMD8;
|
|
|
|
|
|
2015-10-08 13:53:33 -07:00
|
|
|
fs_visitor v(compiler, log_data, mem_ctx, key, &prog_data->base.base,
|
2015-10-01 15:21:57 -07:00
|
|
|
NULL, /* prog; Only used for TEXTURE_RECTANGLE on gen < 8 */
|
2015-10-08 13:53:33 -07:00
|
|
|
shader, 8, shader_time_index);
|
|
|
|
|
if (!v.run_vs(clip_planes)) {
|
|
|
|
|
if (error_str)
|
|
|
|
|
*error_str = ralloc_strdup(mem_ctx, v.fail_msg);
|
2014-10-20 23:29:41 -07:00
|
|
|
|
|
|
|
|
return NULL;
|
2013-04-11 09:55:42 -07:00
|
|
|
}
|
2013-04-11 09:57:23 -07:00
|
|
|
|
2016-04-28 14:20:36 -07:00
|
|
|
prog_data->base.base.dispatch_grf_start_reg = v.payload.num_regs;
|
|
|
|
|
|
2015-10-08 13:53:33 -07:00
|
|
|
fs_generator g(compiler, log_data, mem_ctx, (void *) key,
|
|
|
|
|
&prog_data->base.base, v.promoted_constants,
|
2016-01-14 20:27:51 -08:00
|
|
|
v.runtime_check_aads_emit, MESA_SHADER_VERTEX);
|
2014-10-20 23:29:41 -07:00
|
|
|
if (INTEL_DEBUG & DEBUG_VS) {
|
2015-10-08 13:53:33 -07:00
|
|
|
const char *debug_name =
|
|
|
|
|
ralloc_asprintf(mem_ctx, "%s vertex shader %s",
|
2017-05-08 09:20:21 -07:00
|
|
|
shader->info.label ? shader->info.label :
|
2016-10-13 11:41:23 +11:00
|
|
|
"unnamed",
|
2017-05-08 09:20:21 -07:00
|
|
|
shader->info.name);
|
2015-10-08 13:53:33 -07:00
|
|
|
|
|
|
|
|
g.enable_debug(debug_name);
|
2014-10-20 23:29:41 -07:00
|
|
|
}
|
|
|
|
|
g.generate_code(v.cfg, 8);
|
|
|
|
|
assembly = g.get_assembly(final_assembly_size);
|
2012-11-30 12:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
2014-10-20 23:29:41 -07:00
|
|
|
if (!assembly) {
|
2015-03-11 21:18:42 -07:00
|
|
|
prog_data->base.dispatch_mode = DISPATCH_MODE_4X2_DUAL_OBJECT;
|
|
|
|
|
|
2015-10-08 13:53:33 -07:00
|
|
|
vec4_vs_visitor v(compiler, log_data, key, prog_data,
|
|
|
|
|
shader, clip_planes, mem_ctx,
|
|
|
|
|
shader_time_index, use_legacy_snorm_formula);
|
2015-08-27 23:49:03 -07:00
|
|
|
if (!v.run()) {
|
2015-10-08 13:53:33 -07:00
|
|
|
if (error_str)
|
|
|
|
|
*error_str = ralloc_strdup(mem_ctx, v.fail_msg);
|
2014-10-20 23:29:41 -07:00
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-22 15:01:27 -07:00
|
|
|
assembly = brw_vec4_generate_assembly(compiler, log_data, mem_ctx,
|
|
|
|
|
shader, &prog_data->base, v.cfg,
|
|
|
|
|
final_assembly_size);
|
2014-10-20 23:29:41 -07:00
|
|
|
}
|
2012-11-30 12:55:50 -08:00
|
|
|
|
2012-12-06 22:37:34 -08:00
|
|
|
return assembly;
|
2012-11-26 21:46:27 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} /* extern "C" */
|