Bug 129323

Summary: ecj produces bad bytecode on x86_64
Product: [Fedora] Fedora Reporter: Gary Benson <gbenson>
Component: gccAssignee: Andrew Haley <aph>
Status: CLOSED RAWHIDE QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: rawhide   
Target Milestone: ---   
Target Release: ---   
Hardware: x86_64   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2006-01-24 17:23:09 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
jcf-dump of the class compiled correctly on i386
none
jcf-dump of the class incorrectly compiled on x86_64 none

Description Gary Benson 2004-08-06 13:42:00 UTC
Description of problem:
ecj is producing bad bytecode when used to compile ant.  Lots and lots
of classes throw VerifyErrors relating to incorrect stack sizes when
loaded by gij or by Sun's JVM

Version-Release number of selected component (if applicable):
ecj-2.1.3-4
gcc-3.4.1-7

How reproducible:
Always

Steps to Reproduce:
1. Download the tarball attached to this bug.
2. Extract said tarball and cd into ecj-fail.
3. Run make.

This will take quite a while the first time around because it has to
build all of ecj and quite a chunk of ant.
  
Actual results:
Exception in thread "main" java.lang.VerifyError: verification failed
at PC 43 in
org.apache.tools.ant.taskdefs.optional.clearcase.ClearCase:getClearToolCommand(()Ljava.lang.String;):
stack sizes differ
   at _Jv_BytecodeVerifier.verify_fail(byte, int)
   at _Jv_BytecodeVerifier.state.merge(_Jv_BytecodeVerifier.state,
boolean, int, _Jv_BytecodeVerifier, boolean)
   at _Jv_BytecodeVerifier.verify_instructions_0()
   at _Jv_VerifyMethod(_Jv_InterpMethod)
   at _Jv_PrepareClass(java.lang.Class)
   at _Jv_WaitForState(java.lang.Class, int)
   at java.lang.VMClassLoader.linkClass0(java.lang.Class)
   at java.lang.VMClassLoader.resolveClass(java.lang.Class)
   at java.lang.Class.initializeClass()
   at java.lang.Class.forName(java.lang.String, boolean,
java.lang.ClassLoader)

Expected results:
It shouldn't fail.

Additional info:
I've been testing this on a hammer box.

Comment 1 Gary Benson 2004-08-06 13:46:38 UTC
The tarball was too big to attach to this bug.  Replace step one above
with 'Download http://people.redhat.com/gbenson/ecj-fail.tar.bz2'.

Comment 2 Gary Benson 2004-08-09 14:01:58 UTC
This bug does not occur on i386 boxes.  Furthermore, the bug only
occurs on x86_64 if the bad class is compiled on x86_64, so it looks
like the problem is in the execution of ecj on x86_64.

Comment 3 Gary Benson 2004-08-09 14:07:59 UTC
Created attachment 102513 [details]
jcf-dump of the class compiled correctly on i386

Comment 4 Gary Benson 2004-08-09 14:08:45 UTC
Created attachment 102514 [details]
jcf-dump of the class incorrectly compiled on x86_64

Comment 5 Gary Benson 2004-08-09 14:33:02 UTC
The only difference in the compiled classes is at the end of
getClearToolCommand(): the x86_64 version has a pair of extra dup
instructions.

 34  34   ldc #101=<String "/">
 36  36   invokevirtual #117=<Method java.lang.StringBuffer.append...>
 39  39   invokevirtual #120=<Method java.lang.StringBuffer.toString...>
 --  42   dup
 42  43   astore_1
 43  44   new #107=<Class java.lang.StringBuffer>
 46  47   dup
 47  48   aload_1
 48  49   invokestatic #111=<Method java.lang.String.valueOf...>
 51  52   invokespecial #113=<Method java.lang.StringBuffer.<init>...>
 54  55   ldc #16=<String "cleartool">
 56  57   invokevirtual #117=<Method java.lang.StringBuffer.append...>
 59  60   invokevirtual #120=<Method java.lang.StringBuffer.toString...>
 --  63   dup
 62  64   astore_1
 63  65   aload_1
 64  66   areturn

Comment 6 Gary Benson 2004-08-09 14:39:19 UTC
For the record there is no difference between the ecj bytecode
generated on i386 and x86_64 machines.

Comment 7 Gary Benson 2004-08-09 16:26:54 UTC
The extra dup instruction is coming from here:

File org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java:
372: case T_String :
373:   codeStream.generateStringAppend(currentScope, this, expression);
374:   if (valueRequired) {
375:     codeStream.dup();
376:   }
377:   codeStream.store(localBinding, false);
378:   return;

Comment 8 Bryce McKinlay 2004-08-09 19:01:13 UTC
Hi Gary, I dont see a tarball attached to the bug. 

Does this occur when running ecj as bytecode only (ie ecj in gij) or
also when ecj is compiled to native?

