Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Keep a list of used/defined registers.
This allows us to resolve registers the shader implicitly uses, but we need
to explicitly define in the GLSL profile. This also allows us to remove the
labels_called bit array as redundant functionality.

--HG--
branch : trunk
  • Loading branch information
icculus committed Apr 4, 2008
1 parent 0ed9eaf commit c3abeb7
Showing 1 changed file with 125 additions and 57 deletions.
182 changes: 125 additions & 57 deletions mojoshader.c
Expand Up @@ -205,6 +205,14 @@ typedef struct OutputList
} OutputList;


typedef struct RegisterList
{
RegisterType regtype;
int regnum;
struct RegisterList *next;
} RegisterList;


// result modifiers.
#define MOD_SATURATE 0x01
#define MOD_PP 0x02
Expand Down Expand Up @@ -263,27 +271,21 @@ typedef struct

typedef enum
{
// May apply to any profile...
CTX_FLAGS_USED_ADDR_REG = (1 << 0),
CTX_FLAGS_USED_PRED_REG = (1 << 1),

// Specific to GLSL profile...
CTX_FLAGS_GLSL_LIT_OPCODE = (1 << 2),
CTX_FLAGS_GLSL_DST_OPCODE = (1 << 3),
CTX_FLAGS_GLSL_LRP_OPCODE = (1 << 4),
CTX_FLAGS_GLSL_LIT_OPCODE = (1 << 0),
CTX_FLAGS_GLSL_DST_OPCODE = (1 << 1),
CTX_FLAGS_GLSL_LRP_OPCODE = (1 << 2),
CTX_FLAGS_MASK = 0xFFFFFFFF
} ContextFlags;


#define SCRATCH_BUFFER_SIZE 256
#define SCRATCH_BUFFERS 10

// !!! FIXME: labels_called and the scratch buffers make this pretty big.
// !!! FIXME: the scratch buffers make Context pretty big.
// !!! FIXME: might be worth having one set of static scratch buffers that
// !!! FIXME: are mutex protected?
// !!! FIXME: and replace the bit array for labels_called with a linked list?
// !!! FIXME: maybe just malloc() the label list, since it can't be more than
// !!! FIXME: around (bytes_of_shader / 20) elements in the pathological case?

// Context...this is state that changes as we parse through a shader...
struct Context
{
Expand Down Expand Up @@ -319,8 +321,9 @@ struct Context
uint32 instruction_controls;
uint32 previous_opcode;
ContextFlags flags;
uint8 labels_called[256];
int loops;
RegisterList used_registers;
RegisterList defined_registers;
};


Expand Down Expand Up @@ -366,28 +369,6 @@ static int shader_version_atleast(const Context *ctx, uint8 maj, uint8 min)

// Bit arrays (for storing large arrays of booleans...

static void set_bit_array(uint8 *array, size_t arraylen, int index, int val)
{
const int byteindex = index / 8;
const int bitindex = index % 8;
assert(byteindex < arraylen);

if (val)
array[byteindex] |= (1 << bitindex);
else
array[byteindex] &= ~(1 << bitindex);
} // set_bit_array

static int get_bit_array(const uint8 *array, size_t arraylen, int index)
{
const int byteindex = index / 8;
const int bitindex = index % 8;
assert(byteindex < arraylen);
const uint8 byte = array[byteindex];
return (byte & (1 << bitindex)) ? 1 : 0;
} // get_bit_array


