Files
mesa/src/compiler/nir/tests/ssa_def_bits_used_tests.cpp
T
Alyssa Rosenzweig 7f6491b76d nir: Combine if_uses with instruction uses
Every nir_ssa_def is part of a chain of uses, implemented with doubly linked
lists.  That means each requires 2 * 64-bit = 16 bytes per def, which is
memory intensive. Together they require 32 bytes per def. Not cool.

To cut that memory use in half, we can combine the two linked lists into a
single use list that contains both regular instruction uses and if-uses. To do
this, we augment the nir_src with a boolean "is_if", and reimplement the
abstract if-uses operations on top of that list. That boolean should fit into
the padding already in nir_src so should not actually affect memory use, and in
the future we sneak it into the bottom bit of a pointer.

However, this creates a new inefficiency: now iterating over regular uses
separate from if-uses is (nominally) more expensive. It turns out virtually
every caller of nir_foreach_if_use(_safe) also calls nir_foreach_use(_safe)
immediately before, so we rewrite most of the callers to instead call a new
single `nir_foreach_use_including_if(_safe)` which predicates the logic based on
`src->is_if`. This should mitigate the performance difference.

There's a bit of churn, but this is largely a mechanical set of changes.

Signed-off-by: Alyssa Rosenzweig <alyssa@collabora.com>
Reviewed-by: Faith Ekstrand <faith.ekstrand@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/22343>
2023-04-07 23:48:03 +00:00

257 lines
8.1 KiB
C++

