Liste des Groupes | Revenir à c arch |
On 9/11/2024 5:38 AM, Anton Ertl wrote:Correcting the typos (in case anyone wants to copy-and-paste to godbolt.org for testing):Josh Vanderhoof <x@y.z> writes:anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:...
>George Neuner <gneuner2@comcast.net> writes:On Sun, 08 Sep 2024 15:36:39 GMT, anton@mips.complang.tuwien.ac.at
(Anton Ertl) wrote:1) At first I thought that yes, one could just check whether there is
an overlap of the memory areas. But then I remembered that you cannot
write such a check in standard C without (in the general case)
exercising undefined behaviour; and then the compiler could eliminate
the check or do something else that's unexpected. Do you have such a
check in mind that does not exercise undefined behaviour in the
general case?It is legal to test for equality between pointers to different objects>
so you could test for overlap by testing against every element in the
array. It seems like it should be possible for the compiler to figure
out what's happening and optimize those tests away, but unfortunately
no compiler I tested did it.
That would be an interesting result of the ATUBDNH lunacy: programmers
would see themselves forced to write workarounds such as the one you
suggest (with terrible performance when not optimized), and then C
compiler maintainers would see themselves forced to optimize this kind
of code. The end result would be that both parties have to put in
more effort to eventually get the same result as if ordered comparison
between different objects had been defined from the start.
>
For now, the ATUBDNH advocates tell programmers that they have to work
around the lack of definition, but there is usually no optimization
for that.
>
One case where things work somewhat along the lines you suggest is
unaligned accesses. Traditionally, if knowing that the hardware
supports unaligned accesses, for a 16-bit load one would write:
>
int16_t foo1(int16_t *p)
{
return *p;
}
>
If one does not know that the hardware supports unaligned accesses,
the traditional way to perform such an access (little-endian) is
something like:
>
int16_t foo2(int16_t *p)
{
unsignedchar *q = p;
return (int16_t)(q[0] + (q[1]>>8));
}
It is a unfortunate truth that code that is correct can be inefficient on some compilers, while code that is efficient on those compilers is not correct (according to the C standards) and can fail on other compilers. I may be a "ATUBDNH advocate", but I can certainly acknowledge that much. The C standard is concerned with the behaviour of the code, not its efficiency, and it has always been a fact of life for C programmers that different compilers give better or worse results for different ways of writing source code. Not all code can be written portably /and/ efficiently, without at least some conditional compilation.>
Now, several years ago, somebody told me that the proper way is as
follows:
>
int16_t foo3(int16_t *p)
{
int16_t v;
memcpy(&v,p,2);
return v;
}
>
That way looked horribly inefficient to me, with v having to reside in
memory instead of in a register and then the expensive function call,
and all the decisions that memcpy() has to take depending on the
length argument. But gcc optimizes this idiom into an unaligned load
rather than taking all the steps that I expected (however, I have seen
cases where the code produced on hardware that supports unaligned
accesses is worse than that for foo1()). Of course, if you also want
to support less sophisticated compilers, this idiom may be really slow
on those, although not quite as expensive as your containment check.
>
Would be nice, say, if there were semi-standard compiler macros for various things:Ask, and you shall receive! (Well, sometimes you might receive.)
Endianess (macros exist, typically compiler specific);#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
And, apparently GCC and Clang can't agree on which strategy to use.
Whether or not the target/compiler allows misaligned memory access;Why would you need that? Any decent compiler will know what is allowed for the target (perhaps partly on the basis of compiler flags), and will generate the best allowed code for accesses like foo3() above.
If set, one may use misaligned access.
Whether or not memory uses a single address space;Pointer comparisons are always allowed for equality tests if they are pointers to objects of compatible types. (Function pointers cannot be compared at all.)
If set, all pointer comparisons are allowed.
Clang:See above.
__LITTLE_ENDIAN__, __BIG_ENDIAN__
One or the other is defined based on endian.
GCC:
__BYTE_ORDER__ which may equal one of:
__ORDER_LITTLE_ENDIAN__
__ORDER_BIG_ENDIAN__
__ORDER_PDP_ENDIAN__
MSVC:
REG_DWORD is one of:
REG_DWORD_LITTLE_ENDIAN
REG_DWORD_BIG_ENDIAN
GCC:
__SIZEOF_type__ //gives sizeof various types
Possible:_Alignof(type) has been around since C11.
__MINALIGN_type__ //minimum allowed alignment for type
Maybe also alias pointer control:Faffing around with pointer types - breaking the "effective type" rules - has been a bad idea and risky behaviour since C was standardised. You never need to do it. (I accept, however, that on some weaker or older compilers "doing the right thing" can be noticeably less efficient than writing bad code.) Just get a half-decent compiler and use memcpy(). For any situation where you might think casting pointer types would be a good idea, your sizes are small and known at compile time, so they are easy for the compiler to optimise.
__POINTER_ALIAS__
__POINTER_ALIAS_CONSERVATIVE__
__POINTER_ALIAS_STRICT__
Where, pointer alias can be declared, and:
If conservative, then conservative semantics are being used.
Pointers may be freely cast without concern for pointer aliasing.
Compiler will assume that "non restrict" pointer stores may alias.
If strict, the compiler is using TBAA semantics.
Compiler may assume that aliasing is based on pointer types.
Les messages affichés proviennent d'usenet.