i965: rewrite the code for handling shader subroutine calls
authorBrian Paul <brianp@vmware.com>
Sat, 14 Feb 2009 00:17:52 +0000 (17:17 -0700)
committerBrian Paul <brianp@vmware.com>
Sat, 14 Feb 2009 00:18:36 +0000 (17:18 -0700)
Previously, the prog_instruction::Data field was used to map original Mesa
instructions to brw instructions in order to resolve subroutine calls.  This
was a rather tangled mess.  Plus it's an obstacle to implementing dynamic
allocation/growing of the instruction buffer (it's still a fixed size).

Mesa's GLSL compiler emits a label for each subroutine and CAL instruction.
Now we use those labels to patch the subroutine calls after code generation
has been done.  We just keep a list of all CAL instructions that needs patching
and a list of all subroutine labels.  It's a simple matter to resolve them.

This also consolidates some redundant post-emit code between brw_vs_emit.c and
brw_wm_glsl.c and removes some loops that cleared the prog_instruction::Data
fields at the end.

Plus, a bunch of new comments.

src/mesa/drivers/dri/i965/brw_eu.c
src/mesa/drivers/dri/i965/brw_eu.h
src/mesa/drivers/dri/i965/brw_vs_emit.c
src/mesa/drivers/dri/i965/brw_wm.h
src/mesa/drivers/dri/i965/brw_wm_fp.c
src/mesa/drivers/dri/i965/brw_wm_glsl.c

index b3ae4eef334959ce7bc26fa6324b84b61a5a1d44..c53efba599105ed1e3bdadc1c08af287551d896e 100644 (file)
@@ -129,3 +129,126 @@ const GLuint *brw_get_program( struct brw_compile *p,
    return (const GLuint *)p->store;
 }
 
+
+
+/**
+ * Subroutine calls require special attention.
+ * Mesa instructions may be expanded into multiple hardware instructions
+ * so the prog_instruction::BranchTarget field can't be used as an index
+ * into the hardware instructions.
+ *
+ * The BranchTarget field isn't needed, however.  Mesa's GLSL compiler
+ * emits CAL and BGNSUB instructions with labels that can be used to map
+ * subroutine calls to actual subroutine code blocks.
+ *
+ * The structures and function here implement patching of CAL instructions
+ * so they jump to the right subroutine code...
+ */
+
+
+/**
+ * For each OPCODE_BGNSUB we create one of these.
+ */
+struct brw_glsl_label
+{
+   const char *name; /**< the label string */
+   GLuint position;  /**< the position of the brw instruction for this label */
+   struct brw_glsl_label *next;  /**< next in linked list */
+};
+
+
+/**
+ * For each OPCODE_CAL we create one of these.
+ */
+struct brw_glsl_call
+{
+   GLuint call_inst_pos;  /**< location of the CAL instruction */
+   const char *sub_name;  /**< name of subroutine to call */
+   struct brw_glsl_call *next;  /**< next in linked list */
+};
+
+
+/**
+ * Called for each OPCODE_BGNSUB.
+ */
+void
+brw_save_label(struct brw_compile *c, const char *name, GLuint position)
+{
+   struct brw_glsl_label *label = CALLOC_STRUCT(brw_glsl_label);
+   label->name = name;
+   label->position = position;
+   label->next = c->first_label;
+   c->first_label = label;
+}
+
+
+/**
+ * Called for each OPCODE_CAL.
+ */
+void
+brw_save_call(struct brw_compile *c, const char *name, GLuint call_pos)
+{
+   struct brw_glsl_call *call = CALLOC_STRUCT(brw_glsl_call);
+   call->call_inst_pos = call_pos;
+   call->sub_name = name;
+   call->next = c->first_call;
+   c->first_call = call;
+}
+
+
+/**
+ * Lookup a label, return label's position/offset.
+ */
+static GLuint
+brw_lookup_label(struct brw_compile *c, const char *name)
+{
+   const struct brw_glsl_label *label;
+   for (label = c->first_label; label; label = label->next) {
+      if (strcmp(name, label->name) == 0) {
+         return label->position;
+      }
+   }
+   abort();  /* should never happen */
+   return ~0;
+}
+
+
+/**
+ * When we're done generating code, this function is called to resolve
+ * subroutine calls.
+ */
+void
+brw_resolve_cals(struct brw_compile *c)
+{
+    const struct brw_glsl_call *call;
+
+    for (call = c->first_call; call; call = call->next) {
+        const GLuint sub_loc = brw_lookup_label(c, call->sub_name);
+       struct brw_instruction *brw_call_inst = &c->store[call->call_inst_pos];
+       struct brw_instruction *brw_sub_inst = &c->store[sub_loc];
+       GLint offset = brw_sub_inst - brw_call_inst;
+
+       /* patch brw_inst1 to point to brw_inst2 */
+       brw_set_src1(brw_call_inst, brw_imm_d(offset * 16));
+    }
+
+    /* free linked list of calls */
+    {
+        struct brw_glsl_call *call, *next;
+        for (call = c->first_call; call; call = next) {
+           next = call->next;
+           _mesa_free(call);
+       }
+       c->first_call = NULL;
+    }
+
+    /* free linked list of labels */
+    {
+        struct brw_glsl_label *label, *next;
+       for (label = c->first_label; label; label = next) {
+           next = label->next;
+           _mesa_free(label);
+       }
+       c->first_label = NULL;
+    }
+}
index 9e2b39af9bba7dd9e71dca08cbb91c6ee0e1cb9e..b36a1976efc9fc646d3f936d3b3d54cca683b8ab 100644 (file)
@@ -91,6 +91,11 @@ struct brw_indirect {
 };
 
 
+struct brw_glsl_label;
+struct brw_glsl_call;
+
+
+
 #define BRW_EU_MAX_INSN_STACK 5
 #define BRW_EU_MAX_INSN 1200
 
@@ -106,9 +111,22 @@ struct brw_compile {
    GLuint flag_value;
    GLboolean single_program_flow;
    struct brw_context *brw;
+
+   struct brw_glsl_label *first_label;  /**< linked list of labels */
+   struct brw_glsl_call *first_call;    /**< linked list of CALs */
 };
 
 
+void
+brw_save_label(struct brw_compile *c, const char *name, GLuint position);
+
+void
+brw_save_call(struct brw_compile *c, const char *name, GLuint call_pos);
+
+void
+brw_resolve_cals(struct brw_compile *c);
+
+
 
 static INLINE int type_sz( GLuint type )
 {
index a1328f69671b9ce55568ecdcf909ef1e0cc31d1f..235f826812e19c9a4d1bd6df2ef4fe5e23a9f483 100644 (file)
@@ -954,36 +954,27 @@ static void emit_vertex_write( struct brw_vs_compile *c)
 }
 
 
+/**
+ * Called after code generation to resolve subroutine calls and the
+ * END instruction.
+ * \param end_inst  points to brw code for END instruction
+ * \param last_inst  points to last instruction emitted before vertex write
+ */
 static void 
-post_vs_emit( struct brw_vs_compile *c, struct brw_instruction *end_inst )
+post_vs_emit( struct brw_vs_compile *c,
+              struct brw_instruction *end_inst,
+              struct brw_instruction *last_inst )
 {
-   GLuint nr_insns = c->vp->program.Base.NumInstructions;
-   GLuint insn, target_insn;
-   struct prog_instruction *inst1, *inst2;
-   struct brw_instruction *brw_inst1, *brw_inst2;
-   int offset;
-   for (insn = 0; insn < nr_insns; insn++) {
-       inst1 = &c->vp->program.Base.Instructions[insn];
-       brw_inst1 = inst1->Data;
-       switch (inst1->Opcode) {
-          case OPCODE_CAL:
-          case OPCODE_BRA:
-              target_insn = inst1->BranchTarget;
-              inst2 = &c->vp->program.Base.Instructions[target_insn];
-              brw_inst2 = inst2->Data;
-              offset = brw_inst2 - brw_inst1;
-              brw_set_src1(brw_inst1, brw_imm_d(offset*16));
-              break;
-          case OPCODE_END:
-              offset = end_inst - brw_inst1;
-              brw_set_src1(brw_inst1, brw_imm_d(offset*16));
-              break;
-          default:
-              break;
-       }
-   }
+   GLint offset;
+
+   brw_resolve_cals(&c->func);
+
+   /* patch up the END code to jump past subroutines, etc */
+   offset = last_inst - end_inst;
+   brw_set_src1(end_inst, brw_imm_d(offset * 16));
 }
 
+
 /* Emit the fragment program instructions here.
  */
 void brw_vs_emit(struct brw_vs_compile *c )
@@ -992,7 +983,8 @@ void brw_vs_emit(struct brw_vs_compile *c )
    struct brw_compile *p = &c->func;
    GLuint nr_insns = c->vp->program.Base.NumInstructions;
    GLuint insn, if_insn = 0;
-   struct brw_instruction *end_inst;
+   GLuint end_offset = 0;
+   struct brw_instruction *end_inst, *last_inst;
    struct brw_instruction *if_inst[MAX_IFSN];
    struct brw_indirect stack_index = brw_indirect(0, 0);   
 
@@ -1035,7 +1027,6 @@ void brw_vs_emit(struct brw_vs_compile *c )
       
       /* Get argument regs.  SWZ is special and does this itself.
        */
-      inst->Data = &p->store[p->nr_insn];
       if (inst->Opcode != OPCODE_SWZ)
          for (i = 0; i < 3; i++) {
              struct prog_src_register *src = &inst->SrcReg[i];
@@ -1203,7 +1194,7 @@ void brw_vs_emit(struct brw_vs_compile *c )
         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));
