English Amiga Board


Go Back   English Amiga Board > Coders > Coders. Language > Coders. C/C++

 
 
Thread Tools
Old 04 January 2022, 20:56   #21
vbc
Registered User
 
Join Date: Jan 2021
Location: Germany
Posts: 18
Quote:
Originally Posted by paraj View Post
I have to admit the issue is less clear than I remembered it, and it might be the case that the standard technically doesn't require type-punning through unions to be supported (this stackoverflow answer makes a persuasive argument).
I think I can agree with most of what that poster says.

Quote:
You're also right that it was added through a defect report (DR283). Following the linked discussions, I think it's quite clear (from proposal N980) that the intention was to allow it though.
IMHO "intention" is very tricky regarding a committee like that. While I have never participated in the C standard's group, I have attended ISO meetings creating standards. Typically there are representatives of several vendors with their own agendas. In my experience it is quite common to agree on some vague text that the parties concerned can read in a way they can live with. That is better than endless stalled discussions which annoy the 80% participants who couldn't care less about that specific topic. :-) So, I would stick to the text that was actually agreed on.

If the union hack should really be legal, then there are some pretty tricky decisions. Why should

Code:
 union {int i;float f;} u;

 u.f=3.14;
 return u.i;
and
Code:
 int *pi=&u.i;
 float *pf=&u.f;

 *pf=3.14;
 return *pi;
behave differently? If we want them to behave the same, then we have to pretty much throw away most aliasing optimizations that C99 has tried to enable.

If not, we have a strange situation where - basically - *&u.i may be different from u.i. Apart from being a very ugly concept, there would be the need to specify exactly where the normal aliasing rules start and stop. This is more a question of what the compiler sees rather than a question of types. Specifying this formally probably requires introduction of some new concepts. Just have a look at the (non-)description of gcc's -fstrict-aliasing for the ugly consequences.

AFAIK nothing like that has really been addressed in the C standard so far. If such a radical change was intended, I would expect much more than a hidden footnote.

Quote:
It's certainly widely believed to be, as evidences by this thread
Unfortunately.
vbc is offline  
Old 05 January 2022, 10:17   #22
bebbo
bye
 
Join Date: Jun 2016
Location: Some / Where
Posts: 680
Quote:
Originally Posted by vbc View Post
If the union hack should really be legal, then there are some pretty tricky decisions. Why should

Code:
 union {int i;float f;} u;

 u.f=3.14;
 return u.i;
and
Code:
 int *pi=&u.i;
 float *pf=&u.f;

 *pf=3.14;
 return *pi;
behave differently? If we want them to behave the same, then we have to pretty much throw away most aliasing optimizations that C99 has tried to enable.

What compiler yields different results for these?
bebbo is offline  
Old 05 January 2022, 10:58   #23
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 3,215
Quote:
Originally Posted by vbc View Post
Why should

Code:
 union {int i;float f;} u;

 u.f=3.14;
 return u.i;
and
Code:
 int *pi=&u.i;
 float *pf=&u.f;

 *pf=3.14;
  return *pi;
behave differently?
Because, in the second code, the compiler is free to assume that a float * never points to the same location as an int *, i.e. a float * cannot alias an int *. But in the first code, the two objects share the same memory location by definition, so it must assume that the value may have changed "under its feet".


Quote:
Originally Posted by vbc View Post
If we want them to behave the same, then we have to pretty much throw away most aliasing optimizations that C99 has tried to enable.
Correct. Or almost, you have also the option to use restricted pointers if you want to tell the compiler that certain pointers cannot alias.


Quote:
Originally Posted by vbc View Post
If not, we have a strange situation where - basically - *&u.i may be different from u.i.
This problem you already have to some degree. A pointer to a member of an object can be aliased by a pointer to the object itself, i.e. you can change "struct {int i} s;" through *s.i and through (&s)->i.

Last edited by Thomas Richter; 05 January 2022 at 11:05.
Thomas Richter is offline  
Old 05 January 2022, 10:58   #24
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 3,215
Quote:
Originally Posted by bebbo View Post
What compiler yields different results for these?
Intel icc, for example.
Thomas Richter is offline  
Old 05 January 2022, 11:03   #25
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 3,215
Quote:
Originally Posted by vbc View Post
This is what I did 20-25 years ago.
That is quite a while ago, and C evolved. I suggest to get an up-to-date version of the standard (from DIN, or from ISO, not the CD document, but the IS document), or to go to the moderated c newsgroup to check with some real experts there.



