On 6/4/2013 7:31 PM, David Mirabito wrote:> Hi Carl, > > I don't know much about the specifics of any given optimisation, but if you do something like the following, you can at least see which optimisation pass is responsible. > At the very least perhaps that can point to the next place to investigate. (also interesting to note Apple's shipped clang doesn't exhibit this behaviour, I had to use my own recent svn tree) > > $ clang -arch arm -emit-llvm -S -O0 example.c -o arm.bc > $ opt -print-after-all -O1 arm.bc > > I can see where the branch test is whittled down to an 'if true' and then eliminated, but cannot comment as to why this might be. >From reading C11, I can posit a potential spec explanation: Start with x--. Per C11: If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. The optimizer can therefore conclude that if this program has well-defined behavior, then x can never point to the null pointer constant (since the null pointer constant is not part of any array object). As a result, the "if (!x)" branch would never trigger, and is dead code. So this doesn't look like an invalid optimization per C11, but that doesn't mean that it's necessarily desired behavior in the optimizer. -- Joshua Cranmer Thunderbird and DXR developer Source code archæologist
On Jun 4, 2013, at 8:55 PM, Joshua Cranmer 🐧 <Pidgeot18 at gmail.com> wrote:> From reading C11, I can posit a potential spec explanation: > > Start with x--. Per C11: > If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. > > The optimizer can therefore conclude that if this program has well-defined behavior, then x can never point to the null pointer constant (since the null pointer constant is not part of any array object). As a result, the "if (!x)" branch would never trigger, and is dead code. > > So this doesn't look like an invalid optimization per C11, but that doesn't mean that it's necessarily desired behavior in the optimizer.Thanks Joshua, A bit of spec reading made me think something like that was going on. In the land of embedded systems, zero is often a valid pointer, though, so being able to avoid this kind of optimization would be nice. That said, if that's the way clang is going to be, it's certainly possible to work around. In this specific case, the OS vendor is doing something (IMHO) terrible - aliasing a 'char *' field of a queue structure via a #define statement to act as a recursive mutex counter. Something like: #define recursive_mutex_counter some_field_not_used_by_a_mutex struct queue { char *some_field_not_used_by_a_mutex; }; void mutexReleaseRecursive(struct queue *q) { q->recursive_mutex_counter--; if (q->recursive_mutex_counter == 0) queue_push_back(q, some_dummy_variable); } Which, as I'm sure we can all agree here is heinous. It's easily worked around without changing the vendor's funny-business overloading: q->recursive_mutex_counter = (char *)((uint32_t)q->recursive_mutex_counter - 1); And I can send them a patch to that effect. I guess what I'm looking for is either for clang to make it possible to avoid this kind of optimization, to change the optimizer so it doesn't happen in the first place, or for someone on the list to give me a canonical answer that clang is going to continue behaving this way in the future, so I have something to point to when the OS guys try to push back (I've found other standard-violating behaviour that they've declined to fix). Any input or suggestions from the list are welcome and appreciated! -- Carl Norum carl at lytro.com
> The optimizer can therefore conclude that if this program has > well-defined behavior, then x can never point to the null pointer > constant (since the null pointer constant is not part of any array > object). As a result, the "if (!x)" branch would never trigger, and is > dead code.This is correct: in C you can't create a null pointer by decrementing a valid pointer. The code in question is dangerous and wrong, and needs to be reviewed to look for other similar problems. John
On Jun 5, 2013, at 7:50 AM, John Regehr <regehr at cs.utah.edu> wrote:>> The optimizer can therefore conclude that if this program has well-defined behavior, then x can never point to the null pointer constant (since the null pointer constant is not part of any array object). As a result, the "if (!x)" branch would never trigger, and is dead code. > > This is correct: in C you can't create a null pointer by decrementing a valid pointer. The code in question is dangerous and wrong, and needs to be reviewed to look for other similar problems.OK, cool, thanks. Why no warning or static analyzer error? Other "this comparison is always true" or "this comparison is always false" warnings exist, right? -- Carl
If anyone cares to back me up, I filed a bug and submitted a patch to the OS in question, found here: https://sourceforge.net/tracker/?func=detail&aid=3614352&group_id=111543&atid=659633 -- Carl On Jun 5, 2013, at 7:50 AM, John Regehr <regehr at cs.utah.edu> wrote:>> The optimizer can therefore conclude that if this program has well-defined behavior, then x can never point to the null pointer constant (since the null pointer constant is not part of any array object). As a result, the "if (!x)" branch would never trigger, and is dead code. > > This is correct: in C you can't create a null pointer by decrementing a valid pointer. The code in question is dangerous and wrong, and needs to be reviewed to look for other similar problems. > > John > _______________________________________________ > LLVM Developers mailing list > LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu > http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev