RSS Feed

Category Archives: Uncategorized

GCC compiler “bug” broke my Haxe code

Posted on

In hxBitcoin I have this utility function:

/**
    Unsigned greater than comparison.

    Returns `true` if `a > b` when both `a` and `b` are
    interpreted as unsigned integers; `false` otherwise.
**/
public static inline function u32gtu32(a : Int, b : Int) : Bool
{
    return (a + -2147483648) > (b + -2147483648); // unsigned comparison, see "Hacker's Delight" p. 25.
}

This nifty little trick is from the most excellent book Hacker’s Delight by Henry S. Warren, Jr.

The function worked great, until I ran my unit tests on Linux. u32gtu32(-2147483648, 2147483647) should return true, but it was returning false. A look at the generated C++ offered nothing obvious:

bool FunInteger_obj::u32gtu32( int a,int b){
    return ((a + (int)-2147483648) > (b + (int)-2147483648));
}

To make a long story short, I crafted this simple standalone test:

#include "stdio.h"
extern "C" int main(int argc, char ** argv)
{
    int a = -2147483648;
    int b = 2147483647;
    bool compare = (a + (int)(-2147483648)) > (b + (int)(-2147483648));
    printf("result = %d\n", compare ? 1 : 0);
    return 0;
}

Now this is where it gets fun and interesting!

$ g++ -O2 test.cpp && ./a.out
result = 0
$ g++ -O0 test.cpp && ./a.out
result = 1

Huh? Sure smells like a compiler optimization bug, doesn’t it?

Turns out this is a feature. For optimization purposes, by default GCC assumes there will be no overflow with integer arithmetic (see this).

There are a number of possible remedies, many of which are unappealing for various reasons (for example, adding -fwrapv or -fno-strict-overflow to the compiler options for Linux builds, or passing a special flag to the Haxe build and implementing the u32gtu32 function differently when necessary).

But this simple fix was just the thing — replace the addition with exclusive-or in the original function:

public static inline function u32gtu32(a : Int, b : Int) : Bool
{
    return (a ^ -2147483648) > (b ^ -2147483648); // unsigned comparison, see "Hacker's Delight" p. 25.
}

As a result of this experience, I also added some “run-time tests” — quick tests that are executed at run time even in a distribution release. The reason being that, from the end-user’s perspective, hxBitcoin is a pure Haxe library. The fact that on some platforms it is generated into C++ and compiled automatically by the Haxe/hxcpp framework is an implementation detail. A library user who installs hxBitcoin via haxelib likely will not run the full suite of unit tests. But the validity of the results, as I discovered, can depend greatly on the particulars of the compiler used. (Previously the same code exhibited no issues when compiled as C++ on Windows, OSX, and iOS.) The particular issue I encountered here would cause subtle, silent issues for the end-user without the presence of at least some minimal run-time validation.

Advertisements