Monday, July 15, 2013

Know Your Compiler

We've reached the half-way point for the 2013 Summer Retrochallenge event!  I would, of course, like to be further along with my Micro-C/6809 support package for the CoCo.  But, overall, I think the project is making good progress.  Over the weekend I posted about my success at getting some initial routines implemented, and I was able to show a simple low-res graphics program running.  In the process, I have learned a bit more about how that compiler works and how best to make use of it.

Cast Away

When I was implementing a routine to compute the address for a byte of graphics data, I discovered an anomaly.  I initially tried to use an expression that added a computed value to a constant offset, but found that the resulting value was incorrect.  Surprisingly, separating the addition of the computed value into a different line from the initial assignment of the offset value resulted in a correct result.  Reviewing the generated code showed that the non-working case was adding a 16-bit constant to an 8-bit register -- oops!

Compilers often have quirks, and "small" compilers often have some weird ones.  I suspected that I might have been hitting one of those, so I reviewed the docs that came with Micro-C.  The docs revealed that due to performance considerations (i.e. a preference for 8-bit operations), untyped constants used in an expression with 8-bit types are treated as 8-bit values.  I can't help but think that this sort of situation begs for throwing a warning at compile time, but alas it did not.  Anyway, adding an explicit typecast to a 16-bit type prompted the compiler into generating working code.

Optimism

As I was rooting around in the code generated by the compiler, I noted some oddities.  The code was doing some weird things, like a register store to memory followed immediately by a load from that same memory location to the exact same register.  This sub-optimal behavior reminded me that I was not telling the compiler to run the optimizer.  So, I added the "-o" option to the invocation of cc09 and observed many improvements to the generated code.

When running the test code built without optimization, I observed that the program didn't run significantly faster than the BASIC program upon which it was based.  I originally surmised that the slow speed was the result of including a keyboard scan inside the "blinking" loop.  However, the optimized code was easily observed to run much faster than the non-optimized code!  Maybe compiled languages really are faster... ;-)

Handiwork

The expression in question included a combination of multiplication and division by powers of two, which I recognized could be implemented with logical operations like masking and shifting.  I observed that even with optimization enabled, the generated code was still using multiply and divide operations to compute that expression.  A more modern compiler might be expected to recognize this situation and to use the logical operations instead, but the "peephole" optimizer included with Micro-C doesn't make the connection.

In order to squeeze out a little more performance, I re-implemented the expression using logical operations in the C code itself.  This generated the code as expected, and a modest performance gain was observed with the test program.  Sometimes you just have to take matters into your own hands!

I'll make a study of the generated code available for those interested in such things.  Next time I'll probably move onto implementing some solutions for better integration with BASIC...or maybe I'll do something else...  Anyway, I guess you'll have to stay tuned to find out!

1 comment: