A personal journey into LLVM

Hey everyone,

I know it’s been a while since I’ve posted anything meaningful but I wanted to share a project that I’ve been working on since the xmas holidays.

I was bored apparently. I decided to learn how compilers work and how to write my own language and frontend for LLVM. The result of this journey is giving me a lot more respect for the folks at my old job and those that came before me, if just in the sheer complexity of it all.

Now I did use a lot of what I learned between 2011 and 2022, but I am also fixing a lot of the things that were just wrong IMHO. I’m adding language features that should and could have existed a long time ago.

If you’re curious… I’m calling it Buoy. If anyone is interested, I’ll put together a package for people to look at. Currently it can only compile on macOS, but Windows and Linux are coming soon.

Buoy vs X: Language Differences

This guide is for developers coming from X. Buoy was designed to feel familiar: the same style syntax, the same native-UI philosophy, and the same Automatic Reference Counting memory model. Most code you write in Buoy will look like code you have already written in X.

But Buoy adds a command-line compiler with full cross-compilation, a generics system, several new language features, and a different toolchain philosophy. This document calls out the differences so you know what’s new, what’s changed, and what isn’t there yet.


Toolchain & Compilation

Command-line compiler

Buoy is always command-line first:

buoy hello.bui -o hello
buoy hello.bui --target arm64-linux -o hello
buoy hello.bui --target x86_64-windows -o hello.exe

There is no GUI build step.

Cross-compilation without target SDKs

From a single macOS host you can compile for all six supported targets:

Target --target value
macOS arm64 arm64-darwin
macOS x86_64 x86_64-darwin
Linux arm64 arm64-linux
Linux x86_64 x86_64-linux
Windows arm64 arm64-windows
Windows x86_64 x86_64-windows

None of these targets require the platform SDK installed on the host. Buoy ships pre-built per-target runtimes and SDK stubs. You can cross-compile to all six from one machine.

Distributable compiler binary

The buoy binary vendors LLVM and lld as static libraries. It has no dependency on Homebrew or any system package manager after installation. You can put it on a CI server, copy it to another machine, and it just works.

Declarative UI vs visual designer

X uses a drag-and-drop visual designer embedded in the IDE. Buoy uses textual Window … End Window blocks instead.

Window MyWindow
  Title = "Example"
  Frame = (100, 100, 400, 300)

  Label lblHello
    Text = "Hello, world!"
    Frame = (20, 20, 360, 30)
  End Label
End Window

These blocks lower to a Window subclass at compile time. A round-trip visual layout editor is under construction but not yet shipped. The VSCode extension provides syntax highlighting, go-to-definition, and diagnostics via the Buoy language server.

Optimization

Buoy exposes LLVM optimization levels directly:

buoy app.bui -O2 -o app
buoy app.bui -Os -o app   ' optimize for size

Syntax Familiar from X

These work exactly as you expect:

  • Dim x As Integer / Var x As Integer (Var is an exact synonym for Dim)
  • Const Pi As Double = 3.14159
  • If … Then … Else … End If
  • For i As Integer = 0 To 9 … Next
  • For Each item In collection … Next
  • While … Wend / Do … Loop
  • Select Case … End Select
  • Function, Sub, Return
  • Class, Interface, Module
  • Inherits, Overrides
  • Me, Super, New
  • Implements
  • Try … Catch … Finally … End Try / Raise
  • Delegate Function, AddressOf
  • Event, Handle, RaiseEvent
  • Property … Get … Set … End Property
  • Shared fields and methods
  • True, False, Nil
  • Single-quote ' line comments

Similar but new

  • Import for modules
  • Abstract, Final
  • Assert
  • Async, Await, Task
  • Atomic
  • With and End With
  • Block Literals (inline functions) on MacOS
  • Compound assignments +=, -=, *=, /=
  • Mutex Conditions
  • #Define, #Undef
  • Generics
  • Multi-line comments
  • Exported functions
  • Lazy properties
  • Optional and Result
  • Optional chain

New Language Features

Generics

Buoy has a full generics system. Classes, interfaces, free functions, and instance methods can all be parameterized over types:

Class Box Of T
  Dim Value As T
End Class

Dim b As New Box Of Integer
b.Value = 42
Function Identity Of T(x As T) As T
  Return x
End Function

Dim n As Integer = Identity(42)   ' infers Of Integer at the call site

