/* * 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_codegen.c * Mesa GLSL code generator. Convert AST to IR tree. * \author Brian Paul */ #include "imports.h" #include "macros.h" #include "slang_assemble.h" #include "slang_codegen.h" #include "slang_compile.h" #include "slang_storage.h" #include "slang_error.h" #include "slang_simplify.h" #include "slang_emit.h" #include "slang_ir.h" #include "mtypes.h" #include "program.h" #include "slang_print.h" static slang_function *CurFunction = NULL; static slang_ir_node * slang_assemble_operation(slang_assemble_ctx * A, slang_operation *oper); /** * Allocate storage for given variable, attach it to 'ir'. */ static GLboolean slang_alloc_var_storage(slang_variable *variable, slang_ir_node *ir) { slang_ir_storage *store; assert(variable); /*assert(!variable->aux);*/ if (variable->aux) { store = (slang_ir_storage *) variable->aux; ir->Store = store; if (store) store->Size = -12; } else { /* alloc storage */ store = (slang_ir_storage *) _mesa_calloc(sizeof(*store)); store->File = PROGRAM_TEMPORARY; store->Index = -1; store->Size = -10; variable->aux = store; ir->Store = store; } return GL_TRUE; } static slang_ir_node * new_node(slang_ir_opcode op, slang_ir_node *left, slang_ir_node *right) { slang_ir_node *n = (slang_ir_node *) calloc(1, sizeof(slang_ir_node)); if (n) { n->Opcode = op; n->Children[0] = left; n->Children[1] = right; n->Swizzle = SWIZZLE_NOOP; n->Writemask = WRITEMASK_XYZW; } return n; } static slang_ir_node * new_seq(slang_ir_node *left, slang_ir_node *right) { assert(left); assert(right); return new_node(IR_SEQ, left, right); } static slang_ir_node * new_label(const char *name) { slang_ir_node *n = new_node(IR_LABEL, NULL, NULL); n->Target = _mesa_strdup(name); return n; } static slang_ir_node * new_float_literal(float x, float y, float z, float w) { slang_ir_node *n = new_node(IR_FLOAT, NULL, NULL); n->Value[0] = x; n->Value[1] = y; n->Value[2] = z; n->Value[3] = w; return n; } static slang_ir_node * new_cjump(slang_ir_node *cond, const char *target) { slang_ir_node *n = new_node(IR_CJUMP, cond, NULL); n->Target = _mesa_strdup(target); return n; } static slang_ir_node * new_jump(const char *target) { slang_ir_node *n = new_node(IR_JUMP, NULL, NULL); if (n) { n->Target = _mesa_strdup(target); } return n; } /** * New IR_VAR_DECL node - allocate storage for a new variable. */ static slang_ir_node * new_var_decl(slang_assemble_ctx *A, slang_variable *v) { slang_ir_node *n = new_node(IR_VAR_DECL, NULL, NULL); if (n) { n->Var = v; v->declared = GL_TRUE; } return n; } /** * New IR_VAR node - a reference to a previously declared variable. */ static slang_ir_node * new_var(slang_assemble_ctx *A, slang_operation *oper, slang_atom name, GLuint swizzle) { slang_variable *v = _slang_locate_variable(oper->locals, name, GL_TRUE); slang_ir_node *n = new_node(IR_VAR, NULL, NULL); if (!v) { printf("VAR NOT FOUND %s\n", (char *) name); assert(v); } /** assert(v->declared); **/ assert(!oper->var || oper->var == v); v->used = GL_TRUE; oper->var = v; n->Swizzle = swizzle; n->Var = v; slang_resolve_storage(NULL, n, A->program); return n; } static GLboolean slang_is_writemask(const char *field, GLuint *mask) { const GLuint n = 4; GLuint i, bit, c = 0; for (i = 0; i < n && field[i]; i++) { switch (field[i]) { case 'x': case 'r': bit = WRITEMASK_X; break; case 'y': case 'g': bit = WRITEMASK_Y; break; case 'z': case 'b': bit = WRITEMASK_Z; break; case 'w': case 'a': bit = WRITEMASK_W; break; default: return GL_FALSE; } if (c & bit) return GL_FALSE; c |= bit; } *mask = c; return GL_TRUE; } static slang_ir_node * slang_assemble_return(slang_assemble_ctx * A, slang_operation *oper) { if (oper->num_children == 0) { /* Convert to: * goto __endOfFunction; */ oper->type = slang_oper_goto; oper->a_id = slang_atom_pool_atom(A->atoms, CurFunction->end_label); } else { /* * Convert from: * return expr; * To: * __retVal = expr; * goto __endOfFunction; */ slang_operation *block, *assign, *jump; slang_atom a_retVal; a_retVal = slang_atom_pool_atom(A->atoms, "__retVal"); assert(a_retVal); #if 1 { slang_variable *v = _slang_locate_variable(oper->locals, a_retVal, GL_TRUE); assert(v); } #endif block = slang_operation_new(1); block->type = slang_oper_block_no_new_scope; block->num_children = 2; block->children = slang_operation_new(2); assert(block->locals); block->locals->outer_scope = oper->locals->outer_scope; /* child[0]: __retVal = expr; */ assign = &block->children[0]; assign->type = slang_oper_assign; assign->locals->outer_scope = block->locals; assign->num_children = 2; assign->children = slang_operation_new(2); /* lhs */ assign->children[0].type = slang_oper_identifier; assign->children[0].a_id = a_retVal; assign->children[0].locals->outer_scope = assign->locals; /* rhs */ #if 0 assign->children[1] = oper->children[0]; /* XXX copy */ #else slang_operation_copy(&assign->children[1], &oper->children[0]); #endif /* child[1]: goto __endOfFunction */ jump = &block->children[1]; jump->type = slang_oper_goto; assert(CurFunction->end_label); jump->a_id = slang_atom_pool_atom(A->atoms, CurFunction->end_label); #if 00 printf("NEW RETURN:\n"); slang_print_tree(block, 0); #endif slang_operation_copy(oper, block); /* XXX destruct block */ } /* assemble the new code */ return slang_assemble_operation(A, oper); } /** * Check if the given function is really just a wrapper for an * basic assembly instruction. */ static GLboolean slang_is_asm_function(const slang_function *fun) { if (fun->body->type == slang_oper_block_no_new_scope && fun->body->num_children == 1 && fun->body->children[0].type == slang_oper_asm) { return GL_TRUE; } return GL_FALSE; } /** * Produce inline code for a call to an assembly instruction. */ static slang_operation * slang_inline_asm_function(slang_assemble_ctx *A, slang_function *fun, slang_operation *oper) { const int numArgs = oper->num_children; const slang_operation *args = oper->children; GLuint i; slang_operation *inlined = slang_operation_new(1); /*assert(oper->type == slang_oper_call); or vec4_add, etc */ inlined->type = fun->body->children[0].type; inlined->a_id = fun->body->children[0].a_id; inlined->num_children = numArgs; inlined->children = slang_operation_new(numArgs); #if 0 inlined->locals = slang_variable_scope_copy(oper->locals); #else assert(inlined->locals); inlined->locals->outer_scope = oper->locals->outer_scope; #endif for (i = 0; i < numArgs; i++) { slang_operation_copy(inlined->children + i, args + i); } return inlined; } static void slang_resolve_variable(slang_operation *oper) { if (oper->type != slang_oper_identifier) return; if (!oper->var) { oper->var = _slang_locate_variable(oper->locals, (const slang_atom) oper->a_id, GL_TRUE); if (oper->var) oper->var->used = GL_TRUE; } } /** * Replace particular variables (slang_oper_identifier) with new expressions. */ static void slang_substitute(slang_assemble_ctx *A, slang_operation *oper, GLuint substCount, slang_variable **substOld, slang_operation **substNew, GLboolean isLHS) { switch (oper->type) { case slang_oper_variable_decl: { slang_variable *v = _slang_locate_variable(oper->locals, oper->a_id, GL_TRUE); assert(v); if (v->initializer && oper->num_children == 0) { /* set child of oper to copy of initializer */ oper->num_children = 1; oper->children = slang_operation_new(1); slang_operation_copy(&oper->children[0], v->initializer); } if (oper->num_children == 1) { /* the initializer */ slang_substitute(A, &oper->children[0], substCount, substOld, substNew, GL_FALSE); } } break; case slang_oper_identifier: assert(oper->num_children == 0); if (1/**!isLHS XXX FIX */) { slang_atom id = oper->a_id; slang_variable *v; GLuint i; v = _slang_locate_variable(oper->locals, id, GL_TRUE); if (!v) { printf("var %s not found!\n", (char *) oper->a_id); break; } /* look for a substitution */ for (i = 0; i < substCount; i++) { if (v == substOld[i]) { /* OK, replace this slang_oper_identifier with a new expr */ assert(substNew[i]->type == slang_oper_identifier || substNew[i]->type == slang_oper_literal_float); #if 1 /* DEBUG only */ if (substNew[i]->type == slang_oper_identifier) { assert(substNew[i]->var); assert(substNew[i]->var->a_name); printf("Substitute %s with %s in id node %p\n", (char*)v->a_name, (char*) substNew[i]->var->a_name, (void*) oper); } else printf("Substitute %s with %f in id node %p\n", (char*)v->a_name, substNew[i]->literal[0], (void*) oper); #endif slang_operation_copy(oper, substNew[i]); break; } } } break; case slang_oper_return: /* do return replacement here too */ slang_assemble_return(A, oper); slang_substitute(A, oper, substCount, substOld, substNew, GL_FALSE); break; case slang_oper_assign: case slang_oper_subscript: /* special case: * child[0] can't have substitutions but child[1] can. */ slang_substitute(A, &oper->children[0], substCount, substOld, substNew, GL_TRUE); slang_substitute(A, &oper->children[1], substCount, substOld, substNew, GL_FALSE); break; case slang_oper_field: /* XXX NEW - test */ slang_substitute(A, &oper->children[0], substCount, substOld, substNew, GL_TRUE); break; default: { GLuint i; for (i = 0; i < oper->num_children; i++) slang_substitute(A, &oper->children[i], substCount, substOld, substNew, GL_FALSE); } } } /** * Inline the given function call operation. * Return a new slang_operation that corresponds to the inlined code. */ static slang_operation * slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun, slang_operation *oper, slang_operation *returnOper) { typedef enum { SUBST = 1, COPY_IN, COPY_OUT } ParamMode; ParamMode *paramMode; const GLboolean haveRetValue = _slang_function_has_return_value(fun); const GLuint numArgs = oper->num_children; const GLuint totalArgs = numArgs + haveRetValue; slang_operation *args = oper->children; slang_operation *inlined, *top; slang_variable **substOld; slang_operation **substNew; GLuint substCount, numCopyIn, i; /*assert(oper->type == slang_oper_call); (or (matrix) multiply, etc) */ assert(fun->param_count == totalArgs); /* allocate temporary arrays */ paramMode = (ParamMode *) _mesa_calloc(totalArgs * sizeof(ParamMode)); substOld = (slang_variable **) _mesa_calloc(totalArgs * sizeof(slang_variable *)); substNew = (slang_operation **) _mesa_calloc(totalArgs * sizeof(slang_operation *)); printf("\nInline call to %s (total vars=%d nparams=%d)\n", (char *) fun->header.a_name, fun->parameters->num_variables, numArgs); if (haveRetValue && !returnOper) { /* Create comma sequence for inlined code, the left child will be the * function body and the right child will be a variable (__retVal) * that will get the return value. */ slang_operation *commaSeq; slang_operation *declOper = NULL; slang_variable *resultVar; commaSeq = slang_operation_new(1); commaSeq->type = slang_oper_sequence; assert(commaSeq->locals); commaSeq->locals->outer_scope = oper->locals->outer_scope; commaSeq->num_children = 3; commaSeq->children = slang_operation_new(3); /* allocate the return var */ resultVar = slang_variable_scope_grow(commaSeq->locals); /* printf("ALLOC __retVal from scope %p\n", (void*) commaSeq->locals); */ printf("Alloc __resultTemp in scope %p for retval of calling %s\n", (void*)commaSeq->locals, (char *) fun->header.a_name); resultVar->a_name = slang_atom_pool_atom(A->atoms, "__resultTmp"); resultVar->type = fun->header.type; /* XXX copy? */ /*resultVar->type.qualifier = slang_qual_out;*/ /* child[0] = __resultTmp declaration */ declOper = &commaSeq->children[0]; declOper->type = slang_oper_variable_decl; declOper->a_id = resultVar->a_name; declOper->locals->outer_scope = commaSeq->locals; /*** ??? **/ /* child[1] = function body */ inlined = &commaSeq->children[1]; /* XXXX this may be inappropriate!!!!: */ inlined->locals->outer_scope = commaSeq->locals; /* child[2] = __resultTmp reference */ returnOper = &commaSeq->children[2]; returnOper->type = slang_oper_identifier; returnOper->a_id = resultVar->a_name; returnOper->locals->outer_scope = commaSeq->locals; declOper->locals->outer_scope = commaSeq->locals; top = commaSeq; } else { top = inlined = slang_operation_new(1); /* XXXX this may be inappropriate!!!! */ inlined->locals->outer_scope = oper->locals->outer_scope; } assert(inlined->locals); /* Examine the parameters, look for inout/out params, look for possible * substitutions, etc: * param type behaviour * in copy actual to local * const in substitute param with actual * out copy out */ substCount = 0; for (i = 0; i < totalArgs; i++) { slang_variable *p = &fun->parameters->variables[i]; printf("Param %d: %s %s \n", i, slang_type_qual_string(p->type.qualifier), (char *) p->a_name); if (p->type.qualifier == slang_qual_inout || p->type.qualifier == slang_qual_out) { /* an output param */ slang_operation *arg; if (i < numArgs) arg = &args[i]; else arg = returnOper; paramMode[i] = SUBST; assert(arg->type == slang_oper_identifier /*||arg->type == slang_oper_variable_decl*/); slang_resolve_variable(arg); /* replace parameter 'p' with argument 'arg' */ substOld[substCount] = p; substNew[substCount] = arg; /* will get copied */ substCount++; } else if (p->type.qualifier == slang_qual_const) { /* a constant input param */ if (args[i].type == slang_oper_identifier || args[i].type == slang_oper_literal_float) { /* replace all occurances of this parameter variable with the * actual argument variable or a literal. */ paramMode[i] = SUBST; slang_resolve_variable(&args[i]); substOld[substCount] = p; substNew[substCount] = &args[i]; /* will get copied */ substCount++; } else { paramMode[i] = COPY_IN; } } else { paramMode[i] = COPY_IN; } assert(paramMode[i]); } #if 00 printf("ABOUT to inline body %p with checksum %d\n", (char *) fun->body, slang_checksum_tree(fun->body)); #endif /* actual code inlining: */ slang_operation_copy(inlined, fun->body); #if 000 printf("======================= orig body code ======================\n"); printf("=== params scope = %p\n", (void*) fun->parameters); slang_print_tree(fun->body, 8); printf("======================= copied code =========================\n"); slang_print_tree(inlined, 8); #endif /* do parameter substitution in inlined code: */ slang_substitute(A, inlined, substCount, substOld, substNew, GL_FALSE); #if 000 printf("======================= subst code ==========================\n"); slang_print_tree(inlined, 8); printf("=============================================================\n"); #endif /* New prolog statements: (inserted before the inlined code) * Copy the 'in' arguments. */ numCopyIn = 0; for (i = 0; i < numArgs; i++) { if (paramMode[i] == COPY_IN) { slang_variable *p = &fun->parameters->variables[i]; /* declare parameter 'p' */ slang_operation *decl = slang_operation_insert(&inlined->num_children, &inlined->children, numCopyIn); printf("COPY_IN %s from expr\n", (char*)p->a_name); decl->type = slang_oper_variable_decl; assert(decl->locals); decl->locals = fun->parameters; decl->a_id = p->a_name; decl->num_children = 1; decl->children = slang_operation_new(1); /* child[0] is the var's initializer */ slang_operation_copy(&decl->children[0], args + i); numCopyIn++; } } /* New epilog statements: * 1. Create end of function label to jump to from return statements. * 2. Copy the 'out' parameter vars */ { slang_operation *lab = slang_operation_insert(&inlined->num_children, &inlined->children, inlined->num_children); lab->type = slang_oper_label; lab->a_id = slang_atom_pool_atom(A->atoms, CurFunction->end_label); } for (i = 0; i < totalArgs; i++) { if (paramMode[i] == COPY_OUT) { const slang_variable *p = &fun->parameters->variables[i]; /* actualCallVar = outParam */ /*if (i > 0 || !haveRetValue)*/ slang_operation *ass = slang_operation_insert(&inlined->num_children, &inlined->children, inlined->num_children); ass->type = slang_oper_assign; ass->num_children = 2; ass->locals = _slang_variable_scope_new(inlined->locals); assert(ass->locals); ass->children = slang_operation_new(2); ass->children[0] = args[i]; /*XXX copy */ ass->children[1].type = slang_oper_identifier; ass->children[1].a_id = p->a_name; ass->children[1].locals = _slang_variable_scope_new(ass->locals); } } _mesa_free(paramMode); _mesa_free(substOld); _mesa_free(substNew); printf("Done Inline call to %s (total vars=%d nparams=%d)\n", (char *) fun->header.a_name, fun->parameters->num_variables, numArgs); return top; } static slang_ir_node * slang_assemble_function_call(slang_assemble_ctx *A, slang_function *fun, slang_operation *oper, slang_operation *dest) { slang_ir_node *n; slang_operation *inlined; slang_function *prevFunc; prevFunc = CurFunction; CurFunction = fun; if (!CurFunction->end_label) { char name[200]; sprintf(name, "__endOfFunc_%s_", (char *) CurFunction->header.a_name); CurFunction->end_label = slang_atom_pool_gen(A->atoms, name); } if (slang_is_asm_function(fun) && !dest) { /* assemble assembly function - tree style */ inlined = slang_inline_asm_function(A, fun, oper); } else { /* non-assembly function */ inlined = slang_inline_function_call(A, fun, oper, dest); } /* Replace the function call with the inlined block */ #if 0 slang_operation_construct(oper); slang_operation_copy(oper, inlined); #else *oper = *inlined; #endif #if 1 assert(inlined->locals); printf("*** Inlined code for call to %s:\n", (char*) fun->header.a_name); slang_print_tree(oper, 10); printf("\n"); #endif /* assemble what we just made XXX here??? */ n = slang_assemble_operation(A, oper); CurFunction = prevFunc; return n; } /** * Map "_asm foo" to IR_FOO, etc. */ typedef struct { const char *Name; slang_ir_opcode Opcode; GLuint HaveRetValue, NumParams; } slang_asm_info; static slang_asm_info AsmInfo[] = { /* vec4 binary op */ { "vec4_add", IR_ADD, 1, 2 }, { "vec4_multiply", IR_MUL, 1, 2 }, { "vec4_dot", IR_DOT4, 1, 2 }, { "vec3_dot", IR_DOT3, 1, 2 }, { "vec3_cross", IR_CROSS, 1, 2 }, { "vec4_min", IR_MIN, 1, 2 }, { "vec4_max", IR_MAX, 1, 2 }, { "vec4_seq", IR_SEQ, 1, 2 }, { "vec4_sge", IR_SGE, 1, 2 }, { "vec4_sgt", IR_SGT, 1, 2 }, /* vec4 unary */ { "vec4_floor", IR_FLOOR, 1, 1 }, { "vec4_frac", IR_FRAC, 1, 1 }, { "vec4_abs", IR_ABS, 1, 1 }, /* float binary op */ { "float_add", IR_ADD, 1, 2 }, { "float_subtract", IR_SUB, 1, 2 }, { "float_multiply", IR_MUL, 1, 2 }, { "float_divide", IR_DIV, 1, 2 }, { "float_power", IR_POW, 1, 2 }, /* unary op */ { "int_to_float", IR_I_TO_F, 1, 1 }, { "float_exp", IR_EXP, 1, 1 }, { "float_exp2", IR_EXP2, 1, 1 }, { "float_log2", IR_LOG2, 1, 1 }, { "float_rsq", IR_RSQ, 1, 1 }, { "float_rcp", IR_RCP, 1, 1 }, { "float_sine", IR_SIN, 1, 1 }, { "float_cosine", IR_COS, 1, 1 }, { NULL, IR_NOP, 0, 0 } }; static slang_asm_info * slang_find_asm_info(const char *name) { GLuint i; for (i = 0; AsmInfo[i].Name; i++) { if (_mesa_strcmp(AsmInfo[i].Name, name) == 0) { return AsmInfo + i; } } return NULL; } static GLuint make_writemask(char *field) { GLuint mask = 0x0; while (*field) { switch (*field) { case 'x': mask |= WRITEMASK_X; break; case 'y': mask |= WRITEMASK_Y; break; case 'z': mask |= WRITEMASK_Z; break; case 'w': mask |= WRITEMASK_W; break; default: abort(); } field++; } if (mask == 0x0) return WRITEMASK_XYZW; else return mask; } /** * Generate IR code for an instruction/operation such as: * __asm vec4_dot __retVal.x, v1, v2; */ static slang_ir_node * slang_assemble_asm(slang_assemble_ctx *A, slang_operation *oper, slang_operation *dest) { const slang_asm_info *info; slang_ir_node *kids[2], *n; GLuint j, firstOperand; assert(oper->type == slang_oper_asm); info = slang_find_asm_info((char *) oper->a_id); assert(info); assert(info->NumParams <= 2); if (info->NumParams == oper->num_children) { /* storage for result not specified */ firstOperand = 0; } else { /* storage for result (child[0]) is specified */ firstOperand = 1; } /* assemble child(ren) */ kids[0] = kids[1] = NULL; for (j = 0; j < info->NumParams; j++) { kids[j] = slang_assemble_operation(A, &oper->children[firstOperand + j]); } n = new_node(info->Opcode, kids[0], kids[1]); if (firstOperand) { /* Setup n->Store to be a particular location. Otherwise, storage * for the result (a temporary) will be allocated later. */ GLuint writemask = WRITEMASK_XYZW; slang_operation *dest_oper; slang_ir_node *n0; dest_oper = &oper->children[0]; if (dest_oper->type == slang_oper_field) { /* writemask */ writemask = make_writemask((char*) dest_oper->a_id); dest_oper = &dest_oper->children[0]; } assert(dest_oper->type == slang_oper_identifier); n0 = slang_assemble_operation(A, dest_oper); assert(n0->Var); assert(n0->Store); free(n0); n->Store = n0->Store; n->Writemask = writemask; } return n; } /** * Assemble a function call, given a particular function name. * \param name the function's name (operators like '*' are possible). */ static slang_ir_node * slang_assemble_function_call_name(slang_assemble_ctx *A, const char *name, slang_operation *oper, slang_operation *dest) { slang_operation *params = oper->children; const GLuint param_count = oper->num_children; slang_atom atom; slang_function *fun; atom = slang_atom_pool_atom(A->atoms, name); if (atom == SLANG_ATOM_NULL) return NULL; fun = _slang_locate_function(A->space.funcs, atom, params, param_count, &A->space, A->atoms); if (!fun) { RETURN_ERROR2("Undefined function", name, 0); } return slang_assemble_function_call(A, fun, oper, dest); } static slang_ir_node * slang_assemble_operation(slang_assemble_ctx * A, slang_operation *oper) { switch (oper->type) { case slang_oper_block_no_new_scope: case slang_oper_block_new_scope: /* list of operations */ assert(oper->num_children > 0); { slang_ir_node *n, *first = NULL; GLuint i; for (i = 0; i < oper->num_children; i++) { n = slang_assemble_operation(A, &oper->children[i]); first = first ? new_seq(first, n) : n; } return first; } break; case slang_oper_expression: return slang_assemble_operation(A, &oper->children[0]); break; case slang_oper_while: { slang_ir_node *nStartLabel = new_label("while-start"); slang_ir_node *nCond = slang_assemble_operation(A, &oper->children[0]); slang_ir_node *nNotCond = new_node(IR_NOT, nCond, NULL); slang_ir_node *nBody = slang_assemble_operation(A, &oper->children[1]); slang_ir_node *nEndLabel = new_label("while-end"); slang_ir_node *nCJump = new_cjump(nNotCond, "while-end"); slang_ir_node *nJump = new_jump("while-start"); return new_seq(nStartLabel, new_seq(nCJump, new_seq(nBody, new_seq(nJump, nEndLabel) ) ) ); } break; case slang_oper_less: return new_node(IR_LESS, slang_assemble_operation(A, &oper->children[0]), slang_assemble_operation(A, &oper->children[1])); case slang_oper_add: { slang_ir_node *n; assert(oper->num_children == 2); n = slang_assemble_function_call_name(A, "+", oper, NULL); return n; } case slang_oper_subtract: { slang_ir_node *n; assert(oper->num_children == 2); n = slang_assemble_function_call_name(A, "-", oper, NULL); return n; } case slang_oper_multiply: { slang_ir_node *n; assert(oper->num_children == 2); n = slang_assemble_function_call_name(A, "*", oper, NULL); return n; } case slang_oper_divide: { slang_ir_node *n; assert(oper->num_children == 2); n = slang_assemble_function_call_name(A, "/", oper, NULL); return n; } case slang_oper_variable_decl: { slang_ir_node *n; slang_ir_node *varDecl; slang_variable *v; assert(oper->num_children == 0 || oper->num_children == 1); v = _slang_locate_variable(oper->locals, oper->a_id, GL_TRUE); assert(v); varDecl = new_var_decl(A, v); slang_alloc_var_storage(v, varDecl); if (oper->num_children > 0) { /* child is initializer */ slang_ir_node *var, *init, *rhs; assert(oper->num_children == 1); var = new_var(A, oper, oper->a_id, SWIZZLE_NOOP); /* XXX make copy of this initializer? */ printf("\n*** ASSEMBLE INITIALIZER %p\n", (void*) v->initializer); rhs = slang_assemble_operation(A, &oper->children[0]); init = new_node(IR_MOVE, var, rhs); n = new_seq(varDecl, init); } else if (v->initializer) { slang_ir_node *var, *init, *rhs; var = new_var(A, oper, oper->a_id, SWIZZLE_NOOP); /* XXX make copy of this initializer? */ printf("\n*** ASSEMBLE INITIALIZER %p\n", (void*) v->initializer); rhs = slang_assemble_operation(A, v->initializer); init = new_node(IR_MOVE, var, rhs); n = new_seq(varDecl, init); } else { n = varDecl; } return n; } break; case slang_oper_assign: /* assignment */ /* XXX look for special case of: x = f(a, b) * and replace with f(a, b, x) (where x == hidden __retVal out param) */ if (oper->children[0].type == slang_oper_identifier && oper->children[1].type == slang_oper_call) { /* special case */ slang_ir_node *n; printf(">>>>>>>>>>>>>> Assign function call\n"); n = slang_assemble_function_call_name(A, (const char *) oper->children[1].a_id, &oper->children[1], &oper->children[0]); return n; } else { slang_operation *lhs = &oper->children[0]; slang_ir_node *n, *c0, *c1; GLuint mask = WRITEMASK_XYZW; if (lhs->type == slang_oper_field) { /* writemask */ if (!slang_is_writemask((char *) lhs->a_id, &mask)) mask = WRITEMASK_XYZW; lhs = &lhs->children[0]; } c0 = slang_assemble_operation(A, lhs); c1 = slang_assemble_operation(A, &oper->children[1]); n = new_node(IR_MOVE, c0, c1); n->Writemask = mask; return n; } break; case slang_oper_asm: return slang_assemble_asm(A, oper, NULL); case slang_oper_call: return slang_assemble_function_call_name(A, (const char *) oper->a_id, oper, NULL); break; case slang_oper_return: return slang_assemble_return(A, oper); case slang_oper_goto: return new_jump((char*) oper->a_id); case slang_oper_label: return new_label((char*) oper->a_id); case slang_oper_identifier: { /* If there's a variable associated with this oper (from inlining) * use it. Otherwise, use the oper's var id. */ slang_atom aVar = oper->var ? oper->var->a_name : oper->a_id; slang_ir_node *n = new_var(A, oper, aVar, SWIZZLE_NOOP); assert(oper->var); return n; } break; case slang_oper_field: { slang_assembly_typeinfo ti; slang_assembly_typeinfo_construct(&ti); _slang_typeof_operation(A, &oper->children[0], &ti); if (_slang_type_is_vector(ti.spec.type)) { /* the field should be a swizzle */ const GLuint rows = _slang_type_dim(ti.spec.type); slang_swizzle swz; slang_ir_node *n; if (!_slang_is_swizzle((char *) oper->a_id, rows, &swz)) { RETURN_ERROR("Bad swizzle", 0); } n = slang_assemble_operation(A, &oper->children[0]); n->Swizzle = MAKE_SWIZZLE4(swz.swizzle[0], swz.swizzle[1], swz.swizzle[2], swz.swizzle[3]); return n; } else if (ti.spec.type == slang_spec_float) { const GLuint rows = 1; slang_swizzle swz; slang_ir_node *n; if (!_slang_is_swizzle((char *) oper->a_id, rows, &swz)) { RETURN_ERROR("Bad swizzle", 0); } n = slang_assemble_operation(A, &oper->children[0]); n->Swizzle = MAKE_SWIZZLE4(swz.swizzle[0], swz.swizzle[1], swz.swizzle[2], swz.swizzle[3]); return n; } else { /* the field is a structure member */ abort(); } } break; case slang_oper_subscript: /* array dereference */ if (oper->children[1].type == slang_oper_literal_int) { /* compile-time constant index - OK */ slang_assembly_typeinfo ti; slang_ir_node *base; slang_ir_storage *store2; GLint index; slang_assembly_typeinfo_construct(&ti); _slang_typeof_operation(A, &oper->children[0], &ti); base = slang_assemble_operation(A, &oper->children[0]); assert(base->Opcode == IR_VAR); index = (GLint) oper->children[1].literal[0]; /* printf("element[%d]\n", index); */ #if 1 store2 = (slang_ir_storage *) _mesa_calloc(sizeof(*store2)); *store2 = *base->Store; base->Store = store2; base->Store->Size = -15; #endif assert(base->Store); base->Store->Index += index; base->Store->Size = 1; return base; } else { /* run-time index - TBD */ abort(); } return NULL; case slang_oper_literal_float: return new_float_literal(oper->literal[0], oper->literal[1], oper->literal[2], oper->literal[3]); case slang_oper_literal_int: return new_float_literal(oper->literal[0], 0, 0, 0); case slang_oper_literal_bool: return new_float_literal(oper->literal[0], 0, 0, 0); case slang_oper_postincrement: /* XXX not 100% about this */ { slang_ir_node *var = slang_assemble_operation(A, &oper->children[0]); slang_ir_node *one = new_float_literal(1.0, 1.0, 1.0, 1.0); slang_ir_node *sum = new_node(IR_ADD, var, one); slang_ir_node *assign = new_node(IR_MOVE, var, sum); return assign; } break; case slang_oper_sequence: { slang_ir_node *top = NULL; GLuint i; for (i = 0; i < oper->num_children; i++) { slang_ir_node *n = slang_assemble_operation(A, &oper->children[i]); top = top ? new_seq(top, n) : n; } return top; } break; case slang_oper_none: return NULL; default: printf("Unhandled node type %d\n", oper->type); abort(); return new_node(IR_NOP, NULL, NULL); } abort(); return NULL; } /** * Produce an IR tree from a function AST. * Then call the code emitter to convert the IR tree into a gl_program. */ struct slang_ir_node_ * _slang_codegen_function(slang_assemble_ctx * A, slang_function * fun) { slang_ir_node *n, *endLabel; if (_mesa_strcmp((char *) fun->header.a_name, "main") != 0 && _mesa_strcmp((char *) fun->header.a_name, "foo") != 0 && _mesa_strcmp((char *) fun->header.a_name, "bar") != 0) return 0; printf("\n*********** Assemble function2(%s)\n", (char*)fun->header.a_name); slang_print_function(fun, 1); A->program->Parameters = _mesa_new_parameter_list(); A->program->Varying = _mesa_new_parameter_list(); /*printf("** Begin Simplify\n");*/ slang_simplify(fun->body, &A->space, A->atoms); /*printf("** End Simplify\n");*/ CurFunction = fun; n = slang_assemble_operation(A, fun->body); if (!CurFunction->end_label) CurFunction->end_label = slang_atom_pool_gen(A->atoms, "__endOfFunction_Main"); endLabel = new_label(fun->end_label); n = new_seq(n, endLabel); CurFunction = NULL; printf("************* New body for %s *****\n", (char*)fun->header.a_name); slang_print_function(fun, 1); printf("************* IR for %s *******\n", (char*)fun->header.a_name); slang_print_ir(n, 0); if (_mesa_strcmp((char*) fun->header.a_name, "main") == 0) { _slang_emit_code(n, A->program); } printf("************* End assemble function2 ************\n\n"); return n; }