#include "main/macros.h"
#include "program/prog_parameter.h"
#include "program/prog_print.h"
#include "program/prog_optimize.h"
#include "brw_context.h"
#include "brw_eu.h"
#include "brw_wm.h"

static struct brw_reg get_dst_reg(struct brw_wm_compile *c,
                                  const struct prog_instruction *inst,
                                  GLuint component);

/**
 * Determine if the given fragment program uses GLSL features such
 * as flow conditionals, loops, subroutines.
 * Some GLSL shaders may use these features, others might not.
 */
GLboolean brw_wm_is_glsl(const struct gl_fragment_program *fp)
{
    int i;

    if (INTEL_DEBUG & DEBUG_GLSL_FORCE)
       return GL_TRUE;

    for (i = 0; i < fp->Base.NumInstructions; i++) {
	const struct prog_instruction *inst = &fp->Base.Instructions[i];
	switch (inst->Opcode) {
	    case OPCODE_ARL:
	    case OPCODE_IF:
	    case OPCODE_ENDIF:
	    case OPCODE_CAL:
	    case OPCODE_BRK:
	    case OPCODE_RET:
	    case OPCODE_BGNLOOP:
		return GL_TRUE; 
	    default:
		break;
	}
    }
    return GL_FALSE; 
}



static void
reclaim_temps(struct brw_wm_compile *c);


/** Mark GRF register as used. */
static void
prealloc_grf(struct brw_wm_compile *c, int r)
{
   c->used_grf[r] = GL_TRUE;
}


/** Mark given GRF register as not in use. */
static void
release_grf(struct brw_wm_compile *c, int r)
{
   /*assert(c->used_grf[r]);*/
   c->used_grf[r] = GL_FALSE;
   c->first_free_grf = MIN2(c->first_free_grf, r);
}


/** Return index of a free GRF, mark it as used. */
static int
alloc_grf(struct brw_wm_compile *c)
{
   GLuint r;
   for (r = c->first_free_grf; r < BRW_WM_MAX_GRF; r++) {
      if (!c->used_grf[r]) {
         c->used_grf[r] = GL_TRUE;
         c->first_free_grf = r + 1;  /* a guess */
         return r;
      }
   }

   /* no free temps, try to reclaim some */
   reclaim_temps(c);
   c->first_free_grf = 0;

   /* try alloc again */
   for (r = c->first_free_grf; r < BRW_WM_MAX_GRF; r++) {
      if (!c->used_grf[r]) {
         c->used_grf[r] = GL_TRUE;
         c->first_free_grf = r + 1;  /* a guess */
         return r;
      }
   }

   for (r = 0; r < BRW_WM_MAX_GRF; r++) {
      assert(c->used_grf[r]);
   }

   /* really, no free GRF regs found */
   if (!c->out_of_regs) {
      /* print warning once per compilation */
      _mesa_warning(NULL, "i965: ran out of registers for fragment program");
      c->out_of_regs = GL_TRUE;
   }

   return -1;
}


/** Return number of GRF registers used */
static int
num_grf_used(const struct brw_wm_compile *c)
{
   int r;
   for (r = BRW_WM_MAX_GRF - 1; r >= 0; r--)
      if (c->used_grf[r])
         return r + 1;
   return 0;
}



/**
 * Record the mapping of a Mesa register to a hardware register.
 */
static void set_reg(struct brw_wm_compile *c, int file, int index, 
	int component, struct brw_reg reg)
{
    c->wm_regs[file][index][component].reg = reg;
    c->wm_regs[file][index][component].inited = GL_TRUE;
}

static struct brw_reg alloc_tmp(struct brw_wm_compile *c)
{
    struct brw_reg reg;

    /* if we need to allocate another temp, grow the tmp_regs[] array */
    if (c->tmp_index == c->tmp_max) {
       int r = alloc_grf(c);
       if (r < 0) {
          /*printf("Out of temps in %s\n", __FUNCTION__);*/
          r = 50; /* XXX random register! */
       }
       c->tmp_regs[ c->tmp_max++ ] = r;
    }

    /* form the GRF register */
    reg = brw_vec8_grf(c->tmp_regs[ c->tmp_index++ ], 0);
    /*printf("alloc_temp %d\n", reg.nr);*/
    assert(reg.nr < BRW_WM_MAX_GRF);
    return reg;

}

/**
 * Save current temp register info.
 * There must be a matching call to release_tmps().
 */
static int mark_tmps(struct brw_wm_compile *c)
{
    return c->tmp_index;
}

static void release_tmps(struct brw_wm_compile *c, int mark)
{
    c->tmp_index = mark;
}

