blob: 9b607e640374d5b35753f5f17aadc930ee9ab846 [file] [log] [blame]
#include "main/imports.h"
#include "shader/program.h"
#include "shader/prog_print.h"
#include "slang_compile.h"
#include "slang_compile_variable.h"
#include "slang_mem.h"
#include "slang_vartable.h"
#include "slang_ir.h"
static int dbg = 0;
typedef enum {
FREE,
VAR,
TEMP
} TempState;
/**
* Variable/register info for one variable scope.
*/
struct table
{
int Level;
int NumVars;
slang_variable **Vars; /* array [NumVars] */
TempState Temps[MAX_PROGRAM_TEMPS * 4]; /* per-component state */
int ValSize[MAX_PROGRAM_TEMPS * 4]; /**< For debug only */
struct table *Parent; /** Parent scope table */
};
/**
* A variable table is a stack of tables, one per scope.
*/
struct slang_var_table_
{
GLint CurLevel;
GLuint MaxRegisters;
struct table *Top; /**< Table at top of stack */
};
slang_var_table *
_slang_new_var_table(GLuint maxRegisters)
{
slang_var_table *vt
= (slang_var_table *) _slang_alloc(sizeof(slang_var_table));
if (vt) {
vt->MaxRegisters = maxRegisters;
}
return vt;
}
void
_slang_delete_var_table(slang_var_table *vt)
{
if (vt->Top) {
_mesa_problem(NULL, "non-empty var table in _slang_delete_var_table()");
return;
}
_slang_free(vt);
}
/**
* Create new table, put at head, return ptr to it.
* XXX we should take a maxTemps parameter to indicate how many temporaries
* are available for the current shader/program target.
*/
void
_slang_push_var_table(slang_var_table *vt)
{
struct table *t = (struct table *) _slang_alloc(sizeof(struct table));
if (t) {
t->Level = vt->CurLevel++;
t->Parent = vt->Top;
if (t->Parent) {
/* copy the info indicating which temp regs are in use */
memcpy(t->Temps, t->Parent->Temps, sizeof(t->Temps));
memcpy(t->ValSize, t->Parent->ValSize, sizeof(t->ValSize));
}
vt->Top = t;
if (dbg) printf("Pushing level %d\n", t->Level);
}
}
/**
* Destroy given table, return ptr to Parent
*/
void
_slang_pop_var_table(slang_var_table *vt)
{
struct table *t = vt->Top;
int i;
if (dbg) printf("Popping level %d\n", t->Level);
/* free the storage allocated for each variable */
for (i = 0; i < t->NumVars; i++) {
slang_ir_storage *store = (slang_ir_storage *) t->Vars[i]->aux;
GLint j;
GLuint comp;
if (dbg) printf(" Free var %s, size %d at %d.%s\n",
(char*) t->Vars[i]->a_name, store->Size,
store->Index,
_mesa_swizzle_string(store->Swizzle, 0, 0));
if (store->Size == 1)
comp = GET_SWZ(store->Swizzle, 0);
else
comp = 0;
assert(store->Index >= 0);
for (j = 0; j < store->Size; j++) {
assert(t->Temps[store->Index * 4 + j + comp] == VAR);
t->Temps[store->Index * 4 + j + comp] = FREE;
}
store->Index = -1;
}
if (t->Parent) {
/* just verify that any remaining allocations in this scope
* were for temps
*/
for (i = 0; i < (int) vt->MaxRegisters * 4; i++) {
if (t->Temps[i] != FREE && t->Parent->Temps[i] == FREE) {
if (dbg) printf(" Free reg %d\n", i/4);
assert(t->Temps[i] == TEMP);
}
}
}
if (t->Vars) {
_slang_free(t->Vars);
t->Vars = NULL;
}
vt->Top = t->Parent;
_slang_free(t);
vt->CurLevel--;
}
/**
* Add a new variable to the given symbol table.
*/
void
_slang_add_variable(slang_var_table *vt, slang_variable *v)
{
struct table *t;
assert(vt);
t = vt->Top;
assert(t);
if (dbg) printf("Adding var %s, store %p\n", (char *) v->a_name, v->aux);
t->Vars = (slang_variable **)
_slang_realloc(t->Vars,
t->NumVars * sizeof(slang_variable *),
(t->NumVars + 1) * sizeof(slang_variable *));
t->Vars[t->NumVars] = v;
t->NumVars++;
}
/**
* Look for variable by name in given table.
* If not found, Parent table will be searched.
*/
slang_variable *
_slang_find_variable(const slang_var_table *vt, slang_atom name)
{
struct table *t = vt->Top;
while (1) {
int i;
for (i = 0; i < t->NumVars; i++) {
if (t->Vars[i]->a_name == name)
return t->Vars[i];
}
if (t->Parent)
t = t->Parent;
else
return NULL;
}
}
/**
* Allocation helper.
* \param size var size in floats
* \return position for var, measured in floats
*/
static GLint
alloc_reg(slang_var_table *vt, GLint size, GLboolean isTemp)
{
struct table *t = vt->Top;
/* if size == 1, allocate anywhere, else, pos must be multiple of 4 */
const GLuint step = (size == 1) ? 1 : 4;
GLuint i, j;
assert(size > 0); /* number of floats */
for (i = 0; i <= vt->MaxRegisters * 4 - size; i += step) {
GLuint found = 0;
for (j = 0; j < (GLuint) size; j++) {
if (i + j < vt->MaxRegisters * 4 && t->Temps[i + j] == FREE) {
found++;
}
else {
break;
}
}
if (found == size) {
/* found block of size free regs */
if (size > 1)
assert(i % 4 == 0);
for (j = 0; j < (GLuint) size; j++)
t->Temps[i + j] = isTemp ? TEMP : VAR;
assert(i < MAX_PROGRAM_TEMPS * 4);
t->ValSize[i] = size;
return i;
}
}
return -1;
}
/**
* Allocate temp register(s) for storing a variable.
* \param size size needed, in floats
* \param swizzle returns swizzle mask for accessing var in register
* \return register allocated, or -1
*/
GLboolean
_slang_alloc_var(slang_var_table *vt, slang_ir_storage *store)
{
struct table *t = vt->Top;
const int i = alloc_reg(vt, store->Size, GL_FALSE);
if (i < 0)
return GL_FALSE;
store->Index = i / 4;
if (store->Size == 1) {
const GLuint comp = i % 4;
store->Swizzle = MAKE_SWIZZLE4(comp, comp, comp, comp);
}
else if (store->Size == 2) {
store->Swizzle = MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y,
SWIZZLE_NIL, SWIZZLE_NIL);
}
else if (store->Size == 3) {
store->Swizzle = MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y,
SWIZZLE_Z, SWIZZLE_NIL);
}
else {
store->Swizzle = SWIZZLE_NOOP;
}
if (dbg)
printf("Alloc var storage sz %d at %d.%s (level %d) store %p\n",
store->Size, store->Index,
_mesa_swizzle_string(store->Swizzle, 0, 0),
t->Level,
(void*) store);
return GL_TRUE;
}
/**
* Allocate temp register(s) for storing an unnamed intermediate value.
*/
GLboolean
_slang_alloc_temp(slang_var_table *vt, slang_ir_storage *store)
{
struct table *t = vt->Top;
const int i = alloc_reg(vt, store->Size, GL_TRUE);
if (i < 0)
return GL_FALSE;
assert(store->Index < 0);
store->Index = i / 4;
if (store->Size == 1) {
const GLuint comp = i % 4;
store->Swizzle = MAKE_SWIZZLE4(comp, comp, comp, comp);
}
else {
/* XXX improve swizzled for size=2/3, use for writemask... */
#if 1
if (store->Size == 2) {
store->Swizzle = MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y,
SWIZZLE_NIL, SWIZZLE_NIL);
}
#endif
store->Swizzle = SWIZZLE_NOOP;
}
if (dbg) printf("Alloc temp sz %d at %d.%s (level %d) store %p\n",
store->Size, store->Index,
_mesa_swizzle_string(store->Swizzle, 0, 0), t->Level,
(void *) store);
return GL_TRUE;
}
void
_slang_free_temp(slang_var_table *vt, slang_ir_storage *store)
{
struct table *t = vt->Top;
GLuint i;
GLuint r = store->Index;
assert(store->Size > 0);
assert(r >= 0);
assert(r + store->Size <= vt->MaxRegisters * 4);
if (dbg) printf("Free temp sz %d at %d.%s (level %d) store %p\n",
store->Size, r,
_mesa_swizzle_string(store->Swizzle, 0, 0),
t->Level, (void *) store);
if (store->Size == 1) {
const GLuint comp = GET_SWZ(store->Swizzle, 0);
/* we can actually fail some of these assertions because of the
* troublesome IR_SWIZZLE handling.
*/
#if 0
assert(store->Swizzle == MAKE_SWIZZLE4(comp, comp, comp, comp));
assert(comp < 4);
assert(t->ValSize[r * 4 + comp] == 1);
#endif
assert(t->Temps[r * 4 + comp] == TEMP);
t->Temps[r * 4 + comp] = FREE;
}
else {
/*assert(store->Swizzle == SWIZZLE_NOOP);*/
assert(t->ValSize[r*4] == store->Size);
for (i = 0; i < (GLuint) store->Size; i++) {
assert(t->Temps[r * 4 + i] == TEMP);
t->Temps[r * 4 + i] = FREE;
}
}
}
GLboolean
_slang_is_temp(const slang_var_table *vt, const slang_ir_storage *store)
{
struct table *t = vt->Top;
GLuint comp;
assert(store->Index >= 0);
assert(store->Index < (int) vt->MaxRegisters);
if (store->Swizzle == SWIZZLE_NOOP)
comp = 0;
else
comp = GET_SWZ(store->Swizzle, 0);
if (t->Temps[store->Index * 4 + comp] == TEMP)
return GL_TRUE;
else
return GL_FALSE;
}