{"id":18551773,"url":"https://github.com/tomhea/c2fj","last_synced_at":"2026-03-03T17:33:50.805Z","repository":{"id":115198122,"uuid":"577048115","full_name":"tomhea/c2fj","owner":"tomhea","description":"Compiling C to FlipJump","archived":false,"fork":false,"pushed_at":"2025-01-18T23:00:30.000Z","size":363,"stargazers_count":96,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-04T08:38:24.469Z","etag":null,"topics":["c","compiler","esolangs","oisc"],"latest_commit_sha":null,"homepage":"https://esolangs.org/wiki/FlipJump","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tomhea.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-12-11T20:11:25.000Z","updated_at":"2025-12-11T22:46:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"3a1bb612-defc-45d2-bcb3-9c54130f275d","html_url":"https://github.com/tomhea/c2fj","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/tomhea/c2fj","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomhea%2Fc2fj","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomhea%2Fc2fj/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomhea%2Fc2fj/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomhea%2Fc2fj/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tomhea","download_url":"https://codeload.github.com/tomhea/c2fj/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomhea%2Fc2fj/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30052501,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-03T15:26:47.567Z","status":"ssl_error","status_checked_at":"2026-03-03T15:26:17.132Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","compiler","esolangs","oisc"],"created_at":"2024-11-06T21:10:18.916Z","updated_at":"2026-03-03T17:33:50.783Z","avatar_url":"https://github.com/tomhea.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![GitHub](https://img.shields.io/github/license/tomhea/c2fj)](LICENSE)\n[![Website](https://img.shields.io/website?down_color=red\u0026down_message=down\u0026up_message=up\u0026url=https%3A%2F%2Fesolangs.org%2Fwiki%2FFlipJump)](https://esolangs.org/wiki/FlipJump)\n[![PyPI - Version](https://img.shields.io/pypi/v/c2fj)](https://pypi.org/project/c2fj/)\n\n# c2fj\nCompiling C --\u003e RiscV --\u003e [Flipjump](https://github.com/tomhea/flip-jump) --\u003e .fjm\n\nThis compiler is a proof that any program can be compiled into a bunch of `NOT` operations. Read more about FlipJump: [Github](https://github.com/tomhea/flip-jump), [Esolangs](https://esolangs.org/wiki/FlipJump), [Learn FlipJump](https://github.com/tomhea/flip-jump/wiki/Learn-FlipJump).\n\nAn example program, [primes/main.c](tests/programs/primes/main.c):\n```c\nint main() {\n    printf(\"Calculate primes up to: \");\n    int max_number;\n    scanf(\"%d\", \u0026max_number);\n    \n    ...\n    \n    for (int p = 3; p \u003c= max_number; p += 2) {\n        if (non_prime[p] == false) {\n            for (int i = p*p; i \u003c= max_number; i += p) {\n                non_prime[i] = true;\n            }\n            printf(\"%d\\n\", p);\n        }\n    }\n    \n    return 0;\n}\n```\nCompiled into this:\n\n![img.png](res/compiled_elf.png)\n\nWhich was compiled into this:\n\n![img.png](res/compiled_fj_files.png)\n\nWhich in turn compiled into:\n\n![img.png](res/compiled_fjm.png)\n\nNow, run it (Remember, these are flipjump ops that are running):\n\n```text\nCalculate primes up to: 20\n2\n3\n5\n7\n11\n13\n17\n19\nProgram exited with exit code 0x0.\n```\n\n# How to install\n```\n\u003e\u003e\u003e pip install c2fj\n\u003e\u003e\u003e sudo apt install picolibc-riscv64-unknown-elf\n```\n\n# How to use\n\nSimply `python3 c2fj.py file.c` will compile your c file into an elf, into fj files, into fjm, then run it.\n\n`c2fj` supports the next flags:\n- `--breakpoints` Place a fj-breakpoint at the start of the specified riscv addresses\n- `--single-step` Place fj-breakpoints at the start of all riscv opcodes\n- `--unify_fj` Unify the generated fj files into a single file\n- `--finish-after` Stop the compilation at any step (before running, before creating fjm, etc.)\n- `--build-dir` Save the builds in this directory\n\n## What if my project is more then a single c?\n\nWe support specifying a `Makefile` path, instead of the c file!  \nYour Makefile will have to rely on some constants that `c2fj` will fill:\n```c\nC2FJ_GCC_OPTIONS\nC2FJ_LINKER_SCRIPT\nC2FJ_SOURCES\nC2FJ_INCLUDE_DIRS\nELF_OUT_PATH\n```\n\nAn example Makefile:\n```makefile\nGCC := riscv64-unknown-elf-gcc\nGCC_FLAGS := -O3\n\nSOURCES := $(C2FJ_SOURCES) main.c globals.c calculate_int.c\nOBJECTS := $(SOURCES:.c=.o)\n\nall: |\n\t$(GCC) $(C2FJ_GCC_OPTIONS) $(GCC_FLAGS) $(SOURCES) -I $(C2FJ_INCLUDE_DIRS) -T $(C2FJ_LINKER_SCRIPT) -o $(ELF_OUT_PATH)\n\nclean:\n\trm -r build 2\u003e/dev/null || true\n\n.PHONY: clean all\n\n```\n\nYou can also specify your own linker script. It should contain the following:\n- `_stack_end` (just after the end of the stack)\n- `_sdata` (start of the data section)\n- `__heap_start` (start of the heap)\n\n## How Does It Work?\nFirst your C files are being compile to a RiscV elf.  \nThe compilation is done with [picolibc](https://github.com/picolibc/picolibc), and the project provides it any of the function implementation needed, in order for it to support the next phase of fj-compilation.\n\nFor example, look at `exit` ([c2fj_init.c](c2fj/compilation_files/c2fj_init.c)):\n\n```c\nvoid exit(int status) {\n    asm volatile (\"jal %0, .+10\" ::\"r\"(status):\"memory\");\n    __builtin_unreachable();\n}\n```\n\nIt uses jal with bad offset, thus will be parsed here as: ([riscv_instructions.py](c2fj/riscv_instructions.py))\n```python\nelif imm == JAL_EXIT_IMMEDIATE:\n    return f'    .syscall.exit {register_name(rd)}\\n'\n```\nThus, will get to the flipjump implementation of: ([riscvlib.fj](c2fj/compilation_files/riscvlib.fj))\n```python\ndef exit src_register {\n    stl.output \"Program exited with exit code \"\n    hex.print_uint 2, src_register, 1, 1\n    stl.output \".\\n\"\n    stl.loop\n}\n```\n\nYou can think of it like this: The C-\u003eRiscV compilation compiles the syscalls to a special (invalid) RiscV op, that gets parsed and further compiled into the fj implementation of the \"requested syscall\".\nThe supported syscalls can be found in [c2fj_init.c](c2fj/compilation_files/c2fj_init.c), and they contain `_getc`, `_putc`, `exit`, `sbrk`.\n\nEvery other opcode (Let's follow `addi x10, x11, 7` for example), will be compiled into itself.\n\nThe RiscV -\u003e FlipJump part of the compilation parses the compiled elf, and matches each opcode with the appropriate flipjump macro. For example:  \n```python\nelif opcode == RV_ALU_IMM:\n    if funct3 == RV_ADDI:\n        ops_file.write(i_type('addi', full_op))\n```\n\nThen the `riscv.addi` macro is being used. The riscv ops macros are space-optimized. They are so much optimized, that each takes `30-40` fj-ops in space.  \nThat is by design. The space optimization allows this project to handle very large c code bases, and still being able to compile it without any problem.\nThat means that the compilation time doesn't really depend on the size of your codebase.\n\nThe way it works, is that each opcode is implemented once in the `riscv.start` macro. \nFor example: \n```python\ndo_add:\n    hex.add .HLEN, .rs1, .rs2\n    stl.fret .ret\n```\nNote how `addi` is implemented:\n```python\ndef addi mov_from_rs1, mov_to_rs1, imm \u003c .do_add {\n    .reg_imm_fast_op mov_from_rs1, mov_to_rs1, imm, .do_add\n}\n\n// Sets rs1 according to the given \"fcall_label\", rs2 to the given imm,\n//  fcalls \"do_op\", then moves the result to the appropriate dst reg.\ndef reg_imm_fast_op mov_from_dest, mov_to_rs1, imm, do_op @ table, xor_imm_to_rs2, end \u003c .ret, .zero_rs2, .rs2 {\n    wflip .ret+w, table+dw, .ret\n\n    pad 16\n  table:\n    .ret+dbit+2; do_op          // 4th\n    .ret+dbit+1; mov_to_rs1     // 1st\n    .ret+dbit+1; xor_imm_to_rs2 // 3rd\n    .ret+dbit+0; .zero_rs2      // 2nd\n    .ret+dbit+0; mov_from_dest  // 5th\n    wflip .ret+w, table+5*dw, end   // 6th\n\n  xor_imm_to_rs2:\n    .__xor_by_hex_const .HLEN, .rs2, imm\n    stl.fret .ret\n\n  end:\n}\n\ndef moves_to_from_middle_regs {\n  zero_rs2:\n    hex.zero .HLEN, .rs2\n    stl.fret .ret\n  ...\n}\n```\n\nMost of the space goes on the two `wflip`s (total `@-4` ops).  \nThe line with `1st` is done first, `2nd` goes second, and so on. That's a compact way of doing multiple `fcall`s with a single pair of `wflip`s.  \n\nSo as you see, the macro gets a `mov_to_rs1` and `mov_from_dest` macros. For the example of the `addi x10, x11, 7`, the next macro names will be specified:\n```python\nns riscv {\n  mov_rs1_to_x10:\n    hex.mov .HLEN, .regs.x10, .rs1\n    stl.fret .ret\n\n  mov_x11_to_rs1:\n    hex.mov .HLEN, .rs1, .regs.x11\n    stl.fret .ret\n}\n```\nAnd the `addi x10, x11, 7` opcode will be compiled into `riscv.addi mov_rs1_to_x10, mov_x11_to_rs1, 7`.\n\nSo when the `1st` line is executed, the `mov_x11_to_rs1` code will be executed, and it will return to the start of the `2nd` line.  \nNote that most of the macros use the global fj variables `rs1, rs2, rd` (part of the `riscv` namespace).  \nThen, in the second line `rs2` is being zeroed.  \nThe third line xors the given immediate (`7`) to `rs2`, and the forth line does the actual addition (by jumping to `do_op` which is `do_add` in our case).  \nThe fifth line will move the result (which `do_add` puts in `rs1`) to `x10`, using the given `mov_rs1_to_x10` argument.  \nThen, the macro will finish.\n\nIf you want to understand it better, feel free to _jump_ into the FlipJump and read how things work in the bits and bytes level.\n\nThe next phase uses the `flipjump` python package to compile the given `.fj` files into the compiles `.fjm` file (which is segments of data, and by data I mean bits of flips and jumps).  \nThe last phase, running the `.fjm` file, uses the `flipjump` package to interpret the `.fjm` file, and allows to debug it too.\n\n#### Jumps Tables, Memory?\nIn the previous section I talked about the `ops.fj` file that was created in the compilation process, but there are two more files that gets created in that process too.\n\n##### `mem.fj`:\nThe entire loadable memory of the compiled elf is being loaded into flipjump using this file. It contains all the loadable bytes of the memory in fj `hex` variables.  \nThere are no memory restrictions on it, thus the running program can read/write/execute from it freely.  \nNote that the riscv opcodes are part of the loadable memory too, and you can modify that part of memory too, and it will change, but the compiled riscv-ops themselves (in `ops.fj`) won't change.\n\n##### `jmp.fj`:\nThat's a jump table to every runnable riscv address. That helps us in jumping ops, because the macro addresses of the ops in the `ops.fj` can't be predicted easily.  \nThink of how can you jump to address 0x144. The label `riscv.ADDR_00000144` in `ops.fj` is not in some fixed place, or something that related to `0x144`. Yet, the current;y running opcode ant to jump to address `0x144`. Then what do we do?  \nUse a jump table! It looks something like:\n```python\nsegment .JMP + 0x00000000/4*dw\n;.ADDR_00000000\n;.ADDR_00000004\n...\n;.ADDR_00000144\n```\nThe `0x144` address is at fixed offset from the global `.JMP` address, thus jumping to riscv memory address `0x144` became as easy as jumping to fj-address `.JMP + 0x144*dw` (as `dw` is the length of one fj opcode, in bits).\n\n## Tests\n\nSimply run `pytest` to run the tests.\nThis package is tested on linux and python 3.13.\n\n## Related projects\n- [bf2fj](https://github.com/tomhea/bf2fj) - Brainfuck to FlipJump compiler.\n- [FlipJump](https://github.com/tomhea/flip-jump) - The flipjump language macro assembler, standard library, and interpreter.\n  - [Learn low-level FlipJump](https://github.com/tomhea/flip-jump/wiki/Learn-FlipJump)\n- [fji-cpp](https://github.com/tomhea/fji-cpp) - Faster C++ interpreter for FlipJump.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomhea%2Fc2fj","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomhea%2Fc2fj","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomhea%2Fc2fj/lists"}