/**
 * Convert Mesa src register to brw register.
 *
 * Since we're running in SOA mode each Mesa register corresponds to four
 * hardware registers.  We allocate the hardware registers as needed here.
 *
 * \param file  register file, one of PROGRAM_x
 * \param index  register number
 * \param component  src component (X=0, Y=1, Z=2, W=3)
 * \param nr  not used?!?
 * \param neg  negate value?
 * \param abs  take absolute value?
 */
static struct brw_reg 
get_reg(struct brw_wm_compile *c, int file, int index, int component,
        int nr, GLuint neg, GLuint abs)
{
    struct brw_reg reg;
    switch (file) {
	case PROGRAM_STATE_VAR:
	case PROGRAM_CONSTANT:
	case PROGRAM_UNIFORM:
	    file = PROGRAM_STATE_VAR;
	    break;
	case PROGRAM_UNDEFINED:
	    return brw_null_reg();	
	case PROGRAM_TEMPORARY:
	case PROGRAM_INPUT:
	case PROGRAM_OUTPUT:
	case PROGRAM_PAYLOAD:
	    break;
	default:
	    _mesa_problem(NULL, "Unexpected file in get_reg()");
	    return brw_null_reg();
    }

    assert(index < 256);
    assert(component < 4);

    /* see if we've already allocated a HW register for this Mesa register */
    if (c->wm_regs[file][index][component].inited) {
       /* yes, re-use */
       reg = c->wm_regs[file][index][component].reg;
    }
    else {
	/* no, allocate new register */
       int grf = alloc_grf(c);
       /*printf("alloc grf %d for reg %d:%d.%d\n", grf, file, index, component);*/
       if (grf < 0) {
          /* totally out of temps */
          grf = 51; /* XXX random register! */
       }

       reg = brw_vec8_grf(grf, 0);
       /*printf("Alloc new grf %d for %d.%d\n", reg.nr, index, component);*/

       set_reg(c, file, index, component, reg);
    }

    if (neg & (1 << component)) {
	reg = negate(reg);
    }
    if (abs)
	reg = brw_abs(reg);
    return reg;
}



/**
 * This is called if we run out of GRF registers.  Examine the live intervals
 * of temp regs in the program and free those which won't be used again.
 */
static void
reclaim_temps(struct brw_wm_compile *c)
{
   GLint intBegin[MAX_PROGRAM_TEMPS];
   GLint intEnd[MAX_PROGRAM_TEMPS];
   int index;

   /*printf("Reclaim temps:\n");*/

   _mesa_find_temp_intervals(c->prog_instructions, c->nr_fp_insns,
                             intBegin, intEnd);

   for (index = 0; index < MAX_PROGRAM_TEMPS; index++) {
      if (intEnd[index] != -1 && intEnd[index] < c->cur_inst) {
         /* program temp[i] can be freed */
         int component;
         /*printf("  temp[%d] is dead\n", index);*/
         for (component = 0; component < 4; component++) {
            if (c->wm_regs[PROGRAM_TEMPORARY][index][component].inited) {
               int r = c->wm_regs[PROGRAM_TEMPORARY][index][component].reg.nr;
               release_grf(c, r);
               /*
               printf("  Reclaim temp %d, reg %d at inst %d\n",
                      index, r, c->cur_inst);
               */
               c->wm_regs[PROGRAM_TEMPORARY][index][component].inited = GL_FALSE;
            }
         }
      }
   }
}




/**
 * Preallocate registers.  This sets up the Mesa to hardware register
 * mapping for certain registers, such as constants (uniforms/state vars)
 * and shader inputs.
 */
