Skip to content

feat: Implement compiler builtins for missing declared stdlib functions #203

Description

@CTalkobt

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:
    1. If larger: malloc new, copy old, free old
    2. If smaller: free unused portion (if allocator supports)
    3. Handle NULL ptr (treat as malloc)
    4. 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

  1. 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
  2. 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)
  3. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions