Strict pointer aliasing: is access through a 'volatile' pointer/reference a solution?
Asked Answered
P

2

8

On the heels of a specific problem, a self-answer and comments to it, I'd like to understand if it is a proper solution, workaround/hack or just plain wrong.

Specifically, I rewrote code:

T x = ...;
if (*reinterpret_cast <int*> (&x) == 0)
  ...

As:

T x = ...;
if (*reinterpret_cast <volatile int*> (&x) == 0)
  ...

with a volatile qualifier to the pointer.

Let's just assume that treating T as int in my situation makes sense. Does this accessing through a volatile reference solve pointer aliasing problem?

For a reference, from specification:

[ Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. See 1.9 for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C. — end note ]

EDIT:

The above code did solve my problem at least on GCC 4.5.

Pegg answered 5/6, 2010 at 19:50 Comment(3)
This question is not clearly C++ specific. The C++-style casts can trivially be rewritten in C.Luther
@Luther C and C++ have different language rules howeverCamphor
@MattMcNabb How are the different WRT volatile?Luther
H
17

Volatile can't help you avoid undefined behaviour here. So, if it works for you with GCC it's luck.

Let's assume T is a POD. Then, the proper way to do this is

T x = …;
int i;
memcpy(&i,&x,sizeof i);
if (i==0)
  …

There! No strict aliasing problem and no memory alignment problem. GCC even handles memcpy as an intrinsic function (no function call is inserted in this case).

Heeltap answered 5/6, 2010 at 19:59 Comment(7)
"Volatile can't help you avoid undefined behaviour here" — why? Do you have any source for this statement?Pegg
C++ standard, section 3.10 paragraph 15 is the place you need to look at with respect to strict aliasing. There's no mention of an exception involving volatile.Heeltap
There are cases where it's not undefined behavior. For example struct A { int a; }; int main() { X x; *reinterpret_cast<int*>(&x) = 10; } is fine and perfectly defined according to 9.2/17. The object is of type int, and the lvalue is also of type volatile int, thus the aliasing goes fine.Lenny
@Johannes: In my case T could be anything, only the size matches int. E.g. a pointer on many platforms will do.Pegg
@Johannes: I didn't mean to imply that it's always undefined behaviour. I just meant to say that volatile won't make any difference with respect to 3.10/15.Heeltap
@sellibitze, ah that makes sense now. Somehow i missed the "Does this accessing through a volatile reference solve pointer aliasing problem?" part :) thanksLenny
Using memcpy is not guaranteed to work if the destination has no declared type, and the type as which it will next be read doesn't match the effective type of the source. If one wants to write code "oprimization proof", it's necessary to load and store individual characters in a way that can't be construed as being an operation on a character array.Milker
L
-7

Volatile can't help you avoid undefined behaviour here.

Well, anything regarding volatile is somewhat unclear in the standard. I mostly agreed with your answer, but now I would like to slightly disagree.

In order to understand what volatile means, the standard is not clear for most people, notably some compiler writers. It is better to think: when using volatile (and only when), C/C++ is pretty much high level assembly.

When writing to a volatile lvalue, the compiler will issue a STORE, or multiple STORE if one is not enough (volatile does not imply atomic).

When writing to a volatile lvalue, the compiler will issue a LOAD, or multiple LOAD if one is not enough.

Of course, where there is no explicit LOAD or STORE, the compiler will just issue instructions which imply a LOAD or STORE.

sellibitze gave the best solution: use memcpy for bit reinterpretations.

But if all accesses to a memory region are done with volatile lvalues, it is perfectly clear that the strict aliasing rules do not apply. This is the answer to your question.

Luther answered 15/7, 2012 at 0:35 Comment(7)
-1: First, this is not a forum; we don't address other answers in answers. If you think you have a better answer, then write an answer that addresses the question. Second and more importantly, "anything regarding volatile is somewhat unclear in the standard." No, it isn't. The standard is very clear on how volatile and non-volatile works with regard to the abstract machine.Idzik
@NicolBolas "Second and more importantly, "anything regarding volatile is somewhat unclear in the standard." No, it isn't." A lot of people think it is extremely unclear. If you think it is clear, please explain what it means, and my interpretation is correct.Luther
@NicolBolas "First, this is not a forum;" Except it is a forum. We are discussing things. It's the definition of a forum. "we don't address other answers in answers." So I am supposed to discuss this in a comment, then someone will tell me I discuss too many things in comment...Luther
"So I am supposed to discuss this in a comment, then someone will tell me I discuss too many things in comment." Exactly. That's how we keep discussion out of this Q&A site, thus maintaining a focus on the question. We don't generally want people to address another answer like this in answers; we want the focus to be answering the person's question. Which you admit sellibitze did.Idzik
@NicolBolas 1) This is too large for a comment. 2) Please. Read. My. Answer.Luther
@NicolBolas "we want the focus to be answering the person's question. Which you admit sellibitze did" actually, he did not answered the question of the use of volatile (only the specific example of the use of volatile). I answered the question regarding the use of volatile. sellibitze said: no it does not work. I said: it works, but not the way you wrote it. Anyway, this is a bad idea, and follow sellibitze's suggestion to use memcpy. Your attacks are not warranted.Luther
The behavior of volatile accesses is Implementation-Defined, as such it may be possible for an implementation to define behavior in such a way that it would not guard against aliasing-related UB. A compiler written by sane mature individuals should have no problem writing and reading a volatile location using different types. Alas, some compiler writers would rather seek out excuses for treating something as Undefined Behavior than generate useful code.Milker

© 2022 - 2024 — McMap. All rights reserved.