As described in the previous sections, ZEE supports the two most basic functions of a debugger: setting a breakpoint, and examining the program state once that breakpoint has been reached. In many cases, a single semantic breakpoint for the requested source-level breakpoint does not exist, due to the effects of optimization and parallelization. However, as the ZPL compiler optimizations do not eliminate code, there will will always be a representative instruction in the object code. Although this instruction will lie inside an mloop, and will be executed several times, some portion of the program state will be current at each iteration. By identifying and recording thase values using a combination of tracing and data-flow analysis, we can construct the expected Z-level state. If this ``constructed'' state is then provided for examination, the user need not be aware that the program is not actually stopped at the requested breakpoint.
For example, examine the program in Figure . The three C-factors resulting from lines 36 through 39 have been joined, resulting in the single mloop in the object code. If the user wishes to set a breakpoint on line 39 of the source, the debugger will not be able to map this to a single line of object code, because the assignment to XVel , YVel , and Vel have been merged together.
The representative instruction for line 39 is marked in the object code in Figure , but during any iteration i through the loop, the values in Vel[mylo..i-1] are non-current in RBV, while XVel[i..myhi] are non-current in RFV. If line 36 did not represent the reaching definition for XVel outside the optimized block (i.e. if there were a redefinition of XVel after line 39), then XVel[mylo..i-1] would also be non-current in RBV. (The insertion of temporaries leaves minvel and maxvel with current values, despite their calculations being merged into the mloop).
In selecting a semantic breakpoint for this situation, it is useful to note that upon exit from the mloop all non-current variables are in the roll-back set RBV. So if we halt the program after the loop, every variable with a live definition after the breakpoint must be rolled back, but no variables need to be rolled forward. In order for a variable v in RBV to be recoverable, the previous store to v must be available. While this is not necessarily the case at the semantic breakpoint (the mloop exit), it is certainly the case at the representative instruction, but only for a single element of the array. If we trace these values during the execution of the loop, we can reconstruct the expected value of the array after the execution of the entire block.
The same approach can be used to present the Z-Level view of promoted functions: although the local variables of such functions are scalar by definition, there is (conceptually) a separate copy of each variable for each thread. By constructing all values of these copies, we can present them in array format to represent the separate threads of the region.
Once the user has examined the constructed state of the program at a given breakpoint, he may wish to set another breakpoint whose representative instruction falls inside the same optimized block. However, since execution has already proceeded past this point, such a breakpoint cannot be immediatly constructed. For this reason, ZEE stores a checkpoint at the entrance to an optimized mloop. If two breakpoints inside the loop are requested, the computation is simply restarted from the checkpoint, and the second breakpoint is then constructed.