Graph theory in Dialog

The main heap is used to store most things that are created at runtime. Whenever you assign a new local variable, or construct a new list, it goes here. Unlike the malloc heap from C, there’s no extra metadata stored in this—it’s just a big, unorganized block of memory, with REG_TOP marking the top of it. (Which means it’s not really a heap, per se; it’s more of a stack, but nothing ever gets popped off it.)

The auxiliary heap is used by the runtime to store whatever data it needs. This is where the call stack of choice points and stop points is kept, for example, and where runtime routines can temporarily push values to retrieve later. This one is also not really a heap; it’s a stack, though one that’s in RAM, so the program can poke at lower parts of it when needed. (This is used for local variables, which are stored in an environment frame on the call stack.)

When returning to a choice point or stop point, the previous value of REG_TOP is retrieved from here, which is the only way the heap can shrink. Otherwise, it grows without bound. That’s why the main gameplay loop in the library is implemented with *(repeat forever) (repl) (fail): it ensures that all the heap memory used during a given turn is freed at the end of that turn.

The long-term heap is used to store any values that need to persist past returning to a choice point—that is, values stored in global and per-object variables. Those variables themselves, along with global and per-object flags, are stored in a separate area of RAM outside of the heap, but when you store [1 2 3 4 5] in a global, that list itself is stored on the long-term heap. I haven’t been able to figure out how this one is arranged in memory; it might actually be a heap.

This was added in 0e/01 to avoid putting a maximum size on individual variables. Now instead there’s a maximum on the total size of all variables’ values.

In other words:

This is essentially correct! Exhausting the main heap means you’re using too much dynamic memory, exhausting the auxiliary heap means you’re using too much recursion, and exhausting the long-term heap means you’re storing too much data in persistent variables.

This also means that, if a particular computation uses a horrific amount of memory before returning its result, you can ensure that memory is freed afterward:

(global variable (result of computation $))
(expensive computation into $Result)
    {
        (really expensive computation internal guts into $Tmp)
        (now) (result of computation $Tmp)
        (fail)
    (or)
        (result of computation $Result)
        (now) ~(result of computation $)
    }

This is used by the standard library to free all the memory used by the parser before asking a disambiguation question. It saves the question itself in a global variable (so on the long-term heap), then uses (stop) to free memory, and retrieves it from the variable to ask it.

3 Likes