Few unreachable/unwind fixes

This commit is contained in:
Chad Retz 2017-04-06 15:54:46 -05:00
parent e87b96752b
commit 004c0c4618
3 changed files with 47 additions and 36 deletions

View File

@ -454,28 +454,33 @@ open class FuncBuilder {
java.lang.Double::class.invokeStatic("longBitsToDouble", Double::class, Long::class)) java.lang.Double::class.invokeStatic("longBitsToDouble", Double::class, Long::class))
} }
fun applyBr(ctx: FuncContext, fn: Func, i: Node.Instr.Br) = fun popForBlockEscape(ctx: FuncContext, fn: Func, block: Func.Block) =
fn.blockAtDepth(i.relativeDepth).let { block -> popUntilStackSize(ctx, fn, block, block.origStack.size + block.labelTypes.size, block.labelTypes.isNotEmpty())
// We have to pop all unnecessary values per the spec
val type = block.labelTypes.firstOrNull() fun popUntilStackSize(
fun pop(fn: Func): Func { ctx: FuncContext,
// Have to swap first if there is a type expected fn: Func,
// Note, we check stack size because dead code is allowed to do some crazy block: Func.Block,
// things. untilStackSize: Int,
return (if (type != null && fn.stack.size > 1) fn.stackSwap(block) else fn).let { fn -> keepLast: Boolean
fn.pop().let { (fn, poppedType) -> ): Func {
ctx.debug { "For block ${block.insn}, popping until stack size $untilStackSize, keeping last? $keepLast" }
// Just get the latest, don't actually pop...
val type = if (keepLast) fn.pop().second else null
return (0 until Math.max(0, fn.stack.size - untilStackSize)).fold(fn) { fn, _ ->
// Essentially swap and pop if they want to keep the latest
(if (type != null && fn.stack.size > 1) fn.stackSwap(block) else fn).let { fn ->
fn.pop(block).let { (fn, poppedType) ->
fn.addInsns(InsnNode(if (poppedType.stackSize == 2) Opcodes.POP2 else Opcodes.POP)) fn.addInsns(InsnNode(if (poppedType.stackSize == 2) Opcodes.POP2 else Opcodes.POP))
} }
} }
} }
// How many do we have to pop to get back to expected block size
val currBlockStackSize = fn.stack.size - block.origStack.size
val needToPop = Math.max(0, if (type == null) currBlockStackSize else currBlockStackSize - 1)
ctx.debug {
"Unconditional branch on ${block.insn}, curr stack ${fn.stack}, " +
" orig stack ${block.origStack}, need to pop $needToPop"
} }
(0 until needToPop).fold(fn) { fn, _ -> pop(fn) }.
fun applyBr(ctx: FuncContext, fn: Func, i: Node.Instr.Br) =
fn.blockAtDepth(i.relativeDepth).let { block ->
ctx.debug { "Unconditional branch on ${block.insn}, curr stack ${fn.stack}, orig stack ${block.origStack}" }
popForBlockEscape(ctx, fn, block).
popExpectingMulti(block.labelTypes, block). popExpectingMulti(block.labelTypes, block).
addInsns(JumpInsnNode(Opcodes.GOTO, block.requiredLabel)). addInsns(JumpInsnNode(Opcodes.GOTO, block.requiredLabel)).
markUnreachable() markUnreachable()
@ -1162,21 +1167,26 @@ open class FuncBuilder {
} }
} }
fun applyReturnInsn(ctx: FuncContext, fn: Func) = when (ctx.node.type.ret) { fun applyReturnInsn(ctx: FuncContext, fn: Func): Func {
val block = fn.blockStack.first()
popForBlockEscape(ctx, fn, block).let { fn ->
return when (ctx.node.type.ret) {
null -> null ->
fn.addInsns(InsnNode(Opcodes.RETURN)) fn.addInsns(InsnNode(Opcodes.RETURN))
Node.Type.Value.I32 -> Node.Type.Value.I32 ->
fn.popExpecting(Int::class.ref).addInsns(InsnNode(Opcodes.IRETURN)) fn.popExpecting(Int::class.ref, block).addInsns(InsnNode(Opcodes.IRETURN))
Node.Type.Value.I64 -> Node.Type.Value.I64 ->
fn.popExpecting(Long::class.ref).addInsns(InsnNode(Opcodes.LRETURN)) fn.popExpecting(Long::class.ref, block).addInsns(InsnNode(Opcodes.LRETURN))
Node.Type.Value.F32 -> Node.Type.Value.F32 ->
fn.popExpecting(Float::class.ref).addInsns(InsnNode(Opcodes.FRETURN)) fn.popExpecting(Float::class.ref, block).addInsns(InsnNode(Opcodes.FRETURN))
Node.Type.Value.F64 -> Node.Type.Value.F64 ->
fn.popExpecting(Double::class.ref).addInsns(InsnNode(Opcodes.DRETURN)) fn.popExpecting(Double::class.ref, block).addInsns(InsnNode(Opcodes.DRETURN))
}.let { fn -> }.let { fn ->
if (fn.stack.isNotEmpty()) throw CompileErr.UnusedStackOnReturn(fn.stack) if (fn.stack.isNotEmpty()) throw CompileErr.UnusedStackOnReturn(fn.stack)
fn.markUnreachable() fn.markUnreachable()
} }
}
}
companion object : FuncBuilder() companion object : FuncBuilder()
} }

View File

@ -126,7 +126,7 @@ data class ScriptContext(
private fun assertFailure(a: Script.Cmd.Assertion, e: Throwable, expectedString: String) { private fun assertFailure(a: Script.Cmd.Assertion, e: Throwable, expectedString: String) {
val innerEx = exceptionFromCatch(e) val innerEx = exceptionFromCatch(e)
val msg = exceptionTranslator.translate(innerEx) ?: "<unrecognized error>" val msg = exceptionTranslator.translate(innerEx) ?: "<unrecognized error>"
if (msg != expectedString) if (!msg.contains(expectedString))
throw ScriptAssertionError(a, "Expected failure '$expectedString' got '$msg'", cause = innerEx) throw ScriptAssertionError(a, "Expected failure '$expectedString' got '$msg'", cause = innerEx)
} }

View File

@ -146,7 +146,8 @@ class CoreTestUnit(val name: String, val wast: String, val expectedOutput: Strin
// "switch.wast" TODO: we are in trouble here on the "argument switch" // "switch.wast" TODO: we are in trouble here on the "argument switch"
"tee_local.wast", "tee_local.wast",
"traps.wast", "traps.wast",
"unreached-invalid.wast" "unreached-invalid.wast",
"unwind.wast"
) )
val testsWithErrorToWarningPredicates: Map<String, (Throwable) -> Boolean> = mapOf( val testsWithErrorToWarningPredicates: Map<String, (Throwable) -> Boolean> = mapOf(