static inline char *get_scratch_buffer(Context *ctx)
{
ctx->scratchidx = (ctx->scratchidx + 1) % SCRATCH_BUFFERS;
Expand Down Expand Up @@ -552,6 +533,106 @@ static void floatstr(Context *ctx, char *buf, size_t bufsize, float f,
} // floatstr


// Deal with register lists... !!! FIXME: I sort of hate this.

static void free_reglist(MOJOSHADER_free f, RegisterList *item)
{
while (item != NULL)
{
RegisterList *next = item->next;
f(item);
item = next;
} // while
} // free_reglist

static inline uint32 reg_to_ui32(const RegisterType regtype, const int regnum)
{
return ( ((uint32) regtype) | (((uint32) regnum) << 16) );
} // reg_to_uint32

static void reglist_insert(Context *ctx, RegisterList *prev,
const RegisterType regtype, const int regnum)
{
const uint32 newval = reg_to_ui32(regtype, regnum);
RegisterList *item = prev->next;
while (item != NULL)
{
const uint32 val = reg_to_ui32(item->regtype, item->regnum);
if (newval == val)
return; // already set, so we're done.
else if (newval < val) // insert it here.
break;
else // if (newval > val)
{
// keep going, we're not to the insertion point yet.
prev = item;
item = item->next;
} // else
} // while

// we need to insert an entry after (prev).
item = (RegisterList *) ctx->malloc(sizeof (RegisterList));
if (item == NULL)
out_of_memory(ctx);
else
{
item->regtype = regtype;
item->regnum = regnum;
item->next = prev->next;
prev->next = item;
} // else
} // reglist_insert

static RegisterList *reglist_exists(RegisterList *prev,
const RegisterType regtype,
const int regnum)
{
const uint32 newval = reg_to_ui32(regtype, regnum);
RegisterList *item = prev->next;
while (item != NULL)
{
const uint32 val = reg_to_ui32(item->regtype, item->regnum);
if (newval == val)
return item; // here it is.
else if (newval < val) // should have been here if it existed.
return NULL;
else // if (newval > val)
{
// keep going, we're not to the insertion point yet.
prev = item;
item = item->next;
} // else
} // while

return NULL; // wasn't in the list.
} // reglist_exists

static inline void set_used_register(Context *ctx, const RegisterType regtype,
const int regnum)
{
reglist_insert(ctx, &ctx->used_registers, regtype, regnum);
} // set_used_register

static inline int get_used_register(Context *ctx, const RegisterType regtype,
const int regnum)
{
return (reglist_exists(&ctx->used_registers, regtype, regnum) != NULL);
} // set_used_register

static inline void set_defined_register(Context *ctx, const RegisterType rtype,
const int regnum)
{
reglist_insert(ctx, &ctx->defined_registers, rtype, regnum);
} // set_defined_register

static inline int get_defined_register(Context *ctx, const RegisterType rtype,
const int regnum)
{
return (reglist_exists(&ctx->defined_registers, rtype, regnum) != NULL);
} // get_defined_register



// This is used in more than just the d3d profile.
static const char *get_D3D_register_string(Context *ctx,
RegisterType regtype,
Expand Down Expand Up @@ -1503,9 +1584,9 @@ static void emit_GLSL_end(Context *ctx)
emit_GLSL_RET(ctx);

push_output(ctx, &ctx->globals);
if (ctx->flags & CTX_FLAGS_USED_ADDR_REG)
if (get_used_register(ctx, REG_TYPE_ADDRESS, 0))
output_line(ctx, "ivec a0;");
if (ctx->flags & CTX_FLAGS_USED_PRED_REG)
if (get_used_register(ctx, REG_TYPE_PREDICATE, 0))
output_line(ctx, "bvec p0;");
output_blank_line(ctx);
pop_output(ctx);
Expand Down Expand Up @@ -1860,7 +1941,7 @@ static void emit_GLSL_LABEL(Context *ctx)

// MSDN specs say CALL* has to come before the LABEL, so we know if we
// can ditch the entire function here as unused.
if (!get_bit_array(ctx->labels_called, sizeof (ctx->labels_called), label))
if (!get_used_register(ctx, REG_TYPE_LABEL, label))
ctx->output = &ctx->ignore; // Func not used. Parse, but don't output.

// !!! FIXME: it would be nice if we could determine if a function is
Expand Down Expand Up @@ -2268,18 +2349,6 @@ static const Profile profiles[] =
PROFILE_EMITTER_GLSL(op) \
}

static void register_reference_upkeep(Context *ctx, const RegisterType regtype)
{
// !!! FIXME: make sure there were def/dcl for all referenced vars?
switch (regtype)
{
case REG_TYPE_ADDRESS: ctx->flags |= CTX_FLAGS_USED_ADDR_REG; break;
case REG_TYPE_PREDICATE: ctx->flags |= CTX_FLAGS_USED_PRED_REG; break;
default: break; // don't care.
} // switch
} // register_reference_upkeep


static int parse_destination_token(Context *ctx, DestArgInfo *info)
{
// !!! FIXME: recheck against the spec for ranges (like RASTOUT values, etc).
Expand Down Expand Up @@ -2356,7 +2425,7 @@ static int parse_destination_token(Context *ctx, DestArgInfo *info)
if ((info->regtype < 0) || (info->regtype > REG_TYPE_MAX))
return fail(ctx, "Register type is out of range");

register_reference_upkeep(ctx, info->regtype);
set_used_register(ctx, info->regtype, info->regnum);
return 1;
} // parse_destination_token

Expand Down Expand Up @@ -2403,7 +2472,7 @@ static int parse_source_token(Context *ctx, SourceArgInfo *info)
if ( ((SourceMod) info->src_mod) >= SRCMOD_TOTAL )
return fail(ctx, "Unknown source modifier");

register_reference_upkeep(ctx, info->regtype);
set_used_register(ctx, info->regtype, info->regnum);
return 1;
} // parse_source_token

Expand Down Expand Up @@ -2837,19 +2906,16 @@ static void state_LABEL(Context *ctx)

static void state_CALL(Context *ctx)
{
const int l = ctx->source_args[0].regnum;
if (check_label_register(ctx, 0, "CALL") != FAIL)
set_bit_array(ctx->labels_called, sizeof (ctx->labels_called), l, 1);
check_label_register(ctx, 0, "CALL");
} // state_CALL

static void state_CALLNZ(Context *ctx)
{
const RegisterType regtype = ctx->source_args[1].regtype;
const int l = ctx->source_args[0].regnum;
if ((regtype != REG_TYPE_CONSTBOOL) && (regtype != REG_TYPE_PREDICATE))
fail(ctx, "CALLNZ argument isn't constbool or predicate register");
else if (check_label_register(ctx, 0, "CALLNZ") != FAIL)
set_bit_array(ctx->labels_called, sizeof (ctx->labels_called), l, 1);
else
check_label_register(ctx, 0, "CALLNZ");
} // state_CALLNZ

static void state_MOVA(Context *ctx)
Expand Down Expand Up @@ -3310,6 +3376,8 @@ static void destroy_context(Context *ctx)
free_output_list(f, ctx->subroutines.head.next);
free_output_list(f, ctx->mainline.head.next);
free_output_list(f, ctx->ignore.head.next);
free_reglist(f, ctx->used_registers.next);
free_reglist(f, ctx->defined_registers.next);
if ((ctx->failstr != NULL) && (ctx->failstr != out_of_mem_str))
f((void *) ctx->failstr);
f(ctx);
Expand Down

0 comments on commit c3abeb7

Please sign in to comment.