Monday, October 22, 2007

Use at Least Two Compilers

I recently made some minor changes to Suneido. I compiled with MinGW and it worked fine.

A bit later I compiled with Visual C++ 7 (2003) since that produces the smallest, fastest code at the moment. It wouldn't run at all - crashed immediately on start up!?

I recompiled the MinGW version - it still worked fine.

I reverted my changes and the VC7 version worked again so it definitely appeared to be my changes.

I checked my changes several times but they looked fine.

Finally I found the problem. While making my changes I had done some minor refactoring. (I know, you shouldn't mix the two, but it seemed minor.) I had found something like:
int fn1()
{
static const list = symnum("list");
...
}

int fn2()
{
static const list = symnum("list");
...
}
So I eliminated the duplication by moving the constant outside the functions:
static const list = symnum("list");

int fn1()
{
...
}

int fn2()
{
...
}
The problem is that this changes when symnum is called. The old way it wasn't called until the functions were used the first time. The new way it is called during startup, probably before something else that it needs is initialized. The order of initialization is undefined and obviously varies between compilers. Sure enough, putting this back fixed the problem.

Lessons:
  • build with multiple compilers to catch this kind of dependence on undefined behavior
  • don't mix changes and refactoring, do them one at a time, test in between
If I hadn't caught this now it could have suddenly and mysteriously broken at some time in the future (when something affected the order of initialization) and taken me ten times as long to find because I would have had no idea it was related to this particular change.

No comments: