Show HN: Compile C to Not Gates

Introduction to c2fj

The innovative compiler, c2fj, showcases the capability of transforming any C program into a series of NOT operations. This compiler follows a unique path: C → RiscV → Flipjump → .fjm, providing a testament to the versatility and power of compilers. For more insights, you can explore the repositories and discussions on platforms like GitHub and Esolangs.

Example Program: Calculating Primes


int main() {
    printf("Calculate primes up to: ");
    int max_number;
    scanf("%d", &max_number);
    ...
    for (int p = 3; p <= max_number; p += 2) {
        if (non_prime[p] == false) {
            for (int i = p*p; i <= max_number; i += p) {
                non_prime[i] = true;
            }
            printf("%d\n", p);
        }
    }
    return 0;
}

Upon compilation using c2fj, this program can efficiently calculate prime numbers up to the specified limit. For example, for a limit of 20, the output primes are 2, 3, 5, 7, 11, 13, 17, 19.

Installation Guide

To install c2fj, use the following commands:

>>> pip install c2fj
>>> sudo apt install picolibc-riscv64-unknown-elf

Usage Instructions

To compile your C file, execute:

python3 c2fj.py file.c

This command will compile your C file into an elf, then into fj files, and finally into fjm, before running it.

Supported Flags

  • –breakpoints: Place a fj-breakpoint at the start of the specified RiscV addresses.
  • –single-step: Place fj-breakpoints at the start of all RiscV opcodes.
  • –unify_fj: Unify the generated fj files into a single file.
  • –finish-after: Stop the compilation at any step (before running, before creating fjm, etc.).
  • –build-dir: Save the builds in this directory.

Handling Projects with Multiple C Files

c2fj supports specifying a Makefile path instead of a single C file. Your Makefile should include constants that c2fj will populate:

  • C2FJ_GCC_OPTIONS
  • C2FJ_LINKER_SCRIPT
  • C2FJ_SOURCES
  • C2FJ_INCLUDE_DIRS
  • ELF_OUT_PATH

Example Makefile


GCC := riscv64-unknown-elf-gcc
GCC_FLAGS := -O3

SOURCES := $(C2FJ_SOURCES) main.c globals.c calculate_int.c
OBJECTS := $(SOURCES:.c=.o)

all: |
    $(GCC) $(C2FJ_GCC_OPTIONS) $(GCC_FLAGS) $(SOURCES) -I $(C2FJ_INCLUDE_DIRS) -T $(C2FJ_LINKER_SCRIPT) -o $(ELF_OUT_PATH)

clean:
    rm -r build 2>/dev/null || true

.PHONY: clean all

Technical Insights

The process begins with the compilation of C files into a RiscV elf using picolibc, facilitating the next phase of fj-compilation. Each syscall compiled to a special RiscV opcode is further compiled into the fj implementation of the requested syscall, as defined in c2fj_init.c.

The RiscV to FlipJump compilation converts each opcode into the appropriate flipjump macro, optimizing space to handle extensive C code bases efficiently.

Execution and Optimization

Using flipjump macros,