Using -qoptdebug to help debug optimized programs

The purpose of the -qoptdebug compiler option is to aid the debugging of optimized programs. It does this by creating pseudocode that maps more closely to the instructions and values of an optimized program than the original source code. When a program compiled with this option is loaded into a debugger, you will be debugging the pseudocode rather than your original source. By making optimizations explicit in pseudocode, you can gain a better understanding of how your program is really behaving under optimization. Files containing the pseudocode for your program are generated with the file suffix .optdbg. Only line debugging is supported for this feature.

Compile your program as in the following example:
xlc myprogram.c -O3 -qhot -g -qoptdebug
In this example, your source file is compiled to a.out. The pseudocode for the optimized program is written to a file called myprogram.optdbg which can be referred to while debugging your program.
Notes:
  • The -g or the -qlinedebug option must also be specified in order for the compiled executable to be debuggable. However, if neither of these options are specified, the pseudocode file <output_file>.optdbg containing the optimized pseudocode is still generated.
  • The -qoptdebug option only has an effect when one or more of the optimization options -qhot, -qsmp, -qpdf, or -qipa are specified, or when the optimization levels that imply these options are specified; that is, the optimization levels -O3, -O4, and -O5. The example shows the optimization options -qhot and -O3.

Debugging the optimized program

From the following examples, you can see how the compiler might apply optimizations to a simple program and how debugging it can differ from debugging your original source.

Example 1: Represents the original non-optimized code for a simple program. It presents a couple of optimization opportunities to the compiler. For example, the variables z and d are both assigned by the equivalent expressions x + y. Therefore, these two variables can be consolidated in the optimized source. Also, the loop can be unrolled. In the optimized source, you can see iterations of the loop listed explicitly.

Example 2: Represents a listing of the optimized source as shown in the debugger. Note the unrolled loop and the consolidation of values assigned by the x + y expression.

Example 3: Shows an example of stepping through the optimized source using the debugger. Note, there is no longer a correspondence between the line numbers for these statements in the optimized source as compared to the line numbers in the original source.

Example 1: Original code
#include "stdio.h"

void foo(int x, int y, char* w)
{
	char* s = w+1;
	char* t = w+1;
	int z = x + y;
	int d = x + y;
	int a = printf("TEST\n");

	for (int i = 0; i < 4; i++)
		printf("%d %d %d %s %s\n", a, z, d, s, t);
	}

int main()
{
	char d[] = "DEBUG";
	foo(3, 4, d);
	return 0;
}
Example 2: dbx debugger listing
(dbx) list
    1        3 |  void foo(long x, long y, char * w)
    2        4 |  {
    3        9 |    a = printf("TEST\n");
    4       12 |    printf("%d %d %d %s %s\n",a,x + y,x + y,
                          ((char *)w  + 1),((char *)w  + 1));
    5               printf("%d %d %d %s %s\n",a,x + y,x + y,
                          ((char *)w  + 1),((char *)w  + 1));
    6               printf("%d %d %d %s %s\n",a,x + y,x + y,
                          ((char *)w  + 1),((char *)w  + 1));
    7               printf("%d %d %d %s %s\n",a,x + y,x + y,
                          ((char *)w  + 1),((char *)w  + 1));
    8       13 |    return;
    9             } /* function */
   10
   11
   12       15 |  long main()
   13       16 |  {
   14       17 |    d$init$0 = "DEBUG";
   15       18 |    @PARM.x0 = 3;
   16               @PARM.y1 = 4;
   17               @PARM.w2 = &d;    
   18        9 |    a = printf("TEST\n");    
   19       12 |    printf("%d %d %d %s %s\n",a,@PARM.x0 + @PARM.y1,
                           @PARM.x0 + @PARM.y1,((char *)@PARM.w2  + 1),
                           ((char *)@PARM.w2  + 1));
   20               printf("%d %d %d %s %s\n",a,@PARM.x0 + @PARM.y1,
                           @PARM.x0 + @PARM.y1,((char *)@PARM.w2  + 1),
                           ((char *)@PARM.w2  + 1));    
   21               printf("%d %d %d %s %s\n",a,@PARM.x0 + @PARM.y1,
                           @PARM.x0 + @PARM.y1,((char *)@PARM.w2  + 1),
                           ((char *)@PARM.w2  + 1));    
   22               printf("%d %d %d %s %s\n",a,@PARM.x0 + @PARM.y1,
                           @PARM.x0 + @PARM.y1,((char *)@PARM.w2  + 1),
                           ((char *)@PARM.w2  + 1));    
   23       19 |    rstr = 0;    
   24               return rstr;    
   25       20 |  } /* function */
Example 3: Stepping through optimized source
(dbx) stop at 18
[1] stop at "myprogram.o.optdbg":18
(dbx) run
[1] stopped in main at line 18 in file "myprogram.o.optdbg"
   18        9 |    a = printf("TEST\n");
(dbx) cont
TEST
5 7 7 EBUG EBUG
5 7 7 EBUG EBUG
5 7 7 EBUG EBUG
5 7 7 EBUG EBUG

execution completed