Releases: CharlieTap/chasm
1.3.1
1.3.0
What's changed in Chasm
- Chasm now supports decoding and validating binaries that contain SIMD and relaxed SIMD instructions, the intention here is to provide better error messaging when presented with binaries we are unable to execute. Historically Chasm would return an ambiguous "Unrecognised opcode", but now you will get a message describing what features are present in the binary but not supported by chasm.
- Minor performance improvements to the binary decoder, removing an O(n) search across instructions
Whats changed in the Gradle plugin
- producer mode is now deprecated, it will still function for now but will be removed in a future release. It's successor Glueball, allows you to do the same thing but also generate a native interface alongside your virtualised interface. Meaning you easily swap back and forth between native and virtual execution. Note that Glueball does not have the same level of type support as Producer does for now.
Full Changelog: 1.2.1...1.3.0
1.2.1
What's Changed
- Chasm is now able to both decode and validate binaries containing opcodes from the Threads proposal. The execution phase will come a little later, the proposal specification is still not rebased on top of Wasm 3.0 and I'm apprehensive to finish support whilst the spec is in flux.
- Optimisations to the binary validation algorithm, the original version heavily allocated during instruction simulation. The new version uses mutation where possible and as a result the validation is 5-10% faster.
Latest Version: 1.2.1
Latest Plugin Version: 2.0.1-1.2.1
Full Changelog: 1.2.0...1.2.1
1.2.0
What's Changed
JS Support for codegen produced by Chasms Gradle Plugin
Chasms Gradle Plugin historically produced codegen that was common amongst chasms kotlin multiplatform targets, but Chasm lacks support for the web because using an interpreter in an environment that supports JIT is nonsensical.
With this release the codegen is reworked to leverage a new generic wasm virtual machine interface. This interface uses Chasm under the hood on all native and JVM targets but uses the standard JS Wasm API for JS targets, bringing all that JIT perf.
For most consumers of the plugin no changes will likely be needed, it still generates an interface and impl in the same way as previously. That being said some typing has changed and if you're working with imports there will be breaking changes.
So with that in mind the plugin now has a new major version, alongside a sub version to help disambiguate what version of chasm it uses.
plugin-version-name = "2.0.0-1.2.0"As always please use the example project as a reference, I've updated it to use the latest plugin/codegen and added a web example now that we have JS support.
Wide Arithmetic Proposal
Chasm now supports another new proposal from the future Wasm 4.0 called wide arithmetic
1.1.0
Allocators and Strings
Chasms Gradle Plugin has had the ability for a while to generate Kotlin functions with String return types but has been lacking the ability to specify String parameters. This is because specifying a String as a parameter means allocating it in memory whereas returning a String simply means reading an existing allocation.
In order to safely allocate memory Chasm needs to coordinate via the programs allocator, certain frameworks like emscripten allow exporting the malloc and free when generating wasm binaries. Chasm can then use these functions to safely allocate regions of memory for strings.
This release includes the ability to define an allocator for a module, i.e. an alloc function and a dellocation/free function.
create("StringService") {
binary = layout.projectDirectory.file("src/commonMain/resources/truncate.wasm")
packageName = "com.test.chasm"
allocator = ExportedAllocator("malloc", "free")
}And finally stringParams are now possible in the function definition api, please note an error will be thrown
if a stringParam is defined but no allocator is present on the module definition.
create("StringService") {
binary = layout.projectDirectory.file("src/commonMain/resources/truncate.wasm")
packageName = "com.test.chasm"
allocator = ExportedAllocator("malloc", "free")
function("truncate") {
stringParam("parameterName")
stringReturnType()
}
}As always you can find an example of stringParams and allocators in the example project
Changes to Chasm
- a new generic allocator interface is available alongside a concrete Wasm32Allocator class, it's not expected that these types will be of much use outside of codegen and host function integration.
Changes to Chasm Gradle plugin
- a new optional property
ignoredExportsis now present when configuring interface gen from wasm modules, any export names present in this set will be excluded from the interface gen. - a new allocator property is now present on WasmModules which lets you specify an ExportedAllocator
- a new
stringParamfunction is available when specifying module function definitions - errors that occurs during codegen will now be correctly reported through stderr
1.0.0
Well... a year and a half later that "side project" is now ready. If you've been following Chasms progress you'll know it's been quite a journey! Thank you to all the people that used Chasm and reached out and gave feedback, if it wasn't for the help it wouldn't have been possible ❤️
Wasm 3.0
The Wasm 3.0 spec is on the cusp of being published and I've spent the last couple of weeks preparing for it. In short it takes a bunch of proposals (FuncRefs, TailCall, GC, Exceptions, Memory64 and more) and merges them into the latest iteration of stable Wasm. Chasm supports all of Wasm 3.0 with the exception of Memory64.
Reasons for not supporting Memory64
Memory64 requires larger pointers, memories and tables , which ultimately result in more overhead , more memory and slower runtime performance. Realistically workloads that require more than 4GB of memory are not well suited for Chasm in its current form and given we're only shipping one artefact its not worth slowing the 99% (32 bit workloads) for the 1%. I have plans for supporting this in the future but thats a long way down the road.
Upgrades to the Gradle plugin
Chasms Gradle Plugin now implicitly wires up the correct chasm runtime depending on the Kotlin plugin and targets configured. This was important as the Plugin depends on the tooling api which ships with the runtime and thus it was possible for a user to previously wire up a version of the runtime which is not compatible with the plugin.
As a result you can now configure whether the runtime is hooked up as an API or IMPLEMENTATION dependency using the Gradle plugin extension:
chasm {
// default is IMPLEMENTATION
runtimeDependencyConfiguration = RuntimeDependencyConfiguration.API
}Similarly chasm now supports configuration of type visibility for the Interface and Implementation its plugin generates:
chasm {
modules {
create("TestService") {
// default is PUBLIC
interfaceVisibility = TypeVisibility.PUBLIC
// default is IMPLEMENTATION
implementationVisibility = TypeVisibility.INTERNAL
}
}
}Unified Versioning
Chasms runtime and Gradle plugin now use the same version (1.0.0), given the plugin now implicitly wires up the runtime it made sense to align them so its easy to understand what version of the runtime is being used.
Stable ABI
For the past year the API for chasm has been in flux whilst I tried to figure out the best way to expose the virtual machine and the metadata associated with it. I'm now happy to say its stable and ready to be used, as a result we're now leveraging the experimental binary compatibility validator that shipped with KGP 2.2.0 to ensure we don't accidentally alter the ABI in unexpected ways between releases of the library.
Removal of the statically linked rust memory library
Last year I swapped out the linear memory implementation for native targets with a statically linked rust library. The intention was to enable efficient atomics for the upcoming threads proposal, unfortunately it doesn't look like the threads proposal is going to make it into the Wasm 3.0 release and it needs a little more time in the oven. Leveraging the native memory implementation has drawbacks in that it needs to be deallocated (technically unmapped), the cognitive overhead this adds to the api is not worth it given we don't support threads yet. For that reason its been removed... and yes we've lost our fancy SIMD string searcher for the time being
Better errors when encountering unsupported Wasm binaries
Previously when executing binaries that feature unsupported instructions the runtime would return a generic InstructionNotFound error. The past couple of weeks I've built support for decoding and validating newer (not yet supported) proposals so we can identify them and return better messaging. For example, given a Wasm binary that features Memory64 instructions, chasm will now return an error 'UnsupportedMemory64Proposal'
Full Changelog: 0.9.81...1.0.0
0.9.81
Chasm
Support for names decoding and the extended names proposal
Names sections are customs sections found in wasm binaries which contain names (Strings) used by debuggers and tooling. Previously all custom sections were left uninterpreted and none of their data was surfaced through the api.
This release adds support for optionally decoding the Names Section by providing the following flag in your module config
val moduleConfig = ModuleConfig(
decodeNameSection = true,
)When set the Name Section and all of its subsections will be decoded internally, for now we are only surfacing Function Names data through the api, this can be achieved by calling the moduleInfo api call.
val info = module(binary.get().asFile.readBytes(), moduleConfig)
.map { module ->
moduleInfo(module)
}Chasm Gradle Plugin 0.2.2
Simultaneously we're releasing 0.2.2 of Chasms gradle plugin, it has the following improvements
Improved default parameters in the Chasm Gradle plugin
Using the new module info api names data, the Gradle plugin is able to now give parameters their original names.
For example previously if you have a function like this:
(func $foo (param $a i32)(param $b i32))The gradle plugin would previously produce the following as the param names are not included in the primary module
data.
fun foo(p1: Int, p2: Int): Unit = UnitNow using the new names data the plugin will correctly generate:
fun foo(a: Int, b: Int): Unit = UnitFull Changelog: 0.9.80...0.9.81
0.9.80
Chasm
Bugfixes
- Fixed issue wherefore when calling
readNullTerminatedUtf8Stringwould manipulate memory bounds - Fixed issue where calling
readByteswould not respect the buffer pointer on jvm targets
Windows Support
Windows support has now returned with this release! We previously had issues when compiling with kotlin versions < 2.2.0 as the kotlin native compiler was not leveraging the linker (lld) bundled with LLVM and instead was using a older version which had issues with the static libraries use to represent chasms linear memory implementation. This is now resolved and windows is back in action, consequently CI builds are slower again 🤣
New APIs
There are now api calls that allow you to read and write scalars to memories
readIntwriteIntreadLongwriteLongreadFloatwriteFloatreadDoublewriteDouble
Chasm Gradle Plugin
Chasms Gradle Plugin now allows you more control over the codegen for functions, configurable through the Gradle plugin extension you can specify parameter names and String encoding strategies.
Configuring parameters
For example say you have the following wasm function:
(func $multiple_param_function (export "multiple_param_function") (param i32 f64) (result f64)
local.get 0
f64.convert_i32_s
local.get 1
f64.mul
)You can now configure the names of the params through the gradle extension
chasm {
modules {
create("TestService") {
binary = ...
packageName = ...
function("multiple_param_function") {
intParam("a")
doubleParam("b")
doubleReturnType()
}
}
}
}Configuring string encoding
For example say you have a function in your wasm module which returns a string encoded in two integers:
(func $string_function (export "string_function") (result i32 i32)
i32.const 1
i32.const 18
)You can now tell the codegen to produce a string by providing a function definition in the gradle plugin extension
chasm {
modules {
create("TestService") {
binary = ...
packageName = ...
function("string_function") {
stringReturnType()
}
}
}
}Different compilers use different strategies to encode strings into memory, the codegen supports the following strategies:
enum class StringEncodingStrategy {
/**
* The pointer and length of the string are encoded in two I32s
*/
POINTER_AND_LENGTH,
/**
* The pointer of the string is encoded in an I32 and end of the String is the first null byte
* encountered after that pointer
*/
NULL_TERMINATED,
/**
* The pointer of the string is encoded in an I32 and length of the string is encoded in an int
* found at that pointer with the string following
*/
LENGTH_PREFIXED,
/**
* The pointer and length of the string are encoded in a single I64
*/
PACKED_POINTER_AND_LENGTH,
}By default codegen will assume pointer and length encoding, but the encoding can optionally given in the configuration
chasm {
modules {
create("TestService") {
binary = ...
packageName = ...
function("string_function") {
stringReturnType(StringEncodingStrategy.NULL_TERMINATED)
}
}
}
}As a result of the above configuration the CodegenConfig property transformStrings has been removed.
0.9.71
ABI Corrections
Some external types are now correctly surfaced through chasms ABI which were previously inaccesible
Chasm Gradle Plugin
Versioned separately to chasm's runtime, chasm now exposes a gradle plugin which generates a typesafe Kotlin interface that allows you to interact with wasm binaries entirely agnostic of chasms lower level virtual machine api. Full documentation can be found here.
The plugin supports kotlin.jvm, kotlin.multiplatform and android modules, following the prior links will direct you to demonstrations of each within the example project
Example usage
[plugins]
chasm = { id = "io.github.charlietap.chasm.gradle", version.ref = "0.1.0" }
[libraries]
chasm-jvm = { module = "io.github.charlietap.chasm:chasm-jvm", version.ref = "0.9.71"}plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.chasm)
}
chasm {
modules {
create("ExampleService") {
binary = layout.projectDirectory.file("src/main/resources/example.wasm")
packageName = "com.foo.bar"
}
}
}
dependencies {
implementation(libs.chasm.jvm)
}0.9.70
Wasm GC Strategies
Chasm now supports three GC strategies configurable through the RuntimeConfig:
- Arena (Default)
This is the default mode of operation. In this mode, chasm will rewrite the bytecode of your Wasm functions to include a single
unconditional pause at the end of the function's execution, removing all GC objects which are no longer referenced. It's recommended
to use this mode unless you run into out of memory issues as the alternative (Traditional) requires costly heap checks.
- Manual
In this mode, chasm will perform no heap checks and will not pause for garbage collection. It's expected the user monitors the heap size and manually triggers garbage collection using the GC public API
- Traditional
This mode of operation functions like a traditional garbage collector. Chasm will rewrite the bytecode of your functions and insert heap
checks after every allocation. If the heap exceeds the threshold configurable in the RuntimeConfig, a pause will run, removing all dead
references.