static void prealloc_reg(struct brw_wm_compile *c)
{
    struct intel_context *intel = &c->func.brw->intel;
    int i, j;
    struct brw_reg reg;
    int urb_read_length = 0;
    GLuint inputs = FRAG_BIT_WPOS | c->fp_interp_emitted;
    GLuint reg_index = 0;

    memset(c->used_grf, GL_FALSE, sizeof(c->used_grf));
    c->first_free_grf = 0;

    for (i = 0; i < 4; i++) {
	if (i < (c->key.nr_payload_regs + 1) / 2)
            reg = brw_vec8_grf(i * 2, 0);
        else
            reg = brw_vec8_grf(0, 0);
	set_reg(c, PROGRAM_PAYLOAD, PAYLOAD_DEPTH, i, reg);
    }
    set_reg(c, PROGRAM_PAYLOAD, PAYLOAD_W, 0,
	    brw_vec8_grf(c->key.source_w_reg, 0));
    reg_index += c->key.nr_payload_regs;

    /* constants */
    {
        const GLuint nr_params = c->fp->program.Base.Parameters->NumParameters;
        const GLuint nr_temps = c->fp->program.Base.NumTemporaries;

        /* use a real constant buffer, or just use a section of the GRF? */
        /* XXX this heuristic may need adjustment... */
        if ((nr_params + nr_temps) * 4 + reg_index > 80)
           c->fp->use_const_buffer = GL_TRUE;
        else
           c->fp->use_const_buffer = GL_FALSE;
        /*printf("WM use_const_buffer = %d\n", c->fp->use_const_buffer);*/

        if (c->fp->use_const_buffer) {
           /* We'll use a real constant buffer and fetch constants from
            * it with a dataport read message.
            */

           /* number of float constants in CURBE */
           c->prog_data.nr_params = 0;
        }
        else {
           const struct gl_program_parameter_list *plist = 
              c->fp->program.Base.Parameters;
           int index = 0;

           /* number of float constants in CURBE */
           c->prog_data.nr_params = 4 * nr_params;

           /* loop over program constants (float[4]) */
           for (i = 0; i < nr_params; i++) {
              /* loop over XYZW channels */
              for (j = 0; j < 4; j++, index++) {
                 reg = brw_vec1_grf(reg_index + index / 8, index % 8);
                 /* Save pointer to parameter/constant value.
                  * Constants will be copied in prepare_constant_buffer()
                  */
                 c->prog_data.param[index] = &plist->ParameterValues[i][j];
                 set_reg(c, PROGRAM_STATE_VAR, i, j, reg);
              }
           }
           /* number of constant regs used (each reg is float[8]) */
	   c->nr_creg = ALIGN(nr_params, 2) / 2;
	   reg_index += c->nr_creg;
        }
    }

    /* fragment shader inputs: One 2-reg pair of interpolation
     * coefficients for each vec4 to be set up.
     */
    if (intel->gen >= 6) {
       for (i = 0; i < FRAG_ATTRIB_MAX; i++) {
	  if (!(c->fp->program.Base.InputsRead & BITFIELD64_BIT(i)))
	     continue;

	  reg = brw_vec8_grf(reg_index, 0);
	  for (j = 0; j < 4; j++) {
	     set_reg(c, PROGRAM_PAYLOAD, i, j, reg);
	  }
	  reg_index += 2;
       }
       urb_read_length = reg_index;
    } else {
       for (i = 0; i < VERT_RESULT_MAX; i++) {
	  int fp_input;

	  if (i >= VERT_RESULT_VAR0)
	     fp_input = i - VERT_RESULT_VAR0 + FRAG_ATTRIB_VAR0;
	  else if (i <= VERT_RESULT_TEX7)
	     fp_input = i;
	  else
	     fp_input = -1;

	  if (fp_input >= 0 && inputs & (1 << fp_input)) {
	     urb_read_length = reg_index;
	     reg = brw_vec8_grf(reg_index, 0);
	     for (j = 0; j < 4; j++)
		set_reg(c, PROGRAM_PAYLOAD, fp_input, j, reg);
	  }
	  if (c->key.vp_outputs_written & BITFIELD64_BIT(i)) {
	     reg_index += 2;
	  }
       }
    }

    c->prog_data.first_curbe_grf = c->key.nr_payload_regs;
    c->prog_data.urb_read_length = urb_read_length;
    c->prog_data.curb_read_length = c->nr_creg;
    c->emit_mask_reg = brw_uw1_reg(BRW_GENERAL_REGISTER_FILE, reg_index, 0);
    reg_index++;
    c->stack =  brw_uw16_reg(BRW_GENERAL_REGISTER_FILE, reg_index, 0);
    reg_index += 2;

    /* mark GRF regs [0..reg_index-1] as in-use */
    for (i = 0; i < reg_index; i++)
       prealloc_grf(c, i);

    /* Don't use GRF 126, 127.  Using them seems to lead to GPU lock-ups */
    prealloc_grf(c, 126);
    prealloc_grf(c, 127);

    for (i = 0; i < c->nr_fp_insns; i++) {
	const struct prog_instruction *inst = &c->prog_instructions[i];
	struct brw_reg dst[4];

	switch (inst->Opcode) {
	case OPCODE_TEX:
	case OPCODE_TXB:
	    /* Allocate the channels of texture results contiguously,
	     * since they are written out that way by the sampler unit.
	     */
	    for (j = 0; j < 4; j++) {
		dst[j] = get_dst_reg(c, inst, j);
		if (j != 0)
		    assert(dst[j].nr == dst[j - 1].nr + 1);
	    }
	    break;
	default:
	    break;
	}
    }

    for (i = 0; i < c->nr_fp_insns; i++) {
	const struct prog_instruction *inst = &c->prog_instructions[i];

	switch (inst->Opcode) {
	case WM_DELTAXY:
	    /* Allocate WM_DELTAXY destination on G45/GM45 to an
	     * even-numbered GRF if possible so that we can use the PLN
	     * instruction.
	     */
	    if (inst->DstReg.WriteMask == WRITEMASK_XY &&
		!c->wm_regs[inst->DstReg.File][inst->DstReg.Index][0].inited &&
		!c->wm_regs[inst->DstReg.File][inst->DstReg.Index][1].inited &&
		(IS_G4X(intel->intelScreen->deviceID) || intel->gen == 5)) {
		int grf;

		for (grf = c->first_free_grf & ~1;
		     grf < BRW_WM_MAX_GRF;
		     grf += 2)
		{
		    if (!c->used_grf[grf] && !c->used_grf[grf + 1]) {
			c->used_grf[grf] = GL_TRUE;
			c->used_grf[grf + 1] = GL_TRUE;
			c->first_free_grf = grf + 2;  /* a guess */

			set_reg(c, inst->DstReg.File, inst->DstReg.Index, 0,
				brw_vec8_grf(grf, 0));
			set_reg(c, inst->DstReg.File, inst->DstReg.Index, 1,
				brw_vec8_grf(grf + 1, 0));
			break;
		    }
		}
	    }
	default:
	    break;
	}
    }

    /* An instruction may reference up to three constants.
     * They'll be found in these registers.
     * XXX alloc these on demand!
     */
    if (c->fp->use_const_buffer) {
       for (i = 0; i < 3; i++) {
          c->current_const[i].index = -1;
          c->current_const[i].reg = brw_vec8_grf(alloc_grf(c), 0);
       }
    }
#if 0
    printf("USE CONST BUFFER? %d\n", c->fp->use_const_buffer);
    printf("AFTER PRE_ALLOC, reg_index = %d\n", reg_index);
#endif
}


/**
 * Check if any of the instruction's src registers are constants, uniforms,
 * or statevars.  If so, fetch any constants that we don't already have in
 * the three GRF slots.
 */
static void fetch_constants(struct brw_wm_compile *c,
                            const struct prog_instruction *inst)
{
   struct brw_compile *p = &c->func;
   GLuint i;

   /* loop over instruction src regs */
   for (i = 0; i < 3; i++) {
      const struct prog_src_register *src = &inst->SrcReg[i];
      if (src->File == PROGRAM_STATE_VAR ||
          src->File == PROGRAM_CONSTANT ||
          src->File == PROGRAM_UNIFORM) {
	 c->current_const[i].index = src->Index;

#if 0
	 printf("  fetch const[%d] for arg %d into reg %d\n",
		src->Index, i, c->current_const[i].reg.nr);
#endif

	 /* need to fetch the constant now */
	 brw_dp_READ_4(p,
		       c->current_const[i].reg,  /* writeback dest */
		       src->RelAddr,             /* relative indexing? */
		       16 * src->Index,          /* byte offset */
		       SURF_INDEX_FRAG_CONST_BUFFER/* binding table index */
		       );
      }
   }
}


/**
 * Convert Mesa dst register to brw register.
 */
static struct brw_reg get_dst_reg(struct brw_wm_compile *c, 
                                  const struct prog_instruction *inst,
                                  GLuint component)
{
    const int nr = 1;
    return get_reg(c, inst->DstReg.File, inst->DstReg.Index, component, nr,
	    0, 0);
}


static struct brw_reg
get_src_reg_const(struct brw_wm_compile *c,
                  const struct prog_instruction *inst,
                  GLuint srcRegIndex, GLuint component)
{
   /* We should have already fetched the constant from the constant
    * buffer in fetch_constants().  Now we just have to return a
    * register description that extracts the needed component and
    * smears it across all eight vector components.
    */
   const struct prog_src_register *src = &inst->SrcReg[srcRegIndex];
   struct brw_reg const_reg;

   assert(component < 4);
   assert(srcRegIndex < 3);
   assert(c->current_const[srcRegIndex].index != -1);
   const_reg = c->current_const[srcRegIndex].reg;

   /* extract desired float from the const_reg, and smear */
   const_reg = stride(const_reg, 0, 1, 0);
   const_reg.subnr = component * 4;

   if (src->Negate & (1 << component))
      const_reg = negate(const_reg);
   if (src->Abs)
      const_reg = brw_abs(const_reg);

#if 0
   printf("  form const[%d].%d for arg %d, reg %d\n",
          c->current_const[srcRegIndex].index,
          component,
          srcRegIndex,
          const_reg.nr);
#endif

   return const_reg;
}


