Skip to content

Commit

Permalink
Add beginning of CLI for func split issue #19
Browse files Browse the repository at this point in the history
  • Loading branch information
cretz committed Jul 29, 2018
1 parent 01d8994 commit 9dc4112
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 19 deletions.
2 changes: 1 addition & 1 deletion compiler/src/main/kotlin/asmble/cli/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package asmble.cli
import asmble.util.Logger
import kotlin.system.exitProcess

val commands = listOf(Compile, Help, Invoke, Link, Run, Translate)
val commands = listOf(Compile, Help, Invoke, Link, Run, SplitFunc, Translate)

fun main(args: Array<String>) {
if (args.isEmpty()) return println(
Expand Down
146 changes: 146 additions & 0 deletions compiler/src/main/kotlin/asmble/cli/SplitFunc.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package asmble.cli

import asmble.ast.Node
import asmble.ast.Script
import asmble.ast.opt.SplitLargeFunc

open class SplitFunc : Command<SplitFunc.Args>() {
override val name = "split-func"
override val desc = "Split a WebAssembly function into two"

override fun args(bld: Command.ArgsBuilder) = Args(
inFile = bld.arg(
name = "inFile",
desc = "The wast or wasm WebAssembly file name. Can be '--' to read from stdin."
),
funcName = bld.arg(
name = "funcName",
desc = "The name (or '#' + function space index) of the function to split"
),
inFormat = bld.arg(
name = "inFormat",
opt = "in",
desc = "Either 'wast' or 'wasm' to describe format.",
default = "<use file extension>",
lowPriority = true
),
outFile = bld.arg(
name = "outFile",
opt = "outFile",
desc = "The wast or wasm WebAssembly file name. Can be '--' to write to stdout.",
default = "<inFileSansExt.split.wasm or stdout>",
lowPriority = true
),
outFormat = bld.arg(
name = "outFormat",
opt = "out",
desc = "Either 'wast' or 'wasm' to describe format.",
default = "<use file extension or wast for stdout>",
lowPriority = true
),
compact = bld.flag(
opt = "compact",
desc = "If set for wast out format, will be compacted.",
lowPriority = true
),
minInsnSetLength = bld.arg(
name = "minInsnSetLength",
opt = "minLen",
desc = "The minimum number of instructions allowed for the split off function.",
default = "5",
lowPriority = true
).toInt(),
maxInsnSetLength = bld.arg(
name = "maxInsnSetLength",
opt = "maxLen",
desc = "The maximum number of instructions allowed for the split off function.",
default = "40",
lowPriority = true
).toInt(),
maxNewFuncParamCount = bld.arg(
name = "maxNewFuncParamCount",
opt = "maxParams",
desc = "The maximum number of params allowed for the split off function.",
default = "30",
lowPriority = true
).toInt(),
attempts = bld.arg(
name = "attempts",
opt = "attempts",
desc = "The number of attempts to perform.",
default = "1",
lowPriority = true
).toInt()
).also { bld.done() }

override fun run(args: Args) {
// Load the mod
val translate = Translate().also { it.logger = logger }
val inFormat =
if (args.inFormat != "<use file extension>") args.inFormat
else args.inFile.substringAfterLast('.', "<unknown>")
val script = translate.inToAst(args.inFile, inFormat)
var mod = (script.commands.firstOrNull() as? Script.Cmd.Module)?.module ?: error("Only a single module allowed")

// Do attempts
val splitter = SplitLargeFunc(
minSetLength = args.minInsnSetLength,
maxSetLength = args.maxInsnSetLength,
maxParamCount = args.maxNewFuncParamCount
)
for (attempt in 0 until args.attempts) {
// Find the function
var index = mod.names?.funcNames?.toList()?.find { it.second == args.funcName }?.first
if (index == null && args.funcName.startsWith('#')) index = args.funcName.drop(1).toInt()
val origFunc = index?.let {
mod.funcs.getOrNull(it - mod.imports.count { it.kind is Node.Import.Kind.Func })
} ?: error("Unable to find func")

// Split it
val results = splitter.apply(mod, index)
if (results == null) {
logger.warn { "No instructions after attempt $attempt" }
break
}
val (splitMod, insnsSaved) = results
val newFunc = splitMod.funcs[index - mod.imports.count { it.kind is Node.Import.Kind.Func }]
val splitFunc = splitMod.funcs.last()
logger.warn {
"Split complete, from func with ${origFunc.instructions.size} insns to a func " +
"with ${newFunc.instructions.size} insns + delegated func " +
"with ${splitFunc.instructions.size} insns and ${splitFunc.type.params.size} params, " +
"saved $insnsSaved insns"
}
mod = splitMod
}

// Write it
val outFile = when {
args.outFile != "<inFileSansExt.split.wasm or stdout>" -> args.outFile
args.inFile == "--" -> "--"
else -> args.inFile.replaceAfterLast('.', "split." + args.inFile.substringAfterLast('.'))
}
val outFormat = when {
args.outFormat != "<use file extension or wast for stdout>" -> args.outFormat
outFile == "--" -> "wast"
else -> outFile.substringAfterLast('.', "<unknown>")
}
translate.astToOut(outFile, outFormat, args.compact,
Script(listOf(Script.Cmd.Module(mod, mod.names?.moduleName))))
}

data class Args(
val inFile: String,
val inFormat: String,
val funcName: String,
val outFile: String,
val outFormat: String,
val compact: Boolean,
val minInsnSetLength: Int,
val maxInsnSetLength: Int,
val maxNewFuncParamCount: Int,
val attempts: Int
)

companion object : SplitFunc()
}
40 changes: 22 additions & 18 deletions compiler/src/main/kotlin/asmble/cli/Translate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,7 @@ open class Translate : Command<Translate.Args>() {
if (args.outFormat != "<use file extension or wast for stdout>") args.outFormat
else if (args.outFile == "--") "wast"
else args.outFile.substringAfterLast('.', "<unknown>")
val outStream =
if (args.outFile == "--") System.out
else FileOutputStream(args.outFile)
outStream.use { outStream ->
when (outFormat) {
"wast" -> {
val sexprToStr = if (args.compact) SExprToStr.Compact else SExprToStr
val sexprs = AstToSExpr.fromScript(script)
outStream.write(sexprToStr.fromSExpr(*sexprs.toTypedArray()).toByteArray())
}
"wasm" -> {
val mod = (script.commands.firstOrNull() as? Script.Cmd.Module)?.module ?:
error("Output to WASM requires input be just a single module")
AstToBinary.fromModule(ByteWriter.OutputStream(outStream), mod)
}
else -> error("Unknown out format '$outFormat'")
}
}
astToOut(args.outFile, outFormat, args.compact, script)
}

fun inToAst(inFile: String, inFormat: String): Script {
Expand All @@ -90,6 +73,27 @@ open class Translate : Command<Translate.Args>() {
}
}

fun astToOut(outFile: String, outFormat: String, compact: Boolean, script: Script) {
val outStream =
if (outFile == "--") System.out
else FileOutputStream(outFile)
outStream.use { outStream ->
when (outFormat) {
"wast" -> {
val sexprToStr = if (compact) SExprToStr.Compact else SExprToStr
val sexprs = AstToSExpr.fromScript(script)
outStream.write(sexprToStr.fromSExpr(*sexprs.toTypedArray()).toByteArray())
}
"wasm" -> {
val mod = (script.commands.firstOrNull() as? Script.Cmd.Module)?.module ?:
error("Output to WASM requires input be just a single module")
AstToBinary.fromModule(ByteWriter.OutputStream(outStream), mod)
}
else -> error("Unknown out format '$outFormat'")
}
}
}

data class Args(
val inFile: String,
val inFormat: String,
Expand Down

0 comments on commit 9dc4112

Please sign in to comment.