diff options
Diffstat (limited to 'src/mesa/shader')
-rw-r--r-- | src/mesa/shader/arbprogparse.c | 127 | ||||
-rw-r--r-- | src/mesa/shader/arbprogram.c | 29 | ||||
-rw-r--r-- | src/mesa/shader/nvprogram.c | 29 | ||||
-rw-r--r-- | src/mesa/shader/prog_execute.c | 35 | ||||
-rw-r--r-- | src/mesa/shader/prog_instruction.c | 7 | ||||
-rw-r--r-- | src/mesa/shader/prog_optimize.c | 92 | ||||
-rw-r--r-- | src/mesa/shader/prog_print.c | 15 | ||||
-rw-r--r-- | src/mesa/shader/program.c | 60 | ||||
-rw-r--r-- | src/mesa/shader/program.h | 2 | ||||
-rw-r--r-- | src/mesa/shader/programopt.c | 120 | ||||
-rw-r--r-- | src/mesa/shader/shader_api.c | 5 | ||||
-rw-r--r-- | src/mesa/shader/slang/slang_builtin.c | 2 | ||||
-rw-r--r-- | src/mesa/shader/slang/slang_codegen.c | 1279 | ||||
-rw-r--r-- | src/mesa/shader/slang/slang_codegen.h | 17 | ||||
-rw-r--r-- | src/mesa/shader/slang/slang_compile.c | 3 | ||||
-rw-r--r-- | src/mesa/shader/slang/slang_compile_operation.c | 68 | ||||
-rw-r--r-- | src/mesa/shader/slang/slang_compile_operation.h | 62 | ||||
-rw-r--r-- | src/mesa/shader/slang/slang_compile_variable.h | 1 | ||||
-rw-r--r-- | src/mesa/shader/slang/slang_link.c | 33 | ||||
-rw-r--r-- | src/mesa/shader/slang/slang_preprocess.c | 94 | ||||
-rw-r--r-- | src/mesa/shader/slang/slang_print.c | 10 |
21 files changed, 1647 insertions, 443 deletions
diff --git a/src/mesa/shader/arbprogparse.c b/src/mesa/shader/arbprogparse.c index c7a031067e..bc65aba39a 100644 --- a/src/mesa/shader/arbprogparse.c +++ b/src/mesa/shader/arbprogparse.c @@ -563,6 +563,7 @@ struct var_cache * we take up with our state tokens or constants. Note that * this is _not_ the same as the number of param registers * we eventually use */ + GLuint swizzle; /**< swizzle to access this variable */ struct var_cache *next; }; @@ -581,6 +582,7 @@ var_cache_create (struct var_cache **va) (**va).param_binding_begin = ~0; (**va).param_binding_length = ~0; (**va).alias_binding = NULL; + (**va).swizzle = SWIZZLE_XYZW; (**va).next = NULL; } } @@ -872,15 +874,16 @@ parse_signed_float (const GLubyte ** inst, struct arb_program *Program) * This picks out a constant value from the parsed array. The constant vector is r * returned in the *values array, which should be of length 4. * - * \param values - The 4 component vector with the constant value in it + * \param values - return the vector constant values. + * \param size - returns the number elements in valuesOut [1..4] */ static GLvoid -parse_constant (const GLubyte ** inst, GLfloat *values, struct arb_program *Program, - GLboolean use) +parse_constant(const GLubyte ** inst, GLfloat *values, GLint *size, + struct arb_program *Program, + GLboolean use) { GLuint components, i; - switch (*(*inst)++) { case CONSTANT_SCALAR: if (use == GL_TRUE) { @@ -893,7 +896,7 @@ parse_constant (const GLubyte ** inst, GLfloat *values, struct arb_program *Prog values[1] = values[2] = values[3] = parse_signed_float (inst, Program); } - + *size = 1; break; case CONSTANT_VECTOR: values[0] = values[1] = values[2] = 0; @@ -902,7 +905,12 @@ parse_constant (const GLubyte ** inst, GLfloat *values, struct arb_program *Prog for (i = 0; i < components; i++) { values[i] = parse_signed_float (inst, Program); } + *size = 4; break; + default: + _mesa_problem(NULL, "unexpected case in parse_constant()"); + values[0] = 0.0F; + *size = 0; } } @@ -945,7 +953,7 @@ parse_generic_attrib_num(GLcontext *ctx, const GLubyte ** inst, { GLint i = parse_integer(inst, Program); - if ((i < 0) || (i >= MAX_VERTEX_PROGRAM_ATTRIBS)) + if ((i < 0) || (i >= MAX_VERTEX_GENERIC_ATTRIBS)) { program_error(ctx, Program->Position, "Invalid generic vertex attribute index"); @@ -1014,7 +1022,10 @@ parse_teximage_num (GLcontext * ctx, const GLubyte ** inst, GLint i = parse_integer (inst, Program); if ((i < 0) || (i >= (int)ctx->Const.MaxTextureImageUnits)) { - program_error(ctx, Program->Position, "Invalid texture image index"); + char s[100]; + _mesa_snprintf(s, sizeof(s), "Invalid texture image index %d (%u is max)", + i, ctx->Const.MaxTextureImageUnits); + program_error(ctx, Program->Position, s); return 1; } @@ -1502,10 +1513,10 @@ generic_attrib_check(struct var_cache *vc_head) { int a; struct var_cache *curr; - GLboolean explicitAttrib[MAX_VERTEX_PROGRAM_ATTRIBS], - genericAttrib[MAX_VERTEX_PROGRAM_ATTRIBS]; + GLboolean explicitAttrib[MAX_VERTEX_GENERIC_ATTRIBS], + genericAttrib[MAX_VERTEX_GENERIC_ATTRIBS]; - for (a=0; a<MAX_VERTEX_PROGRAM_ATTRIBS; a++) { + for (a=0; a<MAX_VERTEX_GENERIC_ATTRIBS; a++) { explicitAttrib[a] = GL_FALSE; genericAttrib[a] = GL_FALSE; } @@ -1516,11 +1527,11 @@ generic_attrib_check(struct var_cache *vc_head) if (curr->attrib_is_generic) { GLuint attr = (curr->attrib_binding == 0) ? 0 : (curr->attrib_binding - VERT_ATTRIB_GENERIC0); - assert(attr < MAX_VERTEX_PROGRAM_ATTRIBS); + assert(attr < MAX_VERTEX_GENERIC_ATTRIBS); genericAttrib[attr] = GL_TRUE; } else { - assert(curr->attrib_binding < MAX_VERTEX_PROGRAM_ATTRIBS); + assert(curr->attrib_binding < MAX_VERTEX_GENERIC_ATTRIBS); explicitAttrib[ curr->attrib_binding ] = GL_TRUE; } } @@ -1528,7 +1539,7 @@ generic_attrib_check(struct var_cache *vc_head) curr = curr->next; } - for (a=0; a<MAX_VERTEX_PROGRAM_ATTRIBS; a++) { + for (a=0; a<MAX_VERTEX_GENERIC_ATTRIBS; a++) { if ((explicitAttrib[a]) && (genericAttrib[a])) return 1; } @@ -1816,7 +1827,6 @@ parse_param_elements (GLcontext * ctx, const GLubyte ** inst, GLint idx; GLuint err = 0; gl_state_index state_tokens[STATE_LENGTH] = {0, 0, 0, 0, 0}; - GLfloat const_values[4]; GLubyte token = *(*inst)++; @@ -1908,18 +1918,31 @@ parse_param_elements (GLcontext * ctx, const GLubyte ** inst, case PARAM_CONSTANT: /* parsing something like {1.0, 2.0, 3.0, 4.0} */ - parse_constant (inst, const_values, Program, use); - idx = _mesa_add_named_constant(Program->Base.Parameters, - (char *) param_var->name, - const_values, 4); - if (param_var->param_binding_begin == ~0U) - param_var->param_binding_begin = idx; - param_var->param_binding_type = PROGRAM_STATE_VAR; - /* Note: when we reference this parameter in an instruction later, - * we'll check if it's really a constant/immediate and set the - * instruction register type appropriately. - */ - param_var->param_binding_length++; + { + GLfloat const_values[4]; + GLint size; + parse_constant(inst, const_values, &size, Program, use); + if (param_var->name[0] == ' ') { + /* this is an unnamed constant */ + idx = _mesa_add_unnamed_constant(Program->Base.Parameters, + const_values, size, + ¶m_var->swizzle); + } + else { + /* named parameter/constant */ + idx = _mesa_add_named_constant(Program->Base.Parameters, + (char *) param_var->name, + const_values, size); + } + if (param_var->param_binding_begin == ~0U) + param_var->param_binding_begin = idx; + param_var->param_binding_type = PROGRAM_STATE_VAR; + /* Note: when we reference this parameter in an instruction later, + * we'll check if it's really a constant/immediate and set the + * instruction register type appropriately. + */ + param_var->param_binding_length++; + } break; default: @@ -2428,6 +2451,9 @@ parse_swizzle_mask(const GLubyte ** inst, GLubyte *swizzle, GLint len) return; } } + + if (len == 1) + swizzle[1] = swizzle[2] = swizzle[3] = swizzle[0]; } @@ -2482,7 +2508,7 @@ static GLuint parse_src_reg (GLcontext * ctx, const GLubyte ** inst, struct var_cache **vc_head, struct arb_program *Program, - gl_register_file * File, GLint * Index, + gl_register_file * File, GLint * Index, GLuint *swizzle, GLboolean *IsRelOffset ) { struct var_cache *src; @@ -2491,6 +2517,8 @@ parse_src_reg (GLcontext * ctx, const GLubyte ** inst, *IsRelOffset = 0; + *swizzle = SWIZZLE_XYZW; /* default */ + /* And the binding for the src */ switch (*(*inst)++) { case REGISTER_ATTRIB: @@ -2546,6 +2574,7 @@ parse_src_reg (GLcontext * ctx, const GLubyte ** inst, } *Index = src->param_binding_begin + offset; + *swizzle = src->swizzle; break; case ARRAY_INDEX_RELATIVE: @@ -2568,6 +2597,7 @@ parse_src_reg (GLcontext * ctx, const GLubyte ** inst, /* And store it properly */ *Index = src->param_binding_begin + rel_off; *IsRelOffset = 1; + *swizzle = src->swizzle; } break; } @@ -2579,6 +2609,7 @@ parse_src_reg (GLcontext * ctx, const GLubyte ** inst, *File = (gl_register_file) src->param_binding_type; *Index = src->param_binding_begin; + *swizzle = src->swizzle; break; } break; @@ -2647,6 +2678,21 @@ parse_src_reg (GLcontext * ctx, const GLubyte ** inst, } +static GLuint +swizzle_swizzle(GLuint baseSwizzle, const GLubyte swizzle[4]) +{ + GLuint i, swz, s[4]; + for (i = 0; i < 4; i++) { + GLuint c = swizzle[i]; + if (c <= SWIZZLE_W) + s[i] = GET_SWZ(baseSwizzle, c); + else + s[i] = c; + } + swz = MAKE_SWIZZLE4(s[0], s[1], s[2], s[3]); + return swz; +} + /** * Parse vertex/fragment program vector source register. */ @@ -2661,12 +2707,14 @@ parse_vector_src_reg(GLcontext *ctx, const GLubyte **inst, GLubyte negateMask; GLubyte swizzle[4]; GLboolean isRelOffset; + GLuint baseSwizzle; /* Grab the sign */ negateMask = (parse_sign (inst) == -1) ? NEGATE_XYZW : NEGATE_NONE; /* And the src reg */ - if (parse_src_reg(ctx, inst, vc_head, program, &file, &index, &isRelOffset)) + if (parse_src_reg(ctx, inst, vc_head, program, &file, &index, &baseSwizzle, + &isRelOffset)) return 1; /* finally, the swizzle */ @@ -2674,7 +2722,7 @@ parse_vector_src_reg(GLcontext *ctx, const GLubyte **inst, reg->File = file; reg->Index = index; - reg->Swizzle = MAKE_SWIZZLE4(swizzle[0], swizzle[1], swizzle[2], swizzle[3]); + reg->Swizzle = swizzle_swizzle(baseSwizzle, swizzle); reg->Negate = negateMask; reg->RelAddr = isRelOffset; return 0; @@ -2695,12 +2743,14 @@ parse_scalar_src_reg(GLcontext *ctx, const GLubyte **inst, GLubyte negateMask; GLubyte swizzle[4]; GLboolean isRelOffset; + GLuint baseSwizzle; /* Grab the sign */ negateMask = (parse_sign (inst) == -1) ? NEGATE_XYZW : NEGATE_NONE; /* And the src reg */ - if (parse_src_reg(ctx, inst, vc_head, program, &file, &index, &isRelOffset)) + if (parse_src_reg(ctx, inst, vc_head, program, &file, &index, &baseSwizzle, + &isRelOffset)) return 1; /* finally, the swizzle */ @@ -2708,7 +2758,7 @@ parse_scalar_src_reg(GLcontext *ctx, const GLubyte **inst, reg->File = file; reg->Index = index; - reg->Swizzle = (swizzle[0] << 0); + reg->Swizzle = swizzle_swizzle(baseSwizzle, swizzle); reg->Negate = negateMask; reg->RelAddr = isRelOffset; return 0; @@ -3019,8 +3069,10 @@ parse_fp_instruction (GLcontext * ctx, const GLubyte ** inst, GLubyte negateMask; gl_register_file file; GLint index; + GLuint baseSwizzle; - if (parse_src_reg(ctx, inst, vc_head, Program, &file, &index, &rel)) + if (parse_src_reg(ctx, inst, vc_head, Program, &file, &index, + &baseSwizzle, &rel)) return 1; parse_extended_swizzle_mask(inst, swizzle, &negateMask); fp->SrcReg[0].File = file; @@ -3360,11 +3412,13 @@ parse_vp_instruction (GLcontext * ctx, const GLubyte ** inst, GLboolean relAddr; gl_register_file file; GLint index; + GLuint baseSwizzle; if (parse_dst_reg(ctx, inst, vc_head, Program, &vp->DstReg)) return 1; - if (parse_src_reg(ctx, inst, vc_head, Program, &file, &index, &relAddr)) + if (parse_src_reg(ctx, inst, vc_head, Program, &file, &index, + &baseSwizzle, &relAddr)) return 1; parse_extended_swizzle_mask (inst, swizzle, &negateMask); vp->SrcReg[0].File = file; @@ -3919,6 +3973,13 @@ _mesa_parse_arb_fragment_program(GLcontext* ctx, GLenum target, if (program->FogOption) program->Base.InputsRead |= FRAG_BIT_FOGC; + + /* XXX: assume that ARB fragment programs don't have access to the + * FrontFacing and PointCoord values stuffed into the fog + * coordinate in GLSL shaders. + */ + if (program->Base.InputsRead & FRAG_BIT_FOGC) + program->UsesFogFragCoord = GL_TRUE; if (program->Base.Instructions) _mesa_free(program->Base.Instructions); diff --git a/src/mesa/shader/arbprogram.c b/src/mesa/shader/arbprogram.c index 317d623a22..39136efada 100644 --- a/src/mesa/shader/arbprogram.c +++ b/src/mesa/shader/arbprogram.c @@ -254,6 +254,8 @@ _mesa_EnableVertexAttribArrayARB(GLuint index) return; } + ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib)); + FLUSH_VERTICES(ctx, _NEW_ARRAY); ctx->Array.ArrayObj->VertexAttrib[index].Enabled = GL_TRUE; ctx->Array.ArrayObj->_Enabled |= _NEW_ARRAY_ATTRIB(index); @@ -273,6 +275,8 @@ _mesa_DisableVertexAttribArrayARB(GLuint index) return; } + ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib)); + FLUSH_VERTICES(ctx, _NEW_ARRAY); ctx->Array.ArrayObj->VertexAttrib[index].Enabled = GL_FALSE; ctx->Array.ArrayObj->_Enabled &= ~_NEW_ARRAY_ATTRIB(index); @@ -299,32 +303,41 @@ _mesa_GetVertexAttribdvARB(GLuint index, GLenum pname, GLdouble *params) } +/** + * Return info for a generic vertex attribute array (no alias with + * legacy vertex attributes (pos, normal, color, etc)). + */ void GLAPIENTRY _mesa_GetVertexAttribfvARB(GLuint index, GLenum pname, GLfloat *params) { + const struct gl_client_array *array; GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); - if (index >= MAX_VERTEX_PROGRAM_ATTRIBS) { + if (index >= MAX_VERTEX_GENERIC_ATTRIBS) { _mesa_error(ctx, GL_INVALID_VALUE, "glGetVertexAttribfvARB(index)"); return; } + ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib)); + + array = &ctx->Array.ArrayObj->VertexAttrib[index]; + switch (pname) { case GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB: - params[0] = (GLfloat) ctx->Array.ArrayObj->VertexAttrib[index].Enabled; + params[0] = (GLfloat) array->Enabled; break; case GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB: - params[0] = (GLfloat) ctx->Array.ArrayObj->VertexAttrib[index].Size; + params[0] = (GLfloat) array->Size; break; case GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB: - params[0] = (GLfloat) ctx->Array.ArrayObj->VertexAttrib[index].Stride; + params[0] = (GLfloat) array->Stride; break; case GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB: - params[0] = (GLfloat) ctx->Array.ArrayObj->VertexAttrib[index].Type; + params[0] = (GLfloat) array->Type; break; case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB: - params[0] = ctx->Array.ArrayObj->VertexAttrib[index].Normalized; + params[0] = array->Normalized; break; case GL_CURRENT_VERTEX_ATTRIB_ARB: if (index == 0) { @@ -336,7 +349,7 @@ _mesa_GetVertexAttribfvARB(GLuint index, GLenum pname, GLfloat *params) COPY_4V(params, ctx->Current.Attrib[VERT_ATTRIB_GENERIC0 + index]); break; case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB: - params[0] = (GLfloat) ctx->Array.ArrayObj->VertexAttrib[index].BufferObj->Name; + params[0] = (GLfloat) array->BufferObj->Name; break; default: _mesa_error(ctx, GL_INVALID_ENUM, "glGetVertexAttribfvARB(pname)"); @@ -380,6 +393,8 @@ _mesa_GetVertexAttribPointervARB(GLuint index, GLenum pname, GLvoid **pointer) return; } + ASSERT(index < Elements(ctx->Array.ArrayObj->VertexAttrib)); + *pointer = (GLvoid *) ctx->Array.ArrayObj->VertexAttrib[index].Ptr; } diff --git a/src/mesa/shader/nvprogram.c b/src/mesa/shader/nvprogram.c index 8ba521182b..d6469b17be 100644 --- a/src/mesa/shader/nvprogram.c +++ b/src/mesa/shader/nvprogram.c @@ -354,6 +354,7 @@ _mesa_GetTrackMatrixivNV(GLenum target, GLuint address, void GLAPIENTRY _mesa_GetVertexAttribdvNV(GLuint index, GLenum pname, GLdouble *params) { + const struct gl_client_array *array; GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); @@ -362,15 +363,17 @@ _mesa_GetVertexAttribdvNV(GLuint index, GLenum pname, GLdouble *params) return; } + array = &ctx->Array.ArrayObj->VertexAttrib[index]; + switch (pname) { case GL_ATTRIB_ARRAY_SIZE_NV: - params[0] = ctx->Array.ArrayObj->VertexAttrib[index].Size; + params[0] = array->Size; break; case GL_ATTRIB_ARRAY_STRIDE_NV: - params[0] = ctx->Array.ArrayObj->VertexAttrib[index].Stride; + params[0] = array->Stride; break; case GL_ATTRIB_ARRAY_TYPE_NV: - params[0] = ctx->Array.ArrayObj->VertexAttrib[index].Type; + params[0] = array->Type; break; case GL_CURRENT_ATTRIB_NV: if (index == 0) { @@ -395,6 +398,7 @@ _mesa_GetVertexAttribdvNV(GLuint index, GLenum pname, GLdouble *params) void GLAPIENTRY _mesa_GetVertexAttribfvNV(GLuint index, GLenum pname, GLfloat *params) { + const struct gl_client_array *array; GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); @@ -403,15 +407,17 @@ _mesa_GetVertexAttribfvNV(GLuint index, GLenum pname, GLfloat *params) return; } + array = &ctx->Array.ArrayObj->VertexAttrib[index]; + switch (pname) { case GL_ATTRIB_ARRAY_SIZE_NV: - params[0] = (GLfloat) ctx->Array.ArrayObj->VertexAttrib[index].Size; + params[0] = (GLfloat) array->Size; break; case GL_ATTRIB_ARRAY_STRIDE_NV: - params[0] = (GLfloat) ctx->Array.ArrayObj->VertexAttrib[index].Stride; + params[0] = (GLfloat) array->Stride; break; case GL_ATTRIB_ARRAY_TYPE_NV: - params[0] = (GLfloat) ctx->Array.ArrayObj->VertexAttrib[index].Type; + params[0] = (GLfloat) array->Type; break; case GL_CURRENT_ATTRIB_NV: if (index == 0) { @@ -436,6 +442,7 @@ _mesa_GetVertexAttribfvNV(GLuint index, GLenum pname, GLfloat *params) void GLAPIENTRY _mesa_GetVertexAttribivNV(GLuint index, GLenum pname, GLint *params) { + const struct gl_client_array *array; GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); @@ -444,15 +451,17 @@ _mesa_GetVertexAttribivNV(GLuint index, GLenum pname, GLint *params) return; } + array = &ctx->Array.ArrayObj->VertexAttrib[index]; + switch (pname) { case GL_ATTRIB_ARRAY_SIZE_NV: - params[0] = ctx->Array.ArrayObj->VertexAttrib[index].Size; + params[0] = array->Size; break; case GL_ATTRIB_ARRAY_STRIDE_NV: - params[0] = ctx->Array.ArrayObj->VertexAttrib[index].Stride; + params[0] = array->Stride; break; case GL_ATTRIB_ARRAY_TYPE_NV: - params[0] = ctx->Array.ArrayObj->VertexAttrib[index].Type; + params[0] = array->Type; break; case GL_CURRENT_ATTRIB_NV: if (index == 0) { @@ -467,7 +476,7 @@ _mesa_GetVertexAttribivNV(GLuint index, GLenum pname, GLint *params) params[3] = (GLint) ctx->Current.Attrib[index][3]; break; case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB: - params[0] = ctx->Array.ArrayObj->VertexAttrib[index].BufferObj->Name; + params[0] = array->BufferObj->Name; break; default: _mesa_error(ctx, GL_INVALID_ENUM, "glGetVertexAttribdvNV"); diff --git a/src/mesa/shader/prog_execute.c b/src/mesa/shader/prog_execute.c index 68a59350a1..f4beb9a78b 100644 --- a/src/mesa/shader/prog_execute.c +++ b/src/mesa/shader/prog_execute.c @@ -225,6 +225,13 @@ fetch_vector4(const struct prog_src_register *source, result[2] = -result[2]; result[3] = -result[3]; } + +#ifdef NAN_CHECK + assert(!IS_INF_OR_NAN(result[0])); + assert(!IS_INF_OR_NAN(result[0])); + assert(!IS_INF_OR_NAN(result[0])); + assert(!IS_INF_OR_NAN(result[0])); +#endif } @@ -479,6 +486,13 @@ store_vector4(const struct prog_instruction *inst, } } +#ifdef NAN_CHECK + assert(!IS_INF_OR_NAN(value[0])); + assert(!IS_INF_OR_NAN(value[0])); + assert(!IS_INF_OR_NAN(value[0])); + assert(!IS_INF_OR_NAN(value[0])); +#endif + if (writeMask & WRITEMASK_X) dst[0] = value[0]; if (writeMask & WRITEMASK_Y) @@ -832,10 +846,14 @@ _mesa_execute_program(GLcontext * ctx, break; case OPCODE_EX2: /* Exponential base 2 */ { - GLfloat a[4], result[4]; + GLfloat a[4], result[4], val; fetch_vector1(&inst->SrcReg[0], machine, a); - result[0] = result[1] = result[2] = result[3] = - (GLfloat) _mesa_pow(2.0, a[0]); + val = (GLfloat) _mesa_pow(2.0, a[0]); + /* + if (IS_INF_OR_NAN(val)) + val = 1.0e10; + */ + result[0] = result[1] = result[2] = result[3] = val; store_vector4(inst, machine, result); } break; @@ -911,12 +929,17 @@ _mesa_execute_program(GLcontext * ctx, break; case OPCODE_LG2: /* log base 2 */ { - GLfloat a[4], result[4]; + GLfloat a[4], result[4], val; fetch_vector1(&inst->SrcReg[0], machine, a); /* The fast LOG2 macro doesn't meet the precision requirements. */ - result[0] = result[1] = result[2] = result[3] = - (log(a[0]) * 1.442695F); + if (a[0] == 0.0F) { + val = 0.0F; + } + else { + val = log(a[0]) * 1.442695F; + } + result[0] = result[1] = result[2] = result[3] = val; store_vector4(inst, machine, result); } break; diff --git a/src/mesa/shader/prog_instruction.c b/src/mesa/shader/prog_instruction.c index ae3a003fee..44c961927a 100644 --- a/src/mesa/shader/prog_instruction.c +++ b/src/mesa/shader/prog_instruction.c @@ -343,7 +343,10 @@ _mesa_opcode_string(gl_inst_opcode opcode) { if (opcode < MAX_OPCODE) return InstInfo[opcode].Name; - else - return "OP?"; + else { + static char s[20]; + _mesa_snprintf(s, sizeof(s), "OP%u", opcode); + return s; + } } diff --git a/src/mesa/shader/prog_optimize.c b/src/mesa/shader/prog_optimize.c index a02f5efa41..be903106a0 100644 --- a/src/mesa/shader/prog_optimize.c +++ b/src/mesa/shader/prog_optimize.c @@ -812,98 +812,6 @@ _mesa_reallocate_registers(struct gl_program *prog) } - - - - - - -#if 0 -static void -_mesa_find_temporary_live_intervals(struct gl_program *prog, - GLint firstInst[MAX_PROGRAM_TEMPS], - GLint lastInst[MAX_PROGRAM_TEMPS]) -{ - GLuint i; - - for (i = 0; i < MAX_PROGRAM_TEMPS; i++) { - firstInst[i] = lastInst[i] = -1; - } - - struct loop_info loopStack[MAX_LOOP_NESTING]; - GLuint loopStackDepth = 0; - GLint intBegin[MAX_PROGRAM_TEMPS], intEnd[MAX_PROGRAM_TEMPS]; - GLuint i; - - /* - * Note: we'll return GL_FALSE below if we find relative indexing - * into the TEMP register file. We can't handle that yet. - * We also give up on subroutines for now. - */ - - if (dbg) { - _mesa_printf("Optimize: Begin find intervals\n"); - } - - for (i = 0; i < MAX_PROGRAM_TEMPS; i++){ - intBegin[i] = intEnd[i] = -1; - } - - /* Scan instructions looking for temporary registers */ - for (i = 0; i < prog->NumInstructions; i++) { - const struct prog_instruction *inst = prog->Instructions + i; - if (inst->Opcode == OPCODE_BGNLOOP) { - loopStack[loopStackDepth].Start = i; - loopStack[loopStackDepth].End = inst->BranchTarget; - loopStackDepth++; - } - else if (inst->Opcode == OPCODE_ENDLOOP) { - loopStackDepth--; - } - else if (inst->Opcode == OPCODE_CAL) { - return GL_FALSE; - } - else { - const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode); - GLuint j; - for (j = 0; j < numSrc; j++) { - if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) { - const GLuint index = inst->SrcReg[j].Index; - if (inst->SrcReg[j].RelAddr) - return GL_FALSE; - update_interval(intBegin, intEnd, index, i); - if (loopStackDepth > 0) { - /* extend temp register's interval to end of loop */ - GLuint loopEnd = loopStack[loopStackDepth - 1].End; - update_interval(intBegin, intEnd, index, loopEnd); - } - } - } - if (inst->DstReg.File == PROGRAM_TEMPORARY) { - const GLuint index = inst->DstReg.Index; - if (inst->DstReg.RelAddr) - return GL_FALSE; - update_interval(intBegin, intEnd, index, i); - if (loopStackDepth > 0) { - /* extend temp register's interval to end of loop */ - GLuint loopEnd = loopStack[loopStackDepth - 1].End; - update_interval(intBegin, intEnd, index, loopEnd); - } - } - } - } - - - - -#endif - - - - - - - /** * Apply optimizations to the given program to eliminate unnecessary * instructions, temp regs, etc. diff --git a/src/mesa/shader/prog_print.c b/src/mesa/shader/prog_print.c index 9967f2978d..de7fef1f86 100644 --- a/src/mesa/shader/prog_print.c +++ b/src/mesa/shader/prog_print.c @@ -75,7 +75,11 @@ file_string(gl_register_file f, gl_prog_print_mode mode) case PROGRAM_UNDEFINED: return "UNDEFINED"; default: - return "Unknown program file!"; + { + static char s[20]; + _mesa_snprintf(s, sizeof(s), "FILE%u", f); + return s; + } } } @@ -736,7 +740,10 @@ _mesa_fprint_instruction_opt(FILE *f, mode, prog); } else { - _mesa_fprintf(f, "Other opcode %d\n", inst->Opcode); + fprint_alu_instruction(f, inst, + _mesa_opcode_string(inst->Opcode), + 3/*_mesa_num_inst_src_regs(inst->Opcode)*/, + mode, prog); } break; } @@ -941,6 +948,10 @@ _mesa_write_shader_to_file(const struct gl_shader *shader) fprintf(f, "/*\n"); _mesa_fprint_program_opt(f, shader->Program, PROG_PRINT_DEBUG, GL_TRUE); fprintf(f, "*/\n"); + fprintf(f, "/* Parameters / constants */\n"); + fprintf(f, "/*\n"); + _mesa_fprint_parameter_list(f, shader->Program->Parameters); + fprintf(f, "*/\n"); } fclose(f); diff --git a/src/mesa/shader/program.c b/src/mesa/shader/program.c index d270bf9e1c..4623ff60de 100644 --- a/src/mesa/shader/program.c +++ b/src/mesa/shader/program.c @@ -820,3 +820,63 @@ _mesa_find_free_register(const struct gl_program *prog, GLuint regFile) return -1; } + + + +/** + * "Post-process" a GPU program. This is intended to be used for debugging. + * Example actions include no-op'ing instructions or changing instruction + * behaviour. + */ +void +_mesa_postprocess_program(GLcontext *ctx, struct gl_program *prog) +{ + static const GLfloat white[4] = { 0.5, 0.5, 0.5, 0.5 }; + GLuint i; + GLuint whiteSwizzle; + GLint whiteIndex = _mesa_add_unnamed_constant(prog->Parameters, + white, 4, &whiteSwizzle); + + (void) whiteIndex; + + for (i = 0; i < prog->NumInstructions; i++) { + struct prog_instruction *inst = prog->Instructions + i; + const GLuint n = _mesa_num_inst_src_regs(inst->Opcode); + + (void) n; + + if (_mesa_is_tex_instruction(inst->Opcode)) { +#if 0 + /* replace TEX/TXP/TXB with MOV */ + inst->Opcode = OPCODE_MOV; + inst->DstReg.WriteMask = WRITEMASK_XYZW; + inst->SrcReg[0].Swizzle = SWIZZLE_XYZW; + inst->SrcReg[0].Negate = NEGATE_NONE; +#endif + +#if 0 + /* disable shadow texture mode */ + inst->TexShadow = 0; +#endif + } + + if (inst->Opcode == OPCODE_TXP) { +#if 0 + inst->Opcode = OPCODE_MOV; + inst->DstReg.WriteMask = WRITEMASK_XYZW; + inst->SrcReg[0].File = PROGRAM_CONSTANT; + inst->SrcReg[0].Index = whiteIndex; + inst->SrcReg[0].Swizzle = SWIZZLE_XYZW; + inst->SrcReg[0].Negate = NEGATE_NONE; +#endif +#if 0 + inst->TexShadow = 0; +#endif +#if 0 + inst->Opcode = OPCODE_TEX; + inst->TexShadow = 0; +#endif + } + + } +} diff --git a/src/mesa/shader/program.h b/src/mesa/shader/program.h index 48176162c3..56a4191f57 100644 --- a/src/mesa/shader/program.h +++ b/src/mesa/shader/program.h @@ -122,6 +122,8 @@ _mesa_combine_programs(GLcontext *ctx, extern GLint _mesa_find_free_register(const struct gl_program *prog, GLuint regFile); +extern void +_mesa_postprocess_program(GLcontext *ctx, struct gl_program *prog); #endif /* PROGRAM_H */ diff --git a/src/mesa/shader/programopt.c b/src/mesa/shader/programopt.c index ecd98dc85c..ac5fe0f691 100644 --- a/src/mesa/shader/programopt.c +++ b/src/mesa/shader/programopt.c @@ -45,8 +45,8 @@ * into a vertex program. * May be used to implement the position_invariant option. */ -void -_mesa_insert_mvp_code(GLcontext *ctx, struct gl_vertex_program *vprog) +static void +_mesa_insert_mvp_dp4_code(GLcontext *ctx, struct gl_vertex_program *vprog) { struct prog_instruction *newInst; const GLuint origLen = vprog->Base.NumInstructions; @@ -113,6 +113,121 @@ _mesa_insert_mvp_code(GLcontext *ctx, struct gl_vertex_program *vprog) } +static void +_mesa_insert_mvp_mad_code(GLcontext *ctx, struct gl_vertex_program *vprog) +{ + struct prog_instruction *newInst; + const GLuint origLen = vprog->Base.NumInstructions; + const GLuint newLen = origLen + 4; + GLuint hposTemp; + GLuint i; + + /* + * Setup state references for the modelview/projection matrix. + * XXX we should check if these state vars are already declared. + */ + static const gl_state_index mvpState[4][STATE_LENGTH] = { + { STATE_MVP_MATRIX, 0, 0, 0, STATE_MATRIX_TRANSPOSE }, + { STATE_MVP_MATRIX, 0, 1, 1, STATE_MATRIX_TRANSPOSE }, + { STATE_MVP_MATRIX, 0, 2, 2, STATE_MATRIX_TRANSPOSE }, + { STATE_MVP_MATRIX, 0, 3, 3, STATE_MATRIX_TRANSPOSE }, + }; + GLint mvpRef[4]; + + for (i = 0; i < 4; i++) { + mvpRef[i] = _mesa_add_state_reference(vprog->Base.Parameters, + mvpState[i]); + } + + /* Alloc storage for new instructions */ + newInst = _mesa_alloc_instructions(newLen); + if (!newInst) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, + "glProgramString(inserting position_invariant code)"); + return; + } + + /* TEMP hposTemp; */ + hposTemp = vprog->Base.NumTemporaries++; + + /* + * Generated instructions: + * emit_op2(p, OPCODE_MUL, tmp, 0, swizzle1(src,X), mat[0]); + * emit_op3(p, OPCODE_MAD, tmp, 0, swizzle1(src,Y), mat[1], tmp); + * emit_op3(p, OPCODE_MAD, tmp, 0, swizzle1(src,Z), mat[2], tmp); + * emit_op3(p, OPCODE_MAD, dest, 0, swizzle1(src,W), mat[3], tmp); + */ + _mesa_init_instructions(newInst, 4); + + newInst[0].Opcode = OPCODE_MUL; + newInst[0].DstReg.File = PROGRAM_TEMPORARY; + newInst[0].DstReg.Index = hposTemp; + newInst[0].DstReg.WriteMask = WRITEMASK_XYZW; + newInst[0].SrcReg[0].File = PROGRAM_INPUT; + newInst[0].SrcReg[0].Index = VERT_ATTRIB_POS; + newInst[0].SrcReg[0].Swizzle = SWIZZLE_XXXX; + newInst[0].SrcReg[1].File = PROGRAM_STATE_VAR; + newInst[0].SrcReg[1].Index = mvpRef[0]; + newInst[0].SrcReg[1].Swizzle = SWIZZLE_NOOP; + + for (i = 1; i <= 2; i++) { + newInst[i].Opcode = OPCODE_MAD; + newInst[i].DstReg.File = PROGRAM_TEMPORARY; + newInst[i].DstReg.Index = hposTemp; + newInst[i].DstReg.WriteMask = WRITEMASK_XYZW; + newInst[i].SrcReg[0].File = PROGRAM_INPUT; + newInst[i].SrcReg[0].Index = VERT_ATTRIB_POS; + newInst[i].SrcReg[0].Swizzle = MAKE_SWIZZLE4(i,i,i,i); + newInst[i].SrcReg[1].File = PROGRAM_STATE_VAR; + newInst[i].SrcReg[1].Index = mvpRef[i]; + newInst[i].SrcReg[1].Swizzle = SWIZZLE_NOOP; + newInst[i].SrcReg[2].File = PROGRAM_TEMPORARY; + newInst[i].SrcReg[2].Index = hposTemp; + newInst[1].SrcReg[2].Swizzle = SWIZZLE_NOOP; + } + + newInst[3].Opcode = OPCODE_MAD; + newInst[3].DstReg.File = PROGRAM_OUTPUT; + newInst[3].DstReg.Index = VERT_RESULT_HPOS; + newInst[3].DstReg.WriteMask = WRITEMASK_XYZW; + newInst[3].SrcReg[0].File = PROGRAM_INPUT; + newInst[3].SrcReg[0].Index = VERT_ATTRIB_POS; + newInst[3].SrcReg[0].Swizzle = SWIZZLE_WWWW; + newInst[3].SrcReg[1].File = PROGRAM_STATE_VAR; + newInst[3].SrcReg[1].Index = mvpRef[3]; + newInst[3].SrcReg[1].Swizzle = SWIZZLE_NOOP; + newInst[3].SrcReg[2].File = PROGRAM_TEMPORARY; + newInst[3].SrcReg[2].Index = hposTemp; + newInst[3].SrcReg[2].Swizzle = SWIZZLE_NOOP; + + + /* Append original instructions after new instructions */ + _mesa_copy_instructions (newInst + 4, vprog->Base.Instructions, origLen); + + /* free old instructions */ + _mesa_free_instructions(vprog->Base.Instructions, origLen); + + /* install new instructions */ + vprog->Base.Instructions = newInst; + vprog->Base.NumInstructions = newLen; + vprog->Base.InputsRead |= VERT_BIT_POS; + vprog->Base.OutputsWritten |= (1 << VERT_RESULT_HPOS); +} + + +void +_mesa_insert_mvp_code(GLcontext *ctx, struct gl_vertex_program *vprog) +{ + if (ctx->mvp_with_dp4) + _mesa_insert_mvp_dp4_code( ctx, vprog ); + else + _mesa_insert_mvp_mad_code( ctx, vprog ); +} + + + + + /** * Append extra instructions onto the given fragment program to implement @@ -281,6 +396,7 @@ _mesa_append_fog_code(GLcontext *ctx, struct gl_fragment_program *fprog) fprog->Base.Instructions = newInst; fprog->Base.NumInstructions = inst - newInst; fprog->Base.InputsRead |= FRAG_BIT_FOGC; + fprog->UsesFogFragCoord = GL_TRUE; /* XXX do this? fprog->FogOption = GL_NONE; */ } diff --git a/src/mesa/shader/shader_api.c b/src/mesa/shader/shader_api.c index 8f414a0889..648fbc186b 100644 --- a/src/mesa/shader/shader_api.c +++ b/src/mesa/shader/shader_api.c @@ -402,6 +402,7 @@ _mesa_init_shader_state(GLcontext * ctx) * are generated by the GLSL compiler. */ ctx->Shader.EmitHighLevelInstructions = GL_TRUE; + ctx->Shader.EmitContReturn = GL_TRUE; ctx->Shader.EmitCondCodes = GL_FALSE; ctx->Shader.EmitComments = GL_FALSE; ctx->Shader.Flags = get_shader_flags(); @@ -1509,6 +1510,10 @@ _mesa_use_program(GLcontext *ctx, GLuint program) shProg->Shaders[i]->Name, shProg->Shaders[i]->Type); } + if (shProg->VertexProgram) + printf(" vert prog %u\n", shProg->VertexProgram->Base.Id); + if (shProg->FragmentProgram) + printf(" frag prog %u\n", shProg->FragmentProgram->Base.Id); } } else { diff --git a/src/mesa/shader/slang/slang_builtin.c b/src/mesa/shader/slang/slang_builtin.c index 83e76b77db..154609c26e 100644 --- a/src/mesa/shader/slang/slang_builtin.c +++ b/src/mesa/shader/slang/slang_builtin.c @@ -85,7 +85,7 @@ lookup_statevar(const char *var, GLint index1, GLint index2, const char *field, { "gl_TextureMatrixTranspose", STATE_TEXTURE_MATRIX, 0 }, { "gl_TextureMatrixInverseTranspose", STATE_TEXTURE_MATRIX, STATE_MATRIX_INVERSE }, - { "gl_NormalMatrix", STATE_MODELVIEW_MATRIX, STATE_MATRIX_TRANSPOSE }, + { "gl_NormalMatrix", STATE_MODELVIEW_MATRIX, STATE_MATRIX_INVERSE }, { NULL, 0, 0 } }; diff --git a/src/mesa/shader/slang/slang_codegen.c b/src/mesa/shader/slang/slang_codegen.c index 6d693c9027..24e9952386 100644 --- a/src/mesa/shader/slang/slang_codegen.c +++ b/src/mesa/shader/slang/slang_codegen.c @@ -75,6 +75,11 @@ const GLuint MAX_FOR_LOOP_UNROLL_COMPLEXITY = 256; static slang_ir_node * _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper); +static void +slang_substitute(slang_assemble_ctx *A, slang_operation *oper, + GLuint substCount, slang_variable **substOld, + slang_operation **substNew, GLboolean isLHS); + /** * Retrieves type information about an operation. @@ -326,6 +331,17 @@ sampler_to_texture_index(const slang_type_specifier_type type) } +/** helper to build a SLANG_OPER_IDENTIFIER node */ +static void +slang_operation_identifier(slang_operation *oper, + slang_assemble_ctx *A, + const char *name) +{ + oper->type = SLANG_OPER_IDENTIFIER; + oper->a_id = slang_atom_pool_atom(A->atoms, name); +} + + #define SWIZZLE_ZWWW MAKE_SWIZZLE4(SWIZZLE_Z, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W) /** @@ -429,6 +445,56 @@ _slang_output_index(const char *name, GLenum target) } +/** + * Called when we begin code/IR generation for a new while/do/for loop. + */ +static void +push_loop(slang_assemble_ctx *A, slang_operation *loopOper, slang_ir_node *loopIR) +{ + A->LoopOperStack[A->LoopDepth] = loopOper; + A->LoopIRStack[A->LoopDepth] = loopIR; + A->LoopDepth++; +} + + +/** + * Called when we end code/IR generation for a new while/do/for loop. + */ +static void +pop_loop(slang_assemble_ctx *A) +{ + assert(A->LoopDepth > 0); + A->LoopDepth--; +} + + +/** + * Return pointer to slang_operation for the loop we're currently inside, + * or NULL if not in a loop. + */ +static const slang_operation * +current_loop_oper(const slang_assemble_ctx *A) +{ + if (A->LoopDepth > 0) + return A->LoopOperStack[A->LoopDepth - 1]; + else + return NULL; +} + + +/** + * Return pointer to slang_ir_node for the loop we're currently inside, + * or NULL if not in a loop. + */ +static slang_ir_node * +current_loop_ir(const slang_assemble_ctx *A) +{ + if (A->LoopDepth > 0) + return A->LoopIRStack[A->LoopDepth - 1]; + else + return NULL; +} + /**********************************************************************/ @@ -636,7 +702,7 @@ new_break(slang_ir_node *loopNode) assert(loopNode); assert(loopNode->Opcode == IR_LOOP); if (n) { - /* insert this node at head of linked list */ + /* insert this node at head of linked list of cont/break instructions */ n->List = loopNode->List; loopNode->List = n; } @@ -648,14 +714,15 @@ new_break(slang_ir_node *loopNode) * Make new IR_BREAK_IF_TRUE. */ static slang_ir_node * -new_break_if_true(slang_ir_node *loopNode, slang_ir_node *cond) +new_break_if_true(slang_assemble_ctx *A, slang_ir_node *cond) { + slang_ir_node *loopNode = current_loop_ir(A); slang_ir_node *n; assert(loopNode); assert(loopNode->Opcode == IR_LOOP); n = new_node1(IR_BREAK_IF_TRUE, cond); if (n) { - /* insert this node at head of linked list */ + /* insert this node at head of linked list of cont/break instructions */ n->List = loopNode->List; loopNode->List = n; } @@ -667,14 +734,16 @@ new_break_if_true(slang_ir_node *loopNode, slang_ir_node *cond) * Make new IR_CONT_IF_TRUE node. */ static slang_ir_node * -new_cont_if_true(slang_ir_node *loopNode, slang_ir_node *cond) +new_cont_if_true(slang_assemble_ctx *A, slang_ir_node *cond) { + slang_ir_node *loopNode = current_loop_ir(A); slang_ir_node *n; assert(loopNode); assert(loopNode->Opcode == IR_LOOP); n = new_node1(IR_CONT_IF_TRUE, cond); if (n) { - /* insert this node at head of linked list */ + n->Parent = loopNode; /* pointer to containing loop */ + /* insert this node at head of linked list of cont/break instructions */ n->List = loopNode->List; loopNode->List = n; } @@ -749,6 +818,7 @@ _slang_is_noop(const slang_operation *oper) /** * Recursively search tree for a node of the given type. */ +#if 0 static slang_operation * _slang_find_node_type(slang_operation *oper, slang_operation_type type) { @@ -762,13 +832,14 @@ _slang_find_node_type(slang_operation *oper, slang_operation_type type) } return NULL; } +#endif /** * Count the number of operations of the given time rooted at 'oper'. */ static GLuint -_slang_count_node_type(slang_operation *oper, slang_operation_type type) +_slang_count_node_type(const slang_operation *oper, slang_operation_type type) { GLuint i, count = 0; if (oper->type == type) { @@ -822,6 +893,37 @@ _slang_is_tail_return(const slang_operation *oper) } +/** + * Generate a variable declaration opeartion. + * I.e.: generate AST code for "bool flag = false;" + */ +static void +slang_generate_declaration(slang_assemble_ctx *A, + slang_variable_scope *scope, + slang_operation *decl, + slang_type_specifier_type type, + const char *name, + GLint initValue) +{ + slang_variable *var; + + assert(type == SLANG_SPEC_BOOL || + type == SLANG_SPEC_INT); + + decl->type = SLANG_OPER_VARIABLE_DECL; + + var = slang_variable_scope_grow(scope); + + slang_fully_specified_type_construct(&var->type); + + var->type.specifier.type = type; + var->a_name = slang_atom_pool_atom(A->atoms, name); + decl->a_id = var->a_name; + var->initializer = slang_operation_new(1); + slang_operation_literal_bool(var->initializer, initValue); +} + + static void slang_resolve_variable(slang_operation *oper) { @@ -832,6 +934,159 @@ slang_resolve_variable(slang_operation *oper) /** + * Rewrite AST code for "return expression;". + * + * We return values from functions by assinging the returned value to + * the hidden __retVal variable which is an extra 'out' parameter we add + * to the function signature. + * This code basically converts "return expr;" into "__retVal = expr; return;" + * + * \return the new AST code. + */ +static slang_operation * +gen_return_with_expression(slang_assemble_ctx *A, slang_operation *oper) +{ + slang_operation *blockOper, *assignOper; + + assert(oper->type == SLANG_OPER_RETURN); + + if (A->CurFunction->header.type.specifier.type == SLANG_SPEC_VOID) { + slang_info_log_error(A->log, "illegal return expression"); + return NULL; + } + + blockOper = slang_operation_new(1); + blockOper->type = SLANG_OPER_BLOCK_NO_NEW_SCOPE; + blockOper->locals->outer_scope = oper->locals->outer_scope; + slang_operation_add_children(blockOper, 2); + + if (A->UseReturnFlag) { + /* Emit: + * { + * if (__notRetFlag) + * __retVal = expr; + * __notRetFlag = 0; + * } + */ + { + slang_operation *ifOper = slang_oper_child(blockOper, 0); + ifOper->type = SLANG_OPER_IF; + slang_operation_add_children(ifOper, 3); + { + slang_operation *cond = slang_oper_child(ifOper, 0); + cond->type = SLANG_OPER_IDENTIFIER; + cond->a_id = slang_atom_pool_atom(A->atoms, "__notRetFlag"); + } + { + slang_operation *elseOper = slang_oper_child(ifOper, 2); + elseOper->type = SLANG_OPER_VOID; + } + assignOper = slang_oper_child(ifOper, 1); + } + { + slang_operation *setOper = slang_oper_child(blockOper, 1); + setOper->type = SLANG_OPER_ASSIGN; + slang_operation_add_children(setOper, 2); + { + slang_operation *lhs = slang_oper_child(setOper, 0); + lhs->type = SLANG_OPER_IDENTIFIER; + lhs->a_id = slang_atom_pool_atom(A->atoms, "__notRetFlag"); + } + { + slang_operation *rhs = slang_oper_child(setOper, 1); + slang_operation_literal_bool(rhs, GL_FALSE); + } + } + } + else { + /* Emit: + * { + * __retVal = expr; + * return_inlined; + * } + */ + assignOper = slang_oper_child(blockOper, 0); + { + slang_operation *returnOper = slang_oper_child(blockOper, 1); + returnOper->type = SLANG_OPER_RETURN_INLINED; + assert(returnOper->num_children == 0); + } + } + + /* __retVal = expression; */ + assignOper->type = SLANG_OPER_ASSIGN; + slang_operation_add_children(assignOper, 2); + { + slang_operation *lhs = slang_oper_child(assignOper, 0); + lhs->type = SLANG_OPER_IDENTIFIER; + lhs->a_id = slang_atom_pool_atom(A->atoms, "__retVal"); + } + { + slang_operation *rhs = slang_oper_child(assignOper, 1); + slang_operation_copy(rhs, &oper->children[0]); + } + + ///blockOper->locals->outer_scope = oper->locals->outer_scope; + + /*slang_print_tree(blockOper, 0);*/ + + return blockOper; +} + + +/** + * Rewrite AST code for "return;" (no expression). + */ +static slang_operation * +gen_return_without_expression(slang_assemble_ctx *A, slang_operation *oper) +{ + slang_operation *newRet; + + assert(oper->type == SLANG_OPER_RETURN); + + if (A->CurFunction->header.type.specifier.type != SLANG_SPEC_VOID) { + slang_info_log_error(A->log, "return statement requires an expression"); + return NULL; + } + + if (A->UseReturnFlag) { + /* Emit: + * __notRetFlag = 0; + */ + { + newRet = slang_operation_new(1); + newRet->locals->outer_scope = oper->locals->outer_scope; + newRet->type = SLANG_OPER_ASSIGN; + slang_operation_add_children(newRet, 2); + { + slang_operation *lhs = slang_oper_child(newRet, 0); + lhs->type = SLANG_OPER_IDENTIFIER; + lhs->a_id = slang_atom_pool_atom(A->atoms, "__notRetFlag"); + } + { + slang_operation *rhs = slang_oper_child(newRet, 1); + slang_operation_literal_bool(rhs, GL_FALSE); + } + } + } + else { + /* Emit: + * return_inlined; + */ + newRet = slang_operation_new(1); + newRet->locals->outer_scope = oper->locals->outer_scope; + newRet->type = SLANG_OPER_RETURN_INLINED; + } + + /*slang_print_tree(newRet, 0);*/ + + return newRet; +} + + + + +/** * Replace particular variables (SLANG_OPER_IDENTIFIER) with new expressions. */ static void @@ -866,7 +1121,8 @@ slang_substitute(slang_assemble_ctx *A, slang_operation *oper, GLuint i; v = _slang_variable_locate(oper->locals, id, GL_TRUE); if (!v) { - _mesa_problem(NULL, "var %s not found!\n", (char *) oper->a_id); + if (_mesa_strcmp((char *) oper->a_id, "__notRetFlag")) + _mesa_problem(NULL, "var %s not found!\n", (char *) oper->a_id); return; } @@ -896,62 +1152,24 @@ slang_substitute(slang_assemble_ctx *A, slang_operation *oper, break; case SLANG_OPER_RETURN: - /* do return replacement here too */ - assert(oper->num_children == 0 || oper->num_children == 1); - if (oper->num_children == 1 && !_slang_is_noop(&oper->children[0])) { - /* replace: - * return expr; - * with: - * __retVal = expr; - * return; - * then do substitutions on the assignment. - */ - slang_operation *blockOper, *assignOper, *returnOper; + { + slang_operation *newReturn; + /* generate new 'return' code' */ + if (slang_oper_child(oper, 0)->type == SLANG_OPER_VOID) + newReturn = gen_return_without_expression(A, oper); + else + newReturn = gen_return_with_expression(A, oper); - /* check if function actually has a return type */ - assert(A->CurFunction); - if (A->CurFunction->header.type.specifier.type == SLANG_SPEC_VOID) { - slang_info_log_error(A->log, "illegal return expression"); + if (!newReturn) return; - } - - blockOper = slang_operation_new(1); - blockOper->type = SLANG_OPER_BLOCK_NO_NEW_SCOPE; - blockOper->num_children = 2; - blockOper->locals->outer_scope = oper->locals->outer_scope; - blockOper->children = slang_operation_new(2); - assignOper = blockOper->children + 0; - returnOper = blockOper->children + 1; - - assignOper->type = SLANG_OPER_ASSIGN; - assignOper->num_children = 2; - assignOper->locals->outer_scope = blockOper->locals; - assignOper->children = slang_operation_new(2); - assignOper->children[0].type = SLANG_OPER_IDENTIFIER; - assignOper->children[0].a_id = slang_atom_pool_atom(A->atoms, "__retVal"); - assignOper->children[0].locals->outer_scope = assignOper->locals; - - slang_operation_copy(&assignOper->children[1], - &oper->children[0]); - - returnOper->type = SLANG_OPER_RETURN; /* return w/ no value */ - assert(returnOper->num_children == 0); - /* do substitutions on the "__retVal = expr" sub-tree */ - slang_substitute(A, assignOper, + /* do substitutions on the new 'return' code */ + slang_substitute(A, newReturn, substCount, substOld, substNew, GL_FALSE); - /* install new code */ - slang_operation_copy(oper, blockOper); - slang_operation_destruct(blockOper); - } - else { - /* check if return value was expected */ - assert(A->CurFunction); - if (A->CurFunction->header.type.specifier.type != SLANG_SPEC_VOID) { - slang_info_log_error(A->log, "return statement requires an expression"); - return; - } + /* install new 'return' code */ + slang_operation_copy(oper, newReturn); + slang_operation_destruct(newReturn); } break; @@ -1346,12 +1564,74 @@ slang_inline_function_call(slang_assemble_ctx * A, slang_function *fun, } +/** + * Insert declaration for "bool __notRetFlag" in given block operation. + * This is used when we can't emit "early" return statements in subroutines. + */ +static void +declare_return_flag(slang_assemble_ctx *A, slang_operation *oper) +{ + slang_operation *decl; + + assert(oper->type == SLANG_OPER_BLOCK_NEW_SCOPE || + oper->type == SLANG_OPER_SEQUENCE); + + decl = slang_operation_insert_child(oper, 1); + + slang_generate_declaration(A, oper->locals, decl, + SLANG_SPEC_BOOL, "__notRetFlag", GL_TRUE); + + /*slang_print_tree(oper, 0);*/ +} + + +/** + * Recursively replace instances of the old node type with the new type. + */ +static void +replace_node_type(slang_operation *oper, slang_operation_type oldType, + slang_operation_type newType) +{ + GLuint i; + + if (oper->type == oldType) + oper->type = newType; + + for (i = 0; i < slang_oper_num_children(oper); i++) { + replace_node_type(slang_oper_child(oper, i), oldType, newType); + } +} + + + +/** + * Test if the given function body has an "early return". That is, there's + * a 'return' statement that's not the very last instruction in the body. + */ +static GLboolean +has_early_return(const slang_operation *funcBody) +{ + GLuint retCount = _slang_count_node_type(funcBody, SLANG_OPER_RETURN); + if (retCount == 0) + return GL_FALSE; + else if (retCount == 1 && _slang_is_tail_return(funcBody)) + return GL_FALSE; + else + return GL_TRUE; +} + + +/** + * Emit IR code for a function call. This does one of two things: + * 1. Inline the function's code + * 2. Create an IR for the function's body and create a real call to it. + */ static slang_ir_node * _slang_gen_function_call(slang_assemble_ctx *A, slang_function *fun, slang_operation *oper, slang_operation *dest) { slang_ir_node *n; - slang_operation *inlined; + slang_operation *instance; slang_label *prevFuncEndLabel; char name[200]; @@ -1360,9 +1640,14 @@ _slang_gen_function_call(slang_assemble_ctx *A, slang_function *fun, A->curFuncEndLabel = _slang_label_new(name); assert(A->curFuncEndLabel); + /* + * 'instance' is basically a copy of the function's body with various + * transformations. + */ + if (slang_is_asm_function(fun) && !dest) { /* assemble assembly function - tree style */ - inlined = slang_inline_asm_function(A, fun, oper); + instance = slang_inline_asm_function(A, fun, oper); } else { /* non-assembly function */ @@ -1371,65 +1656,82 @@ _slang_gen_function_call(slang_assemble_ctx *A, slang_function *fun, * 1. insert the inline code * 2. Generate a call to the "inline" code as a subroutine */ + const GLboolean earlyReturn = has_early_return(fun->body); + if (earlyReturn && !A->EmitContReturn) { + A->UseReturnFlag = GL_TRUE; + } - slang_operation *ret = NULL; - - inlined = slang_inline_function_call(A, fun, oper, dest); - if (!inlined) + instance = slang_inline_function_call(A, fun, oper, dest); + if (!instance) return NULL; - ret = _slang_find_node_type(inlined, SLANG_OPER_RETURN); - if (ret) { - /* check if this is a "tail" return */ - if (_slang_count_node_type(inlined, SLANG_OPER_RETURN) == 1 && - _slang_is_tail_return(inlined)) { - /* The only RETURN is the last stmt in the function, no-op it - * and inline the function body. + if (earlyReturn) { + /* The function we're calling has one or more 'return' statements + * that prevent us from inlining the function's code. + * + * In this case, change the function's body type from + * SLANG_OPER_BLOCK_NEW_SCOPE to SLANG_OPER_NON_INLINED_CALL. + * During code emit this will result in a true subroutine call. + * + * Also, convert SLANG_OPER_RETURN_INLINED nodes to SLANG_OPER_RETURN. + */ + slang_operation *callOper; + + assert(instance->type == SLANG_OPER_BLOCK_NEW_SCOPE || + instance->type == SLANG_OPER_SEQUENCE); + + if (_slang_function_has_return_value(fun) && !dest) { + assert(instance->children[0].type == SLANG_OPER_VARIABLE_DECL); + assert(instance->children[2].type == SLANG_OPER_IDENTIFIER); + callOper = &instance->children[1]; + } + else { + callOper = instance; + } + + if (A->UseReturnFlag) { + /* Early returns not supported. Create a _returnFlag variable + * that's set upon 'return' and tested elsewhere to no-op any + * remaining instructions in the subroutine. */ - ret->type = SLANG_OPER_NONE; + assert(callOper->type == SLANG_OPER_BLOCK_NEW_SCOPE || + callOper->type == SLANG_OPER_SEQUENCE); + declare_return_flag(A, callOper); } else { - slang_operation *callOper; - /* The function we're calling has one or more 'return' statements. - * So, we can't truly inline this function because we need to - * implement 'return' with RET (and CAL). - * Nevertheless, we performed "inlining" to make a new instance - * of the function body to deal with static register allocation. - * - * XXX check if there's one 'return' and if it's the very last - * statement in the function - we can optimize that case. + /* We can emit real 'return' statements. If we generated any + * 'inline return' statements during function instantiation, + * change them back to regular 'return' statements. */ - assert(inlined->type == SLANG_OPER_BLOCK_NEW_SCOPE || - inlined->type == SLANG_OPER_SEQUENCE); - - if (_slang_function_has_return_value(fun) && !dest) { - assert(inlined->children[0].type == SLANG_OPER_VARIABLE_DECL); - assert(inlined->children[2].type == SLANG_OPER_IDENTIFIER); - callOper = &inlined->children[1]; - } - else { - callOper = inlined; - } - callOper->type = SLANG_OPER_NON_INLINED_CALL; - callOper->fun = fun; - callOper->label = _slang_label_new_unique((char*) fun->header.a_name); + replace_node_type(instance, SLANG_OPER_RETURN_INLINED, + SLANG_OPER_RETURN); } + + callOper->type = SLANG_OPER_NON_INLINED_CALL; + callOper->fun = fun; + callOper->label = _slang_label_new_unique((char*) fun->header.a_name); + } + else { + /* If there are any 'return' statements remaining, they're at the + * very end of the function and can effectively become no-ops. + */ + replace_node_type(instance, SLANG_OPER_RETURN_INLINED, + SLANG_OPER_VOID); } } - if (!inlined) + if (!instance) return NULL; - /* Replace the function call with the inlined block (or new CALL stmt) */ + /* Replace the function call with the instance block (or new CALL stmt) */ slang_operation_destruct(oper); - *oper = *inlined; - _slang_free(inlined); + *oper = *instance; + _slang_free(instance); #if 0 - assert(inlined->locals); - printf("*** Inlined code for call to %s:\n", - (char*) fun->header.a_name); + assert(instance->locals); + printf("*** Inlined code for call to %s:\n", (char*) fun->header.a_name); slang_print_tree(oper, 10); printf("\n"); #endif @@ -1445,6 +1747,8 @@ _slang_gen_function_call(slang_assemble_ctx *A, slang_function *fun, n->Comment = _slang_strdup(s); } + A->UseReturnFlag = GL_FALSE; + return n; } @@ -2337,19 +2641,252 @@ _slang_is_boolean(slang_assemble_ctx *A, slang_operation *oper) /** + * Check if a loop contains a 'continue' statement. + * Stop looking if we find a nested loop. + */ +static GLboolean +_slang_loop_contains_continue(const slang_operation *oper) +{ + switch (oper->type) { + case SLANG_OPER_CONTINUE: + return GL_TRUE; + case SLANG_OPER_FOR: + case SLANG_OPER_DO: + case SLANG_OPER_WHILE: + /* stop upon finding a nested loop */ + return GL_FALSE; + default: + /* recurse */ + { + GLuint i; + for (i = 0; i < oper->num_children; i++) { + const slang_operation *child = slang_oper_child_const(oper, i); + if (_slang_loop_contains_continue(child)) + return GL_TRUE; + } + } + return GL_FALSE; + } +} + + +/** + * Check if a loop contains a 'continue' or 'break' statement. + * Stop looking if we find a nested loop. + */ +static GLboolean +_slang_loop_contains_continue_or_break(const slang_operation *oper) +{ + switch (oper->type) { + case SLANG_OPER_CONTINUE: + case SLANG_OPER_BREAK: + return GL_TRUE; + case SLANG_OPER_FOR: + case SLANG_OPER_DO: + case SLANG_OPER_WHILE: + /* stop upon finding a nested loop */ + return GL_FALSE; + default: + /* recurse */ + { + GLuint i; + for (i = 0; i < oper->num_children; i++) { + const slang_operation *child = slang_oper_child_const(oper, i); + if (_slang_loop_contains_continue_or_break(child)) + return GL_TRUE; + } + } + return GL_FALSE; + } +} + + +/** + * Replace 'break' and 'continue' statements inside a do and while loops. + * This is a recursive helper function used by + * _slang_gen_do/while_without_continue(). + */ +static void +replace_break_and_cont(slang_assemble_ctx *A, slang_operation *oper) +{ + switch (oper->type) { + case SLANG_OPER_BREAK: + /* replace 'break' with "_notBreakFlag = false; break" */ + { + slang_operation *block = oper; + block->type = SLANG_OPER_BLOCK_NEW_SCOPE; + slang_operation_add_children(block, 2); + { + slang_operation *assign = slang_oper_child(block, 0); + assign->type = SLANG_OPER_ASSIGN; + slang_operation_add_children(assign, 2); + { + slang_operation *lhs = slang_oper_child(assign, 0); + slang_operation_identifier(lhs, A, "_notBreakFlag"); + } + { + slang_operation *rhs = slang_oper_child(assign, 1); + slang_operation_literal_bool(rhs, GL_FALSE); + } + } + { + slang_operation *brk = slang_oper_child(block, 1); + brk->type = SLANG_OPER_BREAK; + assert(!brk->children); + } + } + break; + case SLANG_OPER_CONTINUE: + /* convert continue into a break */ + oper->type = SLANG_OPER_BREAK; + break; + case SLANG_OPER_FOR: + case SLANG_OPER_DO: + case SLANG_OPER_WHILE: + /* stop upon finding a nested loop */ + break; + default: + /* recurse */ + { + GLuint i; + for (i = 0; i < oper->num_children; i++) { + replace_break_and_cont(A, slang_oper_child(oper, i)); + } + } + } +} + + +/** + * Transform a while-loop so that continue statements are converted to breaks. + * Then do normal IR code generation. + * + * Before: + * + * while (LOOPCOND) { + * A; + * if (IFCOND) + * continue; + * B; + * break; + * C; + * } + * + * After: + * + * { + * bool _notBreakFlag = 1; + * while (_notBreakFlag && LOOPCOND) { + * do { + * A; + * if (IFCOND) { + * break; // was continue + * } + * B; + * _notBreakFlag = 0; // was + * break; // break + * C; + * } while (0) + * } + * } + */ +static slang_ir_node * +_slang_gen_while_without_continue(slang_assemble_ctx *A, slang_operation *oper) +{ + slang_operation *top; + slang_operation *innerBody; + + assert(oper->type == SLANG_OPER_WHILE); + + top = slang_operation_new(1); + top->type = SLANG_OPER_BLOCK_NEW_SCOPE; + top->locals->outer_scope = oper->locals->outer_scope; + slang_operation_add_children(top, 2); + + /* declare: bool _notBreakFlag = true */ + { + slang_operation *condDecl = slang_oper_child(top, 0); + slang_generate_declaration(A, top->locals, condDecl, + SLANG_SPEC_BOOL, "_notBreakFlag", GL_TRUE); + } + + /* build outer while-loop: while (_notBreakFlag && LOOPCOND) { ... } */ + { + slang_operation *outerWhile = slang_oper_child(top, 1); + outerWhile->type = SLANG_OPER_WHILE; + slang_operation_add_children(outerWhile, 2); + + /* _notBreakFlag && LOOPCOND */ + { + slang_operation *cond = slang_oper_child(outerWhile, 0); + cond->type = SLANG_OPER_LOGICALAND; + slang_operation_add_children(cond, 2); + { + slang_operation *notBreak = slang_oper_child(cond, 0); + slang_operation_identifier(notBreak, A, "_notBreakFlag"); + } + { + slang_operation *origCond = slang_oper_child(cond, 1); + slang_operation_copy(origCond, slang_oper_child(oper, 0)); + } + } + + /* inner loop */ + { + slang_operation *innerDo = slang_oper_child(outerWhile, 1); + innerDo->type = SLANG_OPER_DO; + slang_operation_add_children(innerDo, 2); + + /* copy original do-loop body into inner do-loop's body */ + innerBody = slang_oper_child(innerDo, 0); + slang_operation_copy(innerBody, slang_oper_child(oper, 1)); + innerBody->locals->outer_scope = innerDo->locals; + + /* inner do-loop's condition is constant/false */ + { + slang_operation *constFalse = slang_oper_child(innerDo, 1); + slang_operation_literal_bool(constFalse, GL_FALSE); + } + } + } + + /* Finally, in innerBody, + * replace "break" with "_notBreakFlag = 0; break" + * replace "continue" with "break" + */ + replace_break_and_cont(A, innerBody); + + /*slang_print_tree(top, 0);*/ + + return _slang_gen_operation(A, top); + + return NULL; +} + + +/** * Generate loop code using high-level IR_LOOP instruction */ static slang_ir_node * -_slang_gen_while(slang_assemble_ctx * A, const slang_operation *oper) +_slang_gen_while(slang_assemble_ctx * A, slang_operation *oper) { /* * LOOP: * BREAK if !expr (child[0]) * body code (child[1]) */ - slang_ir_node *prevLoop, *loop, *breakIf, *body; + slang_ir_node *loop, *breakIf, *body; GLboolean isConst, constTrue; + if (!A->EmitContReturn) { + /* We don't want to emit CONT instructions. If this while-loop has + * a continue, translate it away. + */ + if (_slang_loop_contains_continue(slang_oper_child(oper, 1))) { + return _slang_gen_while_without_continue(A, oper); + } + } + /* type-check expression */ if (!_slang_is_boolean(A, &oper->children[0])) { slang_info_log_error(A->log, "scalar/boolean expression expected for 'while'"); @@ -2364,11 +2901,11 @@ _slang_gen_while(slang_assemble_ctx * A, const slang_operation *oper) return new_node0(IR_NOP); } + /* Begin new loop */ loop = new_loop(NULL); - /* save old, push new loop */ - prevLoop = A->CurLoop; - A->CurLoop = loop; + /* save loop state */ + push_loop(A, oper, loop); if (isConst && constTrue) { /* while(nonzero constant), no conditional break */ @@ -2377,7 +2914,7 @@ _slang_gen_while(slang_assemble_ctx * A, const slang_operation *oper) else { slang_ir_node *cond = new_cond(new_not(_slang_gen_operation(A, &oper->children[0]))); - breakIf = new_break_if_true(A->CurLoop, cond); + breakIf = new_break_if_true(A, cond); } body = _slang_gen_operation(A, &oper->children[1]); loop->Children[0] = new_seq(breakIf, body); @@ -2386,23 +2923,128 @@ _slang_gen_while(slang_assemble_ctx * A, const slang_operation *oper) /* loop->List is head of linked list of break/continue nodes */ if (!loop->List && isConst && constTrue) { /* infinite loop detected */ - A->CurLoop = prevLoop; /* clean-up */ + pop_loop(A); slang_info_log_error(A->log, "Infinite loop detected!"); return NULL; } - /* pop loop, restore prev */ - A->CurLoop = prevLoop; + /* restore loop state */ + pop_loop(A); return loop; } /** + * Transform a do-while-loop so that continue statements are converted to breaks. + * Then do normal IR code generation. + * + * Before: + * + * do { + * A; + * if (IFCOND) + * continue; + * B; + * break; + * C; + * } while (LOOPCOND); + * + * After: + * + * { + * bool _notBreakFlag = 1; + * do { + * do { + * A; + * if (IFCOND) { + * break; // was continue + * } + * B; + * _notBreakFlag = 0; // was + * break; // break + * C; + * } while (0) + * } while (_notBreakFlag && LOOPCOND); + * } + */ +static slang_ir_node * +_slang_gen_do_without_continue(slang_assemble_ctx *A, slang_operation *oper) +{ + slang_operation *top; + slang_operation *innerBody; + + assert(oper->type == SLANG_OPER_DO); + + top = slang_operation_new(1); + top->type = SLANG_OPER_BLOCK_NEW_SCOPE; + top->locals->outer_scope = oper->locals->outer_scope; + slang_operation_add_children(top, 2); + + /* declare: bool _notBreakFlag = true */ + { + slang_operation *condDecl = slang_oper_child(top, 0); + slang_generate_declaration(A, top->locals, condDecl, + SLANG_SPEC_BOOL, "_notBreakFlag", GL_TRUE); + } + + /* build outer do-loop: do { ... } while (_notBreakFlag && LOOPCOND) */ + { + slang_operation *outerDo = slang_oper_child(top, 1); + outerDo->type = SLANG_OPER_DO; + slang_operation_add_children(outerDo, 2); + + /* inner do-loop */ + { + slang_operation *innerDo = slang_oper_child(outerDo, 0); + innerDo->type = SLANG_OPER_DO; + slang_operation_add_children(innerDo, 2); + + /* copy original do-loop body into inner do-loop's body */ + innerBody = slang_oper_child(innerDo, 0); + slang_operation_copy(innerBody, slang_oper_child(oper, 0)); + innerBody->locals->outer_scope = innerDo->locals; + + /* inner do-loop's condition is constant/false */ + { + slang_operation *constFalse = slang_oper_child(innerDo, 1); + slang_operation_literal_bool(constFalse, GL_FALSE); + } + } + + /* _notBreakFlag && LOOPCOND */ + { + slang_operation *cond = slang_oper_child(outerDo, 1); + cond->type = SLANG_OPER_LOGICALAND; + slang_operation_add_children(cond, 2); + { + slang_operation *notBreak = slang_oper_child(cond, 0); + slang_operation_identifier(notBreak, A, "_notBreakFlag"); + } + { + slang_operation *origCond = slang_oper_child(cond, 1); + slang_operation_copy(origCond, slang_oper_child(oper, 1)); + } + } + } + + /* Finally, in innerBody, + * replace "break" with "_notBreakFlag = 0; break" + * replace "continue" with "break" + */ + replace_break_and_cont(A, innerBody); + + /*slang_print_tree(top, 0);*/ + + return _slang_gen_operation(A, top); +} + + +/** * Generate IR tree for a do-while loop using high-level LOOP, IF instructions. */ static slang_ir_node * -_slang_gen_do(slang_assemble_ctx * A, const slang_operation *oper) +_slang_gen_do(slang_assemble_ctx * A, slang_operation *oper) { /* * LOOP: @@ -2410,9 +3052,18 @@ _slang_gen_do(slang_assemble_ctx * A, const slang_operation *oper) * tail code: * BREAK if !expr (child[1]) */ - slang_ir_node *prevLoop, *loop; + slang_ir_node *loop; GLboolean isConst, constTrue; + if (!A->EmitContReturn) { + /* We don't want to emit CONT instructions. If this do-loop has + * a continue, translate it away. + */ + if (_slang_loop_contains_continue(slang_oper_child(oper, 0))) { + return _slang_gen_do_without_continue(A, oper); + } + } + /* type-check expression */ if (!_slang_is_boolean(A, &oper->children[1])) { slang_info_log_error(A->log, "scalar/boolean expression expected for 'do/while'"); @@ -2421,9 +3072,8 @@ _slang_gen_do(slang_assemble_ctx * A, const slang_operation *oper) loop = new_loop(NULL); - /* save old, push new loop */ - prevLoop = A->CurLoop; - A->CurLoop = loop; + /* save loop state */ + push_loop(A, oper, loop); /* loop body: */ loop->Children[0] = _slang_gen_operation(A, &oper->children[0]); @@ -2437,13 +3087,13 @@ _slang_gen_do(slang_assemble_ctx * A, const slang_operation *oper) else { slang_ir_node *cond = new_cond(new_not(_slang_gen_operation(A, &oper->children[1]))); - loop->Children[1] = new_break_if_true(A->CurLoop, cond); + loop->Children[1] = new_break_if_true(A, cond); } /* XXX we should do infinite loop detection, as above */ - /* pop loop, restore prev */ - A->CurLoop = prevLoop; + /* restore loop state */ + pop_loop(A); return loop; } @@ -2485,9 +3135,14 @@ _slang_can_unroll_for_loop(slang_assemble_ctx * A, const slang_operation *oper) const char *varName; slang_atom varId; - assert(oper->type == SLANG_OPER_FOR); + if (oper->type != SLANG_OPER_FOR) + return GL_FALSE; + assert(oper->num_children == 4); + if (_slang_loop_contains_continue_or_break(slang_oper_child_const(oper, 3))) + return GL_FALSE; + /* children[0] must be either "int i=constant" or "i=constant" */ if (oper->children[0].type == SLANG_OPER_BLOCK_NO_NEW_SCOPE) { slang_variable *var; @@ -2582,20 +3237,6 @@ _slang_can_unroll_for_loop(slang_assemble_ctx * A, const slang_operation *oper) } -static void -_unroll_loop_inc(slang_assemble_ctx * A) -{ - A->UnrollLoop++; -} - - -static void -_unroll_loop_dec(slang_assemble_ctx * A) -{ - A->UnrollLoop--; -} - - /** * Unroll a for-loop. * First we determine the number of iterations to unroll. @@ -2612,9 +3253,6 @@ _slang_unroll_for_loop(slang_assemble_ctx * A, const slang_operation *oper) slang_ir_node *n, *root = NULL; slang_atom varId; - /* Set flag so code generator knows we're unrolling loops */ - _unroll_loop_inc( A ); - if (oper->children[0].type == SLANG_OPER_BLOCK_NO_NEW_SCOPE) { /* for (int i=0; ... */ slang_variable *var; @@ -2637,15 +3275,11 @@ _slang_unroll_for_loop(slang_assemble_ctx * A, const slang_operation *oper) /* make a copy of the loop body */ body = slang_operation_new(1); - if (!body) { - _unroll_loop_dec( A ); + if (!body) return NULL; - } - if (!slang_operation_copy(body, &oper->children[3])) { - _unroll_loop_dec( A ); + if (!slang_operation_copy(body, &oper->children[3])) return NULL; - } /* in body, replace instances of 'varId' with literal 'iter' */ { @@ -2656,7 +3290,6 @@ _slang_unroll_for_loop(slang_assemble_ctx * A, const slang_operation *oper) if (!oldVar) { /* undeclared loop variable */ slang_operation_delete(body); - _unroll_loop_dec( A ); return NULL; } @@ -2671,36 +3304,195 @@ _slang_unroll_for_loop(slang_assemble_ctx * A, const slang_operation *oper) /* do IR codegen for body */ n = _slang_gen_operation(A, body); - if (!n) { - _unroll_loop_dec( A ); + if (!n) return NULL; - } root = new_seq(root, n); slang_operation_delete(body); } - _unroll_loop_dec( A ); - return root; } /** + * Replace 'continue' statement with 'break' inside a for-loop. + * This is a recursive helper function used by _slang_gen_for_without_continue(). + */ +static void +replace_continue_with_break(slang_assemble_ctx *A, slang_operation *oper) +{ + switch (oper->type) { + case SLANG_OPER_CONTINUE: + oper->type = SLANG_OPER_BREAK; + break; + case SLANG_OPER_FOR: + case SLANG_OPER_DO: + case SLANG_OPER_WHILE: + /* stop upon finding a nested loop */ + break; + default: + /* recurse */ + { + GLuint i; + for (i = 0; i < oper->num_children; i++) { + replace_continue_with_break(A, slang_oper_child(oper, i)); + } + } + } +} + + +/** + * Transform a for-loop so that continue statements are converted to breaks. + * Then do normal IR code generation. + * + * Before: + * + * for (INIT; LOOPCOND; INCR) { + * A; + * if (IFCOND) { + * continue; + * } + * B; + * } + * + * After: + * + * { + * bool _condFlag = 1; + * for (INIT; _condFlag; ) { + * for ( ; _condFlag = LOOPCOND; INCR) { + * A; + * if (IFCOND) { + * break; + * } + * B; + * } + * if (_condFlag) + * INCR; + * } + * } + */ +static slang_ir_node * +_slang_gen_for_without_continue(slang_assemble_ctx *A, slang_operation *oper) +{ + slang_operation *top; + slang_operation *outerFor, *innerFor, *init, *cond, *incr; + slang_operation *lhs, *rhs; + + assert(oper->type == SLANG_OPER_FOR); + + top = slang_operation_new(1); + top->type = SLANG_OPER_BLOCK_NEW_SCOPE; + top->locals->outer_scope = oper->locals->outer_scope; + slang_operation_add_children(top, 2); + + /* declare: bool _condFlag = true */ + { + slang_operation *condDecl = slang_oper_child(top, 0); + slang_generate_declaration(A, top->locals, condDecl, + SLANG_SPEC_BOOL, "_condFlag", GL_TRUE); + } + + /* build outer loop: for (INIT; _condFlag; ) { */ + outerFor = slang_oper_child(top, 1); + outerFor->type = SLANG_OPER_FOR; + slang_operation_add_children(outerFor, 4); + + init = slang_oper_child(outerFor, 0); + slang_operation_copy(init, slang_oper_child(oper, 0)); + + cond = slang_oper_child(outerFor, 1); + cond->type = SLANG_OPER_IDENTIFIER; + cond->a_id = slang_atom_pool_atom(A->atoms, "_condFlag"); + + incr = slang_oper_child(outerFor, 2); + incr->type = SLANG_OPER_VOID; + + /* body of the outer loop */ + { + slang_operation *block = slang_oper_child(outerFor, 3); + + slang_operation_add_children(block, 2); + block->type = SLANG_OPER_BLOCK_NO_NEW_SCOPE; + + /* build inner loop: for ( ; _condFlag = LOOPCOND; INCR) { */ + { + innerFor = slang_oper_child(block, 0); + + /* make copy of orig loop */ + slang_operation_copy(innerFor, oper); + assert(innerFor->type == SLANG_OPER_FOR); + innerFor->locals->outer_scope = block->locals; + + init = slang_oper_child(innerFor, 0); + init->type = SLANG_OPER_VOID; /* leak? */ + + cond = slang_oper_child(innerFor, 1); + slang_operation_destruct(cond); + cond->type = SLANG_OPER_ASSIGN; + cond->locals = _slang_variable_scope_new(innerFor->locals); + slang_operation_add_children(cond, 2); + + lhs = slang_oper_child(cond, 0); + lhs->type = SLANG_OPER_IDENTIFIER; + lhs->a_id = slang_atom_pool_atom(A->atoms, "_condFlag"); + + rhs = slang_oper_child(cond, 1); + slang_operation_copy(rhs, slang_oper_child(oper, 1)); + } + + /* if (_condFlag) INCR; */ + { + slang_operation *ifop = slang_oper_child(block, 1); + ifop->type = SLANG_OPER_IF; + slang_operation_add_children(ifop, 2); + + /* re-use cond node build above */ + slang_operation_copy(slang_oper_child(ifop, 0), cond); + + /* incr node from original for-loop operation */ + slang_operation_copy(slang_oper_child(ifop, 1), + slang_oper_child(oper, 2)); + } + + /* finally, replace "continue" with "break" in the inner for-loop */ + replace_continue_with_break(A, slang_oper_child(innerFor, 3)); + } + + return _slang_gen_operation(A, top); +} + + + +/** * Generate IR for a for-loop. Unrolling will be done when possible. */ static slang_ir_node * -_slang_gen_for(slang_assemble_ctx * A, const slang_operation *oper) +_slang_gen_for(slang_assemble_ctx * A, slang_operation *oper) { - GLboolean unroll = _slang_can_unroll_for_loop(A, oper); + GLboolean unroll; + + if (!A->EmitContReturn) { + /* We don't want to emit CONT instructions. If this for-loop has + * a continue, translate it away. + */ + if (_slang_loop_contains_continue(slang_oper_child(oper, 3))) { + return _slang_gen_for_without_continue(A, oper); + } + } + unroll = _slang_can_unroll_for_loop(A, oper); if (unroll) { slang_ir_node *code = _slang_unroll_for_loop(A, oper); if (code) return code; } + assert(oper->type == SLANG_OPER_FOR); + /* conventional for-loop code generation */ { /* @@ -2711,24 +3503,23 @@ _slang_gen_for(slang_assemble_ctx * A, const slang_operation *oper) * tail code: * incr code (child[2]) // XXX continue here */ - slang_ir_node *prevLoop, *loop, *cond, *breakIf, *body, *init, *incr; + slang_ir_node *loop, *cond, *breakIf, *body, *init, *incr; init = _slang_gen_operation(A, &oper->children[0]); loop = new_loop(NULL); - /* save old, push new loop */ - prevLoop = A->CurLoop; - A->CurLoop = loop; + /* save loop state */ + push_loop(A, oper, loop); cond = new_cond(new_not(_slang_gen_operation(A, &oper->children[1]))); - breakIf = new_break_if_true(A->CurLoop, cond); + breakIf = new_break_if_true(A, cond); body = _slang_gen_operation(A, &oper->children[3]); incr = _slang_gen_operation(A, &oper->children[2]); loop->Children[0] = new_seq(breakIf, body); loop->Children[1] = incr; /* tail code */ - /* pop loop, restore prev */ - A->CurLoop = prevLoop; + /* restore loop state */ + pop_loop(A); return new_seq(init, loop); } @@ -2738,18 +3529,22 @@ _slang_gen_for(slang_assemble_ctx * A, const slang_operation *oper) static slang_ir_node * _slang_gen_continue(slang_assemble_ctx * A, const slang_operation *oper) { - slang_ir_node *n, *loopNode; + slang_ir_node *n, *cont, *incr = NULL, *loopNode; + assert(oper->type == SLANG_OPER_CONTINUE); - loopNode = A->CurLoop; + loopNode = current_loop_ir(A); assert(loopNode); assert(loopNode->Opcode == IR_LOOP); - n = new_node0(IR_CONT); - if (n) { - n->Parent = loopNode; - /* insert this node at head of linked list */ - n->List = loopNode->List; - loopNode->List = n; + + cont = new_node0(IR_CONT); + if (cont) { + cont->Parent = loopNode; + /* insert this node at head of linked list of cont/break instructions */ + cont->List = loopNode->List; + loopNode->List = cont; } + + n = new_seq(incr, cont); return n; } @@ -2819,24 +3614,20 @@ _slang_gen_if(slang_assemble_ctx * A, const slang_operation *oper) if (is_operation_type(&oper->children[1], SLANG_OPER_BREAK) && !haveElseClause) { /* Special case: generate a conditional break */ - if (!A->CurLoop && A->UnrollLoop) /* trying to unroll */ - return NULL; - ifBody = new_break_if_true(A->CurLoop, cond); + ifBody = new_break_if_true(A, cond); return ifBody; } else if (is_operation_type(&oper->children[1], SLANG_OPER_CONTINUE) - && !haveElseClause) { + && !haveElseClause + && current_loop_oper(A) + && current_loop_oper(A)->type != SLANG_OPER_FOR) { /* Special case: generate a conditional continue */ - if (!A->CurLoop && A->UnrollLoop) /* trying to unroll */ - return NULL; - ifBody = new_cont_if_true(A->CurLoop, cond); + ifBody = new_cont_if_true(A, cond); return ifBody; } else { /* general case */ ifBody = _slang_gen_operation(A, &oper->children[1]); - if (!ifBody) - return NULL; if (haveElseClause) elseBody = _slang_gen_operation(A, &oper->children[2]); else @@ -3242,14 +4033,11 @@ _slang_gen_logical_and(slang_assemble_ctx *A, slang_operation *oper) select = slang_operation_new(1); select->type = SLANG_OPER_SELECT; - select->num_children = 3; - select->children = slang_operation_new(3); + slang_operation_add_children(select, 3); - slang_operation_copy(&select->children[0], &oper->children[0]); - slang_operation_copy(&select->children[1], &oper->children[1]); - select->children[2].type = SLANG_OPER_LITERAL_BOOL; - ASSIGN_4V(select->children[2].literal, 0, 0, 0, 0); /* false */ - select->children[2].literal_size = 1; + slang_operation_copy(slang_oper_child(select, 0), &oper->children[0]); + slang_operation_copy(slang_oper_child(select, 1), &oper->children[1]); + slang_operation_literal_bool(slang_oper_child(select, 2), GL_FALSE); n = _slang_gen_select(A, select); return n; @@ -3268,14 +4056,11 @@ _slang_gen_logical_or(slang_assemble_ctx *A, slang_operation *oper) select = slang_operation_new(1); select->type = SLANG_OPER_SELECT; - select->num_children = 3; - select->children = slang_operation_new(3); + slang_operation_add_children(select, 3); - slang_operation_copy(&select->children[0], &oper->children[0]); - select->children[1].type = SLANG_OPER_LITERAL_BOOL; - ASSIGN_4V(select->children[1].literal, 1, 1, 1, 1); /* true */ - select->children[1].literal_size = 1; - slang_operation_copy(&select->children[2], &oper->children[1]); + slang_operation_copy(slang_oper_child(select, 0), &oper->children[0]); + slang_operation_literal_bool(slang_oper_child(select, 1), GL_TRUE); + slang_operation_copy(slang_oper_child(select, 2), &oper->children[1]); n = _slang_gen_select(A, select); return n; @@ -3288,70 +4073,8 @@ _slang_gen_logical_or(slang_assemble_ctx *A, slang_operation *oper) static slang_ir_node * _slang_gen_return(slang_assemble_ctx * A, slang_operation *oper) { - const GLboolean haveReturnValue - = (oper->num_children == 1 && oper->children[0].type != SLANG_OPER_VOID); - - /* error checking */ - assert(A->CurFunction); - if (haveReturnValue && - A->CurFunction->header.type.specifier.type == SLANG_SPEC_VOID) { - slang_info_log_error(A->log, "illegal return expression"); - return NULL; - } - else if (!haveReturnValue && - A->CurFunction->header.type.specifier.type != SLANG_SPEC_VOID) { - slang_info_log_error(A->log, "return statement requires an expression"); - return NULL; - } - - if (!haveReturnValue) { - return new_return(A->curFuncEndLabel); - } - else { - /* - * Convert from: - * return expr; - * To: - * __retVal = expr; - * return; // goto __endOfFunction - */ - slang_operation *assign; - slang_atom a_retVal; - slang_ir_node *n; - - a_retVal = slang_atom_pool_atom(A->atoms, "__retVal"); - assert(a_retVal); - -#if 1 /* DEBUG */ - { - slang_variable *v = - _slang_variable_locate(oper->locals, a_retVal, GL_TRUE); - if (!v) { - /* trying to return a value in a void-valued function */ - return NULL; - } - } -#endif - - assign = slang_operation_new(1); - assign->type = SLANG_OPER_ASSIGN; - assign->num_children = 2; - assign->children = slang_operation_new(2); - /* lhs (__retVal) */ - assign->children[0].type = SLANG_OPER_IDENTIFIER; - assign->children[0].a_id = a_retVal; - assign->children[0].locals->outer_scope = assign->locals; - /* rhs (expr) */ - /* XXX we might be able to avoid this copy someday */ - slang_operation_copy(&assign->children[1], &oper->children[0]); - - /* assemble the new code */ - n = new_seq(_slang_gen_operation(A, assign), - new_return(A->curFuncEndLabel)); - - slang_operation_delete(assign); - return n; - } + assert(oper->type == SLANG_OPER_RETURN); + return new_return(A->curFuncEndLabel); } @@ -3457,6 +4180,7 @@ _slang_gen_declaration(slang_assemble_ctx *A, slang_operation *oper) assert(oper->type == SLANG_OPER_VARIABLE_DECL); assert(oper->num_children <= 1); + /* lookup the variable by name */ var = _slang_variable_locate(oper->locals, oper->a_id, GL_TRUE); if (!var) @@ -3612,6 +4336,9 @@ root_swizzle(const slang_ir_storage *st) static slang_ir_node * _slang_gen_assignment(slang_assemble_ctx * A, slang_operation *oper) { + slang_operation *pred = NULL; + slang_ir_node *n = NULL; + if (oper->children[0].type == SLANG_OPER_IDENTIFIER) { /* Check that var is writeable */ slang_variable *var @@ -3632,6 +4359,17 @@ _slang_gen_assignment(slang_assemble_ctx * A, slang_operation *oper) (char *) oper->children[0].a_id); return NULL; } + + /* check if we need to predicate this assignment based on __notRetFlag */ + if ((var->is_global || + var->type.qualifier == SLANG_QUAL_OUT || + var->type.qualifier == SLANG_QUAL_INOUT) && A->UseReturnFlag) { + /* create predicate, used below */ + pred = slang_operation_new(1); + pred->type = SLANG_OPER_IDENTIFIER; + pred->a_id = slang_atom_pool_atom(A->atoms, "__notRetFlag"); + pred->locals->outer_scope = oper->locals->outer_scope; + } } if (oper->children[0].type == SLANG_OPER_IDENTIFIER && @@ -3643,14 +4381,12 @@ _slang_gen_assignment(slang_assemble_ctx * A, slang_operation *oper) * cases such as "v.x = f();" - would help with typical vertex * transformation. */ - slang_ir_node *n; n = _slang_gen_function_call_name(A, (const char *) oper->children[1].a_id, &oper->children[1], &oper->children[0]); - return n; } else { - slang_ir_node *n, *lhs, *rhs; + slang_ir_node *lhs, *rhs; /* lhs and rhs type checking */ if (!_slang_assignment_compatible(A, @@ -3690,12 +4426,21 @@ _slang_gen_assignment(slang_assemble_ctx * A, slang_operation *oper) rhs = _slang_gen_swizzle(rhs, newSwizzle); } n = new_node2(IR_COPY, lhs, rhs); - return n; } else { return NULL; } } + + if (n && pred) { + /* predicate the assignment code on __notRetFlag */ + slang_ir_node *top, *cond; + + cond = _slang_gen_operation(A, pred); + top = new_if(cond, n, NULL); + return top; + } + return n; } @@ -4048,16 +4793,14 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper) case SLANG_OPER_WHILE: return _slang_gen_while(A, oper); case SLANG_OPER_BREAK: - if (!A->CurLoop) { - if (!A->UnrollLoop) - slang_info_log_error(A->log, "'break' not in loop"); + if (!current_loop_oper(A)) { + slang_info_log_error(A->log, "'break' not in loop"); return NULL; } - return new_break(A->CurLoop); + return new_break(current_loop_ir(A)); case SLANG_OPER_CONTINUE: - if (!A->CurLoop) { - if (!A->UnrollLoop) - slang_info_log_error(A->log, "'continue' not in loop"); + if (!current_loop_oper(A)) { + slang_info_log_error(A->log, "'continue' not in loop"); return NULL; } return _slang_gen_continue(A, oper); @@ -4182,6 +4925,8 @@ _slang_gen_operation(slang_assemble_ctx * A, slang_operation *oper) return _slang_gen_method_call(A, oper); case SLANG_OPER_RETURN: return _slang_gen_return(A, oper); + case SLANG_OPER_RETURN_INLINED: + return _slang_gen_return(A, oper); case SLANG_OPER_LABEL: return new_label(oper->label); case SLANG_OPER_IDENTIFIER: @@ -4303,6 +5048,8 @@ _slang_codegen_global_variable(slang_assemble_ctx *A, slang_variable *var, const GLint totalSize = _slang_array_size(size, arrayLen); GLint texIndex = sampler_to_texture_index(var->type.specifier.type); + var->is_global = GL_TRUE; + /* check for sampler2D arrays */ if (texIndex == -1 && var->type.specifier._array) texIndex = sampler_to_texture_index(var->type.specifier._array->type); @@ -4601,7 +5348,9 @@ _slang_codegen_function(slang_assemble_ctx * A, slang_function * fun) assert(A->program->Parameters ); assert(A->program->Varying); assert(A->vartable); - A->CurLoop = NULL; + + A->LoopDepth = 0; + A->UseReturnFlag = GL_FALSE; A->CurFunction = fun; /* fold constant expressions, etc. */ diff --git a/src/mesa/shader/slang/slang_codegen.h b/src/mesa/shader/slang/slang_codegen.h index d80013ad34..ee3be55a45 100644 --- a/src/mesa/shader/slang/slang_codegen.h +++ b/src/mesa/shader/slang/slang_codegen.h @@ -31,6 +31,9 @@ #include "slang_compile.h" +#define MAX_LOOP_DEPTH 30 + + typedef struct slang_assemble_ctx_ { slang_atom_pool *atoms; @@ -39,11 +42,19 @@ typedef struct slang_assemble_ctx_ struct gl_sl_pragmas *pragmas; slang_var_table *vartable; slang_info_log *log; - struct slang_label_ *curFuncEndLabel; - struct slang_ir_node_ *CurLoop; + + /* current loop stack */ + const slang_operation *LoopOperStack[MAX_LOOP_DEPTH]; + struct slang_ir_node_ *LoopIRStack[MAX_LOOP_DEPTH]; + GLuint LoopDepth; + + /* current function */ struct slang_function_ *CurFunction; - GLuint UnrollLoop; + struct slang_label_ *curFuncEndLabel; + GLboolean UseReturnFlag; + GLboolean UnresolvedRefs; + GLboolean EmitContReturn; } slang_assemble_ctx; diff --git a/src/mesa/shader/slang/slang_compile.c b/src/mesa/shader/slang/slang_compile.c index d7ad879e97..c1b97c7cb7 100644 --- a/src/mesa/shader/slang/slang_compile.c +++ b/src/mesa/shader/slang/slang_compile.c @@ -1953,6 +1953,7 @@ static int parse_init_declarator(slang_parse_ctx * C, slang_output_ctx * O, const slang_fully_specified_type * type) { + GET_CURRENT_CONTEXT(ctx); /* a hack */ slang_variable *var; slang_atom a_name; @@ -2066,6 +2067,7 @@ parse_init_declarator(slang_parse_ctx * C, slang_output_ctx * O, A.vartable = O->vartable; A.log = C->L; A.curFuncEndLabel = NULL; + A.EmitContReturn = ctx->Shader.EmitContReturn; if (!_slang_codegen_global_variable(&A, var, C->type)) RETURN0; } @@ -2430,6 +2432,7 @@ parse_code_unit(slang_parse_ctx * C, slang_code_unit * unit, A.program = o.program; A.pragmas = &shader->Pragmas; A.vartable = o.vartable; + A.EmitContReturn = ctx->Shader.EmitContReturn; A.log = C->L; /* main() takes no parameters */ diff --git a/src/mesa/shader/slang/slang_compile_operation.c b/src/mesa/shader/slang/slang_compile_operation.c index e650616880..3e2bdbc91f 100644 --- a/src/mesa/shader/slang/slang_compile_operation.c +++ b/src/mesa/shader/slang/slang_compile_operation.c @@ -119,11 +119,13 @@ slang_operation_copy(slang_operation * x, const slang_operation * y) if (!slang_operation_construct(&z)) return GL_FALSE; z.type = y->type; - z.children = (slang_operation *) - _slang_alloc(y->num_children * sizeof(slang_operation)); - if (z.children == NULL) { - slang_operation_destruct(&z); - return GL_FALSE; + if (y->num_children > 0) { + z.children = (slang_operation *) + _slang_alloc(y->num_children * sizeof(slang_operation)); + if (z.children == NULL) { + slang_operation_destruct(&z); + return GL_FALSE; + } } for (z.num_children = 0; z.num_children < y->num_children; z.num_children++) { @@ -152,6 +154,15 @@ slang_operation_copy(slang_operation * x, const slang_operation * y) return GL_FALSE; } } + + /* update scoping for children */ + for (i = 0; i < y->num_children; i++) { + if (y->children[i].locals && + y->children[i].locals->outer_scope == y->locals) { + z.children[i].locals->outer_scope = z.locals; + } + } + #if 0 z.var = y->var; z.fun = y->fun; @@ -198,6 +209,20 @@ slang_operation_delete(slang_operation *oper) } +void +slang_operation_free_children(slang_operation *oper) +{ + GLuint i; + for (i = 0; i < slang_oper_num_children(oper); i++) { + slang_operation *child = slang_oper_child(oper, i); + slang_operation_destruct(child); + } + _slang_free(oper->children); + oper->children = NULL; + oper->num_children = 0; +} + + slang_operation * slang_operation_grow(GLuint *numChildren, slang_operation **children) { @@ -263,6 +288,26 @@ slang_operation_insert(GLuint *numElements, slang_operation **array, } +/** + * Add/insert new child into given node at given position. + * \return pointer to the new child node + */ +slang_operation * +slang_operation_insert_child(slang_operation *oper, GLuint pos) +{ + slang_operation *newOp; + + newOp = slang_operation_insert(&oper->num_children, + &oper->children, + pos); + if (newOp) { + newOp->locals->outer_scope = oper->locals; + } + + return newOp; +} + + void _slang_operation_swap(slang_operation *oper0, slang_operation *oper1) { @@ -272,3 +317,16 @@ _slang_operation_swap(slang_operation *oper0, slang_operation *oper1) } +void +slang_operation_add_children(slang_operation *oper, GLuint num_children) +{ + GLuint i; + assert(oper->num_children == 0); + assert(oper->children == NULL); + oper->num_children = num_children; + oper->children = slang_operation_new(num_children); + for (i = 0; i < num_children; i++) { + oper->children[i].locals = _slang_variable_scope_new(oper->locals); + } +} + diff --git a/src/mesa/shader/slang/slang_compile_operation.h b/src/mesa/shader/slang/slang_compile_operation.h index 37af5d617c..58f1edeed8 100644 --- a/src/mesa/shader/slang/slang_compile_operation.h +++ b/src/mesa/shader/slang/slang_compile_operation.h @@ -42,6 +42,7 @@ typedef enum slang_operation_type_ SLANG_OPER_CONTINUE, /* "continue" statement */ SLANG_OPER_DISCARD, /* "discard" (kill fragment) statement */ SLANG_OPER_RETURN, /* "return" [expr] */ + SLANG_OPER_RETURN_INLINED, /* "return" [expr] from inlined function */ SLANG_OPER_LABEL, /* a jump target */ SLANG_OPER_EXPRESSION, /* [expr] */ SLANG_OPER_IF, /* "if" [0] then [1] else [2] */ @@ -150,6 +151,9 @@ slang_operation_new(GLuint count); extern void slang_operation_delete(slang_operation *oper); +extern void +slang_operation_free_children(slang_operation *oper); + extern slang_operation * slang_operation_grow(GLuint *numChildren, slang_operation **children); @@ -157,8 +161,66 @@ extern slang_operation * slang_operation_insert(GLuint *numChildren, slang_operation **children, GLuint pos); +extern slang_operation * +slang_operation_insert_child(slang_operation *oper, GLuint pos); + extern void _slang_operation_swap(slang_operation *oper0, slang_operation *oper1); +extern void +slang_operation_add_children(slang_operation *oper, GLuint num_children); + + +/** Return number of children of given node */ +static INLINE GLuint +slang_oper_num_children(const slang_operation *oper) +{ + return oper->num_children; +} + +/** Return child of given operation node */ +static INLINE slang_operation * +slang_oper_child(slang_operation *oper, GLuint child) +{ + assert(child < oper->num_children); + return &oper->children[child]; +} + + +/** Return child of given operation node, const version */ +static INLINE const slang_operation * +slang_oper_child_const(const slang_operation *oper, GLuint child) +{ + assert(child < oper->num_children); + return &oper->children[child]; +} + + +/** Init oper to a boolean literal. */ +static INLINE void +slang_operation_literal_bool(slang_operation *oper, GLboolean value) +{ + oper->type = SLANG_OPER_LITERAL_BOOL; + oper->literal[0] = + oper->literal[1] = + oper->literal[2] = + oper->literal[3] = (float) value; + oper->literal_size = 1; +} + + +/** Init oper to an int literal. */ +static INLINE void +slang_operation_literal_int(slang_operation *oper, GLint value) +{ + oper->type = SLANG_OPER_LITERAL_INT; + oper->literal[0] = + oper->literal[1] = + oper->literal[2] = + oper->literal[3] = (float) value; + oper->literal_size = 1; +} + + #endif /* SLANG_COMPILE_OPERATION_H */ diff --git a/src/mesa/shader/slang/slang_compile_variable.h b/src/mesa/shader/slang/slang_compile_variable.h index a667d91b47..b4585599f2 100644 --- a/src/mesa/shader/slang/slang_compile_variable.h +++ b/src/mesa/shader/slang/slang_compile_variable.h @@ -39,6 +39,7 @@ typedef struct slang_variable_ GLuint array_len; /**< only if type == SLANG_SPEC_ARRAy */ struct slang_operation_ *initializer; /**< Optional initializer code */ GLuint size; /**< Variable's size in bytes */ + GLboolean is_global; GLboolean isTemp; /**< a named temporary (__resultTmp) */ GLboolean declared; /**< for debug */ struct slang_ir_storage_ *store; /**< Storage for this var */ diff --git a/src/mesa/shader/slang/slang_link.c b/src/mesa/shader/slang/slang_link.c index 1fdf4db054..f6032d1e9a 100644 --- a/src/mesa/shader/slang/slang_link.c +++ b/src/mesa/shader/slang/slang_link.c @@ -97,7 +97,8 @@ bits_agree(GLbitfield flags1, GLbitfield flags2, GLbitfield bit) * which inputs are centroid-sampled, invariant, etc. */ static GLboolean -link_varying_vars(struct gl_shader_program *shProg, struct gl_program *prog) +link_varying_vars(GLcontext *ctx, + struct gl_shader_program *shProg, struct gl_program *prog) { GLuint *map, i, firstVarying, newFile; GLbitfield *inOutFlags; @@ -156,8 +157,12 @@ link_varying_vars(struct gl_shader_program *shProg, struct gl_program *prog) var->Flags); } + if (shProg->Varying->NumParameters > ctx->Const.MaxVarying) { + link_error(shProg, "Too many varying variables"); + return GL_FALSE; + } + /* Map varying[i] to varying[j]. - * Plus, set prog->Input/OutputFlags[] as described above. * Note: the loop here takes care of arrays or large (sz>4) vars. */ { @@ -319,7 +324,7 @@ _slang_resolve_attributes(struct gl_shader_program *shProg, const struct gl_program *origProg, struct gl_program *linkedProg) { - GLint attribMap[MAX_VERTEX_ATTRIBS]; + GLint attribMap[MAX_VERTEX_GENERIC_ATTRIBS]; GLuint i, j; GLbitfield usedAttributes; /* generics only, not legacy attributes */ @@ -355,7 +360,7 @@ _slang_resolve_attributes(struct gl_shader_program *shProg, } /* initialize the generic attribute map entries to -1 */ - for (i = 0; i < MAX_VERTEX_ATTRIBS; i++) { + for (i = 0; i < MAX_VERTEX_GENERIC_ATTRIBS; i++) { attribMap[i] = -1; } @@ -396,11 +401,11 @@ _slang_resolve_attributes(struct gl_shader_program *shProg, * Start at 1 since generic attribute 0 always aliases * glVertex/position. */ - for (attr = 0; attr < MAX_VERTEX_ATTRIBS; attr++) { + for (attr = 0; attr < MAX_VERTEX_GENERIC_ATTRIBS; attr++) { if (((1 << attr) & usedAttributes) == 0) break; } - if (attr == MAX_VERTEX_ATTRIBS) { + if (attr == MAX_VERTEX_GENERIC_ATTRIBS) { link_error(shProg, "Too many vertex attributes"); return GL_FALSE; } @@ -712,6 +717,8 @@ _slang_link(GLcontext *ctx, struct gl_vertex_program *linked_vprog = vertex_program(_mesa_clone_program(ctx, &vertProg->Base)); shProg->VertexProgram = linked_vprog; /* refcount OK */ + /* vertex program ID not significant; just set Id for debugging purposes */ + shProg->VertexProgram->Base.Id = shProg->Name; ASSERT(shProg->VertexProgram->Base.RefCount == 1); } @@ -720,16 +727,18 @@ _slang_link(GLcontext *ctx, struct gl_fragment_program *linked_fprog = fragment_program(_mesa_clone_program(ctx, &fragProg->Base)); shProg->FragmentProgram = linked_fprog; /* refcount OK */ + /* vertex program ID not significant; just set Id for debugging purposes */ + shProg->FragmentProgram->Base.Id = shProg->Name; ASSERT(shProg->FragmentProgram->Base.RefCount == 1); } /* link varying vars */ if (shProg->VertexProgram) { - if (!link_varying_vars(shProg, &shProg->VertexProgram->Base)) + if (!link_varying_vars(ctx, shProg, &shProg->VertexProgram->Base)) return; } if (shProg->FragmentProgram) { - if (!link_varying_vars(shProg, &shProg->FragmentProgram->Base)) + if (!link_varying_vars(ctx, shProg, &shProg->FragmentProgram->Base)) return; } @@ -834,6 +843,14 @@ _slang_link(GLcontext *ctx, } } + /* Debug: */ + if (0) { + if (shProg->VertexProgram) + _mesa_postprocess_program(ctx, &shProg->VertexProgram->Base); + if (shProg->FragmentProgram) + _mesa_postprocess_program(ctx, &shProg->FragmentProgram->Base); + } + if (ctx->Shader.Flags & GLSL_DUMP) { _mesa_printf("Varying vars:\n"); _mesa_print_parameter_list(shProg->Varying); diff --git a/src/mesa/shader/slang/slang_preprocess.c b/src/mesa/shader/slang/slang_preprocess.c index ff913ad883..e9a24cc009 100644 --- a/src/mesa/shader/slang/slang_preprocess.c +++ b/src/mesa/shader/slang/slang_preprocess.c @@ -1035,11 +1035,11 @@ preprocess_source (slang_string *output, const char *source, /* Parse optional macro parameters. */ while (prod[i++] != PARAM_END) { - if (state.cond.top->effective) { - pp_symbol *param; + pp_symbol *param; - id = (const char *) (&prod[i]); - idlen = _mesa_strlen (id); + id = (const char *) (&prod[i]); + idlen = _mesa_strlen (id); + if (state.cond.top->effective) { pp_annotate (output, "%s, ", id); param = pp_symbols_push (&symbol->parameters); if (param == NULL) @@ -1053,8 +1053,23 @@ preprocess_source (slang_string *output, const char *source, id = (const char *) (&prod[i]); idlen = _mesa_strlen (id); if (state.cond.top->effective) { + slang_string replacement; + expand_state es; + pp_annotate (output, ") %s", id); - slang_string_pushs (&symbol->replacement, id, idlen); + + slang_string_init(&replacement); + slang_string_pushs(&replacement, id, idlen); + + /* Expand macro replacement. */ + es.output = &symbol->replacement; + es.input = slang_string_cstr(&replacement); + es.state = &state; + if (!expand(&es, &state.symbols)) { + slang_string_free(&replacement); + goto error; + } + slang_string_free(&replacement); } i += idlen + 1; } @@ -1286,6 +1301,45 @@ error: /** + * Remove the continuation characters from the input string. + * This is the very first step in preprocessing and is effective + * even inside comment blocks. + * If there is a whitespace between a backslash and a newline, + * this is not considered as a line continuation. + * \return GL_TRUE for success, GL_FALSE otherwise. + */ +static GLboolean +_slang_preprocess_backslashes(slang_string *output, + const char *input) +{ + while (*input) { + if (input[0] == '\\') { + /* If a newline follows, eat the backslash and the newline. */ + if (input[1] == '\r') { + if (input[2] == '\n') { + input += 3; + } else { + input += 2; + } + } else if (input[1] == '\n') { + if (input[2] == '\r') { + input += 3; + } else { + input += 2; + } + } else { + /* Leave the backslash alone. */ + slang_string_pushc(output, *input++); + } + } else { + slang_string_pushc(output, *input++); + } + } + return GL_TRUE; +} + + +/** * Run preprocessor on source code. * \param extensions indicates which GL extensions are enabled * \param output the post-process results @@ -1304,6 +1358,7 @@ _slang_preprocess_directives(slang_string *output, { grammar pid, eid; GLboolean success; + slang_string without_backslashes; pid = grammar_load_from_text ((const byte *) (slang_pp_directives_syn)); if (pid == 0) { @@ -1316,9 +1371,36 @@ _slang_preprocess_directives(slang_string *output, grammar_destroy (pid); return GL_FALSE; } - success = preprocess_source (output, input, pid, eid, elog, extensions, pragmas); + + slang_string_init(&without_backslashes); + success = _slang_preprocess_backslashes(&without_backslashes, input); + + if (0) { + _mesa_printf("Pre-processed shader:\n"); + _mesa_printf("%s", slang_string_cstr(&without_backslashes)); + _mesa_printf("----------------------\n"); + } + + if (success) { + success = preprocess_source(output, + slang_string_cstr(&without_backslashes), + pid, + eid, + elog, + extensions, + pragmas); + } + + slang_string_free(&without_backslashes); grammar_destroy (eid); grammar_destroy (pid); + + if (0) { + _mesa_printf("Post-processed shader:\n"); + _mesa_printf("%s", slang_string_cstr(output)); + _mesa_printf("----------------------\n"); + } + return success; } diff --git a/src/mesa/shader/slang/slang_print.c b/src/mesa/shader/slang/slang_print.c index bf1475f91d..98c7877534 100644 --- a/src/mesa/shader/slang/slang_print.c +++ b/src/mesa/shader/slang/slang_print.c @@ -261,6 +261,7 @@ slang_print_tree(const slang_operation *op, int indent) break; case SLANG_OPER_BLOCK_NEW_SCOPE: + case SLANG_OPER_NON_INLINED_CALL: spaces(indent); printf("{{ // new scope locals=%p outer=%p: ", (void *) op->locals, @@ -348,6 +349,13 @@ slang_print_tree(const slang_operation *op, int indent) slang_print_tree(&op->children[0], indent + 3); break; + case SLANG_OPER_RETURN_INLINED: + spaces(indent); + printf("RETURN_INLINED\n"); + if (op->num_children > 0) + slang_print_tree(&op->children[0], indent + 3); + break; + case SLANG_OPER_LABEL: spaces(indent); printf("LABEL %s\n", (char *) op->a_id); @@ -478,7 +486,7 @@ slang_print_tree(const slang_operation *op, int indent) (void *) scope, (void *) op->locals, (void *) op->locals->outer_scope); - assert(scope); + /*assert(scope);*/ } } break; |