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
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 paragraphmnarrate-passes Target Report Mask(NARRATE_PASSES) Announce passes on stdout as they start.
and change it toif (pass->execute) 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 (pass->execute) { if (TARGET_NARRATE_PASSES) { printf("Starting pass '%s'\n",pass->name); fflush(stdout); } pass->execute(); }
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
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.)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; }
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
to the even uglierif (pass->execute) { if (TARGET_NARRATE_PASSES) { printf("Starting pass '%s'\n",pass->name); fflush(stdout); } pass->execute(); }
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
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")))] "" "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).(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")])
For a define_isn with a nonempty condition, we simply wrap our call around that condition. For example
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"))] "(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")])
(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
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" "")))] "" "")
(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
each time a machine description rule is fired.====> '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] (?)
[ Back to Cynbe's Gcc Debugging Hints ]