On 3/16/07, Erik <sigra@xxxxxxx> wrote:
Ian Lance Taylor skrev: > Erik <sigra@xxxxxxx> writes: > > >> From man:puts I see that it is declared "int puts(const char >> *)". This means that puts does not promise to leave its argument >> unchanged. Therefore the caller must push the argument anew before >> each call. If it had been declared "int puts(const char * const)" >> instead, the push should be moved outside the loop. Unfortunately this >> does not seem to work. I tried with the following program: >> void q(const unsigned int); >> void f() {for (unsigned int x = 0; x != 10; x++) q(77);} >> >> and built it with "gcc -std=c99 -Os -Wall -Wextra -Werror -S": >> .L2: >> subl $12, %esp >> incl %ebx >> pushl $77 >> call q >> addl $16, %esp >> cmpl $10, %ebx >> jne .L2 >> >> As you can see, "pushl $77" is still inside the loop even though q >> promises to not change its argument. This must be a bug. > > This is not a bug. const on an automatic variable in C is more > advisory than anything else. You are not permitted to change a const > object, but you can cast its address to a non-const pointer.
The "lost optimization" in this case has nothing to do with const versus non-const. The issue is that the call deallocates the parameters. The pushl is allocating the argument. While it is possible to avoid the deallocation and reallocation, doing so requires either changing the calling convention or doing "whole program" optimization. In either event, all of the tools that understand the calling convention, like the debugger, would need to be modified. Again, this can be done, but not without investment and a period of "bugs". Note that in C++, you are not permitted to modify a const parameter. The compiler can optimize on that basis, but in this case, the code would be unaffected because of the allocation issue.
It is a bug. If not in gcc, then in C, that allows the programmers to do such strange things, preventing the compiler from optimizing the code. The right thing for gcc to do would be to enable such optimizations and warn when the code casts away constness, that it could break optimized code. I believe that is what gcc already does for some stupidities; gcc performs some optimizations that are broken by reinterpret_cast and gives "warning: dereferencing type-punned pointer will break strict-aliasing rules". So gcc should be modified to be useful for users who need the optimization but not the bizarre misfeatures of C.
Given the need for compatibility with existing programs at the time that const was introduced, I don't think it is possible to have the kind of const safety that you seem to want.
Anyway, it is surely a bug that gcc does not optimize away the "pushl $77" inside the loop from the Ada program.
I think the Ada behavior is correct as well, because of the argument allocation. Skipping that would change the ABI, which should not be done lightly.
Unlike C, Ada was not designed for ugly hacks, but for clarity and for letting compilers perform optimizations.
I think that is an unfair characterization of C. The C language was designed to let programmers do the kinds of optimizations that most compilers of the day were not doing. It enabled many programmers get out of assembly coding. Ada had the advantage of being designed some ten years after C, when compilers could be relied upon to do optimizations. Even so, it was a bit of a stretch, and Ada lost many of its potential users because of poor initial performance. I am not saying that the Ada designers made a mistake, just that there were consequences.
So a promise is a promise. If a function says it will not touch something, it won't.
But, as I've shown, this notion does not apply to the example. -- Lawrence Crowl