--- /dev/null 2016-05-31 09:42:47.975716356 -0700 +++ new/src/jdk.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java 2016-12-09 00:50:08.409036341 -0800 @@ -0,0 +1,4118 @@ +/* + * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.java; + +import static org.graalvm.compiler.bytecode.Bytecodes.AALOAD; +import static org.graalvm.compiler.bytecode.Bytecodes.AASTORE; +import static org.graalvm.compiler.bytecode.Bytecodes.ACONST_NULL; +import static org.graalvm.compiler.bytecode.Bytecodes.ALOAD; +import static org.graalvm.compiler.bytecode.Bytecodes.ALOAD_0; +import static org.graalvm.compiler.bytecode.Bytecodes.ALOAD_1; +import static org.graalvm.compiler.bytecode.Bytecodes.ALOAD_2; +import static org.graalvm.compiler.bytecode.Bytecodes.ALOAD_3; +import static org.graalvm.compiler.bytecode.Bytecodes.ANEWARRAY; +import static org.graalvm.compiler.bytecode.Bytecodes.ARETURN; +import static org.graalvm.compiler.bytecode.Bytecodes.ARRAYLENGTH; +import static org.graalvm.compiler.bytecode.Bytecodes.ASTORE; +import static org.graalvm.compiler.bytecode.Bytecodes.ASTORE_0; +import static org.graalvm.compiler.bytecode.Bytecodes.ASTORE_1; +import static org.graalvm.compiler.bytecode.Bytecodes.ASTORE_2; +import static org.graalvm.compiler.bytecode.Bytecodes.ASTORE_3; +import static org.graalvm.compiler.bytecode.Bytecodes.ATHROW; +import static org.graalvm.compiler.bytecode.Bytecodes.BALOAD; +import static org.graalvm.compiler.bytecode.Bytecodes.BASTORE; +import static org.graalvm.compiler.bytecode.Bytecodes.BIPUSH; +import static org.graalvm.compiler.bytecode.Bytecodes.BREAKPOINT; +import static org.graalvm.compiler.bytecode.Bytecodes.CALOAD; +import static org.graalvm.compiler.bytecode.Bytecodes.CASTORE; +import static org.graalvm.compiler.bytecode.Bytecodes.CHECKCAST; +import static org.graalvm.compiler.bytecode.Bytecodes.D2F; +import static org.graalvm.compiler.bytecode.Bytecodes.D2I; +import static org.graalvm.compiler.bytecode.Bytecodes.D2L; +import static org.graalvm.compiler.bytecode.Bytecodes.DADD; +import static org.graalvm.compiler.bytecode.Bytecodes.DALOAD; +import static org.graalvm.compiler.bytecode.Bytecodes.DASTORE; +import static org.graalvm.compiler.bytecode.Bytecodes.DCMPG; +import static org.graalvm.compiler.bytecode.Bytecodes.DCMPL; +import static org.graalvm.compiler.bytecode.Bytecodes.DCONST_0; +import static org.graalvm.compiler.bytecode.Bytecodes.DCONST_1; +import static org.graalvm.compiler.bytecode.Bytecodes.DDIV; +import static org.graalvm.compiler.bytecode.Bytecodes.DLOAD; +import static org.graalvm.compiler.bytecode.Bytecodes.DLOAD_0; +import static org.graalvm.compiler.bytecode.Bytecodes.DLOAD_1; +import static org.graalvm.compiler.bytecode.Bytecodes.DLOAD_2; +import static org.graalvm.compiler.bytecode.Bytecodes.DLOAD_3; +import static org.graalvm.compiler.bytecode.Bytecodes.DMUL; +import static org.graalvm.compiler.bytecode.Bytecodes.DNEG; +import static org.graalvm.compiler.bytecode.Bytecodes.DREM; +import static org.graalvm.compiler.bytecode.Bytecodes.DRETURN; +import static org.graalvm.compiler.bytecode.Bytecodes.DSTORE; +import static org.graalvm.compiler.bytecode.Bytecodes.DSTORE_0; +import static org.graalvm.compiler.bytecode.Bytecodes.DSTORE_1; +import static org.graalvm.compiler.bytecode.Bytecodes.DSTORE_2; +import static org.graalvm.compiler.bytecode.Bytecodes.DSTORE_3; +import static org.graalvm.compiler.bytecode.Bytecodes.DSUB; +import static org.graalvm.compiler.bytecode.Bytecodes.DUP; +import static org.graalvm.compiler.bytecode.Bytecodes.DUP2; +import static org.graalvm.compiler.bytecode.Bytecodes.DUP2_X1; +import static org.graalvm.compiler.bytecode.Bytecodes.DUP2_X2; +import static org.graalvm.compiler.bytecode.Bytecodes.DUP_X1; +import static org.graalvm.compiler.bytecode.Bytecodes.DUP_X2; +import static org.graalvm.compiler.bytecode.Bytecodes.F2D; +import static org.graalvm.compiler.bytecode.Bytecodes.F2I; +import static org.graalvm.compiler.bytecode.Bytecodes.F2L; +import static org.graalvm.compiler.bytecode.Bytecodes.FADD; +import static org.graalvm.compiler.bytecode.Bytecodes.FALOAD; +import static org.graalvm.compiler.bytecode.Bytecodes.FASTORE; +import static org.graalvm.compiler.bytecode.Bytecodes.FCMPG; +import static org.graalvm.compiler.bytecode.Bytecodes.FCMPL; +import static org.graalvm.compiler.bytecode.Bytecodes.FCONST_0; +import static org.graalvm.compiler.bytecode.Bytecodes.FCONST_1; +import static org.graalvm.compiler.bytecode.Bytecodes.FCONST_2; +import static org.graalvm.compiler.bytecode.Bytecodes.FDIV; +import static org.graalvm.compiler.bytecode.Bytecodes.FLOAD; +import static org.graalvm.compiler.bytecode.Bytecodes.FLOAD_0; +import static org.graalvm.compiler.bytecode.Bytecodes.FLOAD_1; +import static org.graalvm.compiler.bytecode.Bytecodes.FLOAD_2; +import static org.graalvm.compiler.bytecode.Bytecodes.FLOAD_3; +import static org.graalvm.compiler.bytecode.Bytecodes.FMUL; +import static org.graalvm.compiler.bytecode.Bytecodes.FNEG; +import static org.graalvm.compiler.bytecode.Bytecodes.FREM; +import static org.graalvm.compiler.bytecode.Bytecodes.FRETURN; +import static org.graalvm.compiler.bytecode.Bytecodes.FSTORE; +import static org.graalvm.compiler.bytecode.Bytecodes.FSTORE_0; +import static org.graalvm.compiler.bytecode.Bytecodes.FSTORE_1; +import static org.graalvm.compiler.bytecode.Bytecodes.FSTORE_2; +import static org.graalvm.compiler.bytecode.Bytecodes.FSTORE_3; +import static org.graalvm.compiler.bytecode.Bytecodes.FSUB; +import static org.graalvm.compiler.bytecode.Bytecodes.GETFIELD; +import static org.graalvm.compiler.bytecode.Bytecodes.GETSTATIC; +import static org.graalvm.compiler.bytecode.Bytecodes.GOTO; +import static org.graalvm.compiler.bytecode.Bytecodes.GOTO_W; +import static org.graalvm.compiler.bytecode.Bytecodes.I2B; +import static org.graalvm.compiler.bytecode.Bytecodes.I2C; +import static org.graalvm.compiler.bytecode.Bytecodes.I2D; +import static org.graalvm.compiler.bytecode.Bytecodes.I2F; +import static org.graalvm.compiler.bytecode.Bytecodes.I2L; +import static org.graalvm.compiler.bytecode.Bytecodes.I2S; +import static org.graalvm.compiler.bytecode.Bytecodes.IADD; +import static org.graalvm.compiler.bytecode.Bytecodes.IALOAD; +import static org.graalvm.compiler.bytecode.Bytecodes.IAND; +import static org.graalvm.compiler.bytecode.Bytecodes.IASTORE; +import static org.graalvm.compiler.bytecode.Bytecodes.ICONST_0; +import static org.graalvm.compiler.bytecode.Bytecodes.ICONST_1; +import static org.graalvm.compiler.bytecode.Bytecodes.ICONST_2; +import static org.graalvm.compiler.bytecode.Bytecodes.ICONST_3; +import static org.graalvm.compiler.bytecode.Bytecodes.ICONST_4; +import static org.graalvm.compiler.bytecode.Bytecodes.ICONST_5; +import static org.graalvm.compiler.bytecode.Bytecodes.ICONST_M1; +import static org.graalvm.compiler.bytecode.Bytecodes.IDIV; +import static org.graalvm.compiler.bytecode.Bytecodes.IFEQ; +import static org.graalvm.compiler.bytecode.Bytecodes.IFGE; +import static org.graalvm.compiler.bytecode.Bytecodes.IFGT; +import static org.graalvm.compiler.bytecode.Bytecodes.IFLE; +import static org.graalvm.compiler.bytecode.Bytecodes.IFLT; +import static org.graalvm.compiler.bytecode.Bytecodes.IFNE; +import static org.graalvm.compiler.bytecode.Bytecodes.IFNONNULL; +import static org.graalvm.compiler.bytecode.Bytecodes.IFNULL; +import static org.graalvm.compiler.bytecode.Bytecodes.IF_ACMPEQ; +import static org.graalvm.compiler.bytecode.Bytecodes.IF_ACMPNE; +import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPEQ; +import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPGE; +import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPGT; +import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPLE; +import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPLT; +import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPNE; +import static org.graalvm.compiler.bytecode.Bytecodes.IINC; +import static org.graalvm.compiler.bytecode.Bytecodes.ILOAD; +import static org.graalvm.compiler.bytecode.Bytecodes.ILOAD_0; +import static org.graalvm.compiler.bytecode.Bytecodes.ILOAD_1; +import static org.graalvm.compiler.bytecode.Bytecodes.ILOAD_2; +import static org.graalvm.compiler.bytecode.Bytecodes.ILOAD_3; +import static org.graalvm.compiler.bytecode.Bytecodes.IMUL; +import static org.graalvm.compiler.bytecode.Bytecodes.INEG; +import static org.graalvm.compiler.bytecode.Bytecodes.INSTANCEOF; +import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEDYNAMIC; +import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEINTERFACE; +import static org.graalvm.compiler.bytecode.Bytecodes.INVOKESPECIAL; +import static org.graalvm.compiler.bytecode.Bytecodes.INVOKESTATIC; +import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEVIRTUAL; +import static org.graalvm.compiler.bytecode.Bytecodes.IOR; +import static org.graalvm.compiler.bytecode.Bytecodes.IREM; +import static org.graalvm.compiler.bytecode.Bytecodes.IRETURN; +import static org.graalvm.compiler.bytecode.Bytecodes.ISHL; +import static org.graalvm.compiler.bytecode.Bytecodes.ISHR; +import static org.graalvm.compiler.bytecode.Bytecodes.ISTORE; +import static org.graalvm.compiler.bytecode.Bytecodes.ISTORE_0; +import static org.graalvm.compiler.bytecode.Bytecodes.ISTORE_1; +import static org.graalvm.compiler.bytecode.Bytecodes.ISTORE_2; +import static org.graalvm.compiler.bytecode.Bytecodes.ISTORE_3; +import static org.graalvm.compiler.bytecode.Bytecodes.ISUB; +import static org.graalvm.compiler.bytecode.Bytecodes.IUSHR; +import static org.graalvm.compiler.bytecode.Bytecodes.IXOR; +import static org.graalvm.compiler.bytecode.Bytecodes.JSR; +import static org.graalvm.compiler.bytecode.Bytecodes.JSR_W; +import static org.graalvm.compiler.bytecode.Bytecodes.L2D; +import static org.graalvm.compiler.bytecode.Bytecodes.L2F; +import static org.graalvm.compiler.bytecode.Bytecodes.L2I; +import static org.graalvm.compiler.bytecode.Bytecodes.LADD; +import static org.graalvm.compiler.bytecode.Bytecodes.LALOAD; +import static org.graalvm.compiler.bytecode.Bytecodes.LAND; +import static org.graalvm.compiler.bytecode.Bytecodes.LASTORE; +import static org.graalvm.compiler.bytecode.Bytecodes.LCMP; +import static org.graalvm.compiler.bytecode.Bytecodes.LCONST_0; +import static org.graalvm.compiler.bytecode.Bytecodes.LCONST_1; +import static org.graalvm.compiler.bytecode.Bytecodes.LDC; +import static org.graalvm.compiler.bytecode.Bytecodes.LDC2_W; +import static org.graalvm.compiler.bytecode.Bytecodes.LDC_W; +import static org.graalvm.compiler.bytecode.Bytecodes.LDIV; +import static org.graalvm.compiler.bytecode.Bytecodes.LLOAD; +import static org.graalvm.compiler.bytecode.Bytecodes.LLOAD_0; +import static org.graalvm.compiler.bytecode.Bytecodes.LLOAD_1; +import static org.graalvm.compiler.bytecode.Bytecodes.LLOAD_2; +import static org.graalvm.compiler.bytecode.Bytecodes.LLOAD_3; +import static org.graalvm.compiler.bytecode.Bytecodes.LMUL; +import static org.graalvm.compiler.bytecode.Bytecodes.LNEG; +import static org.graalvm.compiler.bytecode.Bytecodes.LOOKUPSWITCH; +import static org.graalvm.compiler.bytecode.Bytecodes.LOR; +import static org.graalvm.compiler.bytecode.Bytecodes.LREM; +import static org.graalvm.compiler.bytecode.Bytecodes.LRETURN; +import static org.graalvm.compiler.bytecode.Bytecodes.LSHL; +import static org.graalvm.compiler.bytecode.Bytecodes.LSHR; +import static org.graalvm.compiler.bytecode.Bytecodes.LSTORE; +import static org.graalvm.compiler.bytecode.Bytecodes.LSTORE_0; +import static org.graalvm.compiler.bytecode.Bytecodes.LSTORE_1; +import static org.graalvm.compiler.bytecode.Bytecodes.LSTORE_2; +import static org.graalvm.compiler.bytecode.Bytecodes.LSTORE_3; +import static org.graalvm.compiler.bytecode.Bytecodes.LSUB; +import static org.graalvm.compiler.bytecode.Bytecodes.LUSHR; +import static org.graalvm.compiler.bytecode.Bytecodes.LXOR; +import static org.graalvm.compiler.bytecode.Bytecodes.MONITORENTER; +import static org.graalvm.compiler.bytecode.Bytecodes.MONITOREXIT; +import static org.graalvm.compiler.bytecode.Bytecodes.MULTIANEWARRAY; +import static org.graalvm.compiler.bytecode.Bytecodes.NEW; +import static org.graalvm.compiler.bytecode.Bytecodes.NEWARRAY; +import static org.graalvm.compiler.bytecode.Bytecodes.NOP; +import static org.graalvm.compiler.bytecode.Bytecodes.POP; +import static org.graalvm.compiler.bytecode.Bytecodes.POP2; +import static org.graalvm.compiler.bytecode.Bytecodes.PUTFIELD; +import static org.graalvm.compiler.bytecode.Bytecodes.PUTSTATIC; +import static org.graalvm.compiler.bytecode.Bytecodes.RET; +import static org.graalvm.compiler.bytecode.Bytecodes.RETURN; +import static org.graalvm.compiler.bytecode.Bytecodes.SALOAD; +import static org.graalvm.compiler.bytecode.Bytecodes.SASTORE; +import static org.graalvm.compiler.bytecode.Bytecodes.SIPUSH; +import static org.graalvm.compiler.bytecode.Bytecodes.SWAP; +import static org.graalvm.compiler.bytecode.Bytecodes.TABLESWITCH; +import static org.graalvm.compiler.bytecode.Bytecodes.nameOf; +import static org.graalvm.compiler.core.common.GraalOptions.DeoptALot; +import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC; +import static org.graalvm.compiler.core.common.GraalOptions.PrintProfilingInformation; +import static org.graalvm.compiler.core.common.GraalOptions.ResolveClassBeforeStaticInvoke; +import static org.graalvm.compiler.core.common.GraalOptions.StressInvokeWithExceptionNode; +import static org.graalvm.compiler.core.common.GraalOptions.UseGraalInstrumentation; +import static org.graalvm.compiler.core.common.type.StampFactory.objectNonNull; +import static org.graalvm.compiler.debug.GraalError.guarantee; +import static org.graalvm.compiler.debug.GraalError.shouldNotReachHere; +import static org.graalvm.compiler.java.BytecodeParserOptions.DumpDuringGraphBuilding; +import static org.graalvm.compiler.java.BytecodeParserOptions.TraceInlineDuringParsing; +import static org.graalvm.compiler.java.BytecodeParserOptions.TraceParserPlugins; +import static org.graalvm.compiler.java.BytecodeParserOptions.UseGuardedIntrinsics; +import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_DURING_PARSING; +import static org.graalvm.compiler.nodes.type.StampTool.isPointerNonNull; +import static java.lang.String.format; +import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateRecompile; +import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateReprofile; +import static jdk.vm.ci.meta.DeoptimizationReason.JavaSubroutineMismatch; +import static jdk.vm.ci.meta.DeoptimizationReason.NullCheckException; +import static jdk.vm.ci.meta.DeoptimizationReason.RuntimeConstraint; +import static jdk.vm.ci.meta.DeoptimizationReason.TypeCheckedInliningViolated; +import static jdk.vm.ci.meta.DeoptimizationReason.UnreachedCode; +import static jdk.vm.ci.meta.DeoptimizationReason.Unresolved; +import static jdk.vm.ci.runtime.JVMCICompiler.INVOCATION_ENTRY_BCI; + +import java.lang.ref.Reference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Formatter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.graalvm.compiler.bytecode.Bytecode; +import org.graalvm.compiler.bytecode.BytecodeDisassembler; +import org.graalvm.compiler.bytecode.BytecodeLookupSwitch; +import org.graalvm.compiler.bytecode.BytecodeProvider; +import org.graalvm.compiler.bytecode.BytecodeStream; +import org.graalvm.compiler.bytecode.BytecodeSwitch; +import org.graalvm.compiler.bytecode.BytecodeTableSwitch; +import org.graalvm.compiler.bytecode.Bytecodes; +import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode; +import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecodeProvider; +import org.graalvm.compiler.common.PermanentBailoutException; +import org.graalvm.compiler.core.common.GraalOptions; +import org.graalvm.compiler.core.common.LocationIdentity; +import org.graalvm.compiler.core.common.calc.Condition; +import org.graalvm.compiler.core.common.calc.FloatConvert; +import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; +import org.graalvm.compiler.core.common.type.AbstractPointerStamp; +import org.graalvm.compiler.core.common.type.ObjectStamp; +import org.graalvm.compiler.core.common.type.Stamp; +import org.graalvm.compiler.core.common.type.StampFactory; +import org.graalvm.compiler.core.common.type.StampPair; +import org.graalvm.compiler.core.common.type.TypeReference; +import org.graalvm.compiler.core.common.util.Util; +import org.graalvm.compiler.debug.Debug; +import org.graalvm.compiler.debug.Debug.Scope; +import org.graalvm.compiler.debug.DebugCloseable; +import org.graalvm.compiler.debug.DebugCounter; +import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.debug.Indent; +import org.graalvm.compiler.debug.TTY; +import org.graalvm.compiler.graph.Graph.Mark; +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.graph.NodeSourcePosition; +import org.graalvm.compiler.graph.iterators.NodeIterable; +import org.graalvm.compiler.java.BciBlockMapping.BciBlock; +import org.graalvm.compiler.java.BciBlockMapping.ExceptionDispatchBlock; +import org.graalvm.compiler.nodeinfo.InputType; +import org.graalvm.compiler.nodes.AbstractBeginNode; +import org.graalvm.compiler.nodes.AbstractMergeNode; +import org.graalvm.compiler.nodes.BeginNode; +import org.graalvm.compiler.nodes.BeginStateSplitNode; +import org.graalvm.compiler.nodes.CallTargetNode; +import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.ControlSplitNode; +import org.graalvm.compiler.nodes.DeoptimizeNode; +import org.graalvm.compiler.nodes.EndNode; +import org.graalvm.compiler.nodes.EntryMarkerNode; +import org.graalvm.compiler.nodes.EntryProxyNode; +import org.graalvm.compiler.nodes.FieldLocationIdentity; +import org.graalvm.compiler.nodes.FixedGuardNode; +import org.graalvm.compiler.nodes.FixedNode; +import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.FullInfopointNode; +import org.graalvm.compiler.nodes.IfNode; +import org.graalvm.compiler.nodes.Invoke; +import org.graalvm.compiler.nodes.InvokeNode; +import org.graalvm.compiler.nodes.InvokeWithExceptionNode; +import org.graalvm.compiler.nodes.KillingBeginNode; +import org.graalvm.compiler.nodes.LogicConstantNode; +import org.graalvm.compiler.nodes.LogicNegationNode; +import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.LoopBeginNode; +import org.graalvm.compiler.nodes.LoopEndNode; +import org.graalvm.compiler.nodes.LoopExitNode; +import org.graalvm.compiler.nodes.MergeNode; +import org.graalvm.compiler.nodes.ParameterNode; +import org.graalvm.compiler.nodes.PiNode; +import org.graalvm.compiler.nodes.ReturnNode; +import org.graalvm.compiler.nodes.StartNode; +import org.graalvm.compiler.nodes.StateSplit; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.UnwindNode; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.calc.AddNode; +import org.graalvm.compiler.nodes.calc.AndNode; +import org.graalvm.compiler.nodes.calc.CompareNode; +import org.graalvm.compiler.nodes.calc.ConditionalNode; +import org.graalvm.compiler.nodes.calc.DivNode; +import org.graalvm.compiler.nodes.calc.FloatConvertNode; +import org.graalvm.compiler.nodes.calc.IntegerBelowNode; +import org.graalvm.compiler.nodes.calc.IntegerEqualsNode; +import org.graalvm.compiler.nodes.calc.IntegerLessThanNode; +import org.graalvm.compiler.nodes.calc.IsNullNode; +import org.graalvm.compiler.nodes.calc.LeftShiftNode; +import org.graalvm.compiler.nodes.calc.MulNode; +import org.graalvm.compiler.nodes.calc.NarrowNode; +import org.graalvm.compiler.nodes.calc.NegateNode; +import org.graalvm.compiler.nodes.calc.NormalizeCompareNode; +import org.graalvm.compiler.nodes.calc.ObjectEqualsNode; +import org.graalvm.compiler.nodes.calc.OrNode; +import org.graalvm.compiler.nodes.calc.RemNode; +import org.graalvm.compiler.nodes.calc.RightShiftNode; +import org.graalvm.compiler.nodes.calc.SignExtendNode; +import org.graalvm.compiler.nodes.calc.SignedDivNode; +import org.graalvm.compiler.nodes.calc.SignedRemNode; +import org.graalvm.compiler.nodes.calc.SubNode; +import org.graalvm.compiler.nodes.calc.UnsignedRightShiftNode; +import org.graalvm.compiler.nodes.calc.XorNode; +import org.graalvm.compiler.nodes.calc.ZeroExtendNode; +import org.graalvm.compiler.nodes.debug.instrumentation.InstrumentationBeginNode; +import org.graalvm.compiler.nodes.extended.AnchoringNode; +import org.graalvm.compiler.nodes.extended.BranchProbabilityNode; +import org.graalvm.compiler.nodes.extended.BytecodeExceptionNode; +import org.graalvm.compiler.nodes.extended.GuardedNode; +import org.graalvm.compiler.nodes.extended.GuardingNode; +import org.graalvm.compiler.nodes.extended.IntegerSwitchNode; +import org.graalvm.compiler.nodes.extended.LoadHubNode; +import org.graalvm.compiler.nodes.extended.LoadMethodNode; +import org.graalvm.compiler.nodes.extended.MembarNode; +import org.graalvm.compiler.nodes.extended.ValueAnchorNode; +import org.graalvm.compiler.nodes.graphbuilderconf.ClassInitializationPlugin; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.BytecodeExceptionMode; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; +import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin; +import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo; +import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext; +import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; +import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver; +import org.graalvm.compiler.nodes.graphbuilderconf.NodePlugin; +import org.graalvm.compiler.nodes.graphbuilderconf.ProfilingPlugin; +import org.graalvm.compiler.nodes.java.ArrayLengthNode; +import org.graalvm.compiler.nodes.java.ExceptionObjectNode; +import org.graalvm.compiler.nodes.java.FinalFieldBarrierNode; +import org.graalvm.compiler.nodes.java.InstanceOfNode; +import org.graalvm.compiler.nodes.java.LoadFieldNode; +import org.graalvm.compiler.nodes.java.LoadIndexedNode; +import org.graalvm.compiler.nodes.java.MethodCallTargetNode; +import org.graalvm.compiler.nodes.java.MonitorEnterNode; +import org.graalvm.compiler.nodes.java.MonitorExitNode; +import org.graalvm.compiler.nodes.java.MonitorIdNode; +import org.graalvm.compiler.nodes.java.NewArrayNode; +import org.graalvm.compiler.nodes.java.NewInstanceNode; +import org.graalvm.compiler.nodes.java.NewMultiArrayNode; +import org.graalvm.compiler.nodes.java.RegisterFinalizerNode; +import org.graalvm.compiler.nodes.java.StoreFieldNode; +import org.graalvm.compiler.nodes.java.StoreIndexedNode; +import org.graalvm.compiler.nodes.spi.StampProvider; +import org.graalvm.compiler.nodes.type.StampTool; +import org.graalvm.compiler.nodes.util.GraphUtil; +import org.graalvm.compiler.phases.OptimisticOptimizations; + +import jdk.vm.ci.code.BailoutException; +import jdk.vm.ci.code.BytecodeFrame; +import jdk.vm.ci.code.CodeUtil; +import jdk.vm.ci.code.site.InfopointReason; +import jdk.vm.ci.meta.ConstantPool; +import jdk.vm.ci.meta.ConstantReflectionProvider; +import jdk.vm.ci.meta.DeoptimizationAction; +import jdk.vm.ci.meta.DeoptimizationReason; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaField; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.JavaMethod; +import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.JavaTypeProfile; +import jdk.vm.ci.meta.JavaTypeProfile.ProfiledType; +import jdk.vm.ci.meta.LineNumberTable; +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ProfilingInfo; +import jdk.vm.ci.meta.RawConstant; +import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.TriState; + +/** + * The {@code GraphBuilder} class parses the bytecode of a method and builds the IR graph. + */ +public class BytecodeParser implements GraphBuilderContext { + + /** + * The minimum value to which {@link BytecodeParserOptions#TraceBytecodeParserLevel} must be set + * to trace the bytecode instructions as they are parsed. + */ + public static final int TRACELEVEL_INSTRUCTIONS = 1; + + /** + * The minimum value to which {@link BytecodeParserOptions#TraceBytecodeParserLevel} must be set + * to trace the frame state before each bytecode instruction as it is parsed. + */ + public static final int TRACELEVEL_STATE = 2; + + /** + * Meters the number of actual bytecodes parsed. + */ + public static final DebugCounter BytecodesParsed = Debug.counter("BytecodesParsed"); + + protected static final DebugCounter EXPLICIT_EXCEPTIONS = Debug.counter("ExplicitExceptions"); + + /** + * A scoped object for tasks to be performed after parsing an intrinsic such as processing + * {@linkplain BytecodeFrame#isPlaceholderBci(int) placeholder} frames states. + */ + static class IntrinsicScope implements AutoCloseable { + FrameState stateBefore; + final Mark mark; + final BytecodeParser parser; + + /** + * Creates a scope for root parsing an intrinsic. + * + * @param parser the parsing context of the intrinsic + */ + IntrinsicScope(BytecodeParser parser) { + this.parser = parser; + assert parser.parent == null; + assert parser.bci() == 0; + mark = null; + } + + /** + * Creates a scope for parsing an intrinsic during graph builder inlining. + * + * @param parser the parsing context of the (non-intrinsic) method calling the intrinsic + * @param args the arguments to the call + */ + IntrinsicScope(BytecodeParser parser, JavaKind[] argSlotKinds, ValueNode[] args) { + assert !parser.parsingIntrinsic(); + this.parser = parser; + mark = parser.getGraph().getMark(); + stateBefore = parser.frameState.create(parser.bci(), parser.getNonIntrinsicAncestor(), false, argSlotKinds, args); + } + + @Override + public void close() { + IntrinsicContext intrinsic = parser.intrinsicContext; + if (intrinsic != null && intrinsic.isPostParseInlined()) { + return; + } + + processPlaceholderFrameStates(intrinsic); + } + + /** + * Fixes up the {@linkplain BytecodeFrame#isPlaceholderBci(int) placeholder} frame states + * added to the graph while parsing/inlining the intrinsic for which this object exists. + */ + private void processPlaceholderFrameStates(IntrinsicContext intrinsic) { + FrameState stateAfterReturn = null; + StructuredGraph graph = parser.getGraph(); + for (Node node : graph.getNewNodes(mark)) { + if (node instanceof FrameState) { + FrameState frameState = (FrameState) node; + if (BytecodeFrame.isPlaceholderBci(frameState.bci)) { + if (frameState.bci == BytecodeFrame.AFTER_BCI) { + FrameStateBuilder frameStateBuilder = parser.frameState; + if (frameState.stackSize() != 0) { + assert frameState.usages().count() == 1; + ValueNode returnVal = frameState.stackAt(0); + assert returnVal == frameState.usages().first(); + + if (parser.currentInvokeReturnType == null) { + assert intrinsic.isCompilationRoot(); + FrameState newFrameState = graph.add(new FrameState(BytecodeFrame.INVALID_FRAMESTATE_BCI)); + frameState.replaceAndDelete(newFrameState); + } else { + /* + * Swap the top-of-stack value with the side-effect return value + * using the frame state. + */ + JavaKind returnKind = parser.currentInvokeReturnType.getJavaKind(); + ValueNode tos = frameStateBuilder.pop(returnKind); + assert tos.getStackKind() == returnVal.getStackKind(); + FrameState newFrameState = frameStateBuilder.create(parser.stream.nextBCI(), parser.getNonIntrinsicAncestor(), false, new JavaKind[]{returnKind}, + new ValueNode[]{returnVal}); + frameState.replaceAndDelete(newFrameState); + frameStateBuilder.push(returnKind, tos); + } + } else { + if (stateAfterReturn == null) { + if (intrinsic != null) { + assert intrinsic.isCompilationRoot(); + stateAfterReturn = graph.add(new FrameState(BytecodeFrame.INVALID_FRAMESTATE_BCI)); + } else { + stateAfterReturn = frameStateBuilder.create(parser.stream.nextBCI(), null); + } + } + frameState.replaceAndDelete(stateAfterReturn); + } + } else if (frameState.bci == BytecodeFrame.BEFORE_BCI) { + if (stateBefore == null) { + stateBefore = graph.start().stateAfter(); + } + if (stateBefore != frameState) { + frameState.replaceAndDelete(stateBefore); + } + } else { + assert frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI; + } + } + } + } + } + } + + private static class Target { + FixedNode fixed; + FrameStateBuilder state; + + Target(FixedNode fixed, FrameStateBuilder state) { + this.fixed = fixed; + this.state = state; + } + } + + @SuppressWarnings("serial") + public static class BytecodeParserError extends GraalError { + + public BytecodeParserError(Throwable cause) { + super(cause); + } + + public BytecodeParserError(String msg, Object... args) { + super(msg, args); + } + } + + private final GraphBuilderPhase.Instance graphBuilderInstance; + protected final StructuredGraph graph; + + private BciBlockMapping blockMap; + private LocalLiveness liveness; + protected final int entryBCI; + private final BytecodeParser parent; + + private LineNumberTable lnt; + private int previousLineNumber; + private int currentLineNumber; + + private ValueNode methodSynchronizedObject; + + private ValueNode returnValue; + private FixedWithNextNode beforeReturnNode; + private ValueNode unwindValue; + private FixedWithNextNode beforeUnwindNode; + + protected FixedWithNextNode lastInstr; // the last instruction added + private boolean controlFlowSplit; + private final InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(this); + + private FixedWithNextNode[] firstInstructionArray; + private FrameStateBuilder[] entryStateArray; + + private int lastBCI; // BCI of lastInstr. This field is for resolving instrumentation target. + + private boolean finalBarrierRequired; + private ValueNode originalReceiver; + + protected BytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, + int entryBCI, IntrinsicContext intrinsicContext) { + this.bytecodeProvider = intrinsicContext == null ? new ResolvedJavaMethodBytecodeProvider() : intrinsicContext.getBytecodeProvider(); + this.code = bytecodeProvider.getBytecode(method); + this.method = code.getMethod(); + this.graphBuilderInstance = graphBuilderInstance; + this.graph = graph; + this.graphBuilderConfig = graphBuilderInstance.graphBuilderConfig; + this.optimisticOpts = graphBuilderInstance.optimisticOpts; + this.metaAccess = graphBuilderInstance.metaAccess; + this.stampProvider = graphBuilderInstance.stampProvider; + this.constantReflection = graphBuilderInstance.constantReflection; + this.constantFieldProvider = graphBuilderInstance.constantFieldProvider; + this.stream = new BytecodeStream(code.getCode()); + this.profilingInfo = graph.useProfilingInfo() ? code.getProfilingInfo() : null; + this.constantPool = code.getConstantPool(); + this.intrinsicContext = intrinsicContext; + this.entryBCI = entryBCI; + this.parent = parent; + this.lastBCI = -1; + + assert code.getCode() != null : "method must contain bytecodes: " + method; + + if (graphBuilderConfig.insertFullInfopoints() && !parsingIntrinsic()) { + lnt = code.getLineNumberTable(); + previousLineNumber = -1; + } + } + + protected GraphBuilderPhase.Instance getGraphBuilderInstance() { + return graphBuilderInstance; + } + + public ValueNode getReturnValue() { + return returnValue; + } + + public FixedWithNextNode getBeforeReturnNode() { + return this.beforeReturnNode; + } + + public ValueNode getUnwindValue() { + return unwindValue; + } + + public FixedWithNextNode getBeforeUnwindNode() { + return this.beforeUnwindNode; + } + + @SuppressWarnings("try") + protected void buildRootMethod() { + FrameStateBuilder startFrameState = new FrameStateBuilder(this, code, graph); + startFrameState.initializeForMethodStart(graph.getAssumptions(), graphBuilderConfig.eagerResolving() || intrinsicContext != null, graphBuilderConfig.getPlugins()); + + try (IntrinsicScope s = intrinsicContext != null ? new IntrinsicScope(this) : null) { + build(graph.start(), startFrameState); + } + + cleanupFinalGraph(); + ComputeLoopFrequenciesClosure.compute(graph); + } + + @SuppressWarnings("try") + protected void build(FixedWithNextNode startInstruction, FrameStateBuilder startFrameState) { + if (PrintProfilingInformation.getValue() && profilingInfo != null) { + TTY.println("Profiling info for " + method.format("%H.%n(%p)")); + TTY.println(Util.indent(profilingInfo.toString(method, CodeUtil.NEW_LINE), " ")); + } + + try (Indent indent = Debug.logAndIndent("build graph for %s", method)) { + if (bytecodeProvider.shouldRecordMethodDependencies()) { + assert getParent() != null || method.equals(graph.method()); + // Record method dependency in the graph + graph.recordMethod(method); + } + + // compute the block map, setup exception handlers and get the entrypoint(s) + BciBlockMapping newMapping = BciBlockMapping.create(stream, code); + this.blockMap = newMapping; + this.firstInstructionArray = new FixedWithNextNode[blockMap.getBlockCount()]; + this.entryStateArray = new FrameStateBuilder[blockMap.getBlockCount()]; + if (!method.isStatic()) { + originalReceiver = startFrameState.loadLocal(0, JavaKind.Object); + } + + /* + * Configure the assertion checking behavior of the FrameStateBuilder. This needs to be + * done only when assertions are enabled, so it is wrapped in an assertion itself. + */ + assert computeKindVerification(startFrameState); + + try (Scope s = Debug.scope("LivenessAnalysis")) { + int maxLocals = method.getMaxLocals(); + liveness = LocalLiveness.compute(stream, blockMap.getBlocks(), maxLocals, blockMap.getLoopCount()); + } catch (Throwable e) { + throw Debug.handle(e); + } + + lastInstr = startInstruction; + this.setCurrentFrameState(startFrameState); + stream.setBCI(0); + + BciBlock startBlock = blockMap.getStartBlock(); + if (this.parent == null) { + StartNode startNode = graph.start(); + if (method.isSynchronized()) { + assert !parsingIntrinsic(); + startNode.setStateAfter(createFrameState(BytecodeFrame.BEFORE_BCI, startNode)); + } else { + if (!parsingIntrinsic()) { + if (graph.method() != null && graph.method().isJavaLangObjectInit()) { + /* + * Don't clear the receiver when Object. is the compilation root. + * The receiver is needed as input to RegisterFinalizerNode. + */ + } else { + frameState.clearNonLiveLocals(startBlock, liveness, true); + } + assert bci() == 0; + startNode.setStateAfter(createFrameState(bci(), startNode)); + } else { + if (startNode.stateAfter() == null) { + FrameState stateAfterStart = createStateAfterStartOfReplacementGraph(); + startNode.setStateAfter(stateAfterStart); + } + } + } + } + + if (method.isSynchronized()) { + // add a monitor enter to the start block + methodSynchronizedObject = synchronizedObject(frameState, method); + frameState.clearNonLiveLocals(startBlock, liveness, true); + assert bci() == 0; + genMonitorEnter(methodSynchronizedObject, bci()); + } + + ProfilingPlugin profilingPlugin = this.graphBuilderConfig.getPlugins().getProfilingPlugin(); + if (profilingPlugin != null && profilingPlugin.shouldProfile(this, method)) { + FrameState stateBefore = frameState.create(bci(), getNonIntrinsicAncestor(), false, null, null); + profilingPlugin.profileInvoke(this, method, stateBefore); + } + + finishPrepare(lastInstr); + + genInfoPointNode(InfopointReason.METHOD_START, null); + + currentBlock = blockMap.getStartBlock(); + setEntryState(startBlock, frameState); + if (startBlock.isLoopHeader) { + appendGoto(startBlock); + } else { + setFirstInstruction(startBlock, lastInstr); + } + + BciBlock[] blocks = blockMap.getBlocks(); + for (BciBlock block : blocks) { + processBlock(block); + } + + if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL) && DumpDuringGraphBuilding.getValue() && this.beforeReturnNode != startInstruction) { + Debug.dump(Debug.INFO_LOG_LEVEL, graph, "Bytecodes parsed: %s.%s", method.getDeclaringClass().getUnqualifiedName(), method.getName()); + } + } + } + + private boolean computeKindVerification(FrameStateBuilder startFrameState) { + if (blockMap.hasJsrBytecodes) { + /* + * The JSR return address is an int value, but stored using the astore bytecode. Instead + * of weakening the kind assertion checking for all methods, we disable it completely + * for methods that contain a JSR bytecode. + */ + startFrameState.disableKindVerification(); + } + + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.canChangeStackKind(this)) { + /* + * We have a plugin that can change the kind of values, so no kind assertion + * checking is possible. + */ + startFrameState.disableKindVerification(); + } + } + return true; + } + + /** + * Hook for subclasses to modify the graph start instruction or append new instructions to it. + * + * @param startInstr the start instruction of the graph + */ + protected void finishPrepare(FixedWithNextNode startInstr) { + } + + protected void cleanupFinalGraph() { + GraphUtil.normalizeLoops(graph); + + // Remove dead parameters. + for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) { + if (param.hasNoUsages()) { + assert param.inputs().isEmpty(); + param.safeDelete(); + } + } + + // Remove redundant begin nodes. + for (BeginNode beginNode : graph.getNodes(BeginNode.TYPE)) { + Node predecessor = beginNode.predecessor(); + if (predecessor instanceof ControlSplitNode) { + // The begin node is necessary. + } else { + if (beginNode.hasUsages()) { + reanchorGuardedNodes(beginNode); + } + GraphUtil.unlinkFixedNode(beginNode); + beginNode.safeDelete(); + } + } + } + + /** + * Removes {@link GuardedNode}s from {@code beginNode}'s usages and re-attaches them to an + * appropriate preceeding {@link GuardingNode}. + */ + protected void reanchorGuardedNodes(BeginNode beginNode) { + // Find the new guarding node + GuardingNode guarding = null; + Node pred = beginNode.predecessor(); + while (pred != null) { + if (pred instanceof BeginNode) { + if (pred.predecessor() instanceof ControlSplitNode) { + guarding = (GuardingNode) pred; + break; + } + } else if (pred.getNodeClass().getAllowedUsageTypes().contains(InputType.Guard)) { + guarding = (GuardingNode) pred; + break; + } + pred = pred.predecessor(); + } + + // Reset the guard for all of beginNode's usages + for (Node usage : beginNode.usages().snapshot()) { + GuardedNode guarded = (GuardedNode) usage; + assert guarded.getGuard() == beginNode; + guarded.setGuard(guarding); + } + assert beginNode.hasNoUsages() : beginNode; + } + + /** + * Creates the frame state after the start node of a graph for an {@link IntrinsicContext + * intrinsic} that is the parse root (either for root compiling or for post-parse inlining). + */ + private FrameState createStateAfterStartOfReplacementGraph() { + assert parent == null; + assert frameState.getMethod().equals(intrinsicContext.getIntrinsicMethod()); + assert bci() == 0; + assert frameState.stackSize() == 0; + FrameState stateAfterStart; + if (intrinsicContext.isPostParseInlined()) { + stateAfterStart = graph.add(new FrameState(BytecodeFrame.BEFORE_BCI)); + } else { + ResolvedJavaMethod original = intrinsicContext.getOriginalMethod(); + ValueNode[] locals; + if (original.getMaxLocals() == frameState.localsSize() || original.isNative()) { + locals = new ValueNode[original.getMaxLocals()]; + for (int i = 0; i < locals.length; i++) { + ValueNode node = frameState.locals[i]; + if (node == FrameState.TWO_SLOT_MARKER) { + node = null; + } + locals[i] = node; + } + } else { + locals = new ValueNode[original.getMaxLocals()]; + int parameterCount = original.getSignature().getParameterCount(!original.isStatic()); + for (int i = 0; i < parameterCount; i++) { + ValueNode param = frameState.locals[i]; + if (param == FrameState.TWO_SLOT_MARKER) { + param = null; + } + locals[i] = param; + assert param == null || param instanceof ParameterNode || param.isConstant(); + } + } + ValueNode[] stack = {}; + int stackSize = 0; + ValueNode[] locks = {}; + List monitorIds = Collections.emptyList(); + stateAfterStart = graph.add(new FrameState(null, new ResolvedJavaMethodBytecode(original), 0, locals, stack, stackSize, locks, monitorIds, false, false)); + } + return stateAfterStart; + } + + /** + * @param type the unresolved type of the constant + */ + protected void handleUnresolvedLoadConstant(JavaType type) { + assert !graphBuilderConfig.eagerResolving(); + append(new DeoptimizeNode(InvalidateRecompile, Unresolved)); + } + + /** + * @param type the unresolved type of the type check + * @param object the object value whose type is being checked against {@code type} + */ + protected void handleUnresolvedCheckCast(JavaType type, ValueNode object) { + assert !graphBuilderConfig.eagerResolving(); + append(new FixedGuardNode(graph.unique(IsNullNode.create(object)), Unresolved, InvalidateRecompile)); + frameState.push(JavaKind.Object, appendConstant(JavaConstant.NULL_POINTER)); + } + + /** + * @param type the unresolved type of the type check + * @param object the object value whose type is being checked against {@code type} + */ + protected void handleUnresolvedInstanceOf(JavaType type, ValueNode object) { + assert !graphBuilderConfig.eagerResolving(); + AbstractBeginNode successor = graph.add(new BeginNode()); + DeoptimizeNode deopt = graph.add(new DeoptimizeNode(InvalidateRecompile, Unresolved)); + append(new IfNode(graph.unique(IsNullNode.create(object)), successor, deopt, 1)); + lastInstr = successor; + frameState.push(JavaKind.Int, appendConstant(JavaConstant.INT_0)); + } + + /** + * @param type the type being instantiated + */ + protected void handleUnresolvedNewInstance(JavaType type) { + assert !graphBuilderConfig.eagerResolving(); + append(new DeoptimizeNode(InvalidateRecompile, Unresolved)); + } + + /** + * @param type the type of the array being instantiated + * @param length the length of the array + */ + protected void handleUnresolvedNewObjectArray(JavaType type, ValueNode length) { + assert !graphBuilderConfig.eagerResolving(); + append(new DeoptimizeNode(InvalidateRecompile, Unresolved)); + } + + /** + * @param type the type being instantiated + * @param dims the dimensions for the multi-array + */ + protected void handleUnresolvedNewMultiArray(JavaType type, ValueNode[] dims) { + assert !graphBuilderConfig.eagerResolving(); + append(new DeoptimizeNode(InvalidateRecompile, Unresolved)); + } + + /** + * @param field the unresolved field + * @param receiver the object containing the field or {@code null} if {@code field} is static + */ + protected void handleUnresolvedLoadField(JavaField field, ValueNode receiver) { + assert !graphBuilderConfig.eagerResolving(); + append(new DeoptimizeNode(InvalidateRecompile, Unresolved)); + } + + /** + * @param field the unresolved field + * @param value the value being stored to the field + * @param receiver the object containing the field or {@code null} if {@code field} is static + */ + protected void handleUnresolvedStoreField(JavaField field, ValueNode value, ValueNode receiver) { + assert !graphBuilderConfig.eagerResolving(); + append(new DeoptimizeNode(InvalidateRecompile, Unresolved)); + } + + /** + * @param type + */ + protected void handleUnresolvedExceptionType(JavaType type) { + assert !graphBuilderConfig.eagerResolving(); + append(new DeoptimizeNode(InvalidateRecompile, Unresolved)); + } + + /** + * @param javaMethod + * @param invokeKind + */ + protected void handleUnresolvedInvoke(JavaMethod javaMethod, InvokeKind invokeKind) { + assert !graphBuilderConfig.eagerResolving(); + append(new DeoptimizeNode(InvalidateRecompile, Unresolved)); + } + + private AbstractBeginNode handleException(ValueNode exceptionObject, int bci) { + assert bci == BytecodeFrame.BEFORE_BCI || bci == bci() : "invalid bci"; + Debug.log("Creating exception dispatch edges at %d, exception object=%s, exception seen=%s", bci, exceptionObject, (profilingInfo == null ? "" : profilingInfo.getExceptionSeen(bci))); + + FrameStateBuilder dispatchState = frameState.copy(); + dispatchState.clearStack(); + + AbstractBeginNode dispatchBegin; + if (exceptionObject == null) { + ExceptionObjectNode newExceptionObject = graph.add(new ExceptionObjectNode(metaAccess)); + dispatchBegin = newExceptionObject; + dispatchState.push(JavaKind.Object, dispatchBegin); + dispatchState.setRethrowException(true); + newExceptionObject.setStateAfter(dispatchState.create(bci, newExceptionObject)); + } else { + dispatchBegin = graph.add(new BeginNode()); + dispatchState.push(JavaKind.Object, exceptionObject); + dispatchState.setRethrowException(true); + } + this.controlFlowSplit = true; + FixedWithNextNode finishedDispatch = finishInstruction(dispatchBegin, dispatchState); + + createHandleExceptionTarget(finishedDispatch, bci, dispatchState); + + return dispatchBegin; + } + + protected void createHandleExceptionTarget(FixedWithNextNode finishedDispatch, int bci, FrameStateBuilder dispatchState) { + BciBlock dispatchBlock = currentBlock.exceptionDispatchBlock(); + /* + * The exception dispatch block is always for the last bytecode of a block, so if we are not + * at the endBci yet, there is no exception handler for this bci and we can unwind + * immediately. + */ + if (bci != currentBlock.endBci || dispatchBlock == null) { + dispatchBlock = blockMap.getUnwindBlock(); + } + + FixedNode target = createTarget(dispatchBlock, dispatchState); + finishedDispatch.setNext(target); + } + + protected ValueNode genLoadIndexed(ValueNode array, ValueNode index, JavaKind kind) { + return LoadIndexedNode.create(graph.getAssumptions(), array, index, kind, metaAccess, constantReflection); + } + + protected void genStoreIndexed(ValueNode array, ValueNode index, JavaKind kind, ValueNode value) { + add(new StoreIndexedNode(array, index, kind, value)); + } + + protected ValueNode genIntegerAdd(ValueNode x, ValueNode y) { + return AddNode.create(x, y); + } + + protected ValueNode genIntegerSub(ValueNode x, ValueNode y) { + return SubNode.create(x, y); + } + + protected ValueNode genIntegerMul(ValueNode x, ValueNode y) { + return MulNode.create(x, y); + } + + protected ValueNode genFloatAdd(ValueNode x, ValueNode y) { + return AddNode.create(x, y); + } + + protected ValueNode genFloatSub(ValueNode x, ValueNode y) { + return SubNode.create(x, y); + } + + protected ValueNode genFloatMul(ValueNode x, ValueNode y) { + return MulNode.create(x, y); + } + + protected ValueNode genFloatDiv(ValueNode x, ValueNode y) { + return DivNode.create(x, y); + } + + protected ValueNode genFloatRem(ValueNode x, ValueNode y) { + return new RemNode(x, y); + } + + protected ValueNode genIntegerDiv(ValueNode x, ValueNode y) { + return new SignedDivNode(x, y); + } + + protected ValueNode genIntegerRem(ValueNode x, ValueNode y) { + return new SignedRemNode(x, y); + } + + protected ValueNode genNegateOp(ValueNode x) { + return (new NegateNode(x)); + } + + protected ValueNode genLeftShift(ValueNode x, ValueNode y) { + return new LeftShiftNode(x, y); + } + + protected ValueNode genRightShift(ValueNode x, ValueNode y) { + return new RightShiftNode(x, y); + } + + protected ValueNode genUnsignedRightShift(ValueNode x, ValueNode y) { + return new UnsignedRightShiftNode(x, y); + } + + protected ValueNode genAnd(ValueNode x, ValueNode y) { + return AndNode.create(x, y); + } + + protected ValueNode genOr(ValueNode x, ValueNode y) { + return OrNode.create(x, y); + } + + protected ValueNode genXor(ValueNode x, ValueNode y) { + return XorNode.create(x, y); + } + + protected ValueNode genNormalizeCompare(ValueNode x, ValueNode y, boolean isUnorderedLess) { + return NormalizeCompareNode.create(x, y, isUnorderedLess, constantReflection); + } + + protected ValueNode genFloatConvert(FloatConvert op, ValueNode input) { + return FloatConvertNode.create(op, input); + } + + protected ValueNode genNarrow(ValueNode input, int bitCount) { + return NarrowNode.create(input, bitCount); + } + + protected ValueNode genSignExtend(ValueNode input, int bitCount) { + return SignExtendNode.create(input, bitCount); + } + + protected ValueNode genZeroExtend(ValueNode input, int bitCount) { + return ZeroExtendNode.create(input, bitCount); + } + + protected void genGoto() { + ProfilingPlugin profilingPlugin = this.graphBuilderConfig.getPlugins().getProfilingPlugin(); + if (profilingPlugin != null && profilingPlugin.shouldProfile(this, method)) { + FrameState stateBefore = frameState.create(bci(), getNonIntrinsicAncestor(), false, null, null); + int targetBci = currentBlock.getSuccessor(0).startBci; + profilingPlugin.profileGoto(this, method, bci(), targetBci, stateBefore); + } + appendGoto(currentBlock.getSuccessor(0)); + assert currentBlock.numNormalSuccessors() == 1; + } + + protected LogicNode genObjectEquals(ValueNode x, ValueNode y) { + return ObjectEqualsNode.create(x, y, constantReflection); + } + + protected LogicNode genIntegerEquals(ValueNode x, ValueNode y) { + return IntegerEqualsNode.create(x, y, constantReflection); + } + + protected LogicNode genIntegerLessThan(ValueNode x, ValueNode y) { + return IntegerLessThanNode.create(x, y, constantReflection); + } + + protected ValueNode genUnique(ValueNode x) { + return graph.addOrUniqueWithInputs(x); + } + + protected LogicNode genUnique(LogicNode x) { + return graph.addOrUniqueWithInputs(x); + } + + protected ValueNode genIfNode(LogicNode condition, FixedNode falseSuccessor, FixedNode trueSuccessor, double d) { + return new IfNode(condition, falseSuccessor, trueSuccessor, d); + } + + protected void genThrow() { + genInfoPointNode(InfopointReason.BYTECODE_POSITION, null); + + ValueNode exception = frameState.pop(JavaKind.Object); + FixedGuardNode nullCheck = append(new FixedGuardNode(graph.unique(IsNullNode.create(exception)), NullCheckException, InvalidateReprofile, true)); + PiNode nonNullException = graph.unique(new PiNode(exception, exception.stamp().join(objectNonNull()), nullCheck)); + lastInstr.setNext(handleException(nonNullException, bci())); + } + + protected LogicNode createInstanceOf(TypeReference type, ValueNode object) { + return InstanceOfNode.create(type, object); + } + + protected AnchoringNode createAnchor(JavaTypeProfile profile) { + if (profile == null || profile.getNotRecordedProbability() > 0.0) { + return null; + } else { + return append(new ValueAnchorNode(null)); + } + } + + protected LogicNode createInstanceOf(TypeReference type, ValueNode object, JavaTypeProfile profile) { + return InstanceOfNode.create(type, object, profile, createAnchor(profile)); + } + + protected LogicNode createInstanceOfAllowNull(TypeReference type, ValueNode object, JavaTypeProfile profile) { + return InstanceOfNode.createAllowNull(type, object, profile, createAnchor(profile)); + } + + protected ValueNode genConditional(ValueNode x) { + return new ConditionalNode((LogicNode) x); + } + + protected NewInstanceNode createNewInstance(ResolvedJavaType type, boolean fillContents) { + return new NewInstanceNode(type, fillContents); + } + + protected NewArrayNode createNewArray(ResolvedJavaType elementType, ValueNode length, boolean fillContents) { + return new NewArrayNode(elementType, length, fillContents); + } + + protected NewMultiArrayNode createNewMultiArray(ResolvedJavaType type, ValueNode[] dimensions) { + return new NewMultiArrayNode(type, dimensions); + } + + protected ValueNode genLoadField(ValueNode receiver, ResolvedJavaField field) { + StampPair stamp = graphBuilderConfig.getPlugins().getOverridingStamp(this, field.getType(), false); + if (stamp == null) { + return LoadFieldNode.create(this.graph.getAssumptions(), receiver, field); + } else { + return LoadFieldNode.createOverrideStamp(stamp, receiver, field); + } + } + + protected ValueNode emitExplicitNullCheck(ValueNode receiver) { + if (StampTool.isPointerNonNull(receiver.stamp())) { + return receiver; + } + BytecodeExceptionNode exception = graph.add(new BytecodeExceptionNode(metaAccess, NullPointerException.class)); + AbstractBeginNode falseSucc = graph.add(new BeginNode()); + PiNode nonNullReceiver = graph.unique(new PiNode(receiver, receiver.stamp().join(objectNonNull()), falseSucc)); + append(new IfNode(graph.unique(IsNullNode.create(receiver)), exception, falseSucc, 0.01)); + lastInstr = falseSucc; + + exception.setStateAfter(createFrameState(bci(), exception)); + exception.setNext(handleException(exception, bci())); + return nonNullReceiver; + } + + protected void emitExplicitBoundsCheck(ValueNode index, ValueNode length) { + AbstractBeginNode trueSucc = graph.add(new BeginNode()); + BytecodeExceptionNode exception = graph.add(new BytecodeExceptionNode(metaAccess, ArrayIndexOutOfBoundsException.class, index)); + append(new IfNode(graph.unique(IntegerBelowNode.create(index, length, constantReflection)), trueSucc, exception, 0.99)); + lastInstr = trueSucc; + + exception.setStateAfter(createFrameState(bci(), exception)); + exception.setNext(handleException(exception, bci())); + } + + protected ValueNode genArrayLength(ValueNode x) { + return ArrayLengthNode.create(x, constantReflection); + } + + protected void genStoreField(ValueNode receiver, ResolvedJavaField field, ValueNode value) { + StoreFieldNode storeFieldNode = new StoreFieldNode(receiver, field, value); + append(storeFieldNode); + storeFieldNode.setStateAfter(this.createFrameState(stream.nextBCI(), storeFieldNode)); + } + + /** + * Ensure that concrete classes are at least linked before generating an invoke. Interfaces may + * never be linked so simply return true for them. + * + * @param target + * @return true if the declared holder is an interface or is linked + */ + private static boolean callTargetIsResolved(JavaMethod target) { + if (target instanceof ResolvedJavaMethod) { + ResolvedJavaMethod resolvedTarget = (ResolvedJavaMethod) target; + ResolvedJavaType resolvedType = resolvedTarget.getDeclaringClass(); + return resolvedType.isInterface() || resolvedType.isLinked(); + } + return false; + } + + protected void genInvokeStatic(JavaMethod target) { + if (callTargetIsResolved(target)) { + ResolvedJavaMethod resolvedTarget = (ResolvedJavaMethod) target; + ResolvedJavaType holder = resolvedTarget.getDeclaringClass(); + if (!holder.isInitialized() && ResolveClassBeforeStaticInvoke.getValue()) { + handleUnresolvedInvoke(target, InvokeKind.Static); + } else { + ValueNode classInit = null; + ClassInitializationPlugin classInitializationPlugin = graphBuilderConfig.getPlugins().getClassInitializationPlugin(); + if (classInitializationPlugin != null && classInitializationPlugin.shouldApply(this, resolvedTarget.getDeclaringClass())) { + FrameState stateBefore = frameState.create(bci(), getNonIntrinsicAncestor(), false, null, null); + classInit = classInitializationPlugin.apply(this, resolvedTarget.getDeclaringClass(), stateBefore); + } + + ValueNode[] args = frameState.popArguments(resolvedTarget.getSignature().getParameterCount(false)); + Invoke invoke = appendInvoke(InvokeKind.Static, resolvedTarget, args); + if (invoke != null) { + invoke.setClassInit(classInit); + } + } + } else { + handleUnresolvedInvoke(target, InvokeKind.Static); + } + } + + protected void genInvokeInterface(JavaMethod target) { + if (callTargetIsResolved(target)) { + ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(true)); + appendInvoke(InvokeKind.Interface, (ResolvedJavaMethod) target, args); + } else { + handleUnresolvedInvoke(target, InvokeKind.Interface); + } + } + + protected void genInvokeDynamic(JavaMethod target) { + if (target instanceof ResolvedJavaMethod) { + JavaConstant appendix = constantPool.lookupAppendix(stream.readCPI4(), Bytecodes.INVOKEDYNAMIC); + if (appendix != null) { + frameState.push(JavaKind.Object, ConstantNode.forConstant(appendix, metaAccess, graph)); + } + ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(false)); + appendInvoke(InvokeKind.Static, (ResolvedJavaMethod) target, args); + } else { + handleUnresolvedInvoke(target, InvokeKind.Static); + } + } + + protected void genInvokeVirtual(JavaMethod target) { + if (callTargetIsResolved(target)) { + /* + * Special handling for runtimes that rewrite an invocation of MethodHandle.invoke(...) + * or MethodHandle.invokeExact(...) to a static adapter. HotSpot does this - see + * https://wikis.oracle.com/display/HotSpotInternals/Method+handles +and+invokedynamic + */ + boolean hasReceiver = !((ResolvedJavaMethod) target).isStatic(); + JavaConstant appendix = constantPool.lookupAppendix(stream.readCPI(), Bytecodes.INVOKEVIRTUAL); + if (appendix != null) { + frameState.push(JavaKind.Object, ConstantNode.forConstant(appendix, metaAccess, graph)); + } + ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(hasReceiver)); + if (hasReceiver) { + appendInvoke(InvokeKind.Virtual, (ResolvedJavaMethod) target, args); + } else { + appendInvoke(InvokeKind.Static, (ResolvedJavaMethod) target, args); + } + } else { + handleUnresolvedInvoke(target, InvokeKind.Virtual); + } + + } + + protected void genInvokeSpecial(JavaMethod target) { + if (callTargetIsResolved(target)) { + assert target != null; + assert target.getSignature() != null; + ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(true)); + appendInvoke(InvokeKind.Special, (ResolvedJavaMethod) target, args); + } else { + handleUnresolvedInvoke(target, InvokeKind.Special); + } + } + + private InvokeKind currentInvokeKind; + private JavaType currentInvokeReturnType; + protected FrameStateBuilder frameState; + protected BciBlock currentBlock; + protected final BytecodeStream stream; + protected final GraphBuilderConfiguration graphBuilderConfig; + protected final ResolvedJavaMethod method; + protected final Bytecode code; + protected final BytecodeProvider bytecodeProvider; + protected final ProfilingInfo profilingInfo; + protected final OptimisticOptimizations optimisticOpts; + protected final ConstantPool constantPool; + protected final MetaAccessProvider metaAccess; + private final ConstantReflectionProvider constantReflection; + private final ConstantFieldProvider constantFieldProvider; + private final StampProvider stampProvider; + protected final IntrinsicContext intrinsicContext; + + @Override + public InvokeKind getInvokeKind() { + return currentInvokeKind; + } + + @Override + public JavaType getInvokeReturnType() { + return currentInvokeReturnType; + } + + private boolean forceInliningEverything; + + @Override + public void handleReplacedInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, boolean inlineEverything) { + boolean previous = forceInliningEverything; + forceInliningEverything = previous || inlineEverything; + try { + appendInvoke(invokeKind, targetMethod, args); + } finally { + forceInliningEverything = previous; + } + } + + private Invoke appendInvoke(InvokeKind initialInvokeKind, ResolvedJavaMethod initialTargetMethod, ValueNode[] args) { + ResolvedJavaMethod targetMethod = initialTargetMethod; + InvokeKind invokeKind = initialInvokeKind; + if (initialInvokeKind.isIndirect()) { + ResolvedJavaType contextType = this.frameState.getMethod().getDeclaringClass(); + ResolvedJavaMethod specialCallTarget = MethodCallTargetNode.findSpecialCallTarget(initialInvokeKind, args[0], initialTargetMethod, contextType); + if (specialCallTarget != null) { + invokeKind = InvokeKind.Special; + targetMethod = specialCallTarget; + } + } + + JavaKind resultType = targetMethod.getSignature().getReturnKind(); + if (DeoptALot.getValue()) { + append(new DeoptimizeNode(DeoptimizationAction.None, RuntimeConstraint)); + frameState.pushReturn(resultType, ConstantNode.defaultForKind(resultType, graph)); + return null; + } + + JavaType returnType = targetMethod.getSignature().getReturnType(method.getDeclaringClass()); + if (graphBuilderConfig.eagerResolving() || parsingIntrinsic()) { + returnType = returnType.resolve(targetMethod.getDeclaringClass()); + } + if (invokeKind.hasReceiver()) { + args[0] = emitExplicitExceptions(args[0], null); + + if (args[0].isNullConstant()) { + append(new DeoptimizeNode(InvalidateRecompile, NullCheckException)); + return null; + } + } + + InlineInfo inlineInfo = null; + try { + currentInvokeReturnType = returnType; + currentInvokeKind = invokeKind; + if (tryNodePluginForInvocation(args, targetMethod)) { + if (TraceParserPlugins.getValue()) { + traceWithContext("used node plugin for %s", targetMethod.format("%h.%n(%p)")); + } + return null; + } + + if (!invokeKind.isIndirect() || (UseGuardedIntrinsics.getValue() && !GeneratePIC.getValue())) { + if (tryInvocationPlugin(invokeKind, args, targetMethod, resultType, returnType)) { + if (TraceParserPlugins.getValue()) { + traceWithContext("used invocation plugin for %s", targetMethod.format("%h.%n(%p)")); + } + return null; + } + } + if (invokeKind.isDirect()) { + + inlineInfo = tryInline(args, targetMethod); + if (inlineInfo == SUCCESSFULLY_INLINED) { + return null; + } + } + } finally { + currentInvokeReturnType = null; + currentInvokeKind = null; + } + + JavaTypeProfile profile = null; + if (invokeKind.isIndirect() && profilingInfo != null && this.optimisticOpts.useTypeCheckHints()) { + profile = profilingInfo.getTypeProfile(bci()); + } + return createNonInlinedInvoke(args, targetMethod, invokeKind, resultType, returnType, inlineInfo, profile); + } + + protected Invoke createNonInlinedInvoke(ValueNode[] args, ResolvedJavaMethod targetMethod, InvokeKind invokeKind, + JavaKind resultType, JavaType returnType, InlineInfo inlineInfo, JavaTypeProfile profile) { + + StampPair returnStamp = graphBuilderConfig.getPlugins().getOverridingStamp(this, returnType, false); + if (returnStamp == null) { + returnStamp = StampFactory.forDeclaredType(graph.getAssumptions(), returnType, false); + } + + MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, targetMethod, args, returnStamp, profile)); + + Invoke invoke; + if (omitInvokeExceptionEdge(callTarget, inlineInfo)) { + invoke = createInvoke(callTarget, resultType); + } else { + invoke = createInvokeWithException(callTarget, resultType); + AbstractBeginNode beginNode = graph.add(new KillingBeginNode(LocationIdentity.any())); + invoke.setNext(beginNode); + lastInstr = beginNode; + } + + for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) { + plugin.notifyNotInlined(this, targetMethod, invoke); + } + + return invoke; + } + + /** + * If the method returns true, the invocation of the given {@link MethodCallTargetNode call + * target} does not need an exception edge. + * + * @param callTarget The call target. + */ + protected boolean omitInvokeExceptionEdge(MethodCallTargetNode callTarget, InlineInfo lastInlineInfo) { + if (lastInlineInfo == InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION) { + return false; + } else if (lastInlineInfo == InlineInfo.DO_NOT_INLINE_NO_EXCEPTION) { + return true; + } else if (graphBuilderConfig.getBytecodeExceptionMode() == BytecodeExceptionMode.CheckAll) { + return false; + } else if (graphBuilderConfig.getBytecodeExceptionMode() == BytecodeExceptionMode.OmitAll) { + return true; + } else { + assert graphBuilderConfig.getBytecodeExceptionMode() == BytecodeExceptionMode.Profile; + // be conservative if information was not recorded (could result in endless + // recompiles otherwise) + return (!StressInvokeWithExceptionNode.getValue() && optimisticOpts.useExceptionProbability() && profilingInfo != null && profilingInfo.getExceptionSeen(bci()) == TriState.FALSE); + } + } + + /** + * Contains all the assertion checking logic around the application of an + * {@link InvocationPlugin}. This class is only loaded when assertions are enabled. + */ + class InvocationPluginAssertions { + final InvocationPlugin plugin; + final ValueNode[] args; + final ResolvedJavaMethod targetMethod; + final JavaKind resultType; + final int beforeStackSize; + final boolean needsNullCheck; + final int nodeCount; + final Mark mark; + + InvocationPluginAssertions(InvocationPlugin plugin, ValueNode[] args, ResolvedJavaMethod targetMethod, JavaKind resultType) { + guarantee(assertionsEnabled(), "%s should only be loaded and instantiated if assertions are enabled", getClass().getSimpleName()); + this.plugin = plugin; + this.targetMethod = targetMethod; + this.args = args; + this.resultType = resultType; + this.beforeStackSize = frameState.stackSize(); + this.needsNullCheck = !targetMethod.isStatic() && args[0].getStackKind() == JavaKind.Object && !StampTool.isPointerNonNull(args[0].stamp()); + this.nodeCount = graph.getNodeCount(); + this.mark = graph.getMark(); + } + + String error(String format, Object... a) { + return String.format(format, a) + String.format("%n\tplugin at %s", plugin.getApplySourceLocation(metaAccess)); + } + + boolean check(boolean pluginResult) { + if (pluginResult == true) { + int expectedStackSize = beforeStackSize + resultType.getSlotCount(); + assert expectedStackSize == frameState.stackSize() : error("plugin manipulated the stack incorrectly: expected=%d, actual=%d", expectedStackSize, frameState.stackSize()); + NodeIterable newNodes = graph.getNewNodes(mark); + assert !needsNullCheck || isPointerNonNull(args[0].stamp()) : error("plugin needs to null check the receiver of %s: receiver=%s", targetMethod.format("%H.%n(%p)"), args[0]); + for (Node n : newNodes) { + if (n instanceof StateSplit) { + StateSplit stateSplit = (StateSplit) n; + assert stateSplit.stateAfter() != null || !stateSplit.hasSideEffect() : error("%s node added by plugin for %s need to have a non-null frame state: %s", + StateSplit.class.getSimpleName(), targetMethod.format("%H.%n(%p)"), stateSplit); + } + } + try { + graphBuilderConfig.getPlugins().getInvocationPlugins().checkNewNodes(BytecodeParser.this, plugin, newNodes); + } catch (Throwable t) { + throw new AssertionError(error("Error in plugin"), t); + } + } else { + assert nodeCount == graph.getNodeCount() : error("plugin that returns false must not create new nodes"); + assert beforeStackSize == frameState.stackSize() : error("plugin that returns false must not modify the stack"); + } + return true; + } + } + + protected static class IntrinsicGuard { + final FixedWithNextNode lastInstr; + final Mark mark; + final AbstractBeginNode nonIntrinsicBranch; + final ValueNode receiver; + final JavaTypeProfile profile; + + public IntrinsicGuard(FixedWithNextNode lastInstr, ValueNode receiver, Mark mark, AbstractBeginNode nonIntrinsicBranch, JavaTypeProfile profile) { + this.lastInstr = lastInstr; + this.receiver = receiver; + this.mark = mark; + this.nonIntrinsicBranch = nonIntrinsicBranch; + this.profile = profile; + } + } + + /** + * Weaves a test of the receiver type to ensure the dispatch will select {@code targetMethod} + * and not another method that overrides it. This should only be called if there is an intrinsic + * (i.e., an {@link InvocationPlugin}) for {@code targetMethod} and the invocation is indirect. + * + * The control flow woven around the intrinsic is as follows: + * + *
+     *  if (LoadMethod(LoadHub(receiver)) == targetMethod) {
+     *       
+     *  } else {
+     *       
+     *  }
+     * 
+ * + * The {@code else} branch is woven by {@link #afterInvocationPluginExecution}. + * + * @return {@code null} if the intrinsic cannot be used otherwise an object to be used by + * {@link #afterInvocationPluginExecution} to weave code for the non-intrinsic branch + */ + protected IntrinsicGuard guardIntrinsic(ValueNode[] args, ResolvedJavaMethod targetMethod, InvocationPluginReceiver pluginReceiver) { + ValueNode intrinsicReceiver = args[0]; + ResolvedJavaType receiverType = StampTool.typeOrNull(intrinsicReceiver); + if (receiverType == null) { + // The verifier guarantees it to be at least type declaring targetMethod + receiverType = targetMethod.getDeclaringClass(); + } + ResolvedJavaMethod resolvedMethod = receiverType.resolveMethod(targetMethod, method.getDeclaringClass()); + if (resolvedMethod == null || resolvedMethod == targetMethod) { + assert resolvedMethod == null || targetMethod.getDeclaringClass().isAssignableFrom(resolvedMethod.getDeclaringClass()); + Mark mark = graph.getMark(); + FixedWithNextNode currentLastInstr = lastInstr; + ValueNode nonNullReceiver = pluginReceiver.get(); + Stamp methodStamp = stampProvider.createMethodStamp(); + LoadHubNode hub = graph.unique(new LoadHubNode(stampProvider, nonNullReceiver)); + LoadMethodNode actual = append(new LoadMethodNode(methodStamp, targetMethod, receiverType, method.getDeclaringClass(), hub)); + ConstantNode expected = graph.unique(ConstantNode.forConstant(methodStamp, targetMethod.getEncoding(), getMetaAccess())); + LogicNode compare = graph.unique(CompareNode.createCompareNode(Condition.EQ, actual, expected, constantReflection)); + + JavaTypeProfile profile = null; + if (profilingInfo != null && this.optimisticOpts.useTypeCheckHints()) { + profile = profilingInfo.getTypeProfile(bci()); + if (profile != null) { + JavaTypeProfile newProfile = adjustProfileForInvocationPlugin(profile, targetMethod); + if (newProfile != profile) { + if (newProfile.getTypes().length == 0) { + // All profiled types select the intrinsic so + // emit a fixed guard instead of a if-then-else. + lastInstr = append(new FixedGuardNode(compare, TypeCheckedInliningViolated, InvalidateReprofile, false)); + return new IntrinsicGuard(currentLastInstr, intrinsicReceiver, mark, null, null); + } + } else { + // No profiled types select the intrinsic so emit a virtual call + return null; + } + profile = newProfile; + } + } + + AbstractBeginNode intrinsicBranch = graph.add(new BeginNode()); + AbstractBeginNode nonIntrinsicBranch = graph.add(new BeginNode()); + append(new IfNode(compare, intrinsicBranch, nonIntrinsicBranch, 0.01)); + lastInstr = intrinsicBranch; + return new IntrinsicGuard(currentLastInstr, intrinsicReceiver, mark, nonIntrinsicBranch, profile); + } else { + // Receiver selects an overriding method so emit a virtual call + return null; + } + } + + /** + * Adjusts the profile for an indirect invocation of a virtual method for which there is an + * intrinsic. The adjustment made by this method is to remove all types from the profile that do + * not override {@code targetMethod}. + * + * @param profile the profile to adjust + * @param targetMethod the virtual method for which there is an intrinsic + * @return the adjusted profile or the original {@code profile} object if no adjustment was made + */ + protected JavaTypeProfile adjustProfileForInvocationPlugin(JavaTypeProfile profile, ResolvedJavaMethod targetMethod) { + if (profile.getTypes().length > 0) { + List retained = new ArrayList<>(); + double notRecordedProbability = profile.getNotRecordedProbability(); + for (ProfiledType ptype : profile.getTypes()) { + if (!ptype.getType().resolveMethod(targetMethod, method.getDeclaringClass()).equals(targetMethod)) { + retained.add(ptype); + } else { + notRecordedProbability += ptype.getProbability(); + } + } + if (!retained.isEmpty()) { + if (retained.size() != profile.getTypes().length) { + return new JavaTypeProfile(profile.getNullSeen(), notRecordedProbability, retained.toArray(new ProfiledType[retained.size()])); + } + } else { + return new JavaTypeProfile(profile.getNullSeen(), notRecordedProbability, new ProfiledType[0]); + } + } + return profile; + } + + /** + * Performs any action required after execution of an invocation plugin. This includes + * {@linkplain InvocationPluginAssertions#check(boolean) checking} invocation plugin invariants + * as well as weaving the {@code else} branch of the code woven by {@link #guardIntrinsic} if + * {@code guard != null}. + */ + protected void afterInvocationPluginExecution(boolean pluginResult, InvocationPluginAssertions assertions, IntrinsicGuard intrinsicGuard, + InvokeKind invokeKind, ValueNode[] args, ResolvedJavaMethod targetMethod, JavaKind resultType, JavaType returnType) { + assert assertions.check(pluginResult); + if (intrinsicGuard != null) { + if (pluginResult) { + if (intrinsicGuard.nonIntrinsicBranch != null) { + // Intrinsic emitted: emit a virtual call to the target method and + // merge it with the intrinsic branch + EndNode intrinsicEnd = append(new EndNode()); + + FrameStateBuilder intrinsicState = null; + FrameStateBuilder nonIntrinisicState = null; + if (resultType != JavaKind.Void) { + intrinsicState = frameState.copy(); + frameState.pop(resultType); + nonIntrinisicState = frameState; + } + + lastInstr = intrinsicGuard.nonIntrinsicBranch; + createNonInlinedInvoke(args, targetMethod, invokeKind, resultType, returnType, null, intrinsicGuard.profile); + + EndNode nonIntrinsicEnd = append(new EndNode()); + AbstractMergeNode mergeNode = graph.add(new MergeNode()); + + mergeNode.addForwardEnd(intrinsicEnd); + if (intrinsicState != null) { + intrinsicState.merge(mergeNode, nonIntrinisicState); + frameState = intrinsicState; + } + mergeNode.addForwardEnd(nonIntrinsicEnd); + mergeNode.setStateAfter(frameState.create(stream.nextBCI(), mergeNode)); + + lastInstr = mergeNode; + } + } else { + // Intrinsic was not applied: remove intrinsic guard + // and restore the original receiver node in the arguments array + for (Node node : graph.getNewNodes(intrinsicGuard.mark)) { + GraphUtil.killCFG(node); + } + lastInstr = intrinsicGuard.lastInstr; + args[0] = intrinsicGuard.receiver; + } + } + } + + protected boolean tryInvocationPlugin(InvokeKind invokeKind, ValueNode[] args, ResolvedJavaMethod targetMethod, JavaKind resultType, JavaType returnType) { + InvocationPlugin plugin = graphBuilderConfig.getPlugins().getInvocationPlugins().lookupInvocation(targetMethod); + if (plugin != null) { + + if (intrinsicContext != null && intrinsicContext.isCallToOriginal(targetMethod)) { + // Self recursive intrinsic means the original + // method should be called. + assert !targetMethod.hasBytecodes() : "TODO: when does this happen?"; + return false; + } + + InvocationPluginReceiver pluginReceiver = invocationPluginReceiver.init(targetMethod, args); + + IntrinsicGuard intrinsicGuard = null; + if (invokeKind.isIndirect()) { + intrinsicGuard = guardIntrinsic(args, targetMethod, pluginReceiver); + if (intrinsicGuard == null) { + return false; + } else if (intrinsicGuard.nonIntrinsicBranch == null) { + assert lastInstr instanceof FixedGuardNode; + } + } + + InvocationPluginAssertions assertions = assertionsEnabled() ? new InvocationPluginAssertions(plugin, args, targetMethod, resultType) : null; + if (plugin.execute(this, targetMethod, pluginReceiver, args)) { + afterInvocationPluginExecution(true, assertions, intrinsicGuard, invokeKind, args, targetMethod, resultType, returnType); + return true; + } else { + afterInvocationPluginExecution(false, assertions, intrinsicGuard, invokeKind, args, targetMethod, resultType, returnType); + } + } + return false; + } + + private boolean tryNodePluginForInvocation(ValueNode[] args, ResolvedJavaMethod targetMethod) { + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.handleInvoke(this, targetMethod, args)) { + return true; + } + } + return false; + } + + private static final InlineInfo SUCCESSFULLY_INLINED = InlineInfo.createStandardInlineInfo(null); + + /** + * Try to inline a method. If the method was inlined, returns {@link #SUCCESSFULLY_INLINED}. + * Otherwise, it returns the {@link InlineInfo} that lead to the decision to not inline it, or + * {@code null} if there is no {@link InlineInfo} for this method. + */ + private InlineInfo tryInline(ValueNode[] args, ResolvedJavaMethod targetMethod) { + boolean canBeInlined = forceInliningEverything || parsingIntrinsic() || targetMethod.canBeInlined(); + if (!canBeInlined) { + return null; + } + + if (forceInliningEverything) { + if (inline(targetMethod, targetMethod, null, args)) { + return SUCCESSFULLY_INLINED; + } else { + return null; + } + } + + for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) { + InlineInfo inlineInfo = plugin.shouldInlineInvoke(this, targetMethod, args); + if (inlineInfo != null) { + if (inlineInfo.getMethodToInline() != null) { + if (inline(targetMethod, inlineInfo.getMethodToInline(), inlineInfo.getIntrinsicBytecodeProvider(), args)) { + return SUCCESSFULLY_INLINED; + } + } + /* Do not inline, and do not ask the remaining plugins. */ + return inlineInfo; + } + } + return null; + } + + @Override + public boolean intrinsify(BytecodeProvider intrinsicBytecodeProvider, ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, InvocationPlugin.Receiver receiver, ValueNode[] args) { + if (receiver != null) { + receiver.get(); + } + boolean res = inline(targetMethod, substitute, intrinsicBytecodeProvider, args); + assert res : "failed to inline " + substitute; + return res; + } + + private boolean inline(ResolvedJavaMethod targetMethod, ResolvedJavaMethod inlinedMethod, BytecodeProvider intrinsicBytecodeProvider, ValueNode[] args) { + if (TraceInlineDuringParsing.getValue() || TraceParserPlugins.getValue()) { + if (targetMethod.equals(inlinedMethod)) { + traceWithContext("inlining call to %s", inlinedMethod.format("%h.%n(%p)")); + } else { + traceWithContext("inlining call to %s as intrinsic for %s", inlinedMethod.format("%h.%n(%p)"), targetMethod.format("%h.%n(%p)")); + } + } + IntrinsicContext intrinsic = this.intrinsicContext; + if (intrinsic != null && intrinsic.isCallToOriginal(targetMethod)) { + if (intrinsic.isCompilationRoot()) { + // A root compiled intrinsic needs to deoptimize + // if the slow path is taken. During frame state + // assignment, the deopt node will get its stateBefore + // from the start node of the intrinsic + append(new DeoptimizeNode(InvalidateRecompile, RuntimeConstraint)); + printInlining(targetMethod, inlinedMethod, true, "compilation root (bytecode parsing)"); + return true; + } else { + // Otherwise inline the original method. Any frame state created + // during the inlining will exclude frame(s) in the + // intrinsic method (see HIRFrameStateBuilder.create(int bci)). + if (intrinsic.getOriginalMethod().isNative()) { + printInlining(targetMethod, inlinedMethod, false, "native method (bytecode parsing)"); + return false; + } + printInlining(targetMethod, inlinedMethod, true, "inline intrinsic (bytecode parsing)"); + parseAndInlineCallee(intrinsic.getOriginalMethod(), args, null); + return true; + } + } else { + boolean isIntrinsic = intrinsicBytecodeProvider != null; + if (intrinsic == null && isIntrinsic) { + assert !inlinedMethod.equals(targetMethod); + intrinsic = new IntrinsicContext(targetMethod, inlinedMethod, intrinsicBytecodeProvider, INLINE_DURING_PARSING); + } + if (inlinedMethod.hasBytecodes()) { + for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) { + plugin.notifyBeforeInline(inlinedMethod); + } + printInlining(targetMethod, inlinedMethod, true, "inline method (bytecode parsing)"); + parseAndInlineCallee(inlinedMethod, args, intrinsic); + for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) { + plugin.notifyAfterInline(inlinedMethod); + } + } else { + printInlining(targetMethod, inlinedMethod, false, "no bytecodes (abstract or native) (bytecode parsing)"); + return false; + } + } + return true; + } + + private void printInlining(ResolvedJavaMethod targetMethod, ResolvedJavaMethod inlinedMethod, boolean success, String msg) { + if (GraalOptions.HotSpotPrintInlining.getValue()) { + if (targetMethod.equals(inlinedMethod)) { + Util.printInlining(inlinedMethod, bci(), getDepth(), success, "%s", msg); + } else { + Util.printInlining(inlinedMethod, bci(), getDepth(), success, "%s intrinsic for %s", msg, targetMethod.format("%h.%n(%p)")); + } + } + } + + /** + * Prints a line to {@link TTY} with a prefix indicating the current parse context. The prefix + * is of the form: + * + *
+     * {SPACE * n} {name of method being parsed} "(" {file name} ":" {line number} ")"
+     * 
+ * + * where {@code n} is the current inlining depth. + * + * @param format a format string + * @param args arguments to the format string + */ + + protected void traceWithContext(String format, Object... args) { + StackTraceElement where = code.asStackTraceElement(bci()); + TTY.println(format("%s%s (%s:%d) %s", nSpaces(getDepth()), method.isConstructor() ? method.format("%h.%n") : method.getName(), where.getFileName(), where.getLineNumber(), + format(format, args))); + } + + protected BytecodeParserError asParserError(Throwable e) { + if (e instanceof BytecodeParserError) { + return (BytecodeParserError) e; + } + BytecodeParser bp = this; + BytecodeParserError res = new BytecodeParserError(e); + while (bp != null) { + res.addContext("parsing " + bp.code.asStackTraceElement(bp.bci())); + bp = bp.parent; + } + return res; + } + + @SuppressWarnings("try") + protected void parseAndInlineCallee(ResolvedJavaMethod targetMethod, ValueNode[] args, IntrinsicContext calleeIntrinsicContext) { + try (IntrinsicScope s = calleeIntrinsicContext != null && !parsingIntrinsic() ? new IntrinsicScope(this, targetMethod.getSignature().toParameterKinds(!targetMethod.isStatic()), args) : null) { + + BytecodeParser parser = graphBuilderInstance.createBytecodeParser(graph, this, targetMethod, INVOCATION_ENTRY_BCI, calleeIntrinsicContext); + FrameStateBuilder startFrameState = new FrameStateBuilder(parser, parser.code, graph); + if (!targetMethod.isStatic()) { + args[0] = nullCheckedValue(args[0]); + } + startFrameState.initializeFromArgumentsArray(args); + parser.build(this.lastInstr, startFrameState); + + FixedWithNextNode calleeBeforeReturnNode = parser.getBeforeReturnNode(); + this.lastInstr = calleeBeforeReturnNode; + JavaKind calleeReturnKind = targetMethod.getSignature().getReturnKind(); + if (calleeBeforeReturnNode != null) { + ValueNode calleeReturnValue = parser.getReturnValue(); + if (calleeReturnValue != null) { + frameState.push(calleeReturnKind.getStackKind(), calleeReturnValue); + } + } + + FixedWithNextNode calleeBeforeUnwindNode = parser.getBeforeUnwindNode(); + if (calleeBeforeUnwindNode != null) { + ValueNode calleeUnwindValue = parser.getUnwindValue(); + assert calleeUnwindValue != null; + calleeBeforeUnwindNode.setNext(handleException(calleeUnwindValue, bci())); + } + } + } + + public MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, StampPair returnStamp, JavaTypeProfile profile) { + return new MethodCallTargetNode(invokeKind, targetMethod, args, returnStamp, profile); + } + + protected InvokeNode createInvoke(CallTargetNode callTarget, JavaKind resultType) { + InvokeNode invoke = append(new InvokeNode(callTarget, bci())); + frameState.pushReturn(resultType, invoke); + invoke.setStateAfter(createFrameState(stream.nextBCI(), invoke)); + return invoke; + } + + protected InvokeWithExceptionNode createInvokeWithException(CallTargetNode callTarget, JavaKind resultType) { + if (currentBlock != null && stream.nextBCI() > currentBlock.endBci) { + /* + * Clear non-live locals early so that the exception handler entry gets the cleared + * state. + */ + frameState.clearNonLiveLocals(currentBlock, liveness, false); + } + + AbstractBeginNode exceptionEdge = handleException(null, bci()); + InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionEdge, bci())); + frameState.pushReturn(resultType, invoke); + invoke.setStateAfter(createFrameState(stream.nextBCI(), invoke)); + return invoke; + } + + protected void genReturn(ValueNode returnVal, JavaKind returnKind) { + if (parsingIntrinsic() && returnVal != null) { + if (returnVal instanceof StateSplit) { + StateSplit stateSplit = (StateSplit) returnVal; + FrameState stateAfter = stateSplit.stateAfter(); + if (stateSplit.hasSideEffect()) { + assert stateSplit != null; + if (stateAfter.bci == BytecodeFrame.AFTER_BCI) { + assert stateAfter.usages().count() == 1; + assert stateAfter.usages().first() == stateSplit; + stateAfter.replaceAtUsages(graph.add(new FrameState(BytecodeFrame.AFTER_BCI, returnVal))); + GraphUtil.killWithUnusedFloatingInputs(stateAfter); + } else { + /* + * This must be the return value from within a partial intrinsification. + */ + assert !BytecodeFrame.isPlaceholderBci(stateAfter.bci); + } + } else { + assert stateAfter == null; + } + } + } + if (parent == null) { + frameState.setRethrowException(false); + frameState.clearStack(); + beforeReturn(returnVal, returnKind); + append(new ReturnNode(returnVal)); + } else { + if (blockMap.getReturnCount() == 1 || !controlFlowSplit) { + // There is only a single return. + beforeReturn(returnVal, returnKind); + this.returnValue = returnVal; + this.beforeReturnNode = this.lastInstr; + this.lastInstr = null; + } else { + frameState.setRethrowException(false); + frameState.clearStack(); + if (returnVal != null) { + frameState.push(returnKind, returnVal); + } + assert blockMap.getReturnCount() > 1; + appendGoto(blockMap.getReturnBlock()); + } + } + } + + private void beforeReturn(ValueNode x, JavaKind kind) { + if (graph.method() != null && graph.method().isJavaLangObjectInit()) { + /* + * Get the receiver from the initial state since bytecode rewriting could do arbitrary + * things to the state of the locals. + */ + ValueNode receiver = graph.start().stateAfter().localAt(0); + assert receiver != null && receiver.getStackKind() == JavaKind.Object; + if (RegisterFinalizerNode.mayHaveFinalizer(receiver, graph.getAssumptions())) { + append(new RegisterFinalizerNode(receiver)); + } + } + genInfoPointNode(InfopointReason.METHOD_END, x); + if (finalBarrierRequired) { + assert originalReceiver != null; + append(new FinalFieldBarrierNode(originalReceiver)); + } + synchronizedEpilogue(BytecodeFrame.AFTER_BCI, x, kind); + } + + protected MonitorEnterNode createMonitorEnterNode(ValueNode x, MonitorIdNode monitorId) { + return new MonitorEnterNode(x, monitorId); + } + + protected void genMonitorEnter(ValueNode x, int bci) { + MonitorIdNode monitorId = graph.add(new MonitorIdNode(frameState.lockDepth(true))); + MonitorEnterNode monitorEnter = append(createMonitorEnterNode(x, monitorId)); + frameState.pushLock(x, monitorId); + monitorEnter.setStateAfter(createFrameState(bci, monitorEnter)); + } + + protected void genMonitorExit(ValueNode x, ValueNode escapedReturnValue, int bci) { + if (frameState.lockDepth(false) == 0) { + throw bailout("unbalanced monitors: too many exits"); + } + MonitorIdNode monitorId = frameState.peekMonitorId(); + ValueNode lockedObject = frameState.popLock(); + if (GraphUtil.originalValue(lockedObject) != GraphUtil.originalValue(x)) { + throw bailout(String.format("unbalanced monitors: mismatch at monitorexit, %s != %s", GraphUtil.originalValue(x), GraphUtil.originalValue(lockedObject))); + } + MonitorExitNode monitorExit = append(new MonitorExitNode(x, monitorId, escapedReturnValue)); + monitorExit.setStateAfter(createFrameState(bci, monitorExit)); + } + + protected void genJsr(int dest) { + BciBlock successor = currentBlock.getJsrSuccessor(); + assert successor.startBci == dest : successor.startBci + " != " + dest + " @" + bci(); + JsrScope scope = currentBlock.getJsrScope(); + int nextBci = getStream().nextBCI(); + if (!successor.getJsrScope().pop().equals(scope)) { + throw new JsrNotSupportedBailout("unstructured control flow (internal limitation)"); + } + if (successor.getJsrScope().nextReturnAddress() != nextBci) { + throw new JsrNotSupportedBailout("unstructured control flow (internal limitation)"); + } + ConstantNode nextBciNode = getJsrConstant(nextBci); + frameState.push(JavaKind.Object, nextBciNode); + appendGoto(successor); + } + + protected void genRet(int localIndex) { + BciBlock successor = currentBlock.getRetSuccessor(); + ValueNode local = frameState.loadLocal(localIndex, JavaKind.Object); + JsrScope scope = currentBlock.getJsrScope(); + int retAddress = scope.nextReturnAddress(); + ConstantNode returnBciNode = getJsrConstant(retAddress); + LogicNode guard = IntegerEqualsNode.create(local, returnBciNode, constantReflection); + guard = graph.unique(guard); + append(new FixedGuardNode(guard, JavaSubroutineMismatch, InvalidateReprofile)); + if (!successor.getJsrScope().equals(scope.pop())) { + throw new JsrNotSupportedBailout("unstructured control flow (ret leaves more than one scope)"); + } + appendGoto(successor); + } + + private ConstantNode getJsrConstant(long bci) { + JavaConstant nextBciConstant = new RawConstant(bci); + Stamp nextBciStamp = StampFactory.forConstant(nextBciConstant); + ConstantNode nextBciNode = new ConstantNode(nextBciConstant, nextBciStamp); + return graph.unique(nextBciNode); + } + + protected void genIntegerSwitch(ValueNode value, ArrayList actualSuccessors, int[] keys, double[] keyProbabilities, int[] keySuccessors) { + if (value.isConstant()) { + JavaConstant constant = (JavaConstant) value.asConstant(); + int constantValue = constant.asInt(); + for (int i = 0; i < keys.length; ++i) { + if (keys[i] == constantValue) { + appendGoto(actualSuccessors.get(keySuccessors[i])); + return; + } + } + appendGoto(actualSuccessors.get(keySuccessors[keys.length])); + } else { + this.controlFlowSplit = true; + double[] successorProbabilities = successorProbabilites(actualSuccessors.size(), keySuccessors, keyProbabilities); + IntegerSwitchNode switchNode = append(new IntegerSwitchNode(value, actualSuccessors.size(), keys, keyProbabilities, keySuccessors)); + for (int i = 0; i < actualSuccessors.size(); i++) { + switchNode.setBlockSuccessor(i, createBlockTarget(successorProbabilities[i], actualSuccessors.get(i), frameState)); + } + } + } + + /** + * Helper function that sums up the probabilities of all keys that lead to a specific successor. + * + * @return an array of size successorCount with the accumulated probability for each successor. + */ + private static double[] successorProbabilites(int successorCount, int[] keySuccessors, double[] keyProbabilities) { + double[] probability = new double[successorCount]; + for (int i = 0; i < keySuccessors.length; i++) { + probability[keySuccessors[i]] += keyProbabilities[i]; + } + return probability; + } + + protected ConstantNode appendConstant(JavaConstant constant) { + assert constant != null; + return ConstantNode.forConstant(constant, metaAccess, graph); + } + + @Override + public T append(T v) { + if (v.graph() != null) { + return v; + } + T added = graph.addOrUnique(v); + if (added == v) { + updateLastInstruction(v); + } + return added; + } + + @Override + public T recursiveAppend(T v) { + if (v.graph() != null) { + return v; + } + T added = graph.addOrUniqueWithInputs(v); + if (added == v) { + updateLastInstruction(v); + } + return added; + } + + private void updateLastInstruction(T v) { + if (UseGraalInstrumentation.getValue()) { + // resolve instrumentation target + if (v instanceof InstrumentationBeginNode) { + InstrumentationBeginNode begin = (InstrumentationBeginNode) v; + if (!begin.isAnchored() && lastBCI != -1) { + int currentBCI = stream.currentBCI(); + // temporarily set the bytecode stream to lastBCI + stream.setBCI(lastBCI); + // The instrumentation should be associated with the predecessor. In case of the + // predecessor being optimized away, e.g., inlining, we should not set the + // target. + if (stream.nextBCI() == currentBCI) { + begin.setTarget(lastInstr); + } + // restore the current BCI + stream.setBCI(currentBCI); + } + } + } + if (v instanceof FixedNode) { + FixedNode fixedNode = (FixedNode) v; + lastInstr.setNext(fixedNode); + if (fixedNode instanceof FixedWithNextNode) { + FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) fixedNode; + assert fixedWithNextNode.next() == null : "cannot append instruction to instruction which isn't end"; + lastInstr = fixedWithNextNode; + lastBCI = stream.currentBCI(); + } else { + lastInstr = null; + lastBCI = -1; + } + } + } + + private Target checkLoopExit(FixedNode target, BciBlock targetBlock, FrameStateBuilder state) { + if (currentBlock != null) { + long exits = currentBlock.loops & ~targetBlock.loops; + if (exits != 0) { + LoopExitNode firstLoopExit = null; + LoopExitNode lastLoopExit = null; + + int pos = 0; + ArrayList exitLoops = new ArrayList<>(Long.bitCount(exits)); + do { + long lMask = 1L << pos; + if ((exits & lMask) != 0) { + exitLoops.add(blockMap.getLoopHeader(pos)); + exits &= ~lMask; + } + pos++; + } while (exits != 0); + + Collections.sort(exitLoops, new Comparator() { + + @Override + public int compare(BciBlock o1, BciBlock o2) { + return Long.bitCount(o2.loops) - Long.bitCount(o1.loops); + } + }); + + int bci = targetBlock.startBci; + if (targetBlock instanceof ExceptionDispatchBlock) { + bci = ((ExceptionDispatchBlock) targetBlock).deoptBci; + } + FrameStateBuilder newState = state.copy(); + for (BciBlock loop : exitLoops) { + LoopBeginNode loopBegin = (LoopBeginNode) getFirstInstruction(loop); + LoopExitNode loopExit = graph.add(new LoopExitNode(loopBegin)); + if (lastLoopExit != null) { + lastLoopExit.setNext(loopExit); + } + if (firstLoopExit == null) { + firstLoopExit = loopExit; + } + lastLoopExit = loopExit; + Debug.log("Target %s Exits %s, scanning framestates...", targetBlock, loop); + newState.clearNonLiveLocals(targetBlock, liveness, true); + newState.insertLoopProxies(loopExit, getEntryState(loop)); + loopExit.setStateAfter(newState.create(bci, loopExit)); + } + + lastLoopExit.setNext(target); + return new Target(firstLoopExit, newState); + } + } + return new Target(target, state); + } + + private FrameStateBuilder getEntryState(BciBlock block) { + return entryStateArray[block.id]; + } + + private void setEntryState(BciBlock block, FrameStateBuilder entryState) { + this.entryStateArray[block.id] = entryState; + } + + private void setFirstInstruction(BciBlock block, FixedWithNextNode firstInstruction) { + this.firstInstructionArray[block.id] = firstInstruction; + } + + private FixedWithNextNode getFirstInstruction(BciBlock block) { + return firstInstructionArray[block.id]; + } + + private FixedNode createTarget(double probability, BciBlock block, FrameStateBuilder stateAfter) { + assert probability >= 0 && probability <= 1.01 : probability; + if (isNeverExecutedCode(probability)) { + return graph.add(new DeoptimizeNode(InvalidateReprofile, UnreachedCode)); + } else { + assert block != null; + return createTarget(block, stateAfter); + } + } + + private FixedNode createTarget(BciBlock block, FrameStateBuilder state) { + return createTarget(block, state, false, false); + } + + private FixedNode createTarget(BciBlock block, FrameStateBuilder state, boolean canReuseInstruction, boolean canReuseState) { + assert block != null && state != null; + assert !block.isExceptionEntry || state.stackSize() == 1; + + if (getFirstInstruction(block) == null) { + /* + * This is the first time we see this block as a branch target. Create and return a + * placeholder that later can be replaced with a MergeNode when we see this block again. + */ + FixedNode targetNode; + if (canReuseInstruction && (block.getPredecessorCount() == 1 || !controlFlowSplit) && !block.isLoopHeader && (currentBlock.loops & ~block.loops) == 0) { + setFirstInstruction(block, lastInstr); + lastInstr = null; + } else { + setFirstInstruction(block, graph.add(new BeginNode())); + } + targetNode = getFirstInstruction(block); + Target target = checkLoopExit(targetNode, block, state); + FixedNode result = target.fixed; + FrameStateBuilder currentEntryState = target.state == state ? (canReuseState ? state : state.copy()) : target.state; + setEntryState(block, currentEntryState); + currentEntryState.clearNonLiveLocals(block, liveness, true); + + Debug.log("createTarget %s: first visit, result: %s", block, targetNode); + return result; + } + + // We already saw this block before, so we have to merge states. + if (!getEntryState(block).isCompatibleWith(state)) { + throw bailout("stacks do not match; bytecodes would not verify"); + } + + if (getFirstInstruction(block) instanceof LoopBeginNode) { + assert (block.isLoopHeader && currentBlock.getId() >= block.getId()) : "must be backward branch"; + /* + * Backward loop edge. We need to create a special LoopEndNode and merge with the loop + * begin node created before. + */ + LoopBeginNode loopBegin = (LoopBeginNode) getFirstInstruction(block); + LoopEndNode loopEnd = graph.add(new LoopEndNode(loopBegin)); + Target target = checkLoopExit(loopEnd, block, state); + FixedNode result = target.fixed; + getEntryState(block).merge(loopBegin, target.state); + + Debug.log("createTarget %s: merging backward branch to loop header %s, result: %s", block, loopBegin, result); + return result; + } + assert currentBlock == null || currentBlock.getId() < block.getId() : "must not be backward branch"; + assert getFirstInstruction(block).next() == null : "bytecodes already parsed for block"; + + if (getFirstInstruction(block) instanceof AbstractBeginNode && !(getFirstInstruction(block) instanceof AbstractMergeNode)) { + /* + * This is the second time we see this block. Create the actual MergeNode and the End + * Node for the already existing edge. + */ + AbstractBeginNode beginNode = (AbstractBeginNode) getFirstInstruction(block); + + // The EndNode for the already existing edge. + EndNode end = graph.add(new EndNode()); + // The MergeNode that replaces the placeholder. + AbstractMergeNode mergeNode = graph.add(new MergeNode()); + FixedNode next = beginNode.next(); + + if (beginNode.predecessor() instanceof ControlSplitNode) { + beginNode.setNext(end); + } else { + beginNode.replaceAtPredecessor(end); + beginNode.safeDelete(); + } + + mergeNode.addForwardEnd(end); + mergeNode.setNext(next); + + setFirstInstruction(block, mergeNode); + } + + AbstractMergeNode mergeNode = (AbstractMergeNode) getFirstInstruction(block); + + // The EndNode for the newly merged edge. + EndNode newEnd = graph.add(new EndNode()); + Target target = checkLoopExit(newEnd, block, state); + FixedNode result = target.fixed; + getEntryState(block).merge(mergeNode, target.state); + mergeNode.addForwardEnd(newEnd); + + Debug.log("createTarget %s: merging state, result: %s", block, result); + return result; + } + + /** + * Returns a block begin node with the specified state. If the specified probability is 0, the + * block deoptimizes immediately. + */ + private AbstractBeginNode createBlockTarget(double probability, BciBlock block, FrameStateBuilder stateAfter) { + FixedNode target = createTarget(probability, block, stateAfter); + AbstractBeginNode begin = BeginNode.begin(target); + + assert !(target instanceof DeoptimizeNode && begin instanceof BeginStateSplitNode && + ((BeginStateSplitNode) begin).stateAfter() != null) : "We are not allowed to set the stateAfter of the begin node," + + " because we have to deoptimize to a bci _before_ the actual if, so that the interpreter can update the profiling information."; + return begin; + } + + private ValueNode synchronizedObject(FrameStateBuilder state, ResolvedJavaMethod target) { + if (target.isStatic()) { + return appendConstant(getConstantReflection().asJavaClass(target.getDeclaringClass())); + } else { + return state.loadLocal(0, JavaKind.Object); + } + } + + @SuppressWarnings("try") + protected void processBlock(BciBlock block) { + // Ignore blocks that have no predecessors by the time their bytecodes are parsed + FixedWithNextNode firstInstruction = getFirstInstruction(block); + if (firstInstruction == null) { + Debug.log("Ignoring block %s", block); + return; + } + try (Indent indent = Debug.logAndIndent("Parsing block %s firstInstruction: %s loopHeader: %b", block, firstInstruction, block.isLoopHeader)) { + + lastInstr = firstInstruction; + frameState = getEntryState(block); + setCurrentFrameState(frameState); + currentBlock = block; + + if (firstInstruction instanceof AbstractMergeNode) { + setMergeStateAfter(block, firstInstruction); + } + + if (block == blockMap.getReturnBlock()) { + handleReturnBlock(); + } else if (block == blockMap.getUnwindBlock()) { + handleUnwindBlock(); + } else if (block instanceof ExceptionDispatchBlock) { + createExceptionDispatch((ExceptionDispatchBlock) block); + } else { + frameState.setRethrowException(false); + iterateBytecodesForBlock(block); + } + } + } + + private void handleUnwindBlock() { + if (parent == null) { + frameState.setRethrowException(false); + createUnwind(); + } else { + ValueNode exception = frameState.pop(JavaKind.Object); + this.unwindValue = exception; + this.beforeUnwindNode = this.lastInstr; + } + } + + private void handleReturnBlock() { + JavaKind returnKind = method.getSignature().getReturnKind().getStackKind(); + ValueNode x = returnKind == JavaKind.Void ? null : frameState.pop(returnKind); + assert frameState.stackSize() == 0; + beforeReturn(x, returnKind); + this.returnValue = x; + this.beforeReturnNode = this.lastInstr; + } + + private void setMergeStateAfter(BciBlock block, FixedWithNextNode firstInstruction) { + AbstractMergeNode abstractMergeNode = (AbstractMergeNode) firstInstruction; + if (abstractMergeNode.stateAfter() == null) { + int bci = block.startBci; + if (block instanceof ExceptionDispatchBlock) { + bci = ((ExceptionDispatchBlock) block).deoptBci; + } + abstractMergeNode.setStateAfter(createFrameState(bci, abstractMergeNode)); + } + } + + private void createUnwind() { + assert frameState.stackSize() == 1 : frameState; + ValueNode exception = frameState.pop(JavaKind.Object); + synchronizedEpilogue(BytecodeFrame.AFTER_EXCEPTION_BCI, null, null); + append(new UnwindNode(exception)); + } + + private void synchronizedEpilogue(int bci, ValueNode currentReturnValue, JavaKind currentReturnValueKind) { + if (method.isSynchronized()) { + if (currentReturnValue != null) { + frameState.push(currentReturnValueKind, currentReturnValue); + } + genMonitorExit(methodSynchronizedObject, currentReturnValue, bci); + assert !frameState.rethrowException(); + } + if (frameState.lockDepth(false) != 0) { + throw bailout("unbalanced monitors: too few exits exiting frame"); + } + } + + private void createExceptionDispatch(ExceptionDispatchBlock block) { + assert frameState.stackSize() == 1 : frameState; + if (block.handler.isCatchAll()) { + assert block.getSuccessorCount() == 1; + appendGoto(block.getSuccessor(0)); + return; + } + + JavaType catchType = block.handler.getCatchType(); + if (graphBuilderConfig.eagerResolving()) { + catchType = lookupType(block.handler.catchTypeCPI(), INSTANCEOF); + } + if (catchType instanceof ResolvedJavaType) { + TypeReference checkedCatchType = TypeReference.createTrusted(graph.getAssumptions(), (ResolvedJavaType) catchType); + + if (graphBuilderConfig.getSkippedExceptionTypes() != null) { + for (ResolvedJavaType skippedType : graphBuilderConfig.getSkippedExceptionTypes()) { + if (skippedType.isAssignableFrom(checkedCatchType.getType())) { + BciBlock nextBlock = block.getSuccessorCount() == 1 ? blockMap.getUnwindBlock() : block.getSuccessor(1); + ValueNode exception = frameState.stack[0]; + FixedNode trueSuccessor = graph.add(new DeoptimizeNode(InvalidateReprofile, UnreachedCode)); + FixedNode nextDispatch = createTarget(nextBlock, frameState); + append(new IfNode(graph.addOrUniqueWithInputs(createInstanceOf(checkedCatchType, exception)), trueSuccessor, nextDispatch, 0)); + return; + } + } + } + + BciBlock nextBlock = block.getSuccessorCount() == 1 ? blockMap.getUnwindBlock() : block.getSuccessor(1); + ValueNode exception = frameState.stack[0]; + /* Anchor for the piNode, which must be before any LoopExit inserted by createTarget. */ + BeginNode piNodeAnchor = graph.add(new BeginNode()); + ObjectStamp checkedStamp = StampFactory.objectNonNull(checkedCatchType); + PiNode piNode = graph.addWithoutUnique(new PiNode(exception, checkedStamp)); + frameState.pop(JavaKind.Object); + frameState.push(JavaKind.Object, piNode); + FixedNode catchSuccessor = createTarget(block.getSuccessor(0), frameState); + frameState.pop(JavaKind.Object); + frameState.push(JavaKind.Object, exception); + FixedNode nextDispatch = createTarget(nextBlock, frameState); + piNodeAnchor.setNext(catchSuccessor); + IfNode ifNode = append(new IfNode(graph.unique(createInstanceOf(checkedCatchType, exception)), piNodeAnchor, nextDispatch, 0.5)); + assert ifNode.trueSuccessor() == piNodeAnchor; + piNode.setGuard(ifNode.trueSuccessor()); + } else { + handleUnresolvedExceptionType(catchType); + } + } + + private void appendGoto(BciBlock successor) { + FixedNode targetInstr = createTarget(successor, frameState, true, true); + if (lastInstr != null && lastInstr != targetInstr) { + lastInstr.setNext(targetInstr); + } + } + + @SuppressWarnings("try") + protected void iterateBytecodesForBlock(BciBlock block) { + if (block.isLoopHeader) { + // Create the loop header block, which later will merge the backward branches of + // the loop. + controlFlowSplit = true; + LoopBeginNode loopBegin = appendLoopBegin(this.lastInstr); + lastInstr = loopBegin; + + // Create phi functions for all local variables and operand stack slots. + frameState.insertLoopPhis(liveness, block.loopId, loopBegin, forceLoopPhis(), stampFromValueForForcedPhis()); + loopBegin.setStateAfter(createFrameState(block.startBci, loopBegin)); + + /* + * We have seen all forward branches. All subsequent backward branches will merge to the + * loop header. This ensures that the loop header has exactly one non-loop predecessor. + */ + setFirstInstruction(block, loopBegin); + /* + * We need to preserve the frame state builder of the loop header so that we can merge + * values for phi functions, so make a copy of it. + */ + setEntryState(block, frameState.copy()); + + Debug.log(" created loop header %s", loopBegin); + } else if (lastInstr instanceof MergeNode) { + /* + * All inputs of non-loop phi nodes are known by now. We can infer the stamp for the + * phi, so that parsing continues with more precise type information. + */ + frameState.inferPhiStamps((AbstractMergeNode) lastInstr); + } + assert lastInstr.next() == null : "instructions already appended at block " + block; + Debug.log(" frameState: %s", frameState); + + lastInstr = finishInstruction(lastInstr, frameState); + + int endBCI = stream.endBCI(); + + stream.setBCI(block.startBci); + int bci = block.startBci; + BytecodesParsed.add(block.endBci - bci); + + /* Reset line number for new block */ + if (graphBuilderConfig.insertFullInfopoints()) { + previousLineNumber = -1; + } + + while (bci < endBCI) { + if (graphBuilderConfig.insertFullInfopoints() && !parsingIntrinsic()) { + currentLineNumber = lnt != null ? lnt.getLineNumber(bci) : -1; + if (currentLineNumber != previousLineNumber) { + genInfoPointNode(InfopointReason.BYTECODE_POSITION, null); + previousLineNumber = currentLineNumber; + } + } + + // read the opcode + int opcode = stream.currentBC(); + assert traceState(); + assert traceInstruction(bci, opcode, bci == block.startBci); + if (parent == null && bci == entryBCI) { + if (block.getJsrScope() != JsrScope.EMPTY_SCOPE) { + throw new JsrNotSupportedBailout("OSR into a JSR scope is not supported"); + } + EntryMarkerNode x = append(new EntryMarkerNode()); + frameState.insertProxies(value -> graph.unique(new EntryProxyNode(value, x))); + x.setStateAfter(createFrameState(bci, x)); + } + + try (DebugCloseable context = openNodeContext()) { + processBytecode(bci, opcode); + } catch (BailoutException e) { + // Don't wrap bailouts as parser errors + throw e; + } catch (Throwable e) { + throw asParserError(e); + } + + if (lastInstr == null || lastInstr.next() != null) { + break; + } + + stream.next(); + bci = stream.currentBCI(); + + assert block == currentBlock; + assert checkLastInstruction(); + lastInstr = finishInstruction(lastInstr, frameState); + if (bci < endBCI) { + if (bci > block.endBci) { + assert !block.getSuccessor(0).isExceptionEntry; + assert block.numNormalSuccessors() == 1; + // we fell through to the next block, add a goto and break + appendGoto(block.getSuccessor(0)); + break; + } + } + } + } + + private DebugCloseable openNodeContext() { + if (graphBuilderConfig.trackNodeSourcePosition() && !parsingIntrinsic()) { + return graph.withNodeSourcePosition(createBytecodePosition()); + } + return null; + } + + /* Also a hook for subclasses. */ + protected boolean forceLoopPhis() { + return graph.isOSR(); + } + + /* Hook for subclasses. */ + protected boolean stampFromValueForForcedPhis() { + return false; + } + + protected boolean checkLastInstruction() { + if (lastInstr instanceof BeginNode) { + // ignore + } else if (lastInstr instanceof StateSplit) { + StateSplit stateSplit = (StateSplit) lastInstr; + if (stateSplit.hasSideEffect()) { + assert stateSplit.stateAfter() != null : "side effect " + lastInstr + " requires a non-null stateAfter"; + } + } + return true; + } + + /* Also a hook for subclasses. */ + protected boolean disableLoopSafepoint() { + return parsingIntrinsic(); + } + + private LoopBeginNode appendLoopBegin(FixedWithNextNode fixedWithNext) { + EndNode preLoopEnd = graph.add(new EndNode()); + LoopBeginNode loopBegin = graph.add(new LoopBeginNode()); + if (disableLoopSafepoint()) { + loopBegin.disableSafepoint(); + } + fixedWithNext.setNext(preLoopEnd); + // Add the single non-loop predecessor of the loop header. + loopBegin.addForwardEnd(preLoopEnd); + return loopBegin; + } + + /** + * Hook for subclasses to modify the last instruction or add other instructions. + * + * @param instr The last instruction (= fixed node) which was added. + * @param state The current frame state. + * @return Returns the (new) last instruction. + */ + protected FixedWithNextNode finishInstruction(FixedWithNextNode instr, FrameStateBuilder state) { + return instr; + } + + private void genInfoPointNode(InfopointReason reason, ValueNode escapedReturnValue) { + if (!parsingIntrinsic() && graphBuilderConfig.insertFullInfopoints()) { + append(new FullInfopointNode(reason, createFrameState(bci(), null), escapedReturnValue)); + } + } + + private boolean traceState() { + if (Debug.isEnabled() && BytecodeParserOptions.TraceBytecodeParserLevel.getValue() >= TRACELEVEL_STATE && Debug.isLogEnabled()) { + frameState.traceState(); + } + return true; + } + + protected void genIf(ValueNode x, Condition cond, ValueNode y) { + assert x.getStackKind() == y.getStackKind(); + assert currentBlock.getSuccessorCount() == 2; + BciBlock trueBlock = currentBlock.getSuccessor(0); + BciBlock falseBlock = currentBlock.getSuccessor(1); + + FrameState stateBefore = null; + ProfilingPlugin profilingPlugin = this.graphBuilderConfig.getPlugins().getProfilingPlugin(); + if (profilingPlugin != null && profilingPlugin.shouldProfile(this, method)) { + stateBefore = frameState.create(bci(), getNonIntrinsicAncestor(), false, null, null); + } + + if (trueBlock == falseBlock) { + // The target block is the same independent of the condition. + appendGoto(trueBlock); + return; + } + + ValueNode a = x; + ValueNode b = y; + + // Check whether the condition needs to mirror the operands. + if (cond.canonicalMirror()) { + a = y; + b = x; + } + + // Create the logic node for the condition. + LogicNode condition = createLogicNode(cond, a, b); + + // Check whether the condition needs to negate the result. + boolean negate = cond.canonicalNegate(); + + // Remove a logic negation node and fold it into the negate boolean. + if (condition instanceof LogicNegationNode) { + LogicNegationNode logicNegationNode = (LogicNegationNode) condition; + negate = !negate; + condition = logicNegationNode.getValue(); + } + + if (condition instanceof LogicConstantNode) { + genConstantTargetIf(trueBlock, falseBlock, negate, condition); + } else { + if (condition.graph() == null) { + condition = graph.unique(condition); + } + + // Need to get probability based on current bci. + double probability = branchProbability(); + + if (negate) { + BciBlock tmpBlock = trueBlock; + trueBlock = falseBlock; + falseBlock = tmpBlock; + probability = 1 - probability; + } + + if (isNeverExecutedCode(probability)) { + append(new FixedGuardNode(condition, UnreachedCode, InvalidateReprofile, true)); + if (profilingPlugin != null && profilingPlugin.shouldProfile(this, method)) { + profilingPlugin.profileGoto(this, method, bci(), falseBlock.startBci, stateBefore); + } + appendGoto(falseBlock); + return; + } else if (isNeverExecutedCode(1 - probability)) { + append(new FixedGuardNode(condition, UnreachedCode, InvalidateReprofile, false)); + if (profilingPlugin != null && profilingPlugin.shouldProfile(this, method)) { + profilingPlugin.profileGoto(this, method, bci(), trueBlock.startBci, stateBefore); + } + appendGoto(trueBlock); + return; + } + + if (profilingPlugin != null && profilingPlugin.shouldProfile(this, method)) { + profilingPlugin.profileIf(this, method, bci(), condition, trueBlock.startBci, falseBlock.startBci, stateBefore); + } + + int oldBci = stream.currentBCI(); + int trueBlockInt = checkPositiveIntConstantPushed(trueBlock); + if (trueBlockInt != -1) { + int falseBlockInt = checkPositiveIntConstantPushed(falseBlock); + if (falseBlockInt != -1) { + if (tryGenConditionalForIf(trueBlock, falseBlock, condition, oldBci, trueBlockInt, falseBlockInt)) { + return; + } + } + } + + this.controlFlowSplit = true; + FixedNode trueSuccessor = createTarget(trueBlock, frameState, false, false); + FixedNode falseSuccessor = createTarget(falseBlock, frameState, false, true); + ValueNode ifNode = genIfNode(condition, trueSuccessor, falseSuccessor, probability); + postProcessIfNode(ifNode); + append(ifNode); + if (parsingIntrinsic()) { + if (x instanceof BranchProbabilityNode) { + ((BranchProbabilityNode) x).simplify(null); + } else if (y instanceof BranchProbabilityNode) { + ((BranchProbabilityNode) y).simplify(null); + } + } + } + } + + /** + * Hook for subclasses to generate custom nodes before an IfNode. + */ + @SuppressWarnings("unused") + protected void postProcessIfNode(ValueNode node) { + } + + private boolean tryGenConditionalForIf(BciBlock trueBlock, BciBlock falseBlock, LogicNode condition, int oldBci, int trueBlockInt, int falseBlockInt) { + if (gotoOrFallThroughAfterConstant(trueBlock) && gotoOrFallThroughAfterConstant(falseBlock) && trueBlock.getSuccessor(0) == falseBlock.getSuccessor(0)) { + genConditionalForIf(trueBlock, condition, oldBci, trueBlockInt, falseBlockInt, false); + return true; + } else if (this.parent != null && returnAfterConstant(trueBlock) && returnAfterConstant(falseBlock)) { + genConditionalForIf(trueBlock, condition, oldBci, trueBlockInt, falseBlockInt, true); + return true; + } + return false; + } + + private void genConditionalForIf(BciBlock trueBlock, LogicNode condition, int oldBci, int trueBlockInt, int falseBlockInt, boolean genReturn) { + ConstantNode trueValue = graph.unique(ConstantNode.forInt(trueBlockInt)); + ConstantNode falseValue = graph.unique(ConstantNode.forInt(falseBlockInt)); + ValueNode conditionalNode = ConditionalNode.create(condition, trueValue, falseValue); + if (conditionalNode.graph() == null) { + conditionalNode = graph.addOrUnique(conditionalNode); + } + if (genReturn) { + JavaKind returnKind = method.getSignature().getReturnKind().getStackKind(); + this.genReturn(conditionalNode, returnKind); + } else { + frameState.push(JavaKind.Int, conditionalNode); + appendGoto(trueBlock.getSuccessor(0)); + stream.setBCI(oldBci); + } + } + + private LogicNode createLogicNode(Condition cond, ValueNode a, ValueNode b) { + LogicNode condition; + assert !a.getStackKind().isNumericFloat(); + if (cond == Condition.EQ || cond == Condition.NE) { + if (a.getStackKind() == JavaKind.Object) { + condition = genObjectEquals(a, b); + } else { + condition = genIntegerEquals(a, b); + } + } else { + assert a.getStackKind() != JavaKind.Object && !cond.isUnsigned(); + condition = genIntegerLessThan(a, b); + } + return condition; + } + + private void genConstantTargetIf(BciBlock trueBlock, BciBlock falseBlock, boolean negate, LogicNode condition) { + LogicConstantNode constantLogicNode = (LogicConstantNode) condition; + boolean value = constantLogicNode.getValue(); + if (negate) { + value = !value; + } + BciBlock nextBlock = falseBlock; + if (value) { + nextBlock = trueBlock; + } + int startBci = nextBlock.startBci; + int targetAtStart = stream.readUByte(startBci); + if (targetAtStart == Bytecodes.GOTO && nextBlock.getPredecessorCount() == 1) { + // This is an empty block. Skip it. + BciBlock successorBlock = nextBlock.successors.get(0); + ProfilingPlugin profilingPlugin = graphBuilderConfig.getPlugins().getProfilingPlugin(); + if (profilingPlugin != null && profilingPlugin.shouldProfile(this, method)) { + FrameState stateBefore = frameState.create(bci(), getNonIntrinsicAncestor(), false, null, null); + profilingPlugin.profileGoto(this, method, bci(), successorBlock.startBci, stateBefore); + } + appendGoto(successorBlock); + assert nextBlock.numNormalSuccessors() == 1; + } else { + ProfilingPlugin profilingPlugin = graphBuilderConfig.getPlugins().getProfilingPlugin(); + if (profilingPlugin != null && profilingPlugin.shouldProfile(this, method)) { + FrameState stateBefore = frameState.create(bci(), getNonIntrinsicAncestor(), false, null, null); + profilingPlugin.profileGoto(this, method, bci(), nextBlock.startBci, stateBefore); + } + appendGoto(nextBlock); + } + } + + private int checkPositiveIntConstantPushed(BciBlock block) { + stream.setBCI(block.startBci); + int currentBC = stream.currentBC(); + if (currentBC >= Bytecodes.ICONST_0 && currentBC <= Bytecodes.ICONST_5) { + int constValue = currentBC - Bytecodes.ICONST_0; + return constValue; + } + return -1; + } + + private boolean gotoOrFallThroughAfterConstant(BciBlock block) { + stream.setBCI(block.startBci); + int currentBCI = stream.nextBCI(); + stream.setBCI(currentBCI); + int currentBC = stream.currentBC(); + return stream.currentBCI() > block.endBci || currentBC == Bytecodes.GOTO || currentBC == Bytecodes.GOTO_W; + } + + private boolean returnAfterConstant(BciBlock block) { + stream.setBCI(block.startBci); + int currentBCI = stream.nextBCI(); + stream.setBCI(currentBCI); + int currentBC = stream.currentBC(); + return currentBC == Bytecodes.IRETURN; + } + + @Override + public StampProvider getStampProvider() { + return stampProvider; + } + + @Override + public MetaAccessProvider getMetaAccess() { + return metaAccess; + } + + @Override + public void push(JavaKind slotKind, ValueNode value) { + assert value.isAlive(); + frameState.push(slotKind, value); + } + + @Override + public ConstantReflectionProvider getConstantReflection() { + return constantReflection; + } + + @Override + public ConstantFieldProvider getConstantFieldProvider() { + return constantFieldProvider; + } + + /** + * Gets the graph being processed by this builder. + */ + @Override + public StructuredGraph getGraph() { + return graph; + } + + @Override + public BytecodeParser getParent() { + return parent; + } + + @Override + public IntrinsicContext getIntrinsic() { + return intrinsicContext; + } + + @Override + public String toString() { + Formatter fmt = new Formatter(); + BytecodeParser bp = this; + String indent = ""; + while (bp != null) { + if (bp != this) { + fmt.format("%n%s", indent); + } + fmt.format("%s [bci: %d, intrinsic: %s]", bp.code.asStackTraceElement(bp.bci()), bp.bci(), bp.parsingIntrinsic()); + fmt.format("%n%s", new BytecodeDisassembler().disassemble(bp.code, bp.bci(), bp.bci() + 10)); + bp = bp.parent; + indent += " "; + } + return fmt.toString(); + } + + @Override + public BailoutException bailout(String string) { + FrameState currentFrameState = createFrameState(bci(), null); + StackTraceElement[] elements = GraphUtil.approxSourceStackTraceElement(currentFrameState); + BailoutException bailout = new PermanentBailoutException(string); + throw GraphUtil.createBailoutException(string, bailout, elements); + } + + private FrameState createFrameState(int bci, StateSplit forStateSplit) { + if (currentBlock != null && bci > currentBlock.endBci) { + frameState.clearNonLiveLocals(currentBlock, liveness, false); + } + return frameState.create(bci, forStateSplit); + } + + @Override + public void setStateAfter(StateSplit sideEffect) { + assert sideEffect.hasSideEffect(); + FrameState stateAfter = createFrameState(stream.nextBCI(), sideEffect); + sideEffect.setStateAfter(stateAfter); + } + + protected NodeSourcePosition createBytecodePosition() { + return frameState.createBytecodePosition(bci()); + } + + public void setCurrentFrameState(FrameStateBuilder frameState) { + this.frameState = frameState; + } + + protected final BytecodeStream getStream() { + return stream; + } + + @Override + public int bci() { + return stream.currentBCI(); + } + + public void loadLocal(int index, JavaKind kind) { + ValueNode value = frameState.loadLocal(index, kind); + frameState.push(kind, value); + } + + public void storeLocal(JavaKind kind, int index) { + ValueNode value = frameState.pop(kind); + frameState.storeLocal(index, kind, value); + } + + private void genLoadConstant(int cpi, int opcode) { + Object con = lookupConstant(cpi, opcode); + + if (con instanceof JavaType) { + // this is a load of class constant which might be unresolved + JavaType type = (JavaType) con; + if (type instanceof ResolvedJavaType) { + frameState.push(JavaKind.Object, appendConstant(getConstantReflection().asJavaClass((ResolvedJavaType) type))); + } else { + handleUnresolvedLoadConstant(type); + } + } else if (con instanceof JavaConstant) { + JavaConstant constant = (JavaConstant) con; + frameState.push(constant.getJavaKind(), appendConstant(constant)); + } else { + throw new Error("lookupConstant returned an object of incorrect type"); + } + } + + private void genLoadIndexed(JavaKind kind) { + ValueNode index = frameState.pop(JavaKind.Int); + ValueNode array = emitExplicitExceptions(frameState.pop(JavaKind.Object), index); + + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.handleLoadIndexed(this, array, index, kind)) { + return; + } + } + + frameState.push(kind, append(genLoadIndexed(array, index, kind))); + } + + private void genStoreIndexed(JavaKind kind) { + ValueNode value = frameState.pop(kind); + ValueNode index = frameState.pop(JavaKind.Int); + ValueNode array = emitExplicitExceptions(frameState.pop(JavaKind.Object), index); + + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.handleStoreIndexed(this, array, index, kind, value)) { + return; + } + } + + genStoreIndexed(array, index, kind, value); + } + + private void genArithmeticOp(JavaKind kind, int opcode) { + ValueNode y = frameState.pop(kind); + ValueNode x = frameState.pop(kind); + ValueNode v; + switch (opcode) { + case IADD: + case LADD: + v = genIntegerAdd(x, y); + break; + case FADD: + case DADD: + v = genFloatAdd(x, y); + break; + case ISUB: + case LSUB: + v = genIntegerSub(x, y); + break; + case FSUB: + case DSUB: + v = genFloatSub(x, y); + break; + case IMUL: + case LMUL: + v = genIntegerMul(x, y); + break; + case FMUL: + case DMUL: + v = genFloatMul(x, y); + break; + case FDIV: + case DDIV: + v = genFloatDiv(x, y); + break; + case FREM: + case DREM: + v = genFloatRem(x, y); + break; + default: + throw shouldNotReachHere(); + } + frameState.push(kind, append(v)); + } + + private void genIntegerDivOp(JavaKind kind, int opcode) { + ValueNode y = frameState.pop(kind); + ValueNode x = frameState.pop(kind); + ValueNode v; + switch (opcode) { + case IDIV: + case LDIV: + v = genIntegerDiv(x, y); + break; + case IREM: + case LREM: + v = genIntegerRem(x, y); + break; + default: + throw shouldNotReachHere(); + } + frameState.push(kind, append(v)); + } + + private void genNegateOp(JavaKind kind) { + ValueNode x = frameState.pop(kind); + frameState.push(kind, append(genNegateOp(x))); + } + + private void genShiftOp(JavaKind kind, int opcode) { + ValueNode s = frameState.pop(JavaKind.Int); + ValueNode x = frameState.pop(kind); + ValueNode v; + switch (opcode) { + case ISHL: + case LSHL: + v = genLeftShift(x, s); + break; + case ISHR: + case LSHR: + v = genRightShift(x, s); + break; + case IUSHR: + case LUSHR: + v = genUnsignedRightShift(x, s); + break; + default: + throw shouldNotReachHere(); + } + frameState.push(kind, append(v)); + } + + private void genLogicOp(JavaKind kind, int opcode) { + ValueNode y = frameState.pop(kind); + ValueNode x = frameState.pop(kind); + ValueNode v; + switch (opcode) { + case IAND: + case LAND: + v = genAnd(x, y); + break; + case IOR: + case LOR: + v = genOr(x, y); + break; + case IXOR: + case LXOR: + v = genXor(x, y); + break; + default: + throw shouldNotReachHere(); + } + frameState.push(kind, append(v)); + } + + private void genCompareOp(JavaKind kind, boolean isUnorderedLess) { + ValueNode y = frameState.pop(kind); + ValueNode x = frameState.pop(kind); + frameState.push(JavaKind.Int, append(genNormalizeCompare(x, y, isUnorderedLess))); + } + + private void genFloatConvert(FloatConvert op, JavaKind from, JavaKind to) { + ValueNode input = frameState.pop(from); + frameState.push(to, append(genFloatConvert(op, input))); + } + + private void genSignExtend(JavaKind from, JavaKind to) { + ValueNode input = frameState.pop(from); + if (from != from.getStackKind()) { + input = append(genNarrow(input, from.getBitCount())); + } + frameState.push(to, append(genSignExtend(input, to.getBitCount()))); + } + + private void genZeroExtend(JavaKind from, JavaKind to) { + ValueNode input = frameState.pop(from); + if (from != from.getStackKind()) { + input = append(genNarrow(input, from.getBitCount())); + } + frameState.push(to, append(genZeroExtend(input, to.getBitCount()))); + } + + private void genNarrow(JavaKind from, JavaKind to) { + ValueNode input = frameState.pop(from); + frameState.push(to, append(genNarrow(input, to.getBitCount()))); + } + + private void genIncrement() { + int index = getStream().readLocalIndex(); + int delta = getStream().readIncrement(); + ValueNode x = frameState.loadLocal(index, JavaKind.Int); + ValueNode y = appendConstant(JavaConstant.forInt(delta)); + frameState.storeLocal(index, JavaKind.Int, append(genIntegerAdd(x, y))); + } + + private void genIfZero(Condition cond) { + ValueNode y = appendConstant(JavaConstant.INT_0); + ValueNode x = frameState.pop(JavaKind.Int); + genIf(x, cond, y); + } + + private void genIfNull(Condition cond) { + ValueNode y = appendConstant(JavaConstant.NULL_POINTER); + ValueNode x = frameState.pop(JavaKind.Object); + genIf(x, cond, y); + } + + private void genIfSame(JavaKind kind, Condition cond) { + ValueNode y = frameState.pop(kind); + ValueNode x = frameState.pop(kind); + genIf(x, cond, y); + } + + protected JavaType lookupType(int cpi, int bytecode) { + maybeEagerlyResolve(cpi, bytecode); + JavaType result = constantPool.lookupType(cpi, bytecode); + assert !graphBuilderConfig.unresolvedIsError() || result instanceof ResolvedJavaType; + return result; + } + + private JavaMethod lookupMethod(int cpi, int opcode) { + maybeEagerlyResolve(cpi, opcode); + JavaMethod result = constantPool.lookupMethod(cpi, opcode); + /* + * In general, one cannot assume that the declaring class being initialized is useful, since + * the actual concrete receiver may be a different class (except for static calls). Also, + * interfaces are initialized only under special circumstances, so that this assertion would + * often fail for interface calls. + */ + assert !graphBuilderConfig.unresolvedIsError() || + (result instanceof ResolvedJavaMethod && (opcode != INVOKESTATIC || ((ResolvedJavaMethod) result).getDeclaringClass().isInitialized())) : result; + return result; + } + + private JavaField lookupField(int cpi, int opcode) { + maybeEagerlyResolve(cpi, opcode); + JavaField result = constantPool.lookupField(cpi, method, opcode); + if (graphBuilderConfig.eagerResolving()) { + assert result instanceof ResolvedJavaField : "Not resolved: " + result; + ResolvedJavaType declaringClass = ((ResolvedJavaField) result).getDeclaringClass(); + if (!declaringClass.isInitialized()) { + assert declaringClass.isInterface() : "Declaring class not initialized but not an interface? " + declaringClass; + declaringClass.initialize(); + } + } + assert !graphBuilderConfig.unresolvedIsError() || (result instanceof ResolvedJavaField && ((ResolvedJavaField) result).getDeclaringClass().isInitialized()) : result; + return result; + } + + private Object lookupConstant(int cpi, int opcode) { + maybeEagerlyResolve(cpi, opcode); + Object result = constantPool.lookupConstant(cpi); + assert !graphBuilderConfig.eagerResolving() || !(result instanceof JavaType) || (result instanceof ResolvedJavaType) : result; + return result; + } + + private void maybeEagerlyResolve(int cpi, int bytecode) { + if (intrinsicContext != null) { + constantPool.loadReferencedType(cpi, bytecode); + } else if (graphBuilderConfig.eagerResolving()) { + /* + * Since we're potentially triggering class initialization here, we need synchronization + * to mitigate the potential for class initialization related deadlock being caused by + * the compiler (e.g., https://github.com/graalvm/graal-core/pull/232/files#r90788550). + */ + synchronized (BytecodeParser.class) { + constantPool.loadReferencedType(cpi, bytecode); + } + } + } + + private JavaTypeProfile getProfileForTypeCheck(TypeReference type) { + if (parsingIntrinsic() || profilingInfo == null || !optimisticOpts.useTypeCheckHints() || type.isExact()) { + return null; + } else { + return profilingInfo.getTypeProfile(bci()); + } + } + + private void genCheckCast() { + int cpi = getStream().readCPI(); + JavaType type = lookupType(cpi, CHECKCAST); + ValueNode object = frameState.pop(JavaKind.Object); + + if (!(type instanceof ResolvedJavaType)) { + handleUnresolvedCheckCast(type, object); + return; + } + TypeReference checkedType = TypeReference.createTrusted(graph.getAssumptions(), (ResolvedJavaType) type); + JavaTypeProfile profile = getProfileForTypeCheck(checkedType); + + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.handleCheckCast(this, object, checkedType.getType(), profile)) { + return; + } + } + + ValueNode castNode = null; + if (profile != null) { + if (profile.getNullSeen().isFalse()) { + object = appendNullCheck(object); + ResolvedJavaType singleType = profile.asSingleType(); + if (singleType != null && checkedType.getType().isAssignableFrom(singleType)) { + LogicNode typeCheck = append(createInstanceOf(TypeReference.createExactTrusted(singleType), object, profile)); + if (typeCheck.isTautology()) { + castNode = object; + } else { + FixedGuardNode fixedGuard = append(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile, false)); + castNode = append(new PiNode(object, StampFactory.objectNonNull(TypeReference.createExactTrusted(singleType)), fixedGuard)); + } + } + } + } + if (castNode == null) { + LogicNode condition = genUnique(createInstanceOfAllowNull(checkedType, object, null)); + if (condition.isTautology()) { + castNode = object; + } else { + FixedGuardNode fixedGuard = append(new FixedGuardNode(condition, DeoptimizationReason.ClassCastException, DeoptimizationAction.InvalidateReprofile, false)); + castNode = append(new PiNode(object, StampFactory.object(checkedType), fixedGuard)); + } + } + frameState.push(JavaKind.Object, castNode); + } + + private ValueNode appendNullCheck(ValueNode object) { + if (object.stamp() instanceof AbstractPointerStamp) { + AbstractPointerStamp stamp = (AbstractPointerStamp) object.stamp(); + if (stamp.nonNull()) { + return object; + } + } + + LogicNode isNull = append(IsNullNode.create(object)); + FixedGuardNode fixedGuard = append(new FixedGuardNode(isNull, DeoptimizationReason.NullCheckException, DeoptimizationAction.InvalidateReprofile, true)); + return append(new PiNode(object, object.stamp().join(StampFactory.objectNonNull()), fixedGuard)); + } + + private void genInstanceOf() { + int cpi = getStream().readCPI(); + JavaType type = lookupType(cpi, INSTANCEOF); + ValueNode object = frameState.pop(JavaKind.Object); + + if (!(type instanceof ResolvedJavaType)) { + handleUnresolvedInstanceOf(type, object); + return; + } + TypeReference resolvedType = TypeReference.createTrusted(graph.getAssumptions(), (ResolvedJavaType) type); + JavaTypeProfile profile = getProfileForTypeCheck(resolvedType); + + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.handleInstanceOf(this, object, resolvedType.getType(), profile)) { + return; + } + } + + LogicNode instanceOfNode = null; + if (profile != null) { + if (profile.getNullSeen().isFalse()) { + object = appendNullCheck(object); + ResolvedJavaType singleType = profile.asSingleType(); + if (singleType != null) { + LogicNode typeCheck = append(createInstanceOf(TypeReference.createExactTrusted(singleType), object, profile)); + if (!typeCheck.isTautology()) { + append(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile)); + } + instanceOfNode = LogicConstantNode.forBoolean(resolvedType.getType().isAssignableFrom(singleType)); + } + } + } + if (instanceOfNode == null) { + instanceOfNode = createInstanceOf(resolvedType, object, null); + } + frameState.push(JavaKind.Int, append(genConditional(genUnique(instanceOfNode)))); + } + + void genNewInstance(int cpi) { + JavaType type = lookupType(cpi, NEW); + + if (!(type instanceof ResolvedJavaType) || !((ResolvedJavaType) type).isInitialized()) { + handleUnresolvedNewInstance(type); + return; + } + ResolvedJavaType resolvedType = (ResolvedJavaType) type; + + ResolvedJavaType[] skippedExceptionTypes = this.graphBuilderConfig.getSkippedExceptionTypes(); + if (skippedExceptionTypes != null) { + for (ResolvedJavaType exceptionType : skippedExceptionTypes) { + if (exceptionType.isAssignableFrom(resolvedType)) { + append(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, RuntimeConstraint)); + return; + } + } + } + + ClassInitializationPlugin classInitializationPlugin = graphBuilderConfig.getPlugins().getClassInitializationPlugin(); + if (classInitializationPlugin != null && classInitializationPlugin.shouldApply(this, resolvedType)) { + FrameState stateBefore = frameState.create(bci(), getNonIntrinsicAncestor(), false, null, null); + classInitializationPlugin.apply(this, resolvedType, stateBefore); + } + + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.handleNewInstance(this, resolvedType)) { + return; + } + } + + frameState.push(JavaKind.Object, append(createNewInstance(resolvedType, true))); + } + + /** + * Gets the kind of array elements for the array type code that appears in a + * {@link Bytecodes#NEWARRAY} bytecode. + * + * @param code the array type code + * @return the kind from the array type code + */ + private static Class arrayTypeCodeToClass(int code) { + switch (code) { + case 4: + return boolean.class; + case 5: + return char.class; + case 6: + return float.class; + case 7: + return double.class; + case 8: + return byte.class; + case 9: + return short.class; + case 10: + return int.class; + case 11: + return long.class; + default: + throw new IllegalArgumentException("unknown array type code: " + code); + } + } + + private void genNewPrimitiveArray(int typeCode) { + ResolvedJavaType elementType = metaAccess.lookupJavaType(arrayTypeCodeToClass(typeCode)); + ValueNode length = frameState.pop(JavaKind.Int); + + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.handleNewArray(this, elementType, length)) { + return; + } + } + + frameState.push(JavaKind.Object, append(createNewArray(elementType, length, true))); + } + + private void genNewObjectArray(int cpi) { + JavaType type = lookupType(cpi, ANEWARRAY); + + if (!(type instanceof ResolvedJavaType)) { + ValueNode length = frameState.pop(JavaKind.Int); + handleUnresolvedNewObjectArray(type, length); + return; + } + + ResolvedJavaType resolvedType = (ResolvedJavaType) type; + + ClassInitializationPlugin classInitializationPlugin = this.graphBuilderConfig.getPlugins().getClassInitializationPlugin(); + if (classInitializationPlugin != null && classInitializationPlugin.shouldApply(this, resolvedType.getArrayClass())) { + FrameState stateBefore = frameState.create(bci(), getNonIntrinsicAncestor(), false, null, null); + classInitializationPlugin.apply(this, resolvedType.getArrayClass(), stateBefore); + } + + ValueNode length = frameState.pop(JavaKind.Int); + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.handleNewArray(this, resolvedType, length)) { + return; + } + } + + frameState.push(JavaKind.Object, append(createNewArray(resolvedType, length, true))); + } + + private void genNewMultiArray(int cpi) { + JavaType type = lookupType(cpi, MULTIANEWARRAY); + int rank = getStream().readUByte(bci() + 3); + ValueNode[] dims = new ValueNode[rank]; + + if (!(type instanceof ResolvedJavaType)) { + for (int i = rank - 1; i >= 0; i--) { + dims[i] = frameState.pop(JavaKind.Int); + } + handleUnresolvedNewMultiArray(type, dims); + return; + } + ResolvedJavaType resolvedType = (ResolvedJavaType) type; + + ClassInitializationPlugin classInitializationPlugin = this.graphBuilderConfig.getPlugins().getClassInitializationPlugin(); + if (classInitializationPlugin != null && classInitializationPlugin.shouldApply(this, resolvedType.getArrayClass())) { + FrameState stateBefore = frameState.create(bci(), getNonIntrinsicAncestor(), false, null, null); + classInitializationPlugin.apply(this, resolvedType.getArrayClass(), stateBefore); + } + + for (int i = rank - 1; i >= 0; i--) { + dims[i] = frameState.pop(JavaKind.Int); + } + + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.handleNewMultiArray(this, resolvedType, dims)) { + return; + } + } + + frameState.push(JavaKind.Object, append(createNewMultiArray(resolvedType, dims))); + } + + private void genGetField(JavaField field) { + ValueNode receiver = emitExplicitExceptions(frameState.pop(JavaKind.Object), null); + + if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaField) field).getDeclaringClass().isInitialized()) { + handleUnresolvedLoadField(field, receiver); + return; + } + ResolvedJavaField resolvedField = (ResolvedJavaField) field; + + if (!parsingIntrinsic() && GeneratePIC.getValue()) { + graph.recordField(resolvedField); + } + + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.handleLoadField(this, receiver, resolvedField)) { + return; + } + } + + frameState.push(field.getJavaKind(), append(genLoadField(receiver, resolvedField))); + if (resolvedField.getName().equals("referent") && resolvedField.getDeclaringClass().equals(metaAccess.lookupJavaType(Reference.class))) { + LocationIdentity referentIdentity = new FieldLocationIdentity(resolvedField); + append(new MembarNode(0, referentIdentity)); + } + } + + /** + * @param receiver the receiver of an object based operation + * @param index the index of an array based operation that is to be tested for out of bounds. + * This is null for a non-array operation. + * @return the receiver value possibly modified to have a tighter stamp + */ + protected ValueNode emitExplicitExceptions(ValueNode receiver, ValueNode index) { + assert receiver != null; + if (graphBuilderConfig.getBytecodeExceptionMode() == BytecodeExceptionMode.OmitAll) { + return receiver; + } + if (graphBuilderConfig.getBytecodeExceptionMode() == BytecodeExceptionMode.Profile && (profilingInfo == null || + (optimisticOpts.useExceptionProbabilityForOperations() && profilingInfo.getExceptionSeen(bci()) == TriState.FALSE && !GraalOptions.StressExplicitExceptionCode.getValue()))) { + return receiver; + } + + ValueNode nonNullReceiver = emitExplicitNullCheck(receiver); + if (index != null) { + ValueNode length = append(genArrayLength(nonNullReceiver)); + emitExplicitBoundsCheck(index, length); + } + EXPLICIT_EXCEPTIONS.increment(); + return nonNullReceiver; + } + + private void genPutField(JavaField field) { + ValueNode value = frameState.pop(field.getJavaKind()); + ValueNode receiver = emitExplicitExceptions(frameState.pop(JavaKind.Object), null); + + if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaField) field).getDeclaringClass().isInitialized()) { + handleUnresolvedStoreField(field, value, receiver); + return; + } + ResolvedJavaField resolvedField = (ResolvedJavaField) field; + + if (!parsingIntrinsic() && GeneratePIC.getValue()) { + graph.recordField(resolvedField); + } + + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.handleStoreField(this, receiver, resolvedField, value)) { + return; + } + } + + if (resolvedField.isFinal() && method.isConstructor()) { + finalBarrierRequired = true; + } + genStoreField(receiver, resolvedField, value); + } + + private void genGetStatic(JavaField field) { + if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaType) field.getDeclaringClass()).isInitialized()) { + handleUnresolvedLoadField(field, null); + return; + } + ResolvedJavaField resolvedField = (ResolvedJavaField) field; + + if (!parsingIntrinsic() && GeneratePIC.getValue()) { + graph.recordField(resolvedField); + } + + /* + * Javac does not allow use of "$assertionsDisabled" for a field name but Eclipse does, in + * which case a suffix is added to the generated field. + */ + if ((parsingIntrinsic() || graphBuilderConfig.omitAssertions()) && resolvedField.isSynthetic() && resolvedField.getName().startsWith("$assertionsDisabled")) { + frameState.push(field.getJavaKind(), ConstantNode.forBoolean(true, graph)); + return; + } + + ClassInitializationPlugin classInitializationPlugin = this.graphBuilderConfig.getPlugins().getClassInitializationPlugin(); + if (classInitializationPlugin != null && classInitializationPlugin.shouldApply(this, resolvedField.getDeclaringClass())) { + FrameState stateBefore = frameState.create(bci(), getNonIntrinsicAncestor(), false, null, null); + classInitializationPlugin.apply(this, resolvedField.getDeclaringClass(), stateBefore); + } + + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.handleLoadStaticField(this, resolvedField)) { + return; + } + } + + frameState.push(field.getJavaKind(), append(genLoadField(null, resolvedField))); + } + + private void genPutStatic(JavaField field) { + ValueNode value = frameState.pop(field.getJavaKind()); + if (!(field instanceof ResolvedJavaField) || !((ResolvedJavaType) field.getDeclaringClass()).isInitialized()) { + handleUnresolvedStoreField(field, value, null); + return; + } + ResolvedJavaField resolvedField = (ResolvedJavaField) field; + + if (!parsingIntrinsic() && GeneratePIC.getValue()) { + graph.recordField(resolvedField); + } + + ClassInitializationPlugin classInitializationPlugin = this.graphBuilderConfig.getPlugins().getClassInitializationPlugin(); + if (classInitializationPlugin != null && classInitializationPlugin.shouldApply(this, resolvedField.getDeclaringClass())) { + FrameState stateBefore = frameState.create(bci(), getNonIntrinsicAncestor(), false, null, null); + classInitializationPlugin.apply(this, resolvedField.getDeclaringClass(), stateBefore); + } + + for (NodePlugin plugin : graphBuilderConfig.getPlugins().getNodePlugins()) { + if (plugin.handleStoreStaticField(this, resolvedField, value)) { + return; + } + } + + genStoreField(null, resolvedField, value); + } + + private double[] switchProbability(int numberOfCases, int bci) { + double[] prob = (profilingInfo == null ? null : profilingInfo.getSwitchProbabilities(bci)); + if (prob != null) { + assert prob.length == numberOfCases; + } else { + Debug.log("Missing probability (switch) in %s at bci %d", method, bci); + prob = new double[numberOfCases]; + for (int i = 0; i < numberOfCases; i++) { + prob[i] = 1.0d / numberOfCases; + } + } + assert allPositive(prob); + return prob; + } + + private static boolean allPositive(double[] a) { + for (double d : a) { + if (d < 0) { + return false; + } + } + return true; + } + + static class SuccessorInfo { + final int blockIndex; + int actualIndex; + + SuccessorInfo(int blockSuccessorIndex) { + this.blockIndex = blockSuccessorIndex; + actualIndex = -1; + } + } + + private void genSwitch(BytecodeSwitch bs) { + int bci = bci(); + ValueNode value = frameState.pop(JavaKind.Int); + + int nofCases = bs.numberOfCases(); + double[] keyProbabilities = switchProbability(nofCases + 1, bci); + + Map bciToBlockSuccessorIndex = new HashMap<>(); + for (int i = 0; i < currentBlock.getSuccessorCount(); i++) { + assert !bciToBlockSuccessorIndex.containsKey(currentBlock.getSuccessor(i).startBci); + if (!bciToBlockSuccessorIndex.containsKey(currentBlock.getSuccessor(i).startBci)) { + bciToBlockSuccessorIndex.put(currentBlock.getSuccessor(i).startBci, new SuccessorInfo(i)); + } + } + + ArrayList actualSuccessors = new ArrayList<>(); + int[] keys = new int[nofCases]; + int[] keySuccessors = new int[nofCases + 1]; + int deoptSuccessorIndex = -1; + int nextSuccessorIndex = 0; + boolean constantValue = value.isConstant(); + for (int i = 0; i < nofCases + 1; i++) { + if (i < nofCases) { + keys[i] = bs.keyAt(i); + } + + if (!constantValue && isNeverExecutedCode(keyProbabilities[i])) { + if (deoptSuccessorIndex < 0) { + deoptSuccessorIndex = nextSuccessorIndex++; + actualSuccessors.add(null); + } + keySuccessors[i] = deoptSuccessorIndex; + } else { + int targetBci = i >= nofCases ? bs.defaultTarget() : bs.targetAt(i); + SuccessorInfo info = bciToBlockSuccessorIndex.get(targetBci); + if (info.actualIndex < 0) { + info.actualIndex = nextSuccessorIndex++; + actualSuccessors.add(currentBlock.getSuccessor(info.blockIndex)); + } + keySuccessors[i] = info.actualIndex; + } + } + + genIntegerSwitch(value, actualSuccessors, keys, keyProbabilities, keySuccessors); + + } + + protected boolean isNeverExecutedCode(double probability) { + return probability == 0 && optimisticOpts.removeNeverExecutedCode(); + } + + protected double branchProbability() { + if (profilingInfo == null) { + return 0.5; + } + assert assertAtIfBytecode(); + double probability = profilingInfo.getBranchTakenProbability(bci()); + if (probability < 0) { + assert probability == -1 : "invalid probability"; + Debug.log("missing probability in %s at bci %d", code, bci()); + probability = 0.5; + } + + if (!optimisticOpts.removeNeverExecutedCode()) { + if (probability == 0) { + probability = 0.0000001; + } else if (probability == 1) { + probability = 0.999999; + } + } + return probability; + } + + private boolean assertAtIfBytecode() { + int bytecode = stream.currentBC(); + switch (bytecode) { + case IFEQ: + case IFNE: + case IFLT: + case IFGE: + case IFGT: + case IFLE: + case IF_ICMPEQ: + case IF_ICMPNE: + case IF_ICMPLT: + case IF_ICMPGE: + case IF_ICMPGT: + case IF_ICMPLE: + case IF_ACMPEQ: + case IF_ACMPNE: + case IFNULL: + case IFNONNULL: + return true; + } + assert false : String.format("%x is not an if bytecode", bytecode); + return true; + } + + public final void processBytecode(int bci, int opcode) { + int cpi; + + // @formatter:off + // Checkstyle: stop + switch (opcode) { + case NOP : /* nothing to do */ break; + case ACONST_NULL : frameState.push(JavaKind.Object, appendConstant(JavaConstant.NULL_POINTER)); break; + case ICONST_M1 : // fall through + case ICONST_0 : // fall through + case ICONST_1 : // fall through + case ICONST_2 : // fall through + case ICONST_3 : // fall through + case ICONST_4 : // fall through + case ICONST_5 : frameState.push(JavaKind.Int, appendConstant(JavaConstant.forInt(opcode - ICONST_0))); break; + case LCONST_0 : // fall through + case LCONST_1 : frameState.push(JavaKind.Long, appendConstant(JavaConstant.forLong(opcode - LCONST_0))); break; + case FCONST_0 : // fall through + case FCONST_1 : // fall through + case FCONST_2 : frameState.push(JavaKind.Float, appendConstant(JavaConstant.forFloat(opcode - FCONST_0))); break; + case DCONST_0 : // fall through + case DCONST_1 : frameState.push(JavaKind.Double, appendConstant(JavaConstant.forDouble(opcode - DCONST_0))); break; + case BIPUSH : frameState.push(JavaKind.Int, appendConstant(JavaConstant.forInt(stream.readByte()))); break; + case SIPUSH : frameState.push(JavaKind.Int, appendConstant(JavaConstant.forInt(stream.readShort()))); break; + case LDC : // fall through + case LDC_W : // fall through + case LDC2_W : genLoadConstant(stream.readCPI(), opcode); break; + case ILOAD : loadLocal(stream.readLocalIndex(), JavaKind.Int); break; + case LLOAD : loadLocal(stream.readLocalIndex(), JavaKind.Long); break; + case FLOAD : loadLocal(stream.readLocalIndex(), JavaKind.Float); break; + case DLOAD : loadLocal(stream.readLocalIndex(), JavaKind.Double); break; + case ALOAD : loadLocal(stream.readLocalIndex(), JavaKind.Object); break; + case ILOAD_0 : // fall through + case ILOAD_1 : // fall through + case ILOAD_2 : // fall through + case ILOAD_3 : loadLocal(opcode - ILOAD_0, JavaKind.Int); break; + case LLOAD_0 : // fall through + case LLOAD_1 : // fall through + case LLOAD_2 : // fall through + case LLOAD_3 : loadLocal(opcode - LLOAD_0, JavaKind.Long); break; + case FLOAD_0 : // fall through + case FLOAD_1 : // fall through + case FLOAD_2 : // fall through + case FLOAD_3 : loadLocal(opcode - FLOAD_0, JavaKind.Float); break; + case DLOAD_0 : // fall through + case DLOAD_1 : // fall through + case DLOAD_2 : // fall through + case DLOAD_3 : loadLocal(opcode - DLOAD_0, JavaKind.Double); break; + case ALOAD_0 : // fall through + case ALOAD_1 : // fall through + case ALOAD_2 : // fall through + case ALOAD_3 : loadLocal(opcode - ALOAD_0, JavaKind.Object); break; + case IALOAD : genLoadIndexed(JavaKind.Int ); break; + case LALOAD : genLoadIndexed(JavaKind.Long ); break; + case FALOAD : genLoadIndexed(JavaKind.Float ); break; + case DALOAD : genLoadIndexed(JavaKind.Double); break; + case AALOAD : genLoadIndexed(JavaKind.Object); break; + case BALOAD : genLoadIndexed(JavaKind.Byte ); break; + case CALOAD : genLoadIndexed(JavaKind.Char ); break; + case SALOAD : genLoadIndexed(JavaKind.Short ); break; + case ISTORE : storeLocal(JavaKind.Int, stream.readLocalIndex()); break; + case LSTORE : storeLocal(JavaKind.Long, stream.readLocalIndex()); break; + case FSTORE : storeLocal(JavaKind.Float, stream.readLocalIndex()); break; + case DSTORE : storeLocal(JavaKind.Double, stream.readLocalIndex()); break; + case ASTORE : storeLocal(JavaKind.Object, stream.readLocalIndex()); break; + case ISTORE_0 : // fall through + case ISTORE_1 : // fall through + case ISTORE_2 : // fall through + case ISTORE_3 : storeLocal(JavaKind.Int, opcode - ISTORE_0); break; + case LSTORE_0 : // fall through + case LSTORE_1 : // fall through + case LSTORE_2 : // fall through + case LSTORE_3 : storeLocal(JavaKind.Long, opcode - LSTORE_0); break; + case FSTORE_0 : // fall through + case FSTORE_1 : // fall through + case FSTORE_2 : // fall through + case FSTORE_3 : storeLocal(JavaKind.Float, opcode - FSTORE_0); break; + case DSTORE_0 : // fall through + case DSTORE_1 : // fall through + case DSTORE_2 : // fall through + case DSTORE_3 : storeLocal(JavaKind.Double, opcode - DSTORE_0); break; + case ASTORE_0 : // fall through + case ASTORE_1 : // fall through + case ASTORE_2 : // fall through + case ASTORE_3 : storeLocal(JavaKind.Object, opcode - ASTORE_0); break; + case IASTORE : genStoreIndexed(JavaKind.Int ); break; + case LASTORE : genStoreIndexed(JavaKind.Long ); break; + case FASTORE : genStoreIndexed(JavaKind.Float ); break; + case DASTORE : genStoreIndexed(JavaKind.Double); break; + case AASTORE : genStoreIndexed(JavaKind.Object); break; + case BASTORE : genStoreIndexed(JavaKind.Byte ); break; + case CASTORE : genStoreIndexed(JavaKind.Char ); break; + case SASTORE : genStoreIndexed(JavaKind.Short ); break; + case POP : // fall through + case POP2 : // fall through + case DUP : // fall through + case DUP_X1 : // fall through + case DUP_X2 : // fall through + case DUP2 : // fall through + case DUP2_X1 : // fall through + case DUP2_X2 : // fall through + case SWAP : frameState.stackOp(opcode); break; + case IADD : // fall through + case ISUB : // fall through + case IMUL : genArithmeticOp(JavaKind.Int, opcode); break; + case IDIV : // fall through + case IREM : genIntegerDivOp(JavaKind.Int, opcode); break; + case LADD : // fall through + case LSUB : // fall through + case LMUL : genArithmeticOp(JavaKind.Long, opcode); break; + case LDIV : // fall through + case LREM : genIntegerDivOp(JavaKind.Long, opcode); break; + case FADD : // fall through + case FSUB : // fall through + case FMUL : // fall through + case FDIV : // fall through + case FREM : genArithmeticOp(JavaKind.Float, opcode); break; + case DADD : // fall through + case DSUB : // fall through + case DMUL : // fall through + case DDIV : // fall through + case DREM : genArithmeticOp(JavaKind.Double, opcode); break; + case INEG : genNegateOp(JavaKind.Int); break; + case LNEG : genNegateOp(JavaKind.Long); break; + case FNEG : genNegateOp(JavaKind.Float); break; + case DNEG : genNegateOp(JavaKind.Double); break; + case ISHL : // fall through + case ISHR : // fall through + case IUSHR : genShiftOp(JavaKind.Int, opcode); break; + case IAND : // fall through + case IOR : // fall through + case IXOR : genLogicOp(JavaKind.Int, opcode); break; + case LSHL : // fall through + case LSHR : // fall through + case LUSHR : genShiftOp(JavaKind.Long, opcode); break; + case LAND : // fall through + case LOR : // fall through + case LXOR : genLogicOp(JavaKind.Long, opcode); break; + case IINC : genIncrement(); break; + case I2F : genFloatConvert(FloatConvert.I2F, JavaKind.Int, JavaKind.Float); break; + case I2D : genFloatConvert(FloatConvert.I2D, JavaKind.Int, JavaKind.Double); break; + case L2F : genFloatConvert(FloatConvert.L2F, JavaKind.Long, JavaKind.Float); break; + case L2D : genFloatConvert(FloatConvert.L2D, JavaKind.Long, JavaKind.Double); break; + case F2I : genFloatConvert(FloatConvert.F2I, JavaKind.Float, JavaKind.Int); break; + case F2L : genFloatConvert(FloatConvert.F2L, JavaKind.Float, JavaKind.Long); break; + case F2D : genFloatConvert(FloatConvert.F2D, JavaKind.Float, JavaKind.Double); break; + case D2I : genFloatConvert(FloatConvert.D2I, JavaKind.Double, JavaKind.Int); break; + case D2L : genFloatConvert(FloatConvert.D2L, JavaKind.Double, JavaKind.Long); break; + case D2F : genFloatConvert(FloatConvert.D2F, JavaKind.Double, JavaKind.Float); break; + case L2I : genNarrow(JavaKind.Long, JavaKind.Int); break; + case I2L : genSignExtend(JavaKind.Int, JavaKind.Long); break; + case I2B : genSignExtend(JavaKind.Byte, JavaKind.Int); break; + case I2S : genSignExtend(JavaKind.Short, JavaKind.Int); break; + case I2C : genZeroExtend(JavaKind.Char, JavaKind.Int); break; + case LCMP : genCompareOp(JavaKind.Long, false); break; + case FCMPL : genCompareOp(JavaKind.Float, true); break; + case FCMPG : genCompareOp(JavaKind.Float, false); break; + case DCMPL : genCompareOp(JavaKind.Double, true); break; + case DCMPG : genCompareOp(JavaKind.Double, false); break; + case IFEQ : genIfZero(Condition.EQ); break; + case IFNE : genIfZero(Condition.NE); break; + case IFLT : genIfZero(Condition.LT); break; + case IFGE : genIfZero(Condition.GE); break; + case IFGT : genIfZero(Condition.GT); break; + case IFLE : genIfZero(Condition.LE); break; + case IF_ICMPEQ : genIfSame(JavaKind.Int, Condition.EQ); break; + case IF_ICMPNE : genIfSame(JavaKind.Int, Condition.NE); break; + case IF_ICMPLT : genIfSame(JavaKind.Int, Condition.LT); break; + case IF_ICMPGE : genIfSame(JavaKind.Int, Condition.GE); break; + case IF_ICMPGT : genIfSame(JavaKind.Int, Condition.GT); break; + case IF_ICMPLE : genIfSame(JavaKind.Int, Condition.LE); break; + case IF_ACMPEQ : genIfSame(JavaKind.Object, Condition.EQ); break; + case IF_ACMPNE : genIfSame(JavaKind.Object, Condition.NE); break; + case GOTO : genGoto(); break; + case JSR : genJsr(stream.readBranchDest()); break; + case RET : genRet(stream.readLocalIndex()); break; + case TABLESWITCH : genSwitch(new BytecodeTableSwitch(getStream(), bci())); break; + case LOOKUPSWITCH : genSwitch(new BytecodeLookupSwitch(getStream(), bci())); break; + case IRETURN : genReturn(frameState.pop(JavaKind.Int), JavaKind.Int); break; + case LRETURN : genReturn(frameState.pop(JavaKind.Long), JavaKind.Long); break; + case FRETURN : genReturn(frameState.pop(JavaKind.Float), JavaKind.Float); break; + case DRETURN : genReturn(frameState.pop(JavaKind.Double), JavaKind.Double); break; + case ARETURN : genReturn(frameState.pop(JavaKind.Object), JavaKind.Object); break; + case RETURN : genReturn(null, JavaKind.Void); break; + case GETSTATIC : cpi = stream.readCPI(); genGetStatic(lookupField(cpi, opcode)); break; + case PUTSTATIC : cpi = stream.readCPI(); genPutStatic(lookupField(cpi, opcode)); break; + case GETFIELD : cpi = stream.readCPI(); genGetField(lookupField(cpi, opcode)); break; + case PUTFIELD : cpi = stream.readCPI(); genPutField(lookupField(cpi, opcode)); break; + case INVOKEVIRTUAL : cpi = stream.readCPI(); genInvokeVirtual(lookupMethod(cpi, opcode)); break; + case INVOKESPECIAL : cpi = stream.readCPI(); genInvokeSpecial(lookupMethod(cpi, opcode)); break; + case INVOKESTATIC : cpi = stream.readCPI(); genInvokeStatic(lookupMethod(cpi, opcode)); break; + case INVOKEINTERFACE: cpi = stream.readCPI(); genInvokeInterface(lookupMethod(cpi, opcode)); break; + case INVOKEDYNAMIC : cpi = stream.readCPI4(); genInvokeDynamic(lookupMethod(cpi, opcode)); break; + case NEW : genNewInstance(stream.readCPI()); break; + case NEWARRAY : genNewPrimitiveArray(stream.readLocalIndex()); break; + case ANEWARRAY : genNewObjectArray(stream.readCPI()); break; + case ARRAYLENGTH : genArrayLength(); break; + case ATHROW : genThrow(); break; + case CHECKCAST : genCheckCast(); break; + case INSTANCEOF : genInstanceOf(); break; + case MONITORENTER : genMonitorEnter(frameState.pop(JavaKind.Object), stream.nextBCI()); break; + case MONITOREXIT : genMonitorExit(frameState.pop(JavaKind.Object), null, stream.nextBCI()); break; + case MULTIANEWARRAY : genNewMultiArray(stream.readCPI()); break; + case IFNULL : genIfNull(Condition.EQ); break; + case IFNONNULL : genIfNull(Condition.NE); break; + case GOTO_W : genGoto(); break; + case JSR_W : genJsr(stream.readBranchDest()); break; + case BREAKPOINT : throw new PermanentBailoutException("concurrent setting of breakpoint"); + default : throw new PermanentBailoutException("Unsupported opcode %d (%s) [bci=%d]", opcode, nameOf(opcode), bci); + } + // @formatter:on + // Checkstyle: resume + } + + private void genArrayLength() { + ValueNode array = emitExplicitExceptions(frameState.pop(JavaKind.Object), null); + frameState.push(JavaKind.Int, append(genArrayLength(array))); + } + + @Override + public ResolvedJavaMethod getMethod() { + return method; + } + + @Override + public Bytecode getCode() { + return code; + } + + public FrameStateBuilder getFrameStateBuilder() { + return frameState; + } + + protected boolean traceInstruction(int bci, int opcode, boolean blockStart) { + if (Debug.isEnabled() && BytecodeParserOptions.TraceBytecodeParserLevel.getValue() >= TRACELEVEL_INSTRUCTIONS && Debug.isLogEnabled()) { + traceInstructionHelper(bci, opcode, blockStart); + } + return true; + } + + private void traceInstructionHelper(int bci, int opcode, boolean blockStart) { + StringBuilder sb = new StringBuilder(40); + sb.append(blockStart ? '+' : '|'); + if (bci < 10) { + sb.append(" "); + } else if (bci < 100) { + sb.append(' '); + } + sb.append(bci).append(": ").append(Bytecodes.nameOf(opcode)); + for (int i = bci + 1; i < stream.nextBCI(); ++i) { + sb.append(' ').append(stream.readUByte(i)); + } + if (!currentBlock.getJsrScope().isEmpty()) { + sb.append(' ').append(currentBlock.getJsrScope()); + } + Debug.log("%s", sb); + } + + @Override + public boolean parsingIntrinsic() { + return intrinsicContext != null; + } + + @Override + public BytecodeParser getNonIntrinsicAncestor() { + BytecodeParser ancestor = parent; + while (ancestor != null && ancestor.parsingIntrinsic()) { + ancestor = ancestor.parent; + } + return ancestor; + } + + static String nSpaces(int n) { + return n == 0 ? "" : format("%" + n + "s", ""); + } + + @SuppressWarnings("all") + private static boolean assertionsEnabled() { + boolean assertionsEnabled = false; + assert assertionsEnabled = true; + return assertionsEnabled; + } +}