Discussion:
ICE: how to debug?
Antoine Leca
2013-02-26 09:24:20 UTC
Permalink
Hi guys,

I got an ICE with new pcc (something which was OK previously), and I
wondered how I can go further in the debugging (ie how to search with -X
and -Z about the problem).

I simplified the offending input as
void f(unsigned u) { int i=(int)( u >> 0) % 5; }
<stdin>, line 2: compiler error: Cannot generate code, node 03571A0 op %

What is the next step? ccom -Ze seems a good starting point, but I do
not know where to search for the faulty node. Can someone help?


Antoine
___
PS: I know >>0 is useless, and using >>1 does not raise the problem; yet
it comes from de-macrofied code where the >>0 makes sense, and I do not
see why such code cannot be translated.
Anders Magnusson
2013-03-01 08:49:40 UTC
Permalink
Post by Antoine Leca
Hi guys,
I got an ICE with new pcc (something which was OK previously), and I
wondered how I can go further in the debugging (ie how to search with -X
and -Z about the problem).
I simplified the offending input as
void f(unsigned u) { int i=(int)( u >> 0) % 5; }
<stdin>, line 2: compiler error: Cannot generate code, node 03571A0 op %
What is the next step? ccom -Ze seems a good starting point, but I do
not know where to search for the faulty node. Can someone help?
It's actually quite simple to get something useful out of pcc:

The failing tree will be written out to stdout if you run ccom directly;
% echo 'void f(unsigned u) { int i=(int)( u >> 0) % 5; }' |
/usr/local/libexec/ccom
.text
.align 4
.globl f
.type f,@function
f:
<stdin>, line 2: compiler error: Cannot generate code, node 0xa5f9f0 op %
0xa5fc70) =, int, REG %rax , SU= 0(@REG,,,,,,)
0xa5fc20) OREG -4(%rbp), int, REG %rax , SU= 0(@REG,,,,,,)
0xa5f9f0) %, int, REG %rax , SU= 0(@REG,,,,,,)
0xa5fb80) OREG -8(%rbp), unsigned, REG %rax , SU= 0(@REG,,,,,,)
0xa5f8a0) ICON $5, int, REG %rax , SU= 0(@REG,,,,,,)

Here we can see that something do not create a type correct tree,
causing it to fail when matching instructions.
The mod instruction gets both an int and an unsigned. This can be
intentional, but in that case the instruction rules must allow it,
and in this case something is doing it wrong.

Further debug: -X to ccom gives flags to the frontend and -Z to the
backend, see the man page for these.
A quick check with -Ze says (first)

PROLOG (0x13070a0): f (local) regs 0xffffffffffffffff autos -1 mintemp
100 minlbl 91
DEFLAB (0x1307120): .L91
NODE (0x1307140):
0x13054c0) =, unsigned, REG %rax , SU= 0(@REG,,,,,,)
0x1305a30) TEMP 100, unsigned, REG %rax , SU= 0(@REG,,,,,,)
0x130b8f0) REG %rdi, unsigned, REG %rax , SU= 0(@REG,,,,,,)
DEFLAB (0x1307160): .L93
NODE (0x13071c0):
0x130bc70) =, int, REG %rax , SU= 0(@REG,,,,,,)
0x130bc20) U*, int, REG %rax , SU= 0(@REG,,,,,,)
0x130bbd0) -, PTR int, REG %rax , SU= 0(@REG,,,,,,)
0x130bae0) REG %rbp, PTR int, REG %rax , SU= 0(@REG,,,,,,)
0x130bb30) ICON $4, long, REG %rax , SU= 0(@REG,,,,,,)
0x130b9f0) %, int, REG %rax , SU= 0(@REG,,,,,,)
0x130bb80) TEMP 100, unsigned, REG %rax , SU= 0(@REG,,,,,,)
0x130b8a0) ICON $5, int, REG %rax , SU= 0(@REG,,,,,,)
DEFLAB (0x13071e0): .L92
EPILOG (0x1307210): f (local) regs 0x0 autos 4 mintemp 101 minlbl 94

This displays everything given to pass2 from pass1. We can already here
see that something in pass1 looses the type correctness.

Well, now it's just to dig deeper after what fails. I assume that the
bug is in clocal().

-- Ragge
Antoine Leca
2013-03-01 12:19:54 UTC
Permalink
<snip very good stuff>

Thanks ragge! That is exactly the information I needed!