/*
* Copyright © 2021 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 <gtest/gtest.h>
#include "nir.h"
#include "nir_builder.h"
#include "nir_range_analysis.h"
class ssa_def_bits_used_test : public ::testing::Test {
protected:
ssa_def_bits_used_test()
{
glsl_type_singleton_init_or_ref();
static const nir_shader_compiler_options options = { };
bld = nir_builder_init_simple_shader(MESA_SHADER_VERTEX, &options,
"ssa_def_bits_used test");
}
~ssa_def_bits_used_test()
{
ralloc_free(bld.shader);
glsl_type_singleton_decref();
}
nir_alu_instr *build_alu_instr(nir_op op, nir_ssa_def *, nir_ssa_def *);
struct nir_builder bld;
};
static bool
is_used_once(const nir_ssa_def *def)
{
return list_is_singular(&def->uses);
}
nir_alu_instr *
ssa_def_bits_used_test::build_alu_instr(nir_op op,
nir_ssa_def *src0, nir_ssa_def *src1)
{
nir_ssa_def *def = nir_build_alu(&bld, op, src0, src1, NULL, NULL);
if (def == NULL)
return NULL;
nir_alu_instr *alu = nir_instr_as_alu(def->parent_instr);
if (alu == NULL)
return NULL;
alu->dest.write_mask = 1;
alu->dest.dest.ssa.num_components = 1;
return alu;
}
TEST_F(ssa_def_bits_used_test, iand_with_const_vector)
{
static const unsigned src0_imm[4] = { 255u << 24, 255u << 16, 255u << 8, 255u };
nir_ssa_def *src0 = nir_imm_ivec4(&bld,
src0_imm[0], src0_imm[1],
src0_imm[2], src0_imm[3]);
nir_ssa_def *src1 = nir_imm_int(&bld, 0xffffffff);
nir_alu_instr *alu = build_alu_instr(nir_op_iand, src0, src1);
ASSERT_NE((void *) 0, alu);
for (unsigned i = 0; i < 4; i++) {
/* If the test is changed, and somehow src1 is used multiple times,
* nir_ssa_def_bits_used will accumulate *all* the uses (as it should).
* This isn't what we're trying to test here.
*/
ASSERT_TRUE(is_used_once(src1));
alu->src[0].swizzle[0] = i;
const uint64_t bits_used = nir_ssa_def_bits_used(alu->src[1].src.ssa);
/* The answer should be the value swizzled from src0. */
EXPECT_EQ(src0_imm[i], bits_used);
}
}
TEST_F(ssa_def_bits_used_test, ior_with_const_vector)
{
static const unsigned src0_imm[4] = { 255u << 24, 255u << 16, 255u << 8, 255u };
nir_ssa_def *src0 = nir_imm_ivec4(&bld,
src0_imm[0], src0_imm[1],
src0_imm[2], src0_imm[3]);
nir_ssa_def *src1 = nir_imm_int(&bld, 0xffffffff);
nir_alu_instr *alu = build_alu_instr(nir_op_ior, src0, src1);
ASSERT_NE((void *) 0, alu);
for (unsigned i = 0; i < 4; i++) {
/* If the test is changed, and somehow src1 is used multiple times,
* nir_ssa_def_bits_used will accumulate *all* the uses (as it should).
* This isn't what we're trying to test here.
*/
ASSERT_TRUE(is_used_once(src1));
alu->src[0].swizzle[0] = i;
const uint64_t bits_used = nir_ssa_def_bits_used(alu->src[1].src.ssa);
/* The answer should be the value swizzled from ~src0. */
EXPECT_EQ(~src0_imm[i], bits_used);
}
}
TEST_F(ssa_def_bits_used_test, extract_i16_with_const_index)
{
nir_ssa_def *src0 = nir_imm_int(&bld, 0xffffffff);
static const unsigned src1_imm[4] = { 9, 1, 0, 9 };
nir_ssa_def *src1 = nir_imm_ivec4(&bld,
src1_imm[0],
src1_imm[1],
src1_imm[2],
src1_imm[3]);
nir_alu_instr *alu = build_alu_instr(nir_op_extract_i16, src0, src1);
ASSERT_NE((void *) 0, alu);
for (unsigned i = 1; i < 3; i++) {
/* If the test is changed, and somehow src1 is used multiple times,
* nir_ssa_def_bits_used will accumulate *all* the uses (as it should).
* This isn't what we're trying to test here.
*/
ASSERT_TRUE(is_used_once(src1));
alu->src[1].swizzle[0] = i;
const uint64_t bits_used = nir_ssa_def_bits_used(alu->src[0].src.ssa);
EXPECT_EQ(0xffffu << (16 * src1_imm[i]), bits_used);
}
}
TEST_F(ssa_def_bits_used_test, extract_u16_with_const_index)
{
nir_ssa_def *src0 = nir_imm_int(&bld, 0xffffffff);
static const unsigned src1_imm[4] = { 9, 1, 0, 9 };
nir_ssa_def *src1 = nir_imm_ivec4(&bld,
src1_imm[0],
src1_imm[1],
src1_imm[2],
src1_imm[3]);
nir_alu_instr *alu = build_alu_instr(nir_op_extract_u16, src0, src1);
ASSERT_NE((void *) 0, alu);
for (unsigned i = 1; i < 3; i++) {
/* If the test is changed, and somehow src1 is used multiple times,
* nir_ssa_def_bits_used will accumulate *all* the uses (as it should).
* This isn't what we're trying to test here.
*/
ASSERT_TRUE(is_used_once(src1));
alu->src[1].swizzle[0] = i;
const uint64_t bits_used = nir_ssa_def_bits_used(alu->src[0].src.ssa);
EXPECT_EQ(0xffffu << (16 * src1_imm[i]), bits_used);
}
}
TEST_F(ssa_def_bits_used_test, extract_i8_with_const_index)
{
nir_ssa_def *src0 = nir_imm_int(&bld, 0xffffffff);
static const unsigned src1_imm[4] = { 3, 2, 1, 0 };
nir_ssa_def *src1 = nir_imm_ivec4(&bld,
src1_imm[0],
src1_imm[1],
src1_imm[2],
src1_imm[3]);
nir_alu_instr *alu = build_alu_instr(nir_op_extract_i8, src0, src1);
ASSERT_NE((void *) 0, alu);
for (unsigned i = 0; i < 4; i++) {
/* If the test is changed, and somehow src1 is used multiple times,
* nir_ssa_def_bits_used will accumulate *all* the uses (as it should).
* This isn't what we're trying to test here.
*/
ASSERT_TRUE(is_used_once(src1));
alu->src[1].swizzle[0] = i;
const uint64_t bits_used = nir_ssa_def_bits_used(alu->src[0].src.ssa);
EXPECT_EQ(0xffu << (8 * src1_imm[i]), bits_used);
}
}
TEST_F(ssa_def_bits_used_test, extract_u8_with_const_index)
{
nir_ssa_def *src0 = nir_imm_int(&bld, 0xffffffff);
static const unsigned src1_imm[4] = { 3, 2, 1, 0 };
nir_ssa_def *src1 = nir_imm_ivec4(&bld,
src1_imm[0],
src1_imm[1],
src1_imm[2],
src1_imm[3]);
nir_alu_instr *alu = build_alu_instr(nir_op_extract_u8, src0, src1);
ASSERT_NE((void *) 0, alu);
for (unsigned i = 0; i < 4; i++) {
/* If the test is changed, and somehow src1 is used multiple times,
* nir_ssa_def_bits_used will accumulate *all* the uses (as it should).
* This isn't what we're trying to test here.
*/
ASSERT_TRUE(is_used_once(src1));
alu->src[1].swizzle[0] = i;
const uint64_t bits_used = nir_ssa_def_bits_used(alu->src[0].src.ssa);
EXPECT_EQ(0xffu << (8 * src1_imm[i]), bits_used);
}
}