Skip to content

Commit 0d44229

Browse files
committed
Make Validator work with TryTable and blocks with return types
1 parent d6d1dbe commit 0d44229

File tree

3 files changed

+135
-25
lines changed

3 files changed

+135
-25
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,11 @@ public static Const forWord(WordBase word) {
609609
// TODO GR-42105 Use forInt
610610
return forLong(word.rawValue());
611611
}
612+
613+
@Override
614+
protected String toInnerString() {
615+
return literal.type + ", " + literal.asText();
616+
}
612617
}
613618

614619
/**

web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/wasm/ast/visitors/WasmValidator.java

Lines changed: 126 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,24 @@
7878
public class WasmValidator extends WasmVisitor {
7979
static class CtrlFrame {
8080
final WasmId.Label label;
81+
final WasmValType returnType;
82+
/**
83+
* Whether the end of this frame (block) is unreachable. There might still be reachable code
84+
* in the frame, but if this is true, execution can never reach the end of the block.
85+
*/
8186
boolean unreachable = false;
87+
/**
88+
* Whether any instructions target this block.
89+
*/
90+
boolean targeted = false;
8291

83-
CtrlFrame(WasmId.Label label) {
92+
CtrlFrame(WasmId.Label label, WasmValType returnType) {
8493
this.label = label;
94+
this.returnType = returnType;
95+
}
96+
97+
public WasmValType getReturnType() {
98+
return returnType;
8599
}
86100
}
87101

@@ -343,21 +357,30 @@ private void errorIf(boolean condition, String msg) {
343357
}
344358
}
345359

