Tracing Execution of Machine Description Rules

Cynbe ru Taren

The gcc description of a machine architecture centers around the machine description file, which in turn centers on define_insn and define_expand rules (among others).

Unfortunately, the interaction between these rules is often inobvious, compile-time checking is weak, and following their operation in gdb is often difficult. One might hope that gcc would include a switch for tracing execution of these rules, but alas it does not.

One can however construct a primitive but useful rule trace without too much effort. Here is how:

First, we'll need a commandline switch to enable rule tracing. Tracing all rule firings for a small test program is one thing; having such a trace running during routine compiles would be entirely too much of a good thing.

Suppose the target machine of your compiler is mymachine. In that case there will be a gcc/config/mymachine directory containing (in particular) mymachine.h, mymachine.md, mymachine.c and (of most immediate interest) mymachine.opt.

Add in mymachine.opt the code paragraph

mnarrate-passes
Target Report Mask(NARRATE_PASSES)
Announce passes on stdout as they start.
Once gcc is recompiled this will define a gcc commandline switch -mnarrate-passes and a macro TARGET_NARRATE_PASSES which will be TRUE when the commandline switch was provided and FALSE otherwise. Now visit passes.c, find the code paragraph
if (pass->execute)
    pass->execute();
and change it to
if (pass->execute) { if (TARGET_NARRATE_PASSES) {  printf("Starting pass '%s'\n",pass->name); fflush(stdout); }
    pass->execute(); }
(This is not very pretty formatting, but by leaving the line numbering the same we avoid needing to regenerate the TAGS file.)

If you now recompile gcc and compile your stimulus program with mymachine-elf-gcc -mnarrate-passes stimulus.c you will see pass-by-pass narration like

Starting pass 'useless'...
Starting pass 'lower'...
Starting pass 'eh'...
[...]

So far, so good! Tracing execution of individual machine description rules is a little bit more work. We will implement this by abusing the condition entries in the machine description rules. These may contain arbitrary C code, which is intended to test whether the rule should fire. We will change them to print out information about the rule as a side-effect of being called. Ugly but reasonably effective.

Start by visiting gcc/config/mymachine/mymachine.h. At the bottom add the lines

extern int  mymachine_seen_first_pass;
extern int  mymachine_trace( const char* name, int opcount, int predicate);

Next visit gcc/config/mymachine/mymachine.c and somewhere near the bottom add the code paragraph

int mymachine_seen_first_pass = 0;

/* This little hack is because some args segfault us;
 * by inspection the good args start with 'f' in hex,
 * so we avoid trying to print out any which do not.
 * Your milage may vary! :-)
 */
static int
mymachine_good_arg( rtx op ) {
    fprintf(stderr,"[%p]",(void*)op);
    if ((((unsigned int)op)>>28) != 0xf) { fprintf(stderr," (?)"); return FALSE; }
    return TRUE;
}

int
mymachine_trace( const char* name, int opcount, int predicate ) {
    if (mymachine_seen_first_pass && TARGET_NARRATE_PASSES && predicate) {
      int i;
      fprintf(stderr,"\n====> '%s' called:\n",name);
      for (i = 0; i < opcount; ++i) {
          fprintf(stderr,"  ==>     operands[%d]:",i);
          if (mymachine_good_arg(recog_data.operand[i]))  debug_rtx(recog_data.operand[i]);
      }
    }
    return predicate;
}
When the compiler runs and calls a rule predicate this will print the name of the rule plus its operands. (recog_data is from gcc/recog.h.)

The mymachine_seen_first_pass flag is needed because calls with invalid operands are made during compiler initialization, before the first pass, and the above code will segfault on these calls.

Next, update passes.c to set the above flag by changing the ugly

if (pass->execute) { if (TARGET_NARRATE_PASSES) {  printf("Starting pass '%s'\n",pass->name); fflush(stdout); }
    pass->execute(); }
to the even uglier
if (pass->execute) { if (TARGET_NARRATE_PASSES) {  printf("Starting pass '%s'\n",pass->name); fflush(stdout); mymachine_seen_first_pass = 1;}
    pass->execute(); }

