diff options
Diffstat (limited to 'src/mesa/slang/slang_emit.c')
-rw-r--r-- | src/mesa/slang/slang_emit.c | 2666 |
1 files changed, 2666 insertions, 0 deletions
diff --git a/src/mesa/slang/slang_emit.c b/src/mesa/slang/slang_emit.c new file mode 100644 index 00000000000..4d4c611dfe4 --- /dev/null +++ b/src/mesa/slang/slang_emit.c @@ -0,0 +1,2666 @@ +/* + * Mesa 3-D graphics library + * + * Copyright (C) 2005-2008 Brian Paul All Rights Reserved. + * Copyright (C) 2008 VMware, Inc. 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 "main/imports.h" +#include "main/context.h" +#include "shader/program.h" +#include "shader/prog_instruction.h" +#include "shader/prog_parameter.h" +#include "shader/prog_print.h" +#include "slang_builtin.h" +#include "slang_emit.h" +#include "slang_mem.h" + + +#define PEEPHOLE_OPTIMIZATIONS 1 +#define ANNOTATE 0 + + +typedef struct +{ + slang_info_log *log; + slang_var_table *vt; + struct gl_program *prog; + struct gl_program **Subroutines; + GLuint NumSubroutines; + + GLuint MaxInstructions; /**< size of prog->Instructions[] buffer */ + + GLboolean UnresolvedFunctions; + + /* code-gen options */ + GLboolean EmitHighLevelInstructions; + GLboolean EmitCondCodes; + GLboolean EmitComments; + GLboolean EmitBeginEndSub; /* XXX TEMPORARY */ +} slang_emit_info; + + + +static struct gl_program * +new_subroutine(slang_emit_info *emitInfo, GLuint *id) +{ + GET_CURRENT_CONTEXT(ctx); + const GLuint n = emitInfo->NumSubroutines; + + emitInfo->Subroutines = (struct gl_program **) + _mesa_realloc(emitInfo->Subroutines, + n * sizeof(struct gl_program *), + (n + 1) * sizeof(struct gl_program *)); + emitInfo->Subroutines[n] = ctx->Driver.NewProgram(ctx, emitInfo->prog->Target, 0); + emitInfo->Subroutines[n]->Parameters = emitInfo->prog->Parameters; + emitInfo->NumSubroutines++; + *id = n; + return emitInfo->Subroutines[n]; +} + + +/** + * Convert a writemask to a swizzle. Used for testing cond codes because + * we only want to test the cond code component(s) that was set by the + * previous instruction. + */ +static GLuint +writemask_to_swizzle(GLuint writemask) +{ + if (writemask == WRITEMASK_X) + return SWIZZLE_XXXX; + if (writemask == WRITEMASK_Y) + return SWIZZLE_YYYY; + if (writemask == WRITEMASK_Z) + return SWIZZLE_ZZZZ; + if (writemask == WRITEMASK_W) + return SWIZZLE_WWWW; + return SWIZZLE_XYZW; /* shouldn't be hit */ +} + + +/** + * Convert a swizzle mask to a writemask. + * Note that the slang_ir_storage->Swizzle field can represent either a + * swizzle mask or a writemask, depending on how it's used. For example, + * when we parse "direction.yz" alone, we don't know whether .yz is a + * writemask or a swizzle. In this case, we encode ".yz" in store->Swizzle + * as a swizzle mask (.yz?? actually). Later, if direction.yz is used as + * an R-value, we use store->Swizzle as-is. Otherwise, if direction.yz is + * used as an L-value, we convert it to a writemask. + */ +static GLuint +swizzle_to_writemask(GLuint swizzle) +{ + GLuint i, writemask = 0x0; + for (i = 0; i < 4; i++) { + GLuint swz = GET_SWZ(swizzle, i); + if (swz <= SWIZZLE_W) { + writemask |= (1 << swz); + } + } + return writemask; +} + + +/** + * Swizzle a swizzle (function composition). + * That is, return swz2(swz1), or said another way: swz1.szw2 + * Example: swizzle_swizzle(".zwxx", ".xxyw") yields ".zzwx" + */ +GLuint +_slang_swizzle_swizzle(GLuint swz1, GLuint swz2) +{ + GLuint i, swz, s[4]; + for (i = 0; i < 4; i++) { + GLuint c = GET_SWZ(swz2, i); + if (c <= SWIZZLE_W) + s[i] = GET_SWZ(swz1, c); + else + s[i] = c; + } + swz = MAKE_SWIZZLE4(s[0], s[1], s[2], s[3]); + return swz; +} + + +/** + * Return the default swizzle mask for accessing a variable of the + * given size (in floats). If size = 1, comp is used to identify + * which component [0..3] of the register holds the variable. + */ +GLuint +_slang_var_swizzle(GLint size, GLint comp) +{ + switch (size) { + case 1: + return MAKE_SWIZZLE4(comp, SWIZZLE_NIL, SWIZZLE_NIL, SWIZZLE_NIL); + case 2: + return MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_NIL, SWIZZLE_NIL); + case 3: + return MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_NIL); + default: + return SWIZZLE_XYZW; + } +} + + + +/** + * Allocate storage for the given node (if it hasn't already been allocated). + * + * Typically this is temporary storage for an intermediate result (such as + * for a multiply or add, etc). + * + * If n->Store does not exist it will be created and will be of the size + * specified by defaultSize. + */ +static GLboolean +alloc_node_storage(slang_emit_info *emitInfo, slang_ir_node *n, + GLint defaultSize) +{ + assert(!n->Var); + if (!n->Store) { + assert(defaultSize > 0); + n->Store = _slang_new_ir_storage(PROGRAM_TEMPORARY, -1, defaultSize); + if (!n->Store) { + return GL_FALSE; + } + } + + /* now allocate actual register(s). I.e. set n->Store->Index >= 0 */ + if (n->Store->Index < 0) { + if (!_slang_alloc_temp(emitInfo->vt, n->Store)) { + slang_info_log_error(emitInfo->log, + "Ran out of registers, too many temporaries"); + _slang_free(n->Store); + n->Store = NULL; + return GL_FALSE; + } + } + return GL_TRUE; +} + + +/** + * Free temporary storage, if n->Store is, in fact, temp storage. + * Otherwise, no-op. + */ +static void +free_node_storage(slang_var_table *vt, slang_ir_node *n) +{ + if (n->Store->File == PROGRAM_TEMPORARY && + n->Store->Index >= 0 && + n->Opcode != IR_SWIZZLE) { + if (_slang_is_temp(vt, n->Store)) { + _slang_free_temp(vt, n->Store); + n->Store->Index = -1; + n->Store = NULL; /* XXX this may not be needed */ + } + } +} + + +/** + * Helper function to allocate a short-term temporary. + * Free it with _slang_free_temp(). + */ +static GLboolean +alloc_local_temp(slang_emit_info *emitInfo, slang_ir_storage *temp, GLint size) +{ + assert(size >= 1); + assert(size <= 4); + memset(temp, 0, sizeof(*temp)); + temp->Size = size; + temp->File = PROGRAM_TEMPORARY; + temp->Index = -1; + return _slang_alloc_temp(emitInfo->vt, temp); +} + + +/** + * Remove any SWIZZLE_NIL terms from given swizzle mask. + * For a swizzle like .z??? generate .zzzz (replicate single component). + * Else, for .wx?? generate .wxzw (insert default component for the position). + */ +static GLuint +fix_swizzle(GLuint swizzle) +{ + GLuint c0 = GET_SWZ(swizzle, 0), + c1 = GET_SWZ(swizzle, 1), + c2 = GET_SWZ(swizzle, 2), + c3 = GET_SWZ(swizzle, 3); + if (c1 == SWIZZLE_NIL && c2 == SWIZZLE_NIL && c3 == SWIZZLE_NIL) { + /* smear first component across all positions */ + c1 = c2 = c3 = c0; + } + else { + /* insert default swizzle components */ + if (c0 == SWIZZLE_NIL) + c0 = SWIZZLE_X; + if (c1 == SWIZZLE_NIL) + c1 = SWIZZLE_Y; + if (c2 == SWIZZLE_NIL) + c2 = SWIZZLE_Z; + if (c3 == SWIZZLE_NIL) + c3 = SWIZZLE_W; + } + return MAKE_SWIZZLE4(c0, c1, c2, c3); +} + + + +/** + * Convert IR storage to an instruction dst register. + */ +static void +storage_to_dst_reg(struct prog_dst_register *dst, const slang_ir_storage *st) +{ + const GLboolean relAddr = st->RelAddr; + const GLint size = st->Size; + GLint index = st->Index; + GLuint swizzle = st->Swizzle; + + assert(index >= 0); + /* if this is storage relative to some parent storage, walk up the tree */ + while (st->Parent) { + st = st->Parent; + assert(st->Index >= 0); + index += st->Index; + swizzle = _slang_swizzle_swizzle(st->Swizzle, swizzle); + } + + assert(st->File != PROGRAM_UNDEFINED); + dst->File = st->File; + + assert(index >= 0); + dst->Index = index; + + assert(size >= 1); + assert(size <= 4); + + if (swizzle != SWIZZLE_XYZW) { + dst->WriteMask = swizzle_to_writemask(swizzle); + } + else { + switch (size) { + case 1: + dst->WriteMask = WRITEMASK_X << GET_SWZ(st->Swizzle, 0); + break; + case 2: + dst->WriteMask = WRITEMASK_XY; + break; + case 3: + dst->WriteMask = WRITEMASK_XYZ; + break; + case 4: + dst->WriteMask = WRITEMASK_XYZW; + break; + default: + ; /* error would have been caught above */ + } + } + + dst->RelAddr = relAddr; +} + + +/** + * Convert IR storage to an instruction src register. + */ +static void +storage_to_src_reg(struct prog_src_register *src, const slang_ir_storage *st) +{ + const GLboolean relAddr = st->RelAddr; + GLint index = st->Index; + GLuint swizzle = st->Swizzle; + + /* if this is storage relative to some parent storage, walk up the tree */ + assert(index >= 0); + while (st->Parent) { + st = st->Parent; + if (st->Index < 0) { + /* an error should have been reported already */ + return; + } + assert(st->Index >= 0); + index += st->Index; + swizzle = _slang_swizzle_swizzle(fix_swizzle(st->Swizzle), swizzle); + } + + assert(st->File >= 0); +#if 1 /* XXX temporary */ + if (st->File == PROGRAM_UNDEFINED) { + slang_ir_storage *st0 = (slang_ir_storage *) st; + st0->File = PROGRAM_TEMPORARY; + } +#endif + assert(st->File < PROGRAM_FILE_MAX); + src->File = st->File; + + assert(index >= 0); + src->Index = index; + + swizzle = fix_swizzle(swizzle); + assert(GET_SWZ(swizzle, 0) <= SWIZZLE_W); + assert(GET_SWZ(swizzle, 1) <= SWIZZLE_W); + assert(GET_SWZ(swizzle, 2) <= SWIZZLE_W); + assert(GET_SWZ(swizzle, 3) <= SWIZZLE_W); + src->Swizzle = swizzle; + + src->RelAddr = relAddr; +} + + +/* + * Setup storage pointing to a scalar constant/literal. + */ +static void +constant_to_storage(slang_emit_info *emitInfo, + GLfloat val, + slang_ir_storage *store) +{ + GLuint swizzle; + GLint reg; + GLfloat value[4]; + + value[0] = val; + reg = _mesa_add_unnamed_constant(emitInfo->prog->Parameters, + value, 1, &swizzle); + + memset(store, 0, sizeof(*store)); + store->File = PROGRAM_CONSTANT; + store->Index = reg; + store->Swizzle = swizzle; +} + + +/** + * 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; + +#if 0 + /* print prev inst */ + if (prog->NumInstructions > 0) { + _mesa_print_instruction(prog->Instructions + prog->NumInstructions - 1); + } +#endif + assert(prog->NumInstructions <= emitInfo->MaxInstructions); + + if (prog->NumInstructions == emitInfo->MaxInstructions) { + /* grow the instruction buffer */ + emitInfo->MaxInstructions += 20; + prog->Instructions = + _mesa_realloc_instructions(prog->Instructions, + prog->NumInstructions, + emitInfo->MaxInstructions); + if (!prog->Instructions) { + return NULL; + } + } + + 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; +} + + +static struct prog_instruction * +emit_arl_load(slang_emit_info *emitInfo, + gl_register_file file, GLint index, GLuint swizzle) +{ + struct prog_instruction *inst = new_instruction(emitInfo, OPCODE_ARL); + if (inst) { + inst->SrcReg[0].File = file; + inst->SrcReg[0].Index = index; + inst->SrcReg[0].Swizzle = fix_swizzle(swizzle); + inst->DstReg.File = PROGRAM_ADDRESS; + inst->DstReg.Index = 0; + inst->DstReg.WriteMask = WRITEMASK_X; + } + return inst; +} + + +/** + * Emit a new instruction with given opcode, operands. + * At this point the instruction may have multiple indirect register + * loads/stores. We convert those into ARL loads and address-relative + * operands. See comments inside. + * At some point in the future we could directly emit indirectly addressed + * registers in Mesa GPU instructions. + */ +static struct prog_instruction * +emit_instruction(slang_emit_info *emitInfo, + gl_inst_opcode opcode, + const slang_ir_storage *dst, + const slang_ir_storage *src0, + const slang_ir_storage *src1, + const slang_ir_storage *src2) +{ + struct prog_instruction *inst; + GLuint numIndirect = 0; + const slang_ir_storage *src[3]; + slang_ir_storage newSrc[3], newDst; + GLuint i; + GLboolean isTemp[3]; + + isTemp[0] = isTemp[1] = isTemp[2] = GL_FALSE; + + src[0] = src0; + src[1] = src1; + src[2] = src2; + + /* count up how many operands are indirect loads */ + for (i = 0; i < 3; i++) { + if (src[i] && src[i]->IsIndirect) + numIndirect++; + } + if (dst && dst->IsIndirect) + numIndirect++; + + /* Take special steps for indirect register loads. + * If we had multiple address registers this would be simpler. + * For example, this GLSL code: + * x[i] = y[j] + z[k]; + * would translate into something like: + * ARL ADDR.x, i; + * ARL ADDR.y, j; + * ARL ADDR.z, k; + * ADD TEMP[ADDR.x+5], TEMP[ADDR.y+9], TEMP[ADDR.z+4]; + * But since we currently only have one address register we have to do this: + * ARL ADDR.x, i; + * MOV t1, TEMP[ADDR.x+9]; + * ARL ADDR.x, j; + * MOV t2, TEMP[ADDR.x+4]; + * ARL ADDR.x, k; + * ADD TEMP[ADDR.x+5], t1, t2; + * The code here figures this out... + */ + if (numIndirect > 0) { + for (i = 0; i < 3; i++) { + if (src[i] && src[i]->IsIndirect) { + /* load the ARL register with the indirect register */ + emit_arl_load(emitInfo, + src[i]->IndirectFile, + src[i]->IndirectIndex, + src[i]->IndirectSwizzle); + + if (numIndirect > 1) { + /* Need to load src[i] into a temporary register */ + slang_ir_storage srcRelAddr; + alloc_local_temp(emitInfo, &newSrc[i], src[i]->Size); + isTemp[i] = GL_TRUE; + + /* set RelAddr flag on src register */ + srcRelAddr = *src[i]; + srcRelAddr.RelAddr = GL_TRUE; + srcRelAddr.IsIndirect = GL_FALSE; /* not really needed */ + + /* MOV newSrc, srcRelAddr; */ + inst = emit_instruction(emitInfo, + OPCODE_MOV, + &newSrc[i], + &srcRelAddr, + NULL, + NULL); + if (!inst) { + return NULL; + } + + src[i] = &newSrc[i]; + } + else { + /* just rewrite the src[i] storage to be ARL-relative */ + newSrc[i] = *src[i]; + newSrc[i].RelAddr = GL_TRUE; + newSrc[i].IsIndirect = GL_FALSE; /* not really needed */ + src[i] = &newSrc[i]; + } + } + } + } + + /* Take special steps for indirect dest register write */ + if (dst && dst->IsIndirect) { + /* load the ARL register with the indirect register */ + emit_arl_load(emitInfo, + dst->IndirectFile, + dst->IndirectIndex, + dst->IndirectSwizzle); + newDst = *dst; + newDst.RelAddr = GL_TRUE; + newDst.IsIndirect = GL_FALSE; + dst = &newDst; + } + + /* OK, emit the instruction and its dst, src regs */ + inst = new_instruction(emitInfo, opcode); + if (!inst) + return NULL; + + if (dst) + storage_to_dst_reg(&inst->DstReg, dst); + + for (i = 0; i < 3; i++) { + if (src[i]) + storage_to_src_reg(&inst->SrcReg[i], src[i]); + } + + /* Free any temp registers that we allocated above */ + for (i = 0; i < 3; i++) { + if (isTemp[i]) + _slang_free_temp(emitInfo->vt, &newSrc[i]); + } + + return inst; +} + + + +/** + * Put a comment on the given instruction. + */ +static void +inst_comment(struct prog_instruction *inst, const char *comment) +{ + if (inst) + inst->Comment = _mesa_strdup(comment); +} + + + +/** + * Return pointer to last instruction in program. + */ +static struct prog_instruction * +prev_instruction(slang_emit_info *emitInfo) +{ + struct gl_program *prog = emitInfo->prog; + if (prog->NumInstructions == 0) + return NULL; + else + return prog->Instructions + prog->NumInstructions - 1; +} + + +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) + _mesa_snprintf(s, sizeof(s), "{%g, %g, %g, %g}", val[0], val[1], val[2], val[3]); + else { + _mesa_snprintf(s, sizeof(s), "%g", val[GET_SWZ(st->Swizzle, 0)]); + } + } + break; + case PROGRAM_TEMPORARY: + if (n->Var) + _mesa_snprintf(s, sizeof(s), "%s", (char *) n->Var->a_name); + else + _mesa_snprintf(s, sizeof(s), "t[%d]", st->Index); + break; + case PROGRAM_STATE_VAR: + case PROGRAM_UNIFORM: + _mesa_snprintf(s, sizeof(s), "%s", prog->Parameters->Parameters[st->Index].Name); + break; + case PROGRAM_VARYING: + _mesa_snprintf(s, sizeof(s), "%s", prog->Varying->Parameters[st->Index].Name); + break; + case PROGRAM_INPUT: + _mesa_snprintf(s, sizeof(s), "input[%d]", st->Index); + break; + case PROGRAM_OUTPUT: + _mesa_snprintf(s, sizeof(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_DP2: + operator = "DP2"; + 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); + _mesa_snprintf(s, len, "%s = %s %s %s %s", dstAnnot, + srcAnnot0, operator, srcAnnot1, srcAnnot2); + + free(dstAnnot); + free(srcAnnot0); + free(srcAnnot1); + free(srcAnnot2); + + return s; +#else + return NULL; +#endif +} + + +/** + * Emit an instruction that's just a comment. + */ +static struct prog_instruction * +emit_comment(slang_emit_info *emitInfo, const char *comment) +{ + struct prog_instruction *inst = new_instruction(emitInfo, OPCODE_NOP); + if (inst) { + inst_comment(inst, comment); + } + return inst; +} + + +/** + * 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) +{ + const slang_ir_info *info = _slang_ir_info(n->Opcode); + struct prog_instruction *inst; + GLuint i; + + assert(info); + assert(info->InstOpcode != OPCODE_NOP); + +#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 */ + if (!alloc_node_storage(emitInfo, n, -1)) { /* dest */ + return NULL; + } + + inst = emit_instruction(emitInfo, + OPCODE_MAD, + n->Store, + n->Children[0]->Children[0]->Store, + n->Children[0]->Children[1]->Store, + n->Children[1]->Store); + + free_node_storage(emitInfo->vt, n->Children[0]->Children[0]); + free_node_storage(emitInfo->vt, n->Children[0]->Children[1]); + free_node_storage(emitInfo->vt, n->Children[1]); + return inst; + } + + 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 */ + if (!alloc_node_storage(emitInfo, n, -1)) { /* dest */ + return NULL; + } + + inst = emit_instruction(emitInfo, + OPCODE_MAD, + n->Store, + n->Children[1]->Children[0]->Store, + n->Children[1]->Children[1]->Store, + n->Children[0]->Store); + + free_node_storage(emitInfo->vt, n->Children[1]->Children[0]); + free_node_storage(emitInfo->vt, n->Children[1]->Children[1]); + free_node_storage(emitInfo->vt, n->Children[0]); + return inst; + } +#endif + + /* gen code for children, may involve temp allocation */ + for (i = 0; i < info->NumParams; i++) { + emit(emitInfo, n->Children[i]); + if (!n->Children[i] || !n->Children[i]->Store) { + /* error recovery */ + return NULL; + } + } + + /* result storage */ + if (!alloc_node_storage(emitInfo, n, -1)) { + return NULL; + } + + inst = emit_instruction(emitInfo, + info->InstOpcode, + n->Store, /* dest */ + (info->NumParams > 0 ? n->Children[0]->Store : NULL), + (info->NumParams > 1 ? n->Children[1]->Store : NULL), + (info->NumParams > 2 ? n->Children[2]->Store : NULL) + ); + + /* free temps */ + for (i = 0; i < info->NumParams; i++) + free_node_storage(emitInfo->vt, n->Children[i]); + + return inst; +} + + +/** + * Emit code for == and != operators. These could normally be handled + * by emit_arith() except we need to be able to handle structure comparisons. + */ +static struct prog_instruction * +emit_compare(slang_emit_info *emitInfo, slang_ir_node *n) +{ + struct prog_instruction *inst = NULL; + GLint size; + + assert(n->Opcode == IR_EQUAL || n->Opcode == IR_NOTEQUAL); + + /* gen code for children */ + emit(emitInfo, n->Children[0]); + emit(emitInfo, n->Children[1]); + + if (n->Children[0]->Store->Size != n->Children[1]->Store->Size) { + /* XXX this error should have been caught in slang_codegen.c */ + slang_info_log_error(emitInfo->log, "invalid operands to == or !="); + n->Store = NULL; + return NULL; + } + + /* final result is 1 bool */ + if (!alloc_node_storage(emitInfo, n, 1)) + return NULL; + + size = n->Children[0]->Store->Size; + + if (size == 1) { + gl_inst_opcode opcode = n->Opcode == IR_EQUAL ? OPCODE_SEQ : OPCODE_SNE; + inst = emit_instruction(emitInfo, + opcode, + n->Store, /* dest */ + n->Children[0]->Store, + n->Children[1]->Store, + NULL); + } + else if (size <= 4) { + /* compare two vectors. + * Unfortunately, there's no instruction to compare vectors and + * return a scalar result. Do it with some compare and dot product + * instructions... + */ + GLuint swizzle; + gl_inst_opcode dotOp; + slang_ir_storage tempStore; + + if (!alloc_local_temp(emitInfo, &tempStore, 4)) { + n->Store = NULL; + return NULL; + /* out of temps */ + } + + if (size == 4) { + dotOp = OPCODE_DP4; + swizzle = SWIZZLE_XYZW; + } + else if (size == 3) { + dotOp = OPCODE_DP3; + swizzle = SWIZZLE_XYZW; + } + else { + assert(size == 2); + dotOp = OPCODE_DP3; /* XXX use OPCODE_DP2 eventually */ + swizzle = MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y); + } + + /* Compute inequality (temp = (A != B)) */ + inst = emit_instruction(emitInfo, + OPCODE_SNE, + &tempStore, + n->Children[0]->Store, + n->Children[1]->Store, + NULL); + if (!inst) { + return NULL; + } + inst_comment(inst, "Compare values"); + + /* Compute val = DOT(temp, temp) (reduction) */ + inst = emit_instruction(emitInfo, + dotOp, + n->Store, + &tempStore, + &tempStore, + NULL); + if (!inst) { + return NULL; + } + inst->SrcReg[0].Swizzle = inst->SrcReg[1].Swizzle = swizzle; /*override*/ + inst_comment(inst, "Reduce vec to bool"); + + _slang_free_temp(emitInfo->vt, &tempStore); /* free temp */ + + if (n->Opcode == IR_EQUAL) { + /* compute val = !val.x with SEQ val, val, 0; */ + slang_ir_storage zero; + constant_to_storage(emitInfo, 0.0, &zero); + inst = emit_instruction(emitInfo, + OPCODE_SEQ, + n->Store, /* dest */ + n->Store, + &zero, + NULL); + if (!inst) { + return NULL; + } + inst_comment(inst, "Invert true/false"); + } + } + else { + /* size > 4, struct or array compare. + * XXX this won't work reliably for structs with padding!! + */ + GLint i, num = (n->Children[0]->Store->Size + 3) / 4; + slang_ir_storage accTemp, sneTemp; + + if (!alloc_local_temp(emitInfo, &accTemp, 4)) + return NULL; + + if (!alloc_local_temp(emitInfo, &sneTemp, 4)) + return NULL; + + for (i = 0; i < num; i++) { + slang_ir_storage srcStore0 = *n->Children[0]->Store; + slang_ir_storage srcStore1 = *n->Children[1]->Store; + srcStore0.Index += i; + srcStore1.Index += i; + + if (i == 0) { + /* SNE accTemp, left[i], right[i] */ + inst = emit_instruction(emitInfo, OPCODE_SNE, + &accTemp, /* dest */ + &srcStore0, + &srcStore1, + NULL); + if (!inst) { + return NULL; + } + inst_comment(inst, "Begin struct/array comparison"); + } + else { + /* SNE sneTemp, left[i], right[i] */ + inst = emit_instruction(emitInfo, OPCODE_SNE, + &sneTemp, /* dest */ + &srcStore0, + &srcStore1, + NULL); + if (!inst) { + return NULL; + } + /* ADD accTemp, accTemp, sneTemp; # like logical-OR */ + inst = emit_instruction(emitInfo, OPCODE_ADD, + &accTemp, /* dest */ + &accTemp, + &sneTemp, + NULL); + if (!inst) { + return NULL; + } + } + } + + /* compute accTemp.x || accTemp.y || accTemp.z || accTemp.w with DOT4 */ + inst = emit_instruction(emitInfo, OPCODE_DP4, + n->Store, + &accTemp, + &accTemp, + NULL); + if (!inst) { + return NULL; + } + inst_comment(inst, "End struct/array comparison"); + + if (n->Opcode == IR_EQUAL) { + /* compute tmp.x = !tmp.x via tmp.x = (tmp.x == 0) */ + slang_ir_storage zero; + constant_to_storage(emitInfo, 0.0, &zero); + inst = emit_instruction(emitInfo, OPCODE_SEQ, + n->Store, /* dest */ + n->Store, + &zero, + NULL); + if (!inst) { + return NULL; + } + inst_comment(inst, "Invert true/false"); + } + + _slang_free_temp(emitInfo->vt, &accTemp); + _slang_free_temp(emitInfo->vt, &sneTemp); + } + + /* free temps */ + free_node_storage(emitInfo->vt, n->Children[0]); + free_node_storage(emitInfo->vt, n->Children[1]); + + 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; + slang_ir_node tmpNode; + + 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; + } + } +#else + (void) inst; +#endif + + if (!alloc_node_storage(emitInfo, n, n->Children[0]->Store->Size)) + return NULL; + + emit(emitInfo, n->Children[1]); + emit(emitInfo, n->Children[2]); + + /* Some GPUs don't allow reading from output registers. So if the + * dest for this clamp() is an output reg, we can't use that reg for + * the intermediate result. Use a temp register instead. + */ + memset(&tmpNode, 0, sizeof(tmpNode)); + if (!alloc_node_storage(emitInfo, &tmpNode, n->Store->Size)) { + return NULL; + } + + /* tmp = max(ch[0], ch[1]) */ + inst = emit_instruction(emitInfo, OPCODE_MAX, + tmpNode.Store, /* dest */ + n->Children[0]->Store, + n->Children[1]->Store, + NULL); + if (!inst) { + return NULL; + } + + /* n->dest = min(tmp, ch[2]) */ + inst = emit_instruction(emitInfo, OPCODE_MIN, + n->Store, /* dest */ + tmpNode.Store, + n->Children[2]->Store, + NULL); + + free_node_storage(emitInfo->vt, &tmpNode); + + 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 (!alloc_node_storage(emitInfo, n, n->Children[0]->Store->Size)) + return NULL; + + inst = emit_instruction(emitInfo, + OPCODE_MOV, + n->Store, /* dest */ + n->Children[0]->Store, + NULL, + NULL); + if (inst) { + inst->SrcReg[0].Negate = NEGATE_XYZW; + } + return inst; +} + + +static struct prog_instruction * +emit_label(slang_emit_info *emitInfo, const slang_ir_node *n) +{ + assert(n->Label); +#if 0 + /* XXX this fails in loop tail code - investigate someday */ + assert(_slang_label_get_location(n->Label) < 0); + _slang_label_set_location(n->Label, emitInfo->prog->NumInstructions, + emitInfo->prog); +#else + if (_slang_label_get_location(n->Label) < 0) + _slang_label_set_location(n->Label, emitInfo->prog->NumInstructions, + emitInfo->prog); +#endif + return NULL; +} + + +/** + * Emit code for a function call. + * Note that for each time a function is called, we emit the function's + * body code again because the set of available registers may be different. + */ +static struct prog_instruction * +emit_fcall(slang_emit_info *emitInfo, slang_ir_node *n) +{ + struct gl_program *progSave; + struct prog_instruction *inst; + GLuint subroutineId; + GLuint maxInstSave; + + assert(n->Opcode == IR_CALL); + assert(n->Label); + + /* save/push cur program */ + maxInstSave = emitInfo->MaxInstructions; + progSave = emitInfo->prog; + + emitInfo->prog = new_subroutine(emitInfo, &subroutineId); + emitInfo->MaxInstructions = emitInfo->prog->NumInstructions; + + _slang_label_set_location(n->Label, emitInfo->prog->NumInstructions, + emitInfo->prog); + + if (emitInfo->EmitBeginEndSub) { + /* BGNSUB isn't a real instruction. + * We require a label (i.e. "foobar:") though, if we're going to + * print the program in the NV format. The BNGSUB instruction is + * really just a NOP to attach the label to. + */ + inst = new_instruction(emitInfo, OPCODE_BGNSUB); + if (!inst) { + return NULL; + } + inst_comment(inst, n->Label->Name); + } + + /* body of function: */ + emit(emitInfo, n->Children[0]); + n->Store = n->Children[0]->Store; + + /* add RET instruction now, if needed */ + inst = prev_instruction(emitInfo); + if (inst && inst->Opcode != OPCODE_RET) { + inst = new_instruction(emitInfo, OPCODE_RET); + if (!inst) { + return NULL; + } + } + + if (emitInfo->EmitBeginEndSub) { + inst = new_instruction(emitInfo, OPCODE_ENDSUB); + if (!inst) { + return NULL; + } + inst_comment(inst, n->Label->Name); + } + + /* pop/restore cur program */ + emitInfo->prog = progSave; + emitInfo->MaxInstructions = maxInstSave; + + /* emit the function call */ + inst = new_instruction(emitInfo, OPCODE_CAL); + if (!inst) { + return NULL; + } + /* The branch target is just the subroutine number (changed later) */ + inst->BranchTarget = subroutineId; + inst_comment(inst, n->Label->Name); + assert(inst->BranchTarget >= 0); + + return inst; +} + + +/** + * Emit code for a 'return' statement. + */ +static struct prog_instruction * +emit_return(slang_emit_info *emitInfo, slang_ir_node *n) +{ + struct prog_instruction *inst; + assert(n); + assert(n->Opcode == IR_RETURN); + assert(n->Label); + inst = new_instruction(emitInfo, OPCODE_RET); + if (inst) { + inst->DstReg.CondMask = COND_TR; /* always return */ + } + return inst; +} + + +static struct prog_instruction * +emit_kill(slang_emit_info *emitInfo) +{ + struct gl_fragment_program *fp; + 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); + if (!inst) { + return NULL; + } + inst->DstReg.CondMask = COND_TR; /* always kill */ + + assert(emitInfo->prog->Target == GL_FRAGMENT_PROGRAM_ARB); + fp = (struct gl_fragment_program *) emitInfo->prog; + fp->UsesKill = GL_TRUE; + + return inst; +} + + +static struct prog_instruction * +emit_tex(slang_emit_info *emitInfo, slang_ir_node *n) +{ + struct prog_instruction *inst; + gl_inst_opcode opcode; + GLboolean shadow = GL_FALSE; + + switch (n->Opcode) { + case IR_TEX: + opcode = OPCODE_TEX; + break; + case IR_TEX_SH: + opcode = OPCODE_TEX; + shadow = GL_TRUE; + break; + case IR_TEXB: + opcode = OPCODE_TXB; + break; + case IR_TEXB_SH: + opcode = OPCODE_TXB; + shadow = GL_TRUE; + break; + case IR_TEXP: + opcode = OPCODE_TXP; + break; + case IR_TEXP_SH: + opcode = OPCODE_TXP; + shadow = GL_TRUE; + break; + default: + _mesa_problem(NULL, "Bad IR TEX code"); + return NULL; + } + + if (n->Children[0]->Opcode == IR_ELEMENT) { + /* array is the sampler (a uniform which'll indicate the texture unit) */ + assert(n->Children[0]->Children[0]->Store); + assert(n->Children[0]->Children[0]->Store->File == PROGRAM_SAMPLER); + + emit(emitInfo, n->Children[0]); + + n->Children[0]->Var = n->Children[0]->Children[0]->Var; + } else { + /* this is the sampler (a uniform which'll indicate the texture unit) */ + assert(n->Children[0]->Store); + assert(n->Children[0]->Store->File == PROGRAM_SAMPLER); + } + + /* emit code for the texcoord operand */ + (void) emit(emitInfo, n->Children[1]); + + /* alloc storage for result of texture fetch */ + if (!alloc_node_storage(emitInfo, n, 4)) + return NULL; + + /* emit TEX instruction; Child[1] is the texcoord */ + inst = emit_instruction(emitInfo, + opcode, + n->Store, + n->Children[1]->Store, + NULL, + NULL); + if (!inst) { + return NULL; + } + + inst->TexShadow = shadow; + + /* Store->Index is the uniform/sampler index */ + assert(n->Children[0]->Store->Index >= 0); + inst->TexSrcUnit = n->Children[0]->Store->Index; + inst->TexSrcTarget = n->Children[0]->Store->TexTarget; + + /* mark the sampler as being used */ + _mesa_use_uniform(emitInfo->prog->Parameters, + (char *) n->Children[0]->Var->a_name); + + return inst; +} + + +/** + * Assignment/copy + */ +static struct prog_instruction * +emit_copy(slang_emit_info *emitInfo, slang_ir_node *n) +{ + struct prog_instruction *inst; + + assert(n->Opcode == IR_COPY); + + /* lhs */ + emit(emitInfo, n->Children[0]); + if (!n->Children[0]->Store || n->Children[0]->Store->Index < 0) { + /* an error should have been already recorded */ + return NULL; + } + + /* rhs */ + assert(n->Children[1]); + inst = emit(emitInfo, n->Children[1]); + + if (!n->Children[1]->Store || n->Children[1]->Store->Index < 0) { + if (!emitInfo->log->text && !emitInfo->UnresolvedFunctions) { + /* XXX this error should have been caught in slang_codegen.c */ + slang_info_log_error(emitInfo->log, "invalid assignment"); + } + return NULL; + } + + assert(n->Children[1]->Store->Index >= 0); + + /*assert(n->Children[0]->Store->Size == n->Children[1]->Store->Size);*/ + + n->Store = n->Children[0]->Store; + + if (n->Store->File == PROGRAM_SAMPLER) { + /* no code generated for sampler assignments, + * just copy the sampler index/target at compile time. + */ + n->Store->Index = n->Children[1]->Store->Index; + n->Store->TexTarget = n->Children[1]->Store->TexTarget; + return NULL; + } + +#if PEEPHOLE_OPTIMIZATIONS + if (inst && + (n->Children[1]->Opcode != IR_SWIZZLE) && + _slang_is_temp(emitInfo->vt, n->Children[1]->Store) && + (inst->DstReg.File == n->Children[1]->Store->File) && + (inst->DstReg.Index == n->Children[1]->Store->Index) && + !n->Children[0]->Store->IsIndirect && + n->Children[0]->Store->Size <= 4) { + /* Peephole optimization: + * The Right-Hand-Side has its results in a temporary place. + * Modify the RHS (and the prev instruction) to store its results + * in the destination specified by n->Children[0]. + * Then, this MOVE is a no-op. + * Ex: + * MUL tmp, x, y; + * MOV a, tmp; + * becomes: + * MUL a, x, y; + */ + + /* fixup the previous instruction (which stored the RHS result) */ + assert(n->Children[0]->Store->Index >= 0); + storage_to_dst_reg(&inst->DstReg, n->Children[0]->Store); + 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[1]->Store->Swizzle == SWIZZLE_NOOP); + dstStore.Size = 4; + srcStore.Size = 4; + while (size >= 4) { + inst = emit_instruction(emitInfo, OPCODE_MOV, + &dstStore, + &srcStore, + NULL, + NULL); + if (!inst) { + return NULL; + } + inst_comment(inst, "IR_COPY block"); + srcStore.Index++; + dstStore.Index++; + size -= 4; + } + } + else { + /* single register move */ + char *srcAnnot, *dstAnnot; + assert(n->Children[0]->Store->Index >= 0); + inst = emit_instruction(emitInfo, OPCODE_MOV, + n->Children[0]->Store, /* dest */ + n->Children[1]->Store, + NULL, + NULL); + if (!inst) { + return NULL; + } + 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_node_storage(emitInfo->vt, n->Children[1]); + return inst; + } +} + + +/** + * An IR_COND node wraps a boolean expression which is used by an + * IF or WHILE test. This is where we'll set condition codes, if needed. + */ +static struct prog_instruction * +emit_cond(slang_emit_info *emitInfo, slang_ir_node *n) +{ + struct prog_instruction *inst; + + assert(n->Opcode == IR_COND); + + if (!n->Children[0]) + return NULL; + + /* emit code for the expression */ + inst = emit(emitInfo, n->Children[0]); + + if (!n->Children[0]->Store) { + /* error recovery */ + return NULL; + } + + assert(n->Children[0]->Store); + /*assert(n->Children[0]->Store->Size == 1);*/ + + if (emitInfo->EmitCondCodes) { + if (inst && + n->Children[0]->Store && + inst->DstReg.File == n->Children[0]->Store->File && + inst->DstReg.Index == n->Children[0]->Store->Index) { + /* The previous instruction wrote to the register who's value + * we're testing. Just fix that instruction so that the + * condition codes are computed. + */ + inst->CondUpdate = GL_TRUE; + n->Store = n->Children[0]->Store; + return inst; + } + 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. + */ + if (!alloc_node_storage(emitInfo, n, 1)) + return NULL; + inst = emit_instruction(emitInfo, OPCODE_MOV, + n->Store, /* dest */ + n->Children[0]->Store, + NULL, + NULL); + if (!inst) { + return NULL; + } + inst->CondUpdate = GL_TRUE; + inst_comment(inst, "COND expr"); + _slang_free_temp(emitInfo->vt, n->Store); + return inst; + } + } + else { + /* No-op: the boolean result of the expression is in a regular reg */ + n->Store = n->Children[0]->Store; + return inst; + } +} + + +/** + * Logical-NOT + */ +static struct prog_instruction * +emit_not(slang_emit_info *emitInfo, slang_ir_node *n) +{ + static const struct { + gl_inst_opcode op, opNot; + } operators[] = { + { OPCODE_SLT, OPCODE_SGE }, + { OPCODE_SLE, OPCODE_SGT }, + { OPCODE_SGT, OPCODE_SLE }, + { OPCODE_SGE, OPCODE_SLT }, + { OPCODE_SEQ, OPCODE_SNE }, + { OPCODE_SNE, OPCODE_SEQ }, + { 0, 0 } + }; + struct prog_instruction *inst; + slang_ir_storage zero; + GLuint i; + + /* child expr */ + inst = emit(emitInfo, n->Children[0]); + +#if PEEPHOLE_OPTIMIZATIONS + if (inst) { + /* if the prev instruction was a comparison instruction, invert it */ + for (i = 0; operators[i].op; i++) { + if (inst->Opcode == operators[i].op) { + inst->Opcode = operators[i].opNot; + n->Store = n->Children[0]->Store; + return inst; + } + } + } +#endif + + /* else, invert using SEQ (v = v == 0) */ + if (!alloc_node_storage(emitInfo, n, n->Children[0]->Store->Size)) + return NULL; + + constant_to_storage(emitInfo, 0.0, &zero); + inst = emit_instruction(emitInfo, + OPCODE_SEQ, + n->Store, + n->Children[0]->Store, + &zero, + NULL); + if (!inst) { + return NULL; + } + inst_comment(inst, "NOT"); + + free_node_storage(emitInfo->vt, n->Children[0]); + + return inst; +} + + +static struct prog_instruction * +emit_if(slang_emit_info *emitInfo, slang_ir_node *n) +{ + struct gl_program *prog = emitInfo->prog; + GLuint ifInstLoc, elseInstLoc = 0; + GLuint condWritemask = 0; + + /* emit condition expression code */ + { + struct prog_instruction *inst; + inst = emit(emitInfo, n->Children[0]); + if (emitInfo->EmitCondCodes) { + if (!inst) { + /* error recovery */ + return NULL; + } + condWritemask = inst->DstReg.WriteMask; + } + } + + if (!n->Children[0]->Store) + return NULL; + +#if 0 + assert(n->Children[0]->Store->Size == 1); /* a bool! */ +#endif + + ifInstLoc = prog->NumInstructions; + if (emitInfo->EmitHighLevelInstructions) { + if (emitInfo->EmitCondCodes) { + /* IF condcode THEN ... */ + struct prog_instruction *ifInst = new_instruction(emitInfo, OPCODE_IF); + if (!ifInst) { + return NULL; + } + ifInst->DstReg.CondMask = COND_NE; /* if cond is non-zero */ + /* only test the cond code (1 of 4) that was updated by the + * previous instruction. + */ + ifInst->DstReg.CondSwizzle = writemask_to_swizzle(condWritemask); + } + else { + struct prog_instruction *inst; + + /* IF src[0] THEN ... */ + inst = emit_instruction(emitInfo, OPCODE_IF, + NULL, /* dst */ + n->Children[0]->Store, /* op0 */ + NULL, + NULL); + if (!inst) { + return NULL; + } + } + } + else { + /* conditional jump to else, or endif */ + struct prog_instruction *ifInst = new_instruction(emitInfo, OPCODE_BRA); + if (!ifInst) { + return NULL; + } + ifInst->DstReg.CondMask = COND_EQ; /* BRA if cond is zero */ + inst_comment(ifInst, "if zero"); + ifInst->DstReg.CondSwizzle = writemask_to_swizzle(condWritemask); + } + + /* if body */ + emit(emitInfo, n->Children[1]); + + if (n->Children[2]) { + /* have else body */ + elseInstLoc = prog->NumInstructions; + if (emitInfo->EmitHighLevelInstructions) { + struct prog_instruction *inst = new_instruction(emitInfo, OPCODE_ELSE); + if (!inst) { + return NULL; + } + prog->Instructions[ifInstLoc].BranchTarget = prog->NumInstructions - 1; + } + else { + /* jump to endif instruction */ + struct prog_instruction *inst = new_instruction(emitInfo, OPCODE_BRA); + if (!inst) { + return NULL; + } + inst_comment(inst, "else"); + inst->DstReg.CondMask = COND_TR; /* always branch */ + prog->Instructions[ifInstLoc].BranchTarget = prog->NumInstructions; + } + emit(emitInfo, n->Children[2]); + } + else { + /* no else body */ + prog->Instructions[ifInstLoc].BranchTarget = prog->NumInstructions; + } + + if (emitInfo->EmitHighLevelInstructions) { + struct prog_instruction *inst = new_instruction(emitInfo, OPCODE_ENDIF); + if (!inst) { + return NULL; + } + } + + if (elseInstLoc) { + /* point ELSE instruction BranchTarget at ENDIF */ + if (emitInfo->EmitHighLevelInstructions) { + prog->Instructions[elseInstLoc].BranchTarget = prog->NumInstructions - 1; + } + else { + prog->Instructions[elseInstLoc].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 *endInst; + GLuint beginInstLoc, tailInstLoc, endInstLoc; + slang_ir_node *ir; + + /* emit OPCODE_BGNLOOP */ + beginInstLoc = prog->NumInstructions; + if (emitInfo->EmitHighLevelInstructions) { + struct prog_instruction *inst = new_instruction(emitInfo, OPCODE_BGNLOOP); + if (!inst) { + return NULL; + } + } + + /* body */ + emit(emitInfo, n->Children[0]); + + /* tail */ + tailInstLoc = prog->NumInstructions; + if (n->Children[1]) { + if (emitInfo->EmitComments) + emit_comment(emitInfo, "Loop tail code:"); + emit(emitInfo, n->Children[1]); + } + + endInstLoc = prog->NumInstructions; + if (emitInfo->EmitHighLevelInstructions) { + /* emit OPCODE_ENDLOOP */ + endInst = new_instruction(emitInfo, OPCODE_ENDLOOP); + if (!endInst) { + return NULL; + } + } + else { + /* emit unconditional BRA-nch */ + endInst = new_instruction(emitInfo, OPCODE_BRA); + if (!endInst) { + return NULL; + } + endInst->DstReg.CondMask = COND_TR; /* always true */ + } + /* ENDLOOP's BranchTarget points to the BGNLOOP inst */ + endInst->BranchTarget = beginInstLoc; + + if (emitInfo->EmitHighLevelInstructions) { + /* BGNLOOP's BranchTarget points to the ENDLOOP inst */ + prog->Instructions[beginInstLoc].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 corresponding ENDLOOP instruction. + */ + for (ir = n->List; ir; ir = ir->List) { + struct prog_instruction *inst = prog->Instructions + ir->InstLocation; + assert(inst->BranchTarget < 0); + if (ir->Opcode == IR_BREAK || + ir->Opcode == IR_BREAK_IF_TRUE) { + assert(inst->Opcode == OPCODE_BRK || + inst->Opcode == OPCODE_BRA); + /* go to instruction at end of loop */ + if (emitInfo->EmitHighLevelInstructions) { + inst->BranchTarget = endInstLoc; + } + else { + inst->BranchTarget = endInstLoc + 1; + } + } + else { + assert(ir->Opcode == IR_CONT || + ir->Opcode == IR_CONT_IF_TRUE); + assert(inst->Opcode == OPCODE_CONT || + inst->Opcode == OPCODE_BRA); + /* go to instruction at tail of loop */ + inst->BranchTarget = endInstLoc; + } + } + return NULL; +} + + +/** + * Unconditional "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; + + if (n->Opcode == IR_CONT) { + /* we need to execute the loop's tail code before doing CONT */ + assert(n->Parent); + assert(n->Parent->Opcode == IR_LOOP); + if (n->Parent->Children[1]) { + /* emit tail code */ + if (emitInfo->EmitComments) { + emit_comment(emitInfo, "continue - tail code:"); + } + emit(emitInfo, n->Parent->Children[1]); + } + } + + /* opcode selection */ + if (emitInfo->EmitHighLevelInstructions) { + opcode = (n->Opcode == IR_CONT) ? OPCODE_CONT : OPCODE_BRK; + } + else { + opcode = OPCODE_BRA; + } + n->InstLocation = emitInfo->prog->NumInstructions; + inst = new_instruction(emitInfo, opcode); + if (inst) { + 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_true(slang_emit_info *emitInfo, slang_ir_node *n) +{ + struct prog_instruction *inst; + + assert(n->Opcode == IR_CONT_IF_TRUE || + n->Opcode == IR_BREAK_IF_TRUE); + + /* evaluate condition expr, setting cond codes */ + inst = emit(emitInfo, n->Children[0]); + if (emitInfo->EmitCondCodes) { + assert(inst); + inst->CondUpdate = GL_TRUE; + } + + n->InstLocation = emitInfo->prog->NumInstructions; + + /* opcode selection */ + if (emitInfo->EmitHighLevelInstructions) { + const gl_inst_opcode opcode + = (n->Opcode == IR_CONT_IF_TRUE) ? OPCODE_CONT : OPCODE_BRK; + if (emitInfo->EmitCondCodes) { + /* Get the writemask from the previous instruction which set + * the condcodes. Use that writemask as the CondSwizzle. + */ + const GLuint condWritemask = inst->DstReg.WriteMask; + inst = new_instruction(emitInfo, opcode); + if (inst) { + inst->DstReg.CondMask = COND_NE; + inst->DstReg.CondSwizzle = writemask_to_swizzle(condWritemask); + } + return inst; + } + else { + /* IF reg + * BRK/CONT; + * ENDIF + */ + GLint ifInstLoc; + ifInstLoc = emitInfo->prog->NumInstructions; + inst = emit_instruction(emitInfo, OPCODE_IF, + NULL, /* dest */ + n->Children[0]->Store, + NULL, + NULL); + if (!inst) { + return NULL; + } + n->InstLocation = emitInfo->prog->NumInstructions; + + inst = new_instruction(emitInfo, opcode); + if (!inst) { + return NULL; + } + inst = new_instruction(emitInfo, OPCODE_ENDIF); + if (!inst) { + return NULL; + } + + emitInfo->prog->Instructions[ifInstLoc].BranchTarget + = emitInfo->prog->NumInstructions - 1; + return inst; + } + } + else { + const GLuint condWritemask = inst->DstReg.WriteMask; + assert(emitInfo->EmitCondCodes); + inst = new_instruction(emitInfo, OPCODE_BRA); + if (inst) { + inst->DstReg.CondMask = COND_NE; + inst->DstReg.CondSwizzle = writemask_to_swizzle(condWritemask); + } + return inst; + } +} + + +/** + * Return the size of a swizzle mask given that some swizzle components + * may be NIL/undefined. For example: + * swizzle_size(".zzxx") = 4 + * swizzle_size(".xy??") = 2 + * swizzle_size(".w???") = 1 + */ +static GLuint +swizzle_size(GLuint swizzle) +{ + GLuint i; + for (i = 0; i < 4; i++) { + if (GET_SWZ(swizzle, i) == SWIZZLE_NIL) + return i; + } + return 4; +} + + +static struct prog_instruction * +emit_swizzle(slang_emit_info *emitInfo, slang_ir_node *n) +{ + struct prog_instruction *inst; + + inst = emit(emitInfo, n->Children[0]); + + if (!n->Store->Parent) { + /* this covers a case such as "(b ? p : q).x" */ + n->Store->Parent = n->Children[0]->Store; + assert(n->Store->Parent); + } + + { + const GLuint swizzle = n->Store->Swizzle; + /* new storage is parent storage with updated Swizzle + Size fields */ + _slang_copy_ir_storage(n->Store, n->Store->Parent); + /* Apply this node's swizzle to parent's storage */ + n->Store->Swizzle = _slang_swizzle_swizzle(n->Store->Swizzle, swizzle); + /* Update size */ + n->Store->Size = swizzle_size(n->Store->Swizzle); + } + + assert(!n->Store->Parent); + assert(n->Store->Index >= 0); + + return inst; +} + + +/** + * Dereference array element: element == array[index] + * This basically involves emitting code for computing the array index + * and updating the node/element's storage info. + */ +static struct prog_instruction * +emit_array_element(slang_emit_info *emitInfo, slang_ir_node *n) +{ + slang_ir_storage *arrayStore, *indexStore; + const int elemSize = n->Store->Size; /* number of floats */ + const GLint elemSizeVec = (elemSize + 3) / 4; /* number of vec4 */ + struct prog_instruction *inst; + + assert(n->Opcode == IR_ELEMENT); + assert(elemSize > 0); + + /* special case for built-in state variables, like light state */ + { + slang_ir_storage *root = n->Store; + assert(!root->Parent); + while (root->Parent) + root = root->Parent; + + if (root->File == PROGRAM_STATE_VAR) { + GLboolean direct; + GLint index = + _slang_alloc_statevar(n, emitInfo->prog->Parameters, &direct); + if (index < 0) { + /* error */ + return NULL; + } + if (direct) { + n->Store->Index = index; + return NULL; /* all done */ + } + } + } + + /* do codegen for array itself */ + emit(emitInfo, n->Children[0]); + arrayStore = n->Children[0]->Store; + + /* The initial array element storage is the array's storage, + * then modified below. + */ + _slang_copy_ir_storage(n->Store, arrayStore); + + + if (n->Children[1]->Opcode == IR_FLOAT) { + /* Constant array index */ + const GLint element = (GLint) n->Children[1]->Value[0]; + + /* this element's storage is the array's storage, plus constant offset */ + n->Store->Index += elemSizeVec * element; + } + else { + /* Variable array index */ + + /* do codegen for array index expression */ + emit(emitInfo, n->Children[1]); + indexStore = n->Children[1]->Store; + + if (indexStore->IsIndirect) { + /* need to put the array index into a temporary since we can't + * directly support a[b[i]] constructs. + */ + + + /*indexStore = tempstore();*/ + } + + + if (elemSize > 4) { + /* need to multiply array index by array element size */ + struct prog_instruction *inst; + slang_ir_storage *indexTemp; + slang_ir_storage elemSizeStore; + + /* allocate 1 float indexTemp */ + indexTemp = _slang_new_ir_storage(PROGRAM_TEMPORARY, -1, 1); + _slang_alloc_temp(emitInfo->vt, indexTemp); + + /* allocate a constant containing the element size */ + constant_to_storage(emitInfo, (float) elemSizeVec, &elemSizeStore); + + /* multiply array index by element size */ + inst = emit_instruction(emitInfo, + OPCODE_MUL, + indexTemp, /* dest */ + indexStore, /* the index */ + &elemSizeStore, + NULL); + if (!inst) { + return NULL; + } + + indexStore = indexTemp; + } + + if (arrayStore->IsIndirect) { + /* ex: in a[i][j], a[i] (the arrayStore) is indirect */ + /* Need to add indexStore to arrayStore->Indirect store */ + slang_ir_storage indirectArray; + slang_ir_storage *indexTemp; + + _slang_init_ir_storage(&indirectArray, + arrayStore->IndirectFile, + arrayStore->IndirectIndex, + 1, + arrayStore->IndirectSwizzle); + + /* allocate 1 float indexTemp */ + indexTemp = _slang_new_ir_storage(PROGRAM_TEMPORARY, -1, 1); + _slang_alloc_temp(emitInfo->vt, indexTemp); + + inst = emit_instruction(emitInfo, + OPCODE_ADD, + indexTemp, /* dest */ + indexStore, /* the index */ + &indirectArray, /* indirect array base */ + NULL); + if (!inst) { + return NULL; + } + + indexStore = indexTemp; + } + + /* update the array element storage info */ + n->Store->IsIndirect = GL_TRUE; + n->Store->IndirectFile = indexStore->File; + n->Store->IndirectIndex = indexStore->Index; + n->Store->IndirectSwizzle = indexStore->Swizzle; + } + + n->Store->Size = elemSize; + n->Store->Swizzle = _slang_var_swizzle(elemSize, 0); + + 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) +{ + slang_ir_storage *root = n->Store; + GLint fieldOffset, fieldSize; + + assert(n->Opcode == IR_FIELD); + + assert(!root->Parent); + while (root->Parent) + root = root->Parent; + + /* If this is the field of a state var, allocate constant/uniform + * storage for it now if we haven't already. + * Note that we allocate storage (uniform/constant slots) for state + * variables here rather than at declaration time so we only allocate + * space for the ones that we actually use! + */ + if (root->File == PROGRAM_STATE_VAR) { + GLboolean direct; + GLint index = _slang_alloc_statevar(n, emitInfo->prog->Parameters, &direct); + if (index < 0) { + slang_info_log_error(emitInfo->log, "Error parsing state variable"); + return NULL; + } + if (direct) { + root->Index = index; + return NULL; /* all done */ + } + } + + /* do codegen for struct */ + emit(emitInfo, n->Children[0]); + assert(n->Children[0]->Store->Index >= 0); + + + fieldOffset = n->Store->Index; + fieldSize = n->Store->Size; + + _slang_copy_ir_storage(n->Store, n->Children[0]->Store); + + n->Store->Index = n->Children[0]->Store->Index + fieldOffset / 4; + n->Store->Size = fieldSize; + + switch (fieldSize) { + case 1: + { + GLint swz = fieldOffset % 4; + n->Store->Swizzle = MAKE_SWIZZLE4(swz, swz, swz, swz); + } + break; + case 2: + n->Store->Swizzle = MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, + SWIZZLE_NIL, SWIZZLE_NIL); + break; + case 3: + n->Store->Swizzle = MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, + SWIZZLE_Z, SWIZZLE_NIL); + break; + default: + n->Store->Swizzle = SWIZZLE_XYZW; + } + + assert(n->Store->Index >= 0); + + return NULL; /* no instruction */ +} + + +/** + * Emit code for a variable declaration. + * This usually doesn't result in any code generation, but just + * memory allocation. + */ +static struct prog_instruction * +emit_var_decl(slang_emit_info *emitInfo, slang_ir_node *n) +{ + 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 */ + /*NEW*/ + if (n->Store->Index < 0 && !_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->store == n->Store); + } + if (emitInfo->EmitComments) { + /* emit NOP with comment describing the variable's storage location */ + char s[1000]; + _mesa_snprintf(s, sizeof(s), "TEMP[%d]%s = variable %s (size %d)", + n->Store->Index, + _mesa_swizzle_string(n->Store->Swizzle, 0, GL_FALSE), + (n->Var ? (char *) n->Var->a_name : "anonymous"), + n->Store->Size); + emit_comment(emitInfo, s); + } + return NULL; +} + + +/** + * Emit code for a reference to a variable. + * Actually, no code is generated but we may do some memory allocation. + * In particular, state vars (uniforms) are allocated on an as-needed basis. + */ +static struct prog_instruction * +emit_var_ref(slang_emit_info *emitInfo, slang_ir_node *n) +{ + assert(n->Store); + assert(n->Store->File != PROGRAM_UNDEFINED); + + if (n->Store->File == PROGRAM_STATE_VAR && n->Store->Index < 0) { + GLboolean direct; + GLint index = _slang_alloc_statevar(n, emitInfo->prog->Parameters, &direct); + if (index < 0) { + /* error */ + char s[100]; + /* XXX isn't this really an out of memory/resources error? */ + _mesa_snprintf(s, sizeof(s), "Undefined variable '%s'", + (char *) n->Var->a_name); + slang_info_log_error(emitInfo->log, s); + return NULL; + } + + n->Store->Index = index; + } + else if (n->Store->File == PROGRAM_UNIFORM || + n->Store->File == PROGRAM_SAMPLER) { + /* mark var as used */ + _mesa_use_uniform(emitInfo->prog->Parameters, (char *) n->Var->a_name); + } + else if (n->Store->File == PROGRAM_INPUT) { + assert(n->Store->Index >= 0); + emitInfo->prog->InputsRead |= (1 << n->Store->Index); + } + + if (n->Store->Index < 0) { + /* probably ran out of registers */ + return NULL; + } + assert(n->Store->Size > 0); + + return NULL; +} + + +static struct prog_instruction * +emit(slang_emit_info *emitInfo, slang_ir_node *n) +{ + struct prog_instruction *inst; + if (!n) + return NULL; + + if (emitInfo->log->error_flag) { + return NULL; + } + + if (n->Comment) { + inst = new_instruction(emitInfo, OPCODE_NOP); + if (inst) { + inst->Comment = _mesa_strdup(n->Comment); + } + inst = 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]); + if (emitInfo->log->error_flag) + return NULL; + inst = emit(emitInfo, n->Children[1]); +#if 0 + assert(!n->Store); +#endif + 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 */ + inst = emit_var_decl(emitInfo, n); + return inst; + + case IR_VAR: + /* Reference to a variable + * Storage should have already been resolved/allocated. + */ + return emit_var_ref(emitInfo, n); + + 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); + + /* Simple arithmetic */ + /* unary */ + case IR_MOVE: + case IR_RSQ: + case IR_RCP: + case IR_FLOOR: + case IR_FRAC: + case IR_F_TO_I: + case IR_I_TO_F: + case IR_ABS: + case IR_SIN: + case IR_COS: + case IR_DDX: + case IR_DDY: + case IR_EXP: + case IR_EXP2: + case IR_LOG2: + case IR_NOISE1: + case IR_NOISE2: + case IR_NOISE3: + case IR_NOISE4: + case IR_NRM4: + case IR_NRM3: + /* binary */ + case IR_ADD: + case IR_SUB: + case IR_MUL: + case IR_DOT4: + case IR_DOT3: + case IR_DOT2: + 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: + /* trinary operators */ + case IR_LRP: + case IR_CMP: + return emit_arith(emitInfo, n); + + case IR_EQUAL: + case IR_NOTEQUAL: + return emit_compare(emitInfo, n); + + case IR_CLAMP: + return emit_clamp(emitInfo, n); + case IR_TEX: + case IR_TEXB: + case IR_TEXP: + case IR_TEX_SH: + case IR_TEXB_SH: + case IR_TEXP_SH: + 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_COPY: + return emit_copy(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_KILL: + return emit_kill(emitInfo); + + case IR_CALL: + /* new variable scope for subroutines/function calls */ + _slang_push_var_table(emitInfo->vt); + inst = emit_fcall(emitInfo, n); + _slang_pop_var_table(emitInfo->vt); + return inst; + + case IR_IF: + return emit_if(emitInfo, n); + + case IR_LOOP: + return emit_loop(emitInfo, n); + case IR_BREAK_IF_TRUE: + case IR_CONT_IF_TRUE: + return emit_cont_break_if_true(emitInfo, n); + 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 emit_return(emitInfo, n); + + case IR_NOP: + return NULL; + + default: + _mesa_problem(NULL, "Unexpected IR opcode in emit()\n"); + } + return NULL; +} + + +/** + * After code generation, any subroutines will be in separate program + * objects. This function appends all the subroutines onto the main + * program and resolves the linking of all the branch/call instructions. + * XXX this logic should really be part of the linking process... + */ +static void +_slang_resolve_subroutines(slang_emit_info *emitInfo) +{ + GET_CURRENT_CONTEXT(ctx); + struct gl_program *mainP = emitInfo->prog; + GLuint *subroutineLoc, i, total; + + subroutineLoc + = (GLuint *) malloc(emitInfo->NumSubroutines * sizeof(GLuint)); + + /* total number of instructions */ + total = mainP->NumInstructions; + for (i = 0; i < emitInfo->NumSubroutines; i++) { + subroutineLoc[i] = total; + total += emitInfo->Subroutines[i]->NumInstructions; + } + + /* adjust BranchTargets within the functions */ + for (i = 0; i < emitInfo->NumSubroutines; i++) { + struct gl_program *sub = emitInfo->Subroutines[i]; + GLuint j; + for (j = 0; j < sub->NumInstructions; j++) { + struct prog_instruction *inst = sub->Instructions + j; + if (inst->Opcode != OPCODE_CAL && inst->BranchTarget >= 0) { + inst->BranchTarget += subroutineLoc[i]; + } + } + } + + /* append subroutines' instructions after main's instructions */ + mainP->Instructions = _mesa_realloc_instructions(mainP->Instructions, + mainP->NumInstructions, + total); + mainP->NumInstructions = total; + for (i = 0; i < emitInfo->NumSubroutines; i++) { + struct gl_program *sub = emitInfo->Subroutines[i]; + _mesa_copy_instructions(mainP->Instructions + subroutineLoc[i], + sub->Instructions, + sub->NumInstructions); + /* delete subroutine code */ + sub->Parameters = NULL; /* prevent double-free */ + _mesa_reference_program(ctx, &emitInfo->Subroutines[i], NULL); + } + + /* free subroutine list */ + if (emitInfo->Subroutines) { + free(emitInfo->Subroutines); + emitInfo->Subroutines = NULL; + } + emitInfo->NumSubroutines = 0; + + /* Examine CAL instructions. + * At this point, the BranchTarget field of the CAL instruction is + * the number/id of the subroutine to call (an index into the + * emitInfo->Subroutines list). + * Translate that into an actual instruction location now. + */ + for (i = 0; i < mainP->NumInstructions; i++) { + struct prog_instruction *inst = mainP->Instructions + i; + if (inst->Opcode == OPCODE_CAL) { + const GLuint f = inst->BranchTarget; + inst->BranchTarget = subroutineLoc[f]; + } + } + + free(subroutineLoc); +} + + + +/** + * Convert the IR tree into GPU instructions. + * \param n root of IR tree + * \param vt variable table + * \param prog program to put GPU instructions into + * \param pragmas controls codegen options + * \param withEnd if true, emit END opcode at end + * \param log log for emitting errors/warnings/info + */ +GLboolean +_slang_emit_code(slang_ir_node *n, slang_var_table *vt, + struct gl_program *prog, + const struct gl_sl_pragmas *pragmas, + GLboolean withEnd, + slang_info_log *log) +{ + GET_CURRENT_CONTEXT(ctx); + GLboolean success; + slang_emit_info emitInfo; + GLuint maxUniforms; + + emitInfo.log = log; + emitInfo.vt = vt; + emitInfo.prog = prog; + emitInfo.Subroutines = NULL; + emitInfo.NumSubroutines = 0; + emitInfo.MaxInstructions = prog->NumInstructions; + + emitInfo.EmitHighLevelInstructions = ctx->Shader.EmitHighLevelInstructions; + emitInfo.EmitCondCodes = ctx->Shader.EmitCondCodes; + emitInfo.EmitComments = ctx->Shader.EmitComments || pragmas->Debug; + emitInfo.EmitBeginEndSub = GL_TRUE; + + if (!emitInfo.EmitCondCodes) { + emitInfo.EmitHighLevelInstructions = GL_TRUE; + } + + /* Check uniform/constant limits */ + if (prog->Target == GL_FRAGMENT_PROGRAM_ARB) { + maxUniforms = ctx->Const.FragmentProgram.MaxUniformComponents / 4; + } + else { + assert(prog->Target == GL_VERTEX_PROGRAM_ARB); + maxUniforms = ctx->Const.VertexProgram.MaxUniformComponents / 4; + } + if (prog->Parameters->NumParameters > maxUniforms) { + slang_info_log_error(log, "Constant/uniform register limit exceeded " + "(max=%u vec4)", maxUniforms); + + return GL_FALSE; + } + + (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); + if (!inst) { + return GL_FALSE; + } + } + + _slang_resolve_subroutines(&emitInfo); + + 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; +} |