Generics are monomorphized at compile time: the compiler produces a concrete copy of the class or function for each distinct type argument. There is no runtime erasure and no boxing overhead.

Optional chaining (?.)

The ?. operator guards an assignment against a Nil receiver. If the receiver is Nil, the assignment is silently skipped:

Dim w As Window = GetWindowMaybe()
w?.Title = "Updated"   ' no-op if w is Nil

This is parse-time sugar for:

If w <> Nil Then
  w.Title = "Updated"
End If

Optional and Result types

Alongside Try/Catch, Buoy provides error-as-value types for cases where you want to propagate absence or failure without exceptions:

Dim result As Optional Of Integer = ParseInt("abc")
If result.HasValue Then
  Print result.Value.ToString
End If
Dim r As Result Of String, IOError = File.ReadAllText("/etc/hosts")
If r.IsSuccess Then
  Print r.Value
Else
  Print r.Error.Message
End If

You are not forced to choose between the two styles — use exceptions for unexpected failures and Optional/Result for expected absence or errors at API boundaries.

Extension methods

You can add methods to any existing type without subclassing:

Extension Function Double Seconds(self As Integer) As Double
  Return self * 1.0
End Extension

Callers write 3.Seconds() just like an instance method. This is how the standard library adds instance-method sugar to built-in types (s.Len(), arr.Count()).

Operator overloading

User-defined types can overload arithmetic, comparison, and equality operators using the Operator keyword:

Class Vector2D
  Dim X, Y As Double

  Operator +(other As Vector2D) As Vector2D
    Return New Vector2D(X + other.X, Y + other.Y)
  End Operator
End Class

Conditional compilation with architecture predicates

#If directives support both platform and CPU predicates:

#If TargetMac
  ' macOS-only code
#ElseIf TargetWindows
  ' Windows-only code
#ElseIf TargetLinux
  ' Linux-only code
#End If

#If TargetArm
  ' ARM code (any platform)
#ElseIf TargetIntel
  ' x86 / x86-64 code
#End If

#Define / #Undef let you define custom predicates. #Error "message" aborts compilation from a branch. Platform-specific Import statements inside an excluded #If branch are not resolved — a missing module in an excluded branch is not an error.

Shared library export

Mark functions as public C-ABI exports with the Export modifier and build a shared library with --library:

Export Function Add(a As Integer, b As Integer) As Integer
  Return a + b
End Function
buoy mylib.bui --library -o mylib.dylib

This produces a .dylib / .dll / .so with a C-callable Add symbol. You can use this to build plugins that other languages (including X) can consume. I have already built a cross-platform X Plugin using this mechanism.


Differences in Shared Concepts

FFI / Declare

Both Buoy and X use Declare Function … Lib "…" syntax. Buoy extends it:

Feature Buoy Xojo
Sized integers Int16, UInt16, Int32, Int64 for C ABI accuracy Limited
ByRef Pass a value by address for C out-parameters Yes
Sequential/Packed structures Sequential Structure / Packed Structure — exact binary layout for C struct marshalling No
Variadic C functions Declare Function Printf Lib "c" (fmt As String, ...) No
Pointer arithmetic CType(ptr, Integer) / CType(n, Ptr) / CType(ptr, String) for inline pointer math and C-string extraction No
Soft Declare Not available — use #If branches instead Yes

Delegates

Both languages use Delegate Function Name(params) As T and AddressOf. In Buoy, delegates are fully FFI-interoperable:

  • A Buoy delegate can be passed directly to a C function expecting a function pointer.
  • A function pointer received from C (As Ptr) can be cast to a delegate type with CType(ptr, DelegateType) and called normally.
  • MarshalToMainThread(AddressOf Foo) synthesizes a C-callable wrapper that marshals a callback onto the main thread.

Objective-C interop (macOS)

Both Buoy and X support Declare … Selector "msg:" for Objective-C message sends. Buoy adds ObjC block literals:

' Inline ObjC block — passed to APIs that expect a block argument
Dim handler As Ptr = Block
  Print "callback fired"
End Block

' Dispatch work to the main thread via GCD
Block MainThread
  label.Text = "done"
End Block

Guard with #If TargetMac.

Memory management

Both Buoy and X use Automatic Reference Counting — objects are freed deterministically when the last reference drops, with no GC pause.

Buoy additions:

  • Weak ClassName — weak reference that reads as Nil after the target is freed (breaks retain cycles)
  • buoy --debug — links a leak detector that prints a report at exit (macOS and Linux only)