Antoine
Antoine Leca
2013-03-01 15:56:53 UTC
Permalink
Post by Anders Magnusson
Post by Antoine Leca
I simplified the offending input as
void f(unsigned u) { int i=(int)( u >> 0) % 5; }
<stdin>, line 2: compiler error: Cannot generate code, node 03571A0 op %
<stdin>, line 2: compiler error: Cannot generate code, node 0xa5f9f0 op %
Here we can see that something do not create a type correct tree,
causing it to fail when matching instructions.
The mod instruction gets both an int and an unsigned. This can be
intentional, but in that case the instruction rules must allow it,
and in this case something is doing it wrong.
<snip>
Post by Anders Magnusson
This displays everything given to pass2 from pass1. We can already here
see that something in pass1 looses the type correctness.
So I went down digging a bit more, comparing with a slightly similar
case (>>1) which compiles OK. I found that -Xe are identical, while at
-Ze there is a difference, with >>1 the left part of the % node receive
a >> operation with type "int", while with >>0 it receives a U*
operation with type "unsigned": clearly, the reduction from >> to U*
lose the (int) type cast.

So I searched a bit into the code, and determined it happens in optim.c
I first found that function zapleft() is pretty misnamed, since it
really zap the node and its right subnode, and keep the left one ;-)
Fortunately there is a label named 'zapright:' around lines 325, which
does pretty much the same operations (zapping the node and its right),
just it _also_ calls makety() to pass the type information from the
to-be-deleted node, to the to-be-kept, formerly left (and is followed by
a call to clocal() to align locals); the effect of such code in cases
like this one is to transform the deleted operation into a cast, if
there is a type change: right the case at hand!

So I changed the calls to the function into goto's to the more complete
code, and my bug went away :-) However I am not completely sure this
does not introduce other bugs down the line.
I also added comments in the places where I believe there could be
similar pitfalls (and I renamed the function.)


Antoine
Anders Magnusson
2013-03-01 17:28:44 UTC
Permalink
Hi,
Post by Antoine Leca
Post by Anders Magnusson
Post by Antoine Leca
I simplified the offending input as
void f(unsigned u) { int i=(int)( u >> 0) % 5; }
<stdin>, line 2: compiler error: Cannot generate code, node 03571A0 op %
<stdin>, line 2: compiler error: Cannot generate code, node 0xa5f9f0 op %
Here we can see that something do not create a type correct tree,
causing it to fail when matching instructions.
The mod instruction gets both an int and an unsigned. This can be
intentional, but in that case the instruction rules must allow it,
and in this case something is doing it wrong.
<snip>
Post by Anders Magnusson
This displays everything given to pass2 from pass1. We can already here
see that something in pass1 looses the type correctness.
So I went down digging a bit more, comparing with a slightly similar
case (>>1) which compiles OK. I found that -Xe are identical, while at
-Ze there is a difference, with >>1 the left part of the % node receive
a >> operation with type "int", while with >>0 it receives a U*
operation with type "unsigned": clearly, the reduction from >> to U*
lose the (int) type cast.
So I searched a bit into the code, and determined it happens in optim.c
I first found that function zapleft() is pretty misnamed, since it
really zap the node and its right subnode, and keep the left one ;-)
Fortunately there is a label named 'zapright:' around lines 325, which
does pretty much the same operations (zapping the node and its right),
just it _also_ calls makety() to pass the type information from the
to-be-deleted node, to the to-be-kept, formerly left (and is followed by
a call to clocal() to align locals); the effect of such code in cases
like this one is to transform the deleted operation into a cast, if
there is a type change: right the case at hand!
Well, zapleft did zap the left node back in time. It still do the same
thing
but no need to supply it with the left node; now it zaps the current
node :-)

Anyway, your findings are correct, but there is more to be aware of:
optim is always called with a type-correct tree, therefore adding a
cast this late shall not be needed. The problem occurs earlier, since
all casts are inserted when the tree is parsed.

A quick search shows that the bug is in amd64/local.c in the statement
around line 379, where an SCONV is removed even if it should not be :-)

You may note that bugs almost always are in the target-specific code,
the machine-independent code seems to be quite good quality now.

-- Ragge
Iain Hibbert
2013-03-01 18:49:32 UTC
Permalink
Post by Anders Magnusson
A quick search shows that the bug is in amd64/local.c in the statement
around line 379, where an SCONV is removed even if it should not be :-)
You may note that bugs almost always are in the target-specific code,
the machine-independent code seems to be quite good quality now.
btw this ICE also happens on i386, so I guess it is there too..

iain

Loading...