/**
 * Convert Mesa src register to brw register.
 */
static struct brw_reg get_src_reg(struct brw_wm_compile *c, 
                                  const struct prog_instruction *inst,
                                  GLuint srcRegIndex, GLuint channel)
{
    const struct prog_src_register *src = &inst->SrcReg[srcRegIndex];
    const GLuint nr = 1;
    const GLuint component = GET_SWZ(src->Swizzle, channel);

    /* Only one immediate value can be used per native opcode, and it
     * has be in the src1 slot, so not all Mesa instructions will get
     * to take advantage of immediate constants.
     */
    if (brw_wm_arg_can_be_immediate(inst->Opcode, srcRegIndex)) {
       const struct gl_program_parameter_list *params;

       params = c->fp->program.Base.Parameters;

       /* Extended swizzle terms */
       if (component == SWIZZLE_ZERO) {
	  return brw_imm_f(0.0F);
       } else if (component == SWIZZLE_ONE) {
	  if (src->Negate)
	     return brw_imm_f(-1.0F);
	  else
	     return brw_imm_f(1.0F);
       }

       if (src->File == PROGRAM_CONSTANT) {
	  float f = params->ParameterValues[src->Index][component];

	  if (src->Abs)
	     f = fabs(f);
	  if (src->Negate)
	     f = -f;

	  return brw_imm_f(f);
       }
    }

    if (c->fp->use_const_buffer &&
        (src->File == PROGRAM_STATE_VAR ||
         src->File == PROGRAM_CONSTANT ||
         src->File == PROGRAM_UNIFORM)) {
       return get_src_reg_const(c, inst, srcRegIndex, component);
    }
    else {
       /* other type of source register */
       return get_reg(c, src->File, src->Index, component, nr, 
                      src->Negate, src->Abs);
    }
}

static void emit_arl(struct brw_wm_compile *c,
                     const struct prog_instruction *inst)
{
    struct brw_compile *p = &c->func;
    struct brw_reg src0, addr_reg;
    brw_set_saturate(p, (inst->SaturateMode != SATURATE_OFF) ? 1 : 0);
    addr_reg = brw_uw8_reg(BRW_ARCHITECTURE_REGISTER_FILE, 
                           BRW_ARF_ADDRESS, 0);
    src0 = get_src_reg(c, inst, 0, 0); /* channel 0 */
    brw_MOV(p, addr_reg, src0);
    brw_set_saturate(p, 0);
}

static INLINE struct brw_reg high_words( struct brw_reg reg )
{
    return stride( suboffset( retype( reg, BRW_REGISTER_TYPE_W ), 1 ),
		   0, 8, 2 );
}

static INLINE struct brw_reg low_words( struct brw_reg reg )
{
    return stride( retype( reg, BRW_REGISTER_TYPE_W ), 0, 8, 2 );
}

static INLINE struct brw_reg even_bytes( struct brw_reg reg )
{
    return stride( retype( reg, BRW_REGISTER_TYPE_B ), 0, 16, 2 );
}

static INLINE struct brw_reg odd_bytes( struct brw_reg reg )
{
    return stride( suboffset( retype( reg, BRW_REGISTER_TYPE_B ), 1 ),
		   0, 16, 2 );
}

/**
 * Resolve subroutine calls after code emit is done.
 */
static void post_wm_emit( struct brw_wm_compile *c )
{
    brw_resolve_cals(&c->func);
}

static void
get_argument_regs(struct brw_wm_compile *c,
		  const struct prog_instruction *inst,
		  int index,
		  struct brw_reg *dst,
		  struct brw_reg *regs,
		  int mask)
{
    struct brw_compile *p = &c->func;
    int i, j;

    for (i = 0; i < 4; i++) {
	if (mask & (1 << i)) {
	    regs[i] = get_src_reg(c, inst, index, i);

	    /* Unalias destination registers from our sources. */
	    if (regs[i].file == BRW_GENERAL_REGISTER_FILE) {
	       for (j = 0; j < 4; j++) {
		   if (memcmp(&regs[i], &dst[j], sizeof(regs[0])) == 0) {
		       struct brw_reg tmp = alloc_tmp(c);
		       brw_MOV(p, tmp, regs[i]);
		       regs[i] = tmp;
		       break;
		   }
	       }
	    }
	}
    }
}

