summaryrefslogtreecommitdiff
path: root/src/mesa/shader/slang/slang_emit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mesa/shader/slang/slang_emit.c')
-rw-r--r--src/mesa/shader/slang/slang_emit.c1621
1 files changed, 1621 insertions, 0 deletions
diff --git a/src/mesa/shader/slang/slang_emit.c b/src/mesa/shader/slang/slang_emit.c
new file mode 100644
index 0000000000..25c107d1cc
--- /dev/null
+++ b/src/mesa/shader/slang/slang_emit.c
@@ -0,0 +1,1621 @@
+/*
+ * Mesa 3-D graphics library
+ * Version: 6.5.3
+ *
+ * Copyright (C) 2005-2007 Brian Paul All Rights Reserved.
+ *
+ * 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 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
+ * BRIAN PAUL 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 slang_emit.c
+ * Emit program instructions (PI code) from IR trees.
+ * \author Brian Paul
+ */
+
+/***
+ *** NOTES
+ ***
+ *** To emit GPU instructions, we basically just do an in-order traversal
+ *** of the IR tree.
+ ***/
+
+
+#include "imports.h"
+#include "context.h"
+#include "macros.h"
+#include "program.h"
+#include "prog_instruction.h"
+#include "prog_parameter.h"
+#include "prog_print.h"
+#include "slang_builtin.h"
+#include "slang_emit.h"
+
+
+#define PEEPHOLE_OPTIMIZATIONS 1
+#define ANNOTATE 0
+
+
+/* XXX temporarily here */
+
+
+typedef struct
+{
+ slang_info_log *log;
+ slang_var_table *vt;
+ struct gl_program *prog;
+ /* code-gen options */
+ GLboolean EmitHighLevelInstructions;
+ GLboolean EmitComments;
+} slang_emit_info;
+
+
+/**
+ * Assembly and IR info
+ */
+typedef struct
+{
+ slang_ir_opcode IrOpcode;
+ const char *IrName;
+ gl_inst_opcode InstOpcode;
+ GLuint ResultSize, NumParams;
+} slang_ir_info;
+
+
+
+static const slang_ir_info IrInfo[] = {
+ /* binary ops */
+ { IR_ADD, "IR_ADD", OPCODE_ADD, 4, 2 },
+ { IR_SUB, "IR_SUB", OPCODE_SUB, 4, 2 },
+ { IR_MUL, "IR_MUL", OPCODE_MUL, 4, 2 },
+ { IR_DIV, "IR_DIV", OPCODE_NOP, 0, 2 }, /* XXX broke */
+ { IR_DOT4, "IR_DOT_4", OPCODE_DP4, 1, 2 },
+ { IR_DOT3, "IR_DOT_3", OPCODE_DP3, 1, 2 },
+ { IR_CROSS, "IR_CROSS", OPCODE_XPD, 3, 2 },
+ { IR_LRP, "IR_LRP", OPCODE_LRP, 4, 3 },
+ { IR_MIN, "IR_MIN", OPCODE_MIN, 4, 2 },
+ { IR_MAX, "IR_MAX", OPCODE_MAX, 4, 2 },
+ { IR_CLAMP, "IR_CLAMP", OPCODE_NOP, 4, 3 }, /* special case: emit_clamp() */
+ { IR_SEQUAL, "IR_SEQUAL", OPCODE_SEQ, 4, 2 },
+ { IR_SNEQUAL, "IR_SNEQUAL", OPCODE_SNE, 4, 2 },
+ { IR_SGE, "IR_SGE", OPCODE_SGE, 4, 2 },
+ { IR_SGT, "IR_SGT", OPCODE_SGT, 4, 2 },
+ { IR_SLE, "IR_SLE", OPCODE_SLE, 4, 2 },
+ { IR_SLT, "IR_SLT", OPCODE_SLT, 4, 2 },
+ { IR_POW, "IR_POW", OPCODE_POW, 1, 2 },
+ /* unary ops */
+ { IR_I_TO_F, "IR_I_TO_F", OPCODE_NOP, 1, 1 },
+ { IR_F_TO_I, "IR_F_TO_I", OPCODE_INT, 4, 1 }, /* 4 floats to 4 ints */
+ { IR_EXP, "IR_EXP", OPCODE_EXP, 1, 1 },
+ { IR_EXP2, "IR_EXP2", OPCODE_EX2, 1, 1 },
+ { IR_LOG2, "IR_LOG2", OPCODE_LG2, 1, 1 },
+ { IR_RSQ, "IR_RSQ", OPCODE_RSQ, 1, 1 },
+ { IR_RCP, "IR_RCP", OPCODE_RCP, 1, 1 },
+ { IR_FLOOR, "IR_FLOOR", OPCODE_FLR, 4, 1 },
+ { IR_FRAC, "IR_FRAC", OPCODE_FRC, 4, 1 },
+ { IR_ABS, "IR_ABS", OPCODE_ABS, 4, 1 },
+ { IR_NEG, "IR_NEG", OPCODE_NOP, 4, 1 }, /* special case: emit_negation() */
+ { IR_DDX, "IR_DDX", OPCODE_DDX, 4, 1 },
+ { IR_DDX, "IR_DDY", OPCODE_DDX, 4, 1 },
+ { IR_SIN, "IR_SIN", OPCODE_SIN, 1, 1 },
+ { IR_COS, "IR_COS", OPCODE_COS, 1, 1 },
+ { IR_NOISE1, "IR_NOISE1", OPCODE_NOISE1, 1, 1 },
+ { IR_NOISE2, "IR_NOISE2", OPCODE_NOISE2, 1, 1 },
+ { IR_NOISE3, "IR_NOISE3", OPCODE_NOISE3, 1, 1 },
+ { IR_NOISE4, "IR_NOISE4", OPCODE_NOISE4, 1, 1 },
+
+ /* other */
+ { IR_SEQ, "IR_SEQ", OPCODE_NOP, 0, 0 },
+ { IR_SCOPE, "IR_SCOPE", OPCODE_NOP, 0, 0 },
+ { IR_LABEL, "IR_LABEL", OPCODE_NOP, 0, 0 },
+ { IR_JUMP, "IR_JUMP", OPCODE_NOP, 0, 0 },
+ { IR_IF, "IR_IF", OPCODE_NOP, 0, 0 },
+ { IR_KILL, "IR_KILL", OPCODE_NOP, 0, 0 },
+ { IR_COND, "IR_COND", OPCODE_NOP, 0, 0 },
+ { IR_CALL, "IR_CALL", OPCODE_NOP, 0, 0 },
+ { IR_MOVE, "IR_MOVE", OPCODE_NOP, 0, 1 },
+ { IR_NOT, "IR_NOT", OPCODE_NOP, 1, 1 },
+ { IR_VAR, "IR_VAR", OPCODE_NOP, 0, 0 },
+ { IR_VAR_DECL, "IR_VAR_DECL", OPCODE_NOP, 0, 0 },
+ { IR_TEX, "IR_TEX", OPCODE_TEX, 4, 1 },
+ { IR_TEXB, "IR_TEXB", OPCODE_TXB, 4, 1 },
+ { IR_TEXP, "IR_TEXP", OPCODE_TXP, 4, 1 },
+ { IR_FLOAT, "IR_FLOAT", OPCODE_NOP, 0, 0 }, /* float literal */
+ { IR_FIELD, "IR_FIELD", OPCODE_NOP, 0, 0 },
+ { IR_ELEMENT, "IR_ELEMENT", OPCODE_NOP, 0, 0 },
+ { IR_SWIZZLE, "IR_SWIZZLE", OPCODE_NOP, 0, 0 },
+ { IR_NOP, NULL, OPCODE_NOP, 0, 0 }
+};
+
+
+static const slang_ir_info *
+slang_find_ir_info(slang_ir_opcode opcode)
+{
+ GLuint i;
+ for (i = 0; IrInfo[i].IrName; i++) {
+ if (IrInfo[i].IrOpcode == opcode) {
+ return IrInfo + i;
+ }
+ }
+ return NULL;
+}
+
+static const char *
+slang_ir_name(slang_ir_opcode opcode)
+{
+ return slang_find_ir_info(opcode)->IrName;
+}
+
+
+/**
+ * Swizzle a swizzle. That is, return swz2(swz1)
+ */
+static GLuint
+swizzle_swizzle(GLuint swz1, GLuint swz2)
+{
+ GLuint i, swz, s[4];
+ for (i = 0; i < 4; i++) {
+ GLuint c = GET_SWZ(swz2, i);
+ s[i] = GET_SWZ(swz1, c);
+ }
+ swz = MAKE_SWIZZLE4(s[0], s[1], s[2], s[3]);
+ return swz;
+}
+
+
+slang_ir_storage *
+_slang_new_ir_storage(enum register_file file, GLint index, GLint size)
+{
+ slang_ir_storage *st;
+ st = (slang_ir_storage *) _mesa_calloc(sizeof(slang_ir_storage));
+ if (st) {
+ st->File = file;
+ st->Index = index;
+ st->Size = size;
+ st->Swizzle = SWIZZLE_NOOP;
+ }
+ return st;
+}
+
+
+static const char *
+swizzle_string(GLuint swizzle)
+{
+ static char s[6];
+ GLuint i;
+ s[0] = '.';
+ for (i = 1; i < 5; i++) {
+ s[i] = "xyzw"[GET_SWZ(swizzle, i-1)];
+ }
+ s[i] = 0;
+ return s;
+}
+
+static const char *
+writemask_string(GLuint writemask)
+{
+ static char s[6];
+ GLuint i, j = 0;
+ s[j++] = '.';
+ for (i = 0; i < 4; i++) {
+ if (writemask & (1 << i))
+ s[j++] = "xyzw"[i];
+ }
+ s[j] = 0;
+ return s;
+}
+
+static const char *
+storage_string(const slang_ir_storage *st)
+{
+ static const char *files[] = {
+ "TEMP",
+ "LOCAL_PARAM",
+ "ENV_PARAM",
+ "STATE",
+ "INPUT",
+ "OUTPUT",
+ "NAMED_PARAM",
+ "CONSTANT",
+ "UNIFORM",
+ "WRITE_ONLY",
+ "ADDRESS",
+ "SAMPLER",
+ "UNDEFINED"
+ };
+ static char s[100];
+#if 0
+ if (st->Size == 1)
+ sprintf(s, "%s[%d]", files[st->File], st->Index);
+ else
+ sprintf(s, "%s[%d..%d]", files[st->File], st->Index,
+ st->Index + st->Size - 1);
+#endif
+ assert(st->File < (GLint) (sizeof(files) / sizeof(files[0])));
+ sprintf(s, "%s[%d]", files[st->File], st->Index);
+ return s;
+}
+
+
+static void
+spaces(int n)
+{
+ while (n-- > 0) {
+ printf(" ");
+ }
+}
+
+#define IND 0
+void
+slang_print_ir(const slang_ir_node *n, int indent)
+{
+ if (!n)
+ return;
+#if !IND
+ if (n->Opcode != IR_SEQ)
+#else
+ printf("%3d:", indent);
+#endif
+ spaces(indent);
+
+ switch (n->Opcode) {
+ case IR_SEQ:
+#if IND
+ printf("SEQ at %p\n", (void*) n);
+#endif
+ assert(n->Children[0]);
+ assert(n->Children[1]);
+ slang_print_ir(n->Children[0], indent + IND);
+ slang_print_ir(n->Children[1], indent + IND);
+ break;
+ case IR_SCOPE:
+ printf("NEW SCOPE\n");
+ assert(!n->Children[1]);
+ slang_print_ir(n->Children[0], indent + 3);
+ break;
+ case IR_MOVE:
+ printf("MOVE (writemask = %s)\n", writemask_string(n->Writemask));
+ slang_print_ir(n->Children[0], indent+3);
+ slang_print_ir(n->Children[1], indent+3);
+ break;
+ case IR_LABEL:
+ printf("LABEL: %s\n", n->Label->Name);
+ break;
+ case IR_COND:
+ printf("COND\n");
+ slang_print_ir(n->Children[0], indent + 3);
+ break;
+ case IR_JUMP:
+ printf("JUMP %s\n", n->Label->Name);
+ break;
+
+ case IR_IF:
+ printf("IF \n");
+ slang_print_ir(n->Children[0], indent+3);
+ spaces(indent);
+ printf("THEN\n");
+ slang_print_ir(n->Children[1], indent+3);
+ if (n->Children[2]) {
+ spaces(indent);
+ printf("ELSE\n");
+ slang_print_ir(n->Children[2], indent+3);
+ }
+ printf("ENDIF\n");
+ break;
+
+ case IR_BEGIN_SUB:
+ printf("BEGIN_SUB\n");
+ break;
+ case IR_END_SUB:
+ printf("END_SUB\n");
+ break;
+ case IR_RETURN:
+ printf("RETURN\n");
+ break;
+ case IR_CALL:
+ printf("CALL\n");
+ break;
+
+ case IR_LOOP:
+ printf("LOOP\n");
+ slang_print_ir(n->Children[0], indent+3);
+ spaces(indent);
+ printf("ENDLOOP\n");
+ break;
+ case IR_CONT:
+ printf("CONT\n");
+ break;
+ case IR_BREAK:
+ printf("BREAK\n");
+ break;
+ case IR_BREAK_IF_FALSE:
+ printf("BREAK_IF_FALSE\n");
+ slang_print_ir(n->Children[0], indent+3);
+ break;
+ case IR_BREAK_IF_TRUE:
+ printf("BREAK_IF_TRUE\n");
+ slang_print_ir(n->Children[0], indent+3);
+ break;
+ case IR_CONT_IF_FALSE:
+ printf("CONT_IF_FALSE\n");
+ slang_print_ir(n->Children[0], indent+3);
+ break;
+ case IR_CONT_IF_TRUE:
+ printf("CONT_IF_TRUE\n");
+ slang_print_ir(n->Children[0], indent+3);
+ break;
+
+ case IR_VAR:
+ printf("VAR %s%s at %s store %p\n",
+ (n->Var ? (char *) n->Var->a_name : "TEMP"),
+ swizzle_string(n->Store->Swizzle),
+ storage_string(n->Store), (void*) n->Store);
+ break;
+ case IR_VAR_DECL:
+ printf("VAR_DECL %s (%p) at %s store %p\n",
+ (n->Var ? (char *) n->Var->a_name : "TEMP"),
+ (void*) n->Var, storage_string(n->Store),
+ (void*) n->Store);
+ break;
+ case IR_FIELD:
+ printf("FIELD %s of\n", n->Field);
+ slang_print_ir(n->Children[0], indent+3);
+ break;
+ case IR_FLOAT:
+ printf("FLOAT %g %g %g %g\n",
+ n->Value[0], n->Value[1], n->Value[2], n->Value[3]);
+ break;
+ case IR_I_TO_F:
+ printf("INT_TO_FLOAT\n");
+ slang_print_ir(n->Children[0], indent+3);
+ break;
+ case IR_F_TO_I:
+ printf("FLOAT_TO_INT\n");
+ slang_print_ir(n->Children[0], indent+3);
+ break;
+ case IR_SWIZZLE:
+ printf("SWIZZLE %s of (store %p) \n",
+ swizzle_string(n->Store->Swizzle), (void*) n->Store);
+ slang_print_ir(n->Children[0], indent + 3);
+ break;
+ default:
+ printf("%s (%p, %p) (store %p)\n", slang_ir_name(n->Opcode),
+ (void*) n->Children[0], (void*) n->Children[1], (void*) n->Store);
+ slang_print_ir(n->Children[0], indent+3);
+ slang_print_ir(n->Children[1], indent+3);
+ }
+}
+
+
+/**
+ * Allocate temporary storage for an intermediate result (such as for
+ * a multiply or add, etc.
+ */
+static GLboolean
+alloc_temp_storage(slang_emit_info *emitInfo, slang_ir_node *n, GLint size)
+{
+ assert(!n->Var);
+ assert(!n->Store);
+ assert(size > 0);
+ n->Store = _slang_new_ir_storage(PROGRAM_TEMPORARY, -1, size);
+ if (!_slang_alloc_temp(emitInfo->vt, n->Store)) {
+ slang_info_log_error(emitInfo->log,
+ "Ran out of registers, too many temporaries");
+ return GL_FALSE;
+ }
+ return GL_TRUE;
+}
+
+
+/**
+ * Free temporary storage, if n->Store is, in fact, temp storage.
+ * Otherwise, no-op.
+ */
+static void
+free_temp_storage(slang_var_table *vt, slang_ir_node *n)
+{
+ if (n->Store->File == PROGRAM_TEMPORARY && n->Store->Index >= 0) {
+ if (_slang_is_temp(vt, n->Store)) {
+ _slang_free_temp(vt, n->Store);
+ n->Store->Index = -1;
+ n->Store->Size = -1;
+ }
+ }
+}
+
+
+/**
+ * Convert IR storage to an instruction dst register.
+ */
+static void
+storage_to_dst_reg(struct prog_dst_register *dst, const slang_ir_storage *st,
+ GLuint writemask)
+{
+ static const GLuint defaultWritemask[4] = {
+ WRITEMASK_X,
+ WRITEMASK_X | WRITEMASK_Y,
+ WRITEMASK_X | WRITEMASK_Y | WRITEMASK_Z,
+ WRITEMASK_X | WRITEMASK_Y | WRITEMASK_Z | WRITEMASK_W
+ };
+ assert(st->Index >= 0);
+ dst->File = st->File;
+ dst->Index = st->Index;
+ assert(st->File != PROGRAM_UNDEFINED);
+ assert(st->Size >= 1);
+ assert(st->Size <= 4);
+ if (st->Size == 1) {
+ GLuint comp = GET_SWZ(st->Swizzle, 0);
+ assert(comp < 4);
+ assert(writemask & WRITEMASK_X);
+ dst->WriteMask = WRITEMASK_X << comp;
+ }
+ else {
+ dst->WriteMask = defaultWritemask[st->Size - 1] & writemask;
+ }
+}
+
+
+/**
+ * Convert IR storage to an instruction src register.
+ */
+static void
+storage_to_src_reg(struct prog_src_register *src, const slang_ir_storage *st)
+{
+ static const GLuint defaultSwizzle[4] = {
+ MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X),
+ MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W),
+ MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W),
+ MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W)
+ };
+ assert(st->File >= 0);
+ assert(st->File < PROGRAM_UNDEFINED);
+ assert(st->Size >= 1);
+ assert(st->Size <= 4);
+ src->File = st->File;
+ src->Index = st->Index;
+ if (st->Swizzle != SWIZZLE_NOOP)
+ src->Swizzle = st->Swizzle;
+ else
+ src->Swizzle = defaultSwizzle[st->Size - 1]; /*XXX really need this?*/
+
+ assert(GET_SWZ(src->Swizzle, 0) != SWIZZLE_NIL);
+ assert(GET_SWZ(src->Swizzle, 1) != SWIZZLE_NIL);
+ assert(GET_SWZ(src->Swizzle, 2) != SWIZZLE_NIL);
+ assert(GET_SWZ(src->Swizzle, 3) != SWIZZLE_NIL);
+}
+
+
+
+/**
+ * Add new instruction at end of given program.
+ * \param prog the program to append instruction onto
+ * \param opcode opcode for the new instruction
+ * \return pointer to the new instruction
+ */
+static struct prog_instruction *
+new_instruction(slang_emit_info *emitInfo, gl_inst_opcode opcode)
+{
+ struct gl_program *prog = emitInfo->prog;
+ struct prog_instruction *inst;
+ prog->Instructions = _mesa_realloc_instructions(prog->Instructions,
+ prog->NumInstructions,
+ prog->NumInstructions + 1);
+ inst = prog->Instructions + prog->NumInstructions;
+ prog->NumInstructions++;
+ _mesa_init_instructions(inst, 1);
+ inst->Opcode = opcode;
+ inst->BranchTarget = -1; /* invalid */
+ /*
+ printf("New inst %d: %p %s\n", prog->NumInstructions-1,(void*)inst,
+ _mesa_opcode_string(inst->Opcode));
+ */
+ return inst;
+}
+
+
+#if 0
+/**
+ * Return pointer to last instruction in program.
+ */
+static struct prog_instruction *
+prev_instruction(struct gl_program *prog)
+{
+ if (prog->NumInstructions == 0)
+ return NULL;
+ else
+ return prog->Instructions + prog->NumInstructions - 1;
+}
+#endif
+
+
+static struct prog_instruction *
+emit(slang_emit_info *emitInfo, slang_ir_node *n);
+
+
+/**
+ * Return an annotation string for given node's storage.
+ */
+static char *
+storage_annotation(const slang_ir_node *n, const struct gl_program *prog)
+{
+#if ANNOTATE
+ const slang_ir_storage *st = n->Store;
+ static char s[100] = "";
+
+ if (!st)
+ return _mesa_strdup("");
+
+ switch (st->File) {
+ case PROGRAM_CONSTANT:
+ if (st->Index >= 0) {
+ const GLfloat *val = prog->Parameters->ParameterValues[st->Index];
+ if (st->Swizzle == SWIZZLE_NOOP)
+ sprintf(s, "{%g, %g, %g, %g}", val[0], val[1], val[2], val[3]);
+ else {
+ sprintf(s, "%g", val[GET_SWZ(st->Swizzle, 0)]);
+ }
+ }
+ break;
+ case PROGRAM_TEMPORARY:
+ if (n->Var)
+ sprintf(s, "%s", (char *) n->Var->a_name);
+ else
+ sprintf(s, "t[%d]", st->Index);
+ break;
+ case PROGRAM_STATE_VAR:
+ case PROGRAM_UNIFORM:
+ sprintf(s, "%s", prog->Parameters->Parameters[st->Index].Name);
+ break;
+ case PROGRAM_VARYING:
+ sprintf(s, "%s", prog->Varying->Parameters[st->Index].Name);
+ break;
+ case PROGRAM_INPUT:
+ sprintf(s, "input[%d]", st->Index);
+ break;
+ case PROGRAM_OUTPUT:
+ sprintf(s, "output[%d]", st->Index);
+ break;
+ default:
+ s[0] = 0;
+ }
+ return _mesa_strdup(s);
+#else
+ return NULL;
+#endif
+}
+
+
+/**
+ * Return an annotation string for an instruction.
+ */
+static char *
+instruction_annotation(gl_inst_opcode opcode, char *dstAnnot,
+ char *srcAnnot0, char *srcAnnot1, char *srcAnnot2)
+{
+#if ANNOTATE
+ const char *operator;
+ char *s;
+ int len = 50;
+
+ if (dstAnnot)
+ len += strlen(dstAnnot);
+ else
+ dstAnnot = _mesa_strdup("");
+
+ if (srcAnnot0)
+ len += strlen(srcAnnot0);
+ else
+ srcAnnot0 = _mesa_strdup("");
+
+ if (srcAnnot1)
+ len += strlen(srcAnnot1);
+ else
+ srcAnnot1 = _mesa_strdup("");
+
+ if (srcAnnot2)
+ len += strlen(srcAnnot2);
+ else
+ srcAnnot2 = _mesa_strdup("");
+
+ switch (opcode) {
+ case OPCODE_ADD:
+ operator = "+";
+ break;
+ case OPCODE_SUB:
+ operator = "-";
+ break;
+ case OPCODE_MUL:
+ operator = "*";
+ break;
+ case OPCODE_DP3:
+ operator = "DP3";
+ break;
+ case OPCODE_DP4:
+ operator = "DP4";
+ break;
+ case OPCODE_XPD:
+ operator = "XPD";
+ break;
+ case OPCODE_RSQ:
+ operator = "RSQ";
+ break;
+ case OPCODE_SGT:
+ operator = ">";
+ break;
+ default:
+ operator = ",";
+ }
+
+ s = (char *) malloc(len);
+ sprintf(s, "%s = %s %s %s %s", dstAnnot,
+ srcAnnot0, operator, srcAnnot1, srcAnnot2);
+ assert(_mesa_strlen(s) < len);
+
+ free(dstAnnot);
+ free(srcAnnot0);
+ free(srcAnnot1);
+ free(srcAnnot2);
+
+ return s;
+#else
+ return NULL;
+#endif
+}
+
+
+
+/**
+ * Generate code for a simple arithmetic instruction.
+ * Either 1, 2 or 3 operands.
+ */
+static struct prog_instruction *
+emit_arith(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ struct prog_instruction *inst;
+ const slang_ir_info *info = slang_find_ir_info(n->Opcode);
+ char *srcAnnot[3], *dstAnnot;
+ GLuint i;
+
+ assert(info);
+ assert(info->InstOpcode != OPCODE_NOP);
+
+ srcAnnot[0] = srcAnnot[1] = srcAnnot[2] = dstAnnot = NULL;
+
+#if PEEPHOLE_OPTIMIZATIONS
+ /* Look for MAD opportunity */
+ if (info->NumParams == 2 &&
+ n->Opcode == IR_ADD && n->Children[0]->Opcode == IR_MUL) {
+ /* found pattern IR_ADD(IR_MUL(A, B), C) */
+ emit(emitInfo, n->Children[0]->Children[0]); /* A */
+ emit(emitInfo, n->Children[0]->Children[1]); /* B */
+ emit(emitInfo, n->Children[1]); /* C */
+ /* generate MAD instruction */
+ inst = new_instruction(emitInfo, OPCODE_MAD);
+ /* operands: A, B, C: */
+ storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Children[0]->Store);
+ storage_to_src_reg(&inst->SrcReg[1], n->Children[0]->Children[1]->Store);
+ storage_to_src_reg(&inst->SrcReg[2], n->Children[1]->Store);
+ free_temp_storage(emitInfo->vt, n->Children[0]->Children[0]);
+ free_temp_storage(emitInfo->vt, n->Children[0]->Children[1]);
+ free_temp_storage(emitInfo->vt, n->Children[1]);
+ }
+ else if (info->NumParams == 2 &&
+ n->Opcode == IR_ADD && n->Children[1]->Opcode == IR_MUL) {
+ /* found pattern IR_ADD(A, IR_MUL(B, C)) */
+ emit(emitInfo, n->Children[0]); /* A */
+ emit(emitInfo, n->Children[1]->Children[0]); /* B */
+ emit(emitInfo, n->Children[1]->Children[1]); /* C */
+ /* generate MAD instruction */
+ inst = new_instruction(emitInfo, OPCODE_MAD);
+ /* operands: B, C, A */
+ storage_to_src_reg(&inst->SrcReg[0], n->Children[1]->Children[0]->Store);
+ storage_to_src_reg(&inst->SrcReg[1], n->Children[1]->Children[1]->Store);
+ storage_to_src_reg(&inst->SrcReg[2], n->Children[0]->Store);
+ free_temp_storage(emitInfo->vt, n->Children[1]->Children[0]);
+ free_temp_storage(emitInfo->vt, n->Children[1]->Children[1]);
+ free_temp_storage(emitInfo->vt, n->Children[0]);
+ }
+ else
+#endif
+ {
+ /* normal case */
+
+ /* gen code for children */
+ for (i = 0; i < info->NumParams; i++)
+ emit(emitInfo, n->Children[i]);
+
+ /* gen this instruction and src registers */
+ inst = new_instruction(emitInfo, info->InstOpcode);
+ for (i = 0; i < info->NumParams; i++)
+ storage_to_src_reg(&inst->SrcReg[i], n->Children[i]->Store);
+
+ /* annotation */
+ for (i = 0; i < info->NumParams; i++)
+ srcAnnot[i] = storage_annotation(n->Children[i], emitInfo->prog);
+
+ /* free temps */
+ for (i = 0; i < info->NumParams; i++)
+ free_temp_storage(emitInfo->vt, n->Children[i]);
+ }
+
+ /* result storage */
+ if (!n->Store) {
+ if (!alloc_temp_storage(emitInfo, n, info->ResultSize))
+ return NULL;
+ }
+ storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+
+ dstAnnot = storage_annotation(n, emitInfo->prog);
+
+ inst->Comment = instruction_annotation(inst->Opcode, dstAnnot, srcAnnot[0],
+ srcAnnot[1], srcAnnot[2]);
+
+ /*_mesa_print_instruction(inst);*/
+ return inst;
+}
+
+
+/**
+ * Generate code for an IR_CLAMP instruction.
+ */
+static struct prog_instruction *
+emit_clamp(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ struct prog_instruction *inst;
+
+ assert(n->Opcode == IR_CLAMP);
+ /* ch[0] = value
+ * ch[1] = min limit
+ * ch[2] = max limit
+ */
+
+ inst = emit(emitInfo, n->Children[0]);
+
+ /* If lower limit == 0.0 and upper limit == 1.0,
+ * set prev instruction's SaturateMode field to SATURATE_ZERO_ONE.
+ * Else,
+ * emit OPCODE_MIN, OPCODE_MAX sequence.
+ */
+#if 0
+ /* XXX this isn't quite finished yet */
+ if (n->Children[1]->Opcode == IR_FLOAT &&
+ n->Children[1]->Value[0] == 0.0 &&
+ n->Children[1]->Value[1] == 0.0 &&
+ n->Children[1]->Value[2] == 0.0 &&
+ n->Children[1]->Value[3] == 0.0 &&
+ n->Children[2]->Opcode == IR_FLOAT &&
+ n->Children[2]->Value[0] == 1.0 &&
+ n->Children[2]->Value[1] == 1.0 &&
+ n->Children[2]->Value[2] == 1.0 &&
+ n->Children[2]->Value[3] == 1.0) {
+ if (!inst) {
+ inst = prev_instruction(prog);
+ }
+ if (inst && inst->Opcode != OPCODE_NOP) {
+ /* and prev instruction's DstReg matches n->Children[0]->Store */
+ inst->SaturateMode = SATURATE_ZERO_ONE;
+ n->Store = n->Children[0]->Store;
+ return inst;
+ }
+ }
+#endif
+
+ if (!n->Store)
+ if (!alloc_temp_storage(emitInfo, n, n->Children[0]->Store->Size))
+ return NULL;
+
+ emit(emitInfo, n->Children[1]);
+ emit(emitInfo, n->Children[2]);
+
+ /* tmp = max(ch[0], ch[1]) */
+ inst = new_instruction(emitInfo, OPCODE_MAX);
+ storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+ storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
+ storage_to_src_reg(&inst->SrcReg[1], n->Children[1]->Store);
+
+ /* tmp = min(tmp, ch[2]) */
+ inst = new_instruction(emitInfo, OPCODE_MIN);
+ storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+ storage_to_src_reg(&inst->SrcReg[0], n->Store);
+ storage_to_src_reg(&inst->SrcReg[1], n->Children[2]->Store);
+
+ return inst;
+}
+
+
+static struct prog_instruction *
+emit_negation(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ /* Implement as MOV dst, -src; */
+ /* XXX we could look at the previous instruction and in some circumstances
+ * modify it to accomplish the negation.
+ */
+ struct prog_instruction *inst;
+
+ emit(emitInfo, n->Children[0]);
+
+ if (!n->Store)
+ if (!alloc_temp_storage(emitInfo, n, n->Children[0]->Store->Size))
+ return NULL;
+
+ inst = new_instruction(emitInfo, OPCODE_MOV);
+ storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+ storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
+ inst->SrcReg[0].NegateBase = NEGATE_XYZW;
+ return inst;
+}
+
+
+static struct prog_instruction *
+emit_label(slang_emit_info *emitInfo, const slang_ir_node *n)
+{
+ assert(n->Label);
+ assert(_slang_label_get_location(n->Label) < 0);
+ _slang_label_set_location(n->Label, emitInfo->prog->NumInstructions,
+ emitInfo->prog);
+ return NULL;
+}
+
+
+static struct prog_instruction *
+emit_jump(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ struct prog_instruction *inst;
+ assert(n);
+ assert(n->Label);
+ inst = new_instruction(emitInfo, OPCODE_BRA);
+ inst->DstReg.CondMask = COND_TR; /* always branch */
+ inst->BranchTarget = _slang_label_get_location(n->Label);
+ if (inst->BranchTarget < 0) {
+ _slang_label_add_reference(n->Label, emitInfo->prog->NumInstructions - 1);
+ }
+ return inst;
+}
+
+
+static struct prog_instruction *
+emit_kill(slang_emit_info *emitInfo)
+{
+ struct prog_instruction *inst;
+ /* NV-KILL - discard fragment depending on condition code.
+ * Note that ARB-KILL depends on sign of vector operand.
+ */
+ inst = new_instruction(emitInfo, OPCODE_KIL_NV);
+ inst->DstReg.CondMask = COND_TR; /* always branch */
+ return inst;
+}
+
+
+static struct prog_instruction *
+emit_tex(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ struct prog_instruction *inst;
+
+ (void) emit(emitInfo, n->Children[1]);
+
+ if (n->Opcode == IR_TEX) {
+ inst = new_instruction(emitInfo, OPCODE_TEX);
+ }
+ else if (n->Opcode == IR_TEXB) {
+ inst = new_instruction(emitInfo, OPCODE_TXB);
+ }
+ else {
+ assert(n->Opcode == IR_TEXP);
+ inst = new_instruction(emitInfo, OPCODE_TXP);
+ }
+
+ if (!n->Store)
+ if (!alloc_temp_storage(emitInfo, n, 4))
+ return NULL;
+
+ storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+
+ /* Child[1] is the coord */
+ assert(n->Children[1]->Store->File != PROGRAM_UNDEFINED);
+ assert(n->Children[1]->Store->Index >= 0);
+ storage_to_src_reg(&inst->SrcReg[0], n->Children[1]->Store);
+
+ /* Child[0] is the sampler (a uniform which'll indicate the texture unit) */
+ assert(n->Children[0]->Store);
+ assert(n->Children[0]->Store->Size >= TEXTURE_1D_INDEX);
+
+ inst->Sampler = n->Children[0]->Store->Index; /* i.e. uniform's index */
+ inst->TexSrcTarget = n->Children[0]->Store->Size;
+ inst->TexSrcUnit = 27; /* Dummy value; the TexSrcUnit will be computed at
+ * link time, using the sampler uniform's value.
+ */
+ return inst;
+}
+
+
+static struct prog_instruction *
+emit_move(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ struct prog_instruction *inst;
+
+ /* lhs */
+ emit(emitInfo, n->Children[0]);
+
+ /* rhs */
+ assert(n->Children[1]);
+ inst = emit(emitInfo, n->Children[1]);
+
+ assert(n->Children[1]->Store->Index >= 0);
+
+ assert(!n->Store);
+ n->Store = n->Children[0]->Store;
+
+#if PEEPHOLE_OPTIMIZATIONS
+ if (inst && _slang_is_temp(emitInfo->vt, n->Children[1]->Store)) {
+ /* Peephole optimization:
+ * Just modify the RHS to put its result into the dest of this
+ * MOVE operation. Then, this MOVE is a no-op.
+ */
+ _slang_free_temp(emitInfo->vt, n->Children[1]->Store);
+ *n->Children[1]->Store = *n->Children[0]->Store;
+ /* fixup the prev (RHS) instruction */
+ assert(n->Children[0]->Store->Index >= 0);
+ storage_to_dst_reg(&inst->DstReg, n->Children[0]->Store, n->Writemask);
+ return inst;
+ }
+ else
+#endif
+ {
+ if (n->Children[0]->Store->Size > 4) {
+ /* move matrix/struct etc (block of registers) */
+ slang_ir_storage dstStore = *n->Children[0]->Store;
+ slang_ir_storage srcStore = *n->Children[1]->Store;
+ GLint size = srcStore.Size;
+ ASSERT(n->Children[0]->Writemask == WRITEMASK_XYZW);
+ ASSERT(n->Children[1]->Store->Swizzle == SWIZZLE_NOOP);
+ dstStore.Size = 4;
+ srcStore.Size = 4;
+ while (size >= 4) {
+ inst = new_instruction(emitInfo, OPCODE_MOV);
+ inst->Comment = _mesa_strdup("IR_MOVE block");
+ storage_to_dst_reg(&inst->DstReg, &dstStore, n->Writemask);
+ storage_to_src_reg(&inst->SrcReg[0], &srcStore);
+ srcStore.Index++;
+ dstStore.Index++;
+ size -= 4;
+ }
+ }
+ else {
+ /* single register move */
+ char *srcAnnot, *dstAnnot;
+ inst = new_instruction(emitInfo, OPCODE_MOV);
+ assert(n->Children[0]->Store->Index >= 0);
+ storage_to_dst_reg(&inst->DstReg, n->Children[0]->Store, n->Writemask);
+ storage_to_src_reg(&inst->SrcReg[0], n->Children[1]->Store);
+ dstAnnot = storage_annotation(n->Children[0], emitInfo->prog);
+ srcAnnot = storage_annotation(n->Children[1], emitInfo->prog);
+ inst->Comment = instruction_annotation(inst->Opcode, dstAnnot,
+ srcAnnot, NULL, NULL);
+ }
+ free_temp_storage(emitInfo->vt, n->Children[1]);
+ return inst;
+ }
+}
+
+
+static struct prog_instruction *
+emit_cond(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ /* Conditional expression (in if/while/for stmts).
+ * Need to update condition code register.
+ * Next instruction is typically an IR_IF.
+ */
+ struct prog_instruction *inst;
+
+ if (!n->Children[0])
+ return NULL;
+
+ inst = emit(emitInfo, n->Children[0]);
+ if (inst) {
+ /* set inst's CondUpdate flag */
+ inst->CondUpdate = GL_TRUE;
+ return inst; /* XXX or null? */
+ }
+ else {
+ /* This'll happen for things like "if (i) ..." where no code
+ * is normally generated for the expression "i".
+ * Generate a move instruction just to set condition codes.
+ * Note: must use full 4-component vector since all four
+ * condition codes must be set identically.
+ */
+ if (!alloc_temp_storage(emitInfo, n, 4))
+ return NULL;
+ inst = new_instruction(emitInfo, OPCODE_MOV);
+ inst->CondUpdate = GL_TRUE;
+ storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+ storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
+ _slang_free_temp(emitInfo->vt, n->Store);
+ inst->Comment = _mesa_strdup("COND expr");
+ return inst; /* XXX or null? */
+ }
+}
+
+
+/**
+ * Logical-NOT
+ */
+static struct prog_instruction *
+emit_not(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ GLfloat zero = 0.0;
+ slang_ir_storage st;
+ struct prog_instruction *inst;
+
+ /* need zero constant */
+ st.File = PROGRAM_CONSTANT;
+ st.Size = 1;
+ st.Index = _mesa_add_unnamed_constant(emitInfo->prog->Parameters, &zero,
+ 1, &st.Swizzle);
+
+ /* child expr */
+ (void) emit(emitInfo, n->Children[0]);
+ /* XXXX if child instr is SGT convert to SLE, if SEQ, SNE, etc */
+
+ if (!n->Store)
+ if (!alloc_temp_storage(emitInfo, n, n->Children[0]->Store->Size))
+ return NULL;
+
+ inst = new_instruction(emitInfo, OPCODE_SEQ);
+ storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+ storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
+ storage_to_src_reg(&inst->SrcReg[1], &st);
+
+ free_temp_storage(emitInfo->vt, n->Children[0]);
+
+ inst->Comment = _mesa_strdup("NOT");
+ return inst;
+}
+
+
+static struct prog_instruction *
+emit_if(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ struct gl_program *prog = emitInfo->prog;
+ struct prog_instruction *ifInst;
+ GLuint ifInstLoc, elseInstLoc = 0;
+
+ emit(emitInfo, n->Children[0]); /* the condition */
+ ifInstLoc = prog->NumInstructions;
+ if (emitInfo->EmitHighLevelInstructions) {
+ ifInst = new_instruction(emitInfo, OPCODE_IF);
+ ifInst->DstReg.CondMask = COND_NE; /* if cond is non-zero */
+ ifInst->DstReg.CondSwizzle = SWIZZLE_X;
+ }
+ else {
+ /* conditional jump to else, or endif */
+ ifInst = new_instruction(emitInfo, OPCODE_BRA);
+ ifInst->DstReg.CondMask = COND_EQ; /* BRA if cond is zero */
+ ifInst->DstReg.CondSwizzle = SWIZZLE_X;
+ ifInst->Comment = _mesa_strdup("if zero");
+ }
+
+ /* if body */
+ emit(emitInfo, n->Children[1]);
+
+ if (n->Children[2]) {
+ /* have else body */
+ elseInstLoc = prog->NumInstructions;
+ if (emitInfo->EmitHighLevelInstructions) {
+ (void) new_instruction(emitInfo, OPCODE_ELSE);
+ }
+ else {
+ /* jump to endif instruction */
+ struct prog_instruction *inst;
+ inst = new_instruction(emitInfo, OPCODE_BRA);
+ inst->Comment = _mesa_strdup("else");
+ inst->DstReg.CondMask = COND_TR; /* always branch */
+ }
+ ifInst = prog->Instructions + ifInstLoc;
+ ifInst->BranchTarget = prog->NumInstructions;
+
+ emit(emitInfo, n->Children[2]);
+ }
+ else {
+ /* no else body */
+ ifInst = prog->Instructions + ifInstLoc;
+ ifInst->BranchTarget = prog->NumInstructions + 1;
+ }
+
+ if (emitInfo->EmitHighLevelInstructions) {
+ (void) new_instruction(emitInfo, OPCODE_ENDIF);
+ }
+
+ if (n->Children[2]) {
+ struct prog_instruction *elseInst;
+ elseInst = prog->Instructions + elseInstLoc;
+ elseInst->BranchTarget = prog->NumInstructions;
+ }
+ return NULL;
+}
+
+
+static struct prog_instruction *
+emit_loop(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ struct gl_program *prog = emitInfo->prog;
+ struct prog_instruction *beginInst, *endInst;
+ GLuint beginInstLoc, endInstLoc;
+ slang_ir_node *ir;
+
+ /* emit OPCODE_BGNLOOP */
+ beginInstLoc = prog->NumInstructions;
+ if (emitInfo->EmitHighLevelInstructions) {
+ (void) new_instruction(emitInfo, OPCODE_BGNLOOP);
+ }
+
+ /* body */
+ emit(emitInfo, n->Children[0]);
+
+ endInstLoc = prog->NumInstructions;
+ if (emitInfo->EmitHighLevelInstructions) {
+ /* emit OPCODE_ENDLOOP */
+ endInst = new_instruction(emitInfo, OPCODE_ENDLOOP);
+ }
+ else {
+ /* emit unconditional BRA-nch */
+ endInst = new_instruction(emitInfo, OPCODE_BRA);
+ endInst->DstReg.CondMask = COND_TR; /* always true */
+ }
+ /* end instruction's BranchTarget points to top of loop */
+ endInst->BranchTarget = beginInstLoc;
+
+ if (emitInfo->EmitHighLevelInstructions) {
+ /* BGNLOOP's BranchTarget points to the ENDLOOP inst */
+ beginInst = prog->Instructions + beginInstLoc;
+ beginInst->BranchTarget = prog->NumInstructions - 1;
+ }
+
+ /* Done emitting loop code. Now walk over the loop's linked list of
+ * BREAK and CONT nodes, filling in their BranchTarget fields (which
+ * will point to the ENDLOOP+1 or BGNLOOP instructions, respectively).
+ */
+ for (ir = n->BranchNode; ir; ir = ir->BranchNode) {
+ struct prog_instruction *inst = prog->Instructions + ir->InstLocation;
+ assert(inst->BranchTarget < 0);
+ if (ir->Opcode == IR_BREAK ||
+ ir->Opcode == IR_BREAK_IF_FALSE ||
+ ir->Opcode == IR_BREAK_IF_TRUE) {
+ assert(inst->Opcode == OPCODE_BRK ||
+ inst->Opcode == OPCODE_BRA);
+ /* go to instruction after end of loop */
+ inst->BranchTarget = endInstLoc + 1;
+ }
+ else {
+ assert(ir->Opcode == IR_CONT ||
+ ir->Opcode == IR_CONT_IF_FALSE ||
+ ir->Opcode == IR_CONT_IF_TRUE);
+ assert(inst->Opcode == OPCODE_CONT ||
+ inst->Opcode == OPCODE_BRA);
+ /* to go instruction at top of loop */
+ inst->BranchTarget = beginInstLoc;
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ * "Continue" or "break" statement.
+ * Either OPCODE_CONT, OPCODE_BRK or OPCODE_BRA will be emitted.
+ */
+static struct prog_instruction *
+emit_cont_break(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ gl_inst_opcode opcode;
+ struct prog_instruction *inst;
+ n->InstLocation = emitInfo->prog->NumInstructions;
+ if (emitInfo->EmitHighLevelInstructions) {
+ opcode = (n->Opcode == IR_CONT) ? OPCODE_CONT : OPCODE_BRK;
+ }
+ else {
+ opcode = OPCODE_BRA;
+ }
+ inst = new_instruction(emitInfo, opcode);
+ inst->DstReg.CondMask = COND_TR; /* always true */
+ return inst;
+}
+
+
+/**
+ * Conditional "continue" or "break" statement.
+ * Either OPCODE_CONT, OPCODE_BRK or OPCODE_BRA will be emitted.
+ */
+static struct prog_instruction *
+emit_cont_break_if(slang_emit_info *emitInfo, slang_ir_node *n,
+ GLboolean breakTrue)
+{
+ gl_inst_opcode opcode;
+ struct prog_instruction *inst;
+
+ /* evaluate condition expr, setting cond codes */
+ inst = emit(emitInfo, n->Children[0]);
+ assert(inst);
+ inst->CondUpdate = GL_TRUE;
+
+ n->InstLocation = emitInfo->prog->NumInstructions;
+ if (emitInfo->EmitHighLevelInstructions) {
+ if (n->Opcode == IR_CONT_IF_TRUE ||
+ n->Opcode == IR_CONT_IF_FALSE)
+ opcode = OPCODE_CONT;
+ else
+ opcode = OPCODE_BRK;
+ }
+ else {
+ opcode = OPCODE_BRA;
+ }
+ inst = new_instruction(emitInfo, opcode);
+ inst->DstReg.CondMask = breakTrue ? COND_NE : COND_EQ;
+ return inst;
+}
+
+
+
+/**
+ * Remove any SWIZZLE_NIL terms from given swizzle mask (smear prev term).
+ * Ex: fix_swizzle("zyNN") -> "zyyy"
+ */
+static GLuint
+fix_swizzle(GLuint swizzle)
+{
+ GLuint swz[4], i;
+ for (i = 0; i < 4; i++) {
+ swz[i] = GET_SWZ(swizzle, i);
+ if (swz[i] == SWIZZLE_NIL) {
+ swz[i] = swz[i - 1];
+ }
+ }
+ return MAKE_SWIZZLE4(swz[0], swz[1], swz[2], swz[3]);
+}
+
+
+static struct prog_instruction *
+emit_swizzle(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ GLuint swizzle;
+
+ /* swizzled storage access */
+ (void) emit(emitInfo, n->Children[0]);
+
+ /* "pull-up" the child's storage info, applying our swizzle info */
+ n->Store->File = n->Children[0]->Store->File;
+ n->Store->Index = n->Children[0]->Store->Index;
+ n->Store->Size = n->Children[0]->Store->Size;
+ /*n->Var = n->Children[0]->Var; XXX for debug */
+ assert(n->Store->Index >= 0);
+
+ swizzle = fix_swizzle(n->Store->Swizzle);
+#ifdef DEBUG
+ {
+ GLuint s = n->Children[0]->Store->Swizzle;
+ assert(GET_SWZ(s, 0) != SWIZZLE_NIL);
+ assert(GET_SWZ(s, 1) != SWIZZLE_NIL);
+ assert(GET_SWZ(s, 2) != SWIZZLE_NIL);
+ assert(GET_SWZ(s, 3) != SWIZZLE_NIL);
+ }
+#endif
+
+ /* apply this swizzle to child's swizzle to get composed swizzle */
+ n->Store->Swizzle = swizzle_swizzle(n->Children[0]->Store->Swizzle,
+ swizzle);
+ return NULL;
+}
+
+
+/**
+ * Dereference array element. Just resolve storage for the array
+ * element represented by this node.
+ */
+static struct prog_instruction *
+emit_array_element(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ assert(n->Store);
+ assert(n->Store->File != PROGRAM_UNDEFINED);
+ assert(n->Store->Size > 0);
+
+ if (n->Store->File == PROGRAM_STATE_VAR) {
+ n->Store->Index = _slang_alloc_statevar(n, emitInfo->prog->Parameters);
+ return NULL;
+ }
+
+
+ if (n->Children[1]->Opcode == IR_FLOAT) {
+ /* Constant index */
+ const GLint arrayAddr = n->Children[0]->Store->Index;
+ const GLint index = (GLint) n->Children[1]->Value[0];
+ n->Store->Index = arrayAddr + index;
+ }
+ else {
+ /* Variable index - PROBLEM */
+ const GLint arrayAddr = n->Children[0]->Store->Index;
+ const GLint index = 0;
+ _mesa_problem(NULL, "variable array indexes not supported yet!");
+ n->Store->Index = arrayAddr + index;
+ }
+ return NULL; /* no instruction */
+}
+
+
+/**
+ * Resolve storage for accessing a structure field.
+ */
+static struct prog_instruction *
+emit_struct_field(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ if (n->Store->File == PROGRAM_STATE_VAR) {
+ n->Store->Index = _slang_alloc_statevar(n, emitInfo->prog->Parameters);
+ }
+ else {
+ _mesa_problem(NULL, "structs/fields not supported yet");
+ }
+ return NULL; /* no instruction */
+}
+
+
+static struct prog_instruction *
+emit(slang_emit_info *emitInfo, slang_ir_node *n)
+{
+ struct prog_instruction *inst;
+ if (!n)
+ return NULL;
+
+ switch (n->Opcode) {
+ case IR_SEQ:
+ /* sequence of two sub-trees */
+ assert(n->Children[0]);
+ assert(n->Children[1]);
+ emit(emitInfo, n->Children[0]);
+ inst = emit(emitInfo, n->Children[1]);
+ assert(!n->Store);
+ n->Store = n->Children[1]->Store;
+ return inst;
+
+ case IR_SCOPE:
+ /* new variable scope */
+ _slang_push_var_table(emitInfo->vt);
+ inst = emit(emitInfo, n->Children[0]);
+ _slang_pop_var_table(emitInfo->vt);
+ return inst;
+
+ case IR_VAR_DECL:
+ /* Variable declaration - allocate a register for it */
+ assert(n->Store);
+ assert(n->Store->File != PROGRAM_UNDEFINED);
+ assert(n->Store->Size > 0);
+ assert(n->Store->Index < 0);
+ if (!n->Var || n->Var->isTemp) {
+ /* a nameless/temporary variable, will be freed after first use */
+ if (!_slang_alloc_temp(emitInfo->vt, n->Store)) {
+ slang_info_log_error(emitInfo->log,
+ "Ran out of registers, too many temporaries");
+ return NULL;
+ }
+ }
+ else {
+ /* a regular variable */
+ _slang_add_variable(emitInfo->vt, n->Var);
+ if (!_slang_alloc_var(emitInfo->vt, n->Store)) {
+ slang_info_log_error(emitInfo->log,
+ "Ran out of registers, too many variables");
+ return NULL;
+ }
+ /*
+ printf("IR_VAR_DECL %s %d store %p\n",
+ (char*) n->Var->a_name, n->Store->Index, (void*) n->Store);
+ */
+ assert(n->Var->aux == n->Store);
+ }
+ if (emitInfo->EmitComments) {
+ /* emit NOP with comment describing the variable's storage location */
+ char s[1000];
+ sprintf(s, "TEMP[%d]%s = %s (size %d)",
+ n->Store->Index,
+ _mesa_swizzle_string(n->Store->Swizzle, 0, GL_FALSE),
+ (char *) n->Var->a_name,
+ n->Store->Size);
+ inst = new_instruction(emitInfo, OPCODE_NOP);
+ inst->Comment = _mesa_strdup(s);
+ return inst;
+ }
+ return NULL;
+
+ case IR_VAR:
+ /* Reference to a variable
+ * Storage should have already been resolved/allocated.
+ */
+ assert(n->Store);
+ assert(n->Store->File != PROGRAM_UNDEFINED);
+
+ if (n->Store->File == PROGRAM_STATE_VAR &&
+ n->Store->Index < 0) {
+ n->Store->Index = _slang_alloc_statevar(n, emitInfo->prog->Parameters);
+ }
+
+ if (n->Store->Index < 0) {
+ printf("#### VAR %s not allocated!\n", (char*)n->Var->a_name);
+ }
+ assert(n->Store->Index >= 0);
+ assert(n->Store->Size > 0);
+ break;
+
+ case IR_ELEMENT:
+ return emit_array_element(emitInfo, n);
+ case IR_FIELD:
+ return emit_struct_field(emitInfo, n);
+ case IR_SWIZZLE:
+ return emit_swizzle(emitInfo, n);
+
+ case IR_I_TO_F:
+ /* just move */
+ emit(emitInfo, n->Children[0]);
+ inst = new_instruction(emitInfo, OPCODE_MOV);
+ if (!n->Store) {
+ if (!alloc_temp_storage(emitInfo, n, 1))
+ return NULL;
+ }
+ storage_to_dst_reg(&inst->DstReg, n->Store, n->Writemask);
+ storage_to_src_reg(&inst->SrcReg[0], n->Children[0]->Store);
+ if (emitInfo->EmitComments)
+ inst->Comment = _mesa_strdup("int to float");
+ return NULL;
+
+ /* Simple arithmetic */
+ /* unary */
+ case IR_RSQ:
+ case IR_RCP:
+ case IR_FLOOR:
+ case IR_FRAC:
+ case IR_F_TO_I:
+ case IR_ABS:
+ case IR_SIN:
+ case IR_COS:
+ case IR_DDX:
+ case IR_DDY:
+ case IR_NOISE1:
+ case IR_NOISE2:
+ case IR_NOISE3:
+ case IR_NOISE4:
+ /* binary */
+ case IR_ADD:
+ case IR_SUB:
+ case IR_MUL:
+ case IR_DOT4:
+ case IR_DOT3:
+ case IR_CROSS:
+ case IR_MIN:
+ case IR_MAX:
+ case IR_SEQUAL:
+ case IR_SNEQUAL:
+ case IR_SGE:
+ case IR_SGT:
+ case IR_SLE:
+ case IR_SLT:
+ case IR_POW:
+ case IR_EXP:
+ case IR_EXP2:
+ /* trinary operators */
+ case IR_LRP:
+ return emit_arith(emitInfo, n);
+ case IR_CLAMP:
+ return emit_clamp(emitInfo, n);
+ case IR_TEX:
+ case IR_TEXB:
+ case IR_TEXP:
+ return emit_tex(emitInfo, n);
+ case IR_NEG:
+ return emit_negation(emitInfo, n);
+ case IR_FLOAT:
+ /* find storage location for this float constant */
+ n->Store->Index = _mesa_add_unnamed_constant(emitInfo->prog->Parameters, n->Value,
+ n->Store->Size,
+ &n->Store->Swizzle);
+ if (n->Store->Index < 0) {
+ slang_info_log_error(emitInfo->log, "Ran out of space for constants");
+ return NULL;
+ }
+ return NULL;
+
+ case IR_MOVE:
+ return emit_move(emitInfo, n);
+
+ case IR_COND:
+ return emit_cond(emitInfo, n);
+
+ case IR_NOT:
+ return emit_not(emitInfo, n);
+
+ case IR_LABEL:
+ return emit_label(emitInfo, n);
+ case IR_JUMP:
+ assert(n);
+ assert(n->Label);
+ return emit_jump(emitInfo, n);
+ case IR_KILL:
+ return emit_kill(emitInfo);
+
+ case IR_IF:
+ return emit_if(emitInfo, n);
+
+ case IR_LOOP:
+ return emit_loop(emitInfo, n);
+ case IR_BREAK_IF_FALSE:
+ case IR_CONT_IF_FALSE:
+ return emit_cont_break_if(emitInfo, n, GL_FALSE);
+ case IR_BREAK_IF_TRUE:
+ case IR_CONT_IF_TRUE:
+ return emit_cont_break_if(emitInfo, n, GL_TRUE);
+ case IR_BREAK:
+ /* fall-through */
+ case IR_CONT:
+ return emit_cont_break(emitInfo, n);
+
+ case IR_BEGIN_SUB:
+ return new_instruction(emitInfo, OPCODE_BGNSUB);
+ case IR_END_SUB:
+ return new_instruction(emitInfo, OPCODE_ENDSUB);
+ case IR_RETURN:
+ return new_instruction(emitInfo, OPCODE_RET);
+
+ case IR_NOP:
+ return NULL;
+
+ default:
+ _mesa_problem(NULL, "Unexpected IR opcode in emit()\n");
+ abort();
+ }
+ return NULL;
+}
+
+
+GLboolean
+_slang_emit_code(slang_ir_node *n, slang_var_table *vt,
+ struct gl_program *prog, GLboolean withEnd,
+ slang_info_log *log)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ GLboolean success;
+ slang_emit_info emitInfo;
+
+ emitInfo.log = log;
+ emitInfo.vt = vt;
+ emitInfo.prog = prog;
+
+ emitInfo.EmitHighLevelInstructions = ctx->Shader.EmitHighLevelInstructions;
+ emitInfo.EmitComments = ctx->Shader.EmitComments;
+
+ (void) emit(&emitInfo, n);
+
+ /* finish up by adding the END opcode to program */
+ if (withEnd) {
+ struct prog_instruction *inst;
+ inst = new_instruction(&emitInfo, OPCODE_END);
+ }
+ success = GL_TRUE;
+
+#if 0
+ printf("*********** End emit code (%u inst):\n", prog->NumInstructions);
+ _mesa_print_program(prog);
+ _mesa_print_program_parameters(ctx,prog);
+#endif
+
+ return success;
+}