Skip to content

Instantly share code, notes, and snippets.

View mlugg's full-sized avatar

Matthew Lugg mlugg

View GitHub Profile
@mlugg
mlugg / inject_debug_info.zig
Created October 9, 2025 12:48
linker script and binary-modifying tool for a freestanding SelfInfo impl
//! This is the bespoke script which finds the DWARF sections and copies them all to where we want
//! them in the `.rodata` section. It's quite primitive, but works fine for ClashOS at least! I'm
//! not going to explain this toooo in-depth; if you want to understand it you'll need to know a bit
//! about how ELF files are structured, feel free to ask me.
// zig fmt: off
/// You might need to modify this, since RISC-V binaries might have some slightly different fields
/// here compared to AArch64 binaries.
const expect_e_ident: *const [16]u8 = &.{
0x7F, 'E', 'L', 'F',
@mlugg
mlugg / debug.zig
Created October 9, 2025 12:22
well-commented basic freestanding SelfInfo implementation
//! This whole file is meant to be exposed as `@import("root").debug`. Everything `pub` here is an
//! override which the standard library will use.
/// This is the allocator which `std.debug` will use to allocate debug info. On freestanding you
/// usually just want to use a `std.heap.FixedBufferAllocator` for this---unless you have a proper
/// memory manager working of course!
pub fn getDebugInfoAllocator() Allocator {
const Global = struct {
/// If you see OOM errors in stack traces, try increasing the size of this buffer. This is
/// 16 MiB, which is about 4x what ClashOS has been observed to use. The amount of memory is
@mlugg
mlugg / debug.zig
Created September 4, 2025 22:43
ClashOS 0.15.1-ish port
pub fn wfe_hang() noreturn {
while (true) {
asm volatile ("wfe");
}
}
const source_files: []const []const u8 = &.{
"debug.zig",
"video_core_mailboxes.zig",
"bootloader.zig",
@mlugg
mlugg / StacklessEventLoop.zig
Last active September 11, 2025 19:58
Theoretical implementation of parts of `std.Io` in Zig based on #23446
gpa: Allocator,
/// I don't know how io_uring works, so my usage here is going to be total bollocks.
/// I'm going to never init this, and I'll write:
/// ```
/// io_uring.pushSqe(.{
/// .op = .sleep,
/// .arg0 = clockid,
/// .arg1 = std.time.ns_per_s,
/// .user = ptr,
/// });
@mlugg
mlugg / incremental.md
Last active March 23, 2025 01:19
Incremental compilation overview

Zig Compiler: Incremental Compilation

A few ground rules:

  • Values in the InternPool are immutable. The meaning of a given Index never changes.
  • The only kind of "exception" to this is that the namespace of types may change. The namespace index is constant, but the contents of that namespace can change while the type lives at the same Index.
  • Incremental compilation works at the level of single units of semantic analysis (AnalUnit). During semantic analysis, every AnalUnit registers "dependencies" on various things ("dependees"). On each incremental update, these dependencies are used to figure out what to re-analyze.
  • An incremental update may analyze more AnalUnits than are necessary, but it will never analyze the
@mlugg
mlugg / decl.md
Last active March 12, 2024 14:20
Zig Wars: Episode III -- Revenge of the Decl

The Problem

Decl has two main responsibilities today.

The first is to act as the "subject" of semantic analysis for anything analyzed in a comptime context. For instance, when analyzing the value of a container-level const, that declaration's Decl is the "owner" of that Sema; errors are marked on it, source locations resolved relative to it, etc. This is also where type owner decls come from - we need some context in which to perform type resolution, so we need a Decl associated with the type itself.

The second is to represent a globally named and/or addressable value. For instance, container-level consts are named and addressable. More interestingly, so are generic instantiations - you can't take their address in Zig today, but they sure as hell have one, and same with name. This is where function instance owner decls come from.

Interestingly, above, we found that owner decls for types and owner decls for function instances are fulfiling two totally separate purposes! To me, tha

@mlugg
mlugg / incremental.md
Last active September 28, 2023 02:34
Incremental Compilation and Dependency Tracking

Definitions

A Decl, like today, is either:

  • A struct, union, enum, or opaque (i.e. a type with a namespace)
  • A container-scope const, var, or fn

Types are immutable. Whenever a type declaration is analyzed, it gets it from the InternPool, keyed on the current generation (so re-analysis of a type declaration across generations always creates a distinct type). The existing owner decl is used, but its value updated to refer to the new type.

@mlugg
mlugg / FastList.hs
Last active November 1, 2020 21:23
Haskell implementation of lists with fast cons/snoc/append
{-
- This is free and unencumbered software released into the public domain.
-
- Anyone is free to copy, modify, publish, use, compile, sell, or
- distribute this software, either in source code form or as a compiled
- binary, for any purpose, commercial or non-commercial, and by any
- means.
-
- In jurisdictions that recognize copyright laws, the author or authors
- of this software dedicate any and all copyright interest in the
{-# LANGUAGE Trustworthy, OverloadedStrings, LambdaCase #-}
module Main where
import Network.Socket
import Network.Socket.ByteString
import Network.Run.TCP
import Common
import Parse
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
module Main where
import Control.Applicative
import Text.Printf