1 /* 2 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.replacements; 24 25 import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; 26 27 import java.lang.reflect.Method; 28 import java.lang.reflect.Modifier; 29 import java.util.ArrayList; 30 import java.util.List; 31 32 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; 33 import org.graalvm.compiler.core.common.type.StampFactory; 34 import org.graalvm.compiler.core.common.type.StampPair; 35 import org.graalvm.compiler.graph.Graph; 36 import org.graalvm.compiler.graph.Node.ValueNumberable; 37 import org.graalvm.compiler.java.FrameStateBuilder; 38 import org.graalvm.compiler.java.GraphBuilderPhase; 39 import org.graalvm.compiler.nodes.AbstractBeginNode; 40 import org.graalvm.compiler.nodes.AbstractMergeNode; 41 import org.graalvm.compiler.nodes.BeginNode; 42 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; 43 import org.graalvm.compiler.nodes.EndNode; 44 import org.graalvm.compiler.nodes.FixedNode; 45 import org.graalvm.compiler.nodes.FixedWithNextNode; 46 import org.graalvm.compiler.nodes.IfNode; 47 import org.graalvm.compiler.nodes.InvokeNode; 48 import org.graalvm.compiler.nodes.InvokeWithExceptionNode; 49 import org.graalvm.compiler.nodes.KillingBeginNode; 50 import org.graalvm.compiler.nodes.LogicNode; 51 import org.graalvm.compiler.nodes.MergeNode; 52 import org.graalvm.compiler.nodes.StructuredGraph; 53 import org.graalvm.compiler.nodes.ValueNode; 54 import org.graalvm.compiler.nodes.calc.FloatingNode; 55 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; 56 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; 57 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderTool; 58 import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext; 59 import org.graalvm.compiler.nodes.java.ExceptionObjectNode; 60 import org.graalvm.compiler.nodes.java.MethodCallTargetNode; 61 import org.graalvm.compiler.nodes.spi.StampProvider; 62 import org.graalvm.compiler.nodes.type.StampTool; 63 import org.graalvm.compiler.phases.OptimisticOptimizations; 64 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; 65 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality; 66 import org.graalvm.compiler.phases.common.inlining.InliningUtil; 67 import org.graalvm.compiler.phases.util.Providers; 68 import org.graalvm.compiler.word.WordTypes; 69 import org.graalvm.word.LocationIdentity; 70 71 import jdk.vm.ci.code.BytecodeFrame; 72 import jdk.vm.ci.meta.ConstantReflectionProvider; 73 import jdk.vm.ci.meta.JavaKind; 74 import jdk.vm.ci.meta.JavaType; 75 import jdk.vm.ci.meta.MetaAccessProvider; 76 import jdk.vm.ci.meta.ResolvedJavaMethod; 77 import jdk.vm.ci.meta.ResolvedJavaType; 78 import jdk.vm.ci.meta.Signature; 79 80 /** 81 * A utility for manually creating a graph. This will be expanded as necessary to support all 82 * subsystems that employ manual graph creation (as opposed to {@linkplain GraphBuilderPhase 83 * bytecode parsing} based graph creation). 84 */ 85 public class GraphKit implements GraphBuilderTool { 86 87 protected final Providers providers; 88 protected final StructuredGraph graph; 89 protected final WordTypes wordTypes; 90 protected final GraphBuilderConfiguration.Plugins graphBuilderPlugins; 91 protected FixedWithNextNode lastFixedNode; 92 93 private final List<Structure> structures; 94 95 protected abstract static class Structure { 96 } 97 98 public GraphKit(StructuredGraph graph, Providers providers, WordTypes wordTypes, GraphBuilderConfiguration.Plugins graphBuilderPlugins) { 99 this.providers = providers; 100 this.graph = graph; 101 this.wordTypes = wordTypes; 102 this.graphBuilderPlugins = graphBuilderPlugins; 103 this.lastFixedNode = graph.start(); 104 105 structures = new ArrayList<>(); 106 /* 107 * Add a dummy element, so that the access of the last element never leads to an exception. 108 */ 109 structures.add(new Structure() { 110 }); 111 } 112 113 @Override 114 public StructuredGraph getGraph() { 115 return graph; 116 } 117 118 @Override 119 public ConstantReflectionProvider getConstantReflection() { 120 return providers.getConstantReflection(); 121 } 122 123 @Override 124 public ConstantFieldProvider getConstantFieldProvider() { 125 return providers.getConstantFieldProvider(); 126 } 127 128 @Override 129 public MetaAccessProvider getMetaAccess() { 130 return providers.getMetaAccess(); 131 } 132 133 @Override 134 public StampProvider getStampProvider() { 135 return providers.getStampProvider(); 136 } 137 138 @Override 139 public boolean parsingIntrinsic() { 140 return true; 141 } 142 143 /** 144 * Ensures a floating node is added to or already present in the graph via {@link Graph#unique}. 145 * 146 * @return a node similar to {@code node} if one exists, otherwise {@code node} 147 */ 148 public <T extends FloatingNode & ValueNumberable> T unique(T node) { 149 return graph.unique(changeToWord(node)); 150 } 151 152 public <T extends ValueNode> T add(T node) { 153 return graph.add(changeToWord(node)); 154 } 155 156 public <T extends ValueNode> T changeToWord(T node) { 157 if (wordTypes != null && wordTypes.isWord(node)) { 158 node.setStamp(wordTypes.getWordStamp(StampTool.typeOrNull(node))); 159 } 160 return node; 161 } 162 163 @Override 164 public <T extends ValueNode> T append(T node) { 165 T result = graph.addOrUniqueWithInputs(changeToWord(node)); 166 if (result instanceof FixedNode) { 167 updateLastFixed((FixedNode) result); 168 } 169 return result; 170 } 171 172 private void updateLastFixed(FixedNode result) { 173 assert lastFixedNode != null; 174 assert result.predecessor() == null; 175 graph.addAfterFixed(lastFixedNode, result); 176 if (result instanceof FixedWithNextNode) { 177 lastFixedNode = (FixedWithNextNode) result; 178 } else { 179 lastFixedNode = null; 180 } 181 } 182 183 public InvokeNode createInvoke(Class<?> declaringClass, String name, ValueNode... args) { 184 return createInvoke(declaringClass, name, InvokeKind.Static, null, BytecodeFrame.UNKNOWN_BCI, args); 185 } 186 187 /** 188 * Creates and appends an {@link InvokeNode} for a call to a given method with a given set of 189 * arguments. The method is looked up via reflection based on the declaring class and name. 190 * 191 * @param declaringClass the class declaring the invoked method 192 * @param name the name of the invoked method 193 * @param args the arguments to the invocation 194 */ 195 public InvokeNode createInvoke(Class<?> declaringClass, String name, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args) { 196 boolean isStatic = invokeKind == InvokeKind.Static; 197 ResolvedJavaMethod method = findMethod(declaringClass, name, isStatic); 198 return createInvoke(method, invokeKind, frameStateBuilder, bci, args); 199 } 200 201 public ResolvedJavaMethod findMethod(Class<?> declaringClass, String name, boolean isStatic) { 202 ResolvedJavaMethod method = null; 203 for (Method m : declaringClass.getDeclaredMethods()) { 204 if (Modifier.isStatic(m.getModifiers()) == isStatic && m.getName().equals(name)) { 205 assert method == null : "found more than one method in " + declaringClass + " named " + name; 206 method = providers.getMetaAccess().lookupJavaMethod(m); 207 } 208 } 209 assert method != null : "did not find method in " + declaringClass + " named " + name; 210 return method; 211 } 212 213 public ResolvedJavaMethod findMethod(Class<?> declaringClass, String name, Class<?>... parameterTypes) { 214 try { 215 Method m = declaringClass.getDeclaredMethod(name, parameterTypes); 216 return providers.getMetaAccess().lookupJavaMethod(m); 217 } catch (NoSuchMethodException | SecurityException e) { 218 throw new AssertionError(e); 219 } 220 } 221 222 /** 223 * Creates and appends an {@link InvokeNode} for a call to a given method with a given set of 224 * arguments. 225 */ 226 public InvokeNode createInvoke(ResolvedJavaMethod method, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args) { 227 assert method.isStatic() == (invokeKind == InvokeKind.Static); 228 Signature signature = method.getSignature(); 229 JavaType returnType = signature.getReturnType(null); 230 assert checkArgs(method, args); 231 StampPair returnStamp = graphBuilderPlugins.getOverridingStamp(this, returnType, false); 232 if (returnStamp == null) { 233 returnStamp = StampFactory.forDeclaredType(graph.getAssumptions(), returnType, false); 234 } 235 MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, method, args, returnStamp, bci)); 236 InvokeNode invoke = append(new InvokeNode(callTarget, bci)); 237 238 if (frameStateBuilder != null) { 239 if (invoke.getStackKind() != JavaKind.Void) { 240 frameStateBuilder.push(returnType.getJavaKind(), invoke); 241 } 242 invoke.setStateAfter(frameStateBuilder.create(bci, invoke)); 243 if (invoke.getStackKind() != JavaKind.Void) { 244 frameStateBuilder.pop(returnType.getJavaKind()); 245 } 246 } 247 return invoke; 248 } 249 250 protected MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, StampPair returnStamp, @SuppressWarnings("unused") int bci) { 251 return new MethodCallTargetNode(invokeKind, targetMethod, args, returnStamp, null); 252 } 253 254 /** 255 * Determines if a given set of arguments is compatible with the signature of a given method. 256 * 257 * @return true if {@code args} are compatible with the signature if {@code method} 258 * @throws AssertionError if {@code args} are not compatible with the signature if 259 * {@code method} 260 */ 261 public boolean checkArgs(ResolvedJavaMethod method, ValueNode... args) { 262 Signature signature = method.getSignature(); 263 boolean isStatic = method.isStatic(); 264 if (signature.getParameterCount(!isStatic) != args.length) { 265 throw new AssertionError(graph + ": wrong number of arguments to " + method); 266 } 267 int argIndex = 0; 268 if (!isStatic) { 269 ResolvedJavaType expectedType = method.getDeclaringClass(); 270 JavaKind expected = wordTypes == null ? expectedType.getJavaKind() : wordTypes.asKind(expectedType); 271 JavaKind actual = args[argIndex++].stamp().getStackKind(); 272 assert expected == actual : graph + ": wrong kind of value for receiver argument of call to " + method + " [" + actual + " != " + expected + "]"; 273 } 274 for (int i = 0; i != signature.getParameterCount(false); i++) { 275 JavaType expectedType = signature.getParameterType(i, method.getDeclaringClass()); 276 JavaKind expected = wordTypes == null ? expectedType.getJavaKind().getStackKind() : wordTypes.asKind(expectedType).getStackKind(); 277 JavaKind actual = args[argIndex++].stamp().getStackKind(); 278 if (expected != actual) { 279 throw new AssertionError(graph + ": wrong kind of value for argument " + i + " of call to " + method + " [" + actual + " != " + expected + "]"); 280 } 281 } 282 return true; 283 } 284 285 /** 286 * Recursively {@linkplain #inline inlines} all invocations currently in the graph. 287 */ 288 public void inlineInvokes() { 289 while (!graph.getNodes().filter(InvokeNode.class).isEmpty()) { 290 for (InvokeNode invoke : graph.getNodes().filter(InvokeNode.class).snapshot()) { 291 inline(invoke); 292 } 293 } 294 295 // Clean up all code that is now dead after inlining. 296 new DeadCodeEliminationPhase().apply(graph); 297 } 298 299 /** 300 * Inlines a given invocation to a method. The graph of the inlined method is processed in the 301 * same manner as for snippets and method substitutions. 302 */ 303 public void inline(InvokeNode invoke) { 304 ResolvedJavaMethod method = ((MethodCallTargetNode) invoke.callTarget()).targetMethod(); 305 306 MetaAccessProvider metaAccess = providers.getMetaAccess(); 307 Plugins plugins = new Plugins(graphBuilderPlugins); 308 GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins); 309 310 StructuredGraph calleeGraph = new StructuredGraph.Builder(invoke.getOptions()).method(method).build(); 311 IntrinsicContext initialReplacementContext = new IntrinsicContext(method, method, providers.getReplacements().getDefaultReplacementBytecodeProvider(), INLINE_AFTER_PARSING); 312 GraphBuilderPhase.Instance instance = new GraphBuilderPhase.Instance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), providers.getConstantFieldProvider(), config, 313 OptimisticOptimizations.NONE, 314 initialReplacementContext); 315 instance.apply(calleeGraph); 316 317 // Remove all frame states from inlinee 318 calleeGraph.clearAllStateAfter(); 319 new DeadCodeEliminationPhase(Optionality.Required).apply(calleeGraph); 320 321 InliningUtil.inline(invoke, calleeGraph, false, method); 322 } 323 324 protected void pushStructure(Structure structure) { 325 structures.add(structure); 326 } 327 328 protected <T extends Structure> T getTopStructure(Class<T> expectedClass) { 329 return expectedClass.cast(structures.get(structures.size() - 1)); 330 } 331 332 protected void popStructure() { 333 structures.remove(structures.size() - 1); 334 } 335 336 protected enum IfState { 337 CONDITION, 338 THEN_PART, 339 ELSE_PART, 340 FINISHED 341 } 342 343 static class IfStructure extends Structure { 344 protected IfState state; 345 protected FixedNode thenPart; 346 protected FixedNode elsePart; 347 } 348 349 /** 350 * Starts an if-block. This call can be followed by a call to {@link #thenPart} to start 351 * emitting the code executed when the condition hold; and a call to {@link #elsePart} to start 352 * emititng the code when the condition does not hold. It must be followed by a call to 353 * {@link #endIf} to close the if-block. 354 * 355 * @param condition The condition for the if-block 356 * @param trueProbability The estimated probability the condition is true 357 */ 358 public void startIf(LogicNode condition, double trueProbability) { 359 AbstractBeginNode thenSuccessor = graph.add(new BeginNode()); 360 AbstractBeginNode elseSuccessor = graph.add(new BeginNode()); 361 append(new IfNode(condition, thenSuccessor, elseSuccessor, trueProbability)); 362 lastFixedNode = null; 363 364 IfStructure s = new IfStructure(); 365 s.state = IfState.CONDITION; 366 s.thenPart = thenSuccessor; 367 s.elsePart = elseSuccessor; 368 pushStructure(s); 369 } 370 371 private IfStructure saveLastIfNode() { 372 IfStructure s = getTopStructure(IfStructure.class); 373 switch (s.state) { 374 case CONDITION: 375 assert lastFixedNode == null; 376 break; 377 case THEN_PART: 378 s.thenPart = lastFixedNode; 379 break; 380 case ELSE_PART: 381 s.elsePart = lastFixedNode; 382 break; 383 case FINISHED: 384 assert false; 385 break; 386 } 387 lastFixedNode = null; 388 return s; 389 } 390 391 public void thenPart() { 392 IfStructure s = saveLastIfNode(); 393 lastFixedNode = (FixedWithNextNode) s.thenPart; 394 s.state = IfState.THEN_PART; 395 } 396 397 public void elsePart() { 398 IfStructure s = saveLastIfNode(); 399 lastFixedNode = (FixedWithNextNode) s.elsePart; 400 s.state = IfState.ELSE_PART; 401 } 402 403 public void endIf() { 404 IfStructure s = saveLastIfNode(); 405 406 FixedWithNextNode thenPart = s.thenPart instanceof FixedWithNextNode ? (FixedWithNextNode) s.thenPart : null; 407 FixedWithNextNode elsePart = s.elsePart instanceof FixedWithNextNode ? (FixedWithNextNode) s.elsePart : null; 408 409 if (thenPart != null && elsePart != null) { 410 /* Both parts are alive, we need a real merge. */ 411 EndNode thenEnd = graph.add(new EndNode()); 412 graph.addAfterFixed(thenPart, thenEnd); 413 EndNode elseEnd = graph.add(new EndNode()); 414 graph.addAfterFixed(elsePart, elseEnd); 415 416 AbstractMergeNode merge = graph.add(new MergeNode()); 417 merge.addForwardEnd(thenEnd); 418 merge.addForwardEnd(elseEnd); 419 420 lastFixedNode = merge; 421 422 } else if (thenPart != null) { 423 /* elsePart ended with a control sink, so we can continue with thenPart. */ 424 lastFixedNode = thenPart; 425 426 } else if (elsePart != null) { 427 /* thenPart ended with a control sink, so we can continue with elsePart. */ 428 lastFixedNode = elsePart; 429 430 } else { 431 /* Both parts ended with a control sink, so no nodes can be added after the if. */ 432 assert lastFixedNode == null; 433 } 434 s.state = IfState.FINISHED; 435 popStructure(); 436 } 437 438 static class InvokeWithExceptionStructure extends Structure { 439 protected enum State { 440 INVOKE, 441 NO_EXCEPTION_EDGE, 442 EXCEPTION_EDGE, 443 FINISHED 444 } 445 446 protected State state; 447 protected ExceptionObjectNode exceptionObject; 448 protected FixedNode noExceptionEdge; 449 protected FixedNode exceptionEdge; 450 } 451 452 public InvokeWithExceptionNode startInvokeWithException(ResolvedJavaMethod method, InvokeKind invokeKind, 453 FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci, ValueNode... args) { 454 455 assert method.isStatic() == (invokeKind == InvokeKind.Static); 456 Signature signature = method.getSignature(); 457 JavaType returnType = signature.getReturnType(null); 458 assert checkArgs(method, args); 459 StampPair returnStamp = graphBuilderPlugins.getOverridingStamp(this, returnType, false); 460 if (returnStamp == null) { 461 returnStamp = StampFactory.forDeclaredType(graph.getAssumptions(), returnType, false); 462 } 463 ExceptionObjectNode exceptionObject = add(new ExceptionObjectNode(getMetaAccess())); 464 if (frameStateBuilder != null) { 465 FrameStateBuilder exceptionState = frameStateBuilder.copy(); 466 exceptionState.clearStack(); 467 exceptionState.push(JavaKind.Object, exceptionObject); 468 exceptionState.setRethrowException(false); 469 exceptionObject.setStateAfter(exceptionState.create(exceptionEdgeBci, exceptionObject)); 470 } 471 MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, method, args, returnStamp, invokeBci)); 472 InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionObject, invokeBci)); 473 AbstractBeginNode noExceptionEdge = graph.add(KillingBeginNode.create(LocationIdentity.any())); 474 invoke.setNext(noExceptionEdge); 475 if (frameStateBuilder != null) { 476 if (invoke.getStackKind() != JavaKind.Void) { 477 frameStateBuilder.push(returnType.getJavaKind(), invoke); 478 } 479 invoke.setStateAfter(frameStateBuilder.create(invokeBci, invoke)); 480 if (invoke.getStackKind() != JavaKind.Void) { 481 frameStateBuilder.pop(returnType.getJavaKind()); 482 } 483 } 484 lastFixedNode = null; 485 486 InvokeWithExceptionStructure s = new InvokeWithExceptionStructure(); 487 s.state = InvokeWithExceptionStructure.State.INVOKE; 488 s.noExceptionEdge = noExceptionEdge; 489 s.exceptionEdge = exceptionObject; 490 s.exceptionObject = exceptionObject; 491 pushStructure(s); 492 493 return invoke; 494 } 495 496 private InvokeWithExceptionStructure saveLastInvokeWithExceptionNode() { 497 InvokeWithExceptionStructure s = getTopStructure(InvokeWithExceptionStructure.class); 498 switch (s.state) { 499 case INVOKE: 500 assert lastFixedNode == null; 501 break; 502 case NO_EXCEPTION_EDGE: 503 s.noExceptionEdge = lastFixedNode; 504 break; 505 case EXCEPTION_EDGE: 506 s.exceptionEdge = lastFixedNode; 507 break; 508 case FINISHED: 509 assert false; 510 break; 511 } 512 lastFixedNode = null; 513 return s; 514 } 515 516 public void noExceptionPart() { 517 InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode(); 518 lastFixedNode = (FixedWithNextNode) s.noExceptionEdge; 519 s.state = InvokeWithExceptionStructure.State.NO_EXCEPTION_EDGE; 520 } 521 522 public void exceptionPart() { 523 InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode(); 524 lastFixedNode = (FixedWithNextNode) s.exceptionEdge; 525 s.state = InvokeWithExceptionStructure.State.EXCEPTION_EDGE; 526 } 527 528 public ExceptionObjectNode exceptionObject() { 529 InvokeWithExceptionStructure s = getTopStructure(InvokeWithExceptionStructure.class); 530 return s.exceptionObject; 531 } 532 533 /** 534 * Finishes a control flow started with {@link #startInvokeWithException}. If necessary, creates 535 * a merge of the non-exception and exception edges. The merge node is returned and the 536 * non-exception edge is the first forward end of the merge, the exception edge is the second 537 * forward end (relevant for phi nodes). 538 */ 539 public AbstractMergeNode endInvokeWithException() { 540 InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode(); 541 FixedWithNextNode noExceptionEdge = s.noExceptionEdge instanceof FixedWithNextNode ? (FixedWithNextNode) s.noExceptionEdge : null; 542 FixedWithNextNode exceptionEdge = s.exceptionEdge instanceof FixedWithNextNode ? (FixedWithNextNode) s.exceptionEdge : null; 543 AbstractMergeNode merge = null; 544 if (noExceptionEdge != null && exceptionEdge != null) { 545 EndNode noExceptionEnd = graph.add(new EndNode()); 546 graph.addAfterFixed(noExceptionEdge, noExceptionEnd); 547 EndNode exceptionEnd = graph.add(new EndNode()); 548 graph.addAfterFixed(exceptionEdge, exceptionEnd); 549 merge = graph.add(new MergeNode()); 550 merge.addForwardEnd(noExceptionEnd); 551 merge.addForwardEnd(exceptionEnd); 552 lastFixedNode = merge; 553 } else if (noExceptionEdge != null) { 554 lastFixedNode = noExceptionEdge; 555 } else if (exceptionEdge != null) { 556 lastFixedNode = exceptionEdge; 557 } else { 558 assert lastFixedNode == null; 559 } 560 s.state = InvokeWithExceptionStructure.State.FINISHED; 561 popStructure(); 562 return merge; 563 } 564 }