In kotlin-wasm-benchmarks ParameterNotNullAssertionBenchmark we pass global property
val OBJ = Any()
as several arguments into methods, like
methodWithEightNotnullParameters(OBJ, OBJ, OBJ, OBJ, OBJ, OBJ, OBJ, OBJ).
With the current schema of the initialization in K/Wasm, OBJ getter first calls the function responsible for the initialization of static properties for the whole file - here $"microBenchmarks.<init properties ParameterNotNullAssertionBenchmark.kt>"
(func $microBenchmarks.<get-OBJ> (;5386;) (type $"#type1038 ") (result (ref null $kotlin.Any))
call $"microBenchmarks.<init properties ParameterNotNullAssertionBenchmark.kt>"
global.get $microBenchmarks.OBJ
return
)
Initialization function first checks, by the global state
(global $"microBenchmarks.properties initialized ParameterNotNullAssertionBenchmark.kt" (;5674;) (mut i32) i32.const 1)
- if the properties were already successfully initialized (value 0) - fast path, just return
- if previous initialization attempt failed (value 2) - call
staticInitializationFailureWithClassName that will throw corresponding error
- if not initialized (value 1) - call initializers inside
try {} catch {} block, so that when the error occur we will be able to catch it and throw corresponding staticInitialization error.
(func $"microBenchmarks.<init properties ParameterNotNullAssertionBenchmark.kt>" (;5400;) (type $"#type3092 ")
(local $~state i32) (local $reason (ref null $kotlin.Throwable))
global.get $"microBenchmarks.properties initialized ParameterNotNullAssertionBenchmark.kt"
local.tee $~state
i32.eqz
if ;; label = @1 // already successfully initialized, state == 0
return
end
local.get $~state
i32.const 2
i32.eq
if ;; label = @1 // previous initialization attempt failed, state == 2 - throw corresponding error
ref.null none
call $kotlin.wasm.internal.staticInitializationFailureWithClassName
unreachable
end
i32.const 0
global.set $"microBenchmarks.properties initialized ParameterNotNullAssertionBenchmark.kt" // state = 0
try ;; label = @1 // try to initialize, catch an error if thrown during initialization
global.get $"#global6460 <classVTable>"
ref.null none
global.get $kotlin.Any_rtti
i32.const 0
struct.new $kotlin.Any
global.set $microBenchmarks.OBJ
catch 0
call $kotlin.wasm.internal.getKotlinException
local.set $reason
i32.const 2 // state = 2
global.set $"microBenchmarks.properties initialized ParameterNotNullAssertionBenchmark.kt"
local.get $reason
ref.null none
call $kotlin.internal.staticInitializationFailure
unreachable
end
nop
)
Here, initializer for OBJ property is primitive and Binaryen seems to be able to prove that it does not throw an error. So, it removes try/catch block and inlines $"microBenchmarks.<init properties ParameterNotNullAssertionBenchmark.kt>" function into <get-OBJ>.
(func $microBenchmarks.<get-OBJ> (;2109;) (type 128) (result (ref null $kotlin.Any_125))
(local i32)
global.get $"microBenchmarks.properties initialized ParameterNotNullAssertionBenchmark.kt"
local.tee 0
if ;; label = @1 // state != 0
local.get 0
i32.const 2
i32.eq
if ;; label = @2 // state == 2 - excessive check
ref.null none
call $kotlin.wasm.internal.staticInitializationFailureWithClassName
unreachable
end
i32.const 0
global.set $"microBenchmarks.properties initialized ParameterNotNullAssertionBenchmark.kt"
global.get $<classVTable>_1501
ref.null none
global.get $kotlin.Any_rtti
i32.const 0
struct.new $kotlin.Any_125
global.set $microBenchmarks.OBJf
end
global.get $microBenchmarks.OBJ
)
Since, try/catch block is gone, there is no more assignment state = 2, but there is a check for it. Without this check Binaryen (as we observed) could inline $microBenchmarks.<get-OBJ> into its callsites and speed up the benchmark. Is it feasible to eliminate such kind of checks?
In kotlin-wasm-benchmarks ParameterNotNullAssertionBenchmark we pass global property
val OBJ = Any()as several arguments into methods, like
methodWithEightNotnullParameters(OBJ, OBJ, OBJ, OBJ, OBJ, OBJ, OBJ, OBJ).With the current schema of the initialization in K/Wasm,
OBJgetter first calls the function responsible for the initialization of static properties for the whole file - here$"microBenchmarks.<init properties ParameterNotNullAssertionBenchmark.kt>"Initialization function first checks, by the global state
(global $"microBenchmarks.properties initialized ParameterNotNullAssertionBenchmark.kt" (;5674;) (mut i32) i32.const 1)staticInitializationFailureWithClassNamethat will throw corresponding errortry {} catch {}block, so that when the error occur we will be able to catch it and throw correspondingstaticInitializationerror.Here, initializer for
OBJproperty is primitive and Binaryen seems to be able to prove that it does not throw an error. So, it removes try/catch block and inlines$"microBenchmarks.<init properties ParameterNotNullAssertionBenchmark.kt>"function into<get-OBJ>.Since, try/catch block is gone, there is no more assignment
state = 2, but there is a check for it. Without this check Binaryen (as we observed) could inline$microBenchmarks.<get-OBJ>into its callsites and speed up the benchmark. Is it feasible to eliminate such kind of checks?