Sign in

Alice ✨

@welltypedwit.ch
3K followers 343 following 6.6K posts

There’s no sense crying over every mistake. You just keep on trying ’til you run out of cake - GLaDOS welltypedwit.ch

PostsRepliesMedia
Alice ✨ @welltypedwit.ch · 03/10/2026
i don't have a blog post yet, but i do have this sketch i drew at 3 am so surely that should clear everything up
(oh no i did not consider that i need to explain it in the alt text)
diagram of a heap in 4 stages. in the first stage (labelled mutator), one object is marked with a "CR-Header (lazy)" info table.

in the second stage (labelled GC), there are two semi spaces and the objects are copied from one to the other.
the CR-Header object is labelled "CR-Header (incomplete)", the objects that were only reachable from the CR-Header objects have been copied into the same block, while the ones that were also reachable through a different object landed in the regularr to-space, but now have a LazyCR info table that points to the CR-Header object.

in the third stage (again a GC), all marked objects are copied directly into the compact region's block. the header is now just labelled CR-Header.

In the 4th stage (again a GC), the objects are copied back from their own block into a regular heap object that contains them all
290
Alice ✨ @welltypedwit.ch · 02/10/2026
okay, i went with something in between. i think this is pretty reasonable thanks ^^
pub struct UnsafeMakeSend<A> {
    contents: A,
}
unsafe impl<A> Send for UnsafeMakeSend<A> {}

impl<A> UnsafeMakeSend<A> {
    pub const fn new(contents: A) -> Self {
        Self { contents }
    }

    pub const unsafe fn get(&self) -> &A {
        &self.contents
    }

