Implement compiler builtins for stdlib functions that are declared in headers but lack implementations. Prioritize compiler codegen (inline/builtin) unless emitted byte size is disproportionate to functionality.
Functions to Implement
Priority 1: Memory Management
calloc(size_t nmemb, size_t size)
- Allocate and zero-initialize memory
- Builtin approach: inline as
malloc(nmemb*size); memset(..., 0, ...)
- Or: single malloc call with zero-init loop
- Size benefit: avoids external function call overhead
- Estimated emit: 10-15 bytes (multiply + malloc call + memset call)
realloc(void *ptr, size_t size)
- Resize allocated block
- More complex: may need:
- If larger: malloc new, copy old, free old
- If smaller: free unused portion (if allocator supports)
- Handle NULL ptr (treat as malloc)
- Handle size=0 (treat as free)
- Builtin approach: inline all paths or generate call to helper
- Estimated emit: 20-30 bytes for full inline
- Decision: Likely too complex for inline, implement as library function instead
Possible Extensions (if time permits):
strlcpy(char *dst, const char *src, size_t size) — Safe string copy
strnlen(const char *s, size_t maxlen) — Bounded string length
strdup(const char *s) — Allocate and copy (needs malloc)
strtok_r(char *str, const char *delim, char **saveptr) — Reentrant tokenizer
Implementation Strategy
For calloc():
Option A (Preferred - Inline):
// Emit as inline sequence
lda #nmemb_low // load size (already computed)
ldx #nmemb_high
jsr malloc // allocate
; result in AXYZ
; Fall through to memset
; Generate memset inline for reasonable sizes
ldy #0
loop:
sta (ptr), y
iny
cpy size_low
bne loop
Option B (Library Call):
Keep as external function in stdlib/, optimize assembly hand-written
For realloc():
Recommend library implementation (stdlib_realloc.s) due to complexity:
- Check if ptr is NULL → call malloc(size)
- Check if size is 0 → call free(ptr), return NULL
- If growing: malloc(size), memcpy(old→new), free(old)
- If shrinking: optional (return same ptr or allocator-specific behavior)
Compiler Changes
Code Generator
- Detect calloc() calls
- If both nmemb and size are constant/small: inline the sequence
- Otherwise: emit JSR calloc with parameters
- Realloc: always emit JSR (no inline due to complexity)
Optimizer
- Constant-fold calloc(N, sizeof(T)) → malloc(N*sizeof(T))
- Detect calloc of constant size and unroll memset loop
- Strength-reduce multiplication if power-of-2
Error Handling
- calloc with size overflow (nmemb * size): propagate from malloc
- realloc with NULL ptr: transparent to user (standard behavior)
Testing
-
Unit tests:
calloc(10, 1) → verify 10 bytes allocated and zeroed
calloc(0, N) → NULL
calloc(N, 0) → NULL
realloc(NULL, 10) → behaves like malloc(10)
realloc(ptr, 0) → frees and returns NULL
realloc(ptr, larger_size) → allocates, copies, frees old
-
Integration tests:
- Array of structs:
calloc(100, sizeof(struct MyType))
- String duplication:
char *s = calloc(len+1, 1); strcpy(s, src);
- Growing arrays:
realloc(arr, new_count * size)
-
Size/performance tests:
- Measure emitted bytes for calloc vs function call
- Compare performance vs library version
- Verify no code bloat
Output Format
- Update stdlib.h documentation to note calloc/realloc as builtins
- Add compiler codegen test: test_calloc.c, test_realloc.c
- Document inline behavior in CLAUDE.md under "Compiler Optimizations"
Priority & Effort
- calloc: High — Simple inline, high value (common pattern)
- realloc: Medium — Complex logic, less common (library is acceptable)
- strlcpy/strnlen: Low — Nice-to-have, can defer to stdlib if needed
Implement compiler builtins for stdlib functions that are declared in headers but lack implementations. Prioritize compiler codegen (inline/builtin) unless emitted byte size is disproportionate to functionality.
Functions to Implement
Priority 1: Memory Management
calloc(size_t nmemb, size_t size)malloc(nmemb*size); memset(..., 0, ...)realloc(void *ptr, size_t size)Possible Extensions (if time permits):
strlcpy(char *dst, const char *src, size_t size)— Safe string copystrnlen(const char *s, size_t maxlen)— Bounded string lengthstrdup(const char *s)— Allocate and copy (needs malloc)strtok_r(char *str, const char *delim, char **saveptr)— Reentrant tokenizerImplementation Strategy
For calloc():
Option A (Preferred - Inline):
Option B (Library Call):
Keep as external function in stdlib/, optimize assembly hand-written
For realloc():
Recommend library implementation (stdlib_realloc.s) due to complexity:
Compiler Changes
Code Generator
Optimizer
Error Handling
Testing
Unit tests:
calloc(10, 1)→ verify 10 bytes allocated and zeroedcalloc(0, N)→ NULLcalloc(N, 0)→ NULLrealloc(NULL, 10)→ behaves like malloc(10)realloc(ptr, 0)→ frees and returns NULLrealloc(ptr, larger_size)→ allocates, copies, frees oldIntegration tests:
calloc(100, sizeof(struct MyType))char *s = calloc(len+1, 1); strcpy(s, src);realloc(arr, new_count * size)Size/performance tests:
Output Format
Priority & Effort