Of course not every CPU has a carry flag...
What if the architecture doesn't have a carry? Then the compiler has to generate one when needed.
What if the architecture has better ways to do the addition anyway? What if the architecture is only 8bit and even regular "int" adds take several instructions anyway, so it makes so sense opmimizing for 128bit adds?
C doesn't even mandate what happens in case of an integer overflow, it has no business exposing carry flags.
If I ever write a C tutorial I'll title it "How I learned to stop worrying and love the compiler".
EDIT: of course in this case you can't really "let the compiler handle it" if you care about bignum performances. The only solution here would be
a/ expose much more hardware state to the language, which as I've already stated doesn't sound very convenient or C-ish enough for me,
b/ modify the optimizer to better handle bignum code or
c/ use a bignum library and possibly make it part of the standard.
I wouldn't be opposed to a standard <bignum.h>, I think it would make some sense. In the meantime I just use gmp.h, it's faster than anything I could come up with anyway, asm or otherwise. Bignum is hard.
Adding two N-bit numbers produces N+1-bit result, and C gives you no direct means of accessing the full result. IMO, this is a language defect and has little to do with HW support. [This pertains also to multiplication: NxN bits -> 2N-bit result.]
If the hardware actually returns the full result, excellent; if no, the compiler has to synthesize code to compute it, if needed. Whether it's needed is inferrable by the code that follows, e.g., the type of the variable the result is assigned to.
IMO, inferring that only a partial result is used (e.g., carry is discarded) is much easier for the optimizer than inferring what some instruction sequence is supposed to do. (E.g., 4+ multiplications and few additions => single 32x32=>64 multiply.)
What is your point? If you want to do multiple precision arithmetic in assembly, then on the architectures which don't have a carry, you must indeed generate one, so what? If a language has some operations with carry, then it is up to the programmer to use them only when he needs them..
> C doesn't even mandate what happens in case of an integer overflow
That's for performance reasons, and I would say that it is the wrong default aka 'premature optimisation', a better language would have 'int' with overflow checks (which could be disabled by a compiler switch) and int_raw for the unsafe&fast behaviour.
I agree that the carry flag shouldn't be exposed directly, but the ability to express "add and check for overflow" would be useful and allow for these optimisations where the architecture supports it.
Edit: But if you want to exploit the processor to its fullest, go with assembly. The compiler is good, but proving optimisations is a hard problem, and complex, single-instruction-specific optimisations aren't going to take off any time soon.
From https://en.wikipedia.org/wiki/Status_register#CPU_architectu... those architectures put the carry result in a general-purpose register, but you still can't access the result in C.
It's such an important addition that I'm bit surprised why it's still not common knowledge.
I have been under the impression that restrict makes it pretty much equivalent to fortran (restrict means that nothing aliases the pointer)
Here's another example:
// C has pretty paltry array support, so it's common for
// people to build their own stuff like this:
struct Array {
float *data;
size_t len;
};
// But then you use it:
void foo(Array *a, Array *b) {
for (i = ...)
for (j = ...)
a->data[i] = b->data[j];
}
and there's nowhere you can put restrict to declare that the two array references here are independent. The data pointer is one of the contexts where restrict is defined to be meaningless.If you're a hyperactive squirrel, you might try rewriting all your code everywhere to do stuff like this:
void foo(Array *a, Array *b) {
for (i = ...)
for (j = ...) {
float *restrict x = a->data;
float *restrict y = b->data;
x[i] = y[i];
}
but then you'll just discover that most compilers ignore restrict in block scopes like this, because the semantics for that are tricky for compilers to deal with, so you still won't win.The author is banging on about uint128_t for example, and bashing library approaches like boost, but uint128_t isn't even part of the C or C++ standards. Its apples and oranges.
There are certain features of the C standard that forces the compiler to generate non-optimal code by default in certain cases (like aliasing rules, as any FORTRAN proponent will remind you). I don't think it's the case in TFA.
So basically, blame the optimizer, not the language.
P.S.: I used this cached version since the site appears to be down: http://webcache.googleusercontent.com/search?q=cache:accu.or...