///|
pub struct ValidateTraceCliArgs {
repeats : Int32
corpora : Array[String]
} derive(Eq, Debug)
///|
pub enum ValidateTraceCliCommand {
Run(ValidateTraceCliArgs)
Help
ListCorpora
} derive(Eq, Debug)
///|
pub struct ValidateTraceBenchmarkResult {
corpus : String
repeats : Int32
elapsed_ms : UInt64
phase_totals : String
helper_totals : String
hotspots : String?
} derive(Eq, Debug)
///|
struct ValidateTraceCorpus {
name : String
mod : @lib.Module
}
///|
fn ValidateTraceCorpus::new(
name : String,
mod : @lib.Module,
) -> ValidateTraceCorpus {
{ name, mod, }
}
///|
fn validate_trace_ascii_lower(input : String) -> String {
let out = StringBuilder::new()
for c in input {
if c >= 'A' && c <= 'Z' {
out.write_char((c.to_int() + 32).unsafe_to_char())
} else {
out.write_char(c)
}
}
out.to_string()
}
///|
fn validate_trace_join(values : Array[String], sep : String) -> String {
let out = StringBuilder::new()
for i = 0; i < values.length(); i = i + 1 {
if i > 0 {
out.write_string(sep)
}
out.write_string(values[i])
}
out.to_string()
}
///|
fn validate_trace_usage_text() -> String {
let corpora = validate_trace_join(validate_trace_corpus_names(), ", ")
"usage: moon run src/validate_trace -- [--repeat ] [--corpus ...] [--list-corpora] [--help|-h]\ncorpora: \{corpora}"
}
///|
fn validate_trace_tail_string(input : String, start : Int32) -> String {
let out = StringBuilder::new()
let mut i = start
while i < input.length() {
out.write_char(input[i].unsafe_to_char())
i += 1
}
out.to_string()
}
///|
fn validate_trace_parse_positive_int(raw : String) -> Int32 {
let text = raw.trim().to_owned()
if text.length() == 0 {
abort("repeat must not be empty")
}
let mut value = 0
for c in text {
if c < '0' || c > '9' {
abort("invalid repeat count '\{raw}'")
}
value = value * 10 + (c.to_int() - '0'.to_int())
}
if value <= 0 {
abort("repeat must be positive")
}
value
}
///|
fn validate_trace_has_repeat_equals_flag(arg : String) -> Bool {
arg.length() >= 9 &&
arg[0] == '-' &&
arg[1] == '-' &&
arg[2] == 'r' &&
arg[3] == 'e' &&
arg[4] == 'p' &&
arg[5] == 'e' &&
arg[6] == 'a' &&
arg[7] == 't' &&
arg[8] == '='
}
///|
fn validate_trace_trim_program_name(argv : Array[String]) -> Array[String] {
if argv.length() == 0 {
return []
}
let head = argv[0]
let should_trim = head.contains("/") ||
head.contains("\\") ||
head.contains(".wasm") ||
head.contains(".exe")
if !should_trim {
return argv
}
let out = Array::new(capacity=argv.length() - 1)
for i = 1; i < argv.length(); i = i + 1 {
out.push(argv[i])
}
out
}
///|
fn validate_trace_deep_control_module() -> @lib.Module {
let depth = 24
let mut body = @lib.Expr::new([
@lib.Instruction::local_get(@lib.LocalIdx::new(0)),
])
for _ in 0.. @lib.Module {
let local_count = 384
let locals = @lib.Locals::empty()
for _ in 0.. @lib.Module {
let func_count = 160
let sig = @lib.RecType::new(
@lib.SubType::comp_type(@lib.CompType::func([], [])),
)
let type_idxs : Array[@lib.TypeIdx] = []
let funcs : Array[@lib.Func] = []
for i = 0; i < func_count; i = i + 1 {
type_idxs.push(@lib.TypeIdx::new(0))
funcs.push(
@lib.Func::new(
@lib.Locals::empty(),
@lib.Expr::new([
@lib.Instruction::i32_const(@lib.I32(i)),
@lib.Instruction::drop(),
@lib.Instruction::i32_const(@lib.I32(i + 1)),
@lib.Instruction::drop(),
]),
),
)
}
@lib.Module::new()
.with_type_sec(@lib.TypeSec::new([sig]))
.with_func_sec(@lib.FuncSec::new(type_idxs))
.with_code_sec(@lib.CodeSec::new(funcs))
}
///|
fn validate_trace_ref_func_heavy_module() -> @lib.Module {
let func_count = 48
let sig = @lib.RecType::new(
@lib.SubType::comp_type(@lib.CompType::func([], [])),
)
let type_idxs : Array[@lib.TypeIdx] = []
let funcs : Array[@lib.Func] = []
let declared_funcs : Array[@lib.FuncIdx] = []
let init_exprs : Array[@lib.Expr] = []
let globals : Array[@lib.Global] = []
let exports : Array[@lib.Export] = []
for i = 0; i < func_count; i = i + 1 {
type_idxs.push(@lib.TypeIdx::new(0))
let func_idx = @lib.FuncIdx::new(i.reinterpret_as_uint())
declared_funcs.push(func_idx)
if i < 16 {
init_exprs.push(@lib.Expr::new([@lib.Instruction::ref_func(func_idx)]))
}
if i < 8 {
globals.push(
@lib.Global::new(
@lib.GlobalType::new(@lib.ValType::funcref(), false),
@lib.Expr::new([@lib.Instruction::ref_func(func_idx)]),
),
)
exports.push(
@lib.Export::new(
@lib.Name::new("rf\{i}"),
@lib.ExternIdx::func(func_idx),
),
)
}
let body_instrs : Array[@lib.Instruction] = []
for j = 0; j < 12; j = j + 1 {
let target = @lib.FuncIdx::new(
((i + j) % func_count).reinterpret_as_uint(),
)
body_instrs.push(@lib.Instruction::ref_func(target))
body_instrs.push(@lib.Instruction::drop())
}
funcs.push(
@lib.Func::new(@lib.Locals::from_types([]), @lib.Expr::new(body_instrs)),
)
}
let elem_decls = @lib.Elem::new(
@lib.ElemMode::declarative(),
@lib.ElemKind::funcs(declared_funcs),
)
let elem_exprs = @lib.Elem::new(
@lib.ElemMode::passive(),
@lib.ElemKind::typed_exprs(
@lib.RefType::new(true, @lib.HeapType::abs(@lib.AbsHeapType::func())),
init_exprs,
),
)
let table_type = @lib.TableType::new(
@lib.RefType::new(true, @lib.HeapType::abs(@lib.AbsHeapType::func())),
@lib.Limits::i32(func_count.reinterpret_as_uint(), None),
)
@lib.Module::new()
.with_type_sec(@lib.TypeSec::new([sig]))
.with_func_sec(@lib.FuncSec::new(type_idxs))
.with_table_sec(
@lib.TableSec::new([
@lib.Table::new(
table_type,
Some(@lib.Expr::new([@lib.Instruction::ref_func(@lib.FuncIdx::new(0))])),
),
]),
)
.with_global_sec(@lib.GlobalSec::new(globals))
.with_export_sec(@lib.ExportSec::new(exports))
.with_elem_sec(@lib.ElemSec::new([elem_decls, elem_exprs]))
.with_code_sec(@lib.CodeSec::new(funcs))
}
///|
fn validate_trace_fixed_corpora() -> Array[ValidateTraceCorpus] {
[
ValidateTraceCorpus::new(
"deep-control",
validate_trace_deep_control_module(),
),
ValidateTraceCorpus::new("wide-locals", validate_trace_wide_locals_module()),
ValidateTraceCorpus::new(
"large-codesec",
validate_trace_large_codesec_module(),
),
ValidateTraceCorpus::new(
"ref-func-heavy",
validate_trace_ref_func_heavy_module(),
),
]
}
///|
fn validate_trace_find_corpus(name : String) -> ValidateTraceCorpus? {
for corpus in validate_trace_fixed_corpora() {
if corpus.name == name {
return Some(corpus)
}
}
None
}
///|
fn validate_trace_find_last_prefix(
lines : Array[String],
prefix : String,
) -> String? {
let mut i = lines.length() - 1
while i >= 0 {
if lines[i].has_prefix(prefix) {
return Some(lines[i])
}
i -= 1
}
None
}
///|
fn validate_trace_run_one(
corpus : ValidateTraceCorpus,
repeats : Int32,
) -> Result[ValidateTraceBenchmarkResult, String] {
if repeats <= 0 {
return Err("validate-trace repeat count must be positive")
}
let mut elapsed_ms = 0UL
let mut phase_totals = ""
let mut helper_totals = ""
let mut hotspots : String? = None
for _ in 0.. ()
Err(err) =>
return Err(
"validate-trace corpus '\{corpus.name}' failed: \{@validate.validation_error_message(err)}",
)
}
elapsed_ms = elapsed_ms + @lib.trace_elapsed_ms(start_ms)
phase_totals = match
validate_trace_find_last_prefix(lines, "phase_totals") {
Some(line) => line
None =>
return Err(
"validate-trace corpus '\{corpus.name}' did not emit phase_totals",
)
}
helper_totals = match
validate_trace_find_last_prefix(lines, "helper_totals") {
Some(line) => line
None =>
return Err(
"validate-trace corpus '\{corpus.name}' did not emit helper_totals",
)
}
hotspots = validate_trace_find_last_prefix(lines, "hotspots ")
}
Ok({
corpus: corpus.name,
repeats,
elapsed_ms,
phase_totals,
helper_totals,
hotspots,
})
}
///|
fn validate_trace_dedup_corpora(corpora : Array[String]) -> Array[String] {
let out : Array[String] = []
for corpus in corpora {
let mut seen = false
for existing in out {
if existing == corpus {
seen = true
break
}
}
if !seen {
out.push(corpus)
}
}
out
}
///|
fn validate_trace_default_corpora() -> Array[String] {
let out : Array[String] = []
for corpus in validate_trace_fixed_corpora() {
out.push(corpus.name)
}
out
}
///|
pub fn validate_trace_corpus_names() -> Array[String] {
validate_trace_default_corpora()
}
///|
pub fn run_validate_trace_benchmark(
corpora : Array[String],
repeats : Int32,
) -> Result[Array[ValidateTraceBenchmarkResult], String] {
let selected = if corpora.length() == 0 {
validate_trace_default_corpora()
} else {
validate_trace_dedup_corpora(corpora)
}
let results : Array[ValidateTraceBenchmarkResult] = []
for name in selected {
let corpus = match validate_trace_find_corpus(name) {
Some(item) => item
None => return Err("unknown validate-trace corpus '\{name}'")
}
match validate_trace_run_one(corpus, repeats) {
Ok(result) => results.push(result)
Err(e) => return Err(e)
}
}
Ok(results)
}
///|
fn format_validate_trace_benchmark_result(
result : ValidateTraceBenchmarkResult,
) -> String {
let out = StringBuilder::new()
out.write_string(
"corpus=\{result.corpus} repeats=\{result.repeats} elapsed_ms=\{result.elapsed_ms}\n",
)
out.write_string(result.phase_totals)
out.write_char('\n')
out.write_string(result.helper_totals)
out.write_char('\n')
match result.hotspots {
Some(line) => out.write_string(line)
None => out.write_string("hotspots none")
}
out.to_string()
}
///|
pub fn parse_validate_trace_cli_command(
args : Array[String],
) -> ValidateTraceCliCommand {
let mut repeats = 1
let corpora : Array[String] = []
let mut i = 0
while i < args.length() {
let arg = args[i].trim().to_owned()
match validate_trace_ascii_lower(arg) {
"--help" | "-h" => {
if args.length() != 1 {
abort("control flags must be provided alone")
}
return ValidateTraceCliCommand::Help
}
"--list-corpora" => {
if args.length() != 1 {
abort("control flags must be provided alone")
}
return ValidateTraceCliCommand::ListCorpora
}
"--repeat" => {
if i + 1 >= args.length() {
abort("missing value for --repeat")
}
repeats = validate_trace_parse_positive_int(args[i + 1])
i += 2
continue
}
"--corpus" => {
if i + 1 >= args.length() {
abort("missing value for --corpus")
}
corpora.push(validate_trace_ascii_lower(args[i + 1].trim().to_owned()))
i += 2
continue
}
_ => ()
}
if validate_trace_has_repeat_equals_flag(arg) {
repeats = validate_trace_parse_positive_int(
validate_trace_tail_string(arg, 9),
)
i += 1
continue
}
if arg.length() >= 2 && arg[0] == '-' && arg[1] == '-' {
abort("unknown flag '\{arg}'")
}
abort(validate_trace_usage_text())
}
ValidateTraceCliCommand::Run({ repeats, corpora, })
}
///|
fn main {
let args = validate_trace_trim_program_name(@env.args())
match parse_validate_trace_cli_command(args) {
ValidateTraceCliCommand::Help => println(validate_trace_usage_text())
ValidateTraceCliCommand::ListCorpora =>
for corpus in validate_trace_corpus_names() {
println(corpus)
}
ValidateTraceCliCommand::Run(parsed) =>
match run_validate_trace_benchmark(parsed.corpora, parsed.repeats) {
Ok(results) =>
for i = 0; i < results.length(); i = i + 1 {
if i > 0 {
println("")
}
println(format_validate_trace_benchmark_result(results[i]))
}
Err(e) => abort(e)
}
}
}