<!-- Start of markdown source --> CLA has a hardware flaw that affects integer comparisons. The signed integer comparison instruction overflows if the difference is too large, such as when the inputs have opposite sign and are near the extreme values. This sets the status register incorrectly. The compiler implements a workaround for this problem; it will check the upper bits with a float comparison before proceeding to do the integer comparison. ### Background CLA comparisons set bits in the SR (status register) to indicate the result. These bits can be set by both the integer comparison instruction MCMP32 and ordinary integer arithmetic instructions, such as MSUB32. For integers, only NF (negative flag) and ZF (zero flag) are relevant (while CLA does have underflow/overflow flags, those flags only apply to floating-point instructions). Once these bits are set, conditional instructions can use one of the following conditions: condition name | meaning | when true ---------------|-----------------------|---------- NEQ | not equal | ZF == 0 EQ | equal | ZF == 1 GT | greater than | ZF == 0 and NF == 0 GEQ | greater than or equal | NF == 0 LT | less than | NF == 1 LEQ | less than or equal | ZF == 1 or NF == 1 The CLA instruction MCMP32 is a 32-bit signed integer comparison. CLA does not have an unsigned integer comparison instruction. The compiler implements unsigned integer comparison with the signed integer comparison instruction by transforming the operands into signed operands such that the result of the comparison of the transformed operands is the same as intended by the original operation. ### Problem The [TMS320x2806x Piccolo Technical Reference Manual SPRUH18G (Apr 2017) ](https://www.ti.com/lit/ug/spruh18g/spruh18g.pdf) says (page 592, MCMP32): > Set ZF and NF flags on the result of MRa - MRb where MRa and MRb are 32-bit integers. > If (MRa == MRb) {ZF=1; NF=0;} > If (MRa > MRb) {ZF=0; NF=0;} > If (MRa < MRb) {ZF=0; NF=1;} This reference manual also says (page 665, MSUB32) that the NF and ZF flags are set based on the bits in the result register, not the values of the input. In the case of overflow, the NF flag will be incorrect. Signed subtraction overflow does not matter to the compiler because it is considered an error (undefined behavior) on the part of the user. However, comparison must not overflow, even if subtracting those inputs would overflow. Consider the inputs ``` MR0 = 0x1 MR1 = 0x80000001 (-2147483647) ``` Note that ``` 1 - (-2147483647) ==> 2147483648 (overflow!) ``` The actual (infinite precision) result is a large positive number, but "MSUB32 MRx, MR0, MR1" sets NF=1, indicating a negative result. Clearly MR0 > MR1, but because MCMP32 behaves as if it were MSUB32, "MCMP32 MR0, MR1" sets ZF=0 and NF=1. This means that MCMP32 cannot reliably compare arbitrary signed integer values if subtraction of those values would overflow. (Note: EQ and NEQ are OK.) ### Detection The compiler will detect comparisons that could have the problem. The compiler will not emit this warning for every integer comparison. It will not emit a warning if the comparison can be shown to be safe. For example, all comparisons to zero are safe. Comparisons between two short ints are also safe. ``` % cat foo.cla int foo(int x, int y) { return x < y; } cl2000 foo.cla --cla_support "foo.cla", line 1: warning #30013-D: Comparison operation uses integer comparison instruction, which does not operate properly for values that would overflow subtraction. Use --cla_signed_compare_workaround=on to have the compiler work around this issue. ``` [[y Implementation Detection of the problem was implemented in C2000 compiler version 18.12.0.LTS ]] ### Workaround For each integer comparison, we need to ensure that either MCMP32 operates correctly on the inputs, or compute the comparison in some other way. If we're going to use the MCMP32 instruction, we need to ensure that the inputs satisfy: ``` -2147483648 <= (int64_t)x - (int64_t)y (int64_t)x - (int64_t)y <= 2147483647 ``` ![a graph](images/CGT-CLA-signed-comparison-problem-region.png) Because unsigned integer comparison is implemented in terms of signed integer comparison, it too has the problem. To be safe, the inputs must satisfy: ``` -2147483648 <= (int64_t)(0x80000000^x) - (int64_t)(0x80000000^y) (int64_t)(0x80000000^x) - (int64_t)(0x80000000^y) <= 2147483647 ``` ![a graph](images/CGT-CLA-unsigned-comparison-problem-region.png) #### Manual Workaround Suppose the compiler indicates there might be a problem with a particular comparison, perhaps: `if (x < y)` There are a few ways to work around the problem in the source code. If you know that the integer value of the operands are such that a subtraction will not overflow, you can give the compiler clues that this comparison is safe: `if (__mlt(x, y))` The "safe" comparison intrisics are \__mlt, \__mleq, \__mgt, and \__mgeq, and the unsigned equivalents \__mltu, \__mlequ, \__mgtu, \__mgequ If you know that the inputs will fit in a short, do this: `if ((short)x < (short)y)` or `for (short i = 0; i < (short)y); i++)` If you know your inputs will fit into float without losing precision, do this: `if ((float)x < (float)y)` or `for (float i = 0; i < (float)y); i++)` Because comparisons to zero cannot overflow, it might be better to rewrite loops as "down counters": `for (int i = y-1; i >= 0; i--) ` #### Compiler Workaround The compiler's strategy for working around this issue is to use the floating-point comparison instruction to detect and avoid situations where the integer comparison instruction will not compute the correct result. For example, given: `x <= y` the compiler will turn this into: `(float)x < (float)y || (float)x == (float)y && (x <= y)` [[y Implementation This workaround is implemented in C2000 compiler releases 16.9.9.LTS and 18.1.5.LTS. ]] But 32-bit floating-point does not have as much precision as 32-bit integer, so how does this work? It is true that casting x and y to float may lose precision and cause rounding, but even so `(float)x < (float)y` implies `x <= y`. To see that this is so, note that `x > y implies (float)x >= (float)y` because the rounding error of (float)y can never be greater than the rounding error of (float)x. So if `(float)x < (float)y` we have a quick answer (yes, x <= y). What about the other cases? If `(float)x > (float)y`, this implies `x >= y` we again have a quick answer (no, x is not <= y). If `(float)x == (float)y` it could be the case that the conversion to float introduced roundoff error, so we can't be sure about whether x is greater than, less than, or equal to y, but we can be sure that x and y are close enough in value that using the integer comparison instruction will not run afoul of the problem, so we can use it to directly test (x <= y). [[y Code size impact While conversion to float is a single instruction on CLA, there are a fair number of delay slots that must be filled, which typically means extra NOPs. If the compiler must use a floating-point comparison instruction to work around a suspect integer comparison, you can end up with a lot of extra instructions. Instead of one MCMP32 instruction, expect to see up to 22 instructions, including NOPs. This impacts both signed and unsigned integer comparison, but does not affect float comparisons. In practice, a lot of integer comparisons are safe (the compiler can be sure the inputs are fairly close together), but if your application uses a lot of 32-bit integer comparisons, you could see a significant increase in code size.]] <!-- End of markdown source --> <div id="footer"></div>