Finally, go through your mymachine.md file wrapping calls to mymachine_trace() around the conditions of the rules of interest.

For a define_insn rule the condition is the third field. In the case of a simple define_insn with an empty condition like

(define_insn "addsi3"
   [(set (match_operand:SI 0 "register_operand"          "=r,r")
         (plus:SI (match_operand:SI 1 "register_operand" "%r,r")
                  (match_operand:SI 2 "arith_operand"     "r,I")))]
   ""
   "add%i2\\t%0, %1, %z2"
   [(set_attr "type" "alu")])
we simply change it to
(define_insn "addsi3"
   [(set (match_operand:SI 0 "register_operand"          "=r,r")
         (plus:SI (match_operand:SI 1 "register_operand" "%r,r")
                  (match_operand:SI 2 "arith_operand"     "r,I")))]
   "mymachine_trace(\"define_insn addsi3\",3,TRUE)"
   "add%i2\\t%0, %1, %z2"
   [(set_attr "type" "alu")])
Here '3' is the number of operands and TRUE is the condition value (an empty condition is equivalent to TRUE).

For a define_isn with a nonempty condition, we simply wrap our call around that condition. For example

(define_insn "movsi_internal"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=m, r,r,r,r,r,r,r")
        (match_operand:SI 1 "general_operand" "rM,m,rM,I,J,K,S,i"))]
  "(register_operand (operands[0], SImode) || reg_or_0_operand(operands[1], SImode))"
  "@
    stw%o0\\t%z1, %0
    ldw%o1\\t%0, %1
    mov\\t%0, %z1
    movi\\t%0, %1
    movui\\t%0, %1
    movhi\\t%0, %U1
    addi\\t%0, gp, %%gprel(%1)
    movhi\\t%0, %H1\;addi\\t%0, %0, %L1"
  [(set_attr "type" "st,ld,alu,alu,alu,alu,alu,alu")])
will become
(define_insn "movsi_internal"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=m, r,r,r,r,r,r,r")
        (match_operand:SI 1 "general_operand" "rM,m,rM,I,J,K,S,i"))]
  "mymachine_trace(\"define_insn movsi_internal\",2,(register_operand(operands[0], SImode) || reg_or_0_operand (operands[1], SImode)))"
  "@
    stw%o0\\t%z1, %0
    ldw%o1\\t%0, %1
    mov\\t%0, %z1
    movi\\t%0, %1
    movui\\t%0, %1
    movhi\\t%0, %U1
    addi\\t%0, gp, %%gprel(%1)
    movhi\\t%0, %H1\;addi\\t%0, %0, %L1"
  [(set_attr "type" "st,ld,alu,alu,alu,alu,alu,alu")])

For define_expand rules we do not use the condition (field 3) because it will only be evaluated once at initialization time. Instead we use the preparation-code slot (field four) which is guaranteed to be executed each time the rule is fired.

Thus, we change a rule like

(define_expand "addsi3"
   [(set (match_operand:SI 0 "register_operand"       "")
      (plus:SI (match_operand:SI 1 "register_operand" "")
       (match_operand:SI 2 "arith_operand"    "")))]
  ""
  "")
to
(define_expand "addsi3"
   [(set (match_operand:SI 0 "register_operand"       "")
      (plus:SI (match_operand:SI 1 "register_operand" "")
       (match_operand:SI 2 "arith_operand"    "")))]
  ""
  "mymachine_trace(\"define_expand addsi3\",3,TRUE);")

(Here the mymachine_trace() return value does not matter.)

Now when you do mymachine-elf-gcc -mnarrate-passes stimulus.c you will get not only pass-by-pass narration, but also narration like

====> 'define_expand addsi3' called:
  ==>     operands[0]:[0xf7e81bf4](mem:DC (reg/f:SI 38 fake_fp) [0 S16 A32])
  ==>     operands[1]:[0xf7f01070](reg:DC 39 fake_ap [-1])
  ==>     operands[2]:[0xabababab] (?)
each time a machine description rule is fired.

[ Back to Cynbe's Gcc Debugging Hints ]


Cynbe ru Taren
Last modified: Tue Oct 9 18:45:09 CDT 2012