mirror of
https://github.com/fluencelabs/asmble
synced 2025-04-25 14:52:21 +00:00
Cleanup and ability to run all but a couple of the spec tests
This commit is contained in:
parent
9a1090c439
commit
b37bc44c50
@ -114,11 +114,17 @@ open class InsnReworker {
|
|||||||
}
|
}
|
||||||
if (count == 0) return inject(stackManips.size)
|
if (count == 0) return inject(stackManips.size)
|
||||||
var countSoFar = 0
|
var countSoFar = 0
|
||||||
|
var foundUnconditionalJump = false
|
||||||
for ((amountChanged, insnIndex) in stackManips.asReversed()) {
|
for ((amountChanged, insnIndex) in stackManips.asReversed()) {
|
||||||
countSoFar += amountChanged
|
countSoFar += amountChanged
|
||||||
|
if (!foundUnconditionalJump) foundUnconditionalJump = insns[insnIndex].let { insn ->
|
||||||
|
insn is Node.Instr.Br || insn is Node.Instr.BrTable ||
|
||||||
|
insn is Node.Instr.Unreachable || insn is Node.Instr.Return
|
||||||
|
}
|
||||||
if (countSoFar == count) return inject(insnIndex)
|
if (countSoFar == count) return inject(insnIndex)
|
||||||
}
|
}
|
||||||
throw CompileErr.StackInjectionMismatch(count, insn)
|
// Only consider it a failure if we didn't hit any unconditional jumps
|
||||||
|
if (!foundUnconditionalJump) throw CompileErr.StackInjectionMismatch(count, insn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go over each insn, determining where to inject
|
// Go over each insn, determining where to inject
|
||||||
|
@ -17,6 +17,12 @@ class SpecTestUnit(val name: String, val wast: String, val expectedOutput: Strin
|
|||||||
|
|
||||||
val shouldFail get() = name.endsWith(".fail")
|
val shouldFail get() = name.endsWith(".fail")
|
||||||
|
|
||||||
|
val skipRunReason get() = when (name) {
|
||||||
|
"br_table" -> "Table issues on jump"
|
||||||
|
"switch" -> "Table issues on jumps (ref 'argument switch')"
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
val defaultMaxMemPages get() = when (name) {
|
val defaultMaxMemPages get() = when (name) {
|
||||||
"nop"-> 20
|
"nop"-> 20
|
||||||
"resizing" -> 830
|
"resizing" -> 830
|
||||||
@ -24,6 +30,31 @@ class SpecTestUnit(val name: String, val wast: String, val expectedOutput: Strin
|
|||||||
else -> 1
|
else -> 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isWarningInsteadOfError(t: Throwable) = when (name) {
|
||||||
|
// NaN bit patterns can be off
|
||||||
|
"float_literals" -> isNanMismatch(t)
|
||||||
|
"float_exprs" -> isNanMismatch(t)
|
||||||
|
// We don't hold table capacity right now
|
||||||
|
// TODO: Figure out how we want to store/retrieve table capacity. Right now
|
||||||
|
// a table is an array, so there is only size not capacity. Since we want to
|
||||||
|
// stay w/ the stdlib of the JVM, the best option may be to store the capacity
|
||||||
|
// as a separate int value and query it or pass it around via import as
|
||||||
|
// necessary. I guess I could use a vector, but it's not worth it just for
|
||||||
|
// capacity since you lose speed.
|
||||||
|
"imports" -> t is ScriptAssertionError && (t.assertion as? Script.Cmd.Assertion.Unlinkable).let {
|
||||||
|
it != null && it.failure == "maximum size larger than declared" &&
|
||||||
|
it.module.imports.singleOrNull()?.kind is Node.Import.Kind.Table
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isNanMismatch(t: Throwable) = t is ScriptAssertionError && (
|
||||||
|
t.assertion is Script.Cmd.Assertion.ReturnNan ||
|
||||||
|
(t.assertion is Script.Cmd.Assertion.Return && (t.assertion as Script.Cmd.Assertion.Return).let {
|
||||||
|
(it.action as? Script.Cmd.Action.Invoke)?.string?.contains("nan") ?: false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
val parseResult: StrToSExpr.ParseResult.Success by lazy {
|
val parseResult: StrToSExpr.ParseResult.Success by lazy {
|
||||||
StrToSExpr.parse(wast).let {
|
StrToSExpr.parse(wast).let {
|
||||||
when (it) {
|
when (it) {
|
||||||
@ -37,150 +68,7 @@ class SpecTestUnit(val name: String, val wast: String, val expectedOutput: Strin
|
|||||||
|
|
||||||
val script: Script by lazy { SExprToAst.toScript(SExpr.Multi(ast)) }
|
val script: Script by lazy { SExprToAst.toScript(SExpr.Multi(ast)) }
|
||||||
|
|
||||||
fun isWarningInsteadOfError(t: Throwable) = testsWithErrorToWarningPredicates[name]?.invoke(t) ?: false
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: We are going down in order. One's we have not yet handled:
|
|
||||||
- br_table.wast - Table issues on jumps
|
|
||||||
- linking.wast - Not handling tables yet
|
|
||||||
- return.wast - Not handling tables yet
|
|
||||||
- switch.wast - Table issues on jumps (ref "argument switch")
|
|
||||||
- typecheck.wast - Not handling tables yet
|
|
||||||
- unreachable.wast - Not handling tables yet
|
|
||||||
*/
|
|
||||||
|
|
||||||
val knownGoodTests = arrayOf(
|
|
||||||
"address.wast",
|
|
||||||
"address-offset-range.fail.wast",
|
|
||||||
"binary.wast",
|
|
||||||
"block.wast",
|
|
||||||
"block-end-label-mismatch.fail.wast",
|
|
||||||
"block-end-label-superfluous.wast",
|
|
||||||
"br.wast",
|
|
||||||
"br_if.wast",
|
|
||||||
"break-drop.wast",
|
|
||||||
"call.wast",
|
|
||||||
"call_indirect.wast",
|
|
||||||
"comments.wast",
|
|
||||||
"conversions.wast",
|
|
||||||
"custom_section.wast",
|
|
||||||
"endianness.wast",
|
|
||||||
"exports.wast",
|
|
||||||
"f32.load32.fail.wast",
|
|
||||||
"f32.load64.fail.wast",
|
|
||||||
"f32.store32.fail.wast",
|
|
||||||
"f32.store64.fail.wast",
|
|
||||||
"f32.wast",
|
|
||||||
"f32_cmp.wast",
|
|
||||||
"f64.load32.fail.wast",
|
|
||||||
"f64.load64.fail.wast",
|
|
||||||
"f64.store32.fail.wast",
|
|
||||||
"f64.store64.fail.wast",
|
|
||||||
"f64.wast",
|
|
||||||
"f64_cmp.wast",
|
|
||||||
"fac.wast",
|
|
||||||
"float_exprs.wast",
|
|
||||||
"float_literals.wast",
|
|
||||||
"float_memory.wast",
|
|
||||||
"float_misc.wast",
|
|
||||||
"forward.wast",
|
|
||||||
"func_ptrs.wast",
|
|
||||||
"func-local-after-body.fail.wast",
|
|
||||||
"func-local-before-param.fail.wast",
|
|
||||||
"func-local-before-result.fail.wast",
|
|
||||||
"func-param-after-body.fail.wast",
|
|
||||||
"func-result-after-body.fail.wast",
|
|
||||||
"func-result-before-param.fail.wast",
|
|
||||||
"func.wast",
|
|
||||||
"get_local.wast",
|
|
||||||
"globals.wast",
|
|
||||||
"i32.load32_s.fail.wast",
|
|
||||||
"i32.load32_u.fail.wast",
|
|
||||||
"i32.load64_s.fail.wast",
|
|
||||||
"i32.load64_u.fail.wast",
|
|
||||||
"i32.store32.fail.wast",
|
|
||||||
"i32.store64.fail.wast",
|
|
||||||
"i32.wast",
|
|
||||||
"i64.load64_s.fail.wast",
|
|
||||||
"i64.load64_u.fail.wast",
|
|
||||||
"i64.store64.fail.wast",
|
|
||||||
"i64.wast",
|
|
||||||
"if.wast",
|
|
||||||
"if-else-end-label-mismatch.fail.wast",
|
|
||||||
"if-else-end-label-superfluous.fail.wast",
|
|
||||||
"if-else-label-mismatch.fail.wast",
|
|
||||||
"if-else-label-superfluous.fail.wast",
|
|
||||||
"if-end-label-mismatch.fail.wast",
|
|
||||||
"if-end-label-superfluous.fail.wast",
|
|
||||||
"import-after-func.fail.wast",
|
|
||||||
"import-after-global.fail.wast",
|
|
||||||
"import-after-memory.fail.wast",
|
|
||||||
"import-after-table.fail.wast",
|
|
||||||
"imports.wast",
|
|
||||||
"int_exprs.wast",
|
|
||||||
"int_literals.wast",
|
|
||||||
"labels.wast",
|
|
||||||
"left-to-right.wast",
|
|
||||||
"linking.wast",
|
|
||||||
"load-align-0.fail.wast",
|
|
||||||
"load-align-odd.fail.wast",
|
|
||||||
"loop.wast",
|
|
||||||
"loop-end-label-mismatch.fail.wast",
|
|
||||||
"loop-end-label-superfluous.fail.wast",
|
|
||||||
"memory.wast",
|
|
||||||
"memory_redundancy.wast",
|
|
||||||
"memory_trap.wast",
|
|
||||||
"names.wast",
|
|
||||||
"nop.wast",
|
|
||||||
"of_string-overflow-hex-u32.fail.wast",
|
|
||||||
"of_string-overflow-hex-u64.fail.wast",
|
|
||||||
"of_string-overflow-s32.fail.wast",
|
|
||||||
"of_string-overflow-s64.fail.wast",
|
|
||||||
"of_string-overflow-u32.fail.wast",
|
|
||||||
"of_string-overflow-u64.fail.wast",
|
|
||||||
"resizing.wast",
|
|
||||||
"select.wast",
|
|
||||||
"set_local.wast",
|
|
||||||
"skip-stack-guard-page.wast",
|
|
||||||
"stack.wast",
|
|
||||||
"start.wast",
|
|
||||||
"store-align-0.fail.wast",
|
|
||||||
"store-align-odd.fail.wast",
|
|
||||||
"store_retval.wast",
|
|
||||||
"tee_local.wast",
|
|
||||||
"traps.wast",
|
|
||||||
"unreached-invalid.wast",
|
|
||||||
"unwind.wast"
|
|
||||||
)
|
|
||||||
|
|
||||||
val testsWithErrorToWarningPredicates: Map<String, (Throwable) -> Boolean> = mapOf(
|
|
||||||
// NaN bit patterns can be off
|
|
||||||
"float_literals" to this::isNanMismatch,
|
|
||||||
"float_exprs" to this::isNanMismatch,
|
|
||||||
// We don't hold table capacity right now
|
|
||||||
// TODO: Figure out how we want to store/retrieve table capacity. Right now
|
|
||||||
// a table is an array, so there is only size not capacity. Since we want to
|
|
||||||
// stay w/ the stdlib of the JVM, the best option may be to store the capacity
|
|
||||||
// as a separate int value and query it or pass it around via import as
|
|
||||||
// necessary. I guess I could use a vector, but it's not worth it just for
|
|
||||||
// capacity since you lose speed.
|
|
||||||
"imports" to { t: Throwable ->
|
|
||||||
t is ScriptAssertionError && (t.assertion as? Script.Cmd.Assertion.Unlinkable).let {
|
|
||||||
it != null && it.failure == "maximum size larger than declared" &&
|
|
||||||
it.module.imports.singleOrNull()?.kind is Node.Import.Kind.Table
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
fun isNanMismatch(t: Throwable) = t is ScriptAssertionError && (
|
|
||||||
t.assertion is Script.Cmd.Assertion.ReturnNan ||
|
|
||||||
(t.assertion is Script.Cmd.Assertion.Return && (t.assertion as Script.Cmd.Assertion.Return).let {
|
|
||||||
(it.action as? Script.Cmd.Action.Invoke)?.string?.contains("nan") ?: false
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
val unitsPath = "/spec/test/core"
|
val unitsPath = "/spec/test/core"
|
||||||
|
|
||||||
val allUnits by lazy { loadFromResourcePath("/local-spec") + loadFromResourcePath(unitsPath) }
|
val allUnits by lazy { loadFromResourcePath("/local-spec") + loadFromResourcePath(unitsPath) }
|
||||||
@ -193,9 +81,7 @@ class SpecTestUnit(val name: String, val wast: String, val expectedOutput: Strin
|
|||||||
fs.use { fs ->
|
fs.use { fs ->
|
||||||
val path = fs?.getPath(basePath) ?: Paths.get(uri)
|
val path = fs?.getPath(basePath) ?: Paths.get(uri)
|
||||||
val testWastFiles = Files.walk(path, 1).
|
val testWastFiles = Files.walk(path, 1).
|
||||||
filter { it.toString().endsWith(".wast") }.
|
filter { it.toString().endsWith(".wast") }
|
||||||
// TODO: remove this when they all succeed
|
|
||||||
filter { knownGoodTests.contains(it.fileName.toString()) }
|
|
||||||
return testWastFiles.map {
|
return testWastFiles.map {
|
||||||
val name = it.fileName.toString().substringBeforeLast(".wast")
|
val name = it.fileName.toString().substringBeforeLast(".wast")
|
||||||
SpecTestUnit(
|
SpecTestUnit(
|
||||||
|
@ -4,6 +4,7 @@ import asmble.SpecTestUnit
|
|||||||
import asmble.io.AstToSExpr
|
import asmble.io.AstToSExpr
|
||||||
import asmble.io.SExprToStr
|
import asmble.io.SExprToStr
|
||||||
import asmble.util.Logger
|
import asmble.util.Logger
|
||||||
|
import org.junit.Assume
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.Parameterized
|
import org.junit.runners.Parameterized
|
||||||
@ -17,6 +18,8 @@ class RunTest(val unit: SpecTestUnit) : Logger by Logger.Print(Logger.Level.INFO
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRun() {
|
fun testRun() {
|
||||||
|
unit.skipRunReason?.let { Assume.assumeTrue("Skipping ${unit.name}, reason: $it", false) }
|
||||||
|
|
||||||
val ex = try { run(); null } catch (e: Throwable) { e }
|
val ex = try { run(); null } catch (e: Throwable) { e }
|
||||||
if (unit.shouldFail) {
|
if (unit.shouldFail) {
|
||||||
assertNotNull(ex, "Expected failure, but succeeded")
|
assertNotNull(ex, "Expected failure, but succeeded")
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
;; TODO: test w/ emscripten compiled code
|
Loading…
x
Reference in New Issue
Block a user