static void brw_wm_emit_glsl(struct brw_context *brw, struct brw_wm_compile *c)
{
   struct intel_context *intel = &brw->intel;
#define MAX_IF_DEPTH 32
#define MAX_LOOP_DEPTH 32
    struct brw_instruction *if_inst[MAX_IF_DEPTH], *loop_inst[MAX_LOOP_DEPTH];
    int if_depth_in_loop[MAX_LOOP_DEPTH];
    GLuint i, if_depth = 0, loop_depth = 0;
    struct brw_compile *p = &c->func;
    struct brw_indirect stack_index = brw_indirect(0, 0);

    c->out_of_regs = GL_FALSE;

    if_depth_in_loop[loop_depth] = 0;

    prealloc_reg(c);
    brw_set_compression_control(p, BRW_COMPRESSION_NONE);
    brw_MOV(p, get_addr_reg(stack_index), brw_address(c->stack));

    if (intel->gen >= 6)
	brw_set_acc_write_control(p, 1);

    for (i = 0; i < c->nr_fp_insns; i++) {
        const struct prog_instruction *inst = &c->prog_instructions[i];
	int dst_flags;
	struct brw_reg args[3][4], dst[4];
	int j;
	int mark = mark_tmps( c );

        c->cur_inst = i;

#if 0
        printf("Inst %d: ", i);
        _mesa_print_instruction(inst);
#endif

        /* fetch any constants that this instruction needs */
        if (c->fp->use_const_buffer)
           fetch_constants(c, inst);

	if (inst->Opcode != OPCODE_ARL) {
	   for (j = 0; j < 4; j++) {
	      if (inst->DstReg.WriteMask & (1 << j))
		 dst[j] = get_dst_reg(c, inst, j);
	      else
		 dst[j] = brw_null_reg();
	   }
	}
	for (j = 0; j < brw_wm_nr_args(inst->Opcode); j++)
	    get_argument_regs(c, inst, j, dst, args[j], WRITEMASK_XYZW);

	dst_flags = inst->DstReg.WriteMask;
	if (inst->SaturateMode == SATURATE_ZERO_ONE)
	    dst_flags |= SATURATE;

	if (inst->CondUpdate)
	    brw_set_conditionalmod(p, BRW_CONDITIONAL_NZ);
	else
	    brw_set_conditionalmod(p, BRW_CONDITIONAL_NONE);

	switch (inst->Opcode) {
	    case WM_PIXELXY:
		emit_pixel_xy(c, dst, dst_flags);
		break;
	    case WM_DELTAXY: 
		emit_delta_xy(p, dst, dst_flags, args[0]);
		break;
	    case WM_PIXELW:
		emit_pixel_w(c, dst, dst_flags, args[0], args[1]);
		break;	
	    case WM_LINTERP:
		emit_linterp(p, dst, dst_flags, args[0], args[1]);
		break;
	    case WM_PINTERP:
		emit_pinterp(p, dst, dst_flags, args[0], args[1], args[2]);
		break;
	    case WM_CINTERP:
		emit_cinterp(p, dst, dst_flags, args[0]);
		break;
	    case WM_WPOSXY:
		emit_wpos_xy(c, dst, dst_flags, args[0]);
		break;
	    case WM_FB_WRITE:
		emit_fb_write(c, args[0], args[1], args[2],
			      INST_AUX_GET_TARGET(inst->Aux),
			      inst->Aux & INST_AUX_EOT);
		break;
	    case WM_FRONTFACING:
		emit_frontfacing(p, dst, dst_flags);
		break;
	    case OPCODE_ADD:
		emit_alu2(p, brw_ADD, dst, dst_flags, args[0], args[1]);
		break;
	    case OPCODE_ARL:
		emit_arl(c, inst);
		break;
	    case OPCODE_FRC:
		emit_alu1(p, brw_FRC, dst, dst_flags, args[0]);
		break;
	    case OPCODE_FLR:
		emit_alu1(p, brw_RNDD, dst, dst_flags, args[0]);
		break;
	    case OPCODE_LRP:
		emit_lrp(p, dst, dst_flags, args[0], args[1], args[2]);
		break;
	    case OPCODE_TRUNC:
		emit_alu1(p, brw_RNDZ, dst, dst_flags, args[0]);
		break;
	    case OPCODE_MOV:
	    case OPCODE_SWZ:
		emit_alu1(p, brw_MOV, dst, dst_flags, args[0]);
		break;
	    case OPCODE_DP2:
		emit_dp2(p, dst, dst_flags, args[0], args[1]);
		break;
	    case OPCODE_DP3:
		emit_dp3(p, dst, dst_flags, args[0], args[1]);
		break;
	    case OPCODE_DP4:
		emit_dp4(p, dst, dst_flags, args[0], args[1]);
		break;
	    case OPCODE_XPD:
		emit_xpd(p, dst, dst_flags, args[0], args[1]);
		break;
	    case OPCODE_DPH:
		emit_dph(p, dst, dst_flags, args[0], args[1]);
		break;
	    case OPCODE_RCP:
		emit_math1(c, BRW_MATH_FUNCTION_INV, dst, dst_flags, args[0]);
		break;
	    case OPCODE_RSQ:
		emit_math1(c, BRW_MATH_FUNCTION_RSQ, dst, dst_flags, args[0]);
		break;
	    case OPCODE_SIN:
		emit_math1(c, BRW_MATH_FUNCTION_SIN, dst, dst_flags, args[0]);
		break;
	    case OPCODE_COS:
		emit_math1(c, BRW_MATH_FUNCTION_COS, dst, dst_flags, args[0]);
		break;
	    case OPCODE_EX2:
		emit_math1(c, BRW_MATH_FUNCTION_EXP, dst, dst_flags, args[0]);
		break;
	    case OPCODE_LG2:
		emit_math1(c, BRW_MATH_FUNCTION_LOG, dst, dst_flags, args[0]);
		break;
	    case OPCODE_CMP:
		emit_cmp(p, dst, dst_flags, args[0], args[1], args[2]);
		break;
	    case OPCODE_MIN:	
		emit_min(p, dst, dst_flags, args[0], args[1]);
		break;
	    case OPCODE_MAX:	
		emit_max(p, dst, dst_flags, args[0], args[1]);
		break;
	    case OPCODE_DDX:
	    case OPCODE_DDY:
		emit_ddxy(p, dst, dst_flags, (inst->Opcode == OPCODE_DDX),
			  args[0]);
                break;
	    case OPCODE_SLT:
		emit_sop(p, dst, dst_flags,
			 BRW_CONDITIONAL_L, args[0], args[1]);
		break;
	    case OPCODE_SLE:
		emit_sop(p, dst, dst_flags,
			 BRW_CONDITIONAL_LE, args[0], args[1]);
		break;
	    case OPCODE_SGT:
		emit_sop(p, dst, dst_flags,
			 BRW_CONDITIONAL_G, args[0], args[1]);
		break;
	    case OPCODE_SGE:
		emit_sop(p, dst, dst_flags,
			 BRW_CONDITIONAL_GE, args[0], args[1]);
		break;
	    case OPCODE_SEQ:
		emit_sop(p, dst, dst_flags,
			 BRW_CONDITIONAL_EQ, args[0], args[1]);
		break;
	    case OPCODE_SNE:
		emit_sop(p, dst, dst_flags,
			 BRW_CONDITIONAL_NEQ, args[0], args[1]);
		break;
	    case OPCODE_SSG:
		emit_sign(p, dst, dst_flags, args[0]);
		break;
	    case OPCODE_MUL:
		emit_alu2(p, brw_MUL, dst, dst_flags, args[0], args[1]);
		break;
	    case OPCODE_POW:
		emit_math2(c, BRW_MATH_FUNCTION_POW,
			   dst, dst_flags, args[0], args[1]);
		break;
	    case OPCODE_MAD:
		emit_mad(p, dst, dst_flags, args[0], args[1], args[2]);
		break;
	    case OPCODE_TEX:
		emit_tex(c, dst, dst_flags, args[0],
			 get_reg(c, PROGRAM_PAYLOAD, PAYLOAD_DEPTH,
				 0, 1, 0, 0),
			 inst->TexSrcTarget,
			 inst->TexSrcUnit,
			 (c->key.shadowtex_mask & (1 << inst->TexSrcUnit)) != 0);
		break;
	    case OPCODE_TXB:
		emit_txb(c, dst, dst_flags, args[0],
			 get_reg(c, PROGRAM_PAYLOAD, PAYLOAD_DEPTH,
				 0, 1, 0, 0),
			 inst->TexSrcTarget,
			 c->fp->program.Base.SamplerUnits[inst->TexSrcUnit]);
		break;
	    case OPCODE_KIL_NV:
		emit_kil_nv(c);
		break;
	    case OPCODE_IF:
		assert(if_depth < MAX_IF_DEPTH);
		if_inst[if_depth++] = brw_IF(p, BRW_EXECUTE_8);
		if_depth_in_loop[loop_depth]++;
		break;
	    case OPCODE_ELSE:
		assert(if_depth > 0);
		if_inst[if_depth-1]  = brw_ELSE(p, if_inst[if_depth-1]);
		break;
	    case OPCODE_ENDIF:
		assert(if_depth > 0);
		brw_ENDIF(p, if_inst[--if_depth]);
		if_depth_in_loop[loop_depth]--;
		break;
	    case OPCODE_BGNSUB:
		brw_save_label(p, inst->Comment, p->nr_insn);
		break;
	    case OPCODE_ENDSUB:
		/* no-op */
		break;
	    case OPCODE_CAL: 
		brw_push_insn_state(p);
		brw_set_mask_control(p, BRW_MASK_DISABLE);
                brw_set_access_mode(p, BRW_ALIGN_1);
                brw_ADD(p, deref_1ud(stack_index, 0), brw_ip_reg(), brw_imm_d(3*16));
                brw_set_access_mode(p, BRW_ALIGN_16);
                brw_ADD(p, get_addr_reg(stack_index),
                         get_addr_reg(stack_index), brw_imm_d(4));
		brw_save_call(&c->func, inst->Comment, p->nr_insn);
                brw_ADD(p, brw_ip_reg(), brw_ip_reg(), brw_imm_d(1*16));
                brw_pop_insn_state(p);
		break;

	    case OPCODE_RET:
		brw_push_insn_state(p);
		brw_set_mask_control(p, BRW_MASK_DISABLE);
                brw_ADD(p, get_addr_reg(stack_index),
                        get_addr_reg(stack_index), brw_imm_d(-4));
                brw_set_access_mode(p, BRW_ALIGN_1);
                brw_MOV(p, brw_ip_reg(), deref_1ud(stack_index, 0));
                brw_set_access_mode(p, BRW_ALIGN_16);
		brw_pop_insn_state(p);

		break;
	    case OPCODE_BGNLOOP:
                /* XXX may need to invalidate the current_constant regs */
		loop_inst[loop_depth++] = brw_DO(p, BRW_EXECUTE_8);
		if_depth_in_loop[loop_depth] = 0;
		break;
	    case OPCODE_BRK:
		brw_BREAK(p, if_depth_in_loop[loop_depth]);
		brw_set_predicate_control(p, BRW_PREDICATE_NONE);
		break;
	    case OPCODE_CONT:
		brw_CONT(p, if_depth_in_loop[loop_depth]);
		brw_set_predicate_control(p, BRW_PREDICATE_NONE);
		break;
	    case OPCODE_ENDLOOP: 
               {
                  struct brw_instruction *inst0, *inst1;
                  GLuint br = 1;

                  if (intel->gen == 5)
                     br = 2;

		  assert(loop_depth > 0);
                  loop_depth--;
                  inst0 = inst1 = brw_WHILE(p, loop_inst[loop_depth]);
                  /* patch all the BREAK/CONT instructions from last BGNLOOP */
                  while (inst0 > loop_inst[loop_depth]) {
                     inst0--;
                     if (inst0->header.opcode == BRW_OPCODE_BREAK &&
			 inst0->bits3.if_else.jump_count == 0) {
			inst0->bits3.if_else.jump_count = br * (inst1 - inst0 + 1);
                     }
                     else if (inst0->header.opcode == BRW_OPCODE_CONTINUE &&
			      inst0->bits3.if_else.jump_count == 0) {
                        inst0->bits3.if_else.jump_count = br * (inst1 - inst0);
                     }
                  }
               }
               break;
	    default:
		printf("unsupported opcode %d (%s) in fragment shader\n",
		       inst->Opcode, inst->Opcode < MAX_OPCODE ?
		       _mesa_opcode_string(inst->Opcode) : "unknown");
	}

	/* Release temporaries containing any unaliased source regs. */
	release_tmps( c, mark );

	if (inst->CondUpdate)
	    brw_set_predicate_control(p, BRW_PREDICATE_NORMAL);
	else
	    brw_set_predicate_control(p, BRW_PREDICATE_NONE);
    }
    post_wm_emit(c);

    if (INTEL_DEBUG & DEBUG_WM) {
      printf("wm-native:\n");
      for (i = 0; i < p->nr_insn; i++)
	 brw_disasm(stdout, &p->store[i], intel->gen);
      printf("\n");
    }
}

/**
 * Do GPU code generation for shaders that use GLSL features such as
 * flow control.  Other shaders will be compiled with the 
 */
void brw_wm_glsl_emit(struct brw_context *brw, struct brw_wm_compile *c)
{
    if (INTEL_DEBUG & DEBUG_WM) {
        printf("brw_wm_glsl_emit:\n");
    }

    /* initial instruction translation/simplification */
    brw_wm_pass_fp(c);

    /* actual code generation */
    brw_wm_emit_glsl(brw, c);

    if (INTEL_DEBUG & DEBUG_WM) {
        brw_wm_print_program(c, "brw_wm_glsl_emit done");
    }

    c->prog_data.total_grf = num_grf_used(c);
    c->prog_data.total_scratch = 0;
}
