This is a curated log of small progress updates and changes happening to Orca. This log is also available as an RSS feed.
I recently merged changes that touch two core idioms of the Orca codebase.
Allocator Interface:
I added an “allocator interface” that is meant to replace the arena parameter in most memory-allocating functions. This allows passing a different allocator implementation such as a heap or a pool when needed. The interface exposes a single function to push data onto the allocator, with a few variants for alignment / initialization and typed objects or arrays allocation. A concrete allocator has a pointer to its allocator interface that you can pass to allocating functions, eg:
oc_str8 str = oc_str8_pushf(arena->allocator, "Hello, world!");
// or
oc_str8 str = oc_str8_pushf(heap->allocator, "Hello, world!");Deallocation is controlled by the caller using the concrete allocator API (e.g. freeing or clearing), e.g.:
oc_arena_clear(arena);
// or
oc_heap_free(heap, str.ptr);If the callee needs to free things before returning, this means it can allocate them on a callee-owned allocator (e.g. a scratch arena), so the generic allocator interface never needs to expose an API to free memory.
Typed Linked Lists:
I added a new intrusive linked-list type that is generic and type-safe, to gradually replace untyped linked lists. This uses a neat trick to attach type info and integer constants to a type definition without increasing the memory footprint of that type. I describe this technique in more details in this blog post. Usage is as follows:
// Define a type that is "listable":
typedef struct my_type
{
// this makes my_type listable
oc_typed_list_links links;
f32 x, y;
} my_type;
// Declare a list type that can hold items of type my_type, linked through the 'links' field.
typedef oc_typed_list(my_type, links) my_type_list;
// Create a list and push some items
my_type_list list = {0};
my_type t1 = {.x = 0, .y = 1};
my_type t2 = {.x = 2, .y = 4};
my_type t3 = {.x = 3, .y = 0};
oc_typed_list_push_back(&list, &t1);
oc_typed_list_push_back(&list, &t2);
oc_typed_list_push_back(&list, &t3);
// Loop through the list and print items
oc_typed_list_for(list, elt)
{
printf("(%f, %f) ", elt->x, elt->y);
}The list is type safe, i.e. trying to insert an element of another type than it was decalred for would fail to compile. The list is also safe with respect to which field it uses to link nodes, which is very useful when elements can be in multiple lists using several oc_typed_list_links fields.
This update introduces major changes in the distribution model of both the Orca tooling and Orca apps:
.orca files that contain only the WebAssembly modules and the app’s resources. (You can still create standalone apps if needed by passing the appropriate flag to the CLI tool.).orca apps, and as the tooling to bundle apps.Orca.app/Contents/macOS to your PATH environment variable, you can then run orca bundle --name MyApp main.wasm to bundle an app, and orca run MyApp.orca to run it.This update also introduces several related changes to the following APIs or internal systems, including:
oc_listI made some modifications to the layout algorithm for our user interface system. There’s still some polish and testing to do so this is not merged yet, but here are the main changes in flight:
relax attribute, which specifies by which amount a box can be shrunk when there’s not enough space in its parent.floating attribute to a position attribute (which controls how a box is positionned) and a footprint attribute (which controls how a box takes space in its parent).wrap attribute which allows wrapping children boxes to newlines when they exceed their parent’s size in the main layout axis. This part required substantially refactoring the layout algorithm, as you can’t treat each axis completely separately anymore, and contraints in one axis can invalidate computations you did in the other.I also started a small experiment on a “code canvas” where you can arrange cards, that could later hold code editors for individual functions or type declarations, or graphical widgets to display and manipulate live data. Depending on what I find working on this, it might become a basis for exploring code, call graphs, and variables in the integrated debugger, and/or to directly modify running Orca apps.
Here is an early proof of concept of the spacing and grouping behavior:
In september I have been working on the architecture for “bare” Orca apps (as opposed to standalone apps that are bundled with the runtime executable). In this model, the app is distributed as a zipped folder containing the app’s resources and WebAssembly modules, and run by a trusted runtime on the user’s machine.
This also changes the distribution of the Orca environment itself, as we now have three components to distribute:
Conveniently, this can be distributed as a single application which contains these three components. By default, this application opens as the launcher, but it can also be called from the command line to give access to the developper tooling and SDK. For example, on Windows you could double click on Orca.exe to open the launcher, but you could also use ./Orca.exe bundle --name MyApp main.wasm to package main.wasm into a new Orca app.
In the process of working on this, I started implementing a few utility APIs that will also become part of the SDK:
That’s all for now! see you next time, and take care ~
Our custom WebAssembly backend is now merged, and replaces our previous third-party interpreter (wasm3). This gives us freedom to design the interpreter in a way that supports our integrated debugger, and will also allow us to experiment with interpreter tech and WebAssembly extensions.
The new backend is called Warm, for WebAssembly Register Machine. Upon loading a module, it performs validation and compiles its functions to a custom unstructured control flow, register-based bytecode (as opposed to the WebAssembly structured control flow stack machine model). This eliminates stack book-keeping by encoding the source and destinations operands in the instructions themselves, and unfolds control flow down to simple jumps. The virtual machine then is a dead-simple loop-over-switch interpreter.
The backend passes the core wasm testsuite. It is currently slower than wasm3 because it doesn’t use threaded code yet. So one obvious avenue for improvement is to do that, either with tail calls (what wasm3 does) or computed gotos (my preference for debuggability and readability reasons, but we’ll have to try). Another one is to do a better job at register allocation, as I chose the very simplest strategy (an infinite register file and a freelist) for this first iteration. Another thing I want to try is to have a simple template JIT, as an intermediate step towards more optimized JIT tiers.
We just landed a file enumeration API in the Orca WebAssembly SDK. This allows apps to query for all files within a given directory, which is performed in a single native call, and then iterate through them on the wasm side. An example with the C API:
void printFilesInDir(oc_arena* arena, const char* path)
{
oc_file dir = oc_file_open(path, OC_FILE_ACCESS_READ, OC_FILE_OPEN_RESTRICT);
if (oc_file_last_error(dir)) {
return;
}
oc_file_list entries = oc_file_listdir(arena, dir);
if (oc_file_last_error(dir)) {
return;
}
oc_file_list_for(entries, elt) {
oc_log_info("Found '%.*s' with type %d\n", oc_str8_ip(elt->basename), elt->type);
}
}
There are still a few holes in the file IO API, but we’ll be working to flesh that out in the coming months.
In addition to regular unit/integration tests, Orca has test apps that have a special oc_on_test() hook. The runtime runs this hook and checks the exit code, relying on the app to log any failures. This can be used to test specific parts of the Orca SDK from the wasm side. These test apps are built and bundled the same way as normal Orca apps. Some recent improvements to the build system now allow bundling apps without requiring a system-wide install of the orca CLI tool. Now the CI can build and run the wasm tests just like the native tests, giving better visibility on potential SDK API breakage.
I fleshed out our Github Sponsors page and added some monthly tiers. They come with symbolic rewards, from getting a distinguishing role in the Orca Discord server, to having your name and link to your homepage listed at the bottom of the landing page of the Orca website. Higher tiers are a good option for companies that want to support Orca through Github Sponsors, and include some amount of consultancy. I also plan on later adding access to private repos to specific tiers, to share bonus content with sponsors (think tutorials, screencasts, or advanced example programs).
If you want to help our work, now is the perfect time to do so :) Thanks for your support!
Martin
We just merged the new Zig build system to replace our old Python build script. Reuben wrote about it here.
The Orca debugger is shaping up. It can pause on breakpoints and step in and out through source code or instructions, navigate call frames, and inspect most variables in unoptimized code.
Our WebAssembly Debugger can now single step through source lines.
I started working on an integrated debugger for Orca. Here’s its very first steps, moving through the bytecode. You’ll notice it’s not WebAssembly bytecode. Instead our custom backend compiles WebAssembly to a register-based machine with jumps for more efficient interpretation (and later JIT-ing).