My problem is how to force the compiler to perform `b` - `a` > 0 when I write `b - a > 0`, because (it is not shown in the above code example) but when I wrote:Code: [Select]if (spin && !unlock && ticks && (epoch - at > 0)) {
I get a program that didn't work because (epoch - at > 0) was assembled as the previous "CMP + BGT" sequence that didn't work.
__CLREX();
return false;
}
I get a program that didn't work because (epoch - at > 0) was assembled as the previous "CMP + BGT" sequence that didn't work for this case.
My problem is how to force the compiler to perform `b` - `a` > 0 when I write `b - a > 0`, because (it is not shown in the above code example) but when I wrote:Code: [Select]if (spin && !unlock && ticks && (epoch - at > 0)) {
I get a program that didn't work because (epoch - at > 0) was assembled as the previous "CMP + BGT" sequence that didn't work.
__CLREX();
return false;
}
"CMP + BGT" is exactly correct and exactly what you asked it to do.
"CMP" and "SUBS" are the same operation, with the sole difference being that "CMP" doesn't write the result of the subtraction to a register.
As I demonstrated before "CMP + BGT" and "SUBS + CMP #0 + BGT" are different.
EDIT:Code: [Select]LDR R0, =0x80000002
will take the branch.
LDR R1, =0x7FFFFFFA
SUBS R2, R0, R1
CMP R2, #0
BGT testCode: [Select]LDR R0, =0x80000002
will not.
LDR R1, =0x7FFFFFFA
CMP R0, R1
BGT test
Nope - unsigned subtraction has standard defined behavior in case of overflow. Signed does not. And, timestamps are the most usual example where overflows happen by design.
It is undefined for the reason that CPUs might exist where signed numbers have a different representation (e.g. a separate sign and absolute value), in which case the overflow would produce different results. I don't know if such CPUs ever existed, I don't think they exist now. So, the only way for the compiler to produce unexpected behaviour is to purposely butcher the code.
Another possibility for the compiler to screw up is to replace (a - b > 0) with (a > b), but this would be an optimization which alters the behaviour and should not be used (see C-99 5.1.2.3.14 for example)
int timeHasElapsed(unsigned timeStamp, unsigned now){
return (now - timeStamp - 1) < 0x80000000u;
}
int timeHasElapsed(unsigned timeStamp, unsigned now){
return ((now - timeStamp - 1) & 0x80000000u) != 0;
}
RISC-V
Many compilers don't use them anymore because they are relatively hard to get to (need to read coprocessor status bits, increasing register pressure).
I get a program that didn't work because (epoch - at > 0) was assembled as the previous "CMP + BGT" sequence that didn't work for this case.
There must be something about these variables that made such optimization possible. Does it still hold when you declare both variables volatile? The "volatile" would prevent the compiler from making any assumptions about values the variables may hold.
Comment in/out the printf lines. You'll see the compare flags suddenly springing back to life. Even on cmp0 where the intermediate result 'sub' is not used in the printf statements, does make the compiler 2nd think it's optimization actions and fixes the issue..
Also writing the subtraction result to some other unused variable seems to work...
Unsigned arithmetic. You only have to watch out when getting too close to the max value, where you could happen to 'miss' the timestamp (a small window of time where your time check went from not elapsed to elapsed but you missed checking it while other things were going on). Its not hard to pick a number as a max time that will always work.
will work in two's complement, one's complement and sign+magnitude, no matter the bit size of an int, correct?
Comment in/out the printf lines. You'll see the compare flags suddenly springing back to life. Even on cmp0 where the intermediate result 'sub' is not used in the printf statements, does make the compiler 2nd think it's optimization actions and fixes the issue..
Also writing the subtraction result to some other unused variable seems to work...
On that compiler version. Maybe not on the next one. Or with different optimisation flags.
Doing it *wrong* by using overflowing signed arithmetic doesn't get made right by finding some combination of tricks that happens to get the answer you want. It's still wrong. And incredibly dangerous to publish.
Code: [Select]int timeHasElapsed(unsigned timeStamp, unsigned now){
return ((now - timeStamp - 1) & 0x80000000u) != 0;
}
If I understand it correctly, this:Code: [Select]bool timeHasElapsed(unsigned timeStamp, unsigned now){
will work in two's complement, one's complement and sign+magnitude, no matter the bit size of an int, correct?
return (now - timeStamp - 1) & ~MAX_INT;
}
static inline int32_t to_int32(uint32_t value)
{
union {
uint32_t u;
int32_t i;
} temp = { .u = value };
return temp.i;
}
static inline int32_t elapsed(uint32_t now, uint32_t then)
{
return to_int32(now - then);
}
int hasElapsed(uint32_t now, uint32_t then)
{
return elapsed(now, then) < 0;
}
(The inline is there for us humans only, and does not affect the compiler at all.)I'm not suggesting anyone to implement any of my volatile/side-effect/fwrapv "hacks". Unsigned is the proper way to do it in C. But maybe I should start programming Rust instead.. that tries to leave as little to UB as possible
will work in two's complement, one's complement and sign+magnitude, no matter the bit size of an int, correct?Not guaranteed by the standard, in fact.
There's no guarantee that INT_MAX is less than UINT_MAX - e.g. in a sign magnitude architecture you might only have native signed operations and unsigned maths would be very inefficient, so the right choice for C is to have UINT_MAX == INT_MAX.
You have to wait for C23. Then two's complement will be the only accepted representation.
Yes signed int overflow in C99 is UB. But perhaps I'm too pragmatic, but I always wonder why and does it apply? Signed magnitude, 1s and 2s complement will have different overflow behaviour, and if the Clang language can assume that is unknown, then it is right to optimize "i+1 > i" to be always "true", even when on a 2s complement computer with i=INT32_MAX , that expression would be false. GCC does the same.
The point at which Clang seems to take 1 step further, is moving variables across the comparison operator, as a mathematical number. So "a - b < 0" is equal to "a < b". GCC doesn't do this, and generates the "intended" subtraction comparison.
You would need to force the Clang compiler to assume integers are stored 2s complement with argument fwrapv. Then it won't do this optimization.. Even for i+1>i will be executed on the CPU using both compilers.
The conclusions I extract from this thread are:
You have to wait for C23. Then two's complement will be the only accepted representation.
Key thing here is that time is kept unsigned.
Key thing here is that time is kept unsigned.Perhaps not if you wanted to represent times prior to the epoch.