346-
private static boolean assertIdsEqual(WasmId first, WasmId second) {
360+
private static boolean idsEqual(WasmId first, WasmId second) {
347361
return Objects.equals(first, second);
348362
}
349363

350364
private CtrlFrame topFrame() {
351365
return Objects.requireNonNull(ctrls.peek());
352366
}
353367

368+
/**
369+
* Whether the current frame is marked as unreachable.
370+
*
371+
* @see #markUnreachable()
372+
*/
373+
private boolean isUnreachable() {
374+
return topFrame().unreachable;
375+
}
376+
354377
private void pushVal(WasmValType t) {
378+
errorIf(isUnreachable(), "Tried to push " + t + " when unreachable");
355379
vals.push(t);
356380
}
357381

358382
private WasmValType popVal() {
359-
CtrlFrame top = topFrame();
360-
if (vals.isEmpty() && top.unreachable) {
383+
if (isUnreachable()) {
361384
return null;
362385
}
363386

@@ -383,6 +406,10 @@ private RuntimeException typeMismatch(WasmValType[] expected, Deque<WasmValType>
383406
* @param expected The top of the stack must match this (the last element is at the very top).
384407
*/
385408
private void popVals(WasmValType... expected) {
409+
if (isUnreachable()) {
410+
return;
411+
}
412+
386413
int numTypes = expected.length;
387414
if (vals.size() < numTypes) {
388415
throw typeMismatch(expected);
@@ -416,12 +443,20 @@ private Global getAndAssertGlobalExists(WasmId.Global global) {
416443
}
417444

418445
private void pushCtrl(WasmId.Label label) {
446+
pushCtrl(label, null);
447+
}
448+
449+
private void pushCtrl(WasmId.Label label, WasmValType resultType) {
419450
assertStackEmpty();
420451
if (label != null) {
421452
// No name conflicts.
422453
assertIdUniqueName(label, ctrls.stream().map(frame -> frame.label).filter(Objects::nonNull).collect(Collectors.toList()));
423454
}
424-
ctrls.push(new CtrlFrame(label));
455+
ctrls.push(new CtrlFrame(label, resultType));
456+
}
457+
458+
private void pushBlockCtrl(Instruction.WasmBlock block) {
459+
pushCtrl(block.getLabel(), block.getResult());
425460
}
426461

427462
private void popCtrl(WasmId.Label expectedLabel) {
@@ -430,6 +465,15 @@ private void popCtrl(WasmId.Label expectedLabel) {
430465
assertStackEmpty();
431466
errorIf(frame.label != expectedLabel, "Expected control frame " + expectedLabel + " but got " + frame.label);
432467
ctrls.pop();
468+
/*
469+
* If the end of the frame was unreachable and no instruction targeted it, it means that it
470+
* is impossible to reach the instruction right after it and we have to mark the outer block
471+
* as unreachable as well.
472+
*/
473+
if (!frame.targeted && frame.unreachable && !ctrls.isEmpty()) {
474+
markUnreachable();
475+
}
476+
433477
}
434478

435479
private void popBlockCtrl(Instruction.WasmBlock block) {
@@ -442,7 +486,16 @@ private void popBlockCtrl(Instruction.WasmBlock block) {
442486
}
443487
}
444488

445-
private void unreachable() {
489+
/**
490+
* Marks the top frame as unreachable.
491+
* <p>
492+
* An unreachable frame satisfies all requirements on the operand stack (i.e.
493+
* {@link #popVals(WasmValType...) never errors.}).
494+
* <p>
495+
* Call this method after visiting any instruction at which execution stops and never returns
496+
* (e.g. {@code br}).
497+
*/
498+
private void markUnreachable() {
446499
CtrlFrame top = topFrame();
447500
vals.clear();
448501
top.unreachable = true;
@@ -461,8 +514,15 @@ private void applyTypeUse(TypeUse typeUse) {
461514
typeUse.results.forEach(this::pushVal);
462515
}
463516

464-
private void assertLabelExists(WasmId.Label label) {
465-
errorIf(ctrls.stream().noneMatch(frame -> assertIdsEqual(label, frame.label)), "Label " + label + " does not exist.");
517+
private CtrlFrame markLabelTargeted(WasmId.Label label) {
518+
CtrlFrame frame = ctrls.stream().filter(f -> idsEqual(label, f.label)).findFirst().orElseThrow(() -> error("Label " + label + " does not exist."));
519+
frame.targeted = true;
520+
return frame;
521+
}
522+
523+
private void markLabelTargetedWithReturnType(WasmId.Label label, WasmValType returnType) {
524+
CtrlFrame frame = markLabelTargeted(label);
525+
errorIf(!Objects.equals(frame.getReturnType(), returnType), "Label " + label + " has return type " + frame.getReturnType() + " but " + returnType + " was expected");
466526
}
467527

468528
/**
@@ -576,14 +636,14 @@ public void visitInstruction(Instruction inst) {
576636

577637
@Override
578638
public void visitBlock(Instruction.Block block) {
579-
pushCtrl(block.getLabel());
639+
pushBlockCtrl(block);
580640
super.visitBlock(block);
581641
popBlockCtrl(block);
582642
}
583643

584644
@Override
585645
public void visitLoop(Instruction.Loop loop) {
586-
pushCtrl(loop.getLabel());
646+
pushBlockCtrl(loop);
587647
super.visitLoop(loop);
588648
popBlockCtrl(loop);
589649
}
@@ -592,19 +652,63 @@ public void visitLoop(Instruction.Loop loop) {
592652
public void visitIf(Instruction.If ifBlock) {
593653
visitInstruction(ifBlock.condition);
594654
popVals(i32);
595-
pushCtrl(ifBlock.getLabel());
655+
pushBlockCtrl(ifBlock);
656+
657+
/*
658+
* Propagating the information about unreachability upward here requires some more work. We
659+
* only want to mark the end of the if-block as unreachable if both the end of the then- and
660+
* else-branches are unreachable.
661+
*/
662+
boolean thenBlockUnreachable = false;
663+
boolean elseBlockUnreachable = false;
664+
665+
// Control frame around both branches to intercept the unreachable state
666+
pushCtrl(null);
667+
668+
// Control frame around the then-branch
669+
pushCtrl(null);
596670
visitInstructions(ifBlock.thenInstructions);
597-
popBlockCtrl(ifBlock);
671+
if (topFrame().unreachable) {
672+
thenBlockUnreachable = true;
673+
topFrame().unreachable = false;
674+
}
675+
popCtrl(null);
676+
598677
if (ifBlock.hasElse()) {
599-
pushCtrl(ifBlock.getLabel());
678+
pushCtrl(null);
600679
visitInstructions(ifBlock.elseInstructions);
601-
popBlockCtrl(ifBlock);
680+
if (topFrame().unreachable) {
681+
elseBlockUnreachable = true;
682+
topFrame().unreachable = false;
683+
}
684+
popCtrl(null);
685+
}
686+
if (thenBlockUnreachable && elseBlockUnreachable) {
687+
// This will mark the parent block as unreachable once we pop this control frame.
688+
markUnreachable();
602689
}
690+
popCtrl(null);
691+
popBlockCtrl(ifBlock);
692+
}
693+
694+
@Override
695+
public void visitTryTable(Instruction.TryTable tryBlock) {
696+
tryBlock.catchBlocks.forEach(this::assertCatchValid);
697+
pushBlockCtrl(tryBlock);
698+
visitInstructions(tryBlock.instructions);
699+
popBlockCtrl(tryBlock);
700+
}
701+
702+
private void assertCatchValid(Instruction.TryTable.Catch catchClause) {
703+
errorIf(!ctxt.hasTag(catchClause.tag), "No matching tag for catch clause: " + catchClause);
704+
List<WasmValType> catchParams = catchClause.tag.typeUse.params;
705+
errorIf(catchParams.size() != 1, "Can only support catch clause tags with a single param, got" + catchParams.size());
706+
markLabelTargetedWithReturnType(catchClause.label, catchParams.getFirst());
603707
}
604708

605709
@Override
606710
public void visitTry(Instruction.Try tryBlock) {
607-
pushCtrl(tryBlock.getLabel());
711+
pushBlockCtrl(tryBlock);
608712
visitInstructions(tryBlock.instructions);
609713

610714
for (Instruction.Try.Catch catchBlock : tryBlock.catchBlocks) {
@@ -620,7 +724,7 @@ public void visitTry(Instruction.Try tryBlock) {
620724

621725
@Override
622726
public void visitUnreachable(Instruction.Unreachable unreachable) {
623-
unreachable();
727+
markUnreachable();
624728
super.visitUnreachable(unreachable);
625729
}
626730

@@ -642,10 +746,10 @@ public void visitBreak(Instruction.Break inst) {
642746

643747
WasmId.Label targetLabel = inst.getTarget();
644748

645-
assertLabelExists(targetLabel);
749+
markLabelTargeted(targetLabel);
646750

647751
if (inst.condition == null) {
648-
unreachable();
752+
markUnreachable();
649753
} else {
650754
popVals(i32);
651755
}
@@ -658,13 +762,13 @@ public void visitBreakTable(Instruction.BreakTable inst) {
658762
popVals(i32);
659763

660764
WasmId.Label defaultLabel = inst.getDefaultTarget();
661-
assertLabelExists(defaultLabel);
765+
markLabelTargeted(defaultLabel);
662766

663767
for (int i = 0; i < inst.numTargets(); i++) {
664-
assertLabelExists(inst.getTarget(i));
768+
markLabelTargeted(inst.getTarget(i));
665769
}
666770

667-
unreachable();
771+
markUnreachable();
668772
}
669773

670774
@Override
@@ -753,6 +857,7 @@ public void visitThrow(Instruction.Throw inst) {
753857

754858
errorIf(!ctxt.hasTag(inst.tag), "No matching tag for throw: " + inst);
755859
applyTypeUse(inst.tag.typeUse);
860+
markUnreachable();
756861
}
757862

758863
@Override

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,6 @@ protected void emitCode() {
184184
module.constructActiveDataSegments();
185185
((WebImageWasmHeapBreakdownProvider) HeapBreakdownProvider.singleton()).setActualTotalHeapSize((int) getFullImageHeapSize());
186186

187-
if (WebImageOptions.DebugOptions.VerificationPhases.getValue(options)) {
188-
validateModule();
189-
}
190-
191187
emitJSCode();
192188

193189
try (Writer writer = Files.newBufferedWriter(watFile)) {
@@ -196,6 +192,10 @@ protected void emitCode() {
196192
throw new RuntimeException(e);
197193
}
198194

195+
if (WebImageOptions.DebugOptions.VerificationPhases.getValue(options)) {
196+
validateModule();
197+
}
198+
199199
assembleWasmFile(watFile, wasmFile);
200200
}
201201

0 commit comments

Comments
 (0)