Current Buoy v1 limitation: ARC is not yet applied to class references stored inside Array or Structure fields. This is planned feature for a future release.

Threading

Buoy provides four additional thread synchronization primitives:

Primitive What it does
Atomic Lock-free 64-bit integer: Increment, Decrement, Load, Store, Exchange, CompareAndSet
Mutex / SyncLock Mutual exclusion; SyncLock releases on all exit paths, including exceptions
Condition Condition variable: Wait, Signal, Broadcast — always paired with a predicate loop inside SyncLock
RunLoop / MainThread Main-thread work queue: MainThread.InvokeAsync, InvokeSync; RunLoop.Pump, PumpOnce

All five primitives lower directly to pthreads (macOS/Linux) or Win32 threads (Windows) with no runtime overhead.

Error handling

Both languages use Try/Catch/Finally/Raise. Buoy also provides Optional Of T and Result Of V, E for error-as-value patterns (see above). There is no forced migration — use exceptions for exceptional conditions and the value types for expected absence or recoverable errors.

String formatting

Buoy’s String.Format supports two styles:

  • VB.NET-style format specifiers: String.Format("{0:0.00}", value)
  • BASIC picture masks: String.Format("##.##", value)

Both are accepted.

Task / Await / Async

Task represents a unit of background work. Task.Run spawns an OS thread, runs the given function, and returns an ARC-managed handle. The caller can block until the work finishes with task.Wait() or poll for completion with task.IsComplete().

Mark a Function or Sub with Async to indicate it performs work that may eventually suspend. Call an Async function with Await to retrieve its return value (or wait for it to complete).

Automatic macOS SDK Updates

To update an application to a newer version of the macOS SDK, you only need to update Xcode on your machine and then recompile your bouy app. Minimum macOS versions are still defined as a command-line option.


UI Controls

Buoy implements a native-widget-per-platform philosophy (AppKit on macOS, Win32 on Windows, GTK4 on Linux). Controls minimal but currently working:

Control Notes
Window Top-level native window
Label Static text
Button Clickable button
ImageView Displays a Picture
ProgressBar Progress display with Indeterminate mode
Slider Range slider with ValueChanged event
TextField Single-line text input; FieldType enum (Normal, Password, Email, Number)
Checkbox Tri-state toggle (False / True / Indeterminate)
TextArea Multi-line text input
RadioGroup + RadioButton Mutually-exclusive selection
ComboBox Dropdown; pick-only or editable
ListBox Virtualized multi-column grid with ListBoxDataSource data binding
View Container for child controls
TabView Tabbed container
PagePanel Tab-less page stack
Canvas Custom 2D drawing
MenuBar / Menu / MenuItem Application and per-window menus

Every control exposes a NativeWidget As Ptr property for platform Declares against the raw OS handle (NSView, HWND, GtkWidget).


What Buoy Does Not Have Yet

Feature Status
Visual IDE / RAD designer VSCode extension with LSP is available; drag-drop designer is under construction.
Drag and drop Under research
Xojo Plugin SDK (*.xojo_plugin) Buoy can build plugins via Export and --library; it cannot consume existing Xojo plugins

Wow, this sounds very impressive!! If there is something to test, i’m in. Macbook Pro M1 Max on Tahoe.

Really impressive !

StackView would be a nice addition to the list of controls.

Suggesting to use VSCode as an IDE is the way to go. Don’t lose time on reinventing the wheel, focus on compiler/language features :slightly_smiling_face:

I’ll put something together today.

I’ll say this now, writing code this way is not for the feint of heart. I don’t have a converter for X and there are still a lot of language features missing. It’s amazing how after all those years of using an IDE that VSCode feels so foreign for this type of project.

There are compilable samples for just about everything and unit tests whose numbers just passed the 530 mark last night.

AI Usage

For the AI people out there… with all the examples and the command line compiler, Claude has gotten amazingly adept at writing code in Buoy. Most of the language features have been designed with Claude, and some of the more complicated items even implemented with it with a thorough review. For instance, WithEnd Withwas added last night with the help of Claude and works out of the gate.

Have there been issues with using AI? Of course. I argued with Claude for three nights trying to get it to understand the concept of a macOS Block being automatically redirected back to the main thread, with an ultimate cost of about 3.5 million tokens… and I use several token optimizers to keep those numbers low. Thank god for token overage limits.

