diff options
author | jfrijters <jfrijters> | 2015-07-02 10:21:17 +0300 |
---|---|---|
committer | jfrijters <jfrijters> | 2015-07-02 10:21:17 +0300 |
commit | 2299e0e8f388e3a77944eeb1b40935975026fccd (patch) | |
tree | 3d8f075f0c7bbc9a4745e912792049151ef4bd58 | |
parent | 5120f4f902f00b49e64e44ee6d4a617a2b8e6145 (diff) |
Improved -removeassertions ikvmc optimization to remove more code (esp. allow otherwise empty <clinit> methods to be optimized away).
-rw-r--r-- | openjdk/response.txt | 46 | ||||
-rw-r--r-- | runtime/ClassFile.cs | 137 | ||||
-rw-r--r-- | runtime/ClassLoaderWrapper.cs | 4 | ||||
-rw-r--r-- | runtime/DynamicTypeWrapper.cs | 15 | ||||
-rw-r--r-- | runtime/verifier.cs | 24 |
5 files changed, 184 insertions, 42 deletions
diff --git a/openjdk/response.txt b/openjdk/response.txt index 37e32097..9bc69a05 100644 --- a/openjdk/response.txt +++ b/openjdk/response.txt @@ -193,7 +193,7 @@ } { -out:IKVM.OpenJDK.Corba.dll - -baseaddress:0x57350000 + -baseaddress:0x57320000 -recurse:resources.zip/com/sun/corba/* -recurse:resources.zip/com/sun/jndi/cosnaming/jndiprovider.properties -resource:com/sun/corba/se/impl/logging/LogStrings.properties=@OPENJDK@/build/linux-x86_64-normal-server-release/jdk/classes/com/sun/corba/se/impl/logging/LogStrings.properties @@ -314,7 +314,7 @@ } { -out:IKVM.OpenJDK.XML.API.dll - -baseaddress:0x57980000 + -baseaddress:0x57950000 -resource:META-INF/services/sun.util.spi.XmlPropertiesProvider=@OPENJDK@/jdk/src/share/classes/sun/util/xml/META-INF/services/sun.util.spi.XmlPropertiesProvider @OPENJDK@/jaxp/src/javax/xml/*.class @OPENJDK@/jaxp/src/javax/xml/datatype/*.class @@ -349,7 +349,7 @@ } { -out:IKVM.OpenJDK.XML.XPath.dll - -baseaddress:0x57A40000 + -baseaddress:0x57A10000 -recurse:resources.zip/com/sun/org/apache/xalan/internal/res/* @OPENJDK@/jaxp/src/com/sun/org/apache/xalan/internal/extensions/*.class @OPENJDK@/jaxp/src/com/sun/org/apache/xalan/internal/res/*.class @@ -374,7 +374,7 @@ } { -out:IKVM.OpenJDK.XML.Parse.dll - -baseaddress:0x57DA0000 + -baseaddress:0x57D70000 -recurse:resources.zip/com/sun/org/apache/xerces/* -recurse:resources.zip/com/sun/org/apache/xml/internal/* -recurse:resources.zip/com/sun/xml/internal/stream/* @@ -437,7 +437,7 @@ } { -out:IKVM.OpenJDK.XML.Transform.dll - -baseaddress:0x58580000 + -baseaddress:0x58550000 @OPENJDK@/jaxp/src/com/sun/java_cup/internal/runtime/*.class @OPENJDK@/jaxp/src/com/sun/org/apache/bcel/internal/*.class @OPENJDK@/jaxp/src/com/sun/org/apache/bcel/internal/classfile/*.class @@ -461,7 +461,7 @@ } { -out:IKVM.OpenJDK.XML.Bind.dll - -baseaddress:0x589D0000 + -baseaddress:0x589A0000 -recurse:resources.zip/javax/xml/bind/* -recurse:resources.zip/com/sun/xml/internal/bind/* -recurse:resources.zip/com/sun/xml/internal/fastinfoset/* @@ -520,7 +520,7 @@ } { -out:IKVM.OpenJDK.XML.WebServices.dll - -baseaddress:0x58DF0000 + -baseaddress:0x58DC0000 -recurse:resources.zip/com/sun/xml/internal/messaging/* -recurse:resources.zip/com/sun/xml/internal/ws/* @OPENJDK@/jaxws/src/share/jaxws_classes/com/oracle/webservices/internal/api/*.class @@ -661,7 +661,7 @@ } { -out:IKVM.OpenJDK.XML.Crypto.dll - -baseaddress:0x59600000 + -baseaddress:0x595D0000 @OPENJDK@/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/*.class @OPENJDK@/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/*.class @OPENJDK@/jdk/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/*.class @@ -697,7 +697,7 @@ } { -out:IKVM.OpenJDK.SwingAWT.dll - -baseaddress:0x59780000 + -baseaddress:0x59750000 -remap:swingawt.xml -resource:META-INF/services/sun.java2d.pipe.RenderingEngine=resources/META-INF/services/sun.java2d.pipe.RenderingEngine -resource:META-INF/services/sun.java2d.cmm.CMMServiceProvider=@OPENJDK@/jdk/src/share/classes/sun/java2d/cmm/lcms/META-INF/services/sun.java2d.cmm.CMMServiceProvider @@ -797,14 +797,14 @@ } { -out:IKVM.OpenJDK.Charsets.dll - -baseaddress:0x5A9B0000 + -baseaddress:0x5A980000 -resource:sun/nio/cs/ext/sjis0213.dat=@OPENJDK@/build/linux-x86_64-normal-server-release/jdk/gensrc/sun/nio/cs/ext/sjis0213.dat @OPENJDK@/build/linux-x86_64-normal-server-release/jdk/gensrc/sun/nio/cs/ext/*.class @OPENJDK@/jdk/src/share/classes/sun/nio/cs/ext/*.class } { -out:IKVM.OpenJDK.Util.dll - -baseaddress:0x5AEC0000 + -baseaddress:0x5AE90000 -recurse:resources.zip/com/sun/java/util/jar/pack/intrinsic.properties -recurse:resources.zip/sun/launcher/resources/* -resource:META-INF/services/java.nio.file.spi.FileSystemProvider=@OPENJDK@/jdk/src/share/demo/nio/zipfs/src/META-INF/services/java.nio.file.spi.FileSystemProvider @@ -830,7 +830,7 @@ } { -out:IKVM.OpenJDK.Text.dll - -baseaddress:0x5B190000 + -baseaddress:0x5B160000 -recurse:resources.zip/sun/text/resources/* @OPENJDK@/jdk/src/share/classes/java/text/*.class @OPENJDK@/jdk/src/share/classes/java/text/spi/*.class @@ -842,7 +842,7 @@ } { -out:IKVM.OpenJDK.Security.dll - -baseaddress:0x5B340000 + -baseaddress:0x5B310000 -remap:security.xml sun/security/jgss/wrapper/*.class @OPENJDK@/build/linux-x86_64-normal-server-release/jdk/gensrc/sun/security/util/*.class @@ -916,7 +916,7 @@ } { -out:IKVM.OpenJDK.Management.dll - -baseaddress:0x5BB80000 + -baseaddress:0x5BB50000 java/lang/management/*.class sun/management/*.class rmistubs/org/omg/stub/javax/management/remote/rmi/*.class @@ -950,7 +950,7 @@ } { -out:IKVM.OpenJDK.Misc.dll - -baseaddress:0x5BF10000 + -baseaddress:0x5BEE0000 @OPENJDK@/build/linux-x86_64-normal-server-release/jdk/gensrc/sun/misc/resources/*.class @OPENJDK@/corba/src/share/classes/javax/activity/*.class @OPENJDK@/jaxws/src/share/jaxws_classes/javax/annotation/*.class @@ -981,7 +981,7 @@ } { -out:IKVM.OpenJDK.Naming.dll - -baseaddress:0x5BFD0000 + -baseaddress:0x5BFA0000 -recurse:resources.zip/com/sun/jndi/ldap/jndiprovider.properties -resource:META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor=@OPENJDK@/jdk/src/share/classes/sun/net/spi/nameservice/dns/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor sun/net/dns/*.class @@ -1007,7 +1007,7 @@ } { -out:IKVM.OpenJDK.Jdbc.dll - -baseaddress:0x5C150000 + -baseaddress:0x5C120000 -resource:META-INF/services/java.sql.Driver=resources/META-INF/services/java.sql.Driver -recurse:resources.zip/com/sun/rowset/* -recurse:resources.zip/javax/sql/* @@ -1024,7 +1024,7 @@ } { -out:IKVM.OpenJDK.Remoting.dll - -baseaddress:0x5C2D0000 + -baseaddress:0x5C2A0000 -recurse:resources.zip/sun/rmi/* rmistubs/com/sun/jndi/rmi/registry/*.class rmistubs/java/rmi/activation/*.class @@ -1050,7 +1050,7 @@ } { -out:IKVM.OpenJDK.Beans.dll - -baseaddress:0x5C420000 + -baseaddress:0x5C3F0000 -resource:META-INF/mailcap.default=@OPENJDK@/jaxws/src/share/jaf_classes/META-INF/mailcap.default -resource:META-INF/mimetypes.default=@OPENJDK@/jaxws/src/share/jaf_classes/META-INF/mimetypes.default @OPENJDK@/jaxws/src/share/jaf_classes/com/sun/activation/registries/*.class @@ -1066,7 +1066,7 @@ } { -out:IKVM.OpenJDK.Media.dll - -baseaddress:0x5C510000 + -baseaddress:0x5C4E0000 -resource:META-INF/services/javax.print.PrintServiceLookup=@OPENJDK@/jdk/src/windows/classes/sun/print/services/javax.print.PrintServiceLookup -resource:META-INF/services/javax.print.StreamPrintServiceFactory=@OPENJDK@/jdk/src/windows/classes/sun/print/services/javax.print.StreamPrintServiceFactory -resource:META-INF/services/javax.sound.midi.spi.MidiDeviceProvider=@OPENJDK@/jdk/src/share/classes/com/sun/media/sound/services/javax.sound.midi.spi.MidiDeviceProvider @@ -1113,7 +1113,7 @@ } { -out:IKVM.OpenJDK.Nashorn.dll - -baseaddress:0x5C780000 + -baseaddress:0x5C750000 -resource:META-INF/services/javax.script.ScriptEngineFactory=@OPENJDK@/nashorn/src/META-INF/services/javax.script.ScriptEngineFactory -resource:jdk/nashorn/internal/runtime/resources/version.properties=resources/nashorn/version.properties -recurse:@OPENJDK@/nashorn/src/*.js @@ -1151,7 +1151,7 @@ } { -out:IKVM.OpenJDK.Localedata.dll - -baseaddress:0x5CCF0000 + -baseaddress:0x5CCC0000 @OPENJDK@/build/linux-x86_64-normal-server-release/jdk/gensrc/sun/util/resources/*.class @OPENJDK@/build/linux-x86_64-normal-server-release/jdk/gensrc/sun/util/resources/ar/*.class @OPENJDK@/build/linux-x86_64-normal-server-release/jdk/gensrc/sun/util/resources/be/*.class @@ -1252,7 +1252,7 @@ } { -out:IKVM.OpenJDK.Cldrdata.dll - -baseaddress:0x5D1D0000 + -baseaddress:0x5D1A0000 @OPENJDK@/build/linux-x86_64-normal-server-release/jdk/gensrc/sun/text/resources/cldr/*.class @OPENJDK@/build/linux-x86_64-normal-server-release/jdk/gensrc/sun/text/resources/cldr/aa/*.class @OPENJDK@/build/linux-x86_64-normal-server-release/jdk/gensrc/sun/text/resources/cldr/af/*.class diff --git a/runtime/ClassFile.cs b/runtime/ClassFile.cs index 89e66874..9752fe83 100644 --- a/runtime/ClassFile.cs +++ b/runtime/ClassFile.cs @@ -51,6 +51,7 @@ namespace IKVM.Internal LineNumberTable = 2, RelaxedClassNameValidation = 4, TrustedAnnotations = 8, + RemoveAssertions = 16, } sealed class ClassFile @@ -68,6 +69,7 @@ namespace IKVM.Internal private const ushort FLAG_LAMBDAFORM_COMPILED = 0x800; private const ushort FLAG_LAMBDAFORM_HIDDEN = 0x1000; private const ushort FLAG_FORCEINLINE = 0x2000; + private const ushort FLAG_HAS_ASSERTIONS = 0x4000; private ConstantPoolItemClass[] interfaces; private Field[] fields; private Method[] methods; @@ -346,6 +348,10 @@ namespace IKVM.Internal { throw new ClassFormatError("{0} (Method \"{1}\" has illegal signature \"{2}\")", Name, name, sig); } + if((options & ClassFileParseOptions.RemoveAssertions) != 0 && methods[i].IsClassInitializer) + { + RemoveAssertionInit(methods[i]); + } } } CheckDuplicates<FieldOrMethod>(methods, "Repetitive method name/signature"); @@ -1311,6 +1317,14 @@ namespace IKVM.Internal flags |= FLAG_MASK_INTERNAL; } + internal bool HasAssertions + { + get + { + return (flags & FLAG_HAS_ASSERTIONS) != 0; + } + } + internal bool HasInitializedFields { get @@ -2706,6 +2720,11 @@ namespace IKVM.Internal } } + internal void PatchConstantValue(object value) + { + constantValue = value; + } + internal bool IsStaticFinalConstant { get { return (access_flags & (Modifiers.Final | Modifiers.Static)) == (Modifiers.Final | Modifiers.Static) && constantValue != null; } @@ -3864,5 +3883,123 @@ namespace IKVM.Internal } return null; } + + private void RemoveAssertionInit(Method m) + { + /* We match the following code sequence: + * 0 ldc <class X> + * 2 invokevirtual <Method java/lang/Class desiredAssertionStatus()Z> + * 5 ifne 12 + * 8 iconst_1 + * 9 goto 13 + * 12 iconst_0 + * 13 putstatic <Field <this class> boolean <static final field>> + */ + ConstantPoolItemFieldref fieldref; + Field field; + if (m.Instructions[0].NormalizedOpCode == NormalizedByteCode.__ldc && SafeIsConstantPoolClass(m.Instructions[0].Arg1) + && m.Instructions[1].NormalizedOpCode == NormalizedByteCode.__invokevirtual && IsDesiredAssertionStatusMethodref(m.Instructions[1].Arg1) + && m.Instructions[2].NormalizedOpCode == NormalizedByteCode.__ifne && m.Instructions[2].TargetIndex == 5 + && m.Instructions[3].NormalizedOpCode == NormalizedByteCode.__iconst && m.Instructions[3].Arg1 == 1 + && m.Instructions[4].NormalizedOpCode == NormalizedByteCode.__goto && m.Instructions[4].TargetIndex == 6 + && m.Instructions[5].NormalizedOpCode == NormalizedByteCode.__iconst && m.Instructions[5].Arg1 == 0 + && m.Instructions[6].NormalizedOpCode == NormalizedByteCode.__putstatic && (fieldref = SafeGetFieldref(m.Instructions[6].Arg1)) != null + && fieldref.Class == Name && fieldref.Signature == "Z" + && (field = GetField(fieldref.Name, fieldref.Signature)) != null + && field.IsStatic && field.IsFinal + && !HasBranchIntoRegion(m.Instructions, 7, m.Instructions.Length, 0, 7) + && !HasStaticFieldWrite(m.Instructions, 7, m.Instructions.Length, field) + && !HasExceptionHandlerInRegion(m.ExceptionTable, 0, 7)) + { + field.PatchConstantValue(true); + m.Instructions[0].PatchOpCode(NormalizedByteCode.__goto, 7); + flags |= FLAG_HAS_ASSERTIONS; + } + } + + private bool IsDesiredAssertionStatusMethodref(int cpi) + { + ConstantPoolItemMethodref method = SafeGetMethodref(cpi) as ConstantPoolItemMethodref; + return method != null + && method.Class == "java.lang.Class" + && method.Name == "desiredAssertionStatus" + && method.Signature == "()Z"; + } + + private static bool HasBranchIntoRegion(Method.Instruction[] instructions, int checkStart, int checkEnd, int regionStart, int regionEnd) + { + for (int i = checkStart; i < checkEnd; i++) + { + switch (instructions[i].NormalizedOpCode) + { + case NormalizedByteCode.__ifeq: + case NormalizedByteCode.__ifne: + case NormalizedByteCode.__iflt: + case NormalizedByteCode.__ifge: + case NormalizedByteCode.__ifgt: + case NormalizedByteCode.__ifle: + case NormalizedByteCode.__if_icmpeq: + case NormalizedByteCode.__if_icmpne: + case NormalizedByteCode.__if_icmplt: + case NormalizedByteCode.__if_icmpge: + case NormalizedByteCode.__if_icmpgt: + case NormalizedByteCode.__if_icmple: + case NormalizedByteCode.__if_acmpeq: + case NormalizedByteCode.__if_acmpne: + case NormalizedByteCode.__ifnull: + case NormalizedByteCode.__ifnonnull: + case NormalizedByteCode.__goto: + case NormalizedByteCode.__jsr: + if (instructions[i].TargetIndex > regionStart && instructions[i].TargetIndex < regionEnd) + { + return true; + } + break; + case NormalizedByteCode.__tableswitch: + case NormalizedByteCode.__lookupswitch: + if (instructions[i].DefaultTarget > regionStart && instructions[i].DefaultTarget < regionEnd) + { + return true; + } + for (int j = 0; j < instructions[i].SwitchEntryCount; j++) + { + if (instructions[i].GetSwitchTargetIndex(j) > regionStart && instructions[i].GetSwitchTargetIndex(j) < regionEnd) + { + return true; + } + } + break; + } + } + return false; + } + + private bool HasStaticFieldWrite(Method.Instruction[] instructions, int checkStart, int checkEnd, Field field) + { + for (int i = checkStart; i < checkEnd; i++) + { + if (instructions[i].NormalizedOpCode == NormalizedByteCode.__putstatic) + { + ConstantPoolItemFieldref fieldref = SafeGetFieldref(instructions[i].Arg1); + if (fieldref != null && fieldref.Class == Name && fieldref.Name == field.Name && fieldref.Signature == field.Signature) + { + return true; + } + } + } + return false; + } + + private static bool HasExceptionHandlerInRegion(Method.ExceptionTableEntry[] entries, int regionStart, int regionEnd) + { + for (int i = 0; i < entries.Length; i++) + { + if (entries[i].handlerIndex > regionStart && entries[i].handlerIndex < regionEnd) + { + return true; + } + } + return false; + } } } diff --git a/runtime/ClassLoaderWrapper.cs b/runtime/ClassLoaderWrapper.cs index 48a60068..d04d05dd 100644 --- a/runtime/ClassLoaderWrapper.cs +++ b/runtime/ClassLoaderWrapper.cs @@ -1495,6 +1495,10 @@ namespace IKVM.Internal { cfp |= ClassFileParseOptions.TrustedAnnotations; } + if (RemoveAsserts) + { + cfp |= ClassFileParseOptions.RemoveAssertions; + } return cfp; #else ClassFileParseOptions cfp = ClassFileParseOptions.LineNumberTable; diff --git a/runtime/DynamicTypeWrapper.cs b/runtime/DynamicTypeWrapper.cs index 6b1f19c0..72685c14 100644 --- a/runtime/DynamicTypeWrapper.cs +++ b/runtime/DynamicTypeWrapper.cs @@ -1147,7 +1147,20 @@ namespace IKVM.Internal noop = true; for (int i = 0; i < m.Instructions.Length; i++) { - NormalizedByteCode bc = m.Instructions[i].NormalizedOpCode; + NormalizedByteCode bc; + while ((bc = m.Instructions[i].NormalizedOpCode) == NormalizedByteCode.__goto) + { + int target = m.Instructions[i].TargetIndex; + if (target <= i) + { + // backward branch means we can't do anything + noop = false; + return false; + } + // we must skip the unused instructions because the "remove assertions" optimization + // uses a goto to remove the (now unused) code + i = target; + } if (bc == NormalizedByteCode.__getstatic || bc == NormalizedByteCode.__putstatic) { ClassFile.ConstantPoolItemFieldref fld = classFile.SafeGetFieldref(m.Instructions[i].Arg1); diff --git a/runtime/verifier.cs b/runtime/verifier.cs index b9c5991a..fdeb809d 100644 --- a/runtime/verifier.cs +++ b/runtime/verifier.cs @@ -2572,19 +2572,9 @@ sealed class MethodAnalyzer // Optimization pass if (classLoader.RemoveAsserts) { - FieldWrapper assertionsDisabled = null; - foreach (FieldWrapper fw in wrapper.GetFields()) - { - // HACK we assume that all compilers use the same name for this field (ecj and javac do) - if (fw.Name == "$assertionsDisabled" && fw.Signature == "Z" - && (fw.Modifiers & (IKVM.Attributes.Modifiers.AccessMask | IKVM.Attributes.Modifiers.Final | IKVM.Attributes.Modifiers.Static | IKVM.Attributes.Modifiers.Synthetic)) - == (IKVM.Attributes.Modifiers.Static | IKVM.Attributes.Modifiers.Final | IKVM.Attributes.Modifiers.Synthetic)) - { - assertionsDisabled = fw; - break; - } - } - if (assertionsDisabled != null) + // While the optimization is general, in practice it never happens that a getstatic is used on a final field, + // so we only look for this if assert initialization has been optimized out. + if (classFile.HasAssertions) { // compute branch targets InstructionFlags[] flags = MethodAnalyzer.ComputePartialReachability(codeInfo, method.Instructions, exceptions, 0, false); @@ -2596,12 +2586,10 @@ sealed class MethodAnalyzer && instructions[i + 1].TargetIndex > i && (flags[i + 1] & InstructionFlags.BranchTarget) == 0) { - ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instructions[i].Arg1); - if (cpi.GetField() == assertionsDisabled) + ConstantFieldWrapper field = classFile.GetFieldref(instructions[i].Arg1).GetField() as ConstantFieldWrapper; + if (field != null && field.FieldTypeWrapper == PrimitiveTypeWrapper.BOOLEAN && (bool)field.GetConstantValue()) { - // We've found an assertion. We patch the instruction to branch around it so that - // the assertion code will be unreachable (and hence optimized away). - // Note that the goto will be optimized away later by the code generator (which removes unnecessary branches). + // We know the branch will always be taken, so we replace the getstatic/ifne by a goto. instructions[i].PatchOpCode(NormalizedByteCode.__goto, instructions[i + 1].TargetIndex); } } |