diff options
author | Christoph Bumiller <[email protected]> | 2010-09-02 18:31:49 +0200 |
---|---|---|
committer | Christoph Bumiller <[email protected]> | 2010-09-02 18:31:49 +0200 |
commit | 222d2f2ac2c7d93cbc0643082c78278ad2c8cfce (patch) | |
tree | b79152c238022b2a901201c22e5809ac520732bf /src/mesa/program | |
parent | 443abc80db9e1a288ce770e76cccd43664348098 (diff) | |
parent | e73c5501b2fe20290d1b691c85a5d82ac3a0431c (diff) |
Merge remote branch 'origin/master' into nv50-compiler
Conflicts:
src/gallium/drivers/nv50/nv50_program.c
Diffstat (limited to 'src/mesa/program')
-rw-r--r-- | src/mesa/program/hash_table.c | 31 | ||||
-rw-r--r-- | src/mesa/program/hash_table.h | 35 | ||||
-rw-r--r-- | src/mesa/program/ir_to_mesa.cpp | 2796 | ||||
-rw-r--r-- | src/mesa/program/ir_to_mesa.h | 38 | ||||
-rw-r--r-- | src/mesa/program/prog_execute.c | 29 | ||||
-rw-r--r-- | src/mesa/program/prog_instruction.h | 2 | ||||
-rw-r--r-- | src/mesa/program/prog_optimize.c | 84 | ||||
-rw-r--r-- | src/mesa/program/prog_parameter.c | 96 | ||||
-rw-r--r-- | src/mesa/program/prog_parameter.h | 16 | ||||
-rw-r--r-- | src/mesa/program/prog_print.c | 38 | ||||
-rw-r--r-- | src/mesa/program/prog_print.h | 7 | ||||
-rw-r--r-- | src/mesa/program/prog_uniform.c | 4 | ||||
-rw-r--r-- | src/mesa/program/prog_uniform.h | 5 | ||||
-rw-r--r-- | src/mesa/program/symbol_table.c | 59 | ||||
-rw-r--r-- | src/mesa/program/symbol_table.h | 3 |
15 files changed, 3074 insertions, 169 deletions
diff --git a/src/mesa/program/hash_table.c b/src/mesa/program/hash_table.c index fa6ba2bfdfc..f7ef366c1a0 100644 --- a/src/mesa/program/hash_table.c +++ b/src/mesa/program/hash_table.c @@ -142,6 +142,23 @@ hash_table_insert(struct hash_table *ht, void *data, const void *key) insert_at_head(& ht->buckets[bucket], & node->link); } +void +hash_table_remove(struct hash_table *ht, const void *key) +{ + const unsigned hash_value = (*ht->hash)(key); + const unsigned bucket = hash_value % ht->num_buckets; + struct node *node; + + foreach(node, & ht->buckets[bucket]) { + struct hash_node *hn = (struct hash_node *) node; + + if ((*ht->compare)(hn->key, key) == 0) { + remove_from_list(node); + free(node); + return; + } + } +} unsigned hash_table_string_hash(const void *key) @@ -157,3 +174,17 @@ hash_table_string_hash(const void *key) return hash; } + + +unsigned +hash_table_pointer_hash(const void *key) +{ + return (unsigned)((uintptr_t) key / sizeof(void *)); +} + + +int +hash_table_pointer_compare(const void *key1, const void *key2) +{ + return key1 == key2 ? 0 : 1; +} diff --git a/src/mesa/program/hash_table.h b/src/mesa/program/hash_table.h index e750906f961..f1c4fdcd1fa 100644 --- a/src/mesa/program/hash_table.h +++ b/src/mesa/program/hash_table.h @@ -36,6 +36,10 @@ struct hash_table; typedef unsigned (*hash_func_t)(const void *key); typedef int (*hash_compare_func_t)(const void *key1, const void *key2); +#ifdef __cplusplus +extern "C" { +#endif + /** * Hash table constructor * @@ -88,6 +92,10 @@ extern void *hash_table_find(struct hash_table *ht, const void *key); extern void hash_table_insert(struct hash_table *ht, void *data, const void *key); +/** + * Remove a specific element from a hash table. + */ +extern void hash_table_remove(struct hash_table *ht, const void *key); /** * Compute hash value of a string @@ -112,4 +120,31 @@ extern unsigned hash_table_string_hash(const void *key); */ #define hash_table_string_compare ((hash_compare_func_t) strcmp) + +/** + * Compute hash value of a pointer + * + * \param key Pointer to be used as a hash key + * + * \note + * The memory pointed to by \c key is \b never accessed. The value of \c key + * itself is used as the hash key + * + * \sa hash_table_pointer_compare + */ +unsigned +hash_table_pointer_hash(const void *key); + + +/** + * Compare two pointers used as keys + * + * \sa hash_table_pointer_hash + */ +int +hash_table_pointer_compare(const void *key1, const void *key2); + +#ifdef __cplusplus +} +#endif #endif /* HASH_TABLE_H */ diff --git a/src/mesa/program/ir_to_mesa.cpp b/src/mesa/program/ir_to_mesa.cpp new file mode 100644 index 00000000000..f0e14b8ece3 --- /dev/null +++ b/src/mesa/program/ir_to_mesa.cpp @@ -0,0 +1,2796 @@ +/* + * Copyright (C) 2005-2007 Brian Paul All Rights Reserved. + * Copyright (C) 2008 VMware, Inc. All Rights Reserved. + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * \file ir_to_mesa.cpp + * + * Translates the IR to ARB_fragment_program text if possible, + * printing the result + */ + +#include <stdio.h> +#include "main/compiler.h" +#include "ir.h" +#include "ir_visitor.h" +#include "ir_print_visitor.h" +#include "ir_expression_flattening.h" +#include "glsl_types.h" +#include "glsl_parser_extras.h" +#include "../glsl/program.h" +#include "ir_optimization.h" +#include "ast.h" + +extern "C" { +#include "main/mtypes.h" +#include "main/shaderapi.h" +#include "main/shaderobj.h" +#include "main/uniforms.h" +#include "program/hash_table.h" +#include "program/prog_instruction.h" +#include "program/prog_optimize.h" +#include "program/prog_print.h" +#include "program/program.h" +#include "program/prog_uniform.h" +#include "program/prog_parameter.h" +} + +static int swizzle_for_size(int size); + +/** + * This struct is a corresponding struct to Mesa prog_src_register, with + * wider fields. + */ +typedef struct ir_to_mesa_src_reg { + ir_to_mesa_src_reg(int file, int index, const glsl_type *type) + { + this->file = file; + this->index = index; + if (type && (type->is_scalar() || type->is_vector() || type->is_matrix())) + this->swizzle = swizzle_for_size(type->vector_elements); + else + this->swizzle = SWIZZLE_XYZW; + this->negate = 0; + this->reladdr = NULL; + } + + ir_to_mesa_src_reg() + { + this->file = PROGRAM_UNDEFINED; + this->index = 0; + this->swizzle = 0; + this->negate = 0; + this->reladdr = NULL; + } + + int file; /**< PROGRAM_* from Mesa */ + int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */ + GLuint swizzle; /**< SWIZZLE_XYZWONEZERO swizzles from Mesa. */ + int negate; /**< NEGATE_XYZW mask from mesa */ + /** Register index should be offset by the integer in this reg. */ + ir_to_mesa_src_reg *reladdr; +} ir_to_mesa_src_reg; + +typedef struct ir_to_mesa_dst_reg { + int file; /**< PROGRAM_* from Mesa */ + int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */ + int writemask; /**< Bitfield of WRITEMASK_[XYZW] */ + GLuint cond_mask:4; + /** Register index should be offset by the integer in this reg. */ + ir_to_mesa_src_reg *reladdr; +} ir_to_mesa_dst_reg; + +extern ir_to_mesa_src_reg ir_to_mesa_undef; + +class ir_to_mesa_instruction : public exec_node { +public: + /* Callers of this talloc-based new need not call delete. It's + * easier to just talloc_free 'ctx' (or any of its ancestors). */ + static void* operator new(size_t size, void *ctx) + { + void *node; + + node = talloc_zero_size(ctx, size); + assert(node != NULL); + + return node; + } + + enum prog_opcode op; + ir_to_mesa_dst_reg dst_reg; + ir_to_mesa_src_reg src_reg[3]; + /** Pointer to the ir source this tree came from for debugging */ + ir_instruction *ir; + GLboolean cond_update; + int sampler; /**< sampler index */ + int tex_target; /**< One of TEXTURE_*_INDEX */ + GLboolean tex_shadow; + + class function_entry *function; /* Set on OPCODE_CAL or OPCODE_BGNSUB */ +}; + +class variable_storage : public exec_node { +public: + variable_storage(ir_variable *var, int file, int index) + : file(file), index(index), var(var) + { + /* empty */ + } + + int file; + int index; + ir_variable *var; /* variable that maps to this, if any */ +}; + +class function_entry : public exec_node { +public: + ir_function_signature *sig; + + /** + * identifier of this function signature used by the program. + * + * At the point that Mesa instructions for function calls are + * generated, we don't know the address of the first instruction of + * the function body. So we make the BranchTarget that is called a + * small integer and rewrite them during set_branchtargets(). + */ + int sig_id; + + /** + * Pointer to first instruction of the function body. + * + * Set during function body emits after main() is processed. + */ + ir_to_mesa_instruction *bgn_inst; + + /** + * Index of the first instruction of the function body in actual + * Mesa IR. + * + * Set after convertion from ir_to_mesa_instruction to prog_instruction. + */ + int inst; + + /** Storage for the return value. */ + ir_to_mesa_src_reg return_reg; +}; + +class ir_to_mesa_visitor : public ir_visitor { +public: + ir_to_mesa_visitor(); + ~ir_to_mesa_visitor(); + + function_entry *current_function; + + GLcontext *ctx; + struct gl_program *prog; + struct gl_shader_program *shader_program; + + int next_temp; + + variable_storage *find_variable_storage(ir_variable *var); + + function_entry *get_function_signature(ir_function_signature *sig); + + ir_to_mesa_src_reg get_temp(const glsl_type *type); + void reladdr_to_temp(ir_instruction *ir, + ir_to_mesa_src_reg *reg, int *num_reladdr); + + struct ir_to_mesa_src_reg src_reg_for_float(float val); + + /** + * \name Visit methods + * + * As typical for the visitor pattern, there must be one \c visit method for + * each concrete subclass of \c ir_instruction. Virtual base classes within + * the hierarchy should not have \c visit methods. + */ + /*@{*/ + virtual void visit(ir_variable *); + virtual void visit(ir_loop *); + virtual void visit(ir_loop_jump *); + virtual void visit(ir_function_signature *); + virtual void visit(ir_function *); + virtual void visit(ir_expression *); + virtual void visit(ir_swizzle *); + virtual void visit(ir_dereference_variable *); + virtual void visit(ir_dereference_array *); + virtual void visit(ir_dereference_record *); + virtual void visit(ir_assignment *); + virtual void visit(ir_constant *); + virtual void visit(ir_call *); + virtual void visit(ir_return *); + virtual void visit(ir_discard *); + virtual void visit(ir_texture *); + virtual void visit(ir_if *); + /*@}*/ + + struct ir_to_mesa_src_reg result; + + /** List of variable_storage */ + exec_list variables; + + /** List of function_entry */ + exec_list function_signatures; + int next_signature_id; + + /** List of ir_to_mesa_instruction */ + exec_list instructions; + + ir_to_mesa_instruction *ir_to_mesa_emit_op0(ir_instruction *ir, + enum prog_opcode op); + + ir_to_mesa_instruction *ir_to_mesa_emit_op1(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0); + + ir_to_mesa_instruction *ir_to_mesa_emit_op2(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1); + + ir_to_mesa_instruction *ir_to_mesa_emit_op3(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1, + ir_to_mesa_src_reg src2); + + void ir_to_mesa_emit_scalar_op1(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0); + + void ir_to_mesa_emit_scalar_op2(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1); + + GLboolean try_emit_mad(ir_expression *ir, + int mul_operand); + + int get_sampler_uniform_value(ir_dereference *deref); + + void *mem_ctx; +}; + +ir_to_mesa_src_reg ir_to_mesa_undef = ir_to_mesa_src_reg(PROGRAM_UNDEFINED, 0, NULL); + +ir_to_mesa_dst_reg ir_to_mesa_undef_dst = { + PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP, COND_TR, NULL, +}; + +ir_to_mesa_dst_reg ir_to_mesa_address_reg = { + PROGRAM_ADDRESS, 0, WRITEMASK_X, COND_TR, NULL +}; + +static void fail_link(struct gl_shader_program *prog, const char *fmt, ...) PRINTFLIKE(2, 3); + +static void fail_link(struct gl_shader_program *prog, const char *fmt, ...) + { + va_list args; + va_start(args, fmt); + prog->InfoLog = talloc_vasprintf_append(prog->InfoLog, fmt, args); + va_end(args); + + prog->LinkStatus = GL_FALSE; + } + +static int swizzle_for_size(int size) +{ + int size_swizzles[4] = { + MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X), + MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y), + MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_Z), + MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W), + }; + + return size_swizzles[size - 1]; +} + +ir_to_mesa_instruction * +ir_to_mesa_visitor::ir_to_mesa_emit_op3(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1, + ir_to_mesa_src_reg src2) +{ + ir_to_mesa_instruction *inst = new(mem_ctx) ir_to_mesa_instruction(); + int num_reladdr = 0; + + /* If we have to do relative addressing, we want to load the ARL + * reg directly for one of the regs, and preload the other reladdr + * sources into temps. + */ + num_reladdr += dst.reladdr != NULL; + num_reladdr += src0.reladdr != NULL; + num_reladdr += src1.reladdr != NULL; + num_reladdr += src2.reladdr != NULL; + + reladdr_to_temp(ir, &src2, &num_reladdr); + reladdr_to_temp(ir, &src1, &num_reladdr); + reladdr_to_temp(ir, &src0, &num_reladdr); + + if (dst.reladdr) { + ir_to_mesa_emit_op1(ir, OPCODE_ARL, ir_to_mesa_address_reg, + *dst.reladdr); + + num_reladdr--; + } + assert(num_reladdr == 0); + + inst->op = op; + inst->dst_reg = dst; + inst->src_reg[0] = src0; + inst->src_reg[1] = src1; + inst->src_reg[2] = src2; + inst->ir = ir; + + inst->function = NULL; + + this->instructions.push_tail(inst); + + return inst; +} + + +ir_to_mesa_instruction * +ir_to_mesa_visitor::ir_to_mesa_emit_op2(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1) +{ + return ir_to_mesa_emit_op3(ir, op, dst, src0, src1, ir_to_mesa_undef); +} + +ir_to_mesa_instruction * +ir_to_mesa_visitor::ir_to_mesa_emit_op1(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0) +{ + assert(dst.writemask != 0); + return ir_to_mesa_emit_op3(ir, op, dst, + src0, ir_to_mesa_undef, ir_to_mesa_undef); +} + +ir_to_mesa_instruction * +ir_to_mesa_visitor::ir_to_mesa_emit_op0(ir_instruction *ir, + enum prog_opcode op) +{ + return ir_to_mesa_emit_op3(ir, op, ir_to_mesa_undef_dst, + ir_to_mesa_undef, + ir_to_mesa_undef, + ir_to_mesa_undef); +} + +inline ir_to_mesa_dst_reg +ir_to_mesa_dst_reg_from_src(ir_to_mesa_src_reg reg) +{ + ir_to_mesa_dst_reg dst_reg; + + dst_reg.file = reg.file; + dst_reg.index = reg.index; + dst_reg.writemask = WRITEMASK_XYZW; + dst_reg.cond_mask = COND_TR; + dst_reg.reladdr = reg.reladdr; + + return dst_reg; +} + +inline ir_to_mesa_src_reg +ir_to_mesa_src_reg_from_dst(ir_to_mesa_dst_reg reg) +{ + return ir_to_mesa_src_reg(reg.file, reg.index, NULL); +} + +/** + * Emits Mesa scalar opcodes to produce unique answers across channels. + * + * Some Mesa opcodes are scalar-only, like ARB_fp/vp. The src X + * channel determines the result across all channels. So to do a vec4 + * of this operation, we want to emit a scalar per source channel used + * to produce dest channels. + */ +void +ir_to_mesa_visitor::ir_to_mesa_emit_scalar_op2(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg orig_src0, + ir_to_mesa_src_reg orig_src1) +{ + int i, j; + int done_mask = ~dst.writemask; + + /* Mesa RCP is a scalar operation splatting results to all channels, + * like ARB_fp/vp. So emit as many RCPs as necessary to cover our + * dst channels. + */ + for (i = 0; i < 4; i++) { + GLuint this_mask = (1 << i); + ir_to_mesa_instruction *inst; + ir_to_mesa_src_reg src0 = orig_src0; + ir_to_mesa_src_reg src1 = orig_src1; + + if (done_mask & this_mask) + continue; + + GLuint src0_swiz = GET_SWZ(src0.swizzle, i); + GLuint src1_swiz = GET_SWZ(src1.swizzle, i); + for (j = i + 1; j < 4; j++) { + if (!(done_mask & (1 << j)) && + GET_SWZ(src0.swizzle, j) == src0_swiz && + GET_SWZ(src1.swizzle, j) == src1_swiz) { + this_mask |= (1 << j); + } + } + src0.swizzle = MAKE_SWIZZLE4(src0_swiz, src0_swiz, + src0_swiz, src0_swiz); + src1.swizzle = MAKE_SWIZZLE4(src1_swiz, src1_swiz, + src1_swiz, src1_swiz); + + inst = ir_to_mesa_emit_op2(ir, op, + dst, + src0, + src1); + inst->dst_reg.writemask = this_mask; + done_mask |= this_mask; + } +} + +void +ir_to_mesa_visitor::ir_to_mesa_emit_scalar_op1(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0) +{ + ir_to_mesa_src_reg undef = ir_to_mesa_undef; + + undef.swizzle = SWIZZLE_XXXX; + + ir_to_mesa_emit_scalar_op2(ir, op, dst, src0, undef); +} + +struct ir_to_mesa_src_reg +ir_to_mesa_visitor::src_reg_for_float(float val) +{ + ir_to_mesa_src_reg src_reg(PROGRAM_CONSTANT, -1, NULL); + + src_reg.index = _mesa_add_unnamed_constant(this->prog->Parameters, + &val, 1, &src_reg.swizzle); + + return src_reg; +} + +static int +type_size(const struct glsl_type *type) +{ + unsigned int i; + int size; + + switch (type->base_type) { + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + case GLSL_TYPE_FLOAT: + case GLSL_TYPE_BOOL: + if (type->is_matrix()) { + return type->matrix_columns; + } else { + /* Regardless of size of vector, it gets a vec4. This is bad + * packing for things like floats, but otherwise arrays become a + * mess. Hopefully a later pass over the code can pack scalars + * down if appropriate. + */ + return 1; + } + case GLSL_TYPE_ARRAY: + return type_size(type->fields.array) * type->length; + case GLSL_TYPE_STRUCT: + size = 0; + for (i = 0; i < type->length; i++) { + size += type_size(type->fields.structure[i].type); + } + return size; + case GLSL_TYPE_SAMPLER: + /* Samplers take up one slot in UNIFORMS[], but they're baked in + * at link time. + */ + return 1; + default: + assert(0); + return 0; + } +} + +/** + * In the initial pass of codegen, we assign temporary numbers to + * intermediate results. (not SSA -- variable assignments will reuse + * storage). Actual register allocation for the Mesa VM occurs in a + * pass over the Mesa IR later. + */ +ir_to_mesa_src_reg +ir_to_mesa_visitor::get_temp(const glsl_type *type) +{ + ir_to_mesa_src_reg src_reg; + int swizzle[4]; + int i; + + src_reg.file = PROGRAM_TEMPORARY; + src_reg.index = next_temp; + src_reg.reladdr = NULL; + next_temp += type_size(type); + + if (type->is_array() || type->is_record()) { + src_reg.swizzle = SWIZZLE_NOOP; + } else { + for (i = 0; i < type->vector_elements; i++) + swizzle[i] = i; + for (; i < 4; i++) + swizzle[i] = type->vector_elements - 1; + src_reg.swizzle = MAKE_SWIZZLE4(swizzle[0], swizzle[1], + swizzle[2], swizzle[3]); + } + src_reg.negate = 0; + + return src_reg; +} + +variable_storage * +ir_to_mesa_visitor::find_variable_storage(ir_variable *var) +{ + + variable_storage *entry; + + foreach_iter(exec_list_iterator, iter, this->variables) { + entry = (variable_storage *)iter.get(); + + if (entry->var == var) + return entry; + } + + return NULL; +} + +struct statevar_element { + const char *field; + int tokens[STATE_LENGTH]; + int swizzle; + bool array_indexed; +}; + +static struct statevar_element gl_DepthRange_elements[] = { + {"near", {STATE_DEPTH_RANGE, 0, 0}, SWIZZLE_XXXX}, + {"far", {STATE_DEPTH_RANGE, 0, 0}, SWIZZLE_YYYY}, + {"diff", {STATE_DEPTH_RANGE, 0, 0}, SWIZZLE_ZZZZ}, +}; + +static struct statevar_element gl_ClipPlane_elements[] = { + {NULL, {STATE_CLIPPLANE, 0, 0}, SWIZZLE_XYZW} +}; + +static struct statevar_element gl_Point_elements[] = { + {"size", {STATE_POINT_SIZE}, SWIZZLE_XXXX}, + {"sizeMin", {STATE_POINT_SIZE}, SWIZZLE_YYYY}, + {"sizeMax", {STATE_POINT_SIZE}, SWIZZLE_ZZZZ}, + {"fadeThresholdSize", {STATE_POINT_SIZE}, SWIZZLE_WWWW}, + {"distanceConstantAttenuation", {STATE_POINT_ATTENUATION}, SWIZZLE_XXXX}, + {"distanceLinearAttenuation", {STATE_POINT_ATTENUATION}, SWIZZLE_YYYY}, + {"distanceQuadraticAttenuation", {STATE_POINT_ATTENUATION}, SWIZZLE_ZZZZ}, +}; + +static struct statevar_element gl_FrontMaterial_elements[] = { + {"emission", {STATE_MATERIAL, 0, STATE_EMISSION}, SWIZZLE_XYZW}, + {"ambient", {STATE_MATERIAL, 0, STATE_AMBIENT}, SWIZZLE_XYZW}, + {"diffuse", {STATE_MATERIAL, 0, STATE_DIFFUSE}, SWIZZLE_XYZW}, + {"specular", {STATE_MATERIAL, 0, STATE_SPECULAR}, SWIZZLE_XYZW}, + {"shininess", {STATE_MATERIAL, 0, STATE_SHININESS}, SWIZZLE_XXXX}, +}; + +static struct statevar_element gl_BackMaterial_elements[] = { + {"emission", {STATE_MATERIAL, 1, STATE_EMISSION}, SWIZZLE_XYZW}, + {"ambient", {STATE_MATERIAL, 1, STATE_AMBIENT}, SWIZZLE_XYZW}, + {"diffuse", {STATE_MATERIAL, 1, STATE_DIFFUSE}, SWIZZLE_XYZW}, + {"specular", {STATE_MATERIAL, 1, STATE_SPECULAR}, SWIZZLE_XYZW}, + {"shininess", {STATE_MATERIAL, 1, STATE_SHININESS}, SWIZZLE_XXXX}, +}; + +static struct statevar_element gl_LightSource_elements[] = { + {"ambient", {STATE_LIGHT, 0, STATE_AMBIENT}, SWIZZLE_XYZW}, + {"diffuse", {STATE_LIGHT, 0, STATE_DIFFUSE}, SWIZZLE_XYZW}, + {"specular", {STATE_LIGHT, 0, STATE_SPECULAR}, SWIZZLE_XYZW}, + {"position", {STATE_LIGHT, 0, STATE_POSITION}, SWIZZLE_XYZW}, + {"halfVector", {STATE_LIGHT, 0, STATE_HALF_VECTOR}, SWIZZLE_XYZW}, + {"spotDirection", {STATE_LIGHT, 0, STATE_SPOT_DIRECTION}, SWIZZLE_XYZW}, + {"spotCosCutoff", {STATE_LIGHT, 0, STATE_SPOT_DIRECTION}, SWIZZLE_WWWW}, + {"spotCutoff", {STATE_LIGHT, 0, STATE_SPOT_CUTOFF}, SWIZZLE_XXXX}, + {"spotExponent", {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_WWWW}, + {"constantAttenuation", {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_XXXX}, + {"linearAttenuation", {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_YYYY}, + {"quadraticAttenuation", {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_ZZZZ}, +}; + +static struct statevar_element gl_LightModel_elements[] = { + {"ambient", {STATE_LIGHTMODEL_AMBIENT, 0}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_FrontLightModelProduct_elements[] = { + {"sceneColor", {STATE_LIGHTMODEL_SCENECOLOR, 0}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_BackLightModelProduct_elements[] = { + {"sceneColor", {STATE_LIGHTMODEL_SCENECOLOR, 1}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_FrontLightProduct_elements[] = { + {"ambient", {STATE_LIGHTPROD, 0, 0, STATE_AMBIENT}, SWIZZLE_XYZW}, + {"diffuse", {STATE_LIGHTPROD, 0, 0, STATE_DIFFUSE}, SWIZZLE_XYZW}, + {"specular", {STATE_LIGHTPROD, 0, 0, STATE_SPECULAR}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_BackLightProduct_elements[] = { + {"ambient", {STATE_LIGHTPROD, 0, 1, STATE_AMBIENT}, SWIZZLE_XYZW}, + {"diffuse", {STATE_LIGHTPROD, 0, 1, STATE_DIFFUSE}, SWIZZLE_XYZW}, + {"specular", {STATE_LIGHTPROD, 0, 1, STATE_SPECULAR}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_TextureEnvColor_elements[] = { + {NULL, {STATE_TEXENV_COLOR, 0}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_EyePlaneS_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_S}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_EyePlaneT_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_T}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_EyePlaneR_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_R}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_EyePlaneQ_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_Q}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_ObjectPlaneS_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_S}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_ObjectPlaneT_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_T}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_ObjectPlaneR_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_R}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_ObjectPlaneQ_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_Q}, SWIZZLE_XYZW}, +}; + +static struct statevar_element gl_Fog_elements[] = { + {"color", {STATE_FOG_COLOR}, SWIZZLE_XYZW}, + {"density", {STATE_FOG_PARAMS}, SWIZZLE_XXXX}, + {"start", {STATE_FOG_PARAMS}, SWIZZLE_YYYY}, + {"end", {STATE_FOG_PARAMS}, SWIZZLE_ZZZZ}, + {"scale", {STATE_FOG_PARAMS}, SWIZZLE_WWWW}, +}; + +#define MATRIX(name, statevar, modifier) \ + static struct statevar_element name ## _elements[] = { \ + { NULL, { statevar, 0, 0, 0, modifier}, SWIZZLE_XYZW }, \ + { NULL, { statevar, 0, 1, 1, modifier}, SWIZZLE_XYZW }, \ + { NULL, { statevar, 0, 2, 2, modifier}, SWIZZLE_XYZW }, \ + { NULL, { statevar, 0, 3, 3, modifier}, SWIZZLE_XYZW }, \ + } + +MATRIX(gl_ModelViewMatrix, + STATE_MODELVIEW_MATRIX, STATE_MATRIX_TRANSPOSE); +MATRIX(gl_ModelViewMatrixInverse, + STATE_MODELVIEW_MATRIX, STATE_MATRIX_INVTRANS); +MATRIX(gl_ModelViewMatrixTranspose, + STATE_MODELVIEW_MATRIX, 0); +MATRIX(gl_ModelViewMatrixInverseTranspose, + STATE_MODELVIEW_MATRIX, STATE_MATRIX_INVERSE); + +MATRIX(gl_ProjectionMatrix, + STATE_PROJECTION_MATRIX, STATE_MATRIX_TRANSPOSE); +MATRIX(gl_ProjectionMatrixInverse, + STATE_PROJECTION_MATRIX, STATE_MATRIX_INVTRANS); +MATRIX(gl_ProjectionMatrixTranspose, + STATE_PROJECTION_MATRIX, 0); +MATRIX(gl_ProjectionMatrixInverseTranspose, + STATE_PROJECTION_MATRIX, STATE_MATRIX_INVERSE); + +MATRIX(gl_ModelViewProjectionMatrix, + STATE_MVP_MATRIX, STATE_MATRIX_TRANSPOSE); +MATRIX(gl_ModelViewProjectionMatrixInverse, + STATE_MVP_MATRIX, STATE_MATRIX_INVTRANS); +MATRIX(gl_ModelViewProjectionMatrixTranspose, + STATE_MVP_MATRIX, 0); +MATRIX(gl_ModelViewProjectionMatrixInverseTranspose, + STATE_MVP_MATRIX, STATE_MATRIX_INVERSE); + +MATRIX(gl_TextureMatrix, + STATE_TEXTURE_MATRIX, STATE_MATRIX_TRANSPOSE); +MATRIX(gl_TextureMatrixInverse, + STATE_TEXTURE_MATRIX, STATE_MATRIX_INVTRANS); +MATRIX(gl_TextureMatrixTranspose, + STATE_TEXTURE_MATRIX, 0); +MATRIX(gl_TextureMatrixInverseTranspose, + STATE_TEXTURE_MATRIX, STATE_MATRIX_INVERSE); + +static struct statevar_element gl_NormalMatrix_elements[] = { + { NULL, { STATE_MODELVIEW_MATRIX, 0, 0, 0, STATE_MATRIX_INVERSE}, + SWIZZLE_XYZW }, + { NULL, { STATE_MODELVIEW_MATRIX, 0, 1, 1, STATE_MATRIX_INVERSE}, + SWIZZLE_XYZW }, + { NULL, { STATE_MODELVIEW_MATRIX, 0, 2, 2, STATE_MATRIX_INVERSE}, + SWIZZLE_XYZW }, +}; + +#undef MATRIX + +#define STATEVAR(name) {#name, name ## _elements, Elements(name ## _elements)} + +static struct { + const char *name; + struct statevar_element *elements; + int num_elements; +} statevars[] = { + STATEVAR(gl_DepthRange), + STATEVAR(gl_ClipPlane), + STATEVAR(gl_Point), + STATEVAR(gl_FrontMaterial), + STATEVAR(gl_BackMaterial), + STATEVAR(gl_LightSource), + STATEVAR(gl_LightModel), + STATEVAR(gl_FrontLightModelProduct), + STATEVAR(gl_BackLightModelProduct), + STATEVAR(gl_FrontLightProduct), + STATEVAR(gl_BackLightProduct), + STATEVAR(gl_TextureEnvColor), + STATEVAR(gl_EyePlaneS), + STATEVAR(gl_EyePlaneT), + STATEVAR(gl_EyePlaneR), + STATEVAR(gl_EyePlaneQ), + STATEVAR(gl_ObjectPlaneS), + STATEVAR(gl_ObjectPlaneT), + STATEVAR(gl_ObjectPlaneR), + STATEVAR(gl_ObjectPlaneQ), + STATEVAR(gl_Fog), + + STATEVAR(gl_ModelViewMatrix), + STATEVAR(gl_ModelViewMatrixInverse), + STATEVAR(gl_ModelViewMatrixTranspose), + STATEVAR(gl_ModelViewMatrixInverseTranspose), + + STATEVAR(gl_ProjectionMatrix), + STATEVAR(gl_ProjectionMatrixInverse), + STATEVAR(gl_ProjectionMatrixTranspose), + STATEVAR(gl_ProjectionMatrixInverseTranspose), + + STATEVAR(gl_ModelViewProjectionMatrix), + STATEVAR(gl_ModelViewProjectionMatrixInverse), + STATEVAR(gl_ModelViewProjectionMatrixTranspose), + STATEVAR(gl_ModelViewProjectionMatrixInverseTranspose), + + STATEVAR(gl_TextureMatrix), + STATEVAR(gl_TextureMatrixInverse), + STATEVAR(gl_TextureMatrixTranspose), + STATEVAR(gl_TextureMatrixInverseTranspose), + + STATEVAR(gl_NormalMatrix), +}; + +void +ir_to_mesa_visitor::visit(ir_variable *ir) +{ + if (strcmp(ir->name, "gl_FragCoord") == 0) { + struct gl_fragment_program *fp = (struct gl_fragment_program *)this->prog; + + fp->OriginUpperLeft = ir->origin_upper_left; + fp->PixelCenterInteger = ir->pixel_center_integer; + } + + if (ir->mode == ir_var_uniform && strncmp(ir->name, "gl_", 3) == 0) { + unsigned int i; + + struct variable_storage *entry; + entry = new(mem_ctx) variable_storage(ir, PROGRAM_TEMPORARY, + this->next_temp); + this->variables.push_tail(entry); + this->next_temp += type_size(ir->type); + + for (i = 0; i < Elements(statevars); i++) { + if (strcmp(ir->name, statevars[i].name) == 0) + break; + } + + if (i == Elements(statevars)) { + fail_link(this->shader_program, + "Failed to find builtin uniform `%s'\n", ir->name); + return; + } + + ir_to_mesa_dst_reg dst = + ir_to_mesa_dst_reg_from_src(ir_to_mesa_src_reg(PROGRAM_TEMPORARY, + entry->index, NULL)); + + int array_count; + if (ir->type->is_array()) { + array_count = ir->type->length; + } else { + array_count = 1; + } + + for (int a = 0; a < array_count; a++) { + for (int j = 0; j < statevars[i].num_elements; j++) { + struct statevar_element *element = &statevars[i].elements[j]; + int tokens[STATE_LENGTH]; + + memcpy(tokens, element->tokens, sizeof(element->tokens)); + if (ir->type->is_array()) { + tokens[1] = a; + } + + int index = _mesa_add_state_reference(this->prog->Parameters, + (gl_state_index *)tokens); + ir_to_mesa_src_reg src(PROGRAM_STATE_VAR, index, NULL); + src.swizzle = element->swizzle; + ir_to_mesa_emit_op1(ir, OPCODE_MOV, dst, src); + /* even a float takes up a whole vec4 reg in a struct/array. */ + dst.index++; + } + } + if (dst.index != entry->index + type_size(ir->type)) { + fail_link(this->shader_program, + "failed to load builtin uniform `%s' (%d/%d regs loaded)\n", + ir->name, dst.index - entry->index, + type_size(ir->type)); + } + } +} + +void +ir_to_mesa_visitor::visit(ir_loop *ir) +{ + assert(!ir->from); + assert(!ir->to); + assert(!ir->increment); + assert(!ir->counter); + + ir_to_mesa_emit_op0(NULL, OPCODE_BGNLOOP); + visit_exec_list(&ir->body_instructions, this); + ir_to_mesa_emit_op0(NULL, OPCODE_ENDLOOP); +} + +void +ir_to_mesa_visitor::visit(ir_loop_jump *ir) +{ + switch (ir->mode) { + case ir_loop_jump::jump_break: + ir_to_mesa_emit_op0(NULL, OPCODE_BRK); + break; + case ir_loop_jump::jump_continue: + ir_to_mesa_emit_op0(NULL, OPCODE_CONT); + break; + } +} + + +void +ir_to_mesa_visitor::visit(ir_function_signature *ir) +{ + assert(0); + (void)ir; +} + +void +ir_to_mesa_visitor::visit(ir_function *ir) +{ + /* Ignore function bodies other than main() -- we shouldn't see calls to + * them since they should all be inlined before we get to ir_to_mesa. + */ + if (strcmp(ir->name, "main") == 0) { + const ir_function_signature *sig; + exec_list empty; + + sig = ir->matching_signature(&empty); + + assert(sig); + + foreach_iter(exec_list_iterator, iter, sig->body) { + ir_instruction *ir = (ir_instruction *)iter.get(); + + ir->accept(this); + } + } +} + +GLboolean +ir_to_mesa_visitor::try_emit_mad(ir_expression *ir, int mul_operand) +{ + int nonmul_operand = 1 - mul_operand; + ir_to_mesa_src_reg a, b, c; + + ir_expression *expr = ir->operands[mul_operand]->as_expression(); + if (!expr || expr->operation != ir_binop_mul) + return false; + + expr->operands[0]->accept(this); + a = this->result; + expr->operands[1]->accept(this); + b = this->result; + ir->operands[nonmul_operand]->accept(this); + c = this->result; + + this->result = get_temp(ir->type); + ir_to_mesa_emit_op3(ir, OPCODE_MAD, + ir_to_mesa_dst_reg_from_src(this->result), a, b, c); + + return true; +} + +void +ir_to_mesa_visitor::reladdr_to_temp(ir_instruction *ir, + ir_to_mesa_src_reg *reg, int *num_reladdr) +{ + if (!reg->reladdr) + return; + + ir_to_mesa_emit_op1(ir, OPCODE_ARL, ir_to_mesa_address_reg, *reg->reladdr); + + if (*num_reladdr != 1) { + ir_to_mesa_src_reg temp = get_temp(glsl_type::vec4_type); + + ir_to_mesa_emit_op1(ir, OPCODE_MOV, + ir_to_mesa_dst_reg_from_src(temp), *reg); + *reg = temp; + } + + (*num_reladdr)--; +} + +void +ir_to_mesa_visitor::visit(ir_expression *ir) +{ + unsigned int operand; + struct ir_to_mesa_src_reg op[2]; + struct ir_to_mesa_src_reg result_src; + struct ir_to_mesa_dst_reg result_dst; + const glsl_type *vec4_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 4, 1); + const glsl_type *vec3_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 3, 1); + const glsl_type *vec2_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 2, 1); + + /* Quick peephole: Emit OPCODE_MAD(a, b, c) instead of ADD(MUL(a, b), c) + */ + if (ir->operation == ir_binop_add) { + if (try_emit_mad(ir, 1)) + return; + if (try_emit_mad(ir, 0)) + return; + } + + for (operand = 0; operand < ir->get_num_operands(); operand++) { + this->result.file = PROGRAM_UNDEFINED; + ir->operands[operand]->accept(this); + if (this->result.file == PROGRAM_UNDEFINED) { + ir_print_visitor v; + printf("Failed to get tree for expression operand:\n"); + ir->operands[operand]->accept(&v); + exit(1); + } + op[operand] = this->result; + + /* Matrix expression operands should have been broken down to vector + * operations already. + */ + assert(!ir->operands[operand]->type->is_matrix()); + } + + this->result.file = PROGRAM_UNDEFINED; + + /* Storage for our result. Ideally for an assignment we'd be using + * the actual storage for the result here, instead. + */ + result_src = get_temp(ir->type); + /* convenience for the emit functions below. */ + result_dst = ir_to_mesa_dst_reg_from_src(result_src); + /* Limit writes to the channels that will be used by result_src later. + * This does limit this temp's use as a temporary for multi-instruction + * sequences. + */ + result_dst.writemask = (1 << ir->type->vector_elements) - 1; + + switch (ir->operation) { + case ir_unop_logic_not: + ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst, + op[0], src_reg_for_float(0.0)); + break; + case ir_unop_neg: + op[0].negate = ~op[0].negate; + result_src = op[0]; + break; + case ir_unop_abs: + ir_to_mesa_emit_op1(ir, OPCODE_ABS, result_dst, op[0]); + break; + case ir_unop_sign: + ir_to_mesa_emit_op1(ir, OPCODE_SSG, result_dst, op[0]); + break; + case ir_unop_rcp: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_RCP, result_dst, op[0]); + break; + + case ir_unop_exp2: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_EX2, result_dst, op[0]); + break; + case ir_unop_exp: + case ir_unop_log: + assert(!"not reached: should be handled by ir_explog_to_explog2"); + break; + case ir_unop_log2: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_LG2, result_dst, op[0]); + break; + case ir_unop_sin: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_SIN, result_dst, op[0]); + break; + case ir_unop_cos: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_COS, result_dst, op[0]); + break; + + case ir_unop_dFdx: + ir_to_mesa_emit_op1(ir, OPCODE_DDX, result_dst, op[0]); + break; + case ir_unop_dFdy: + ir_to_mesa_emit_op1(ir, OPCODE_DDY, result_dst, op[0]); + break; + + case ir_binop_add: + ir_to_mesa_emit_op2(ir, OPCODE_ADD, result_dst, op[0], op[1]); + break; + case ir_binop_sub: + ir_to_mesa_emit_op2(ir, OPCODE_SUB, result_dst, op[0], op[1]); + break; + + case ir_binop_mul: + ir_to_mesa_emit_op2(ir, OPCODE_MUL, result_dst, op[0], op[1]); + break; + case ir_binop_div: + assert(!"not reached: should be handled by ir_div_to_mul_rcp"); + case ir_binop_mod: + assert(!"ir_binop_mod should have been converted to b * fract(a/b)"); + break; + + case ir_binop_less: + ir_to_mesa_emit_op2(ir, OPCODE_SLT, result_dst, op[0], op[1]); + break; + case ir_binop_greater: + ir_to_mesa_emit_op2(ir, OPCODE_SGT, result_dst, op[0], op[1]); + break; + case ir_binop_lequal: + ir_to_mesa_emit_op2(ir, OPCODE_SLE, result_dst, op[0], op[1]); + break; + case ir_binop_gequal: + ir_to_mesa_emit_op2(ir, OPCODE_SGE, result_dst, op[0], op[1]); + break; + case ir_binop_equal: + /* "==" operator producing a scalar boolean. */ + if (ir->operands[0]->type->is_vector() || + ir->operands[1]->type->is_vector()) { + ir_to_mesa_src_reg temp = get_temp(glsl_type::vec4_type); + ir_to_mesa_emit_op2(ir, OPCODE_SNE, + ir_to_mesa_dst_reg_from_src(temp), op[0], op[1]); + ir_to_mesa_emit_op2(ir, OPCODE_DP4, result_dst, temp, temp); + ir_to_mesa_emit_op2(ir, OPCODE_SEQ, + result_dst, result_src, src_reg_for_float(0.0)); + } else { + ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst, op[0], op[1]); + } + break; + case ir_binop_nequal: + /* "!=" operator producing a scalar boolean. */ + if (ir->operands[0]->type->is_vector() || + ir->operands[1]->type->is_vector()) { + ir_to_mesa_src_reg temp = get_temp(glsl_type::vec4_type); + ir_to_mesa_emit_op2(ir, OPCODE_SNE, + ir_to_mesa_dst_reg_from_src(temp), op[0], op[1]); + ir_to_mesa_emit_op2(ir, OPCODE_DP4, result_dst, temp, temp); + ir_to_mesa_emit_op2(ir, OPCODE_SNE, + result_dst, result_src, src_reg_for_float(0.0)); + } else { + ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, op[0], op[1]); + } + break; + + case ir_unop_any: + switch (ir->operands[0]->type->vector_elements) { + case 4: + ir_to_mesa_emit_op2(ir, OPCODE_DP4, result_dst, op[0], op[0]); + break; + case 3: + ir_to_mesa_emit_op2(ir, OPCODE_DP3, result_dst, op[0], op[0]); + break; + case 2: + ir_to_mesa_emit_op2(ir, OPCODE_DP2, result_dst, op[0], op[0]); + break; + default: + assert(!"unreached: ir_unop_any of non-bvec"); + break; + } + ir_to_mesa_emit_op2(ir, OPCODE_SNE, + result_dst, result_src, src_reg_for_float(0.0)); + break; + + case ir_binop_logic_xor: + ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, op[0], op[1]); + break; + + case ir_binop_logic_or: + /* This could be a saturated add and skip the SNE. */ + ir_to_mesa_emit_op2(ir, OPCODE_ADD, + result_dst, + op[0], op[1]); + + ir_to_mesa_emit_op2(ir, OPCODE_SNE, + result_dst, + result_src, src_reg_for_float(0.0)); + break; + + case ir_binop_logic_and: + /* the bool args are stored as float 0.0 or 1.0, so "mul" gives us "and". */ + ir_to_mesa_emit_op2(ir, OPCODE_MUL, + result_dst, + op[0], op[1]); + break; + + case ir_binop_dot: + if (ir->operands[0]->type == vec4_type) { + assert(ir->operands[1]->type == vec4_type); + ir_to_mesa_emit_op2(ir, OPCODE_DP4, + result_dst, + op[0], op[1]); + } else if (ir->operands[0]->type == vec3_type) { + assert(ir->operands[1]->type == vec3_type); + ir_to_mesa_emit_op2(ir, OPCODE_DP3, + result_dst, + op[0], op[1]); + } else if (ir->operands[0]->type == vec2_type) { + assert(ir->operands[1]->type == vec2_type); + ir_to_mesa_emit_op2(ir, OPCODE_DP2, + result_dst, + op[0], op[1]); + } + break; + + case ir_binop_cross: + ir_to_mesa_emit_op2(ir, OPCODE_XPD, result_dst, op[0], op[1]); + break; + + case ir_unop_sqrt: + /* sqrt(x) = x * rsq(x). */ + ir_to_mesa_emit_scalar_op1(ir, OPCODE_RSQ, result_dst, op[0]); + ir_to_mesa_emit_op2(ir, OPCODE_MUL, result_dst, result_src, op[0]); + /* For incoming channels <= 0, set the result to 0. */ + op[0].negate = ~op[0].negate; + ir_to_mesa_emit_op3(ir, OPCODE_CMP, result_dst, + op[0], result_src, src_reg_for_float(0.0)); + break; + case ir_unop_rsq: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_RSQ, result_dst, op[0]); + break; + case ir_unop_i2f: + case ir_unop_b2f: + case ir_unop_b2i: + /* Mesa IR lacks types, ints are stored as truncated floats. */ + result_src = op[0]; + break; + case ir_unop_f2i: + ir_to_mesa_emit_op1(ir, OPCODE_TRUNC, result_dst, op[0]); + break; + case ir_unop_f2b: + case ir_unop_i2b: + ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, + op[0], src_reg_for_float(0.0)); + break; + case ir_unop_trunc: + ir_to_mesa_emit_op1(ir, OPCODE_TRUNC, result_dst, op[0]); + break; + case ir_unop_ceil: + op[0].negate = ~op[0].negate; + ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]); + result_src.negate = ~result_src.negate; + break; + case ir_unop_floor: + ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]); + break; + case ir_unop_fract: + ir_to_mesa_emit_op1(ir, OPCODE_FRC, result_dst, op[0]); + break; + + case ir_binop_min: + ir_to_mesa_emit_op2(ir, OPCODE_MIN, result_dst, op[0], op[1]); + break; + case ir_binop_max: + ir_to_mesa_emit_op2(ir, OPCODE_MAX, result_dst, op[0], op[1]); + break; + case ir_binop_pow: + ir_to_mesa_emit_scalar_op2(ir, OPCODE_POW, result_dst, op[0], op[1]); + break; + + case ir_unop_bit_not: + case ir_unop_u2f: + case ir_binop_lshift: + case ir_binop_rshift: + case ir_binop_bit_and: + case ir_binop_bit_xor: + case ir_binop_bit_or: + assert(!"GLSL 1.30 features unsupported"); + break; + } + + this->result = result_src; +} + + +void +ir_to_mesa_visitor::visit(ir_swizzle *ir) +{ + ir_to_mesa_src_reg src_reg; + int i; + int swizzle[4]; + + /* Note that this is only swizzles in expressions, not those on the left + * hand side of an assignment, which do write masking. See ir_assignment + * for that. + */ + + ir->val->accept(this); + src_reg = this->result; + assert(src_reg.file != PROGRAM_UNDEFINED); + + for (i = 0; i < 4; i++) { + if (i < ir->type->vector_elements) { + switch (i) { + case 0: + swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.x); + break; + case 1: + swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.y); + break; + case 2: + swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.z); + break; + case 3: + swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.w); + break; + } + } else { + /* If the type is smaller than a vec4, replicate the last + * channel out. + */ + swizzle[i] = swizzle[ir->type->vector_elements - 1]; + } + } + + src_reg.swizzle = MAKE_SWIZZLE4(swizzle[0], + swizzle[1], + swizzle[2], + swizzle[3]); + + this->result = src_reg; +} + +void +ir_to_mesa_visitor::visit(ir_dereference_variable *ir) +{ + variable_storage *entry = find_variable_storage(ir->var); + + if (!entry) { + switch (ir->var->mode) { + case ir_var_uniform: + entry = new(mem_ctx) variable_storage(ir->var, PROGRAM_UNIFORM, + ir->var->location); + this->variables.push_tail(entry); + break; + case ir_var_in: + case ir_var_out: + case ir_var_inout: + /* The linker assigns locations for varyings and attributes, + * including deprecated builtins (like gl_Color), user-assign + * generic attributes (glBindVertexLocation), and + * user-defined varyings. + * + * FINISHME: We would hit this path for function arguments. Fix! + */ + assert(ir->var->location != -1); + if (ir->var->mode == ir_var_in || + ir->var->mode == ir_var_inout) { + entry = new(mem_ctx) variable_storage(ir->var, + PROGRAM_INPUT, + ir->var->location); + + if (this->prog->Target == GL_VERTEX_PROGRAM_ARB && + ir->var->location >= VERT_ATTRIB_GENERIC0) { + _mesa_add_attribute(prog->Attributes, + ir->var->name, + _mesa_sizeof_glsl_type(ir->var->type->gl_type), + ir->var->type->gl_type, + ir->var->location - VERT_ATTRIB_GENERIC0); + } + } else { + entry = new(mem_ctx) variable_storage(ir->var, + PROGRAM_OUTPUT, + ir->var->location); + } + + break; + case ir_var_auto: + case ir_var_temporary: + entry = new(mem_ctx) variable_storage(ir->var, PROGRAM_TEMPORARY, + this->next_temp); + this->variables.push_tail(entry); + + next_temp += type_size(ir->var->type); + break; + } + + if (!entry) { + printf("Failed to make storage for %s\n", ir->var->name); + exit(1); + } + } + + this->result = ir_to_mesa_src_reg(entry->file, entry->index, ir->var->type); +} + +void +ir_to_mesa_visitor::visit(ir_dereference_array *ir) +{ + ir_constant *index; + ir_to_mesa_src_reg src_reg; + int element_size = type_size(ir->type); + + index = ir->array_index->constant_expression_value(); + + ir->array->accept(this); + src_reg = this->result; + + if (index) { + src_reg.index += index->value.i[0] * element_size; + } else { + ir_to_mesa_src_reg array_base = this->result; + /* Variable index array dereference. It eats the "vec4" of the + * base of the array and an index that offsets the Mesa register + * index. + */ + ir->array_index->accept(this); + + ir_to_mesa_src_reg index_reg; + + if (element_size == 1) { + index_reg = this->result; + } else { + index_reg = get_temp(glsl_type::float_type); + + ir_to_mesa_emit_op2(ir, OPCODE_MUL, + ir_to_mesa_dst_reg_from_src(index_reg), + this->result, src_reg_for_float(element_size)); + } + + src_reg.reladdr = talloc(mem_ctx, ir_to_mesa_src_reg); + memcpy(src_reg.reladdr, &index_reg, sizeof(index_reg)); + } + + /* If the type is smaller than a vec4, replicate the last channel out. */ + if (ir->type->is_scalar() || ir->type->is_vector()) + src_reg.swizzle = swizzle_for_size(ir->type->vector_elements); + else + src_reg.swizzle = SWIZZLE_NOOP; + + this->result = src_reg; +} + +void +ir_to_mesa_visitor::visit(ir_dereference_record *ir) +{ + unsigned int i; + const glsl_type *struct_type = ir->record->type; + int offset = 0; + + ir->record->accept(this); + + for (i = 0; i < struct_type->length; i++) { + if (strcmp(struct_type->fields.structure[i].name, ir->field) == 0) + break; + offset += type_size(struct_type->fields.structure[i].type); + } + this->result.swizzle = swizzle_for_size(ir->type->vector_elements); + this->result.index += offset; +} + +/** + * We want to be careful in assignment setup to hit the actual storage + * instead of potentially using a temporary like we might with the + * ir_dereference handler. + */ +static struct ir_to_mesa_dst_reg +get_assignment_lhs(ir_dereference *ir, ir_to_mesa_visitor *v) +{ + /* The LHS must be a dereference. If the LHS is a variable indexed array + * access of a vector, it must be separated into a series conditional moves + * before reaching this point (see ir_vec_index_to_cond_assign). + */ + assert(ir->as_dereference()); + ir_dereference_array *deref_array = ir->as_dereference_array(); + if (deref_array) { + assert(!deref_array->array->type->is_vector()); + } + + /* Use the rvalue deref handler for the most part. We'll ignore + * swizzles in it and write swizzles using writemask, though. + */ + ir->accept(v); + return ir_to_mesa_dst_reg_from_src(v->result); +} + +void +ir_to_mesa_visitor::visit(ir_assignment *ir) +{ + struct ir_to_mesa_dst_reg l; + struct ir_to_mesa_src_reg r; + int i; + + ir->rhs->accept(this); + r = this->result; + + l = get_assignment_lhs(ir->lhs, this); + + /* FINISHME: This should really set to the correct maximal writemask for each + * FINISHME: component written (in the loops below). This case can only + * FINISHME: occur for matrices, arrays, and structures. + */ + if (ir->write_mask == 0) { + assert(!ir->lhs->type->is_scalar() && !ir->lhs->type->is_vector()); + l.writemask = WRITEMASK_XYZW; + } else if (ir->lhs->type->is_scalar()) { + /* FINISHME: This hack makes writing to gl_FragData, which lives in the + * FINISHME: W component of fragment shader output zero, work correctly. + */ + l.writemask = WRITEMASK_XYZW; + } else { + assert(ir->lhs->type->is_vector()); + l.writemask = ir->write_mask; + } + + assert(l.file != PROGRAM_UNDEFINED); + assert(r.file != PROGRAM_UNDEFINED); + + if (ir->condition) { + ir_to_mesa_src_reg condition; + + ir->condition->accept(this); + condition = this->result; + + /* We use the OPCODE_CMP (a < 0 ? b : c) for conditional moves, + * and the condition we produced is 0.0 or 1.0. By flipping the + * sign, we can choose which value OPCODE_CMP produces without + * an extra computing the condition. + */ + condition.negate = ~condition.negate; + for (i = 0; i < type_size(ir->lhs->type); i++) { + ir_to_mesa_emit_op3(ir, OPCODE_CMP, l, + condition, r, ir_to_mesa_src_reg_from_dst(l)); + l.index++; + r.index++; + } + } else { + for (i = 0; i < type_size(ir->lhs->type); i++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); + l.index++; + r.index++; + } + } +} + + +void +ir_to_mesa_visitor::visit(ir_constant *ir) +{ + ir_to_mesa_src_reg src_reg; + GLfloat stack_vals[4] = { 0 }; + GLfloat *values = stack_vals; + unsigned int i; + + /* Unfortunately, 4 floats is all we can get into + * _mesa_add_unnamed_constant. So, make a temp to store an + * aggregate constant and move each constant value into it. If we + * get lucky, copy propagation will eliminate the extra moves. + */ + + if (ir->type->base_type == GLSL_TYPE_STRUCT) { + ir_to_mesa_src_reg temp_base = get_temp(ir->type); + ir_to_mesa_dst_reg temp = ir_to_mesa_dst_reg_from_src(temp_base); + + foreach_iter(exec_list_iterator, iter, ir->components) { + ir_constant *field_value = (ir_constant *)iter.get(); + int size = type_size(field_value->type); + + assert(size > 0); + + field_value->accept(this); + src_reg = this->result; + + for (i = 0; i < (unsigned int)size; i++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, temp, src_reg); + + src_reg.index++; + temp.index++; + } + } + this->result = temp_base; + return; + } + + if (ir->type->is_array()) { + ir_to_mesa_src_reg temp_base = get_temp(ir->type); + ir_to_mesa_dst_reg temp = ir_to_mesa_dst_reg_from_src(temp_base); + int size = type_size(ir->type->fields.array); + + assert(size > 0); + + for (i = 0; i < ir->type->length; i++) { + ir->array_elements[i]->accept(this); + src_reg = this->result; + for (int j = 0; j < size; j++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, temp, src_reg); + + src_reg.index++; + temp.index++; + } + } + this->result = temp_base; + return; + } + + if (ir->type->is_matrix()) { + ir_to_mesa_src_reg mat = get_temp(ir->type); + ir_to_mesa_dst_reg mat_column = ir_to_mesa_dst_reg_from_src(mat); + + for (i = 0; i < ir->type->matrix_columns; i++) { + assert(ir->type->base_type == GLSL_TYPE_FLOAT); + values = &ir->value.f[i * ir->type->vector_elements]; + + src_reg = ir_to_mesa_src_reg(PROGRAM_CONSTANT, -1, NULL); + src_reg.index = _mesa_add_unnamed_constant(this->prog->Parameters, + values, + ir->type->vector_elements, + &src_reg.swizzle); + ir_to_mesa_emit_op1(ir, OPCODE_MOV, mat_column, src_reg); + + mat_column.index++; + } + + this->result = mat; + return; + } + + src_reg.file = PROGRAM_CONSTANT; + switch (ir->type->base_type) { + case GLSL_TYPE_FLOAT: + values = &ir->value.f[0]; + break; + case GLSL_TYPE_UINT: + for (i = 0; i < ir->type->vector_elements; i++) { + values[i] = ir->value.u[i]; + } + break; + case GLSL_TYPE_INT: + for (i = 0; i < ir->type->vector_elements; i++) { + values[i] = ir->value.i[i]; + } + break; + case GLSL_TYPE_BOOL: + for (i = 0; i < ir->type->vector_elements; i++) { + values[i] = ir->value.b[i]; + } + break; + default: + assert(!"Non-float/uint/int/bool constant"); + } + + this->result = ir_to_mesa_src_reg(PROGRAM_CONSTANT, -1, ir->type); + this->result.index = _mesa_add_unnamed_constant(this->prog->Parameters, + values, + ir->type->vector_elements, + &this->result.swizzle); +} + +function_entry * +ir_to_mesa_visitor::get_function_signature(ir_function_signature *sig) +{ + function_entry *entry; + + foreach_iter(exec_list_iterator, iter, this->function_signatures) { + entry = (function_entry *)iter.get(); + + if (entry->sig == sig) + return entry; + } + + entry = talloc(mem_ctx, function_entry); + entry->sig = sig; + entry->sig_id = this->next_signature_id++; + entry->bgn_inst = NULL; + + /* Allocate storage for all the parameters. */ + foreach_iter(exec_list_iterator, iter, sig->parameters) { + ir_variable *param = (ir_variable *)iter.get(); + variable_storage *storage; + + storage = find_variable_storage(param); + assert(!storage); + + storage = new(mem_ctx) variable_storage(param, PROGRAM_TEMPORARY, + this->next_temp); + this->variables.push_tail(storage); + + this->next_temp += type_size(param->type); + } + + if (!sig->return_type->is_void()) { + entry->return_reg = get_temp(sig->return_type); + } else { + entry->return_reg = ir_to_mesa_undef; + } + + this->function_signatures.push_tail(entry); + return entry; +} + +void +ir_to_mesa_visitor::visit(ir_call *ir) +{ + ir_to_mesa_instruction *call_inst; + ir_function_signature *sig = ir->get_callee(); + function_entry *entry = get_function_signature(sig); + int i; + + /* Process in parameters. */ + exec_list_iterator sig_iter = sig->parameters.iterator(); + foreach_iter(exec_list_iterator, iter, *ir) { + ir_rvalue *param_rval = (ir_rvalue *)iter.get(); + ir_variable *param = (ir_variable *)sig_iter.get(); + + if (param->mode == ir_var_in || + param->mode == ir_var_inout) { + variable_storage *storage = find_variable_storage(param); + assert(storage); + + param_rval->accept(this); + ir_to_mesa_src_reg r = this->result; + + ir_to_mesa_dst_reg l; + l.file = storage->file; + l.index = storage->index; + l.reladdr = NULL; + l.writemask = WRITEMASK_XYZW; + l.cond_mask = COND_TR; + + for (i = 0; i < type_size(param->type); i++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); + l.index++; + r.index++; + } + } + + sig_iter.next(); + } + assert(!sig_iter.has_next()); + + /* Emit call instruction */ + call_inst = ir_to_mesa_emit_op1(ir, OPCODE_CAL, + ir_to_mesa_undef_dst, ir_to_mesa_undef); + call_inst->function = entry; + + /* Process out parameters. */ + sig_iter = sig->parameters.iterator(); + foreach_iter(exec_list_iterator, iter, *ir) { + ir_rvalue *param_rval = (ir_rvalue *)iter.get(); + ir_variable *param = (ir_variable *)sig_iter.get(); + + if (param->mode == ir_var_out || + param->mode == ir_var_inout) { + variable_storage *storage = find_variable_storage(param); + assert(storage); + + ir_to_mesa_src_reg r; + r.file = storage->file; + r.index = storage->index; + r.reladdr = NULL; + r.swizzle = SWIZZLE_NOOP; + r.negate = 0; + + param_rval->accept(this); + ir_to_mesa_dst_reg l = ir_to_mesa_dst_reg_from_src(this->result); + + for (i = 0; i < type_size(param->type); i++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); + l.index++; + r.index++; + } + } + + sig_iter.next(); + } + assert(!sig_iter.has_next()); + + /* Process return value. */ + this->result = entry->return_reg; +} + +class get_sampler_name : public ir_hierarchical_visitor +{ +public: + get_sampler_name(ir_to_mesa_visitor *mesa, ir_dereference *last) + { + this->mem_ctx = mesa->mem_ctx; + this->mesa = mesa; + this->name = NULL; + this->offset = 0; + this->last = last; + } + + virtual ir_visitor_status visit(ir_dereference_variable *ir) + { + this->name = ir->var->name; + return visit_continue; + } + + virtual ir_visitor_status visit_leave(ir_dereference_record *ir) + { + this->name = talloc_asprintf(mem_ctx, "%s.%s", name, ir->field); + return visit_continue; + } + + virtual ir_visitor_status visit_leave(ir_dereference_array *ir) + { + ir_constant *index = ir->array_index->as_constant(); + int i; + + if (index) { + i = index->value.i[0]; + } else { + /* GLSL 1.10 and 1.20 allowed variable sampler array indices, + * while GLSL 1.30 requires that the array indices be + * constant integer expressions. We don't expect any driver + * to actually work with a really variable array index, so + * all that would work would be an unrolled loop counter that ends + * up being constant above. + */ + mesa->shader_program->InfoLog = + talloc_asprintf_append(mesa->shader_program->InfoLog, + "warning: Variable sampler array index " + "unsupported.\nThis feature of the language " + "was removed in GLSL 1.20 and is unlikely " + "to be supported for 1.10 in Mesa.\n"); + i = 0; + } + if (ir != last) { + this->name = talloc_asprintf(mem_ctx, "%s[%d]", name, i); + } else { + offset = i; + } + return visit_continue; + } + + ir_to_mesa_visitor *mesa; + const char *name; + void *mem_ctx; + int offset; + ir_dereference *last; +}; + +int +ir_to_mesa_visitor::get_sampler_uniform_value(ir_dereference *sampler) +{ + get_sampler_name getname(this, sampler); + + sampler->accept(&getname); + + GLint index = _mesa_lookup_parameter_index(prog->Parameters, -1, + getname.name); + + if (index < 0) { + fail_link(this->shader_program, + "failed to find sampler named %s.\n", getname.name); + return 0; + } + + index += getname.offset; + + return this->prog->Parameters->ParameterValues[index][0]; +} + +void +ir_to_mesa_visitor::visit(ir_texture *ir) +{ + ir_to_mesa_src_reg result_src, coord, lod_info, projector; + ir_to_mesa_dst_reg result_dst, coord_dst; + ir_to_mesa_instruction *inst = NULL; + prog_opcode opcode = OPCODE_NOP; + + ir->coordinate->accept(this); + + /* Put our coords in a temp. We'll need to modify them for shadow, + * projection, or LOD, so the only case we'd use it as is is if + * we're doing plain old texturing. Mesa IR optimization should + * handle cleaning up our mess in that case. + */ + coord = get_temp(glsl_type::vec4_type); + coord_dst = ir_to_mesa_dst_reg_from_src(coord); + ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, + this->result); + + if (ir->projector) { + ir->projector->accept(this); + projector = this->result; + } + + /* Storage for our result. Ideally for an assignment we'd be using + * the actual storage for the result here, instead. + */ + result_src = get_temp(glsl_type::vec4_type); + result_dst = ir_to_mesa_dst_reg_from_src(result_src); + + switch (ir->op) { + case ir_tex: + opcode = OPCODE_TEX; + break; + case ir_txb: + opcode = OPCODE_TXB; + ir->lod_info.bias->accept(this); + lod_info = this->result; + break; + case ir_txl: + opcode = OPCODE_TXL; + ir->lod_info.lod->accept(this); + lod_info = this->result; + break; + case ir_txd: + case ir_txf: + assert(!"GLSL 1.30 features unsupported"); + break; + } + + if (ir->projector) { + if (opcode == OPCODE_TEX) { + /* Slot the projector in as the last component of the coord. */ + coord_dst.writemask = WRITEMASK_W; + ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, projector); + coord_dst.writemask = WRITEMASK_XYZW; + opcode = OPCODE_TXP; + } else { + ir_to_mesa_src_reg coord_w = coord; + coord_w.swizzle = SWIZZLE_WWWW; + + /* For the other TEX opcodes there's no projective version + * since the last slot is taken up by lod info. Do the + * projective divide now. + */ + coord_dst.writemask = WRITEMASK_W; + ir_to_mesa_emit_op1(ir, OPCODE_RCP, coord_dst, projector); + + coord_dst.writemask = WRITEMASK_XYZ; + ir_to_mesa_emit_op2(ir, OPCODE_MUL, coord_dst, coord, coord_w); + + coord_dst.writemask = WRITEMASK_XYZW; + coord.swizzle = SWIZZLE_XYZW; + } + } + + if (ir->shadow_comparitor) { + /* Slot the shadow value in as the second to last component of the + * coord. + */ + ir->shadow_comparitor->accept(this); + coord_dst.writemask = WRITEMASK_Z; + ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, this->result); + coord_dst.writemask = WRITEMASK_XYZW; + } + + if (opcode == OPCODE_TXL || opcode == OPCODE_TXB) { + /* Mesa IR stores lod or lod bias in the last channel of the coords. */ + coord_dst.writemask = WRITEMASK_W; + ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, lod_info); + coord_dst.writemask = WRITEMASK_XYZW; + } + + inst = ir_to_mesa_emit_op1(ir, opcode, result_dst, coord); + + if (ir->shadow_comparitor) + inst->tex_shadow = GL_TRUE; + + inst->sampler = get_sampler_uniform_value(ir->sampler); + + const glsl_type *sampler_type = ir->sampler->type; + + switch (sampler_type->sampler_dimensionality) { + case GLSL_SAMPLER_DIM_1D: + inst->tex_target = (sampler_type->sampler_array) + ? TEXTURE_1D_ARRAY_INDEX : TEXTURE_1D_INDEX; + break; + case GLSL_SAMPLER_DIM_2D: + inst->tex_target = (sampler_type->sampler_array) + ? TEXTURE_2D_ARRAY_INDEX : TEXTURE_2D_INDEX; + break; + case GLSL_SAMPLER_DIM_3D: + inst->tex_target = TEXTURE_3D_INDEX; + break; + case GLSL_SAMPLER_DIM_CUBE: + inst->tex_target = TEXTURE_CUBE_INDEX; + break; + case GLSL_SAMPLER_DIM_RECT: + inst->tex_target = TEXTURE_RECT_INDEX; + break; + case GLSL_SAMPLER_DIM_BUF: + assert(!"FINISHME: Implement ARB_texture_buffer_object"); + break; + default: + assert(!"Should not get here."); + } + + this->result = result_src; +} + +void +ir_to_mesa_visitor::visit(ir_return *ir) +{ + if (ir->get_value()) { + ir_to_mesa_dst_reg l; + int i; + + assert(current_function); + + ir->get_value()->accept(this); + ir_to_mesa_src_reg r = this->result; + + l = ir_to_mesa_dst_reg_from_src(current_function->return_reg); + + for (i = 0; i < type_size(current_function->sig->return_type); i++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); + l.index++; + r.index++; + } + } + + ir_to_mesa_emit_op0(ir, OPCODE_RET); +} + +void +ir_to_mesa_visitor::visit(ir_discard *ir) +{ + struct gl_fragment_program *fp = (struct gl_fragment_program *)this->prog; + + assert(ir->condition == NULL); /* FINISHME */ + + ir_to_mesa_emit_op0(ir, OPCODE_KIL_NV); + fp->UsesKill = GL_TRUE; +} + +void +ir_to_mesa_visitor::visit(ir_if *ir) +{ + ir_to_mesa_instruction *cond_inst, *if_inst, *else_inst = NULL; + ir_to_mesa_instruction *prev_inst; + + prev_inst = (ir_to_mesa_instruction *)this->instructions.get_tail(); + + ir->condition->accept(this); + assert(this->result.file != PROGRAM_UNDEFINED); + + if (ctx->Shader.EmitCondCodes) { + cond_inst = (ir_to_mesa_instruction *)this->instructions.get_tail(); + + /* See if we actually generated any instruction for generating + * the condition. If not, then cook up a move to a temp so we + * have something to set cond_update on. + */ + if (cond_inst == prev_inst) { + ir_to_mesa_src_reg temp = get_temp(glsl_type::bool_type); + cond_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_MOV, + ir_to_mesa_dst_reg_from_src(temp), + result); + } + cond_inst->cond_update = GL_TRUE; + + if_inst = ir_to_mesa_emit_op0(ir->condition, OPCODE_IF); + if_inst->dst_reg.cond_mask = COND_NE; + } else { + if_inst = ir_to_mesa_emit_op1(ir->condition, + OPCODE_IF, ir_to_mesa_undef_dst, + this->result); + } + + this->instructions.push_tail(if_inst); + + visit_exec_list(&ir->then_instructions, this); + + if (!ir->else_instructions.is_empty()) { + else_inst = ir_to_mesa_emit_op0(ir->condition, OPCODE_ELSE); + visit_exec_list(&ir->else_instructions, this); + } + + if_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_ENDIF, + ir_to_mesa_undef_dst, ir_to_mesa_undef); +} + +ir_to_mesa_visitor::ir_to_mesa_visitor() +{ + result.file = PROGRAM_UNDEFINED; + next_temp = 1; + next_signature_id = 1; + current_function = NULL; + mem_ctx = talloc_new(NULL); +} + +ir_to_mesa_visitor::~ir_to_mesa_visitor() +{ + talloc_free(mem_ctx); +} + +static struct prog_src_register +mesa_src_reg_from_ir_src_reg(ir_to_mesa_src_reg reg) +{ + struct prog_src_register mesa_reg; + + mesa_reg.File = reg.file; + assert(reg.index < (1 << INST_INDEX_BITS) - 1); + mesa_reg.Index = reg.index; + mesa_reg.Swizzle = reg.swizzle; + mesa_reg.RelAddr = reg.reladdr != NULL; + mesa_reg.Negate = reg.negate; + mesa_reg.Abs = 0; + mesa_reg.HasIndex2 = GL_FALSE; + mesa_reg.RelAddr2 = 0; + mesa_reg.Index2 = 0; + + return mesa_reg; +} + +static void +set_branchtargets(ir_to_mesa_visitor *v, + struct prog_instruction *mesa_instructions, + int num_instructions) +{ + int if_count = 0, loop_count = 0; + int *if_stack, *loop_stack; + int if_stack_pos = 0, loop_stack_pos = 0; + int i, j; + + for (i = 0; i < num_instructions; i++) { + switch (mesa_instructions[i].Opcode) { + case OPCODE_IF: + if_count++; + break; + case OPCODE_BGNLOOP: + loop_count++; + break; + case OPCODE_BRK: + case OPCODE_CONT: + mesa_instructions[i].BranchTarget = -1; + break; + default: + break; + } + } + + if_stack = talloc_zero_array(v->mem_ctx, int, if_count); + loop_stack = talloc_zero_array(v->mem_ctx, int, loop_count); + + for (i = 0; i < num_instructions; i++) { + switch (mesa_instructions[i].Opcode) { + case OPCODE_IF: + if_stack[if_stack_pos] = i; + if_stack_pos++; + break; + case OPCODE_ELSE: + mesa_instructions[if_stack[if_stack_pos - 1]].BranchTarget = i; + if_stack[if_stack_pos - 1] = i; + break; + case OPCODE_ENDIF: + mesa_instructions[if_stack[if_stack_pos - 1]].BranchTarget = i; + if_stack_pos--; + break; + case OPCODE_BGNLOOP: + loop_stack[loop_stack_pos] = i; + loop_stack_pos++; + break; + case OPCODE_ENDLOOP: + loop_stack_pos--; + /* Rewrite any breaks/conts at this nesting level (haven't + * already had a BranchTarget assigned) to point to the end + * of the loop. + */ + for (j = loop_stack[loop_stack_pos]; j < i; j++) { + if (mesa_instructions[j].Opcode == OPCODE_BRK || + mesa_instructions[j].Opcode == OPCODE_CONT) { + if (mesa_instructions[j].BranchTarget == -1) { + mesa_instructions[j].BranchTarget = i; + } + } + } + /* The loop ends point at each other. */ + mesa_instructions[i].BranchTarget = loop_stack[loop_stack_pos]; + mesa_instructions[loop_stack[loop_stack_pos]].BranchTarget = i; + break; + case OPCODE_CAL: + foreach_iter(exec_list_iterator, iter, v->function_signatures) { + function_entry *entry = (function_entry *)iter.get(); + + if (entry->sig_id == mesa_instructions[i].BranchTarget) { + mesa_instructions[i].BranchTarget = entry->inst; + break; + } + } + break; + default: + break; + } + } +} + +static void +print_program(struct prog_instruction *mesa_instructions, + ir_instruction **mesa_instruction_annotation, + int num_instructions) +{ + ir_instruction *last_ir = NULL; + int i; + int indent = 0; + + for (i = 0; i < num_instructions; i++) { + struct prog_instruction *mesa_inst = mesa_instructions + i; + ir_instruction *ir = mesa_instruction_annotation[i]; + + fprintf(stdout, "%3d: ", i); + + if (last_ir != ir && ir) { + int j; + + for (j = 0; j < indent; j++) { + fprintf(stdout, " "); + } + ir->print(); + printf("\n"); + last_ir = ir; + + fprintf(stdout, " "); /* line number spacing. */ + } + + indent = _mesa_fprint_instruction_opt(stdout, mesa_inst, indent, + PROG_PRINT_DEBUG, NULL); + } +} + +static void +count_resources(struct gl_program *prog) +{ + unsigned int i; + + prog->SamplersUsed = 0; + + for (i = 0; i < prog->NumInstructions; i++) { + struct prog_instruction *inst = &prog->Instructions[i]; + + if (_mesa_is_tex_instruction(inst->Opcode)) { + prog->SamplerTargets[inst->TexSrcUnit] = + (gl_texture_index)inst->TexSrcTarget; + prog->SamplersUsed |= 1 << inst->TexSrcUnit; + if (inst->TexShadow) { + prog->ShadowSamplers |= 1 << inst->TexSrcUnit; + } + } + } + + _mesa_update_shader_textures_used(prog); +} + +struct uniform_sort { + struct gl_uniform *u; + int pos; +}; + +/* The shader_program->Uniforms list is almost sorted in increasing + * uniform->{Frag,Vert}Pos locations, but not quite when there are + * uniforms shared between targets. We need to add parameters in + * increasing order for the targets. + */ +static int +sort_uniforms(const void *a, const void *b) +{ + struct uniform_sort *u1 = (struct uniform_sort *)a; + struct uniform_sort *u2 = (struct uniform_sort *)b; + + return u1->pos - u2->pos; +} + +/* Add the uniforms to the parameters. The linker chose locations + * in our parameters lists (which weren't created yet), which the + * uniforms code will use to poke values into our parameters list + * when uniforms are updated. + */ +static void +add_uniforms_to_parameters_list(struct gl_shader_program *shader_program, + struct gl_shader *shader, + struct gl_program *prog) +{ + unsigned int i; + unsigned int next_sampler = 0, num_uniforms = 0; + struct uniform_sort *sorted_uniforms; + + sorted_uniforms = talloc_array(NULL, struct uniform_sort, + shader_program->Uniforms->NumUniforms); + + for (i = 0; i < shader_program->Uniforms->NumUniforms; i++) { + struct gl_uniform *uniform = shader_program->Uniforms->Uniforms + i; + int parameter_index = -1; + + switch (shader->Type) { + case GL_VERTEX_SHADER: + parameter_index = uniform->VertPos; + break; + case GL_FRAGMENT_SHADER: + parameter_index = uniform->FragPos; + break; + case GL_GEOMETRY_SHADER: + parameter_index = uniform->GeomPos; + break; + } + + /* Only add uniforms used in our target. */ + if (parameter_index != -1) { + sorted_uniforms[num_uniforms].pos = parameter_index; + sorted_uniforms[num_uniforms].u = uniform; + num_uniforms++; + } + } + + qsort(sorted_uniforms, num_uniforms, sizeof(struct uniform_sort), + sort_uniforms); + + for (i = 0; i < num_uniforms; i++) { + struct gl_uniform *uniform = sorted_uniforms[i].u; + int parameter_index = sorted_uniforms[i].pos; + const glsl_type *type = uniform->Type; + unsigned int size; + + if (type->is_vector() || + type->is_scalar()) { + size = type->vector_elements; + } else { + size = type_size(type) * 4; + } + + gl_register_file file; + if (type->is_sampler() || + (type->is_array() && type->fields.array->is_sampler())) { + file = PROGRAM_SAMPLER; + } else { + file = PROGRAM_UNIFORM; + } + + GLint index = _mesa_lookup_parameter_index(prog->Parameters, -1, + uniform->Name); + + if (index < 0) { + index = _mesa_add_parameter(prog->Parameters, file, + uniform->Name, size, type->gl_type, + NULL, NULL, 0x0); + + /* Sampler uniform values are stored in prog->SamplerUnits, + * and the entry in that array is selected by this index we + * store in ParameterValues[]. + */ + if (file == PROGRAM_SAMPLER) { + for (unsigned int j = 0; j < size / 4; j++) + prog->Parameters->ParameterValues[index + j][0] = next_sampler++; + } + + /* The location chosen in the Parameters list here (returned + * from _mesa_add_uniform) has to match what the linker chose. + */ + if (index != parameter_index) { + fail_link(shader_program, "Allocation of uniform `%s' to target " + "failed (%d vs %d)\n", + uniform->Name, index, parameter_index); + } + } + } + + talloc_free(sorted_uniforms); +} + +static void +set_uniform_initializer(GLcontext *ctx, void *mem_ctx, + struct gl_shader_program *shader_program, + const char *name, const glsl_type *type, + ir_constant *val) +{ + if (type->is_record()) { + ir_constant *field_constant; + + field_constant = (ir_constant *)val->components.get_head(); + + for (unsigned int i = 0; i < type->length; i++) { + const glsl_type *field_type = type->fields.structure[i].type; + const char *field_name = talloc_asprintf(mem_ctx, "%s.%s", name, + type->fields.structure[i].name); + set_uniform_initializer(ctx, mem_ctx, shader_program, field_name, + field_type, field_constant); + field_constant = (ir_constant *)field_constant->next; + } + return; + } + + int loc = _mesa_get_uniform_location(ctx, shader_program, name); + + if (loc == -1) { + fail_link(shader_program, + "Couldn't find uniform for initializer %s\n", name); + return; + } + + for (unsigned int i = 0; i < (type->is_array() ? type->length : 1); i++) { + ir_constant *element; + const glsl_type *element_type; + if (type->is_array()) { + element = val->array_elements[i]; + element_type = type->fields.array; + } else { + element = val; + element_type = type; + } + + void *values; + + if (element_type->base_type == GLSL_TYPE_BOOL) { + int *conv = talloc_array(mem_ctx, int, element_type->components()); + for (unsigned int j = 0; j < element_type->components(); j++) { + conv[j] = element->value.b[j]; + } + values = (void *)conv; + element_type = glsl_type::get_instance(GLSL_TYPE_INT, + element_type->vector_elements, + 1); + } else { + values = &element->value; + } + + if (element_type->is_matrix()) { + _mesa_uniform_matrix(ctx, shader_program, + element_type->matrix_columns, + element_type->vector_elements, + loc, 1, GL_FALSE, (GLfloat *)values); + loc += element_type->matrix_columns; + } else { + _mesa_uniform(ctx, shader_program, loc, element_type->matrix_columns, + values, element_type->gl_type); + loc += type_size(element_type); + } + } +} + +static void +set_uniform_initializers(GLcontext *ctx, + struct gl_shader_program *shader_program) +{ + void *mem_ctx = NULL; + + for (unsigned int i = 0; i < shader_program->_NumLinkedShaders; i++) { + struct gl_shader *shader = shader_program->_LinkedShaders[i]; + foreach_iter(exec_list_iterator, iter, *shader->ir) { + ir_instruction *ir = (ir_instruction *)iter.get(); + ir_variable *var = ir->as_variable(); + + if (!var || var->mode != ir_var_uniform || !var->constant_value) + continue; + + if (!mem_ctx) + mem_ctx = talloc_new(NULL); + + set_uniform_initializer(ctx, mem_ctx, shader_program, var->name, + var->type, var->constant_value); + } + } + + talloc_free(mem_ctx); +} + +struct gl_program * +get_mesa_program(GLcontext *ctx, struct gl_shader_program *shader_program, + struct gl_shader *shader) +{ + ir_to_mesa_visitor v; + struct prog_instruction *mesa_instructions, *mesa_inst; + ir_instruction **mesa_instruction_annotation; + int i; + struct gl_program *prog; + GLenum target; + const char *target_string; + GLboolean progress; + + switch (shader->Type) { + case GL_VERTEX_SHADER: + target = GL_VERTEX_PROGRAM_ARB; + target_string = "vertex"; + break; + case GL_FRAGMENT_SHADER: + target = GL_FRAGMENT_PROGRAM_ARB; + target_string = "fragment"; + break; + default: + assert(!"should not be reached"); + return NULL; + } + + validate_ir_tree(shader->ir); + + prog = ctx->Driver.NewProgram(ctx, target, shader_program->Name); + if (!prog) + return NULL; + prog->Parameters = _mesa_new_parameter_list(); + prog->Varying = _mesa_new_parameter_list(); + prog->Attributes = _mesa_new_parameter_list(); + v.ctx = ctx; + v.prog = prog; + v.shader_program = shader_program; + + add_uniforms_to_parameters_list(shader_program, shader, prog); + + /* Emit Mesa IR for main(). */ + visit_exec_list(shader->ir, &v); + v.ir_to_mesa_emit_op0(NULL, OPCODE_END); + + /* Now emit bodies for any functions that were used. */ + do { + progress = GL_FALSE; + + foreach_iter(exec_list_iterator, iter, v.function_signatures) { + function_entry *entry = (function_entry *)iter.get(); + + if (!entry->bgn_inst) { + v.current_function = entry; + + entry->bgn_inst = v.ir_to_mesa_emit_op0(NULL, OPCODE_BGNSUB); + entry->bgn_inst->function = entry; + + visit_exec_list(&entry->sig->body, &v); + + ir_to_mesa_instruction *last; + last = (ir_to_mesa_instruction *)v.instructions.get_tail(); + if (last->op != OPCODE_RET) + v.ir_to_mesa_emit_op0(NULL, OPCODE_RET); + + ir_to_mesa_instruction *end; + end = v.ir_to_mesa_emit_op0(NULL, OPCODE_ENDSUB); + end->function = entry; + + progress = GL_TRUE; + } + } + } while (progress); + + prog->NumTemporaries = v.next_temp; + + int num_instructions = 0; + foreach_iter(exec_list_iterator, iter, v.instructions) { + num_instructions++; + } + + mesa_instructions = + (struct prog_instruction *)calloc(num_instructions, + sizeof(*mesa_instructions)); + mesa_instruction_annotation = talloc_array(v.mem_ctx, ir_instruction *, + num_instructions); + + mesa_inst = mesa_instructions; + i = 0; + foreach_iter(exec_list_iterator, iter, v.instructions) { + ir_to_mesa_instruction *inst = (ir_to_mesa_instruction *)iter.get(); + + mesa_inst->Opcode = inst->op; + mesa_inst->CondUpdate = inst->cond_update; + mesa_inst->DstReg.File = inst->dst_reg.file; + mesa_inst->DstReg.Index = inst->dst_reg.index; + mesa_inst->DstReg.CondMask = inst->dst_reg.cond_mask; + mesa_inst->DstReg.WriteMask = inst->dst_reg.writemask; + mesa_inst->DstReg.RelAddr = inst->dst_reg.reladdr != NULL; + mesa_inst->SrcReg[0] = mesa_src_reg_from_ir_src_reg(inst->src_reg[0]); + mesa_inst->SrcReg[1] = mesa_src_reg_from_ir_src_reg(inst->src_reg[1]); + mesa_inst->SrcReg[2] = mesa_src_reg_from_ir_src_reg(inst->src_reg[2]); + mesa_inst->TexSrcUnit = inst->sampler; + mesa_inst->TexSrcTarget = inst->tex_target; + mesa_inst->TexShadow = inst->tex_shadow; + mesa_instruction_annotation[i] = inst->ir; + + /* Set IndirectRegisterFiles. */ + if (mesa_inst->DstReg.RelAddr) + prog->IndirectRegisterFiles |= 1 << mesa_inst->DstReg.File; + + for (unsigned src = 0; src < 3; src++) + if (mesa_inst->SrcReg[src].RelAddr) + prog->IndirectRegisterFiles |= 1 << mesa_inst->SrcReg[src].File; + + if (ctx->Shader.EmitNoIfs && mesa_inst->Opcode == OPCODE_IF) { + fail_link(shader_program, "Couldn't flatten if statement\n"); + } + + switch (mesa_inst->Opcode) { + case OPCODE_BGNSUB: + inst->function->inst = i; + mesa_inst->Comment = strdup(inst->function->sig->function_name()); + break; + case OPCODE_ENDSUB: + mesa_inst->Comment = strdup(inst->function->sig->function_name()); + break; + case OPCODE_CAL: + mesa_inst->BranchTarget = inst->function->sig_id; /* rewritten later */ + break; + case OPCODE_ARL: + prog->NumAddressRegs = 1; + break; + default: + break; + } + + mesa_inst++; + i++; + } + + set_branchtargets(&v, mesa_instructions, num_instructions); + + if (ctx->Shader.Flags & GLSL_DUMP) { + printf("\n"); + printf("GLSL IR for linked %s program %d:\n", target_string, + shader_program->Name); + _mesa_print_ir(shader->ir, NULL); + printf("\n"); + printf("\n"); + printf("Mesa IR for linked %s program %d:\n", target_string, + shader_program->Name); + print_program(mesa_instructions, mesa_instruction_annotation, + num_instructions); + } + + prog->Instructions = mesa_instructions; + prog->NumInstructions = num_instructions; + + do_set_program_inouts(shader->ir, prog); + count_resources(prog); + + _mesa_reference_program(ctx, &shader->Program, prog); + + if ((ctx->Shader.Flags & GLSL_NO_OPT) == 0) { + _mesa_optimize_program(ctx, prog); + } + + return prog; +} + +extern "C" { +GLboolean +_mesa_ir_compile_shader(GLcontext *ctx, struct gl_shader *shader) +{ + assert(shader->CompileStatus); + (void) ctx; + + return GL_TRUE; +} + +GLboolean +_mesa_ir_link_shader(GLcontext *ctx, struct gl_shader_program *prog) +{ + assert(prog->LinkStatus); + + for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) { + bool progress; + exec_list *ir = prog->_LinkedShaders[i]->ir; + + do { + progress = false; + + /* Lowering */ + do_mat_op_to_vec(ir); + do_mod_to_fract(ir); + do_div_to_mul_rcp(ir); + do_explog_to_explog2(ir); + + progress = do_common_optimization(ir, true) || progress; + + if (ctx->Shader.EmitNoIfs) + progress = do_if_to_cond_assign(ir) || progress; + + progress = do_vec_index_to_cond_assign(ir) || progress; + } while (progress); + + validate_ir_tree(ir); + } + + for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) { + struct gl_program *linked_prog; + bool ok = true; + + linked_prog = get_mesa_program(ctx, prog, prog->_LinkedShaders[i]); + + switch (prog->_LinkedShaders[i]->Type) { + case GL_VERTEX_SHADER: + _mesa_reference_vertprog(ctx, &prog->VertexProgram, + (struct gl_vertex_program *)linked_prog); + ok = ctx->Driver.ProgramStringNotify(ctx, GL_VERTEX_PROGRAM_ARB, + linked_prog); + break; + case GL_FRAGMENT_SHADER: + _mesa_reference_fragprog(ctx, &prog->FragmentProgram, + (struct gl_fragment_program *)linked_prog); + ok = ctx->Driver.ProgramStringNotify(ctx, GL_FRAGMENT_PROGRAM_ARB, + linked_prog); + break; + } + if (!ok) { + return GL_FALSE; + } + _mesa_reference_program(ctx, &linked_prog, NULL); + } + + return GL_TRUE; +} + +void +_mesa_glsl_compile_shader(GLcontext *ctx, struct gl_shader *shader) +{ + struct _mesa_glsl_parse_state *state = + new(shader) _mesa_glsl_parse_state(ctx, shader->Type, shader); + + const char *source = shader->Source; + state->error = preprocess(state, &source, &state->info_log, + &ctx->Extensions); + + if (ctx->Shader.Flags & GLSL_DUMP) { + printf("GLSL source for shader %d:\n", shader->Name); + printf("%s\n", shader->Source); + } + + if (!state->error) { + _mesa_glsl_lexer_ctor(state, source); + _mesa_glsl_parse(state); + _mesa_glsl_lexer_dtor(state); + } + + talloc_free(shader->ir); + shader->ir = new(shader) exec_list; + if (!state->error && !state->translation_unit.is_empty()) + _mesa_ast_to_hir(shader->ir, state); + + if (!state->error && !shader->ir->is_empty()) { + validate_ir_tree(shader->ir); + + /* Do some optimization at compile time to reduce shader IR size + * and reduce later work if the same shader is linked multiple times + */ + while (do_common_optimization(shader->ir, false)) + ; + + validate_ir_tree(shader->ir); + } + + shader->symbols = state->symbols; + + shader->CompileStatus = !state->error; + shader->InfoLog = state->info_log; + shader->Version = state->language_version; + memcpy(shader->builtins_to_link, state->builtins_to_link, + sizeof(shader->builtins_to_link[0]) * state->num_builtins_to_link); + shader->num_builtins_to_link = state->num_builtins_to_link; + + if (ctx->Shader.Flags & GLSL_LOG) { + _mesa_write_shader_to_file(shader); + } + + if (ctx->Shader.Flags & GLSL_DUMP) { + if (shader->CompileStatus) { + printf("GLSL IR for shader %d:\n", shader->Name); + _mesa_print_ir(shader->ir, NULL); + printf("\n\n"); + } else { + printf("GLSL shader %d failed to compile.\n", shader->Name); + } + if (shader->InfoLog && shader->InfoLog[0] != 0) { + printf("GLSL shader %d info log:\n", shader->Name); + printf("%s\n", shader->InfoLog); + } + } + + /* Retain any live IR, but trash the rest. */ + reparent_ir(shader->ir, shader->ir); + + talloc_free(state); + + if (shader->CompileStatus) { + if (!ctx->Driver.CompileShader(ctx, shader)) + shader->CompileStatus = GL_FALSE; + } +} + +void +_mesa_glsl_link_shader(GLcontext *ctx, struct gl_shader_program *prog) +{ + unsigned int i; + + _mesa_clear_shader_program_data(ctx, prog); + + prog->LinkStatus = GL_TRUE; + + for (i = 0; i < prog->NumShaders; i++) { + if (!prog->Shaders[i]->CompileStatus) { + fail_link(prog, "linking with uncompiled shader"); + prog->LinkStatus = GL_FALSE; + } + } + + prog->Varying = _mesa_new_parameter_list(); + _mesa_reference_vertprog(ctx, &prog->VertexProgram, NULL); + _mesa_reference_fragprog(ctx, &prog->FragmentProgram, NULL); + + if (prog->LinkStatus) { + link_shaders(ctx, prog); + } + + if (prog->LinkStatus) { + if (!ctx->Driver.LinkShader(ctx, prog)) { + prog->LinkStatus = GL_FALSE; + } + } + + set_uniform_initializers(ctx, prog); + + if (ctx->Shader.Flags & GLSL_DUMP) { + if (!prog->LinkStatus) { + printf("GLSL shader program %d failed to link\n", prog->Name); + } + + if (prog->InfoLog && prog->InfoLog[0] != 0) { + printf("GLSL shader program %d info log:\n", prog->Name); + printf("%s\n", prog->InfoLog); + } + } +} + +} /* extern "C" */ diff --git a/src/mesa/program/ir_to_mesa.h b/src/mesa/program/ir_to_mesa.h new file mode 100644 index 00000000000..ecaacde4bb0 --- /dev/null +++ b/src/mesa/program/ir_to_mesa.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "main/config.h" +#include "main/mtypes.h" + +void _mesa_glsl_compile_shader(GLcontext *ctx, struct gl_shader *sh); +void _mesa_glsl_link_shader(GLcontext *ctx, struct gl_shader_program *prog); +GLboolean _mesa_ir_compile_shader(GLcontext *ctx, struct gl_shader *shader); +GLboolean _mesa_ir_link_shader(GLcontext *ctx, struct gl_shader_program *prog); + +#ifdef __cplusplus +} +#endif diff --git a/src/mesa/program/prog_execute.c b/src/mesa/program/prog_execute.c index 1670c91b6ad..2ae5bc572af 100644 --- a/src/mesa/program/prog_execute.c +++ b/src/mesa/program/prog_execute.c @@ -771,6 +771,13 @@ _mesa_execute_program(GLcontext * ctx, result[2] = a[2] < 0.0F ? b[2] : c[2]; result[3] = a[3] < 0.0F ? b[3] : c[3]; store_vector4(inst, machine, result); + if (DEBUG_PROG) { + printf("CMP (%g %g %g %g) = (%g %g %g %g) < 0 ? (%g %g %g %g) : (%g %g %g %g)\n", + result[0], result[1], result[2], result[3], + a[0], a[1], a[2], a[3], + b[0], b[1], b[2], b[3], + c[0], c[1], c[2], c[3]); + } } break; case OPCODE_COS: @@ -1679,6 +1686,22 @@ _mesa_execute_program(GLcontext * ctx, store_vector4(inst, machine, color); } break; + case OPCODE_TXL: + /* Texel lookup with explicit LOD */ + { + GLfloat texcoord[4], color[4], lod; + + fetch_vector4(&inst->SrcReg[0], machine, texcoord); + + /* texcoord[3] is the LOD */ + lod = texcoord[3]; + + machine->FetchTexelLod(ctx, texcoord, lod, + machine->Samplers[inst->TexSrcUnit], color); + + store_vector4(inst, machine, color); + } + break; case OPCODE_TXP: /* GL_ARB_fragment_program only */ /* Texture lookup w/ projective divide */ { @@ -1842,7 +1865,11 @@ _mesa_execute_program(GLcontext * ctx, numExec++; if (numExec > maxExec) { - _mesa_problem(ctx, "Infinite loop detected in fragment program"); + static GLboolean reported = GL_FALSE; + if (!reported) { + _mesa_problem(ctx, "Infinite loop detected in fragment program"); + reported = GL_TRUE; + } return GL_TRUE; } diff --git a/src/mesa/program/prog_instruction.h b/src/mesa/program/prog_instruction.h index 098b366ab56..ca90de7ce1c 100644 --- a/src/mesa/program/prog_instruction.h +++ b/src/mesa/program/prog_instruction.h @@ -401,7 +401,7 @@ struct prog_instruction /** * For BRA and CAL instructions, the location to jump to. * For BGNLOOP, points to ENDLOOP (and vice-versa). - * For BRK, points to BGNLOOP (which points to ENDLOOP). + * For BRK, points to ENDLOOP * For IF, points to ELSE or ENDIF. * For ELSE, points to ENDIF. */ diff --git a/src/mesa/program/prog_optimize.c b/src/mesa/program/prog_optimize.c index c78187c983d..0dc779073db 100644 --- a/src/mesa/program/prog_optimize.c +++ b/src/mesa/program/prog_optimize.c @@ -34,7 +34,12 @@ #define MAX_LOOP_NESTING 50 - +/* MAX_PROGRAM_TEMPS is a low number (256), and we want to be able to + * register allocate many temporary values into that small number of + * temps. So allow large temporary indices coming into the register + * allocator. + */ +#define REG_ALLOCATE_MAX_PROGRAM_TEMPS ((1 << INST_INDEX_BITS) - 1) static GLboolean dbg = GL_FALSE; @@ -233,7 +238,7 @@ replace_regs(struct gl_program *prog, gl_register_file file, const GLint map[]) static GLboolean _mesa_remove_dead_code_global(struct gl_program *prog) { - GLboolean tempRead[MAX_PROGRAM_TEMPS][4]; + GLboolean tempRead[REG_ALLOCATE_MAX_PROGRAM_TEMPS][4]; GLboolean *removeInst; /* per-instruction removal flag */ GLuint i, rem = 0, comp; @@ -258,7 +263,7 @@ _mesa_remove_dead_code_global(struct gl_program *prog) if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) { const GLuint index = inst->SrcReg[j].Index; GLuint read_mask; - ASSERT(index < MAX_PROGRAM_TEMPS); + ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS); read_mask = get_src_arg_mask(inst, j, NO_MASK); if (inst->SrcReg[j].RelAddr) { @@ -281,7 +286,7 @@ _mesa_remove_dead_code_global(struct gl_program *prog) /* check dst reg */ if (inst->DstReg.File == PROGRAM_TEMPORARY) { const GLuint index = inst->DstReg.Index; - ASSERT(index < MAX_PROGRAM_TEMPS); + ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS); if (inst->DstReg.RelAddr) { if (dbg) @@ -753,7 +758,11 @@ _mesa_remove_extra_moves(struct gl_program *prog) nesting--; break; case OPCODE_MOV: - if (i > 0 && can_downward_mov_be_modifed(mov) && nesting == 0) { + if (i > 0 && + can_downward_mov_be_modifed(mov) && + mov->SrcReg[0].File == PROGRAM_TEMPORARY && + nesting == 0) + { /* see if this MOV can be removed */ const GLuint id = mov->SrcReg[0].Index; @@ -826,7 +835,7 @@ struct interval struct interval_list { GLuint Num; - struct interval Intervals[MAX_PROGRAM_TEMPS]; + struct interval Intervals[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; }; @@ -913,15 +922,33 @@ sort_interval_list_by_start(struct interval_list *list) #endif } +struct loop_info +{ + GLuint Start, End; /**< Start, end instructions of loop */ +}; /** * Update the intermediate interval info for register 'index' and * instruction 'ic'. */ static void -update_interval(GLint intBegin[], GLint intEnd[], GLuint index, GLuint ic) +update_interval(GLint intBegin[], GLint intEnd[], + struct loop_info *loopStack, GLuint loopStackDepth, + GLuint index, GLuint ic) { - ASSERT(index < MAX_PROGRAM_TEMPS); + int i; + + /* If the register is used in a loop, extend its lifetime through the end + * of the outermost loop that doesn't contain its definition. + */ + for (i = 0; i < loopStackDepth; i++) { + if (intBegin[index] < loopStack[i].Start) { + ic = loopStack[i].End; + break; + } + } + + ASSERT(index < REG_ALLOCATE_MAX_PROGRAM_TEMPS); if (intBegin[index] == -1) { ASSERT(intEnd[index] == -1); intBegin[index] = intEnd[index] = ic; @@ -938,18 +965,14 @@ update_interval(GLint intBegin[], GLint intEnd[], GLuint index, GLuint ic) GLboolean _mesa_find_temp_intervals(const struct prog_instruction *instructions, GLuint numInstructions, - GLint intBegin[MAX_PROGRAM_TEMPS], - GLint intEnd[MAX_PROGRAM_TEMPS]) + GLint intBegin[REG_ALLOCATE_MAX_PROGRAM_TEMPS], + GLint intEnd[REG_ALLOCATE_MAX_PROGRAM_TEMPS]) { - struct loop_info - { - GLuint Start, End; /**< Start, end instructions of loop */ - }; struct loop_info loopStack[MAX_LOOP_NESTING]; GLuint loopStackDepth = 0; GLuint i; - for (i = 0; i < MAX_PROGRAM_TEMPS; i++){ + for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++){ intBegin[i] = intEnd[i] = -1; } @@ -975,24 +998,16 @@ _mesa_find_temp_intervals(const struct prog_instruction *instructions, 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); - } + update_interval(intBegin, intEnd, loopStack, loopStackDepth, + index, i); } } 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); - } + update_interval(intBegin, intEnd, loopStack, loopStackDepth, + index, i); } } } @@ -1012,7 +1027,8 @@ static GLboolean find_live_intervals(struct gl_program *prog, struct interval_list *liveIntervals) { - GLint intBegin[MAX_PROGRAM_TEMPS], intEnd[MAX_PROGRAM_TEMPS]; + GLint intBegin[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; + GLint intEnd[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; GLuint i; /* @@ -1032,7 +1048,7 @@ find_live_intervals(struct gl_program *prog, /* Build live intervals list from intermediate arrays */ liveIntervals->Num = 0; - for (i = 0; i < MAX_PROGRAM_TEMPS; i++) { + for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++) { if (intBegin[i] >= 0) { struct interval inv; inv.Reg = i; @@ -1068,10 +1084,10 @@ find_live_intervals(struct gl_program *prog, /** Scan the array of used register flags to find free entry */ static GLint -alloc_register(GLboolean usedRegs[MAX_PROGRAM_TEMPS]) +alloc_register(GLboolean usedRegs[REG_ALLOCATE_MAX_PROGRAM_TEMPS]) { GLuint k; - for (k = 0; k < MAX_PROGRAM_TEMPS; k++) { + for (k = 0; k < REG_ALLOCATE_MAX_PROGRAM_TEMPS; k++) { if (!usedRegs[k]) { usedRegs[k] = GL_TRUE; return k; @@ -1093,8 +1109,8 @@ static void _mesa_reallocate_registers(struct gl_program *prog) { struct interval_list liveIntervals; - GLint registerMap[MAX_PROGRAM_TEMPS]; - GLboolean usedRegs[MAX_PROGRAM_TEMPS]; + GLint registerMap[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; + GLboolean usedRegs[REG_ALLOCATE_MAX_PROGRAM_TEMPS]; GLuint i; GLint maxTemp = -1; @@ -1103,7 +1119,7 @@ _mesa_reallocate_registers(struct gl_program *prog) _mesa_print_program(prog); } - for (i = 0; i < MAX_PROGRAM_TEMPS; i++){ + for (i = 0; i < REG_ALLOCATE_MAX_PROGRAM_TEMPS; i++){ registerMap[i] = -1; usedRegs[i] = GL_FALSE; } diff --git a/src/mesa/program/prog_parameter.c b/src/mesa/program/prog_parameter.c index aac488c79ab..6bf8a081b02 100644 --- a/src/mesa/program/prog_parameter.c +++ b/src/mesa/program/prog_parameter.c @@ -284,93 +284,6 @@ _mesa_add_unnamed_constant(struct gl_program_parameter_list *paramList, return pos; } - -/** - * Add a uniform to the parameter list. - * Note that if the uniform is an array, size may be greater than - * what's implied by the datatype. - * \param name uniform's name - * \param size number of floats to allocate - * \param datatype GL_FLOAT_VEC3, GL_FLOAT_MAT4, etc. - */ -GLint -_mesa_add_uniform(struct gl_program_parameter_list *paramList, - const char *name, GLuint size, GLenum datatype, - const GLfloat *values) -{ - GLint i = _mesa_lookup_parameter_index(paramList, -1, name); - ASSERT(datatype != GL_NONE); - if (i >= 0 && paramList->Parameters[i].Type == PROGRAM_UNIFORM) { - ASSERT(paramList->Parameters[i].Size == size); - ASSERT(paramList->Parameters[i].DataType == datatype); - /* already in list */ - return i; - } - else { - i = _mesa_add_parameter(paramList, PROGRAM_UNIFORM, name, - size, datatype, values, NULL, 0x0); - return i; - } -} - - -/** - * Mark the named uniform as 'used'. - */ -void -_mesa_use_uniform(struct gl_program_parameter_list *paramList, - const char *name) -{ - GLuint i; - for (i = 0; i < paramList->NumParameters; i++) { - struct gl_program_parameter *p = paramList->Parameters + i; - if ((p->Type == PROGRAM_UNIFORM || p->Type == PROGRAM_SAMPLER) && - strcmp(p->Name, name) == 0) { - p->Used = GL_TRUE; - /* Note that large uniforms may occupy several slots so we're - * not done searching yet. - */ - } - } -} - - -/** - * Add a sampler to the parameter list. - * \param name uniform's name - * \param datatype GL_SAMPLER_2D, GL_SAMPLER_2D_RECT_ARB, etc. - * \param index the sampler number (as seen in TEX instructions) - * \return sampler index (starting at zero) or -1 if error - */ -GLint -_mesa_add_sampler(struct gl_program_parameter_list *paramList, - const char *name, GLenum datatype) -{ - GLint i = _mesa_lookup_parameter_index(paramList, -1, name); - if (i >= 0 && paramList->Parameters[i].Type == PROGRAM_SAMPLER) { - ASSERT(paramList->Parameters[i].Size == 1); - ASSERT(paramList->Parameters[i].DataType == datatype); - /* already in list */ - return (GLint) paramList->ParameterValues[i][0]; - } - else { - GLuint i; - const GLint size = 1; /* a sampler is basically a texture unit number */ - GLfloat value[4]; - GLint numSamplers = 0; - for (i = 0; i < paramList->NumParameters; i++) { - if (paramList->Parameters[i].Type == PROGRAM_SAMPLER) - numSamplers++; - } - value[0] = (GLfloat) numSamplers; - value[1] = value[2] = value[3] = 0.0F; - (void) _mesa_add_parameter(paramList, PROGRAM_SAMPLER, name, - size, datatype, value, NULL, 0x0); - return numSamplers; - } -} - - /** * Add parameter representing a varying variable. */ @@ -569,8 +482,10 @@ _mesa_lookup_parameter_constant(const struct gl_program_parameter_list *list, assert(vSize >= 1); assert(vSize <= 4); - if (!list) - return -1; + if (!list) { + *posOut = -1; + return GL_FALSE; + } for (i = 0; i < list->NumParameters; i++) { if (list->Parameters[i].Type == PROGRAM_CONSTANT) { @@ -591,7 +506,7 @@ _mesa_lookup_parameter_constant(const struct gl_program_parameter_list *list, if (vSize == 1) { /* look for v[0] anywhere within float[4] value */ GLuint j; - for (j = 0; j < 4; j++) { + for (j = 0; j < list->Parameters[i].Size; j++) { if (list->ParameterValues[i][j] == v[0]) { /* found it */ *posOut = i; @@ -657,7 +572,6 @@ _mesa_clone_parameter_list(const struct gl_program_parameter_list *list) list->ParameterValues[i], NULL, 0x0); ASSERT(j >= 0); pCopy = clone->Parameters + j; - pCopy->Used = p->Used; pCopy->Flags = p->Flags; /* copy state indexes */ if (p->Type == PROGRAM_STATE_VAR) { diff --git a/src/mesa/program/prog_parameter.h b/src/mesa/program/prog_parameter.h index cc3378ae201..10cbbe57a6c 100644 --- a/src/mesa/program/prog_parameter.h +++ b/src/mesa/program/prog_parameter.h @@ -64,8 +64,7 @@ struct gl_program_parameter * The next program parameter's Size will be Size-4 of this parameter. */ GLuint Size; - GLboolean Used; /**< Helper flag for GLSL uniform tracking */ - GLboolean Initialized; /**< Has the ParameterValue[] been set? */ + GLboolean Initialized; /**< debug: Has the ParameterValue[] been set? */ GLbitfield Flags; /**< Bitmask of PROG_PARAM_*_BIT */ /** * A sequence of STATE_* tokens and integers to identify GL state. @@ -132,19 +131,6 @@ _mesa_add_unnamed_constant(struct gl_program_parameter_list *paramList, GLuint *swizzleOut); extern GLint -_mesa_add_uniform(struct gl_program_parameter_list *paramList, - const char *name, GLuint size, GLenum datatype, - const GLfloat *values); - -extern void -_mesa_use_uniform(struct gl_program_parameter_list *paramList, - const char *name); - -extern GLint -_mesa_add_sampler(struct gl_program_parameter_list *paramList, - const char *name, GLenum datatype); - -extern GLint _mesa_add_varying(struct gl_program_parameter_list *paramList, const char *name, GLuint size, GLenum datatype, GLbitfield flags); diff --git a/src/mesa/program/prog_print.c b/src/mesa/program/prog_print.c index 6056c459e4c..00aa6de963b 100644 --- a/src/mesa/program/prog_print.c +++ b/src/mesa/program/prog_print.c @@ -540,12 +540,12 @@ fprint_comment(FILE *f, const struct prog_instruction *inst) } -static void -fprint_alu_instruction(FILE *f, - const struct prog_instruction *inst, - const char *opcode_string, GLuint numRegs, - gl_prog_print_mode mode, - const struct gl_program *prog) +void +_mesa_fprint_alu_instruction(FILE *f, + const struct prog_instruction *inst, + const char *opcode_string, GLuint numRegs, + gl_prog_print_mode mode, + const struct gl_program *prog) { GLuint j; @@ -582,8 +582,8 @@ void _mesa_print_alu_instruction(const struct prog_instruction *inst, const char *opcode_string, GLuint numRegs) { - fprint_alu_instruction(stderr, inst, opcode_string, - numRegs, PROG_PRINT_DEBUG, NULL); + _mesa_fprint_alu_instruction(stderr, inst, opcode_string, + numRegs, PROG_PRINT_DEBUG, NULL); } @@ -791,16 +791,16 @@ _mesa_fprint_instruction_opt(FILE *f, default: if (inst->Opcode < MAX_OPCODE) { /* typical alu instruction */ - fprint_alu_instruction(f, inst, - _mesa_opcode_string(inst->Opcode), - _mesa_num_inst_src_regs(inst->Opcode), - mode, prog); + _mesa_fprint_alu_instruction(f, inst, + _mesa_opcode_string(inst->Opcode), + _mesa_num_inst_src_regs(inst->Opcode), + mode, prog); } else { - fprint_alu_instruction(f, inst, - _mesa_opcode_string(inst->Opcode), - 3/*_mesa_num_inst_src_regs(inst->Opcode)*/, - mode, prog); + _mesa_fprint_alu_instruction(f, inst, + _mesa_opcode_string(inst->Opcode), + 3/*_mesa_num_inst_src_regs(inst->Opcode)*/, + mode, prog); } break; } @@ -1033,11 +1033,11 @@ _mesa_write_shader_to_file(const struct gl_shader *shader) fprintf(f, "/* Compile status: %s */\n", shader->CompileStatus ? "ok" : "fail"); - if (!shader->CompileStatus) { - fprintf(f, "/* Log Info: */\n"); + fprintf(f, "/* Log Info: */\n"); + if (shader->InfoLog) { fputs(shader->InfoLog, f); } - else { + if (shader->CompileStatus && shader->Program) { fprintf(f, "/* GPU code */\n"); fprintf(f, "/*\n"); _mesa_fprint_program_opt(f, shader->Program, PROG_PRINT_DEBUG, GL_TRUE); diff --git a/src/mesa/program/prog_print.h b/src/mesa/program/prog_print.h index 4667373f379..78b90aeb4d6 100644 --- a/src/mesa/program/prog_print.h +++ b/src/mesa/program/prog_print.h @@ -66,6 +66,13 @@ extern void _mesa_print_swizzle(GLuint swizzle); extern void +_mesa_fprint_alu_instruction(FILE *f, + const struct prog_instruction *inst, + const char *opcode_string, GLuint numRegs, + gl_prog_print_mode mode, + const struct gl_program *prog); + +extern void _mesa_print_alu_instruction(const struct prog_instruction *inst, const char *opcode_string, GLuint numRegs); diff --git a/src/mesa/program/prog_uniform.c b/src/mesa/program/prog_uniform.c index 5aa9878d43c..28acb8871a7 100644 --- a/src/mesa/program/prog_uniform.c +++ b/src/mesa/program/prog_uniform.c @@ -44,6 +44,10 @@ void _mesa_free_uniform_list(struct gl_uniform_list *list) { GLuint i; + + if (!list) + return; + for (i = 0; i < list->NumUniforms; i++) { free((void *) list->Uniforms[i].Name); } diff --git a/src/mesa/program/prog_uniform.h b/src/mesa/program/prog_uniform.h index 7988d534a7d..67f78006eac 100644 --- a/src/mesa/program/prog_uniform.h +++ b/src/mesa/program/prog_uniform.h @@ -51,10 +51,7 @@ struct gl_uniform GLint FragPos; GLint GeomPos; GLboolean Initialized; /**< For debug. Has this uniform been set? */ -#if 0 - GLenum DataType; /**< GL_FLOAT, GL_FLOAT_VEC2, etc */ - GLuint Size; /**< Number of components (1..4) */ -#endif + const struct glsl_type *Type; }; diff --git a/src/mesa/program/symbol_table.c b/src/mesa/program/symbol_table.c index 6a5d6868974..09e7cb44ef3 100644 --- a/src/mesa/program/symbol_table.c +++ b/src/mesa/program/symbol_table.c @@ -58,7 +58,9 @@ struct symbol { */ int name_space; - + /** Scope depth where this symbol was defined. */ + unsigned depth; + /** * Arbitrary user supplied data. */ @@ -73,7 +75,7 @@ struct symbol_header { struct symbol_header *next; /** Symbol name. */ - const char *name; + char *name; /** Linked list of symbols with the same name. */ struct symbol *symbols; @@ -104,6 +106,9 @@ struct _mesa_symbol_table { /** List of all symbol headers in the table. */ struct symbol_header *hdr; + + /** Current scope depth. */ + unsigned depth; }; @@ -157,6 +162,7 @@ _mesa_symbol_table_pop_scope(struct _mesa_symbol_table *table) struct symbol *sym = scope->symbols; table->current_scope = scope->next; + table->depth--; free(scope); @@ -184,6 +190,7 @@ _mesa_symbol_table_push_scope(struct _mesa_symbol_table *table) scope->next = table->current_scope; table->current_scope = scope; + table->depth++; } @@ -261,6 +268,36 @@ _mesa_symbol_table_iterator_next(struct _mesa_symbol_table_iterator *iter) } +/** + * Determine the scope "distance" of a symbol from the current scope + * + * \return + * A non-negative number for the number of scopes between the current scope + * and the scope where a symbol was defined. A value of zero means the current + * scope. A negative number if the symbol does not exist. + */ +int +_mesa_symbol_table_symbol_scope(struct _mesa_symbol_table *table, + int name_space, const char *name) +{ + struct symbol_header *const hdr = find_symbol(table, name); + struct symbol *sym; + + if (hdr != NULL) { + for (sym = hdr->symbols; sym != NULL; sym = sym->next_with_same_name) { + assert(sym->hdr == hdr); + + if ((name_space == -1) || (sym->name_space == name_space)) { + assert(sym->depth <= table->depth); + return sym->depth - table->depth; + } + } + } + + return -1; +} + + void * _mesa_symbol_table_find_symbol(struct _mesa_symbol_table *table, int name_space, const char *name) @@ -300,21 +337,34 @@ _mesa_symbol_table_add_symbol(struct _mesa_symbol_table *table, if (hdr == NULL) { hdr = calloc(1, sizeof(*hdr)); - hdr->name = name; + hdr->name = strdup(name); - hash_table_insert(table->ht, hdr, name); + hash_table_insert(table->ht, hdr, hdr->name); hdr->next = table->hdr; table->hdr = hdr; } check_symbol_table(table); + /* If the symbol already exists in this namespace at this scope, it cannot + * be added to the table. + */ + for (sym = hdr->symbols + ; (sym != NULL) && (sym->name_space != name_space) + ; sym = sym->next_with_same_name) { + /* empty */ + } + + if (sym && (sym->depth == table->depth)) + return -1; + sym = calloc(1, sizeof(*sym)); sym->next_with_same_name = hdr->symbols; sym->next_with_same_scope = table->current_scope->symbols; sym->hdr = hdr; sym->name_space = name_space; sym->data = declaration; + sym->depth = table->depth; assert(sym->hdr == hdr); @@ -354,6 +404,7 @@ _mesa_symbol_table_dtor(struct _mesa_symbol_table *table) for (hdr = table->hdr; hdr != NULL; hdr = next) { next = hdr->next; + free(hdr->name); free(hdr); } diff --git a/src/mesa/program/symbol_table.h b/src/mesa/program/symbol_table.h index 0c054ef1396..1d570fc1a09 100644 --- a/src/mesa/program/symbol_table.h +++ b/src/mesa/program/symbol_table.h @@ -33,6 +33,9 @@ extern void _mesa_symbol_table_pop_scope(struct _mesa_symbol_table *table); extern int _mesa_symbol_table_add_symbol(struct _mesa_symbol_table *symtab, int name_space, const char *name, void *declaration); +extern int _mesa_symbol_table_symbol_scope(struct _mesa_symbol_table *table, + int name_space, const char *name); + extern void *_mesa_symbol_table_find_symbol( struct _mesa_symbol_table *symtab, int name_space, const char *name); |