Skip to content

Commit d6580ba

Browse files
Validation: Fix br_table branch copy check
1 parent 4f2312b commit d6580ba

File tree

2 files changed

+56
-40
lines changed

2 files changed

+56
-40
lines changed

Sources/WasmKit/Translator.swift

Lines changed: 54 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,10 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
470470
return makeValueSource(self.values[height - 1 - depth])
471471
}
472472

473+
func peekType(depth: Int) -> MetaValue {
474+
return self.values[height - 1 - depth].type
475+
}
476+
473477
private func makeValueSource(_ value: MetaValueOnStack) -> ValueSource {
474478
let source: ValueSource
475479
switch value {
@@ -987,17 +991,29 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
987991
case .local(let localIndex):
988992
// Re-push local variables to the stack
989993
_ = try valueStack.pushLocal(localIndex, locals: &locals)
990-
case .vreg:
994+
case .vreg, nil:
991995
_ = valueStack.push(type)
992-
case nil:
993-
_ = valueStack.push(.unknown)
994996
case .const(let index, let type):
995997
valueStack.pushConst(index, type: type)
996998
}
997999
}
9981000
return stackHeight
9991001
}
10001002

1003+
private func checkStackTop(_ valueTypes: [ValueType]) throws {
1004+
for (stackDepth, type) in valueTypes.reversed().enumerated() {
1005+
guard try checkBeforePop(typeHint: type) else { return }
1006+
let actual = valueStack.peekType(depth: stackDepth)
1007+
switch actual {
1008+
case .some(let actualType):
1009+
guard actualType == type else {
1010+
throw ValidationError("Expected \(type) on the stack top but got \(actualType)")
1011+
}
1012+
case .unknown: break
1013+
}
1014+
}
1015+
}
1016+
10011017
private mutating func visitReturnLike() throws {
10021018
var copies: [(source: VReg, dest: VReg)] = []
10031019
for (index, resultType) in self.type.results.enumerated().reversed() {
@@ -1308,10 +1324,11 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
13081324
}
13091325

13101326
mutating func visitBrIf(relativeDepth: UInt32) throws -> Output {
1311-
guard let condition = try popVRegOperand(.i32) else { return }
1312-
13131327
let frame = try controlStack.branchTarget(relativeDepth: relativeDepth)
1328+
let condition = try popVRegOperand(.i32)
1329+
13141330
if frame.copyCount == 0 {
1331+
guard let condition else { return }
13151332
// Optimization where we don't need copying values when the branch taken
13161333
iseqBuilder.emitWithLabel(Instruction.brIf, frame.continuation) { _, selfPC, continuation in
13171334
let relativeOffset = continuation.offsetFromHead - selfPC.offsetFromHead
@@ -1323,37 +1340,39 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
13231340
}
13241341
preserveOnStack(depth: valueStack.height - frame.stackHeight)
13251342

1326-
// If branch taken, fallthrough to landing pad, copy stack values
1327-
// then branch to the actual place
1328-
// If branch not taken, branch to the next of the landing pad
1329-
//
1330-
// (block (result i32)
1331-
// (i32.const 42)
1332-
// (i32.const 24)
1333-
// (local.get 0)
1334-
// (br_if 0) ------+
1335-
// (local.get 1) |
1336-
// ) <-------+
1337-
//
1338-
// [0x00] (i32.const 42 reg:0)
1339-
// [0x01] (i32.const 24 reg:1)
1340-
// [0x02] (local.get 0 result=reg:2)
1341-
// [0x03] (br_if_z offset=+0x3 cond=reg:2) --+
1342-
// [0x04] (stack.copy reg:1 -> reg:0) |
1343-
// [0x05] (br offset=+0x2) --------+ |
1344-
// [0x06] (local.get 1 reg:2) <----|---------+
1345-
// [0x07] ... <-------+
1346-
let onBranchNotTaken = iseqBuilder.allocLabel()
1347-
iseqBuilder.emitWithLabel(Instruction.brIfNot, onBranchNotTaken) { _, conditionCheckAt, continuation in
1348-
let relativeOffset = continuation.offsetFromHead - conditionCheckAt.offsetFromHead
1349-
return Instruction.BrIfOperand(condition: LVReg(condition), offset: Int32(relativeOffset))
1350-
}
1351-
try copyOnBranch(targetFrame: frame)
1352-
try emitBranch(Instruction.br, relativeDepth: relativeDepth) { offset, copyCount, popCount in
1353-
return offset
1343+
if let condition {
1344+
// If branch taken, fallthrough to landing pad, copy stack values
1345+
// then branch to the actual place
1346+
// If branch not taken, branch to the next of the landing pad
1347+
//
1348+
// (block (result i32)
1349+
// (i32.const 42)
1350+
// (i32.const 24)
1351+
// (local.get 0)
1352+
// (br_if 0) ------+
1353+
// (local.get 1) |
1354+
// ) <-------+
1355+
//
1356+
// [0x00] (i32.const 42 reg:0)
1357+
// [0x01] (i32.const 24 reg:1)
1358+
// [0x02] (local.get 0 result=reg:2)
1359+
// [0x03] (br_if_z offset=+0x3 cond=reg:2) --+
1360+
// [0x04] (stack.copy reg:1 -> reg:0) |
1361+
// [0x05] (br offset=+0x2) --------+ |
1362+
// [0x06] (local.get 1 reg:2) <----|---------+
1363+
// [0x07] ... <-------+
1364+
let onBranchNotTaken = iseqBuilder.allocLabel()
1365+
iseqBuilder.emitWithLabel(Instruction.brIfNot, onBranchNotTaken) { _, conditionCheckAt, continuation in
1366+
let relativeOffset = continuation.offsetFromHead - conditionCheckAt.offsetFromHead
1367+
return Instruction.BrIfOperand(condition: LVReg(condition), offset: Int32(relativeOffset))
1368+
}
1369+
try copyOnBranch(targetFrame: frame)
1370+
try emitBranch(Instruction.br, relativeDepth: relativeDepth) { offset, copyCount, popCount in
1371+
return offset
1372+
}
1373+
try iseqBuilder.pinLabelHere(onBranchNotTaken)
13541374
}
13551375
try popPushValues(frame.copyTypes)
1356-
try iseqBuilder.pinLabelHere(onBranchNotTaken)
13571376
}
13581377

13591378
mutating func visitBrTable(targets: WasmParser.BrTable) throws -> Output {
@@ -1413,7 +1432,7 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
14131432
guard frame.copyTypes.count == defaultFrame.copyTypes.count else {
14141433
throw ValidationError("Expected the same copy types for all branches in `br_table` but got \(frame.copyTypes) and \(defaultFrame.copyTypes)")
14151434
}
1416-
try popPushValues(frame.copyTypes)
1435+
try checkStackTop(frame.copyTypes)
14171436

14181437
do {
14191438
let relativeOffset = iseqBuilder.insertingPC.offsetFromHead - brTableAt.offsetFromHead

Tests/WasmKitTests/SpectestTests.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,9 @@ final class SpectestTests: XCTestCase {
2020
let defaultConfig = EngineConfiguration()
2121
let ok = try await spectest(
2222
path: Self.testPaths,
23-
include: [],
23+
include: [
24+
],
2425
exclude: [
25-
"table_get.wast",
26-
"table_grow.wast",
27-
"table_size.wast",
28-
"unreached-invalid.wast",
2926
],
3027
parallel: true,
3128
configuration: defaultConfig

0 commit comments

Comments
 (0)