Skip to content

Commit 656ece2

Browse files
committed
Implement try_table based exception handling
This is now the default
1 parent fa53259 commit 656ece2

File tree

7 files changed

+137
-15
lines changed

7 files changed

+137
-15
lines changed

web-image/mx.web-image/mx_web_image.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"web-image:WEBIMAGE_CLOSURE_SUPPORT",
6464
"web-image:WEBIMAGE_GOOGLE_CLOSURE",
6565
]
66-
# Hosted options defined in the web-image-enterprise suite
66+
# Hosted options defined in the web-image suite
6767
# This list has to be kept in sync with the code (the 'webimageoptions' gate tag checks this)
6868
# See also WebImageConfiguration.hosted_options
6969
web_image_hosted_options = [
@@ -92,6 +92,7 @@
9292
"GrowthTriggerThreshold=",
9393
"HeapGrowthFactor=",
9494
"ImageHeapObjectsPerFunction=",
95+
"LegacyExceptions",
9596
"JSComments=",
9697
"JSRuntime=",
9798
"LogFilter=",

web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/NativeImageWasmGeneratorRunner.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
import com.oracle.svm.hosted.webimage.options.WebImageOptions.CompilerBackend;
5555
import com.oracle.svm.hosted.webimage.util.BenchmarkLogger;
5656
import com.oracle.svm.hosted.webimage.wasm.WebImageWasmLMJavaMainSupport;
57-
import com.oracle.svm.hosted.webimage.wasm.codegen.BinaryenCompat;
5857
import com.oracle.svm.hosted.webimage.wasmgc.WebImageWasmGCJavaMainSupport;
5958
import com.oracle.svm.webimage.WebImageJSJavaMainSupport;
6059
import com.oracle.svm.webimage.WebImageJavaMainSupport;
@@ -167,11 +166,6 @@ public int build(ImageClassLoader classLoader) {
167166
// For the Wasm backends, turn off closure compiler
168167
optionProvider.getHostedValues().put(WebImageOptions.ClosureCompiler, false);
169168

170-
if (backend == CompilerBackend.WASMGC && !optionProvider.getHostedValues().containsKey(BinaryenCompat.Options.UseBinaryen)) {
171-
// For WasmGC backend, use binaryen by default
172-
optionProvider.getHostedValues().put(BinaryenCompat.Options.UseBinaryen, true);
173-
}
174-
175169
if (!optionProvider.getHostedValues().containsKey(WebImageOptions.NamingConvention)) {
176170
// The naming convention does not affect the binary image (unless debug information
177171
// is embedded) and the REDUCED mode makes the text file a lot easier to read

web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/codegen/reconstruction/stackifier/StackifierData.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ public class StackifierData implements ReconstructionData {
5858
*/
5959
private HIRBlock[] blocks;
6060

61+
private ControlFlowGraph cfg;
62+
6163
/**
6264
* Mapping from a basic block to its index in {@link #blocks}.
6365
*/
@@ -92,6 +94,10 @@ public HIRBlock[] getBlocks() {
9294
return blocks;
9395
}
9496

97+
public ControlFlowGraph getCfg() {
98+
return cfg;
99+
}
100+
95101
public EconomicMap<HIRBlock, Scope> getEnclosingScope() {
96102
return enclosingScope;
97103
}
@@ -134,6 +140,7 @@ public void setLabeledBlockEnd(EconomicMap<HIRBlock, LabeledBlock> labeledBlockE
134140

135141
public void setSortedBlocks(HIRBlock[] sortedBlocks, ControlFlowGraph cfg) {
136142
this.blocks = sortedBlocks;
143+
this.cfg = cfg;
137144
this.blockIndexSortOrder = new BlockMap<>(cfg);
138145
for (int i = 0; i < sortedBlocks.length; ++i) {
139146
this.blockIndexSortOrder.put(sortedBlocks[i], i);

web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/WebImageWasmOptions.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ public class WebImageWasmOptions {
4949
"Has no effect on code size, the binary format does not have comments.")//
5050
public static final EnumOptionKey<CommentVerbosity> WasmComments = new EnumOptionKey<>(CommentVerbosity.NORMAL);
5151

52+
@Option(help = "Enable the legacy exception proposal using try-catch instead of try_table") //
53+
public static final HostedOptionKey<Boolean> LegacyExceptions = new HostedOptionKey<>(false);
54+
5255
@Option(help = "Assemble the Wasm binary file with debug names.")//
5356
public static final HostedOptionKey<Boolean> DebugNames = new HostedOptionKey<>(false) {
5457

web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/codegen/BinaryenCompat.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,17 @@
2727

2828
import java.util.Objects;
2929

30+
import org.graalvm.collections.UnmodifiableEconomicMap;
31+
3032
import com.oracle.svm.core.option.HostedOptionKey;
33+
import com.oracle.svm.hosted.webimage.options.WebImageOptions;
34+
import com.oracle.svm.hosted.webimage.wasm.WebImageWasmOptions;
3135
import com.oracle.svm.hosted.webimage.wasm.ast.visitors.WasmPrinter;
3236
import com.oracle.svm.webimage.wasm.types.WasmValType;
3337

3438
import jdk.graal.compiler.options.Option;
39+
import jdk.graal.compiler.options.OptionKey;
40+
import jdk.graal.compiler.options.OptionValues;
3541
import jdk.vm.ci.code.site.Reference;
3642

3743
/**
@@ -43,7 +49,23 @@ public class BinaryenCompat {
4349

4450
public static class Options {
4551
@Option(help = "Use Binaryen (wasm-as) to assemble the final Wasm binary")//
46-
public static final HostedOptionKey<Boolean> UseBinaryen = new HostedOptionKey<>(false);
52+
public static final HostedOptionKey<Boolean> UseBinaryen = new HostedOptionKey<>(false) {
53+
@Override
54+
public Boolean getValue(OptionValues values) {
55+
assert checkDescriptorExists();
56+
return getValueOrDefault(values.getMap());
57+
}
58+
59+
@Override
60+
public Boolean getValueOrDefault(UnmodifiableEconomicMap<OptionKey<?>, Object> values) {
61+
if (values.containsKey(this)) {
62+
return (Boolean) values.get(this);
63+
}
64+
65+
// Binaryen is the default for WasmGC or when the new exception handling is used.
66+
return WebImageOptions.getBackend() == WebImageOptions.CompilerBackend.WASMGC || !WebImageWasmOptions.LegacyExceptions.getValueOrDefault(values);
67+
}
68+
};
4769
}
4870

4971
public static boolean usesBinaryen() {

web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/codegen/WasmIRWalker.java

Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import java.util.Map;
3636
import java.util.stream.Stream;
3737

38+
import com.oracle.svm.hosted.webimage.wasm.WebImageWasmOptions;
39+
import com.oracle.svm.hosted.webimage.wasm.ast.id.WebImageWasmIds;
3840
import org.graalvm.collections.Pair;
3941

4042
import com.oracle.svm.core.graal.nodes.ReadExceptionObjectNode;
@@ -184,13 +186,16 @@ protected void predeclareVariables(StructuredGraph graph) {
184186
*
185187
* @param currentBlock block from which to jump from
186188
* @param successor target of the jump
189+
* @return Whether a jump was generated
187190
*/
188-
private void generateForwardJump(HIRBlock currentBlock, HIRBlock successor) {
191+
private boolean generateForwardJump(HIRBlock currentBlock, HIRBlock successor) {
189192
WasmId.Label id = getForwardJumpTarget(currentBlock, successor);
190193

191194
if (id != null) {
192195
masm.genInst(new Break(id), "forward jump");
196+
return true;
193197
}
198+
return false;
194199
}
195200

196201
private WasmId.Label getForwardJumpTarget(HIRBlock currentBlock, HIRBlock successor) {
@@ -278,6 +283,85 @@ protected void lowerLoopEnd(LoopEndNode loopEnd) {
278283
/**
279284
* Lower a WithExceptionNode.
280285
*
286+
* @param currentBlock basic block that ends with {@link WithExceptionNode}
287+
* @param lastNode the {@link WithExceptionNode}
288+
* @see #lowerWithExceptionExnRef(HIRBlock, WithExceptionNode)
289+
* @see #lowerWithExceptionLegacy(HIRBlock, WithExceptionNode)
290+
*/
291+
@Override
292+
protected void lowerWithException(HIRBlock currentBlock, WithExceptionNode lastNode) {
293+
assert currentBlock.getEndNode() == lastNode : currentBlock.toString(Verbosity.Name);
294+
if (WebImageWasmOptions.LegacyExceptions.getValue()) {
295+
lowerWithExceptionLegacy(currentBlock, lastNode);
296+
} else {
297+
lowerWithExceptionExnRef(currentBlock, lastNode);
298+
}
299+
}
300+
301+
/**
302+
* Lower a WithExceptionNode using the exnref exception handling proposal.
303+
*
304+
* <pre>
305+
* {@code
306+
* (block $exnBlock (result $throwable)
307+
* (try_table (catch $exc_tag $exnBlock)
308+
* WithExceptionNode();
309+
* br Successor
310+
* )
311+
* )
312+
* (local.set $exc_var)
313+
* ExceptionEdge();
314+
* }
315+
* </pre>
316+
*
317+
* The {@link WithExceptionNode} is wrapped in a {@code try_table} block, which is surrounded by
318+
* a block that's the target of the catch scope. After the block around the {@code try_table}
319+
* instruction, the thrown value is already on the stack, we store it in a dedicated local
320+
* variable ({@link WebImageWasmNodeLowerer#exceptionObjectVariable}) so that it can be read
321+
* later by {@link ReadExceptionObjectNode}. The
322+
* {@link com.oracle.svm.hosted.webimage.wasm.phases.WasmLabeledBlockGeneration} ensures that
323+
* the regular successor always requires a forward jump.
324+
*
325+
* @see #lowerWithException(HIRBlock, WithExceptionNode)
326+
*/
327+
protected void lowerWithExceptionExnRef(HIRBlock currentBlock, WithExceptionNode lastNode) {
328+
WebImageWasmIds.InternalLabel exceptionHandlerLabel = masm.idFactory.newInternalLabel("exn" + currentBlock.getId());
329+
Instruction.Block exceptionTargetBlock = new Instruction.Block(exceptionHandlerLabel, masm.getWasmProviders().util().getThrowableType());
330+
masm.genInst(exceptionTargetBlock);
331+
332+
masm.childScope(exceptionTargetBlock.instructions, exceptionTargetBlock);
333+
Instruction.TryTable tryBlock = new Instruction.TryTable(null);
334+
tryBlock.addCatch(masm.getKnownIds().getJavaThrowableTag(), exceptionHandlerLabel);
335+
masm.genInst(tryBlock, lastNode);
336+
337+
masm.childScope(tryBlock.instructions, tryBlock);
338+
lowerNode(lastNode);
339+
HIRBlock normSucc = cfg.blockFor(lastNode.next());
340+
boolean didJump = generateForwardJump(currentBlock, normSucc);
341+
GraalError.guarantee(didJump, "No jump was inserted after a WithExceptionNode");
342+
masm.parentScope(tryBlock);
343+
344+
masm.parentScope(exceptionTargetBlock);
345+
346+
masm.genInst(masm.nodeLowerer().exceptionObjectVariable.setter(new Instruction.Nop()), "Store exception object");
347+
masm.lowerCatchPreamble();
348+
349+
CatchScopeContainer scopeEntry = (CatchScopeContainer) stackifierData.getScopeEntry(lastNode);
350+
Scope catchScope = scopeEntry.getCatchScope();
351+
if (catchScope != null) {
352+
lowerBlocks(catchScope.getSortedBlocks(stackifierData));
353+
// Just a sanity check
354+
masm.genInst(new Unreachable(), "End of catch block is unreachable, it must break out");
355+
} else {
356+
HIRBlock excpSucc = cfg.blockFor(lastNode.exceptionEdge());
357+
boolean didJumpAfterCatch = generateForwardJump(currentBlock, excpSucc);
358+
GraalError.guarantee(didJumpAfterCatch, "No jump was inserted in catch block");
359+
}
360+
}
361+
362+
/**
363+
* Lower a WithExceptionNode using the legacy exception handling proposal.
364+
*
281365
* <pre>
282366
* {@code
283367
* (try
@@ -301,13 +385,9 @@ protected void lowerLoopEnd(LoopEndNode loopEnd) {
301385
* ({@link WebImageWasmNodeLowerer#exceptionObjectVariable}) so that it can be read later by
302386
* {@link ReadExceptionObjectNode}.
303387
*
304-
* @param currentBlock basic block that ends with {@link WithExceptionNode}
305-
* @param lastNode the {@link WithExceptionNode}
388+
* @see #lowerWithException(HIRBlock, WithExceptionNode)
306389
*/
307-
@Override
308-
protected void lowerWithException(HIRBlock currentBlock, WithExceptionNode lastNode) {
309-
assert currentBlock.getEndNode() == lastNode : currentBlock.toString(Verbosity.Name);
310-
390+
protected void lowerWithExceptionLegacy(HIRBlock currentBlock, WithExceptionNode lastNode) {
311391
Instruction.Try tryBlock = new Instruction.Try(null);
312392
masm.genInst(tryBlock, lastNode);
313393

web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/phases/WasmLabeledBlockGeneration.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import com.oracle.svm.hosted.webimage.codegen.reconstruction.stackifier.LabeledBlockGeneration;
2929
import com.oracle.svm.hosted.webimage.codegen.reconstruction.stackifier.StackifierData;
3030

31+
import com.oracle.svm.hosted.webimage.wasm.WebImageWasmOptions;
32+
import jdk.graal.compiler.nodes.WithExceptionNode;
3133
import jdk.graal.compiler.nodes.cfg.ControlFlowGraph;
3234
import jdk.graal.compiler.nodes.cfg.HIRBlock;
3335

@@ -53,6 +55,19 @@ public boolean isLabeledBlockNeeded(HIRBlock block, HIRBlock successor) {
5355
return true;
5456
}
5557

58+
if (!WebImageWasmOptions.LegacyExceptions.getValue() && block.getEndNode() instanceof WithExceptionNode withExceptionNode) {
59+
HIRBlock normSucc = stackifierData.getCfg().blockFor(withExceptionNode.next());
60+
if (normSucc.equals(successor)) {
61+
/*
62+
* With the new exception handling, we need an explicit labeled block when going
63+
* from the WithExceptionNode to its regular successor because in the Wasm code, the
64+
* successor does not appear directly after, the catch block does, and we would then
65+
* fall through to that.
66+
*/
67+
return true;
68+
}
69+
}
70+
5671
return false;
5772
}
5873
}

0 commit comments

Comments
 (0)