Quote:
Originally Posted by vbc View Post
It may make that specific code work on specific versions of specific compilers. But it still violates the C standard and relies on internals of a specific compiler without the need to do so. There are alternatives which do not cause undefined behaviour, like using char-pointers.
That it violates the C standard is currently a claim you make, but not a statement. Avoiding aliasing by going through char * is another option, yes.
Thomas Richter is offline  
Old 05 January 2022, 11:14   #26
paraj
Registered User
 
paraj's Avatar
 
Join Date: Feb 2017
Location: Denmark
Posts: 1,098
Quote:
Originally Posted by bebbo View Post
What compiler yields different results for these?

Needs a little bit of indirection, but it's a classic strict aliasing violation: http://franke.ms/cex/z/T6T1EW
paraj is offline  
Old 05 January 2022, 12:19   #27
alkis
Registered User
 
Join Date: Dec 2010
Location: Athens/Greece
Age: 53
Posts: 719
Quote:
Originally Posted by paraj View Post
Needs a little bit of indirection, but it's a classic strict aliasing violation: http://franke.ms/cex/z/T6T1EW
The foo function loses the union relationship. If what are you after is the same output then you should declare both arguments as volatile.

https://godbolt.org/z/v7d7nof18
alkis is offline  
Old 05 January 2022, 12:27   #28
paraj
Registered User
 
paraj's Avatar
 
Join Date: Feb 2017
Location: Denmark
Posts: 1,098
I'm becoming more convinced that at best it's unclear whether type-punning via a union is allowed.

The GCC manual explicitly states:
Quote:
[..] you can use a union instead of a cast (note that this is a GCC extension which might not work with other compilers):
And from a paper from the CompCert authors:
Quote:
In order to enable type-based alias analysis, we have to ensure that only under certain conditions a union can be read using a pointer to another variant than Since the C11 standardis unclear about these conditions, we follow the GCC documentation [4] on it.
Some more reference:

https://davmac.wordpress.com/2010/02/26/c99-revisited/

Rant by Linus Torvalds: https://lkml.org/lkml/2018/6/5/769

Edit:
Quote:
Originally Posted by alkis View Post
The foo function loses the union relationship. If what are you after is the same output then you should declare both arguments as volatile.

https://godbolt.org/z/v7d7nof18
You lose the union relationship as soon as pointers become involved. It's still violating the strict aliasing rules. It just happens to work because the compiler must re-read "*pi" due to the volatile specifier.

Notice that it stops working if you do: printf("%d\n", *(int*)pi);

Last edited by paraj; 05 January 2022 at 12:41.
paraj is offline  
Old 05 January 2022, 14:13   #29
alkis
Registered User
 
Join Date: Dec 2010
Location: Athens/Greece
Age: 53
Posts: 719
Quote:
Originally Posted by paraj View Post
I'm becoming more convinced that at best it's unclear whether type-punning via a union is allowed.

The GCC manual explicitly states:

And from a paper from the CompCert authors:


Some more reference:

https://davmac.wordpress.com/2010/02/26/c99-revisited/

Rant by Linus Torvalds: https://lkml.org/lkml/2018/6/5/769
Thanks for the links. Interesting read.
alkis is offline  
Old 06 January 2022, 17:11   #30
vbc
Registered User
 
Join Date: Jan 2021
Location: Germany
Posts: 18
Quote:
Originally Posted by Thomas Richter View Post
Because, in the second code, the compiler is free to assume that a float * never points to the same location as an int *, i.e. a float * cannot alias an int *. But in the first code, the two objects share the same memory location by definition, so it must assume that the value may have changed "under its feet".
In the second version the two objects share the same memory location by definition as well. It is just harder to detect for the compiler.

Quote:
This problem you already have to some degree. A pointer to a member of an object can be aliased by a pointer to the object itself, i.e. you can change "struct {int i} s;" through *s.i and through (&s)->i.
This is different and it is reasonably well specified. In the union hack there is a difference in meaning between accessing an object directly versus accessing it through a (correctly typed) pointer to the object. This is a completely new concept to C.

Quote:
That is quite a while ago, and C evolved.
Not that much since C99 and AFAIK not at all regarding this topic. Also I have not seen anyone citing any changes in later versions of the standard affecting this discussion and I have never seen use of the union hack accompanied by a remark that it only works on something later than C99.