If it occurs only as bytecode, is the same bytecode being run on both
platforms? IIRC there are cases where "gcj -C" will generate different
bytecode on i386 vs x86_64.

Comment 9 Tom Tromey 2004-08-09 21:31:44 UTC
I looked into this a little.
Problems like this are very ugly since it is hard to debug
interpreted code.

I dug around in ecj a little.
The extra dup is probably coming from
SingleNameReference.generateCompoundAssignment;
there is some code in here for String assignments.

In this case the "+=" generating incorrect code is a
statement expression, so we can be pretty sure that
this method is being called (indirectly) from
Expression.generateCode(); look for the phrase
"expression statement".  (See also
CompoundAssignment.generateCode())

I added some debugging prints to various methods, and
from what I can tell it looks like the "valueRequired"
parameter is being set incorrectly.  Given that this
is passed around and not really mucked with, at the moment
I suspect a libffi bug.  A simple test case didn't show it
though.

Here's what my prints show; I added them to CompoundAssignment
and SingleNameReference:

in generateCode: false
  type is: org.eclipse.jdt.internal.compiler.ast.SingleNameReference
got to generateCompoundAssignment: true
  value now: true
   duping
in generateCode: false
  type is: org.eclipse.jdt.internal.compiler.ast.SingleNameReference
got to generateCompoundAssignment: true
  value now: true
   duping

Comment 10 Tom Tromey 2004-08-09 21:52:28 UTC
This simple test program shows the bug:

public class t
{
    protected final String getClearToolCommand(Object a, Object b,
Object c, Object d, int e, int f, boolean x) {
        return x ? "hi" : "byte";
    }
                                                                     
          
    public static void main(String[] args)
    {
        System.out.println(new t().getClearToolCommand(null, null,
null, null, 0, 0, false));
    }
}
                                                                     
          
For me, on x86-64, with gcc 3.4 from the RH 3.4 branch, this prints
"hi" when it should print "byte".
If I remove one of the Object or int arguments, it prints the correct
answer.
This suggests a libffi bug to me.


Comment 11 Tom Tromey 2004-08-09 22:55:56 UTC
3.5 doesn't have this problem.
So, one solution would be to back-port the 3.5 x86-64 libffi
changes to our 3.4 branch.


Comment 12 Andrew Haley 2004-08-10 14:30:04 UTC
From: Andrew Haley <aph>
To: java-patches.org
Subject: libffi patch: x86_64 stack args incorrectly aligned
Date: Tue, 10 Aug 2004 15:24:02 +0100

Thanks to Gary Benson and Tom tromey for finding this.  On the x86_64,
we weren't 8-aligning args on the stack that weren't register
allocated.  I'm not really surprised we never noticed this before,
because it requires a lot of method arguments to show the problem.

I committed this, and an accompanying test case.

Andrew.



2004-08-10  Andrew Haley  <aph>

	* src/x86/ffi64.c (ffi_prep_args ): 8-align all stack arguments.

Index: ffi64.c
===================================================================
RCS file: /cvs/gcc/gcc/libffi/src/x86/ffi64.c,v
retrieving revision 1.6
diff -u -r1.6 ffi64.c
--- ffi64.c	21 Jan 2004 06:11:08 -0000	1.6
+++ ffi64.c	10 Aug 2004 13:32:32 -0000
@@ -341,6 +341,8 @@
 	{
 	  /* Pass this argument in memory.  */
 	  argp = (void *)ALIGN(argp, (*p_arg)->alignment);
+	  /* Stack arguments are *always* at least 8 byte aligned.  */
+	  argp = (void *)ALIGN(argp, 8);
 	  memcpy (argp, *p_argv, (*p_arg)->size);
 	  argp += (*p_arg)->size;
 	}

2004-08-10  Andrew Haley  <aph>

	* testsuite/libjava.lang/err14.java: New file.
	* testsuite/libjava.lang/err14.out: New file.

Index: libjava.lang/err14.java
===================================================================
RCS file: libjava.lang/err14.java
diff -N libjava.lang/err14.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ libjava.lang/err14.java     10 Aug 2004 14:18:27 -0000
@@ -0,0 +1,20 @@
+/* Check for incorrectly aligned byte args.  */
+
+public class err14
+{
+  protected final String getClearToolCommand(Object a, Object b,
+                                             Object c, Object d, 
+                                             int e, int f, boolean x) 
+  {
+    return x ? "hi" : "byte";
+  }
+  
+  
+  public static void main(String[] args)
+  {
+    System.out.println(new err14().getClearToolCommand(null, null,
+                                                       null, null, 0,
0, false));
+    System.out.println(new err14().getClearToolCommand(null, null,
+                                                       null, null, 0,
0, true));
+  }
+}
Index: libjava.lang/err14.out
===================================================================
RCS file: libjava.lang/err14.out
diff -N libjava.lang/err14.out
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ libjava.lang/err14.out      10 Aug 2004 14:18:27 -0000
@@ -0,0 +1,2 @@
+byte
+hi