    pub const unsafe fn get_mut(&mut self) -> &mut A {
        &mut self.contents
    }
struct BlockAllocator {
    free_blocks: Mutex<UnsafeMakeSend<BlockList>>,
}

static GLOBAL_BLOCK_ALLOCATOR: BlockAllocator = BlockAllocator {
    free_blocks: Mutex::new(UnsafeMakeSend::new(BlockList::new())),
};
010
Alice ✨ @welltypedwit.ch · 02/10/2026
so, what is the intended™ way of doing this in rust? BlockList is an intrusive list of blocks, so it contains lots of pointers and doesn't implement Send. i know what i'm doing though so i *can* get the warning to shut up by making the global mutable (which means that any access is unsafe anyway)
struct BlockAllocator {
    free_blocks: Mutex<BlockList>,
}

static mut GLOBAL_BLOCK_ALLOCATOR: BlockAllocator = BlockAllocator {
    free_blocks: Mutex::new(BlockList::new()),
};
230
Alice ✨ @welltypedwit.ch · 27/09/2026
i love tailcc
$ time vega build -O3
Executed in  365.52 millis
...
$ time vega build -O0
Executed in    7.32 secs
080
Alice ✨ @welltypedwit.ch · 26/09/2026
omg how can you call yourself a "type theorist and compiler engineer" and then struggle to understand this trivial sentence from SPJ's "implementation of functional programming languages" that says literallly nothing other than that naively substituting variablles in a syntax tree is inefficient"
In Chapter 12 we saw that a lambda abstraction can be applied to an argument by constructing an instance of the body of the abstraction with substitutions made for occurrences of the formal parameter. Unfortunately, this involved an inefficient traversal of the tree representing the body of the abstraction, and the presence of free variables seemed to make a more efficient implementation rather difficult.
1100
Alice ✨ @welltypedwit.ch · 18/09/2026
well that's... quite the channel
screenshot of a youtube video from this channel titled "Nobody Can Agree on the GREAT REPLACEMENT DEBATE". the thumbnail includes a huge caption "They REFUSED Farming Grants to WHITES"
080
Alice ✨ @welltypedwit.ch · 12/09/2026
you know, i thought the reputation of cousot papers was at least a little exaggerated but no, they actually look like that lol (this is page 3)
a page full of symbols and operators and... biblically accurate expressions spanning several lines casually inlined into regular sentences as if they were obviously understandable
8609
Alice ✨ @welltypedwit.ch · 29/08/2026
how on earth does that happen what
llvm code where %unboxed3 is allocated as a 17 byte array but a pointer is taken to it at an offset of 25 bytes (which will create a poison value since the getelementptr instruction has an `inbounds` flag)
020
Alice ✨ @welltypedwit.ch · 29/08/2026
uh oh
text search for "poison" showing 7 results
120
Alice ✨ @welltypedwit.ch · 29/08/2026
ah well that explains it
optimized llvm code that passes `poison` (deferred undefined behavior) to @vega_allocate_uninitialized_array
110
Alice ✨ @welltypedwit.ch · 29/08/2026
this is caused by literally a single line of vega btw lol
main : () -> ()
main() = {
    IO.writeStream(FileDescriptor.stdout(), String.toByteStream("abcd"))
}
130
Alice ✨ @welltypedwit.ch · 29/08/2026
uhh is this bad?
Fatal glibc error: malloc.c:2199 (sysmalloc): assertion failed: (old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)
280
Alice ✨ @welltypedwit.ch · 26/08/2026
i love llvm
docs for a function "removePredecessor":

Note that this function does not actually remove the predecessor.
0400
Alice ✨ @welltypedwit.ch · 19/08/2026
i know how to write c++
struct StackFrameAssignments {
    assignments :: DenseMap<Value *, unsigned>
};
0110
Alice ✨ @welltypedwit.ch · 19/08/2026
F. the first one is literally a joke with the punchline removed. the "code snippet" thing is directly plagiarized from another post but with the code removed so that it doesn't make sense anymore. also, elixir? chiptunes?? where did that come from
Have you ever thought static type systems spent too much time preventing runtime crashes? Have you ever wished a missing type constraint could invoke undefined behavior? Then you should try writing an obscure parser combinator in Haskell while listening to chiptunes!Type systems: Debating whether to rewrite it in Elixir or just add 63 extra sections to the proof.Code snippet: test:Main:blah(f_0 : (FunctionPointer * Boxed), x_1 : ALT) — can anyone guess what function this implements? ^^Vibe: Smart enough to know better, dumb enough to do it anyway.If you want, tell me:What programming language or type system are we debugging?Should we add more abstract algebra or just give up and use a segfault?
120
Alice ✨ @welltypedwit.ch · 18/08/2026
it's also in the blog post
You define your tables, you define your endpoints, and you use them in your server and client code! Currently we support Elm and Haskell integration, and we are planning to add more. Adding languages is pretty easy, so let us know if there is a language you are excited about! We want to prioritize based on the feedback we get during the public alpha.
030
Alice ✨ @welltypedwit.ch · 14/08/2026
isn't that a cute hack
{- | Even though we statically link with our plugin, stack does not
re-link the project when the plugin archive changes.

In order to force it to do so anyway, we use this hack:
We declare a template haskell dependency from an otherwise empty module
(Vega.ShadowStackDependency).
This way, whenever the archive changes, stack will recompile only that
module, which doesn't actually do anything but still makes it
re-link with the new version of the plugin.
-}
addShadowStackDependency :: TH.DecsQ
addShadowStackDependency = do
    TH.addDependentFile "llvm-shadow-stack/libVegaShadowStackPlugin.a"
    pure []
1161
Alice ✨ @welltypedwit.ch · 12/08/2026
must be nice lol. in the fp world we have of course long left toy benchmarks behind in favor of representative real world programs such as *checks notes* inserting a million values into a red-black tree
The use of synthetic or small scale, 'toy', benchmarks has long been criticised as inad
equate [Zorn, 1989] . Such benchmarks risk introducing systematic errors because they do
not reflect the interactions in memory allocation that occur in real programs, or because
their working sets are sufficiently small that they exhibit locality effects that real programs
would not. Wilson et al [1995a] provide an excellent critique of such practices. Fortunately,
other than for stress testing, synthetic and toy benchmarks have been largely abandoned
in favour of larger scale benchmark suites, consisting of widely used programs that are
believed to represent a wide range of typical behaviour (for example, the DaCapo suite for
Java [Blackburn et al, 2006b]).
2323
Alice ✨ @welltypedwit.ch · 10/08/2026
if you ported your litmus test to haskell, the `T : Copy` constraint would be considered a "wanted superclass constraint" and therefore could only be satisfied by superclass constraints of other constraints that are "paterson-smaller" than `T : Magic` (and in particular not by `T : Magic` itself)
ghci> class Copy a
ghci> class Copy a => Magic a
ghci> instance Magic a => Magic a
<interactive>:70:10: error: [GHC-39999]
    Could not deduce ‘Copy a’
      arising from the superclasses of an instance declaration
    from the context: Magic a
      bound by the instance declaration at <interactive>:70:10-27
    Possible fix:
      add (Copy a) to the context of the instance declaration
010
Alice ✨ @welltypedwit.ch · 02/08/2026
oh that's... cute
643 videos No views Updated today
120
Alice ✨ @welltypedwit.ch · 11/07/2026
and the *best* haskell habit i have is writing cross-referencable notes directly in source files
    pub unsafe fn boxed_element(&self, index: usize) -> *const u8 {
        debug_assert!(index > 0 && index < self.layout.boxed_count);
        // In objects at-rest, boxed elements are stored first.
        // See Note [At-rest vs in-flight] in src/Vega/Compilation/LLVM/Layout.hs
{- Note [At-rest vs in-flight]:
-------------------------------

Runtime values can occur in two states:
-   "in-flight" values are values that are actively being manipulated.
    if these are large, they may be stored in an alloca allocation (in 'CompoundValue.unboxedPointer')
    similarly to values at rest, but if they are small enough, we will decompose them into separate
    llvm scalar values. In particular, individual scalar representations like IntRep become
    values with a single "decomposed" scalar variable.
    Boxed values occuring in a product or sum are always decomposed into their own Seq of variables.
    This is important since we need to be able to mark them as stack roots for the garbage collector.

    This representation also affects how values are passed to functions. Any decomposed or boxed values are
    passed directly as LLVM variables, while unboxed pointers are passed as byval pointer arguments.

-   Values "at-rest" are values that are stored in memory somewhere. In particular, these are values stored in boxed
    heap objects or arrays.
    In this case, all values are stored in memory, but in order to simplify loads and stores, they are stored in a very similar
    format to in-flight values. The exact sizes and offsets can be computed based on the value's 'Layout'.
    +---------------------+------------------------------+--------------------+
    | boxed (n x 8 bytes) | decomposedScalars (variable) | unboxed (variable) |
    +---------------------+------------------------------+--------------------+

-}
1181
Alice ✨ @welltypedwit.ch · 11/07/2026
rustc vehemently disagrees (obviously)
the same code with a

 _: *const HeapObject = heap_object_pointer

that shows an error under heap_object_pointer
the error message:

mismatched types
expected raw pointer `*const HeapObject`
found raw pointer `*const u8`
160
Alice ✨ @welltypedwit.ch · 11/07/2026
uh, why does my rust-analyzer think that subtracting 8 bytes from a *const u8 (`object`) should turn it into a *const HeapObject?
                let heap_object_pointer = object.byte_sub(HeapObject::HEADER_SIZE_IN_BYTES);
                heap_object_pointer as *const HeapObject

there is an inlay hint that heap_object_pointer ostensibly has type *const HeapObject
270
Alice ✨ @welltypedwit.ch · 06/07/2026
uh oh
thread '<unnamed>' (172361) panicked at src/heap.rs:31:22:
misaligned pointer dereference: address must be a multiple of 0x8 but is 0x564040da86f9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
090
Alice ✨ @welltypedwit.ch · 22/06/2026
finally omg
All tests passed (6 known failures).
030
Alice ✨ @welltypedwit.ch · 22/06/2026
aaaaaa (the amazing bit here is that the `store` and `memcpy` take arguments in the opposite order)
diff swapping the order of argumetns to LLVMBuilder.buildStore
250
Alice ✨ @welltypedwit.ch · 22/06/2026
how does this keep happening
diff swapping the arguments to LLVMBuilder.buildMemCpy
160
Alice ✨ @welltypedwit.ch · 22/06/2026
"hmm why is this segfaulting all the time" (i made the same mistake with alignment)
  %unboxed = alloca [16 x i8], align 512
  store i64 %z_7, ptr %unboxed, align 512
050
Alice ✨ @welltypedwit.ch · 22/06/2026
new vega feature: all ints are backed by 512 bits by defautl lol (that's what i get for not consistently using my custom `Size` type that abstracts over the bytes/bits distinction)
llvm declaration for a vega function that takes and returns an i512
3140
Alice ✨ @welltypedwit.ch · 20/06/2026
uh oh (there are like 100 of these)
~100 error messages about various types not matching type 'a', followed by:

ERROR: PANIC (the 'impossible' happened): Skolem corresponding to a non-parameter representation type variable found while trying to convert representations to core. This should not have happened!
       Skolem: r$a
040
Alice ✨ @welltypedwit.ch · 20/06/2026
"hmm i wonder what the error message for this is going to look like"
ERROR: PANIC (the 'impossible' happened): unsubstituted type variable in unification: MkLocalName {parent = MkDeclarationName {moduleName = MkModuleName {package = MkPackageName "meridian", subModules = fromList ["Route"]}, name = "executeWith"}, name = "r$matchData", count = 0}:
190
Alice ✨ @welltypedwit.ch · 18/06/2026
yes! (this is after calling .gc 9 times) it goes down slightly, but that's probably because it's able to clean up the intermediate arrays from when it had to resize it's pretty non-deterministic but that's to be expected with a concurrent GC i guess
before: rss: 843.56640625, heapUsed: 753.8318328857422
after: rss: 767.37890625, heapUsed: 677.5433197021484
120
Alice ✨ @welltypedwit.ch · 18/06/2026
i don't understand what's going on with that FloatArray. it doesn't show up in any of deno's metrics for me, but if i use a regular array then it does definitely get reclaimed *and* the RSS goes down (but not as much as the real usage obviously)
let lol = []

function leakyBoi() {
    let array = []
    for(let i = 0; i < 1024*1024*4; i++) {
        array.push(i)
    }
    lol.push(() => array[69])
}


for (let i = 0; i < 20; i++) {
    leakyBoi()
}
console.log(`before: rss: ${Deno.memoryUsage().rss / 1024 / 1024}, heapUsed: ${Deno.memoryUsage().heapUsed / 1024 / 1024}`)
lol = []
globalThis.gc()
console.log(`after: rss: ${Deno.memoryUsage().rss / 1024 / 1024}, heapUsed: ${Deno.memoryUsage().heapUsed / 1024 / 1024}`)
before: rss: 831.65625, heapUsed: 738.6234588623047
after: rss: 99.29296875, heapUsed: 5.880638122558594
220
Alice ✨ @welltypedwit.ch · 17/06/2026
so sad to see the rust foundation just now starting to take sponsorships from evil companies. surely this never happened before
rust foundation sponsor page for "Helsing", joined july 2023:

Helsing is a pan-European, multi-domestic defense company with national entities in Germany, the UK, and France. They believe that software, in particular artificial intelligence, will be the key to protecting democracy. Their teams design technology products that transform the operational capabilities of both new and existing defense platforms, to deliver faster and more accurate decision-making at the tactical, operational, and strategic levels. Approximately 50% of Helsing’s code is Rust-native and the other half is slowly but surely getting replaced with Rust.
1150
Alice ✨ @welltypedwit.ch · 04/06/2026
def P := 5
def NP := 4
def AI := 1

def p_equals_NP : P = NP + AI := by
  rfl
1171
Alice ✨ @welltypedwit.ch · 04/06/2026
one profiling build later, the time is definitely being spent in LLVMTargetMachineEmitToFile but also compiling the textual IR with clang takes <1s
speedscope profile where the vast majority of the time (39.41s) is being spent on Vega.Driver.LLVM.Target.targetMachineEmitToFile (which is just targetMachineEmitToFile inlined into Vega.Driver) and a tiny sliver of other things on the right
160
Alice ✨ @welltypedwit.ch · 03/06/2026
what is happening. apparently the time to compile this code is exponential in the number of ArrayList.push calls (every call ~quadruples it) but the weird part is that my debug output for everything is there in <1s so apparently this time is all spent in LLVM??
main : () -> ()
main() = {
    let list = ArrayList.newWithCapacity(3)
    ArrayList.push(list, 65u8)
    ArrayList.push(list, 66u8)
    ArrayList.push(list, 67u8)
    ArrayList.push(list, 68u8)
    ArrayList.push(list, 69u8)
    ArrayList.push(list, 70u8)
    ArrayList.push(list, 71u8)
    ArrayList.push(list, 72u8)
    ArrayList.push(list, 73u8)
    -- ArrayList.push(list, 74u8)
    -- ArrayList.push(list, 75u8)
    -- ArrayList.push(list, 76u8)
    print(String.fromByteArray(ArrayList.freeze(list)))
2140
Alice ✨ @welltypedwit.ch · 01/06/2026
wow you're consistent
85
most followers: 80
most posts per day: 91
most liked posts: 96
biggest reply guiy: 78
longest posts: 82
010
Alice ✨ @welltypedwit.ch · 28/05/2026
thisisfine.jpg
vscode minimap where almost everything is red
0240
Alice ✨ @welltypedwit.ch · 21/05/2026
i git pulled vega's repo on a different machine after ~2 weeks and got a little surprised for a second lol (i renamed the test directory)
git pull text thingy showing 187(!!) files changed, 2051 insertions, 444 deletions
050
Alice ✨ @welltypedwit.ch · 17/05/2026
um
vega panic "Non-record type in record field access" showing what is clearly a record type
230
Alice ✨ @welltypedwit.ch · 16/05/2026
writing tests for a compiler is fun because you'll come up with all these pieces of code that literally noone will ever write
-- type: compile

f : { g : forall (a : Type(Boxed)). a -> a } -> forall (a : Type(Boxed)). a -> a
f(record) = record.g

main : () -> ()
main() = {}
2372
Alice ✨ @welltypedwit.ch · 15/05/2026
vega is now officially HTTP-server-complete!! github.com/Innf107/meri...
import internal:Internal (intToInt32, emptyArray)
import std:IO/Posix/FileDescriptor (stderr)
import std:IO/Posix (print, printFD)
import std:String (append)

import meridian:Error (fatalError, handleNegative)
import meridian:Socket (Socket, createTCP, bindTCP, listen, accept)
import meridian:Parser (makeParser, parseRequest)
import meridian:Request (Request)
import meridian:Response (Response, sendResponse)
import meridian:Meridian (run)

main : () -> ()
main() = {
    run({ 
        port = 3000u16, 
        onRequest = \request -> {
            Response({
                headers = emptyArray(),
                status = 200,
                statusMessage = "OK",
                body = "<h1>OMG IT WORKS</h1>",
                contentType = "text/html"
            })
        }
    })
}
http://localhost:3000 in firefox showing "OMG IT WORKS"
2391
Alice ✨ @welltypedwit.ch · 14/05/2026
also, the fix was 2 lines of code lol
diff changing

let path = [MIR.SumConstructorPath index, MIR.ProductFieldPath productIndex]

to

let path = case parameters of
        -- If the constructor only has a single parameter, we don't have an internal product
        [_] -> [MIR.SumConstructorPath index]
        _ -> [MIR.SumConstructorPath index, MIR.ProductFieldPath productIndex]
020
Alice ✨ @welltypedwit.ch · 14/05/2026
this is fun, if i dump the resulting MIR, i get different debug logs (it's because if i don't dump the MIR, the value that fails here (from a pure computation) isn't actually forced until after all declarations have been at least partially compiled already)
invocation of VEGA_TRACE=coreToMIR vega build where there are several more declarations being compiled after meridian:Parser:parseUntilSpace before showing a compiler panicinvocation for VEGA_TRACE=coreToMIR vega build --debug-mir=file
where the compiler panic happens immediately after compiling meridian:Parser:parseUntilSpace
170
Alice ✨ @welltypedwit.ch · 13/05/2026
like, i already had this constant here but i guess i just didn't use it
-- | The offset of the actual array contents from the start of the object data (just *after* the header)
arrayContentOffset :: Int
arrayContentOffset = 8
140
Alice ✨ @welltypedwit.ch · 13/05/2026
well that's just not true (an array is a pointer to the length, followed by the contents so this needs to offset the pointer by 8). thanks past alice
-- Arrays are represented as pointers to their contents so unsafeArrayContents doesn't actually need to do anything
150
Alice ✨ @welltypedwit.ch · 12/05/2026
at this stage of debugging
trace Debug "???????????"
0296
Alice ✨ @welltypedwit.ch · 12/05/2026
literally all but two tests are failing that's kind of impressive. (and those two aren't failing because they're not being run on the js backend ^^)
71/80 TESTS FAILED (7 known failures)
060
Alice ✨ @welltypedwit.ch · 12/05/2026
much better
vega MIR for the same function but this time it only uses a single unbox operation and unboxes it at the correct representation (IntRep * ArrayRep($0) * IntRep * ArrayRep($0) * IntRep)
140