Stackview should be relatively easy to implement in Bouy and I think it’s already on the list of things to do. If not, I’ll add it.

For anyone who is curious…

Control position is just x, y, width and height by default. From the beginning, I wanted to use autolayout anchors as they are by far the easiest entry point aside from SwiftUI IMHO. What I found was that I got X-style locks for free, so control positioning is:

  1. Absolute first
  2. Lock with auto anchor conversion
  3. Autolayout Anchors
  4. Autolayout Constraints

Editors

As far as VSCode goes, I’m not at 100% features yet, but I’m getting there. Autocomplete suggestions largely work but still need tweaking. I still need to make it so you can compile and debug directly, but it’s not overly tedious to compile and read compile errors yet.

There is the start of a layout editor. I was saying yesterday that it’s amazing how tedious it is to create a layout editor without a layout editor. But we’ll get there.

My goal is to be able to write my little tools I do for my day job in this rather than X. If only to remove my reliance on a language that I only use once in a while and now costs way too much.

For anyone that wants to give this a whirl, this is today’s build:

Buoy build 3311052

A few things to note:

  • I feel that this is still very much a 0.0.1 version. A lot works and a lot crashes.
  • The build is signed but not notarized yet, so you’ll need to remove the quarantine flag
  • In a terminal, type buoy -h to see all of the command-line options
  • The editors folder contains the current VSCode extension and instructions on how to install it.
  • The lib folder needs to be next to the buoy compiler in the same folder.
  • The docsfolder contains raw documentation files, tutorials and a folder called learning which is laid out like a book to learn programming via buoy.
  • The samples folder contains sample projects that I and Claude have created as features came into being. They should all compile, standalone.
  • The first draft of the layout editor is available in samples/ui/layout editor/layout-editor.bui. You can compile it with:
    buoy /path/to/layout-editor.bui —target <your build target> -o <your build name>

Available Targets:

arm64-darwin
x86_64-darwin
universal-darwin
x86_64-linux
arm64-linux
x86_64-windows
arm64-windows

These three targets are only available for Xojo plugins and external libraries at this time:

arm64-ios
arm64-ios-sim
x64_64-ios-sim

Build Names

Output files help determine the form of the output. If you are building a gui app for macOS, make sure you include the .appextension to get the bundle. Otherwise you’ll just get the executable binary.

if any of you find issues, please file an issue on this GitHub repo.

https://github.com/stretched-out-software/buoy-issues/issues

just put in first issue on GitHub

yup sorry. I didn’t test Mac builds on any machines other than my dev machine :man_facepalming:

Buoy Download

BTW - this build includes a first draft at a cross-platform stackview and fleshed out intrinsicWidth.

I am using macos tahoe

Is the new build still failing?

I have my first hello-world compile running, nice :slight_smile: Only 34 Kbyte!

./buoy ./samples/hello.bui --target=arm64-darwin -o hello --app-type='console'

We are out for dinner . It is our 22th anniversary today

Congratulations to both of you and have another 100 aears in happyness, lov an friendship :slight_smile:

As an alternative to VS Code, I assume it would be possible to create a Buoy language module in BBEdit? …This could be fun little side project for me, or someone :thinking:

I’m not married to the idea that I need a GUI drag-and-drop designer. After all I’ve been writing SwiftUI for the past year with the Preview pane disabled. But as much I love building small native macOS apps using Swift, I still miss the Basic syntax.

Between you and Garry’s project, it’s great to see that the Basic syntax could live on for quite some time on macOS. Thank you for your efforts :slight_smile:

Honestly there are very few things that I go to BBEdit for any more. My whole life is pretty much VSCode, Terminal and Lens these days.

That’s understandable.

But I’m still much more of a traditionalist and purposely avoiding all that AI stuff.

Do you mind if I (or someone else) give the BBEdit thing a go?

I’ve never built a language module for BBEdit of course, so I don’t know if it’s possible or if the idea is just too early?

Go for it.

I was right with you until my company basically required us to start learning. I’ll tell ya, it’s not cheap, certainly costs more than a yearly subscription to X, but I’m able to do so much more with the $100/mo subscription now that I’ve learned the ropes.

Last month, I blew through my company sponsored credits on May 8th. It was an interesting discussion to have with my boss who had been pushing AI to say, you got me hooked and I’ve adjusted my workflows to use AI agents, so either I go back or you need to pay for more.

'Course that was before Anthropic decided to quadruple their fees.

just got back and tested.. working now