Memory management
fbc tries to avoid memory allocations as much as possible, since they are pretty slow generally. The linked list implemented in list.bas comes with a builtin memory pool, so pretty much every list is pooled. The memory pool pre-allocates large chunks and can then quickly hand out many small nodes. Those lists are used for simple things like the list of libraries to link into an executable, but also for heavier things like AST nodes. The memory pool is supposed to speed things up (no idea if this was ever verified though).
In many places the compiler simply uses global/static variables, for example fixed-length strings, in order to avoid memory allocations. Tokens are a nice example: lex.bas parses input characters into tokens, and stores the token text in static buffers. Token text, that could be: variable names, string literals, and so on. All tokens are stored here though, so the preprocessor can correctly record macros. Now take into account the huge number of tokens the parser has to deal with: For example, FB's current Windows headers result in ~100k tokens. Dynamically allocating a buffer for every token would quickly become inefficient.
Of course the token length is limited by using a static buffer, but fbc's default of 1024 bytes should be enough for everyone. Similar length limitations apply to many things in the compiler because of the use of fixed-length buffers. In most situations, the buffers in the compiler are not used to their full potential, i.e. they are bigger than they need to be.
All that does not mean the compiler does not use dynamic memory allocations at all. It does, in situations when allocating is easier than using a list/pool and speed is not critical. FB's builtin string type is used in many places too. As long as the string's are kept allocated, they are very efficient. Expansion of macro parameter stringifying in the pre-processor uses a strReplace() based on string's, and it is fast (enough). Besides that, dynamic strings, which are basically the same as string's, are used everywhere in the pre-processor, from macro recording to macro expansion.
Out-of-memory situations/allocation failures are not seriously handled. There are NULL checks in some places where allocate() is called, but these checks are pointless, since the rest of fbc does not check for NULL. NULL is sometimes used to indicate an error, for example by some astNew*() functions. Also, the compiler does not deallocate() everything, but lets the OS do the cleanup.
In many places the compiler simply uses global/static variables, for example fixed-length strings, in order to avoid memory allocations. Tokens are a nice example: lex.bas parses input characters into tokens, and stores the token text in static buffers. Token text, that could be: variable names, string literals, and so on. All tokens are stored here though, so the preprocessor can correctly record macros. Now take into account the huge number of tokens the parser has to deal with: For example, FB's current Windows headers result in ~100k tokens. Dynamically allocating a buffer for every token would quickly become inefficient.
Of course the token length is limited by using a static buffer, but fbc's default of 1024 bytes should be enough for everyone. Similar length limitations apply to many things in the compiler because of the use of fixed-length buffers. In most situations, the buffers in the compiler are not used to their full potential, i.e. they are bigger than they need to be.
All that does not mean the compiler does not use dynamic memory allocations at all. It does, in situations when allocating is easier than using a list/pool and speed is not critical. FB's builtin string type is used in many places too. As long as the string's are kept allocated, they are very efficient. Expansion of macro parameter stringifying in the pre-processor uses a strReplace() based on string's, and it is fast (enough). Besides that, dynamic strings, which are basically the same as string's, are used everywhere in the pre-processor, from macro recording to macro expansion.
Out-of-memory situations/allocation failures are not seriously handled. There are NULL checks in some places where allocate() is called, but these checks are pointless, since the rest of fbc does not check for NULL. NULL is sometimes used to indicate an error, for example by some astNew*() functions. Also, the compiler does not deallocate() everything, but lets the OS do the cleanup.