blob: 1e900404c9c937949fd76a2b3c218f08f0a4147d [file] [log] [blame]
/*
* Copyright © 2014 Broadcom
*
* 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.
*/
/**
* @file v3d_vir_lower_uniforms.c
*
* This is the pre-code-generation pass for fixing up instructions that try to
* read from multiple uniform values.
*/
#include "v3d_compiler.h"
#include "util/hash_table.h"
#include "util/u_math.h"
static inline uint32_t
index_hash(const void *key)
{
return (uintptr_t)key;
}
static inline bool
index_compare(const void *a, const void *b)
{
return a == b;
}
static void
add_uniform(struct hash_table *ht, struct qreg reg)
{
struct hash_entry *entry;
void *key = (void *)(uintptr_t)(reg.index + 1);
entry = _mesa_hash_table_search(ht, key);
if (entry) {
entry->data++;
} else {
_mesa_hash_table_insert(ht, key, (void *)(uintptr_t)1);
}
}
static void
remove_uniform(struct hash_table *ht, struct qreg reg)
{
struct hash_entry *entry;
void *key = (void *)(uintptr_t)(reg.index + 1);
entry = _mesa_hash_table_search(ht, key);
assert(entry);
entry->data = (void *)(((uintptr_t) entry->data) - 1);
if (entry->data == NULL)
_mesa_hash_table_remove(ht, entry);
}
static bool
is_lowerable_uniform(struct qinst *inst, int i)
{
if (inst->src[i].file != QFILE_UNIF)
return false;
return i != vir_get_implicit_uniform_src(inst);
}
/* Returns the number of different uniform values referenced by the
* instruction.
*/
static uint32_t
vir_get_instruction_uniform_count(struct qinst *inst)
{
uint32_t count = 0;
for (int i = 0; i < vir_get_nsrc(inst); i++) {
if (inst->src[i].file != QFILE_UNIF)
continue;
bool is_duplicate = false;
for (int j = 0; j < i; j++) {
if (inst->src[j].file == QFILE_UNIF &&
inst->src[j].index == inst->src[i].index) {
is_duplicate = true;
break;
}
}
if (!is_duplicate)
count++;
}
return count;
}
void
vir_lower_uniforms(struct v3d_compile *c)
{
struct hash_table *ht =
_mesa_hash_table_create(c, index_hash, index_compare);
/* Walk the instruction list, finding which instructions have more
* than one uniform referenced, and add those uniform values to the
* ht.
*/
vir_for_each_inst_inorder(inst, c) {
uint32_t nsrc = vir_get_nsrc(inst);
if (vir_get_instruction_uniform_count(inst) <= 1)
continue;
for (int i = 0; i < nsrc; i++) {
if (is_lowerable_uniform(inst, i))
add_uniform(ht, inst->src[i]);
}
}
while (ht->entries) {
/* Find the most commonly used uniform in instructions that
* need a uniform lowered.
*/
uint32_t max_count = 0;
uint32_t max_index = 0;
struct hash_entry *entry;
hash_table_foreach(ht, entry) {
uint32_t count = (uintptr_t)entry->data;
uint32_t index = (uintptr_t)entry->key - 1;
if (count > max_count) {
max_count = count;
max_index = index;
}
}
struct qreg unif = vir_reg(QFILE_UNIF, max_index);
/* Now, find the instructions using this uniform and make them
* reference a temp instead.
*/
vir_for_each_block(block, c) {
struct qreg temp = c->undef;
vir_for_each_inst(inst, block) {
uint32_t nsrc = vir_get_nsrc(inst);
uint32_t count = vir_get_instruction_uniform_count(inst);
if (count <= 1)
continue;
bool removed = false;
for (int i = 0; i < nsrc; i++) {
if (is_lowerable_uniform(inst, i) &&
inst->src[i].index == max_index) {
/* If the block doesn't have a
* load of the uniform yet,
* add it now. We could
* potentially do better and
* CSE MOVs from multiple
* blocks into dominating
* blocks, except that may
* cause troubles for register
* allocation.
*/
if (temp.file == QFILE_NULL) {
c->cursor =
vir_before_inst(inst);
temp = vir_MOV(c, unif);
}
inst->src[i] = temp;
remove_uniform(ht, unif);
removed = true;
}
}
if (removed)
count--;
/* If the instruction doesn't need lowering any more,
* then drop it from the list.
*/
if (count <= 1) {
for (int i = 0; i < nsrc; i++) {
if (is_lowerable_uniform(inst, i))
remove_uniform(ht, inst->src[i]);
}
}
}
}
}
_mesa_hash_table_destroy(ht, NULL);
}