Can BCC (Bruce's C Compiler) fit into 64 KiB of 16-bit x86 code?
================================================================
I'm looking for an early version of the compiler backend of BCC
(Bruce'S C Compiler), nowadays part of Dev86, satisfying all below:
* It can compile its own source code targeting 16-bit x86.
* It has all its features (such as code generation for i386 and 8086
and the preprocessor), except possibly floating point support.
* After assembling it, adding libc and linking statically, the total
code size is less than 64 KiB.
* (The total data size doesn't matter much, initialized data
(read-only and read-write in total) is typically less than 6 KiB.)
Such a BCC with <64 KiB of code would be significant, because using
it there would be a fully open-source, in-place upgrade path from
Minix 1.5.10 i86 to a fully open-source Minix i386 (and also i86).
(As of 2026, none of Minix 1.0–2.0.4 is fully open source, because
the source code of these has never been published: C preprocessor,
C compiler backend, assembler, linker.)
* * *
My research so far:
I was able to find an early version of BCC from 1993-07-17 (as
bcc.tar.gz), and compile it with an optimizing C compiler (OpenWatcom
v2, command:
owcc -bdos -s -O2 -fno-stack-check -fsigned-char -W -Wall -o sc.exe
bcc-cc1.c assign.c codefrag.c debug.c declare.c express.c
exptree.c floatop.c function.c gencode.c genloads.c glogcode.c
hardop.c input.c label.c loadexp.c longop.c output.c preproc.c
preserve.c scan.c softop.c state.c table.c type.c
), total file size is <69.5 KiB, total code size is <63.5 KiB.
bcc.tar.gz
<https://ftp.gwdg.de/pub/linux/misc/linux.org.uk/people/linux/8086/
Compilers/bcc.tar.gz>
However, BCC is a non-optimizing compiler, and it generates large
machine code. When compiling its own source code, I was able to
generate an executable with total code size ~66.7 KiB. After removing
all floating-point code, the total code size is <65.05 KiB. That's
1.05 KiB larger than my hard limit of 64 KiB. It seems unlikely that
I'm able to make the libc code 1.05 KiB smaller. (Please note that
this is the Minix 1.5.10 libc (small), not the OpenWatcom v2 libc for
DOS (large).)
The BCC compiler backend uses only a few libc functions:
* dynamic memory allocation (malloc, realloc, free) (~1200 bytes of
code)
* unbuffered, POSIX file I/O (open, creat, read, write, close,
isatty)
* exit (exit)
* string operations (memcpy, memset, strcat, strcmp, strcpy, strlen,
strrchr)
* arithmetic operations on 32-bit signed and unsigned integers (27
BCC helper functions)
* (it doesn't use any of <stdio.h>)
Is maybe an earlier version of BCC smaller? (I wasn't able to find
the source code of any earlier version.)
The 16-bit x86 code in an even earlier version of BCC (from
1990-06-10, in bccbin16.tar.Z) fits to <59 KiB, including libc code.
However, source code is not included. It's also unclear which
C compiler was used to compile it. It was definitely not the
ACK C compiler shipping with Minix 1.5.10 i86, because that generates
different function trailers (i.e. instead of pop bp and ret).
* * *
I was able to bring the code size down from 66.7 KiB to <62.65 KiB
using the following tricks:
* Removing code from BCC. I only removed (most of) floating point
support, and that was enough to bring the code size down to 64 KiB
+ 500 bytes.
* Removing code from the libc: for example code for populating errno,
code for setting environ, malloc debugging code, the final 2
arguments of callm1, unused functions in used object files. This
has brought the code size down to 64 KiB - 20 bytes.
* Rewriting syscall wrapper functions (exit, read, write, close,
open, creat, isatty) and syscall helper functions (callm1, callm3,
callx) in manually size-optimized 8086 assembly, and partially
merging some of them so that they can share some code. Originally
these were written in C.
* Rewriting the start function (crtso) in manually size-optimized
8086 assembly.
* Shortening the assembly implementation of some libc functions
(memcpy, memset, strcmp, strcpy, strcat, strlen, strrchr), such as
making memmove and memcmp always operate on bytes (rather than
aligned words).
* Inlining the strrchr libc function into its single C call site.
In the middle of the work I've realized that the maximum code size
(a.out a_text) of a program file Minix 1.5.10 i86 is able to run is
64 KiB - 256 bytes == 0xff00 bytes == 65280 bytes, which is less than
64 KiB. Fortunately, my final code fits within this as well.
When trying to run this compiler on Minix i86 to compile itself, it
ran out of memory. I fixed this by reducing the size of some static
arrays (such as the expression stack). I took the new (smaller) array
sizes from binary analysis of an earlier version of BCC (1990-06-15),
whose source code hasn't been released so far.
Code sizes of the BCC backend on Minix 1.5.10 i86, using my tiny
libc:
* Compiled using ACK 3.1 shipped with Minix, with optimization
cc -O: <50.83 KiB
* Compiled using itself (no optimization): <62.65 KiB
Please note that when compiling with ACK 3.1, I've discovered
2 silent code generation bugs in ACK 3.1. For example, it generates
incorrect code for bump = -bump; if bump is a 32-bit integer (long).
I worked around it by doing bump = ~bump + 1; instead. For these bugs
it was quite painful to pinpoint which C source code line gets
compiled incorrectly. After that, the workaround was straightforward.
From: <https://retrocomputing.stackexchange.com/questions/32395/
can-bcc-bruces-c-compiler-fit-into-64-kib-of-16-bit-x86-code>