Quote:
That it violates the C standard is currently a claim you make, but not a statement.
I have quoted the part of the standard that disallows that practice.

Basically the only use case for this hack is a kind of reinterpret-cast. However, this can already done by using a char-pointer. If there was a need to specify a better way to do that, the union hack is a really bad way. Apart from being inconsistent, it is neither efficient nor easy to write and it may break if someone does seemingly harmless changes to the code like using a pointer to the union.
vbc is offline  
Old 09 January 2022, 13:29   #31
bebbo
bye
 
Join Date: Jun 2016
Location: Some / Where
Posts: 680
Well, unions are a good thing to do those reinterpret casts. And they are safe for direct use:
set union members as needed

read union members as needed


It's the standard way in gcc and its libs, e.g.

Code:
union _FP_UNION_S
{
  SFtype flt;
  struct _FP_STRUCT_LAYOUT
  {
#if __BYTE_ORDER == __BIG_ENDIAN
    unsigned sign : 1;
    unsigned exp  : _FP_EXPBITS_S;
    unsigned frac : _FP_FRACBITS_S - (_FP_IMPLBIT_S != 0);
#else
    unsigned frac : _FP_FRACBITS_S - (_FP_IMPLBIT_S != 0);
    unsigned exp  : _FP_EXPBITS_S;
    unsigned sign : 1;
#endif
  } bits __attribute__ ((packed));
};
and my libnix variant uses it (ok, that's more an argument against... ^^)
Code:
// a union to handle the
union _d_bits {
    double d;
    struct {
        unsigned sign :1;
        unsigned exp :11;
        unsigned frac0 :20;
        unsigned frac1 :32;
    } b;
    unsigned u;
};
it allows to write good readable code without unreadable cast stuff.



And yes, some of the examples here will not produce the expected result, but no one <insert attributes> would use these unions in that bogus way.


Plus since the code gets tested, all bugs get fixed^^
bebbo is offline  
Old 12 January 2022, 16:04   #32
vbc
Registered User
 
Join Date: Jan 2021
Location: Germany
Posts: 18
Quote:
Originally Posted by bebbo View Post
Well, unions are a good thing to do those reinterpret casts. And they are safe for direct use:
As has been shown, they are not.

Quote:
It's the standard way in gcc and its libs, e.g.
We are talking about the C standard, not a specific compiler.

Quote:
it allows to write good readable code without unreadable cast stuff.
You can not seriously think that declaring a union with two types, assigning one element and then reading another one is good and readable. It is a crutch just like using memcpy or similar. However, those crutches are at least allowed by the standard.
vbc is offline  
Old 13 January 2022, 16:39   #33
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 3,215
Quote:
Originally Posted by vbc View Post
As has been shown, they are not.
Look, I'm not too much an expert of ANSI C pecularities, but I'm working with the language since quite a while with multiple compilers at multiple architectures. I believe it may be worth to investigate the issue at a somewhat more rigorous level than what can happen at this audience, but my personal experience is that this idiom is robust under multiple architectures and compilers, having checked at least gcc,icc,VS and clang.


Of course, the world of C is much larger than this, but for many practical matters, that's fine.


Quote:
Originally Posted by vbc View Post
You can not seriously think that declaring a union with two types, assigning one element and then reading another one is good and readable.
I beg to disagree. It is a coding idiom of which I believe many C software developers have been exposed to, and as such they know how to interpret it - and that's much better than a memcpy(). It is certainly a workaround for a C-language shortcoming, namely not having a reinterpret_cast() as C++ has, but it is a very common, if not the *most common* and *most popular* idiom I have seen in partical matters to overcome this limitation.


Thus, I would be quite resiliant to put it aside so quickly. It is in active use, and at least in my experience it has been shown to be quite portable, if not to be the most portable hack of possible hacks so solve the problem at hand.
Thomas Richter is offline  
Old 13 January 2022, 20:56   #34
paraj
Registered User
 
paraj's Avatar
 
Join Date: Feb 2017
Location: Denmark
Posts: 1,098
Quote:
Originally Posted by Thomas Richter View Post
Look, I'm not too much an expert of ANSI C pecularities, but I'm working with the language since quite a while with multiple compilers at multiple architectures. I believe it may be worth to investigate the issue at a somewhat more rigorous level than what can happen at this audience, but my personal experience is that this idiom is robust under multiple architectures and compilers, having checked at least gcc,icc,VS and clang.


Of course, the world of C is much larger than this, but for many practical matters, that's fine.

I beg to disagree. It is a coding idiom of which I believe many C software developers have been exposed to, and as such they know how to interpret it - and that's much better than a memcpy(). It is certainly a workaround for a C-language shortcoming, namely not having a reinterpret_cast() as C++ has, but it is a very common, if not the *most common* and *most popular* idiom I have seen in partical matters to overcome this limitation.


Thus, I would be quite resiliant to put it aside so quickly. It is in active use, and at least in my experience it has been shown to be quite portable, if not to be the most portable hack of possible hacks so solve the problem at hand.

As I see it there are two separate issues: 1) is this type-punning using a union technically allowed by the C99 standard 2) is it a widely supported and often used practice (and assumed to be legal).

The first point is a question for language lawyers that I agree won't be resolved here (though I'd be very interested in the discussion outcome).

The second one I think is true (see e.g. netlib's dtoa for a classic example). A lot of useful source code relies on this behavior whether it's strictly allowed or not. It's a bit similar to the issue of single line comments in C89 (though the impact is obviously much greater for the compiler writer in this case).

From an engineering perspective if I were relying on type-punning via unions, I'd stick with a know good version after verifying the (hopefully few) places where I relied on them and stick with a know-good version while lobby for a compiler change/flag. Testing out trivial versions I don't see vbcc generating anything that would break union type-punning BTW. Would be interesting to see one where it does break.
paraj is offline  
Old 29 January 2022, 13:33   #35
Hedeon
Semi-Retired
 
Join Date: Mar 2012
Location: Leiden / The Netherlands
Posts: 1,993
To get back to my first posting. The following code:

Code:
void fetch_vertex_ff (void* driver, float *pointer) {
WriteFloat(driver, pointer[0]);
WriteFloat(driver, pointer[1]);
}
Ends up confusingly in

Code:
fmove.s (a5),fp0
lea 132(a4),a2 //(driver)
fmove.s fp0,-(a7)
move.l a4,-(a7)
move.l (a2),a3 //(WriteFloat function)
jsr (a3)
move.l 4(a5),-(a7) //(now a direct store to stack instead of first to float)
move.l (a4),-(a7)
move.l (a2),a2
jsr (a2)
This is with -O3.
Hedeon is offline  
Old 29 January 2022, 17:18   #36
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 3,215
What's confusing here? The code is correct, just a bit more complicated than necessary. The single-move reads a single precision floating point of 4 bytes size, extends it to double precision, and then later rounds it back to single precision, and stores it on the stack.
Thomas Richter is offline  
Old 30 January 2022, 20:12   #37
bebbo
bye
 
Join Date: Jun 2016
Location: Some / Where
Posts: 680
Quote:
Originally Posted by Hedeon View Post
To get back to my first posting. The following code:

Code:
void fetch_vertex_ff (void* driver, float *pointer) {
WriteFloat(driver, pointer[0]);
WriteFloat(driver, pointer[1]);
}
Ends up confusingly in

Code:
fmove.s (a5),fp0
lea 132(a4),a2 //(driver)
fmove.s fp0,-(a7)
move.l a4,-(a7)
move.l (a2),a3 //(WriteFloat function)
jsr (a3)
move.l 4(a5),-(a7) //(now a direct store to stack instead of first to float)
move.l (a4),-(a7)
move.l (a2),a2
jsr (a2)
This is with -O3.

what compiler?

if the function parameter is a float, no conversion is needed.

Last edited by bebbo; 30 January 2022 at 20:33.
bebbo is offline  
 


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools

Similar Threads
Thread Thread Starter Forum Replies Last Post
VBCC - What's going on here? deimos Coders. C/C++ 69 28 July 2018 16:14
Space Hulk (1993) - Question about ray casting & graphics Cherno Nostalgia & memories 0 27 August 2017 10:24
Integers vs floats (FFP/Sing/Doub) + printf() guy lateur Coders. Asm / Hardware 63 18 July 2017 17:57
Ray casting sandruzzo Coders. General 14 21 June 2017 01:06
AmiDevCpp and Floats AmigaEd Coders. General 0 18 January 2006 03:16

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 16:43.

Top

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Page generated in 0.10566 seconds with 15 queries