-        inst->Data = &p->store[p->nr_insn];
+         brw_save_call(p, inst->Comment, p->nr_insn);
         brw_ADD(p, brw_ip_reg(), brw_ip_reg(), brw_imm_d(1*16));
          break;
       case OPCODE_RET:
@@ -1214,13 +1205,21 @@ void brw_vs_emit(struct brw_vs_compile *c )
         brw_set_access_mode(p, BRW_ALIGN_16);
         break;
       case OPCODE_END: 
+         end_offset = p->nr_insn;
+         /* this instruction will get patched later to jump past subroutine
+          * code, etc.
+          */
          brw_ADD(p, brw_ip_reg(), brw_ip_reg(), brw_imm_d(1*16));
          break;
       case OPCODE_PRINT:
+         /* no-op */
+         break;
       case OPCODE_BGNSUB:
+         brw_save_label(p, inst->Comment, p->nr_insn);
+         break;
       case OPCODE_ENDSUB:
-         /* no-op instructions */
-        break;
+         /* no-op */
+         break;
       default:
         _mesa_problem(NULL, "Unsupported opcode %i (%s) in vertex shader",
                        inst->Opcode, inst->Opcode < MAX_OPCODE ?
@@ -1258,9 +1257,11 @@ void brw_vs_emit(struct brw_vs_compile *c )
       release_tmps(c);
    }
 
-   end_inst = &p->store[p->nr_insn];
+   end_inst = &p->store[end_offset];
+   last_inst = &p->store[p->nr_insn];
+
+   /* The END instruction will be patched to jump to this code */
    emit_vertex_write(c);
-   post_vs_emit(c, end_inst);
-   for (insn = 0; insn < nr_insns; insn++)
-       c->vp->program.Base.Instructions[insn].Data = NULL;
+
+   post_vs_emit(c, end_inst, last_inst);
 }
index 0f46a25b1a17425e642dbaf7f71056d03c8e3df3..582031f5f6c9c5a1856c1ef147ea8cbc38083199 100644 (file)
@@ -281,4 +281,6 @@ void brw_wm_lookup_iz( GLuint line_aa,
 
 GLboolean brw_wm_is_glsl(const struct gl_fragment_program *fp);
 void brw_wm_glsl_emit(struct brw_context *brw, struct brw_wm_compile *c);
+
+
 #endif
index 0a44f8bcca168772e008aacc1444f0b80ff1df2a..8b5ccdba931983983d7f45b5ef9380c1145d5cb9 100644 (file)
@@ -183,7 +183,6 @@ static struct prog_instruction *emit_insn(struct brw_wm_compile *c,
 {
    struct prog_instruction *inst = get_fp_inst(c);
    *inst = *inst0;
-   inst->Data = (void *)inst0;
    return inst;
 }
 
index 0d3e2eb7701852c4862550112b4b811330b5f2ba..d99aa372a9b9d3ba1b586c10d0a4e6430ad43532 100644 (file)
@@ -2252,28 +2252,12 @@ static void emit_tex(struct brw_wm_compile *c,
        brw_MOV(p, dst[3], brw_imm_f(1.0));
 }
 
+/**
+ * Resolve subroutine calls after code emit is done.
+ */
 static void post_wm_emit( struct brw_wm_compile *c )
 {
-    GLuint nr_insns = c->fp->program.Base.NumInstructions;
-    GLuint insn, target_insn;
-    struct prog_instruction *inst1, *inst2;
-    struct brw_instruction *brw_inst1, *brw_inst2;
-    int offset;
-    for (insn = 0; insn < nr_insns; insn++) {
-       inst1 = &c->fp->program.Base.Instructions[insn];
-       brw_inst1 = inst1->Data;
-       switch (inst1->Opcode) {
-           case OPCODE_CAL:
-               target_insn = inst1->BranchTarget;
-               inst2 = &c->fp->program.Base.Instructions[target_insn];
-               brw_inst2 = inst2->Data;
-               offset = brw_inst2 - brw_inst1;
-               brw_set_src1(brw_inst1, brw_imm_d(offset*16));
-               break;
-           default:
-               break;
-       }
-    }
+    brw_resolve_cals(&c->func);
 }
 
 static void brw_wm_emit_glsl(struct brw_context *brw, struct brw_wm_compile *c)
@@ -2293,10 +2277,6 @@ static void brw_wm_emit_glsl(struct brw_context *brw, struct brw_wm_compile *c)
 
     for (i = 0; i < c->nr_fp_insns; i++) {
        struct prog_instruction *inst = &c->prog_instructions[i];
-       struct prog_instruction *orig_inst;
-
-       if ((orig_inst = inst->Data) != 0)
-           orig_inst->Data = current_insn(p);
 
        if (inst->CondUpdate)
            brw_set_conditionalmod(p, BRW_CONDITIONAL_NZ);
@@ -2454,7 +2434,10 @@ static void brw_wm_emit_glsl(struct brw_context *brw, struct brw_wm_compile *c)
                brw_ENDIF(p, if_inst[--if_insn]);
                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);
@@ -2464,8 +2447,7 @@ static void brw_wm_emit_glsl(struct brw_context *brw, struct brw_wm_compile *c)
                 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));
-                orig_inst = inst->Data;
-                orig_inst->Data = &p->store[p->nr_insn];
+               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;
@@ -2518,8 +2500,6 @@ static void brw_wm_emit_glsl(struct brw_context *brw, struct brw_wm_compile *c)
            brw_set_predicate_control(p, BRW_PREDICATE_NONE);
     }
     post_wm_emit(c);
-    for (i = 0; i < c->fp->program.Base.NumInstructions; i++)
-       c->fp->program.Base.Instructions[i].Data = NULL;
 }