├── .github └── workflows │ ├── poetry-publish.yml │ └── tests.yml ├── .gitignore ├── CMakeLists ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── c2fj ├── __init__.py ├── c2fj_main.py ├── compilation_files │ ├── Makefile_single_c_generic │ ├── c2fj_init.c │ ├── include │ │ └── c2fj_syscall.h │ ├── linker_script.ld │ └── riscvlib.fj ├── riscv_instructions.py └── riscv_to_fj.py ├── gitattributes.txt ├── pyproject.toml ├── res ├── c2fj_stats.png ├── compiled_elf.png ├── compiled_fj_files.png └── compiled_fjm.png └── tests ├── conftest.py ├── programs ├── hello_float │ ├── input.txt │ ├── main.c │ └── output.txt ├── hello_input │ ├── input.txt │ ├── main.c │ └── output.txt ├── hello_input_number │ ├── input.txt │ ├── main.c │ └── output.txt ├── hello_math │ ├── input.txt │ ├── main.c │ └── output.txt ├── hello_world │ ├── input.txt │ ├── main.c │ └── output.txt ├── multiple_files │ ├── Makefile │ ├── calculate_int.c │ ├── calculate_int.h │ ├── globals.c │ ├── input.txt │ ├── main.c │ └── output.txt ├── primes │ ├── input.txt │ ├── main.c │ └── output.txt ├── print_alice │ ├── input.txt │ ├── main.c │ └── output.txt ├── riscv_ops__all_c_syscalls │ ├── Makefile │ ├── input.txt │ ├── main.c │ └── output.txt ├── riscv_ops__alu │ ├── Makefile │ ├── input.txt │ ├── main.c │ └── output.txt ├── riscv_ops__alu_imm │ ├── Makefile │ ├── input.txt │ ├── main.c │ └── output.txt ├── riscv_ops__jumps │ ├── Makefile │ ├── input.txt │ ├── main.c │ └── output.txt ├── riscv_ops__memory │ ├── Makefile │ ├── input.txt │ ├── main.c │ └── output.txt ├── riscv_ops__rv32m │ ├── Makefile │ ├── input.txt │ ├── main.c │ └── output.txt └── sanity │ ├── input.txt │ ├── main.c │ └── output.txt └── test_c2fj.py /.github/workflows/poetry-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload the new c2fj python package to pypi, using poetry-publish, everytime a new release is created. 2 | 3 | 4 | name: Upload python package 5 | on: 6 | release: 7 | types: [published] 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Build and publish to pypi 14 | uses: JRubics/poetry-publish@v1.16 15 | with: 16 | pypi_token: ${{ secrets.PYPI_API_TOKEN }} 17 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | # Note: add all job names to the deploy-status.needs. 2 | 3 | name: Tests 4 | 5 | on: 6 | workflow_dispatch: 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | test_programs: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v4 18 | with: 19 | python-version: 3.13 20 | - name: Install python dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install .[tests] 24 | - name: Install compilation dependencies 25 | run: | 26 | sudo apt-get install -y picolibc-riscv64-unknown-elf 27 | - name: Test pytest 28 | run: | 29 | pytest -n auto -vv --junitxml=./run-test-results.xml 30 | - name: Show tests results 31 | if: always() 32 | uses: pmeier/pytest-results-action@main 33 | with: 34 | path: ./*-test-results.xml 35 | 36 | # This allows us to have a branch protection rule this entire workflow 37 | deploy-status: 38 | runs-on: ubuntu-latest 39 | needs: [ test_programs ] 40 | if: always() 41 | steps: 42 | - name: Successful deploy 43 | if: ${{ !(contains(needs.*.result, 'failure')) }} 44 | run: exit 0 45 | - name: Failing deploy 46 | if: ${{ contains(needs.*.result, 'failure') }} 47 | run: exit 1 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | 3 | # Prerequisites 4 | *.d 5 | 6 | # Object files 7 | *.o 8 | *.ko 9 | *.obj 10 | *.elf 11 | 12 | # Linker output 13 | *.ilk 14 | *.map 15 | *.exp 16 | 17 | # Precompiled Headers 18 | *.gch 19 | *.pch 20 | 21 | # Libraries 22 | *.lib 23 | *.a 24 | *.la 25 | *.lo 26 | 27 | # Shared objects (inc. Windows DLLs) 28 | *.dll 29 | *.so 30 | *.so.* 31 | *.dylib 32 | 33 | # Executables 34 | *.exe 35 | *.out 36 | *.app 37 | *.i*86 38 | *.x86_64 39 | *.hex 40 | 41 | # Debug files 42 | *.dSYM/ 43 | *.su 44 | *.idb 45 | *.pdb 46 | 47 | # Kernel Module Compile Results 48 | *.mod* 49 | *.cmd 50 | .tmp_versions/ 51 | modules.order 52 | Module.symvers 53 | Mkfile.old 54 | dkms.conf 55 | 56 | c2fj/.idea/ 57 | c2fj/cmake-build-debug/ 58 | *pyc 59 | build/ 60 | -------------------------------------------------------------------------------- /CMakeLists: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/CMakeLists -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | [flipjumpproject@gmail.com](mailto:flipjumpproject@gmail.com). 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series of 86 | actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or permanent 93 | ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within the 113 | community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.1, available at 119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 126 | [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | [translations]: https://www.contributor-covenant.org/translations 133 | 134 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2025, tomhea 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GitHub](https://img.shields.io/github/license/tomhea/c2fj)](LICENSE) 2 | [![Website](https://img.shields.io/website?down_color=red&down_message=down&up_message=up&url=https%3A%2F%2Fesolangs.org%2Fwiki%2FFlipJump)](https://esolangs.org/wiki/FlipJump) 3 | [![PyPI - Version](https://img.shields.io/pypi/v/c2fj)](https://pypi.org/project/c2fj/) 4 | 5 | # c2fj 6 | Compiling C --> RiscV --> [Flipjump](https://github.com/tomhea/flip-jump) --> .fjm 7 | 8 | This 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). 9 | 10 | An example program, [primes/main.c](tests/programs/primes/main.c): 11 | ```c 12 | int main() { 13 | printf("Calculate primes up to: "); 14 | int max_number; 15 | scanf("%d", &max_number); 16 | 17 | ... 18 | 19 | for (int p = 3; p <= max_number; p += 2) { 20 | if (non_prime[p] == false) { 21 | for (int i = p*p; i <= max_number; i += p) { 22 | non_prime[i] = true; 23 | } 24 | printf("%d\n", p); 25 | } 26 | } 27 | 28 | return 0; 29 | } 30 | ``` 31 | Compiled into this: 32 | 33 | ![img.png](res/compiled_elf.png) 34 | 35 | Which was compiled into this: 36 | 37 | ![img.png](res/compiled_fj_files.png) 38 | 39 | Which in turn compiled into: 40 | 41 | ![img.png](res/compiled_fjm.png) 42 | 43 | Now, run it (Remember, these are flipjump ops that are running): 44 | 45 | ```text 46 | Calculate primes up to: 20 47 | 2 48 | 3 49 | 5 50 | 7 51 | 11 52 | 13 53 | 17 54 | 19 55 | Program exited with exit code 0x0. 56 | ``` 57 | 58 | # How to install 59 | ``` 60 | >>> pip install c2fj 61 | >>> sudo apt install picolibc-riscv64-unknown-elf 62 | ``` 63 | 64 | # How to use 65 | 66 | Simply `python3 c2fj.py file.c` will compile your c file into an elf, into fj files, into fjm, then run it. 67 | 68 | `c2fj` supports the next flags: 69 | - `--breakpoints` Place a fj-breakpoint at the start of the specified riscv addresses 70 | - `--single-step` Place fj-breakpoints at the start of all riscv opcodes 71 | - `--unify_fj` Unify the generated fj files into a single file 72 | - `--finish-after` Stop the compilation at any step (before running, before creating fjm, etc.) 73 | - `--build-dir` Save the builds in this directory 74 | 75 | ## What if my project is more then a single c? 76 | 77 | We support specifying a `Makefile` path, instead of the c file! 78 | Your Makefile will have to rely on some constants that `c2fj` will fill: 79 | ```c 80 | C2FJ_GCC_OPTIONS 81 | C2FJ_LINKER_SCRIPT 82 | C2FJ_SOURCES 83 | C2FJ_INCLUDE_DIRS 84 | ELF_OUT_PATH 85 | ``` 86 | 87 | An example Makefile: 88 | ```makefile 89 | GCC := riscv64-unknown-elf-gcc 90 | GCC_FLAGS := -O3 91 | 92 | SOURCES := $(C2FJ_SOURCES) main.c globals.c calculate_int.c 93 | OBJECTS := $(SOURCES:.c=.o) 94 | 95 | all: | 96 | $(GCC) $(C2FJ_GCC_OPTIONS) $(GCC_FLAGS) $(SOURCES) -I $(C2FJ_INCLUDE_DIRS) -T $(C2FJ_LINKER_SCRIPT) -o $(ELF_OUT_PATH) 97 | 98 | clean: 99 | rm -r build 2>/dev/null || true 100 | 101 | .PHONY: clean all 102 | 103 | ``` 104 | 105 | You can also specify your own linker script. It should contain the following: 106 | - `_stack_end` (just after the end of the stack) 107 | - `_sdata` (start of the data section) 108 | - `__heap_start` (start of the heap) 109 | 110 | ## How Does It Work? 111 | First your C files are being compile to a RiscV elf. 112 | The 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. 113 | 114 | For example, look at `exit` ([c2fj_init.c](c2fj/compilation_files/c2fj_init.c)): 115 | 116 | ```c 117 | void exit(int status) { 118 | asm volatile ("jal %0, .+10" ::"r"(status):"memory"); 119 | __builtin_unreachable(); 120 | } 121 | ``` 122 | 123 | It uses jal with bad offset, thus will be parsed here as: ([riscv_instructions.py](c2fj/riscv_instructions.py)) 124 | ```python 125 | elif imm == JAL_EXIT_IMMEDIATE: 126 | return f' .syscall.exit {register_name(rd)}\n' 127 | ``` 128 | Thus, will get to the flipjump implementation of: ([riscvlib.fj](c2fj/compilation_files/riscvlib.fj)) 129 | ```python 130 | def exit src_register { 131 | stl.output "Program exited with exit code " 132 | hex.print_uint 2, src_register, 1, 1 133 | stl.output ".\n" 134 | stl.loop 135 | } 136 | ``` 137 | 138 | You can think of it like this: The C->RiscV compilation compiles the syscalls to a special (invalid) RiscV op, that gets parsed and further compiled into the fj implementation of the "requested syscall". 139 | The supported syscalls can be found in [c2fj_init.c](c2fj/compilation_files/c2fj_init.c), and they contain `_getc`, `_putc`, `exit`, `sbrk`. 140 | 141 | Every other opcode (Let's follow `addi x10, x11, 7` for example), will be compiled into itself. 142 | 143 | The RiscV -> FlipJump part of the compilation parses the compiled elf, and matches each opcode with the appropriate flipjump macro. For example: 144 | ```python 145 | elif opcode == RV_ALU_IMM: 146 | if funct3 == RV_ADDI: 147 | ops_file.write(i_type('addi', full_op)) 148 | ``` 149 | 150 | Then 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. 151 | That 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. 152 | That means that the compilation time doesn't really depend on the size of your codebase. 153 | 154 | The way it works, is that each opcode is implemented once in the `riscv.start` macro. 155 | For example: 156 | ```python 157 | do_add: 158 | hex.add .HLEN, .rs1, .rs2 159 | stl.fret .ret 160 | ``` 161 | Note how `addi` is implemented: 162 | ```python 163 | def addi mov_from_rs1, mov_to_rs1, imm < .do_add { 164 | .reg_imm_fast_op mov_from_rs1, mov_to_rs1, imm, .do_add 165 | } 166 | 167 | // Sets rs1 according to the given "fcall_label", rs2 to the given imm, 168 | // fcalls "do_op", then moves the result to the appropriate dst reg. 169 | def reg_imm_fast_op mov_from_dest, mov_to_rs1, imm, do_op @ table, xor_imm_to_rs2, end < .ret, .zero_rs2, .rs2 { 170 | wflip .ret+w, table+dw, .ret 171 | 172 | pad 16 173 | table: 174 | .ret+dbit+2; do_op // 4th 175 | .ret+dbit+1; mov_to_rs1 // 1st 176 | .ret+dbit+1; xor_imm_to_rs2 // 3rd 177 | .ret+dbit+0; .zero_rs2 // 2nd 178 | .ret+dbit+0; mov_from_dest // 5th 179 | wflip .ret+w, table+5*dw, end // 6th 180 | 181 | xor_imm_to_rs2: 182 | .__xor_by_hex_const .HLEN, .rs2, imm 183 | stl.fret .ret 184 | 185 | end: 186 | } 187 | 188 | def moves_to_from_middle_regs { 189 | zero_rs2: 190 | hex.zero .HLEN, .rs2 191 | stl.fret .ret 192 | ... 193 | } 194 | ``` 195 | 196 | Most of the space goes on the two `wflip`s (total `@-4` ops). 197 | The 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. 198 | 199 | So 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: 200 | ```python 201 | ns riscv { 202 | mov_rs1_to_x10: 203 | hex.mov .HLEN, .regs.x10, .rs1 204 | stl.fret .ret 205 | 206 | mov_x11_to_rs1: 207 | hex.mov .HLEN, .rs1, .regs.x11 208 | stl.fret .ret 209 | } 210 | ``` 211 | And the `addi x10, x11, 7` opcode will be compiled into `riscv.addi mov_rs1_to_x10, mov_x11_to_rs1, 7`. 212 | 213 | So 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. 214 | Note that most of the macros use the global fj variables `rs1, rs2, rd` (part of the `riscv` namespace). 215 | Then, in the second line `rs2` is being zeroed. 216 | The 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). 217 | The fifth line will move the result (which `do_add` puts in `rs1`) to `x10`, using the given `mov_rs1_to_x10` argument. 218 | Then, the macro will finish. 219 | 220 | If you want to understand it better, feel free to _jump_ into the FlipJump and read how things work in the bits and bytes level. 221 | 222 | The 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). 223 | The last phase, running the `.fjm` file, uses the `flipjump` package to interpret the `.fjm` file, and allows to debug it too. 224 | 225 | #### Jumps Tables, Memory? 226 | In 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. 227 | 228 | ##### `mem.fj`: 229 | The 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. 230 | There are no memory restrictions on it, thus the running program can read/write/execute from it freely. 231 | Note 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. 232 | 233 | ##### `jmp.fj`: 234 | That'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. 235 | Think 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? 236 | Use a jump table! It looks something like: 237 | ```python 238 | segment .JMP + 0x00000000/4*dw 239 | ;.ADDR_00000000 240 | ;.ADDR_00000004 241 | ... 242 | ;.ADDR_00000144 243 | ``` 244 | The `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). 245 | 246 | ## Tests 247 | 248 | Simply run `pytest` to run the tests. 249 | This package is tested on linux and python 3.13. 250 | 251 | ## Related projects 252 | - [bf2fj](https://github.com/tomhea/bf2fj) - Brainfuck to FlipJump compiler. 253 | - [FlipJump](https://github.com/tomhea/flip-jump) - The flipjump language macro assembler, standard library, and interpreter. 254 | - [Learn low-level FlipJump](https://github.com/tomhea/flip-jump/wiki/Learn-FlipJump) 255 | - [fji-cpp](https://github.com/tomhea/fji-cpp) - Faster C++ interpreter for FlipJump. 256 | -------------------------------------------------------------------------------- /c2fj/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/c2fj/__init__.py -------------------------------------------------------------------------------- /c2fj/c2fj_main.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import contextlib 3 | import os 4 | import shutil 5 | from enum import Enum 6 | from pathlib import Path 7 | from tempfile import TemporaryDirectory 8 | from typing import List, Optional, Iterator, Union 9 | 10 | import flipjump 11 | from flipjump.utils import PrintTimer 12 | 13 | from c2fj.riscv_to_fj import create_fj_files_from_riscv_elf 14 | 15 | COMPILATION_FILES_DIR = Path(__file__).parent / "compilation_files" 16 | 17 | C2FJ_MAKE_VARS = { 18 | 'C2FJ_GCC_OPTIONS': '-march=rv32im -mabi=ilp32 --specs=picolibc.specs --crt0=hosted ' 19 | '-nostartfiles -fno-merge-constants -fno-toplevel-reorder -fdata-sections -ffunction-sections', 20 | 'C2FJ_LINKER_SCRIPT': f'{COMPILATION_FILES_DIR / "linker_script.ld"}', 21 | 'C2FJ_SOURCES': f'{COMPILATION_FILES_DIR / "c2fj_init.c"}', 22 | 'C2FJ_INCLUDE_DIRS': f'{COMPILATION_FILES_DIR / "include"}', 23 | } 24 | 25 | C_EXTENSIONS = ['.c', '.h', '.cc'] 26 | ELF_EXTENSIONS = ['.elf', '.out'] 27 | 28 | 29 | class BuildNames(Enum): 30 | ELF = 'main.elf' 31 | OPS_FJ = 'ops.fj' 32 | MEMORY_FJ = 'mem.fj' 33 | JUMPS_FJ = 'jmp.fj' 34 | UNIFIED_FJ = 'unified.fj' 35 | FJM = 'main.fjm' 36 | FJ_DEBUG = 'debug.fjd' 37 | 38 | 39 | def compile_c_to_riscv(file: Path, build_path: Path) -> None: 40 | if file.suffix in ELF_EXTENSIONS: 41 | shutil.copy(file, build_path) 42 | return 43 | 44 | with PrintTimer(' make c->riscv: '): 45 | if file.suffix in C_EXTENSIONS: 46 | C2FJ_MAKE_VARS['SINGLE_C_FILE'] = str(file) 47 | makefile = COMPILATION_FILES_DIR / "Makefile_single_c_generic" 48 | else: 49 | makefile = file 50 | 51 | C2FJ_MAKE_VARS['ELF_OUT_PATH'] = str(build_path) 52 | 53 | make_vars_string = ' '.join(f'{k}="{v}"' for k, v in C2FJ_MAKE_VARS.items()) 54 | assert 0 == os.system(f"make -s -f {makefile} -C {makefile.parent} {make_vars_string}"), "Make c->riscv failed." 55 | 56 | 57 | def get_fj_files_in_order(build_dir: Path) -> List[Path]: 58 | """ 59 | @return: The fj files in their compilation order. 60 | """ 61 | return [COMPILATION_FILES_DIR / 'riscvlib.fj', build_dir / BuildNames.MEMORY_FJ.value, 62 | build_dir / BuildNames.JUMPS_FJ.value, build_dir / BuildNames.OPS_FJ.value] 63 | 64 | 65 | def unify_fj_files(ordered_fj_files: List[Path], build_path: Path) -> None: 66 | with build_path.open('w') as unified: 67 | for fj_file in ordered_fj_files: 68 | with fj_file.open('r') as f: 69 | unified.write(f'// START {fj_file.name}\n\n') 70 | unified.write(f.read()) 71 | unified.write(f'// END {fj_file.name}\n\n\n\n') 72 | 73 | 74 | def compile_riscv_to_fj(build_dir: Path, unify_fj: bool = False) -> None: 75 | with PrintTimer(' comp riscv->fj: '): 76 | create_fj_files_from_riscv_elf( 77 | elf_path=build_dir / BuildNames.ELF.value, 78 | mem_path=build_dir / BuildNames.MEMORY_FJ.value, 79 | jmp_path=build_dir / BuildNames.JUMPS_FJ.value, 80 | ops_path=build_dir / BuildNames.OPS_FJ.value, 81 | ) 82 | 83 | if unify_fj: 84 | unify_fj_files(get_fj_files_in_order(build_dir), build_dir / BuildNames.UNIFIED_FJ.value) 85 | 86 | 87 | def compile_fj_to_fjm(build_dir: Path) -> None: 88 | flipjump.assemble( 89 | fj_file_paths=get_fj_files_in_order(build_dir), 90 | output_fjm_path=build_dir / BuildNames.FJM.value, 91 | warning_as_errors=False, 92 | debugging_file_path=build_dir / BuildNames.FJ_DEBUG.value, 93 | show_statistics=False, 94 | ) 95 | 96 | 97 | def run_fjm(build_dir: Path, breakpoint_addresses: Optional[List[int]] = None, single_step: bool = False) -> None: 98 | flipjump.debug( 99 | fjm_path=build_dir / BuildNames.FJM.value, 100 | debugging_file=build_dir / BuildNames.FJ_DEBUG.value, 101 | last_ops_debugging_list_length=6000, 102 | breakpoints_contains={"riscv.ADDR_"} if single_step else None, 103 | breakpoints={f"riscv.ADDR_{addr:08X}" for addr in breakpoint_addresses} if breakpoint_addresses else None, 104 | ) 105 | 106 | 107 | class FinishCompilingAfter(Enum): 108 | ELF = 'elf' 109 | FJ = 'fj' 110 | FJM = 'fjm' 111 | RUN = 'run' 112 | 113 | 114 | def c2fj(file: Path, build_dir: Union[None, str, Path] = None, unify_fj: bool = False, 115 | finish_compiling_after: FinishCompilingAfter = FinishCompilingAfter.RUN, 116 | breakpoint_addresses: Optional[List[int]] = None, single_step: bool = False) -> None: 117 | with get_build_directory(build_dir) as build_dir: 118 | compile_c_to_riscv(file, build_dir / BuildNames.ELF.value) 119 | if finish_compiling_after == FinishCompilingAfter.ELF: 120 | return 121 | 122 | compile_riscv_to_fj(build_dir, unify_fj) 123 | if finish_compiling_after == FinishCompilingAfter.FJ: 124 | return 125 | 126 | compile_fj_to_fjm(build_dir) 127 | if finish_compiling_after == FinishCompilingAfter.FJM: 128 | return 129 | 130 | run_fjm(build_dir, breakpoint_addresses=breakpoint_addresses, single_step=single_step) 131 | 132 | 133 | @contextlib.contextmanager # type: ignore 134 | def get_build_directory(build_dir: Union[None, str, Path]) -> Iterator[Path]: 135 | if build_dir is None: 136 | with TemporaryDirectory() as temp_dir: 137 | yield Path(temp_dir).absolute() 138 | return 139 | 140 | build_dir = Path(build_dir).absolute() 141 | if not build_dir.exists() or not build_dir.is_dir(): 142 | raise NotADirectoryError(f"This isn't a directory: {build_dir}") 143 | 144 | yield build_dir 145 | return 146 | 147 | 148 | def main() -> None: 149 | argument_parser = argparse.ArgumentParser('c2fj', description='Compile C to fj') 150 | argument_parser.add_argument('file', metavar='PATH', help=f'Can be a makefile, ' 151 | f'a single c file (ends with {" ".join(C_EXTENSIONS)}), ' 152 | f'or a compiled elf file (ends with {" ".join(ELF_EXTENSIONS)})') 153 | argument_parser.add_argument('--build-dir', metavar='PATH', default=None, 154 | help='If specified, the builds will be stored in this directory') 155 | argument_parser.add_argument('--unify-fj', '-u', action='store_true', 156 | help=f'Unify the build fj files into a single "{BuildNames.UNIFIED_FJ.value}" file') 157 | argument_parser.add_argument('--finish-after', '-f', metavar='PHASE', 158 | default=FinishCompilingAfter.RUN.value, 159 | choices=[e.value for e in FinishCompilingAfter]) 160 | argument_parser.add_argument('--breakpoints', '-b', metavar='ADDR', nargs='+', default=None, 161 | type=lambda s: int(s, 0), help='breakpoint addresses') 162 | argument_parser.add_argument('--single-step', '-s', action='store_true', 163 | help='Stop at the start of every riscv opcode') 164 | args = argument_parser.parse_args() 165 | 166 | file = Path(args.file) 167 | if not file.exists() or not file.is_file(): 168 | raise FileNotFoundError(f"This isn't a file: {file}") 169 | 170 | c2fj(file.absolute(), args.build_dir, args.unify_fj, FinishCompilingAfter(args.finish_after), 171 | args.breakpoints, args.single_step) 172 | 173 | 174 | if __name__ == '__main__': 175 | main() 176 | -------------------------------------------------------------------------------- /c2fj/compilation_files/Makefile_single_c_generic: -------------------------------------------------------------------------------- 1 | GCC := riscv64-unknown-elf-gcc 2 | GCC_FLAGS := -O3 3 | 4 | SOURCES := $(C2FJ_SOURCES) $(SINGLE_C_FILE) 5 | OBJECTS := $(SOURCES:.c=.o) 6 | 7 | all: | 8 | $(GCC) $(C2FJ_GCC_OPTIONS) $(GCC_FLAGS) $(SOURCES) -I $(C2FJ_INCLUDE_DIRS) -T $(C2FJ_LINKER_SCRIPT) -o $(ELF_OUT_PATH) 9 | 10 | clean: 11 | rm -r build 2>/dev/null || true 12 | 13 | .PHONY: clean all 14 | -------------------------------------------------------------------------------- /c2fj/compilation_files/c2fj_init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "c2fj_syscall.h" 7 | 8 | 9 | extern uint32_t _stack_end; 10 | extern uint32_t _sdata; 11 | extern uint32_t __heap_start; 12 | extern void __libc_init_array(void); 13 | extern void __libc_fini_array(void); 14 | extern int main(); 15 | 16 | 17 | caddr_t sbrk(int incr) { 18 | asm volatile ("jal %0, .+14" : "+r"(incr)::"memory"); 19 | return (caddr_t) incr; // The fj-sbrk returns the previous address in the same register. 20 | } 21 | 22 | void exit(int status) { 23 | asm volatile ("jal %0, .+10" ::"r"(status):"memory"); 24 | __builtin_unreachable(); 25 | } 26 | 27 | int close(int file) { 28 | return -1; 29 | } 30 | 31 | int fstat(int file, struct stat *st) { 32 | st->st_mode = S_IFCHR; 33 | 34 | return 0; 35 | } 36 | 37 | int isatty(int file) { 38 | return 1; 39 | } 40 | 41 | int lseek(int file, int ptr, int dir) { 42 | return 0; 43 | } 44 | 45 | void kill(int pid, int sig) { 46 | return; 47 | } 48 | 49 | int getpid(void) { 50 | return -1; 51 | } 52 | 53 | int write(int file, const char *ptr, int len) { 54 | if ((file != 1) && (file != 2)) { 55 | return -1; 56 | } 57 | 58 | const char* end_ptr = ptr + len; 59 | for (; ptr < end_ptr; ptr++) { 60 | char char_to_print = *ptr; 61 | c2fj_putc(char_to_print); 62 | } 63 | return len; 64 | } 65 | 66 | int puts(const char* str) { 67 | for (char ch = *str; ch; ch = *(++str)) { 68 | c2fj_putc(ch); 69 | } 70 | c2fj_putc('\n'); 71 | return 0; 72 | } 73 | 74 | int _putc(char c, FILE* file) { 75 | (void) file; 76 | c2fj_putc(c); 77 | return c; 78 | } 79 | 80 | int _getc(FILE* file) { 81 | (void) file; 82 | return c2fj_getc(); 83 | } 84 | 85 | 86 | int read(int file, char *ptr, int len) { 87 | if (file != 0) { 88 | return -1; 89 | } 90 | 91 | char* start_ptr = ptr; 92 | char* end_ptr = ptr + len; 93 | for (; ptr < end_ptr; ptr++) { 94 | *ptr = c2fj_getc(); 95 | if (*ptr == '\n') { 96 | return (ptr - start_ptr) + 1; 97 | } 98 | } 99 | return len; 100 | } 101 | 102 | 103 | static FILE __stdin = FDEV_SETUP_STREAM(NULL, _getc, NULL, __SRD); 104 | static FILE __stdout = FDEV_SETUP_STREAM(_putc, NULL, NULL, __SWR); 105 | FILE *const stdin = &__stdin; 106 | FILE *const stdout = &__stdout; 107 | __strong_reference(stdout, stderr); 108 | 109 | 110 | int fputc(int c, FILE* file) { 111 | if (file != stdout && file != stderr) { 112 | return EOF; 113 | } 114 | 115 | c2fj_putc(c); 116 | return c; 117 | } 118 | 119 | 120 | __attribute__((naked)) void _start(void) { 121 | asm volatile ("la sp, _stack_end - 8":::"memory"); 122 | asm volatile ("la gp, _sdata + 0x800":::"memory"); 123 | sbrk((int32_t)&__heap_start - (int32_t)sbrk(0)); 124 | 125 | __libc_init_array(); 126 | int status = main(); 127 | 128 | __libc_fini_array(); 129 | exit(status); 130 | 131 | while (1); 132 | } 133 | -------------------------------------------------------------------------------- /c2fj/compilation_files/include/c2fj_syscall.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | // Stringify macros 5 | #define __TO_STRING_INNER(x) #x 6 | #define __TO_STRING(x) __TO_STRING_INNER(x) 7 | 8 | 9 | #define __DEBUG_P_VALUE_INNER(p_value) (1002 + 4 * (p_value)) 10 | #define __DEBUG_P_VALUE(p_value) __DEBUG_P_VALUE_INNER(p_value) 11 | #define __DEBUG_P_VALUE_STRING(p_value) __TO_STRING(__DEBUG_P_VALUE(p_value)) 12 | #define c2fj_debug_p(p_value) asm volatile ("jal x0, .+" __DEBUG_P_VALUE_STRING(p_value):::"memory"); 13 | 14 | 15 | #define __PRINT_CHAR_VALUE_INNER(p_value) (3002 + 4 * (p_value)) 16 | #define __PRINT_CHAR_VALUE(p_value) __PRINT_CHAR_VALUE_INNER(p_value) 17 | #define __PRINT_CHAR_VALUE_STRING(p_value) __TO_STRING(__PRINT_CHAR_VALUE(p_value)) 18 | #define c2fj_print_char(p_value) asm volatile ("jal x0, .+" __PRINT_CHAR_VALUE_STRING(p_value):::"memory"); 19 | 20 | 21 | #define __FORCE_INLINE static __attribute__((always_inline)) inline 22 | 23 | 24 | __FORCE_INLINE void c2fj_print_registers() { 25 | asm volatile ("jal x0, .+18":::"memory"); 26 | } 27 | 28 | __FORCE_INLINE void c2fj_print_register(int reg) { 29 | asm volatile ("jal %0, .+22" ::"r"(reg):"memory"); 30 | } 31 | 32 | __FORCE_INLINE void c2fj_putc(const char char_to_print) { 33 | asm volatile ("jal %0, .+2" ::"r"(char_to_print):"memory"); 34 | } 35 | 36 | __FORCE_INLINE char c2fj_getc() { 37 | char new_byte; 38 | asm volatile ("jal %0, .+6" : "=r"(new_byte)::"memory"); 39 | return new_byte; 40 | } 41 | -------------------------------------------------------------------------------- /c2fj/compilation_files/linker_script.ld: -------------------------------------------------------------------------------- 1 | RAM_START_ADDRESS = 0x20000000; 2 | MEMORY_SIZE = 0x100000000; 3 | GUARD_SEGMENT_SIZE = 0x1000; 4 | STACK_SIZE = 0x20000; 5 | 6 | RAM_STACK_START = RAM_START_ADDRESS + GUARD_SEGMENT_SIZE; 7 | GENERIC_RAM_START = RAM_STACK_START + STACK_SIZE + GUARD_SEGMENT_SIZE; 8 | GENERIC_RAM_END = MEMORY_SIZE - GUARD_SEGMENT_SIZE - GENERIC_RAM_START; 9 | 10 | MEMORY 11 | { 12 | rom (rx) : ORIGIN = 0x00000000, LENGTH = RAM_START_ADDRESS 13 | ram_stack (rwx) : ORIGIN = RAM_STACK_START, LENGTH = STACK_SIZE 14 | ram (rwx) : ORIGIN = GENERIC_RAM_START, LENGTH = GENERIC_RAM_END 15 | } 16 | 17 | /* Entry point of the program */ 18 | ENTRY(_start) 19 | 20 | /* Section Definitions */ 21 | SECTIONS 22 | { 23 | .text : 24 | { 25 | *(.text.*) 26 | *(.rodata.*) 27 | _etext = .; 28 | } > rom 29 | 30 | /* stack section */ 31 | .stack (NOLOAD): 32 | { 33 | . = ALIGN(8); 34 | _stack = .; 35 | . = . + STACK_SIZE; 36 | _stack_end = .; 37 | . = ALIGN(8); 38 | } > ram_stack 39 | 40 | .data : 41 | { 42 | _sdata = .; 43 | *(.data*); 44 | *(.sdata*); 45 | *(.rodata*); 46 | *(.eh_frame*); 47 | _edata = .; 48 | } > ram AT >rom 49 | 50 | .preinit_array : 51 | { 52 | PROVIDE_HIDDEN (__preinit_array_start = .); 53 | KEEP (*(.preinit_array*)) 54 | PROVIDE_HIDDEN (__preinit_array_end = .); 55 | } > ram 56 | .init_array : 57 | { 58 | PROVIDE_HIDDEN (__init_array_start = .); 59 | KEEP (*(SORT(.init_array.*))) 60 | KEEP (*(.init_array*)) 61 | PROVIDE_HIDDEN (__init_array_end = .); 62 | } > ram 63 | .fini_array : 64 | { 65 | PROVIDE_HIDDEN (__fini_array_start = .); 66 | KEEP (*(SORT(.fini_array.*))) 67 | KEEP (*(.fini_array*)) 68 | PROVIDE_HIDDEN (__fini_array_end = .); 69 | } > ram 70 | 71 | /* .bss section which is used for uninitialized data */ 72 | .bss (NOLOAD) : 73 | { 74 | _sbss = . ; 75 | *(.bss) 76 | *(.bss.*) 77 | *(COMMON) 78 | _ebss = . ; 79 | } > ram 80 | 81 | .heap (NOLOAD): 82 | { 83 | . = ALIGN(8); 84 | __heap_start = . ; 85 | . = ALIGN(MEMORY_SIZE - GUARD_SEGMENT_SIZE); 86 | __heap_end = . ; 87 | } > ram 88 | } 89 | -------------------------------------------------------------------------------- /c2fj/compilation_files/riscvlib.fj: -------------------------------------------------------------------------------- 1 | 2 | 3 | // ---------- Init: 4 | 5 | 6 | ns riscv { 7 | MEM = 1<<(w-1) // start of memory 8 | JMP = .MEM - (.MEM / 32) // start of jump table 9 | BLEN = 4 // register size in byte-variables 10 | HLEN = .BLEN * 2 // register size in hex-variables 11 | REGISTER_SIZE = .HLEN*dw // register-variable size in memory bits 12 | 13 | 14 | def byte val { 15 | ;(val > 0xff ? 0xff : (val < 0 ? 0 : val)) * dw 16 | } 17 | def byte { 18 | .byte 0 19 | } 20 | ns byte { 21 | def vec n, val { 22 | rep(n, i) ..byte (val>>(8*i))&0xff 23 | } 24 | } 25 | 26 | def init { 27 | .regs.init 28 | .fast_macros 29 | .init_sbrk_ptr 30 | } 31 | 32 | ns regs { 33 | def init > start { 34 | start: 35 | .init_x0_to_x4 36 | .init_x5_to_x7 37 | .init_x8_to_x9 38 | .init_x10_to_x17 39 | .init_x18_to_x27 40 | .init_x28_to_x31 41 | .init_pc 42 | } 43 | 44 | def register { 45 | hex.vec ..HLEN 46 | } 47 | 48 | def init_x0_to_x4 > x0, zero, x1, ra, x2, sp, x3, gp, x4, tp { 49 | x0: 50 | zero: 51 | .register 52 | x1: 53 | ra: 54 | .register 55 | x2: 56 | sp: 57 | .register 58 | x3: 59 | gp: 60 | .register 61 | x4: 62 | tp: 63 | .register 64 | } 65 | 66 | def init_x5_to_x7 > x5, t0, x6, t1, x7, t2 { 67 | x5: 68 | t0: 69 | .register 70 | x6: 71 | t1: 72 | .register 73 | x7: 74 | t2: 75 | .register 76 | } 77 | 78 | def init_x8_to_x9 > x8, s0, fp, x9, s1 { 79 | x8: 80 | s0: 81 | fp: 82 | .register 83 | x9: 84 | s1: 85 | .register 86 | } 87 | 88 | def init_x10_to_x17 > x10, a0, x11, a1, x12, a2, x13, a3, x14, a4, x15, a5, x16, a6, x17, a7 { 89 | x10: 90 | a0: 91 | .register 92 | x11: 93 | a1: 94 | .register 95 | x12: 96 | a2: 97 | .register 98 | x13: 99 | a3: 100 | .register 101 | x14: 102 | a4: 103 | .register 104 | x15: 105 | a5: 106 | .register 107 | x16: 108 | a6: 109 | .register 110 | x17: 111 | a7: 112 | .register 113 | } 114 | 115 | def init_x18_to_x27 > x18, s2, x19, s3, x20, s4, x21, s5, x22, s6, x23, s7, x24, s8, x25, s9, x26, s10, x27, s11 { 116 | x18: 117 | s2: 118 | .register 119 | x19: 120 | s3: 121 | .register 122 | x20: 123 | s4: 124 | .register 125 | x21: 126 | s5: 127 | .register 128 | x22: 129 | s6: 130 | .register 131 | x23: 132 | s7: 133 | .register 134 | x24: 135 | s8: 136 | .register 137 | x25: 138 | s9: 139 | .register 140 | x26: 141 | s10: 142 | .register 143 | x27: 144 | s11: 145 | .register 146 | } 147 | 148 | def init_x28_to_x31 > x28, t3, x29, t4, x30, t5, x31, t6 { 149 | x28: 150 | t3: 151 | .register 152 | x29: 153 | t4: 154 | .register 155 | x30: 156 | t5: 157 | .register 158 | x31: 159 | t6: 160 | .register 161 | } 162 | 163 | def init_pc > pc { 164 | pc: 165 | .register 166 | } 167 | } 168 | 169 | def init_sbrk_ptr > sbrk_ptr { 170 | sbrk_ptr: 171 | hex.vec .HLEN, 0xfffff000 172 | } 173 | 174 | 175 | ns syscall { 176 | // Time Complexity: 4@+268 177 | // Space Complexity: 6@+364 178 | // prints the exit code (stored at src_register), then finishes. 179 | def exit src_register { 180 | stl.output "Program exited with exit code " 181 | hex.print_uint 2, src_register, 1, 1 182 | stl.output ".\n" 183 | stl.loop 184 | } 185 | 186 | // Time Complexity: 2@ 187 | // Space Complexity: 2@+52 188 | // output the least-significant byte of src_register. 189 | def write_byte src_register { 190 | hex.print src_register 191 | } 192 | 193 | // Time Complexity: 10@+14 194 | // Space Complexity: 10@+108 195 | // dst_register = input(8bits) 196 | def read_byte dst_register < ..regs.zero { 197 | hex.zero ..HLEN-2, dst_register + 2*dw 198 | hex.input dst_register 199 | rep(dst_register == ..regs.zero, _) hex.zero 2, dst_register 200 | } 201 | 202 | // Increments riscv.heap_ptr by the value in register. 203 | // Returns the old riscv.heap_ptr value in register. 204 | def sbrk register @ old_heap_ptr, end < riscv.sbrk_ptr { 205 | hex.mov ..HLEN, old_heap_ptr, riscv.sbrk_ptr 206 | hex.add ..HLEN, riscv.sbrk_ptr, register 207 | hex.mov ..HLEN, register, old_heap_ptr 208 | ;end 209 | 210 | old_heap_ptr: 211 | riscv.regs.register 212 | 213 | end: 214 | } 215 | 216 | // Prints all the register values. 217 | def debug_print_regs < ..print_all_regs, ..ret { 218 | stl.fcall ..print_all_regs, ..ret 219 | } 220 | 221 | // Outputs the hex value of src_register. 222 | def debug_print_reg src_register { 223 | stl.output "Value of register: " 224 | hex.print_uint ..HLEN, src_register, 1, 1 225 | stl.output "\n" 226 | } 227 | 228 | // Prints the given string. 229 | def print_string str { 230 | stl.output str 231 | } 232 | } 233 | 234 | 235 | def start entry_point { 236 | stl.startup_and_init_all // inits everything flipjump needs 237 | ;entry_point // start executing riscv opcodes 238 | .init // inits registers, constants, global code stubs 239 | } 240 | 241 | 242 | def inc_pc current_address { 243 | .__xor_pc_value_by_const current_address ^ (current_address + 4) 244 | } 245 | def __xor_pc_value_by_const constant < .regs.pc { 246 | .__xor_by_hex_const .HLEN, .regs.pc, constant 247 | } 248 | def __xor_by_hex_const n, dst, constant { 249 | rep(n, i) .__xor_by_hex_const dst + i*dw, (constant >> (i*4)) & 0xf 250 | } 251 | def __xor_by_hex_const dst, constant { 252 | rep(constant != 0, _) stl.wflip_macro dst + w, constant * dw 253 | } 254 | 255 | def jump_to_constant_address current_address, new_address { 256 | .__xor_pc_value_by_const current_address ^ new_address 257 | ;.JMP + new_address/4*dw 258 | } 259 | 260 | def jump_to_pc @ ptr < .regs.pc { 261 | hex.zero w/4, ptr 262 | hex.xor .HLEN, ptr, .regs.pc 263 | rep(#w-2, _) hex.shl_bit w/4, ptr 264 | hex.add_constant w/4, ptr, .JMP 265 | 266 | hex.ptr_jump ptr 267 | 268 | ptr: 269 | hex.vec w/4 270 | } 271 | 272 | } 273 | 274 | 275 | 276 | // Fast calls: 277 | 278 | 279 | ns riscv { 280 | // Initialize the opcode implementation stubs, to be jumped into by some of the riscv op macros. 281 | def fast_macros > ret, rs1, rs2, rd, mem_ptr { 282 | ret: 283 | ;0 284 | 285 | 286 | rs1: // most ops use the first .HLEN. mul ops use the extra .HLEN. 287 | hex.vec 2*.HLEN 288 | rs2: // most ops use the first .HLEN. mul ops use the extra .HLEN. 289 | hex.vec 2*.HLEN 290 | 291 | 292 | hex.vec .HLEN 293 | rd: // most ops use the first .HLEN. mul ops use .HLEN extra before and after. 294 | hex.vec 2*.HLEN 295 | 296 | 297 | mem_ptr: 298 | hex.vec w/4 299 | 300 | 301 | .moves_to_from_middle_regs 302 | .fast_calculate_mem_ptr 303 | .fast_debug_print_regs 304 | .fast_unimplemented_op 305 | 306 | .fast_add_to_pc 307 | 308 | .fast_cmp 309 | .fast_jump_to_pc 310 | 311 | .fast_read_memory 312 | .fast_write_memory 313 | 314 | .fast_sll 315 | .fast_sr 316 | 317 | .fast_alu 318 | .fast_slt 319 | .fast_sltu 320 | 321 | .fast_mul 322 | .fast_div_rem 323 | } 324 | 325 | def fast_unimplemented_op < .regs.pc > do_unimplemented_op { 326 | do_unimplemented_op: 327 | stl.output "Executed unimplemented op in address " 328 | hex.print_uint .HLEN, .regs.pc, 1, 1 329 | stl.output "\nFinished with error.\n\n" 330 | stl.loop 331 | } 332 | def unimplemented_op < .do_unimplemented_op { 333 | ;.do_unimplemented_op 334 | } 335 | 336 | 337 | def fast_jump_to_pc > jump_to_pc { 338 | jump_to_pc: 339 | .jump_to_pc 340 | } 341 | 342 | // Print the registers starting_index+{0,1,2,3} 343 | def _print_4_regs starting_index < .regs.start { 344 | hex.print_as_digit .HLEN, .regs.start + .HLEN*dw*(starting_index+0), 1 345 | stl.output " " 346 | hex.print_as_digit .HLEN, .regs.start + .HLEN*dw*(starting_index+1), 1 347 | stl.output " " 348 | hex.print_as_digit .HLEN, .regs.start + .HLEN*dw*(starting_index+2), 1 349 | stl.output " " 350 | hex.print_as_digit .HLEN, .regs.start + .HLEN*dw*(starting_index+3), 1 351 | stl.output "\n" 352 | } 353 | 354 | // Prints all the register values. 355 | def fast_debug_print_regs < .regs.pc, .sbrk_ptr, .ret > print_all_regs { 356 | print_all_regs: 357 | stl.output "\n" 358 | stl.output "x00..03 " 359 | ._print_4_regs 0x00 360 | stl.output "x04..07 " 361 | ._print_4_regs 0x04 362 | stl.output "x08..11 " 363 | ._print_4_regs 0x08 364 | stl.output "x12..15 " 365 | ._print_4_regs 0x0c 366 | stl.output "x16..19 " 367 | ._print_4_regs 0x10 368 | stl.output "x20..23 " 369 | ._print_4_regs 0x14 370 | stl.output "x24..27 " 371 | ._print_4_regs 0x18 372 | stl.output "x28..31 " 373 | ._print_4_regs 0x1c 374 | 375 | stl.output "pc,sbrk " 376 | hex.print_as_digit .HLEN, .regs.pc, 1 377 | stl.output " " 378 | hex.print_as_digit .HLEN, .sbrk_ptr, 1 379 | stl.output "\n\n" 380 | 381 | stl.fret .ret 382 | } 383 | 384 | def validate_mem_ptr_below_sbrk_ptr @ raise_error, end < .mem_ptr, .sbrk_ptr, .regs.pc { 385 | hex.cmp .HLEN, .mem_ptr, .sbrk_ptr, end, raise_error, raise_error 386 | 387 | raise_error: 388 | stl.output "Tried to access memory address above current brk:" 389 | stl.output "\n sbrk_ptr = " 390 | hex.print_uint .HLEN, .sbrk_ptr, 1, 1 391 | stl.output "\n mem_ptr = " 392 | hex.print_uint .HLEN, .mem_ptr, 1, 1 393 | stl.output "\n Current Address = " 394 | hex.print_uint .HLEN, .regs.pc, 1, 1 395 | stl.output "\nFinished with error.\n\n" 396 | stl.loop 397 | 398 | end: 399 | } 400 | 401 | // Sign extend rd from 2*num_of_bytes to .HLEN. 402 | def sign_extend_rd num_of_bytes @ negative_extension, end < .rd { 403 | hex.sign 2*num_of_bytes, .rd, negative_extension, end 404 | 405 | negative_extension: 406 | hex.not .HLEN - 2*num_of_bytes, .rd + 2*num_of_bytes * dw 407 | 408 | end: 409 | } 410 | 411 | // Read 1/2/4 bytes from the memory (where .mem_ptr points to) to .rd. 412 | // Notes: 413 | // - Assumes the value in mem_ptr is the fj address (i.e 0x8000000000001000 for the 32'th byte, not 32). 414 | // - The do_read{1,2}_sign_extended also sign extend the result in .rd to .HLEN. 415 | // - Expects return address in .ret. 416 | def fast_read_memory < .ret, .mem_ptr, .rd \ 417 | > do_read1, do_read1_sign_extended, do_read2, do_read2_sign_extended, do_read4 { 418 | do_read1: 419 | hex.read_byte 1, .rd, .mem_ptr 420 | hex.zero .HLEN-2, .rd+2*dw 421 | stl.fret .ret 422 | 423 | do_read1_sign_extended: 424 | hex.read_byte 1, .rd, .mem_ptr 425 | hex.zero .HLEN-2, .rd+2*dw 426 | .sign_extend_rd 1 427 | stl.fret .ret 428 | 429 | do_read2: 430 | hex.read_byte 2, .rd, .mem_ptr 431 | hex.zero .HLEN-4, .rd+4*dw 432 | stl.fret .ret 433 | 434 | do_read2_sign_extended: 435 | hex.read_byte 2, .rd, .mem_ptr 436 | hex.zero .HLEN-4, .rd+4*dw 437 | .sign_extend_rd 2 438 | stl.fret .ret 439 | 440 | do_read4: 441 | hex.read_byte 4, .rd, .mem_ptr 442 | stl.fret .ret 443 | } 444 | 445 | // Writes 1/2/4 bytes from .rs2 to the memory (where .mem_ptr points to). 446 | // Notes: 447 | // - Assumes the value in mem_ptr is the fj address (i.e 0x8000000000001000 for the 32'th byte, not 32). 448 | // - Expects return address in .ret. 449 | def fast_write_memory < .ret, .mem_ptr, .rs2 > do_write1, do_write2, do_write4 { 450 | do_write1: 451 | hex.write_byte .mem_ptr, .rs2 452 | stl.fret .ret 453 | do_write2: 454 | hex.write_byte 2, .mem_ptr, .rs2 455 | stl.fret .ret 456 | do_write4: 457 | hex.write_byte 4, .mem_ptr, .rs2 458 | stl.fret .ret 459 | } 460 | 461 | // In-place shift-left .rs1[:8] by .rs2[:2]%32. Return to .ret. 462 | def fast_sll @ check16,shift16, check8,shift8, check4,shift4, bit_shifts,bit_shifts_switch, \ 463 | do_3_shifts,do_2_shifts,do_1_shifts,do_0_shifts < .rs2, .rs1, .ret > do_sll { 464 | do_sll: 465 | 466 | check16: 467 | hex.if_flags .rs2+dw, 0xaaaa, check8, shift16 // if the lsb in on. 468 | shift16: 469 | hex.shl_hex .HLEN, 4, .rs1 470 | .rs2+dw+dbit+0; check8 471 | 472 | check8: 473 | hex.if_flags .rs2, 0xff00, check4, shift8 // if .rs2 >= 8 474 | shift8: 475 | hex.shl_hex .HLEN, 2, .rs1 476 | .rs2+dbit+3; check4 477 | 478 | check4: 479 | hex.if_flags .rs2, 0x00f0, bit_shifts, shift4 // if 4 <= .rs2 < 8 480 | shift4: 481 | hex.shl_hex .HLEN, .rs1 482 | .rs2+dbit+2; bit_shifts 483 | 484 | bit_shifts: 485 | wflip .rs2+w, bit_shifts_switch, .rs2 486 | pad 16 487 | bit_shifts_switch: 488 | ;do_0_shifts 489 | ;do_1_shifts 490 | ;do_2_shifts 491 | ;do_3_shifts 492 | 493 | do_3_shifts: 494 | hex.shl_bit .HLEN, .rs1 495 | do_2_shifts: 496 | hex.shl_bit .HLEN, .rs1 497 | do_1_shifts: 498 | hex.shl_bit .HLEN, .rs1 499 | do_0_shifts: 500 | wflip .rs2+w, bit_shifts_switch 501 | 502 | stl.fret .ret 503 | } 504 | 505 | // In-place shift-left .rs1[:8] by .rs2[:2]%32. Return to .ret. 506 | // If jump to do_sra does an arithmetical shift, else if jump to do_srl does a logical shift. 507 | def fast_sr @ do_sr, check16,shift16, check8,shift8, check4,shift4, bit_shifts,bit_shifts_switch, \ 508 | do_3_shifts,do_2_shifts,do_1_shifts,do_0_shifts, should_shift_1s,end, negative_data, is_arithmetical_shift \ 509 | < .rs2, .rs1, .ret > do_srl, do_sra { 510 | do_srl: 511 | bit.zero is_arithmetical_shift 512 | ;do_sr 513 | 514 | do_sra: 515 | bit.one is_arithmetical_shift 516 | ;do_sr 517 | 518 | do_sr: 519 | 520 | bit.zero should_shift_1s 521 | hex.sign .HLEN, .rs1, negative_data, check16 522 | negative_data: 523 | bit.xor should_shift_1s, is_arithmetical_shift 524 | 525 | check16: 526 | hex.if_flags .rs2+dw, 0xaaaa, check8, shift16 // if the lsb in on. 527 | shift16: 528 | hex.shr_hex .HLEN, 4, .rs1 529 | .rs2+dw+dbit+0; 530 | bit.if0 .should_shift_1s, check8 531 | hex.not 4, .rs1+(.HLEN-4)*dw 532 | 533 | check8: 534 | hex.if_flags .rs2, 0xff00, check4, shift8 // if .rs2 >= 8 535 | shift8: 536 | hex.shr_hex .HLEN, 2, .rs1 537 | .rs2+dbit+3; 538 | bit.if0 .should_shift_1s, check4 539 | hex.not 2, .rs1+(.HLEN-2)*dw 540 | 541 | check4: 542 | hex.if_flags .rs2, 0x00f0, bit_shifts, shift4 // if 4 <= .rs2 < 8 543 | shift4: 544 | hex.shr_hex .HLEN, .rs1 545 | .rs2+dbit+2; 546 | bit.if0 .should_shift_1s, bit_shifts 547 | hex.not .rs1+(.HLEN-1)*dw 548 | 549 | bit_shifts: 550 | wflip .rs2+w, bit_shifts_switch, .rs2 551 | pad 16 552 | bit_shifts_switch: 553 | ;do_0_shifts 554 | ;do_1_shifts 555 | ;do_2_shifts 556 | ;do_3_shifts 557 | 558 | do_3_shifts: 559 | hex.shr_bit .HLEN, .rs1 560 | bit.exact_xor .rs1 + (.HLEN-1)*dw + dbit+3, .should_shift_1s 561 | do_2_shifts: 562 | hex.shr_bit .HLEN, .rs1 563 | bit.exact_xor .rs1 + (.HLEN-1)*dw + dbit+3, .should_shift_1s 564 | do_1_shifts: 565 | hex.shr_bit .HLEN, .rs1 566 | bit.exact_xor .rs1 + (.HLEN-1)*dw + dbit+3, .should_shift_1s 567 | do_0_shifts: 568 | wflip .rs2+w, bit_shifts_switch, end 569 | 570 | should_shift_1s: 571 | bit.bit 572 | 573 | is_arithmetical_shift: 574 | bit.bit 575 | 576 | end: 577 | stl.fret .ret 578 | } 579 | 580 | def fast_alu < .rs1, .rs2, .ret > do_add, do_sub, do_xor, do_or, do_and { 581 | do_add: 582 | hex.add .HLEN, .rs1, .rs2 583 | stl.fret .ret 584 | do_sub: 585 | hex.sub .HLEN, .rs1, .rs2 586 | stl.fret .ret 587 | do_xor: 588 | hex.xor .HLEN, .rs1, .rs2 589 | stl.fret .ret 590 | do_or: 591 | hex.or .HLEN, .rs1, .rs2 592 | stl.fret .ret 593 | do_and: 594 | hex.and .HLEN, .rs1, .rs2 595 | stl.fret .ret 596 | } 597 | 598 | // Set mem_ptr according to rs1 + rs2. Validate that mem_ptr is below sbrk_ptr. 599 | def fast_calculate_mem_ptr < .rs1, .rs2, .mem_ptr, .ret > calculate_mem_ptr { 600 | calculate_mem_ptr: 601 | hex.zero w/4, .mem_ptr 602 | hex.xor .HLEN, .mem_ptr, .rs1 603 | hex.add .HLEN, .mem_ptr, .rs2 604 | 605 | .validate_mem_ptr_below_sbrk_ptr 606 | 607 | rep((#w)/4, _) hex.shl_hex w/4, .mem_ptr 608 | rep((#w)%4, _) hex.shl_bit w/4, .mem_ptr 609 | .__xor_by_hex_const w/4, .mem_ptr, .MEM 610 | 611 | stl.fret .ret 612 | } 613 | 614 | // Sets rd to 1 if [signed] rs1 do_slt { 616 | do_slt: 617 | hex.zero .HLEN, .rd 618 | hex.sub .HLEN, .rs1, .rs2 619 | hex.sign .HLEN, .rs1, write1, end 620 | 621 | write1: 622 | .rd+dbit;end 623 | 624 | end: 625 | stl.fret .ret 626 | } 627 | // Sets rd to 1 if [unsigned] rs1 do_sltu { 629 | do_sltu: 630 | hex.zero .HLEN, .rd 631 | hex.cmp .HLEN, .rs1, .rs2, write1, end, end 632 | 633 | write1: 634 | .rd+dbit; end 635 | 636 | end: 637 | stl.fret .ret 638 | } 639 | 640 | def fast_cmp @ dont_jump, take_jump < .rs1, .rs2, .ret > do_beq, do_bne, do_bltu, do_bgeu, do_blt, do_bge { 641 | do_beq: 642 | hex.cmp .HLEN, .rs1, .rs2, dont_jump, take_jump, dont_jump 643 | 644 | do_bne: 645 | hex.cmp .HLEN, .rs1, .rs2, take_jump, dont_jump, take_jump 646 | 647 | do_bltu: 648 | hex.cmp .HLEN, .rs1, .rs2, take_jump, dont_jump, dont_jump 649 | 650 | do_bgeu: 651 | hex.cmp .HLEN, .rs1, .rs2, dont_jump, take_jump, take_jump 652 | 653 | do_blt: 654 | hex.sub .HLEN, .rs1, .rs2 655 | hex.sign .HLEN, .rs1, take_jump, dont_jump 656 | 657 | do_bge: 658 | hex.sub .HLEN, .rs1, .rs2 659 | hex.sign .HLEN, .rs1, dont_jump, take_jump 660 | 661 | 662 | dont_jump: 663 | ;.ret 664 | 665 | take_jump: 666 | .ret+dbit; .ret 667 | } 668 | 669 | // Adds .rs1 to .regs.pc 670 | def fast_add_to_pc < .ret, .regs.pc, .rs1 > do_add_to_pc { 671 | do_add_to_pc: 672 | hex.add .HLEN, .regs.pc, .rs1 673 | stl.fret .ret 674 | } 675 | 676 | def fast_mul @ sign_extend_rs1, sign_extend_rs2, check_if_rs2_sign_extended, end_upper_mul\ 677 | < .ret, .rd, .rs1, .rs2 > do_mul, do_mulh, do_mulhu, do_mulhsu { 678 | do_mul: 679 | hex.mul .HLEN*2, .rd, .rs1, .rs2 680 | stl.fret .ret 681 | 682 | do_mulh: 683 | hex.sign .HLEN, .rs2, sign_extend_rs2, .do_mulhsu 684 | sign_extend_rs2: 685 | hex.not .HLEN, .rs2+.HLEN*dw // sign extend to 64bits is like treating rs2 as signed. 686 | ;.do_mulhsu 687 | 688 | do_mulhsu: 689 | hex.sign .HLEN, .rs1, sign_extend_rs1, .do_mulhu 690 | sign_extend_rs1: 691 | hex.not .HLEN, .rs1+.HLEN*dw // sign extend to 64bits is like treating rs1 as signed. 692 | ;.do_mulhu 693 | 694 | do_mulhu: 695 | hex.mul .HLEN*2, .rd - .HLEN*dw, .rs1, .rs2 696 | 697 | hex.if0 .rs1+.HLEN*dw, check_if_rs2_sign_extended // if rs1 wasn't sign extended, skip 698 | hex.not .HLEN, .rs1+.HLEN*dw 699 | 700 | check_if_rs2_sign_extended: 701 | hex.if0 .rs2+.HLEN*dw, end_upper_mul // if rs2 wasn't sign extended, skip 702 | hex.not .HLEN, .rs2+.HLEN*dw 703 | 704 | end_upper_mul: 705 | stl.fret .ret 706 | } 707 | 708 | // Divides .rs1 by .rs2, stores the result in .rd and the reminder in .rs1. 709 | // If divides by 0 - print error message and exit. 710 | def fast_div_rem @ div0_exit_with_error, rem_result, return\ 711 | < .ret, .rd, .rs1, .rs2, .regs.pc\ 712 | > do_unsigned_div_rem, do_signed_div_rem { 713 | div0_exit_with_error: 714 | stl.output "\nMath error: Tried to divide " 715 | hex.print_uint .HLEN, .rs1, 1, 1 716 | stl.output " by 0.\n" 717 | stl.output " Current Address = " 718 | hex.print_uint .HLEN, .regs.pc, 1, 1 719 | stl.output ".\n Finished with error.\n\n" 720 | stl.loop 721 | 722 | rem_result: 723 | hex.vec .HLEN 724 | 725 | do_unsigned_div_rem: 726 | hex.div .HLEN, .HLEN, .rd, rem_result, .rs1, .rs2, div0_exit_with_error 727 | ;return 728 | 729 | do_signed_div_rem: 730 | hex.idiv .HLEN, .HLEN, .rd, rem_result, .rs1, .rs2, div0_exit_with_error, 1 731 | ;return 732 | 733 | return: 734 | hex.mov .HLEN, .rs1, rem_result 735 | stl.fret .ret 736 | } 737 | } 738 | 739 | 740 | 741 | // Move to/from register fcalls 742 | 743 | ns riscv { 744 | def moves_to_from_middle_regs\ 745 | < .ret, .rs1, .rs2, .rd, .regs.pc,\ 746 | .regs.x1, .regs.x2, .regs.x3, .regs.x4, .regs.x5, .regs.x6, .regs.x7, .regs.x8, .regs.x9, .regs.x10, .regs.x11,\ 747 | .regs.x12, .regs.x13, .regs.x14, .regs.x15, .regs.x16, .regs.x17, .regs.x18, .regs.x19, .regs.x20, .regs.x21,\ 748 | .regs.x22, .regs.x23, .regs.x24, .regs.x25, .regs.x26, .regs.x27, .regs.x28, .regs.x29, .regs.x30, .regs.x31\ 749 | > zero_rs2, zero_pc,\ 750 | xor_x0_to_rs2, xor_x1_to_rs2, xor_x2_to_rs2, xor_x3_to_rs2, xor_x4_to_rs2, xor_x5_to_rs2, xor_x6_to_rs2,\ 751 | xor_x7_to_rs2, xor_x8_to_rs2, xor_x9_to_rs2, xor_x10_to_rs2, xor_x11_to_rs2, xor_x12_to_rs2, xor_x13_to_rs2,\ 752 | xor_x14_to_rs2, xor_x15_to_rs2, xor_x16_to_rs2, xor_x17_to_rs2, xor_x18_to_rs2, xor_x19_to_rs2, xor_x20_to_rs2,\ 753 | xor_x21_to_rs2, xor_x22_to_rs2, xor_x23_to_rs2, xor_x24_to_rs2, xor_x25_to_rs2, xor_x26_to_rs2, xor_x27_to_rs2,\ 754 | xor_x28_to_rs2, xor_x29_to_rs2, xor_x30_to_rs2, xor_x31_to_rs2,\ 755 | mov_x0_to_rs1, mov_x1_to_rs1, mov_x2_to_rs1, mov_x3_to_rs1, mov_x4_to_rs1, mov_x5_to_rs1, mov_x6_to_rs1,\ 756 | mov_x7_to_rs1, mov_x8_to_rs1, mov_x9_to_rs1, mov_x10_to_rs1, mov_x11_to_rs1, mov_x12_to_rs1, mov_x13_to_rs1,\ 757 | mov_x14_to_rs1, mov_x15_to_rs1, mov_x16_to_rs1, mov_x17_to_rs1, mov_x18_to_rs1, mov_x19_to_rs1, mov_x20_to_rs1,\ 758 | mov_x21_to_rs1, mov_x22_to_rs1, mov_x23_to_rs1, mov_x24_to_rs1, mov_x25_to_rs1, mov_x26_to_rs1, mov_x27_to_rs1,\ 759 | mov_x28_to_rs1, mov_x29_to_rs1, mov_x30_to_rs1, mov_x31_to_rs1,\ 760 | mov_rd_to_x0, mov_rd_to_x1, mov_rd_to_x2, mov_rd_to_x3, mov_rd_to_x4, mov_rd_to_x5, mov_rd_to_x6, mov_rd_to_x7,\ 761 | mov_rd_to_x8, mov_rd_to_x9, mov_rd_to_x10, mov_rd_to_x11, mov_rd_to_x12, mov_rd_to_x13, mov_rd_to_x14,\ 762 | mov_rd_to_x15, mov_rd_to_x16, mov_rd_to_x17, mov_rd_to_x18, mov_rd_to_x19, mov_rd_to_x20, mov_rd_to_x21,\ 763 | mov_rd_to_x22, mov_rd_to_x23, mov_rd_to_x24, mov_rd_to_x25, mov_rd_to_x26, mov_rd_to_x27, mov_rd_to_x28,\ 764 | mov_rd_to_x29, mov_rd_to_x30, mov_rd_to_x31,\ 765 | mov_rs1_to_x0, mov_rs1_to_x1, mov_rs1_to_x2, mov_rs1_to_x3, mov_rs1_to_x4, mov_rs1_to_x5, mov_rs1_to_x6,\ 766 | mov_rs1_to_x7, mov_rs1_to_x8, mov_rs1_to_x9, mov_rs1_to_x10, mov_rs1_to_x11, mov_rs1_to_x12, mov_rs1_to_x13,\ 767 | mov_rs1_to_x14, mov_rs1_to_x15, mov_rs1_to_x16, mov_rs1_to_x17, mov_rs1_to_x18, mov_rs1_to_x19, mov_rs1_to_x20,\ 768 | mov_rs1_to_x21, mov_rs1_to_x22, mov_rs1_to_x23, mov_rs1_to_x24, mov_rs1_to_x25, mov_rs1_to_x26, mov_rs1_to_x27,\ 769 | mov_rs1_to_x28, mov_rs1_to_x29, mov_rs1_to_x30, mov_rs1_to_x31,\ 770 | zero_x0, zero_x1, zero_x2, zero_x3, zero_x4, zero_x5, zero_x6, zero_x7, zero_x8, zero_x9, zero_x10, zero_x11,\ 771 | zero_x12, zero_x13, zero_x14, zero_x15, zero_x16, zero_x17, zero_x18, zero_x19, zero_x20, zero_x21, zero_x22,\ 772 | zero_x23, zero_x24, zero_x25, zero_x26, zero_x27, zero_x28, zero_x29, zero_x30, zero_x31 { 773 | zero_rs2: 774 | hex.zero .HLEN, .rs2 775 | stl.fret .ret 776 | 777 | zero_pc: 778 | hex.zero .HLEN, .regs.pc 779 | stl.fret .ret 780 | 781 | xor_x0_to_rs2: 782 | stl.fret .ret 783 | xor_x1_to_rs2: 784 | hex.xor .HLEN, .rs2, .regs.x1 785 | stl.fret .ret 786 | xor_x2_to_rs2: 787 | hex.xor .HLEN, .rs2, .regs.x2 788 | stl.fret .ret 789 | xor_x3_to_rs2: 790 | hex.xor .HLEN, .rs2, .regs.x3 791 | stl.fret .ret 792 | xor_x4_to_rs2: 793 | hex.xor .HLEN, .rs2, .regs.x4 794 | stl.fret .ret 795 | xor_x5_to_rs2: 796 | hex.xor .HLEN, .rs2, .regs.x5 797 | stl.fret .ret 798 | xor_x6_to_rs2: 799 | hex.xor .HLEN, .rs2, .regs.x6 800 | stl.fret .ret 801 | xor_x7_to_rs2: 802 | hex.xor .HLEN, .rs2, .regs.x7 803 | stl.fret .ret 804 | xor_x8_to_rs2: 805 | hex.xor .HLEN, .rs2, .regs.x8 806 | stl.fret .ret 807 | xor_x9_to_rs2: 808 | hex.xor .HLEN, .rs2, .regs.x9 809 | stl.fret .ret 810 | xor_x10_to_rs2: 811 | hex.xor .HLEN, .rs2, .regs.x10 812 | stl.fret .ret 813 | xor_x11_to_rs2: 814 | hex.xor .HLEN, .rs2, .regs.x11 815 | stl.fret .ret 816 | xor_x12_to_rs2: 817 | hex.xor .HLEN, .rs2, .regs.x12 818 | stl.fret .ret 819 | xor_x13_to_rs2: 820 | hex.xor .HLEN, .rs2, .regs.x13 821 | stl.fret .ret 822 | xor_x14_to_rs2: 823 | hex.xor .HLEN, .rs2, .regs.x14 824 | stl.fret .ret 825 | xor_x15_to_rs2: 826 | hex.xor .HLEN, .rs2, .regs.x15 827 | stl.fret .ret 828 | xor_x16_to_rs2: 829 | hex.xor .HLEN, .rs2, .regs.x16 830 | stl.fret .ret 831 | xor_x17_to_rs2: 832 | hex.xor .HLEN, .rs2, .regs.x17 833 | stl.fret .ret 834 | xor_x18_to_rs2: 835 | hex.xor .HLEN, .rs2, .regs.x18 836 | stl.fret .ret 837 | xor_x19_to_rs2: 838 | hex.xor .HLEN, .rs2, .regs.x19 839 | stl.fret .ret 840 | xor_x20_to_rs2: 841 | hex.xor .HLEN, .rs2, .regs.x20 842 | stl.fret .ret 843 | xor_x21_to_rs2: 844 | hex.xor .HLEN, .rs2, .regs.x21 845 | stl.fret .ret 846 | xor_x22_to_rs2: 847 | hex.xor .HLEN, .rs2, .regs.x22 848 | stl.fret .ret 849 | xor_x23_to_rs2: 850 | hex.xor .HLEN, .rs2, .regs.x23 851 | stl.fret .ret 852 | xor_x24_to_rs2: 853 | hex.xor .HLEN, .rs2, .regs.x24 854 | stl.fret .ret 855 | xor_x25_to_rs2: 856 | hex.xor .HLEN, .rs2, .regs.x25 857 | stl.fret .ret 858 | xor_x26_to_rs2: 859 | hex.xor .HLEN, .rs2, .regs.x26 860 | stl.fret .ret 861 | xor_x27_to_rs2: 862 | hex.xor .HLEN, .rs2, .regs.x27 863 | stl.fret .ret 864 | xor_x28_to_rs2: 865 | hex.xor .HLEN, .rs2, .regs.x28 866 | stl.fret .ret 867 | xor_x29_to_rs2: 868 | hex.xor .HLEN, .rs2, .regs.x29 869 | stl.fret .ret 870 | xor_x30_to_rs2: 871 | hex.xor .HLEN, .rs2, .regs.x30 872 | stl.fret .ret 873 | xor_x31_to_rs2: 874 | hex.xor .HLEN, .rs2, .regs.x31 875 | stl.fret .ret 876 | 877 | mov_x0_to_rs1: 878 | hex.zero .HLEN, .rs1 879 | stl.fret .ret 880 | mov_x1_to_rs1: 881 | hex.mov .HLEN, .rs1, .regs.x1 882 | stl.fret .ret 883 | mov_x2_to_rs1: 884 | hex.mov .HLEN, .rs1, .regs.x2 885 | stl.fret .ret 886 | mov_x3_to_rs1: 887 | hex.mov .HLEN, .rs1, .regs.x3 888 | stl.fret .ret 889 | mov_x4_to_rs1: 890 | hex.mov .HLEN, .rs1, .regs.x4 891 | stl.fret .ret 892 | mov_x5_to_rs1: 893 | hex.mov .HLEN, .rs1, .regs.x5 894 | stl.fret .ret 895 | mov_x6_to_rs1: 896 | hex.mov .HLEN, .rs1, .regs.x6 897 | stl.fret .ret 898 | mov_x7_to_rs1: 899 | hex.mov .HLEN, .rs1, .regs.x7 900 | stl.fret .ret 901 | mov_x8_to_rs1: 902 | hex.mov .HLEN, .rs1, .regs.x8 903 | stl.fret .ret 904 | mov_x9_to_rs1: 905 | hex.mov .HLEN, .rs1, .regs.x9 906 | stl.fret .ret 907 | mov_x10_to_rs1: 908 | hex.mov .HLEN, .rs1, .regs.x10 909 | stl.fret .ret 910 | mov_x11_to_rs1: 911 | hex.mov .HLEN, .rs1, .regs.x11 912 | stl.fret .ret 913 | mov_x12_to_rs1: 914 | hex.mov .HLEN, .rs1, .regs.x12 915 | stl.fret .ret 916 | mov_x13_to_rs1: 917 | hex.mov .HLEN, .rs1, .regs.x13 918 | stl.fret .ret 919 | mov_x14_to_rs1: 920 | hex.mov .HLEN, .rs1, .regs.x14 921 | stl.fret .ret 922 | mov_x15_to_rs1: 923 | hex.mov .HLEN, .rs1, .regs.x15 924 | stl.fret .ret 925 | mov_x16_to_rs1: 926 | hex.mov .HLEN, .rs1, .regs.x16 927 | stl.fret .ret 928 | mov_x17_to_rs1: 929 | hex.mov .HLEN, .rs1, .regs.x17 930 | stl.fret .ret 931 | mov_x18_to_rs1: 932 | hex.mov .HLEN, .rs1, .regs.x18 933 | stl.fret .ret 934 | mov_x19_to_rs1: 935 | hex.mov .HLEN, .rs1, .regs.x19 936 | stl.fret .ret 937 | mov_x20_to_rs1: 938 | hex.mov .HLEN, .rs1, .regs.x20 939 | stl.fret .ret 940 | mov_x21_to_rs1: 941 | hex.mov .HLEN, .rs1, .regs.x21 942 | stl.fret .ret 943 | mov_x22_to_rs1: 944 | hex.mov .HLEN, .rs1, .regs.x22 945 | stl.fret .ret 946 | mov_x23_to_rs1: 947 | hex.mov .HLEN, .rs1, .regs.x23 948 | stl.fret .ret 949 | mov_x24_to_rs1: 950 | hex.mov .HLEN, .rs1, .regs.x24 951 | stl.fret .ret 952 | mov_x25_to_rs1: 953 | hex.mov .HLEN, .rs1, .regs.x25 954 | stl.fret .ret 955 | mov_x26_to_rs1: 956 | hex.mov .HLEN, .rs1, .regs.x26 957 | stl.fret .ret 958 | mov_x27_to_rs1: 959 | hex.mov .HLEN, .rs1, .regs.x27 960 | stl.fret .ret 961 | mov_x28_to_rs1: 962 | hex.mov .HLEN, .rs1, .regs.x28 963 | stl.fret .ret 964 | mov_x29_to_rs1: 965 | hex.mov .HLEN, .rs1, .regs.x29 966 | stl.fret .ret 967 | mov_x30_to_rs1: 968 | hex.mov .HLEN, .rs1, .regs.x30 969 | stl.fret .ret 970 | mov_x31_to_rs1: 971 | hex.mov .HLEN, .rs1, .regs.x31 972 | stl.fret .ret 973 | 974 | mov_rd_to_x0: 975 | stl.fret .ret 976 | mov_rd_to_x1: 977 | hex.mov .HLEN, .regs.x1, .rd 978 | stl.fret .ret 979 | mov_rd_to_x2: 980 | hex.mov .HLEN, .regs.x2, .rd 981 | stl.fret .ret 982 | mov_rd_to_x3: 983 | hex.mov .HLEN, .regs.x3, .rd 984 | stl.fret .ret 985 | mov_rd_to_x4: 986 | hex.mov .HLEN, .regs.x4, .rd 987 | stl.fret .ret 988 | mov_rd_to_x5: 989 | hex.mov .HLEN, .regs.x5, .rd 990 | stl.fret .ret 991 | mov_rd_to_x6: 992 | hex.mov .HLEN, .regs.x6, .rd 993 | stl.fret .ret 994 | mov_rd_to_x7: 995 | hex.mov .HLEN, .regs.x7, .rd 996 | stl.fret .ret 997 | mov_rd_to_x8: 998 | hex.mov .HLEN, .regs.x8, .rd 999 | stl.fret .ret 1000 | mov_rd_to_x9: 1001 | hex.mov .HLEN, .regs.x9, .rd 1002 | stl.fret .ret 1003 | mov_rd_to_x10: 1004 | hex.mov .HLEN, .regs.x10, .rd 1005 | stl.fret .ret 1006 | mov_rd_to_x11: 1007 | hex.mov .HLEN, .regs.x11, .rd 1008 | stl.fret .ret 1009 | mov_rd_to_x12: 1010 | hex.mov .HLEN, .regs.x12, .rd 1011 | stl.fret .ret 1012 | mov_rd_to_x13: 1013 | hex.mov .HLEN, .regs.x13, .rd 1014 | stl.fret .ret 1015 | mov_rd_to_x14: 1016 | hex.mov .HLEN, .regs.x14, .rd 1017 | stl.fret .ret 1018 | mov_rd_to_x15: 1019 | hex.mov .HLEN, .regs.x15, .rd 1020 | stl.fret .ret 1021 | mov_rd_to_x16: 1022 | hex.mov .HLEN, .regs.x16, .rd 1023 | stl.fret .ret 1024 | mov_rd_to_x17: 1025 | hex.mov .HLEN, .regs.x17, .rd 1026 | stl.fret .ret 1027 | mov_rd_to_x18: 1028 | hex.mov .HLEN, .regs.x18, .rd 1029 | stl.fret .ret 1030 | mov_rd_to_x19: 1031 | hex.mov .HLEN, .regs.x19, .rd 1032 | stl.fret .ret 1033 | mov_rd_to_x20: 1034 | hex.mov .HLEN, .regs.x20, .rd 1035 | stl.fret .ret 1036 | mov_rd_to_x21: 1037 | hex.mov .HLEN, .regs.x21, .rd 1038 | stl.fret .ret 1039 | mov_rd_to_x22: 1040 | hex.mov .HLEN, .regs.x22, .rd 1041 | stl.fret .ret 1042 | mov_rd_to_x23: 1043 | hex.mov .HLEN, .regs.x23, .rd 1044 | stl.fret .ret 1045 | mov_rd_to_x24: 1046 | hex.mov .HLEN, .regs.x24, .rd 1047 | stl.fret .ret 1048 | mov_rd_to_x25: 1049 | hex.mov .HLEN, .regs.x25, .rd 1050 | stl.fret .ret 1051 | mov_rd_to_x26: 1052 | hex.mov .HLEN, .regs.x26, .rd 1053 | stl.fret .ret 1054 | mov_rd_to_x27: 1055 | hex.mov .HLEN, .regs.x27, .rd 1056 | stl.fret .ret 1057 | mov_rd_to_x28: 1058 | hex.mov .HLEN, .regs.x28, .rd 1059 | stl.fret .ret 1060 | mov_rd_to_x29: 1061 | hex.mov .HLEN, .regs.x29, .rd 1062 | stl.fret .ret 1063 | mov_rd_to_x30: 1064 | hex.mov .HLEN, .regs.x30, .rd 1065 | stl.fret .ret 1066 | mov_rd_to_x31: 1067 | hex.mov .HLEN, .regs.x31, .rd 1068 | stl.fret .ret 1069 | 1070 | mov_rs1_to_x0: 1071 | stl.fret .ret 1072 | mov_rs1_to_x1: 1073 | hex.mov .HLEN, .regs.x1, .rs1 1074 | stl.fret .ret 1075 | mov_rs1_to_x2: 1076 | hex.mov .HLEN, .regs.x2, .rs1 1077 | stl.fret .ret 1078 | mov_rs1_to_x3: 1079 | hex.mov .HLEN, .regs.x3, .rs1 1080 | stl.fret .ret 1081 | mov_rs1_to_x4: 1082 | hex.mov .HLEN, .regs.x4, .rs1 1083 | stl.fret .ret 1084 | mov_rs1_to_x5: 1085 | hex.mov .HLEN, .regs.x5, .rs1 1086 | stl.fret .ret 1087 | mov_rs1_to_x6: 1088 | hex.mov .HLEN, .regs.x6, .rs1 1089 | stl.fret .ret 1090 | mov_rs1_to_x7: 1091 | hex.mov .HLEN, .regs.x7, .rs1 1092 | stl.fret .ret 1093 | mov_rs1_to_x8: 1094 | hex.mov .HLEN, .regs.x8, .rs1 1095 | stl.fret .ret 1096 | mov_rs1_to_x9: 1097 | hex.mov .HLEN, .regs.x9, .rs1 1098 | stl.fret .ret 1099 | mov_rs1_to_x10: 1100 | hex.mov .HLEN, .regs.x10, .rs1 1101 | stl.fret .ret 1102 | mov_rs1_to_x11: 1103 | hex.mov .HLEN, .regs.x11, .rs1 1104 | stl.fret .ret 1105 | mov_rs1_to_x12: 1106 | hex.mov .HLEN, .regs.x12, .rs1 1107 | stl.fret .ret 1108 | mov_rs1_to_x13: 1109 | hex.mov .HLEN, .regs.x13, .rs1 1110 | stl.fret .ret 1111 | mov_rs1_to_x14: 1112 | hex.mov .HLEN, .regs.x14, .rs1 1113 | stl.fret .ret 1114 | mov_rs1_to_x15: 1115 | hex.mov .HLEN, .regs.x15, .rs1 1116 | stl.fret .ret 1117 | mov_rs1_to_x16: 1118 | hex.mov .HLEN, .regs.x16, .rs1 1119 | stl.fret .ret 1120 | mov_rs1_to_x17: 1121 | hex.mov .HLEN, .regs.x17, .rs1 1122 | stl.fret .ret 1123 | mov_rs1_to_x18: 1124 | hex.mov .HLEN, .regs.x18, .rs1 1125 | stl.fret .ret 1126 | mov_rs1_to_x19: 1127 | hex.mov .HLEN, .regs.x19, .rs1 1128 | stl.fret .ret 1129 | mov_rs1_to_x20: 1130 | hex.mov .HLEN, .regs.x20, .rs1 1131 | stl.fret .ret 1132 | mov_rs1_to_x21: 1133 | hex.mov .HLEN, .regs.x21, .rs1 1134 | stl.fret .ret 1135 | mov_rs1_to_x22: 1136 | hex.mov .HLEN, .regs.x22, .rs1 1137 | stl.fret .ret 1138 | mov_rs1_to_x23: 1139 | hex.mov .HLEN, .regs.x23, .rs1 1140 | stl.fret .ret 1141 | mov_rs1_to_x24: 1142 | hex.mov .HLEN, .regs.x24, .rs1 1143 | stl.fret .ret 1144 | mov_rs1_to_x25: 1145 | hex.mov .HLEN, .regs.x25, .rs1 1146 | stl.fret .ret 1147 | mov_rs1_to_x26: 1148 | hex.mov .HLEN, .regs.x26, .rs1 1149 | stl.fret .ret 1150 | mov_rs1_to_x27: 1151 | hex.mov .HLEN, .regs.x27, .rs1 1152 | stl.fret .ret 1153 | mov_rs1_to_x28: 1154 | hex.mov .HLEN, .regs.x28, .rs1 1155 | stl.fret .ret 1156 | mov_rs1_to_x29: 1157 | hex.mov .HLEN, .regs.x29, .rs1 1158 | stl.fret .ret 1159 | mov_rs1_to_x30: 1160 | hex.mov .HLEN, .regs.x30, .rs1 1161 | stl.fret .ret 1162 | mov_rs1_to_x31: 1163 | hex.mov .HLEN, .regs.x31, .rs1 1164 | stl.fret .ret 1165 | 1166 | zero_x0: 1167 | stl.fret .ret 1168 | zero_x1: 1169 | hex.zero .HLEN, .regs.x1 1170 | stl.fret .ret 1171 | zero_x2: 1172 | hex.zero .HLEN, .regs.x2 1173 | stl.fret .ret 1174 | zero_x3: 1175 | hex.zero .HLEN, .regs.x3 1176 | stl.fret .ret 1177 | zero_x4: 1178 | hex.zero .HLEN, .regs.x4 1179 | stl.fret .ret 1180 | zero_x5: 1181 | hex.zero .HLEN, .regs.x5 1182 | stl.fret .ret 1183 | zero_x6: 1184 | hex.zero .HLEN, .regs.x6 1185 | stl.fret .ret 1186 | zero_x7: 1187 | hex.zero .HLEN, .regs.x7 1188 | stl.fret .ret 1189 | zero_x8: 1190 | hex.zero .HLEN, .regs.x8 1191 | stl.fret .ret 1192 | zero_x9: 1193 | hex.zero .HLEN, .regs.x9 1194 | stl.fret .ret 1195 | zero_x10: 1196 | hex.zero .HLEN, .regs.x10 1197 | stl.fret .ret 1198 | zero_x11: 1199 | hex.zero .HLEN, .regs.x11 1200 | stl.fret .ret 1201 | zero_x12: 1202 | hex.zero .HLEN, .regs.x12 1203 | stl.fret .ret 1204 | zero_x13: 1205 | hex.zero .HLEN, .regs.x13 1206 | stl.fret .ret 1207 | zero_x14: 1208 | hex.zero .HLEN, .regs.x14 1209 | stl.fret .ret 1210 | zero_x15: 1211 | hex.zero .HLEN, .regs.x15 1212 | stl.fret .ret 1213 | zero_x16: 1214 | hex.zero .HLEN, .regs.x16 1215 | stl.fret .ret 1216 | zero_x17: 1217 | hex.zero .HLEN, .regs.x17 1218 | stl.fret .ret 1219 | zero_x18: 1220 | hex.zero .HLEN, .regs.x18 1221 | stl.fret .ret 1222 | zero_x19: 1223 | hex.zero .HLEN, .regs.x19 1224 | stl.fret .ret 1225 | zero_x20: 1226 | hex.zero .HLEN, .regs.x20 1227 | stl.fret .ret 1228 | zero_x21: 1229 | hex.zero .HLEN, .regs.x21 1230 | stl.fret .ret 1231 | zero_x22: 1232 | hex.zero .HLEN, .regs.x22 1233 | stl.fret .ret 1234 | zero_x23: 1235 | hex.zero .HLEN, .regs.x23 1236 | stl.fret .ret 1237 | zero_x24: 1238 | hex.zero .HLEN, .regs.x24 1239 | stl.fret .ret 1240 | zero_x25: 1241 | hex.zero .HLEN, .regs.x25 1242 | stl.fret .ret 1243 | zero_x26: 1244 | hex.zero .HLEN, .regs.x26 1245 | stl.fret .ret 1246 | zero_x27: 1247 | hex.zero .HLEN, .regs.x27 1248 | stl.fret .ret 1249 | zero_x28: 1250 | hex.zero .HLEN, .regs.x28 1251 | stl.fret .ret 1252 | zero_x29: 1253 | hex.zero .HLEN, .regs.x29 1254 | stl.fret .ret 1255 | zero_x30: 1256 | hex.zero .HLEN, .regs.x30 1257 | stl.fret .ret 1258 | zero_x31: 1259 | hex.zero .HLEN, .regs.x31 1260 | stl.fret .ret 1261 | } 1262 | 1263 | // Sets rs1,rs2 according to the given "fcall_labels", fcalls "do_op", then moves the result to the appropriate dst reg. 1264 | def reg_reg_fast_op mov_from_dest, mov_to_rs1, xor_to_rs2, do_op @ table, end < .ret, .zero_rs2 { 1265 | wflip .ret+w, table+dw, .ret 1266 | 1267 | pad 16 1268 | table: 1269 | .ret+dbit+2; do_op // 4th 1270 | .ret+dbit+1; mov_to_rs1 // 1st 1271 | .ret+dbit+1; xor_to_rs2 // 3rd 1272 | .ret+dbit+0; .zero_rs2 // 2nd 1273 | .ret+dbit+0; mov_from_dest // 5th 1274 | wflip .ret+w, table+5*dw, end // 6th 1275 | 1276 | end: 1277 | } 1278 | 1279 | // Sets rs1 according to the given "fcall_label", rs2 to the given imm, 1280 | // fcalls "do_op", then moves the result to the appropriate dst reg. 1281 | def reg_imm_fast_op mov_from_dest, mov_to_rs1, imm, do_op @ table, xor_imm_to_rs2, end < .ret, .zero_rs2, .rs2 { 1282 | wflip .ret+w, table+dw, .ret 1283 | 1284 | pad 16 1285 | table: 1286 | .ret+dbit+2; do_op // 4th 1287 | .ret+dbit+1; mov_to_rs1 // 1st 1288 | .ret+dbit+1; xor_imm_to_rs2 // 3rd 1289 | .ret+dbit+0; .zero_rs2 // 2nd 1290 | .ret+dbit+0; mov_from_dest // 5th 1291 | wflip .ret+w, table+5*dw, end // 6th 1292 | 1293 | xor_imm_to_rs2: 1294 | .__xor_by_hex_const .HLEN, .rs2, imm 1295 | stl.fret .ret 1296 | 1297 | end: 1298 | } 1299 | 1300 | 1301 | // Sets rs1 according to the given "fcall_label", rs2 to the given imm, 1302 | // fcalls "calculate_mem_ptr", then sets rs2 according to the given "fcall_label", and fcalls "do_write". 1303 | def fast_write_op mov_to_rs1, xor_to_rs2, imm, do_write @ table, xor_imm_to_rs2, end\ 1304 | < .ret, .zero_rs2, .rs2, .calculate_mem_ptr { 1305 | wflip .ret+w, table+dw, .ret 1306 | 1307 | pad 16 1308 | table: 1309 | .ret+dbit+2; .calculate_mem_ptr // 4th 1310 | .ret+dbit+1; mov_to_rs1 // 1st 1311 | .ret+dbit+1; xor_imm_to_rs2 // 3rd 1312 | .ret+dbit+0; .zero_rs2 // 2nd 1313 | .ret+dbit+1; xor_imm_to_rs2 // 5th 1314 | wflip .ret+w, table+5*dw, end // 8th 1315 | .ret+dbit+0; xor_to_rs2 // 6th 1316 | .ret+dbit+1; do_write // 7th 1317 | 1318 | xor_imm_to_rs2: 1319 | .__xor_by_hex_const .HLEN, .rs2, imm 1320 | stl.fret .ret 1321 | 1322 | end: 1323 | } 1324 | 1325 | // Sets rs1 according to the given "fcall_label", rs2 to the given imm, 1326 | // fcalls "calculate_mem_ptr", then sets rs2 according to the given "fcall_label", and fcalls "do_write". 1327 | def fast_read_op mov_from_dest, mov_to_rs1, imm, do_read @ table, xor_imm_to_rs2, end\ 1328 | < .ret, .zero_rs2, .rs2, .calculate_mem_ptr { 1329 | wflip .ret+w, table+dw, .ret 1330 | 1331 | pad 16 1332 | table: 1333 | .ret+dbit+2; .calculate_mem_ptr // 4th 1334 | .ret+dbit+1; mov_to_rs1 // 1st 1335 | .ret+dbit+1; xor_imm_to_rs2 // 3rd 1336 | .ret+dbit+0; .zero_rs2 // 2nd 1337 | .ret+dbit+1; do_read // 5th 1338 | ; // spaced 1339 | .ret+dbit+0; mov_from_dest // 6th 1340 | wflip .ret+w, table+7*dw, end // 7th 1341 | 1342 | xor_imm_to_rs2: 1343 | .__xor_by_hex_const .HLEN, .rs2, imm 1344 | stl.fret .ret 1345 | 1346 | end: 1347 | } 1348 | 1349 | 1350 | // Sets rs1,rs2 according to the given "fcall_labels", fcalls "do_cmp", then set{+jump} the new pc. 1351 | // Jumps to "pc+imm" if the "do_cmp" flipped ".ret+dbit", else jumps to "pc+4". 1352 | def branch_fast_op mov_to_rs1, xor_to_rs2, do_cmp, addr, imm @ table, dont_jump, take_jump < .ret, .zero_rs2 { 1353 | wflip .ret+w, table+dw, .ret 1354 | 1355 | pad 16 1356 | table: 1357 | .ret+dbit+2; do_cmp // 4th 1358 | .ret+dbit+1; mov_to_rs1 // 1st 1359 | .ret+dbit+1; xor_to_rs2 // 3rd 1360 | .ret+dbit+0; .zero_rs2 // 2nd 1361 | wflip .ret+w, table+4*dw, dont_jump // 5th if "do_cmp" didn't flip ".ret+dbit" 1362 | wflip .ret+w, table+5*dw, take_jump // 5th if "do_cmp" did flip ".ret+dbit" 1363 | 1364 | take_jump: 1365 | .jump_to_constant_address addr, imm + addr 1366 | 1367 | dont_jump: 1368 | .inc_pc addr 1369 | } 1370 | 1371 | // Resets register rd using "fcall zero_rd", then set rd value to imm. 1372 | def set_register zero_rd, rd, imm < .ret, .regs.zero { 1373 | stl.fcall zero_rd, .ret 1374 | rep(rd != .regs.zero, _) .__xor_by_hex_const .HLEN, rd, imm 1375 | } 1376 | 1377 | 1378 | // Sets rs1 according to the given "fcall_label", .regs.pc to the given imm, then fcalls ".do_add_to_pc". 1379 | def jalr_fast_op mov_to_rs1, imm @ table, xor_imm_to_pc < .ret, .zero_pc, .regs.pc, .do_add_to_pc, .jump_to_pc { 1380 | wflip .ret+w, table+dw, .ret 1381 | 1382 | pad 16 1383 | table: 1384 | .ret+dbit+2; .do_add_to_pc // 4th 1385 | .ret+dbit+1; mov_to_rs1 // 1st 1386 | .ret+dbit+1; xor_imm_to_pc // 3rd 1387 | .ret+dbit+0; .zero_pc // 2nd 1388 | wflip .ret+w, table+4*dw, .jump_to_pc // 5th 1389 | 1390 | xor_imm_to_pc: 1391 | .__xor_by_hex_const .HLEN, .regs.pc, imm 1392 | stl.fret .ret 1393 | } 1394 | 1395 | } 1396 | 1397 | 1398 | // ---------- opcodes 1399 | 1400 | ns riscv { 1401 | def jal zero_rd, rd, imm, current_address { 1402 | .set_register zero_rd, rd, current_address + 4 1403 | .jump_to_constant_address current_address, current_address + imm 1404 | } 1405 | 1406 | def jalr zero_rd, rd, mov_to_rs1, imm, current_address { 1407 | .set_register zero_rd, rd, current_address + 4 1408 | .jalr_fast_op mov_to_rs1, imm 1409 | } 1410 | 1411 | def lui zero_rd, rd, imm { 1412 | .set_register zero_rd, rd, imm 1413 | } 1414 | 1415 | def auipc zero_rd, rd, imm, addr { 1416 | .set_register zero_rd, rd, imm + addr 1417 | } 1418 | 1419 | def beq mov_to_rs1, xor_to_rs2, imm, addr < .do_beq { 1420 | .branch_fast_op mov_to_rs1, xor_to_rs2, .do_beq, addr, imm 1421 | } 1422 | def bne mov_to_rs1, xor_to_rs2, imm, addr < .do_bne { 1423 | .branch_fast_op mov_to_rs1, xor_to_rs2, .do_bne, addr, imm 1424 | } 1425 | def bltu mov_to_rs1, xor_to_rs2, imm, addr < .do_bltu { 1426 | .branch_fast_op mov_to_rs1, xor_to_rs2, .do_bltu, addr, imm 1427 | } 1428 | def bgeu mov_to_rs1, xor_to_rs2, imm, addr < .do_bgeu { 1429 | .branch_fast_op mov_to_rs1, xor_to_rs2, .do_bgeu, addr, imm 1430 | } 1431 | def blt mov_to_rs1, xor_to_rs2, imm, addr < .do_blt { 1432 | .branch_fast_op mov_to_rs1, xor_to_rs2, .do_blt, addr, imm 1433 | } 1434 | def bge mov_to_rs1, xor_to_rs2, imm, addr < .do_bge { 1435 | .branch_fast_op mov_to_rs1, xor_to_rs2, .do_bge, addr, imm 1436 | } 1437 | 1438 | def lb mov_from_rd, mov_to_rs1, imm < .do_read1_sign_extended { 1439 | .fast_read_op mov_from_rd, mov_to_rs1, imm, .do_read1_sign_extended 1440 | } 1441 | def lbu mov_from_rd, mov_to_rs1, imm < .do_read1 { 1442 | .fast_read_op mov_from_rd, mov_to_rs1, imm, .do_read1 1443 | } 1444 | def lh mov_from_rd, mov_to_rs1, imm < .do_read2_sign_extended { 1445 | .fast_read_op mov_from_rd, mov_to_rs1, imm, .do_read2_sign_extended 1446 | } 1447 | def lhu mov_from_rd, mov_to_rs1, imm < .do_read2 { 1448 | .fast_read_op mov_from_rd, mov_to_rs1, imm, .do_read2 1449 | } 1450 | def lw mov_from_rd, mov_to_rs1, imm < .do_read4 { 1451 | .fast_read_op mov_from_rd, mov_to_rs1, imm, .do_read4 1452 | } 1453 | 1454 | def sb mov_to_rs1, xor_to_rs2, imm < .do_write1 { 1455 | .fast_write_op mov_to_rs1, xor_to_rs2, imm, .do_write1 1456 | } 1457 | def sh mov_to_rs1, xor_to_rs2, imm < .do_write2 { 1458 | .fast_write_op mov_to_rs1, xor_to_rs2, imm, .do_write2 1459 | } 1460 | def sw mov_to_rs1, xor_to_rs2, imm < .do_write4 { 1461 | .fast_write_op mov_to_rs1, xor_to_rs2, imm, .do_write4 1462 | } 1463 | 1464 | def addi mov_from_rs1, mov_to_rs1, imm < .do_add { 1465 | .reg_imm_fast_op mov_from_rs1, mov_to_rs1, imm, .do_add 1466 | } 1467 | def xori mov_from_rs1, mov_to_rs1, imm < .do_xor { 1468 | .reg_imm_fast_op mov_from_rs1, mov_to_rs1, imm, .do_xor 1469 | } 1470 | def ori mov_from_rs1, mov_to_rs1, imm < .do_or { 1471 | .reg_imm_fast_op mov_from_rs1, mov_to_rs1, imm, .do_or 1472 | } 1473 | def andi mov_from_rs1, mov_to_rs1, imm < .do_and { 1474 | .reg_imm_fast_op mov_from_rs1, mov_to_rs1, imm, .do_and 1475 | } 1476 | 1477 | // Sets rd to 1 if [signed] rs1> (rs2 % 32) [logical shift] 1526 | def srl mov_from_rs1, mov_to_rs1, xor_to_rs2 < .do_srl { 1527 | .reg_reg_fast_op mov_from_rs1, mov_to_rs1, xor_to_rs2, .do_srl 1528 | } 1529 | // rd := rs1 >> (rs2 % 32) [arithmetical shift] 1530 | def sra mov_from_rs1, mov_to_rs1, xor_to_rs2 < .do_sra { 1531 | .reg_reg_fast_op mov_from_rs1, mov_to_rs1, xor_to_rs2, .do_sra 1532 | } 1533 | 1534 | def mul mov_from_rs1, mov_to_rs1, xor_to_rs2 < .do_mul { 1535 | .reg_reg_fast_op mov_from_rs1, mov_to_rs1, xor_to_rs2, .do_mul 1536 | } 1537 | def mulh mov_from_rs1, mov_to_rs1, xor_to_rs2 < .do_mulh { 1538 | .reg_reg_fast_op mov_from_rs1, mov_to_rs1, xor_to_rs2, .do_mulh 1539 | } 1540 | def mulhu mov_from_rs1, mov_to_rs1, xor_to_rs2 < .do_mulhu { 1541 | .reg_reg_fast_op mov_from_rs1, mov_to_rs1, xor_to_rs2, .do_mulhu 1542 | } 1543 | def mulhsu mov_from_rs1, mov_to_rs1, xor_to_rs2 < .do_mulhsu { 1544 | .reg_reg_fast_op mov_from_rs1, mov_to_rs1, xor_to_rs2, .do_mulhsu 1545 | } 1546 | def div mov_from_rd, mov_to_rs1, xor_to_rs2 < .do_signed_div_rem { 1547 | .reg_reg_fast_op mov_from_rd, mov_to_rs1, xor_to_rs2, .do_signed_div_rem 1548 | } 1549 | def divu mov_from_rd, mov_to_rs1, xor_to_rs2 < .do_unsigned_div_rem { 1550 | .reg_reg_fast_op mov_from_rd, mov_to_rs1, xor_to_rs2, .do_unsigned_div_rem 1551 | } 1552 | def rem mov_from_rs1, mov_to_rs1, xor_to_rs2 < .do_signed_div_rem { 1553 | .reg_reg_fast_op mov_from_rs1, mov_to_rs1, xor_to_rs2, .do_signed_div_rem 1554 | } 1555 | def remu mov_from_rs1, mov_to_rs1, xor_to_rs2 < .do_unsigned_div_rem { 1556 | .reg_reg_fast_op mov_from_rs1, mov_to_rs1, xor_to_rs2, .do_unsigned_div_rem 1557 | } 1558 | } 1559 | -------------------------------------------------------------------------------- /c2fj/riscv_instructions.py: -------------------------------------------------------------------------------- 1 | from typing import TextIO 2 | 3 | RV_LUI = 0b0110111 4 | RV_AUIPC = 0b0010111 5 | RV_JAL = 0b1101111 6 | RV_JALR = 0b1100111 7 | 8 | RV_B = 0b1100011 9 | RV_BEQ = 0b000 10 | RV_BNE = 0b001 11 | RV_BLT = 0b100 12 | RV_BGE = 0b101 13 | RV_BLTU = 0b110 14 | RV_BGEU = 0b111 15 | 16 | RV_L = 0b0000011 17 | RV_LB = 0b000 18 | RV_LH = 0b001 19 | RV_LW = 0b010 20 | RV_LBU = 0b100 21 | RV_LHU = 0b101 22 | 23 | RV_S = 0b0100011 24 | RV_SB = 0b000 25 | RV_SH = 0b001 26 | RV_SW = 0b010 27 | 28 | RV_ALU_IMM = 0b0010011 29 | RV_ADDI = 0b000 30 | RV_SLTI = 0b010 31 | RV_SLTIU = 0b011 32 | RV_XORI = 0b100 33 | RV_ORI = 0b110 34 | RV_ANDI = 0b111 35 | RV_SLLI = 0b001 36 | RV_SRI = 0b101 37 | RV_SLLI_FUNCT7 = 0b0000000 38 | RV_SRLI_FUNCT7 = 0b0000000 39 | RV_SRAI_FUNCT7 = 0b0100000 40 | 41 | RV_ALU = 0b0110011 42 | RV_ADD_SUB = 0b000 43 | RV_SLL = 0b001 44 | RV_SLT = 0b010 45 | RV_SLTU = 0b011 46 | RV_XOR = 0b100 47 | RV_SR = 0b101 48 | RV_OR = 0b110 49 | RV_AND = 0b111 50 | RV_ADD_FUNCT7 = 0b0000000 51 | RV_SUB_FUNCT7 = 0b0100000 52 | RV_SLL_FUNCT7 = 0b0000000 53 | RV_SLT_FUNCT7 = 0b0000000 54 | RV_SLTU_FUNCT7 = 0b0000000 55 | RV_XOR_FUNCT7 = 0b0000000 56 | RV_SRL_FUNCT7 = 0b0000000 57 | RV_SRA_FUNCT7 = 0b0100000 58 | RV_OR_FUNCT7 = 0b0000000 59 | RV_AND_FUNCT7 = 0b0000000 60 | 61 | RV32M_FUNCT7 = 0b0000001 62 | RV_MUL = 0b000 63 | RV_MULH = 0b001 64 | RV_MULHSU = 0b010 65 | RV_MULHU = 0b011 66 | RV_DIV = 0b100 67 | RV_DIVU = 0b101 68 | RV_REM = 0b110 69 | RV_REMU = 0b111 70 | 71 | RV_FENCE = 0b0001111 72 | RV_FENCE_FUNCT3 = 0b000 73 | 74 | RV_CALL = 0b1110011 75 | RV_ECALL_FULL_OP = RV_CALL 76 | RV_EBREAK_FULL_OP = (1 << 20) | RV_CALL 77 | 78 | JAL_WRITE_IMMEDIATE = 2 79 | JAL_READ_IMMEDIATE = 6 80 | JAL_EXIT_IMMEDIATE = 10 81 | JAL_SBRK_IMMEDIATE = 14 82 | JAL_DEBUG_REGISTERS_IMMEDIATE = 18 83 | JAL_DEBUG_PRINT_REGISTER_IMMEDIATE = 22 84 | JAL_DEBUG_P_START_IMMEDIATE = 1002 85 | JAL_DEBUG_P_END_IMMEDIATE = 2022 86 | JAL_PRINT_CHAR_START_IMMEDIATE = 3002 87 | JAL_PRINT_CHAR_END_IMMEDIATE = 4022 88 | 89 | global pc_changed 90 | 91 | 92 | class InvalidOpcode(ValueError): 93 | pass 94 | 95 | 96 | def sign_extend(constant: int, bit_width: int) -> int: 97 | if constant & (1 << (bit_width - 1)): 98 | return constant - (1 << bit_width) 99 | return constant 100 | 101 | 102 | def fj_hex(constant: int) -> str: 103 | if constant < 0: 104 | return f'(0 - {hex(abs(constant))})' 105 | return hex(constant) 106 | 107 | 108 | def register_name(register_index: int) -> str: 109 | """ 110 | @param register_index: the 5 lsb will be used. 111 | """ 112 | return f'.regs.x{register_index & 0x1f}' 113 | 114 | 115 | def zero_register(register_index: int) -> str: 116 | """ 117 | @param register_index: the 5 lsb will be used. 118 | """ 119 | return f'.zero_x{register_index & 0x1f}' 120 | 121 | 122 | def mov_to_rs1(register_index: int) -> str: 123 | """ 124 | @param register_index: the 5 lsb will be used. 125 | """ 126 | return f'.mov_x{register_index & 0x1f}_to_rs1' 127 | 128 | 129 | def xor_to_rs2(register_index: int) -> str: 130 | """ 131 | @param register_index: the 5 lsb will be used. 132 | """ 133 | return f'.xor_x{register_index & 0x1f}_to_rs2' 134 | 135 | 136 | def mov_rs1_to(register_index: int) -> str: 137 | """ 138 | @param register_index: the 5 lsb will be used. 139 | """ 140 | return f'.mov_rs1_to_x{register_index & 0x1f}' 141 | 142 | 143 | def mov_rd_to(register_index: int) -> str: 144 | """ 145 | @param register_index: the 5 lsb will be used. 146 | """ 147 | return f'.mov_rd_to_x{register_index & 0x1f}' 148 | 149 | 150 | def get_hex_comment(op: int) -> str: 151 | return f'// op 0x{op:08x}' 152 | 153 | 154 | def r_type(macro_name: str, op: int, dst_is_rs1: bool = True) -> str: 155 | rd = (op >> 7) & 0x1f 156 | rs1 = (op >> 15) & 0x1f 157 | rs2 = (op >> 20) & 0x1f 158 | 159 | mov_to_dst_reg = mov_rs1_to(rd) if dst_is_rs1 else mov_rd_to(rd) 160 | return f' .{macro_name} {mov_to_dst_reg}, {mov_to_rs1(rs1)}, {xor_to_rs2(rs2)}\n' 161 | 162 | 163 | def i_type(macro_name: str, op: int, dst_is_rs1: bool = True) -> str: 164 | imm = sign_extend(op >> 20, 12) 165 | rs1 = (op >> 15) & 0x1f 166 | rd = (op >> 7) & 0x1f 167 | 168 | mov_to_dst_reg = mov_rs1_to(rd) if dst_is_rs1 else mov_rd_to(rd) 169 | 170 | if macro_name in ['addi', 'xori', 'ori', 'andi', 'slti', 'sltiu']: 171 | return f' .{macro_name} {mov_to_dst_reg}, {mov_to_rs1(rs1)}, {fj_hex(imm)}\n' 172 | 173 | return f' .{macro_name} {register_name(rd)}, {register_name(rs1)}, {fj_hex(imm)}\n' 174 | 175 | 176 | def load_type(macro_name: str, op: int) -> str: 177 | imm = sign_extend(op >> 20, 12) 178 | rs1 = (op >> 15) & 0x1f 179 | rd = (op >> 7) & 0x1f 180 | 181 | return f' .{macro_name} {mov_rd_to(rd)}, {mov_to_rs1(rs1)}, {fj_hex(imm)}\n' 182 | 183 | 184 | def shift_imm_op(macro_name: str, op: int) -> str: 185 | shift_const = (op >> 20) & 0x1f 186 | rs1 = (op >> 15) & 0x1f 187 | rd = (op >> 7) & 0x1f 188 | 189 | return f' .{macro_name} {mov_rs1_to(rd)}, {mov_to_rs1(rs1)}, {fj_hex(shift_const)}\n' 190 | 191 | 192 | def jalr_op(op: int, addr: int) -> str: 193 | imm = sign_extend(op >> 20, 12) 194 | rs1 = (op >> 15) & 0x1f 195 | rd = (op >> 7) & 0x1f 196 | 197 | global pc_changed 198 | pc_changed = True 199 | 200 | return f' .jalr {zero_register(rd)}, {register_name(rd)}, {mov_to_rs1(rs1)}, {fj_hex(imm)}, {addr}\n' 201 | 202 | 203 | def s_type(macro_name: str, op: int) -> str: 204 | imm11_5 = op >> 25 205 | imm4_0 = (op >> 7) & 0x1f 206 | imm = (imm11_5 << 5) | (imm4_0 << 0) 207 | imm = sign_extend(imm, 12) 208 | 209 | rs1 = (op >> 15) & 0x1f 210 | rs2 = (op >> 20) & 0x1f 211 | 212 | return f' .{macro_name} {mov_to_rs1(rs1)}, {xor_to_rs2(rs2)}, {fj_hex(imm)}\n' 213 | 214 | 215 | def b_type(macro_name: str, op: int, addr: int) -> str: 216 | imm12 = op >> 31 217 | imm10_5 = (op >> 25) & 0x3f 218 | imm4_1 = (op >> 8) & 0xf 219 | imm11 = (op >> 7) & 0x1 220 | imm = (imm12 << 12) | (imm10_5 << 5) | (imm4_1 << 1) | (imm11 << 11) 221 | imm = sign_extend(imm, 13) 222 | 223 | rs1 = (op >> 15) & 0x1f 224 | rs2 = (op >> 20) & 0x1f 225 | 226 | global pc_changed 227 | pc_changed = True 228 | 229 | return f' .{macro_name} {mov_to_rs1(rs1)}, {xor_to_rs2(rs2)}, {fj_hex(imm)}, {addr}\n' 230 | 231 | 232 | def u_type(op: int) -> [int, int]: 233 | imm = sign_extend(op & 0xfffff000, 32) 234 | rd = (op >> 7) & 0x1f 235 | return imm, rd 236 | 237 | 238 | def lui_op(op: int) -> str: 239 | imm, rd = u_type(op) 240 | return f' .lui {zero_register(rd)}, {register_name(rd)}, {fj_hex(imm)}\n' 241 | 242 | 243 | def auipc_op(op: int, addr: int) -> str: 244 | imm, rd = u_type(op) 245 | return f' .auipc {zero_register(rd)}, {register_name(rd)}, {fj_hex(imm)}, {addr}\n' 246 | 247 | 248 | def jal_op(macro_name: str, op: int, addr: int) -> str: 249 | imm20 = op >> 31 250 | imm10_1 = (op >> 21) & 0x3ff 251 | imm11 = (op >> 20) & 0x1 252 | imm_19_12 = (op >> 12) & 0xff 253 | imm = (imm20 << 20) | (imm10_1 << 1) | (imm11 << 11) | (imm_19_12 << 12) 254 | imm = sign_extend(imm, 21) 255 | 256 | rd = (op >> 7) & 0x1f 257 | 258 | global pc_changed 259 | 260 | if imm % 4 == 2: 261 | if JAL_DEBUG_P_START_IMMEDIATE <= imm <= JAL_DEBUG_P_END_IMMEDIATE: 262 | p_imm = (imm - JAL_DEBUG_P_START_IMMEDIATE) // 4 263 | imm_str = f'"debug_p{p_imm:02X}\\n"' 264 | return f' .syscall.print_string {imm_str}\n' 265 | 266 | if JAL_PRINT_CHAR_START_IMMEDIATE <= imm <= JAL_PRINT_CHAR_END_IMMEDIATE: 267 | char_imm = (imm - JAL_PRINT_CHAR_START_IMMEDIATE) // 4 268 | return f' .syscall.print_string {char_imm}\n' 269 | 270 | if imm == JAL_WRITE_IMMEDIATE: 271 | return f' .syscall.write_byte {register_name(rd)}\n' 272 | elif imm == JAL_READ_IMMEDIATE: 273 | return f' .syscall.read_byte {register_name(rd)}\n' 274 | elif imm == JAL_EXIT_IMMEDIATE: 275 | return f' .syscall.exit {register_name(rd)}\n' 276 | elif imm == JAL_SBRK_IMMEDIATE: 277 | return f' .syscall.sbrk {register_name(rd)}\n' 278 | elif imm == JAL_DEBUG_REGISTERS_IMMEDIATE: 279 | return f' .syscall.debug_print_regs\n' 280 | elif imm == JAL_DEBUG_PRINT_REGISTER_IMMEDIATE: 281 | return f' .syscall.debug_print_reg {register_name(rd)}\n' 282 | else: 283 | raise InvalidOpcode(f"Bad imm offset in j-type op: 0x{op:08x} (address 0x{addr:08x}).") 284 | 285 | pc_changed = True 286 | return f' .{macro_name} {zero_register(rd)}, {register_name(rd)}, {fj_hex(imm)}, {addr}\n' 287 | 288 | 289 | def write_branch_op(ops_file: TextIO, full_op: int, addr: int, funct3: int, funct7: int) -> None: 290 | if funct3 == RV_BEQ: 291 | ops_file.write(b_type('beq', full_op, addr)) 292 | elif funct3 == RV_BNE: 293 | ops_file.write(b_type('bne', full_op, addr)) 294 | elif funct3 == RV_BLT: 295 | ops_file.write(b_type('blt', full_op, addr)) 296 | elif funct3 == RV_BGE: 297 | ops_file.write(b_type('bge', full_op, addr)) 298 | elif funct3 == RV_BLTU: 299 | ops_file.write(b_type('bltu', full_op, addr)) 300 | elif funct3 == RV_BGEU: 301 | ops_file.write(b_type('bgeu', full_op, addr)) 302 | else: 303 | raise InvalidOpcode(f"bad funct3 at branch op: 0x{full_op:08x} (address 0x{addr:08x}).") 304 | 305 | 306 | def write_load_op(ops_file: TextIO, full_op: int, addr: int, funct3: int, funct7: int) -> None: 307 | if funct3 == RV_LB: 308 | ops_file.write(load_type('lb', full_op)) 309 | elif funct3 == RV_LH: 310 | ops_file.write(load_type('lh', full_op)) 311 | elif funct3 == RV_LW: 312 | ops_file.write(load_type('lw', full_op)) 313 | elif funct3 == RV_LBU: 314 | ops_file.write(load_type('lbu', full_op)) 315 | elif funct3 == RV_LHU: 316 | ops_file.write(load_type('lhu', full_op)) 317 | else: 318 | raise InvalidOpcode(f"bad funct3 at load op: 0x{full_op:08x} (address 0x{addr:08x}).") 319 | 320 | 321 | def write_store_op(ops_file: TextIO, full_op: int, addr: int, funct3: int, funct7: int) -> None: 322 | if funct3 == RV_SB: 323 | ops_file.write(s_type('sb', full_op)) 324 | elif funct3 == RV_SH: 325 | ops_file.write(s_type('sh', full_op)) 326 | elif funct3 == RV_SW: 327 | ops_file.write(s_type('sw', full_op)) 328 | else: 329 | raise InvalidOpcode(f"bad funct3 at store op: 0x{full_op:08x} (address 0x{addr:08x}).") 330 | 331 | 332 | def write_alu_imm_op(ops_file: TextIO, full_op: int, addr: int, funct3: int, funct7: int) -> None: 333 | if funct3 == RV_ADDI: 334 | ops_file.write(i_type('addi', full_op)) 335 | elif funct3 == RV_SLTI: 336 | ops_file.write(i_type('slti', full_op, dst_is_rs1=False)) 337 | elif funct3 == RV_SLTIU: 338 | ops_file.write(i_type('sltiu', full_op, dst_is_rs1=False)) 339 | elif funct3 == RV_XORI: 340 | ops_file.write(i_type('xori', full_op)) 341 | elif funct3 == RV_ORI: 342 | ops_file.write(i_type('ori', full_op)) 343 | elif funct3 == RV_ANDI: 344 | ops_file.write(i_type('andi', full_op)) 345 | elif funct3 == RV_SLLI and funct7 == RV_SLLI_FUNCT7: 346 | ops_file.write(shift_imm_op('slli', full_op)) 347 | elif funct3 == RV_SRI and funct7 == RV_SRLI_FUNCT7: 348 | ops_file.write(shift_imm_op('srli', full_op)) 349 | elif funct3 == RV_SRI and funct7 == RV_SRAI_FUNCT7: 350 | ops_file.write(shift_imm_op('srai', full_op)) 351 | else: 352 | raise InvalidOpcode(f"bad funct3/funct7 at alu_imm op: 0x{full_op:08x} (address 0x{addr:08x}).") 353 | 354 | 355 | def write_alu_op(ops_file: TextIO, full_op: int, addr: int, funct3: int, funct7: int) -> None: 356 | if funct3 == RV_ADD_SUB and funct7 == RV_ADD_FUNCT7: 357 | ops_file.write(r_type('add', full_op)) 358 | elif funct3 == RV_ADD_SUB and funct7 == RV_SUB_FUNCT7: 359 | ops_file.write(r_type('sub', full_op)) 360 | elif funct3 == RV_XOR and funct7 == RV_XOR_FUNCT7: 361 | ops_file.write(r_type('xor', full_op)) 362 | elif funct3 == RV_OR and funct7 == RV_OR_FUNCT7: 363 | ops_file.write(r_type('or', full_op)) 364 | elif funct3 == RV_AND and funct7 == RV_AND_FUNCT7: 365 | ops_file.write(r_type('and', full_op)) 366 | elif funct3 == RV_SLT and funct7 == RV_SLT_FUNCT7: 367 | ops_file.write(r_type('slt', full_op, dst_is_rs1=False)) 368 | elif funct3 == RV_SLTU and funct7 == RV_SLTU_FUNCT7: 369 | ops_file.write(r_type('sltu', full_op, dst_is_rs1=False)) 370 | elif funct3 == RV_SLL and funct7 == RV_SLL_FUNCT7: 371 | ops_file.write(r_type('sll', full_op)) 372 | elif funct3 == RV_SR and funct7 == RV_SRL_FUNCT7: 373 | ops_file.write(r_type('srl', full_op)) 374 | elif funct3 == RV_SR and funct7 == RV_SRA_FUNCT7: 375 | ops_file.write(r_type('sra', full_op)) 376 | else: 377 | raise InvalidOpcode(f"bad funct3/funct7 at alu op: 0x{full_op:08x} (address 0x{addr:08x}).") 378 | 379 | 380 | def write_rv32m_op(ops_file: TextIO, full_op: int, addr: int, funct3: int, funct7: int) -> None: 381 | if funct3 == RV_MUL: 382 | ops_file.write(r_type('mul', full_op, dst_is_rs1=False)) 383 | elif funct3 == RV_MULH: 384 | ops_file.write(r_type('mulh', full_op, dst_is_rs1=False)) 385 | elif funct3 == RV_MULHSU: 386 | ops_file.write(r_type('mulhsu', full_op, dst_is_rs1=False)) 387 | elif funct3 == RV_MULHU: 388 | ops_file.write(r_type('mulhu', full_op, dst_is_rs1=False)) 389 | elif funct3 == RV_DIV: 390 | ops_file.write(r_type('div', full_op, dst_is_rs1=False)) 391 | elif funct3 == RV_DIVU: 392 | ops_file.write(r_type('divu', full_op, dst_is_rs1=False)) 393 | elif funct3 == RV_REM: 394 | ops_file.write(r_type('rem', full_op, dst_is_rs1=True)) 395 | elif funct3 == RV_REMU: 396 | ops_file.write(r_type('remu', full_op, dst_is_rs1=True)) 397 | 398 | 399 | def write_op_safe(ops_file: TextIO, full_op: int, addr: int, error_on_unimplemented_op: bool): 400 | try: 401 | write_op(ops_file, full_op, addr) 402 | except InvalidOpcode: 403 | if error_on_unimplemented_op: 404 | raise 405 | ops_file.write('riscv.unimplemented_op\n') 406 | 407 | 408 | def write_op(ops_file: TextIO, full_op: int, addr: int) -> None: 409 | opcode = full_op & 0x7f 410 | funct3 = (full_op >> 12) & 7 411 | funct7 = full_op >> 25 412 | 413 | global pc_changed 414 | pc_changed = False 415 | 416 | if opcode == RV_LUI: 417 | ops_file.write(lui_op(full_op)) 418 | elif opcode == RV_AUIPC: 419 | ops_file.write(auipc_op(full_op, addr)) 420 | elif opcode == RV_JAL: 421 | ops_file.write(jal_op('jal', full_op, addr)) 422 | elif opcode == RV_JALR: 423 | if funct3 != 0: 424 | raise InvalidOpcode(f"bad funct3 at jalr op: 0x{full_op:08x} (address 0x{addr:08x}).") 425 | else: 426 | ops_file.write(jalr_op(full_op, addr)) 427 | 428 | elif opcode == RV_B: 429 | write_branch_op(ops_file, full_op, addr, funct3, funct7) 430 | elif opcode == RV_L: 431 | write_load_op(ops_file, full_op, addr, funct3, funct7) 432 | elif opcode == RV_S: 433 | write_store_op(ops_file, full_op, addr, funct3, funct7) 434 | 435 | elif opcode == RV_ALU_IMM: 436 | write_alu_imm_op(ops_file, full_op, addr, funct3, funct7) 437 | elif opcode == RV_ALU: 438 | if funct7 == RV32M_FUNCT7: 439 | write_rv32m_op(ops_file, full_op, addr, funct3, funct7) 440 | else: 441 | write_alu_op(ops_file, full_op, addr, funct3, funct7) 442 | 443 | elif opcode == RV_FENCE: 444 | if funct3 == RV_FENCE_FUNCT3: 445 | raise InvalidOpcode(f"C2fj doesn't support fence ops: encoding=0x{full_op:08x} (address 0x{addr:08x}).") 446 | else: 447 | raise InvalidOpcode(f"bad funct3 at fence op: 0x{full_op:08x} (address 0x{addr:08x}).") 448 | 449 | elif opcode == RV_CALL: 450 | if full_op == RV_ECALL_FULL_OP: 451 | raise InvalidOpcode( 452 | f"C2fj doesn't support ecall ops (newlib shouldn't have produced it) (address 0x{addr:08x}).") 453 | elif full_op == RV_EBREAK_FULL_OP: 454 | raise InvalidOpcode( 455 | f"C2fj doesn't support ebreak ops (newlib shouldn't have produced it) (address 0x{addr:08x}).") 456 | else: 457 | raise InvalidOpcode(f"bad instruction at env call/break op: 0x{full_op:08x} (address 0x{addr:08x}).") 458 | 459 | else: 460 | raise InvalidOpcode(f"invalid op: 0x{full_op:08x} (address 0x{addr:08x}).") 461 | 462 | if not pc_changed: 463 | ops_file.write(f' .inc_pc 0x{addr:08x}\n') 464 | 465 | ops_file.write('\n') 466 | -------------------------------------------------------------------------------- /c2fj/riscv_to_fj.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import TextIO, Iterator, Tuple 3 | 4 | import elftools.elf.elffile # type: ignore 5 | 6 | from c2fj.riscv_instructions import write_op_safe 7 | 8 | 9 | def get_symbol_value(elf: elftools.elf.elffile.ELFFile, symbol_name: str) -> int: 10 | symtab_section = elf.get_section_by_name('.symtab') 11 | if symtab_section is None: 12 | raise ValueError('Symbol section not found.') 13 | 14 | for symbol in symtab_section.iter_symbols(): 15 | if symbol.name == symbol_name: 16 | return symbol.entry.st_value 17 | 18 | raise ValueError(f'Symbol name "{symbol_name}" not found.') 19 | 20 | 21 | def get_addr_label_name(addr: int) -> str: 22 | return f'ADDR_{addr:08X}' 23 | 24 | 25 | def write_jump_to_addr_label(jmp_file: TextIO, addr: int) -> None: 26 | jmp_file.write(f';.{get_addr_label_name(addr)}\n') 27 | 28 | 29 | def write_declare_addr_label(ops_file: TextIO, addr: int) -> None: 30 | ops_file.write(f'{get_addr_label_name(addr)}:\n') 31 | 32 | 33 | def write_memory_data(mem_file: TextIO, data: bytes, virtual_address: int, reserved_bytes_size: int) -> None: 34 | mem_file.write(f'segment .MEM + 0x{virtual_address:08x}*dw\n') 35 | # for byte in data: 36 | # mem_file.write(f'riscv.byte {byte}\n') 37 | if len(data) > 0: 38 | mem_file.write(f'riscv.byte.vec {len(data)}, 0x{data[::-1].hex()}\n') 39 | if reserved_bytes_size > 0: 40 | mem_file.write(f'reserve {hex(reserved_bytes_size)}*dw\n') 41 | mem_file.write(f'\n\n') 42 | 43 | 44 | def ops_and_addr_iterator(data: bytes, virtual_address: int) -> Iterator[Tuple[int, int]]: 45 | return ((int.from_bytes(data[i:i + 4], 'little'), virtual_address + i) 46 | for i in range(0, len(data), 4)) 47 | 48 | 49 | def write_ops_and_jumps(ops_file: TextIO, jmp_file: TextIO, data: bytes, virtual_address: int, 50 | error_on_unimplemented_op: bool = False) -> None: 51 | jmp_file.write(f'segment .JMP + 0x{virtual_address:08x}/4*dw\n') 52 | for op, addr in ops_and_addr_iterator(data, virtual_address): 53 | write_jump_to_addr_label(jmp_file, addr) 54 | write_declare_addr_label(ops_file, addr) 55 | write_op_safe(ops_file, op, addr, error_on_unimplemented_op) 56 | 57 | jmp_file.write(f'\n\n') 58 | ops_file.write(f'\n\n') 59 | 60 | 61 | def write_open_riscv_namespace(file: TextIO) -> None: 62 | file.write(f"ns riscv {{\n\n\n") 63 | 64 | 65 | def write_init_riscv_ops(ops_file: TextIO, start_addr: int) -> None: 66 | ops_file.write(f"segment 0\n" 67 | f".start .{get_addr_label_name(start_addr)}\n\n\n") 68 | 69 | 70 | def write_close_riscv_namespace(file: TextIO) -> None: 71 | file.write(f"}}\n") 72 | 73 | 74 | def is_loaded_to_memory(segment): 75 | return segment['p_type'] == 'PT_LOAD' 76 | 77 | 78 | def is_segment_executable(segment): 79 | return segment['p_flags'] & 1 80 | 81 | 82 | def get_virtual_start_address(segment): 83 | return segment['p_vaddr'] 84 | 85 | 86 | def get_reserved_byte_size(segment): 87 | return segment['p_memsz'] - segment['p_filesz'] 88 | 89 | 90 | def get_segment_data(segment) -> bytes: 91 | return segment.data() 92 | 93 | 94 | def write_segment(mem_file: TextIO, jmp_file: TextIO, ops_file: TextIO, segment) -> None: 95 | virtual_address = get_virtual_start_address(segment) 96 | reserved_byte_size = get_reserved_byte_size(segment) 97 | data = get_segment_data(segment) 98 | 99 | if is_segment_executable(segment): 100 | write_ops_and_jumps(ops_file, jmp_file, data, virtual_address) 101 | write_memory_data(mem_file, data, virtual_address, reserved_byte_size) 102 | 103 | 104 | def get_start_address(elf: elftools.elf.elffile.ELFFile) -> int: 105 | return elf['e_entry'] 106 | 107 | 108 | def write_file_prefixes(mem_file: TextIO, jmp_file: TextIO, ops_file: TextIO, 109 | elf: elftools.elf.elffile.ELFFile) -> None: 110 | for file in (mem_file, jmp_file, ops_file): 111 | write_open_riscv_namespace(file) 112 | 113 | write_init_riscv_ops(ops_file, get_start_address(elf)) 114 | 115 | 116 | def write_file_suffixes(mem_file: TextIO, jmp_file: TextIO, ops_file: TextIO) -> None: 117 | for file in (mem_file, jmp_file, ops_file): 118 | write_close_riscv_namespace(file) 119 | 120 | 121 | def get_segments(elf: elftools.elf.elffile.ELFFile) -> Iterator: 122 | return elf.iter_segments() 123 | 124 | 125 | def create_fj_files_from_riscv_elf(elf_path: Path, mem_path: Path, jmp_path: Path, ops_path: Path) -> None: 126 | with mem_path.open('w') as mem_file, \ 127 | jmp_path.open('w') as jmp_file, \ 128 | ops_path.open('w') as ops_file, \ 129 | elf_path.open('rb') as elf_file: 130 | 131 | elf = elftools.elf.elffile.ELFFile(elf_file) 132 | write_file_prefixes(mem_file, jmp_file, ops_file, elf) 133 | 134 | for segment in get_segments(elf): 135 | if is_loaded_to_memory(segment): 136 | write_segment(mem_file, jmp_file, ops_file, segment) 137 | 138 | write_file_suffixes(mem_file, jmp_file, ops_file) 139 | -------------------------------------------------------------------------------- /gitattributes.txt: -------------------------------------------------------------------------------- 1 | # Show statistics for FlipJump files (as Text files, until FlipJump is Supported in linguist/lib/linguist/languages.yml). 2 | *.fj linguist-language=Text linguist-detectable 3 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["poetry-core"] 3 | build-backend = "poetry.core.masonry.api" 4 | 5 | 6 | [tool.poetry] 7 | name = "c2fj" 8 | version = "1.0.0" 9 | description = "A C to flipjump compiler" 10 | authors = ["Tom Herman "] 11 | license = "BSD-2-Clause-Simplified" 12 | readme = "README.md" 13 | 14 | homepage = "https://esolangs.org/wiki/FlipJump" 15 | repository = "https://github.com/tomhea/c2fj" 16 | 17 | keywords = ["esolang", "oisc", "assembly", "compiler"] 18 | classifiers = [ 19 | "Topic :: Education", 20 | "Topic :: Software Development :: Assemblers", 21 | "Topic :: Software Development :: Compilers", 22 | "Topic :: Software Development :: Debuggers", 23 | "Topic :: Software Development :: Interpreters", 24 | "Topic :: Software Development :: Libraries", 25 | "Operating System :: OS Independent", 26 | "Programming Language :: C", 27 | "Programming Language :: Python :: 3.8", 28 | "Programming Language :: Python :: 3.9", 29 | "Programming Language :: Python :: 3.10", 30 | "Programming Language :: Python :: 3.11", 31 | "Programming Language :: Python :: 3.12", 32 | "Programming Language :: Python :: 3.13", 33 | "Programming Language :: Other", 34 | "Intended Audience :: Developers", 35 | "Intended Audience :: Education", 36 | "Intended Audience :: Science/Research", 37 | ] 38 | 39 | 40 | [tool.poetry.dependencies] 41 | python = "^3.8.1" 42 | flipjump = ">=1.3.0" 43 | pyelftools = ">=0.31" 44 | 45 | # developement 46 | pytest = { version = "^7.4.0", optional = true } 47 | pytest-xdist = { version = "^3.3.1", optional = true } 48 | 49 | [tool.poetry.extras] 50 | tests = ["pytest", "pytest-xdist"] 51 | 52 | [tool.poetry.scripts] 53 | c2fj = 'c2fj.c2fj_main:main' 54 | -------------------------------------------------------------------------------- /res/c2fj_stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/res/c2fj_stats.png -------------------------------------------------------------------------------- /res/compiled_elf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/res/compiled_elf.png -------------------------------------------------------------------------------- /res/compiled_fj_files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/res/compiled_fj_files.png -------------------------------------------------------------------------------- /res/compiled_fjm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/res/compiled_fjm.png -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | pytest.register_assert_rewrite("flipjump") 5 | -------------------------------------------------------------------------------- /tests/programs/hello_float/input.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/tests/programs/hello_float/input.txt -------------------------------------------------------------------------------- /tests/programs/hello_float/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | int main() { 5 | volatile double x = 1.23; 6 | volatile double y = 4.56; 7 | volatile double ans = x * y; 8 | printf("1.23 * 4.56 == %.5f\n", ans); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /tests/programs/hello_float/output.txt: -------------------------------------------------------------------------------- 1 | 1.23 * 4.56 == 5.60880 2 | Program exited with exit code 0x0. 3 | -------------------------------------------------------------------------------- /tests/programs/hello_input/input.txt: -------------------------------------------------------------------------------- 1 | Tom 2 | -------------------------------------------------------------------------------- /tests/programs/hello_input/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "c2fj_syscall.h" 4 | 5 | 6 | int main() { 7 | char name[20] = {0}; 8 | printf("Enter your name: "); 9 | scanf("%19s", name); 10 | printf("Hello %s!\n", name); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /tests/programs/hello_input/output.txt: -------------------------------------------------------------------------------- 1 | Enter your name: Hello Tom! 2 | Program exited with exit code 0x0. 3 | -------------------------------------------------------------------------------- /tests/programs/hello_input_number/input.txt: -------------------------------------------------------------------------------- 1 | 12345678 2 | -------------------------------------------------------------------------------- /tests/programs/hello_input_number/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "c2fj_syscall.h" 4 | 5 | 6 | int main() { 7 | int my_number; 8 | printf("Enter your name: "); 9 | scanf("%d", &my_number); 10 | printf("Hello %d!\n", my_number); 11 | return my_number % 100; 12 | } 13 | -------------------------------------------------------------------------------- /tests/programs/hello_input_number/output.txt: -------------------------------------------------------------------------------- 1 | Enter your name: Hello 12345678! 2 | Program exited with exit code 0x4E. 3 | -------------------------------------------------------------------------------- /tests/programs/hello_math/input.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/tests/programs/hello_math/input.txt -------------------------------------------------------------------------------- /tests/programs/hello_math/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | int main() { 5 | volatile int x = 3; 6 | volatile int y = 7; 7 | volatile int ans = x * y; 8 | printf("3 * 7 == %d\n", ans); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /tests/programs/hello_math/output.txt: -------------------------------------------------------------------------------- 1 | 3 * 7 == 21 2 | Program exited with exit code 0x0. 3 | -------------------------------------------------------------------------------- /tests/programs/hello_world/input.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/tests/programs/hello_world/input.txt -------------------------------------------------------------------------------- /tests/programs/hello_world/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | int main() { 5 | printf("Hello world\n"); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/programs/hello_world/output.txt: -------------------------------------------------------------------------------- 1 | Hello world 2 | Program exited with exit code 0x0. 3 | -------------------------------------------------------------------------------- /tests/programs/multiple_files/Makefile: -------------------------------------------------------------------------------- 1 | GCC := riscv64-unknown-elf-gcc 2 | GCC_FLAGS := -O3 3 | 4 | SOURCES := $(C2FJ_SOURCES) main.c globals.c calculate_int.c 5 | OBJECTS := $(SOURCES:.c=.o) 6 | 7 | all: | 8 | $(GCC) $(C2FJ_GCC_OPTIONS) $(GCC_FLAGS) $(SOURCES) -I $(C2FJ_INCLUDE_DIRS) -T $(C2FJ_LINKER_SCRIPT) -o $(ELF_OUT_PATH) 9 | 10 | clean: 11 | rm -r build 2>/dev/null || true 12 | 13 | .PHONY: clean all 14 | -------------------------------------------------------------------------------- /tests/programs/multiple_files/calculate_int.c: -------------------------------------------------------------------------------- 1 | extern int c; 2 | extern int d; 3 | 4 | 5 | int calculate_int() { 6 | static int e = 8; 7 | static int f; 8 | int a = 1; 9 | int b = 2; 10 | 11 | a ^= b; 12 | a |= b; 13 | a &= b; 14 | 15 | a++; 16 | b++; 17 | c++; 18 | d++; 19 | e++; 20 | f++; 21 | 22 | a += b; 23 | a += c; 24 | a += d; 25 | a += e; 26 | a += f; 27 | 28 | return a; 29 | } 30 | -------------------------------------------------------------------------------- /tests/programs/multiple_files/calculate_int.h: -------------------------------------------------------------------------------- 1 | int calculate_int(); 2 | -------------------------------------------------------------------------------- /tests/programs/multiple_files/globals.c: -------------------------------------------------------------------------------- 1 | int c = 0x34; 2 | int d; 3 | -------------------------------------------------------------------------------- /tests/programs/multiple_files/input.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/tests/programs/multiple_files/input.txt -------------------------------------------------------------------------------- /tests/programs/multiple_files/main.c: -------------------------------------------------------------------------------- 1 | #include "calculate_int.h" 2 | 3 | 4 | int main() { 5 | return calculate_int(); 6 | } 7 | -------------------------------------------------------------------------------- /tests/programs/multiple_files/output.txt: -------------------------------------------------------------------------------- 1 | Program exited with exit code 0x46. 2 | -------------------------------------------------------------------------------- /tests/programs/primes/input.txt: -------------------------------------------------------------------------------- 1 | 100 2 | -------------------------------------------------------------------------------- /tests/programs/primes/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | int main() { 7 | printf("Calculate primes up to: "); 8 | int max_number; 9 | scanf("%d", &max_number); 10 | 11 | if (max_number < 0) { 12 | return 1; 13 | } 14 | if (max_number <= 1) { 15 | return 0; 16 | } 17 | printf("2\n"); 18 | 19 | bool* non_prime = malloc(max_number + 1); 20 | for (int i = 0; i <= max_number; i++) { 21 | non_prime[i] = false; 22 | } 23 | 24 | for (int p = 3; p <= max_number; p += 2) { 25 | if (non_prime[p] == false) { 26 | for (int i = p*p; i <= max_number; i += p) { 27 | non_prime[i] = true; 28 | } 29 | printf("%d\n", p); 30 | } 31 | } 32 | 33 | free(non_prime); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /tests/programs/primes/output.txt: -------------------------------------------------------------------------------- 1 | Calculate primes up to: 2 2 | 3 3 | 5 4 | 7 5 | 11 6 | 13 7 | 17 8 | 19 9 | 23 10 | 29 11 | 31 12 | 37 13 | 41 14 | 43 15 | 47 16 | 53 17 | 59 18 | 61 19 | 67 20 | 71 21 | 73 22 | 79 23 | 83 24 | 89 25 | 97 26 | Program exited with exit code 0x0. 27 | -------------------------------------------------------------------------------- /tests/programs/print_alice/input.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/tests/programs/print_alice/input.txt -------------------------------------------------------------------------------- /tests/programs/print_alice/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | char* alice_txt = " ALICE\'S ADVENTURES IN WONDERLAND\n\n Lewis Carroll\n\n THE MILLENNIUM FULCRUM EDITION 3.0\n\n\n\n\n CHAPTER I\n\n Down the Rabbit-Hole\n\n\n Alice was beginning to get very tired of sitting by her sister\non the bank, and of having nothing to do: once or twice she had\npeeped into the book her sister was reading, but it had no\npictures or conversations in it, `and what is the use of a book,\'\nthought Alice `without pictures or conversation?\'\n\n So she was considering in her own mind (as well as she could,\nfor the hot day made her feel very sleepy and stupid), whether\nthe pleasure of making a daisy-chain would be worth the trouble\nof getting up and picking the daisies, when suddenly a White\nRabbit with pink eyes ran close by her.\n\n There was nothing so VERY remarkable in that; nor did Alice\nthink it so VERY much out of the way to hear the Rabbit say to\nitself, `Oh dear! Oh dear! I shall be late!\' (when she thought\nit over afterwards, it occurred to her that she ought to have\nwondered at this, but at the time it all seemed quite natural);\nbut when the Rabbit actually TOOK A WATCH OUT OF ITS WAISTCOAT-\nPOCKET, and looked at it, and then hurried on, Alice started to\nher feet, for it flashed across her mind that she had never\nbefore seen a rabbit with either a waistcoat-pocket, or a watch to\ntake out of it, and burning with curiosity, she ran across the\nfield after it, and fortunately was just in time to see it pop\ndown a large rabbit-hole under the hedge.\n\n In another moment down went Alice after it, never once\nconsidering how in the world she was to get out again.\n\n The rabbit-hole went straight on like a tunnel for some way,\nand then dipped suddenly down, so suddenly that Alice had not a\nmoment to think about stopping herself before she found herself\nfalling down a very deep well.\n\n Either the well was very deep, or she fell very slowly, for she\nhad plenty of time as she went down to look about her and to\nwonder what was going to happen next. First, she tried to look\ndown and make out what she was coming to, but it was too dark to\nsee anything; then she looked at the sides of the well, and\nnoticed that they were filled with cupboards and book-shelves;\nhere and there she saw maps and pictures hung upon pegs. She\ntook down a jar from one of the shelves as she passed; it was\nlabelled `ORANGE MARMALADE\', but to her great disappointment it\nwas empty: she did not like to drop the jar for fear of killing\nsomebody, so managed to put it into one of the cupboards as she\nfell past it.\n\n `Well!\' thought Alice to herself, `after such a fall as this, I\nshall think nothing of tumbling down stairs! How brave they\'ll\nall think me at home! Why, I wouldn\'t say anything about it,\neven if I fell off the top of the house!\' (Which was very likely\ntrue.)\n\n Down, down, down. Would the fall NEVER come to an end! `I\nwonder how many miles I\'ve fallen by this time?\' she said aloud.\n`I must be getting somewhere near the centre of the earth. Let\nme see: that would be four thousand miles down, I think--\' (for,\nyou see, Alice had learnt several things of this sort in her\nlessons in the schoolroom, and though this was not a VERY good\nopportunity for showing off her knowledge, as there was no one to\nlisten to her, still it was good practice to say it over) `--yes,\nthat\'s about the right distance--but then I wonder what Latitude\nor Longitude I\'ve got to?\' (Alice had no idea what Latitude was,\nor Longitude either, but thought they were nice grand words to\nsay.)\n\n"; 4 | 5 | 6 | int main() { 7 | puts(alice_txt); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /tests/programs/print_alice/output.txt: -------------------------------------------------------------------------------- 1 | ALICE'S ADVENTURES IN WONDERLAND 2 | 3 | Lewis Carroll 4 | 5 | THE MILLENNIUM FULCRUM EDITION 3.0 6 | 7 | 8 | 9 | 10 | CHAPTER I 11 | 12 | Down the Rabbit-Hole 13 | 14 | 15 | Alice was beginning to get very tired of sitting by her sister 16 | on the bank, and of having nothing to do: once or twice she had 17 | peeped into the book her sister was reading, but it had no 18 | pictures or conversations in it, `and what is the use of a book,' 19 | thought Alice `without pictures or conversation?' 20 | 21 | So she was considering in her own mind (as well as she could, 22 | for the hot day made her feel very sleepy and stupid), whether 23 | the pleasure of making a daisy-chain would be worth the trouble 24 | of getting up and picking the daisies, when suddenly a White 25 | Rabbit with pink eyes ran close by her. 26 | 27 | There was nothing so VERY remarkable in that; nor did Alice 28 | think it so VERY much out of the way to hear the Rabbit say to 29 | itself, `Oh dear! Oh dear! I shall be late!' (when she thought 30 | it over afterwards, it occurred to her that she ought to have 31 | wondered at this, but at the time it all seemed quite natural); 32 | but when the Rabbit actually TOOK A WATCH OUT OF ITS WAISTCOAT- 33 | POCKET, and looked at it, and then hurried on, Alice started to 34 | her feet, for it flashed across her mind that she had never 35 | before seen a rabbit with either a waistcoat-pocket, or a watch to 36 | take out of it, and burning with curiosity, she ran across the 37 | field after it, and fortunately was just in time to see it pop 38 | down a large rabbit-hole under the hedge. 39 | 40 | In another moment down went Alice after it, never once 41 | considering how in the world she was to get out again. 42 | 43 | The rabbit-hole went straight on like a tunnel for some way, 44 | and then dipped suddenly down, so suddenly that Alice had not a 45 | moment to think about stopping herself before she found herself 46 | falling down a very deep well. 47 | 48 | Either the well was very deep, or she fell very slowly, for she 49 | had plenty of time as she went down to look about her and to 50 | wonder what was going to happen next. First, she tried to look 51 | down and make out what she was coming to, but it was too dark to 52 | see anything; then she looked at the sides of the well, and 53 | noticed that they were filled with cupboards and book-shelves; 54 | here and there she saw maps and pictures hung upon pegs. She 55 | took down a jar from one of the shelves as she passed; it was 56 | labelled `ORANGE MARMALADE', but to her great disappointment it 57 | was empty: she did not like to drop the jar for fear of killing 58 | somebody, so managed to put it into one of the cupboards as she 59 | fell past it. 60 | 61 | `Well!' thought Alice to herself, `after such a fall as this, I 62 | shall think nothing of tumbling down stairs! How brave they'll 63 | all think me at home! Why, I wouldn't say anything about it, 64 | even if I fell off the top of the house!' (Which was very likely 65 | true.) 66 | 67 | Down, down, down. Would the fall NEVER come to an end! `I 68 | wonder how many miles I've fallen by this time?' she said aloud. 69 | `I must be getting somewhere near the centre of the earth. Let 70 | me see: that would be four thousand miles down, I think--' (for, 71 | you see, Alice had learnt several things of this sort in her 72 | lessons in the schoolroom, and though this was not a VERY good 73 | opportunity for showing off her knowledge, as there was no one to 74 | listen to her, still it was good practice to say it over) `--yes, 75 | that's about the right distance--but then I wonder what Latitude 76 | or Longitude I've got to?' (Alice had no idea what Latitude was, 77 | or Longitude either, but thought they were nice grand words to 78 | say.) 79 | 80 | 81 | Program exited with exit code 0x0. 82 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__all_c_syscalls/Makefile: -------------------------------------------------------------------------------- 1 | GCC := riscv64-unknown-elf-gcc 2 | GCC_FLAGS := -O0 3 | 4 | SOURCES := $(C2FJ_SOURCES) main.c 5 | OBJECTS := $(SOURCES:.c=.o) 6 | 7 | all: | 8 | $(GCC) $(C2FJ_GCC_OPTIONS) $(GCC_FLAGS) $(SOURCES) -I $(C2FJ_INCLUDE_DIRS) -T $(C2FJ_LINKER_SCRIPT) -o $(ELF_OUT_PATH) 9 | 10 | clean: 11 | rm -r build 2>/dev/null || true 12 | 13 | .PHONY: clean all 14 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__all_c_syscalls/input.txt: -------------------------------------------------------------------------------- 1 | N 2 | Hello all! That's me 3 | Yoo 4 | ABCQ 5 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__all_c_syscalls/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "c2fj_syscall.h" 5 | 6 | 7 | #define PRINT_A2 "jal a2, .+22\n" 8 | extern caddr_t sbrk(int incr); 9 | extern int close(int file); 10 | extern int fstat(int file, struct stat *st); 11 | extern int isatty(int file); 12 | extern int lseek(int file, int ptr, int dir); 13 | extern void kill(int pid, int sig); 14 | extern int getpid(void); 15 | extern void exit(int status); 16 | 17 | extern int write(int file, const char *ptr, int len); 18 | extern int puts(const char *str); 19 | extern int read(int file, char *ptr, int len); 20 | extern int fputc(int c, FILE* file); 21 | extern int _getc(FILE* file); 22 | extern int _putc(char c, FILE* file); 23 | 24 | 25 | int main() { 26 | // Tested any function in `c2fj_init.c`, `c2fj_syscall.h`, but the c2fj_print_registers() 27 | 28 | c2fj_debug_p(0); 29 | c2fj_debug_p(1); 30 | c2fj_debug_p(0x3A); 31 | c2fj_debug_p(0xA3); 32 | c2fj_debug_p(0xFF); 33 | c2fj_print_char('\n'); 34 | 35 | c2fj_print_char('C'); 36 | c2fj_print_char('h'); 37 | c2fj_print_char('a'); 38 | c2fj_print_char('r'); 39 | c2fj_print_char('!'); 40 | c2fj_print_char('\n'); 41 | 42 | int c = 'Y'; 43 | c2fj_putc(c); 44 | c = '\n'; 45 | c2fj_putc(c); 46 | 47 | int x = 0x123456; 48 | c2fj_print_register(x); 49 | x--; 50 | c2fj_print_register(x); 51 | 52 | c = c2fj_getc(); 53 | c2fj_putc(c); 54 | c2fj_putc(c2fj_getc()); // read & print newline 55 | 56 | c2fj_print_char('\n'); 57 | 58 | uint32_t orig_sbrk = (uint32_t)sbrk(0); 59 | sbrk(0xfffff124 - orig_sbrk); 60 | uint32_t new_sbrk = (uint32_t)sbrk(0); 61 | sbrk(orig_sbrk - new_sbrk); 62 | c2fj_print_register(new_sbrk); 63 | c2fj_print_register((uint32_t)sbrk(0) - orig_sbrk); 64 | 65 | c2fj_print_register(close(7)); 66 | 67 | struct stat st; 68 | st.st_mode = S_IFDIR; 69 | c2fj_print_register(fstat(7, &st)); 70 | c2fj_print_register((uint32_t)st.st_mode - (uint32_t)(S_IFCHR)); 71 | 72 | c2fj_print_register(isatty(7)); 73 | c2fj_print_register(lseek(1, 2, 3)); 74 | kill(131, 9); 75 | c2fj_print_register(getpid()); 76 | 77 | c2fj_print_char('\n'); 78 | 79 | int res_w0 = write(0, "Hi0\n", 4); 80 | c2fj_print_register(res_w0); 81 | int res_w1 = write(1, "Hi1\n", 4); 82 | c2fj_print_register(res_w1); 83 | int res_w2 = write(2, "Hi2\n", 4); 84 | c2fj_print_register(res_w2); 85 | int res_w3 = write(3, "Hi3\n", 4); 86 | c2fj_print_register(res_w3); 87 | c2fj_print_char('\n'); 88 | 89 | int res_p0 = puts("Yooo"); 90 | c2fj_print_register(res_p0); 91 | int res_p1 = puts(""); 92 | c2fj_print_register(res_p1); 93 | int res_p2 = puts("Yeah\nMe!\n"); 94 | c2fj_print_register(res_p2); 95 | c2fj_print_char('\n'); 96 | 97 | char buf[20] = {0}; 98 | int res_r0 = read(0, buf, 4); 99 | puts(buf); 100 | c2fj_print_register(res_r0); 101 | 102 | int res_r1 = read(0, buf, 20); 103 | puts(buf); 104 | c2fj_print_register(res_r1); 105 | 106 | int res_r2 = read(1, buf, 4); 107 | puts(buf); 108 | c2fj_print_register(res_r2); 109 | 110 | int res_r3 = read(0, buf, 4); 111 | puts(buf); 112 | c2fj_print_register(res_r3); 113 | 114 | c2fj_print_char('\n'); 115 | 116 | char ch; 117 | 118 | int res_putc0 = _putc('a', stdin); 119 | int res_putc1 = _putc('b', stdout); 120 | int res_putc2 = _putc('c', stderr); 121 | ch = 'A'; 122 | int res_putc3 = _putc(ch, stdout); 123 | ch++; 124 | int res_putc4 = _putc(ch, stdout); 125 | c2fj_print_char('\n'); 126 | c2fj_print_register(res_putc0); 127 | c2fj_print_register(res_putc1); 128 | c2fj_print_register(res_putc2); 129 | c2fj_print_register(res_putc3); 130 | c2fj_print_register(res_putc4); 131 | c2fj_print_char('\n'); 132 | 133 | int res_fputc0 = fputc('a', stdin); 134 | int res_fputc1 = fputc('b', stdout); 135 | int res_fputc2 = fputc('c', stderr); 136 | ch = 'A'; 137 | int res_fputc3 = fputc(ch, stdout); 138 | ch++; 139 | int res_fputc4 = fputc(ch, stdout); 140 | c2fj_print_char('\n'); 141 | c2fj_print_register(res_fputc0); 142 | c2fj_print_register(res_fputc1); 143 | c2fj_print_register(res_fputc2); 144 | c2fj_print_register(res_fputc3); 145 | c2fj_print_register(res_fputc4); 146 | c2fj_print_char('\n'); 147 | 148 | _putc(_getc(stdin), stdout); 149 | _putc(_getc(stdout), stdout); // works, not validated. 150 | _putc(_getc(stderr), stdout); // works, not validated. 151 | ch = _getc(stdin); 152 | ch++; 153 | _putc(ch, stdout); 154 | _putc(_getc(stdin), stdout); // read & print newline 155 | c2fj_print_char('\n'); 156 | 157 | exit(7); 158 | return 0; 159 | } 160 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__all_c_syscalls/output.txt: -------------------------------------------------------------------------------- 1 | debug_p00 2 | debug_p01 3 | debug_p3A 4 | debug_pA3 5 | debug_pFF 6 | 7 | Char! 8 | Y 9 | Value of register: 0x123456 10 | Value of register: 0x123455 11 | N 12 | 13 | Value of register: 0xFFFFF124 14 | Value of register: 0x0 15 | Value of register: 0xFFFFFFFF 16 | Value of register: 0x0 17 | Value of register: 0x0 18 | Value of register: 0x1 19 | Value of register: 0x0 20 | Value of register: 0xFFFFFFFF 21 | 22 | Value of register: 0xFFFFFFFF 23 | Hi1 24 | Value of register: 0x4 25 | Hi2 26 | Value of register: 0x4 27 | Value of register: 0xFFFFFFFF 28 | 29 | Yooo 30 | Value of register: 0x0 31 | 32 | Value of register: 0x0 33 | Yeah 34 | Me! 35 | 36 | Value of register: 0x0 37 | 38 | Hell 39 | Value of register: 0x4 40 | o all! That's me 41 | 42 | Value of register: 0x11 43 | o all! That's me 44 | 45 | Value of register: 0xFFFFFFFF 46 | Yoo 47 | l! That's me 48 | 49 | Value of register: 0x4 50 | 51 | abcAB 52 | Value of register: 0x61 53 | Value of register: 0x62 54 | Value of register: 0x63 55 | Value of register: 0x41 56 | Value of register: 0x42 57 | 58 | bcAB 59 | Value of register: 0xFFFFFFFF 60 | Value of register: 0x62 61 | Value of register: 0x63 62 | Value of register: 0x41 63 | Value of register: 0x42 64 | 65 | ABCR 66 | 67 | Program exited with exit code 0x7. 68 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__alu/Makefile: -------------------------------------------------------------------------------- 1 | GCC := riscv64-unknown-elf-gcc 2 | GCC_FLAGS := -O0 3 | 4 | SOURCES := $(C2FJ_SOURCES) main.c 5 | OBJECTS := $(SOURCES:.c=.o) 6 | 7 | all: | 8 | $(GCC) $(C2FJ_GCC_OPTIONS) $(GCC_FLAGS) $(SOURCES) -I $(C2FJ_INCLUDE_DIRS) -T $(C2FJ_LINKER_SCRIPT) -o $(ELF_OUT_PATH) 9 | 10 | clean: 11 | rm -r build 2>/dev/null || true 12 | 13 | .PHONY: clean all 14 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__alu/input.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/tests/programs/riscv_ops__alu/input.txt -------------------------------------------------------------------------------- /tests/programs/riscv_ops__alu/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "c2fj_syscall.h" 3 | 4 | 5 | #define PRINT_A2 "jal a2, .+22\n" 6 | #define PRINT_LN "jal a2, .+3042\n" 7 | 8 | 9 | void test_slt() { 10 | asm volatile( 11 | "lui a0, 0x12345\n" 12 | "addi a0, a0, 0x678\n" 13 | 14 | "addi a1, zero, 0\n" 15 | "slt a2, a0, a1\n" 16 | PRINT_A2 17 | "addi a1, zero, -1\n" 18 | "slt a2, a0, a1\n" 19 | PRINT_A2 20 | "addi a1, zero, 0\n" 21 | "sltu a2, a0, a1\n" 22 | PRINT_A2 23 | "addi a1, zero, -1\n" 24 | "sltu a2, a0, a1\n" 25 | PRINT_A2 26 | PRINT_LN 27 | 28 | "lui a0, 0x00000\n" 29 | "addi a0, a0, 0x123\n" 30 | 31 | "addi a1, zero, 0\n" 32 | "slt a2, a0, a1\n" 33 | PRINT_A2 34 | "addi a1, zero, -1\n" 35 | "slt a2, a0, a1\n" 36 | PRINT_A2 37 | "addi a1, zero, 0x400\n" 38 | "slt a2, a0, a1\n" 39 | PRINT_A2 40 | "addi a1, zero, 0\n" 41 | "sltu a2, a0, a1\n" 42 | PRINT_A2 43 | "addi a1, zero, -1\n" 44 | "sltu a2, a0, a1\n" 45 | PRINT_A2 46 | "addi a1, zero, 0x400\n" 47 | "sltu a2, a0, a1\n" 48 | PRINT_A2 49 | PRINT_LN 50 | 51 | "addi a0, zero, -0xA0\n" 52 | 53 | "addi a1, zero, 0\n" 54 | "slt a2, a0, a1\n" 55 | PRINT_A2 56 | "addi a1, zero, -1\n" 57 | "slt a2, a0, a1\n" 58 | PRINT_A2 59 | "addi a1, zero, 0x400\n" 60 | "slt a2, a0, a1\n" 61 | PRINT_A2 62 | "addi a1, zero, -0x400\n" 63 | "slt a2, a0, a1\n" 64 | PRINT_A2 65 | "addi a1, zero, 0\n" 66 | "sltu a2, a0, a1\n" 67 | PRINT_A2 68 | "addi a1, zero, -1\n" 69 | "sltu a2, a0, a1\n" 70 | PRINT_A2 71 | "addi a1, zero, 0x400\n" 72 | "sltu a2, a0, a1\n" 73 | PRINT_A2 74 | "addi a1, zero, -0x400\n" 75 | "sltu a2, a0, a1\n" 76 | PRINT_A2 77 | PRINT_LN 78 | :::"memory"); 79 | } 80 | 81 | void test_shifts() { 82 | asm volatile( 83 | "lui a0, 0x12345\n" 84 | "addi a0, a0, 0x678\n" 85 | 86 | "addi a1, zero, 0\n" 87 | "sll a2, a0, a1\n" 88 | PRINT_A2 89 | "addi a1, zero, 1\n" 90 | "sll a2, a0, a1\n" 91 | PRINT_A2 92 | "addi a1, zero, 4\n" 93 | "sll a2, a0, a1\n" 94 | PRINT_A2 95 | "addi a1, zero, 5\n" 96 | "sll a2, a0, a1\n" 97 | PRINT_A2 98 | "addi a1, zero, 69\n" 99 | "sll a2, a0, a1\n" 100 | PRINT_A2 101 | "addi a1, zero, 28\n" 102 | "sll a2, a0, a1\n" 103 | PRINT_A2 104 | "addi a1, zero, 31\n" 105 | "sll a2, a0, a1\n" 106 | PRINT_A2 107 | PRINT_LN 108 | 109 | "addi a1, zero, 0\n" 110 | "srl a2, a0, a1\n" 111 | PRINT_A2 112 | "addi a1, zero, 1\n" 113 | "srl a2, a0, a1\n" 114 | PRINT_A2 115 | "addi a1, zero, 4\n" 116 | "srl a2, a0, a1\n" 117 | PRINT_A2 118 | "addi a1, zero, 5\n" 119 | "srl a2, a0, a1\n" 120 | PRINT_A2 121 | "addi a1, zero, 69\n" 122 | "srl a2, a0, a1\n" 123 | PRINT_A2 124 | "addi a1, zero, 28\n" 125 | "srl a2, a0, a1\n" 126 | PRINT_A2 127 | "addi a1, zero, 31\n" 128 | "srl a2, a0, a1\n" 129 | PRINT_A2 130 | PRINT_LN 131 | 132 | "addi a1, zero, 0\n" 133 | "sra a2, a0, a1\n" 134 | PRINT_A2 135 | "addi a1, zero, 1\n" 136 | "sra a2, a0, a1\n" 137 | PRINT_A2 138 | "addi a1, zero, 4\n" 139 | "sra a2, a0, a1\n" 140 | PRINT_A2 141 | "addi a1, zero, 5\n" 142 | "sra a2, a0, a1\n" 143 | PRINT_A2 144 | "addi a1, zero, 69\n" 145 | "sra a2, a0, a1\n" 146 | PRINT_A2 147 | "addi a1, zero, 28\n" 148 | "sra a2, a0, a1\n" 149 | PRINT_A2 150 | "addi a1, zero, 31\n" 151 | "sra a2, a0, a1\n" 152 | PRINT_A2 153 | PRINT_LN 154 | 155 | "lui a0, 0x87654\n" 156 | "addi a0, a0, 0x321\n" 157 | 158 | "addi a1, zero, 0\n" 159 | "sll a2, a0, a1\n" 160 | PRINT_A2 161 | "addi a1, zero, 1\n" 162 | "sll a2, a0, a1\n" 163 | PRINT_A2 164 | "addi a1, zero, 4\n" 165 | "sll a2, a0, a1\n" 166 | PRINT_A2 167 | "addi a1, zero, 5\n" 168 | "sll a2, a0, a1\n" 169 | PRINT_A2 170 | "addi a1, zero, 69\n" 171 | "sll a2, a0, a1\n" 172 | PRINT_A2 173 | "addi a1, zero, 28\n" 174 | "sll a2, a0, a1\n" 175 | PRINT_A2 176 | "addi a1, zero, 31\n" 177 | "sll a2, a0, a1\n" 178 | PRINT_A2 179 | PRINT_LN 180 | 181 | "addi a1, zero, 0\n" 182 | "srl a2, a0, a1\n" 183 | PRINT_A2 184 | "addi a1, zero, 1\n" 185 | "srl a2, a0, a1\n" 186 | PRINT_A2 187 | "addi a1, zero, 4\n" 188 | "srl a2, a0, a1\n" 189 | PRINT_A2 190 | "addi a1, zero, 5\n" 191 | "srl a2, a0, a1\n" 192 | PRINT_A2 193 | "addi a1, zero, 69\n" 194 | "srl a2, a0, a1\n" 195 | PRINT_A2 196 | "addi a1, zero, 28\n" 197 | "srl a2, a0, a1\n" 198 | PRINT_A2 199 | "addi a1, zero, 31\n" 200 | "srl a2, a0, a1\n" 201 | PRINT_A2 202 | PRINT_LN 203 | 204 | "addi a1, zero, 0\n" 205 | "sra a2, a0, a1\n" 206 | PRINT_A2 207 | "addi a1, zero, 1\n" 208 | "sra a2, a0, a1\n" 209 | PRINT_A2 210 | "addi a1, zero, 4\n" 211 | "sra a2, a0, a1\n" 212 | PRINT_A2 213 | "addi a1, zero, 5\n" 214 | "sra a2, a0, a1\n" 215 | PRINT_A2 216 | "addi a1, zero, 69\n" 217 | "sra a2, a0, a1\n" 218 | PRINT_A2 219 | "addi a1, zero, 28\n" 220 | "sra a2, a0, a1\n" 221 | PRINT_A2 222 | "addi a1, zero, 31\n" 223 | "sra a2, a0, a1\n" 224 | PRINT_A2 225 | PRINT_LN 226 | :::"memory"); 227 | } 228 | 229 | void test_regular_alu() { 230 | asm volatile( 231 | "lui a0, 0x12345\n" 232 | "addi a0, a0, 0x678\n" 233 | 234 | "addi a1, zero, 0x111\n" 235 | "add a2, a0, a1\n" 236 | PRINT_A2 237 | "addi a1, zero, -0x111\n" 238 | "add a2, a0, a1\n" 239 | PRINT_A2 240 | "addi a1, zero, 0\n" 241 | "add a2, a0, a1\n" 242 | PRINT_A2 243 | "addi a1, zero, -0x679\n" 244 | "add a2, a0, a1\n" 245 | PRINT_A2 246 | "addi a1, zero, 0x111\n" 247 | "add a2, zero, a1\n" 248 | PRINT_A2 249 | "lui a1, 0xEDCBB\n" 250 | "addi a1, a1, -0x677\n" 251 | "add a2, a0, a1\n" 252 | PRINT_A2 253 | PRINT_LN 254 | 255 | "addi a1, zero, 0x111\n" 256 | "sub a2, a0, a1\n" 257 | PRINT_A2 258 | "addi a1, zero, -0x111\n" 259 | "sub a2, a0, a1\n" 260 | PRINT_A2 261 | "addi a1, zero, 0\n" 262 | "sub a2, a0, a1\n" 263 | PRINT_A2 264 | "addi a1, zero, 0x679\n" 265 | "sub a2, a0, a1\n" 266 | PRINT_A2 267 | "addi a1, zero, -0x679\n" 268 | "sub a2, a0, a1\n" 269 | PRINT_A2 270 | "addi a1, zero, 0x111\n" 271 | "sub a2, zero, a1\n" 272 | PRINT_A2 273 | "lui a1, 0x12345\n" 274 | "addi a1, a1, 0x679\n" 275 | "sub a2, a0, a1\n" 276 | PRINT_A2 277 | PRINT_LN 278 | 279 | "addi a1, zero, -0x1\n" 280 | "xor a2, a0, a1\n" 281 | PRINT_A2 282 | "addi a1, zero, -0x10\n" 283 | "xor a2, a0, a1\n" 284 | PRINT_A2 285 | "addi a1, zero, 0x0\n" 286 | "xor a2, a0, a1\n" 287 | PRINT_A2 288 | "addi a1, zero, 0x135\n" 289 | "xor a2, a0, a1\n" 290 | PRINT_A2 291 | PRINT_LN 292 | 293 | "addi a1, zero, -0x1\n" 294 | "or a2, a0, a1\n" 295 | PRINT_A2 296 | "addi a1, zero, -0x10\n" 297 | "or a2, a0, a1\n" 298 | PRINT_A2 299 | "addi a1, zero, 0x0\n" 300 | "or a2, a0, a1\n" 301 | PRINT_A2 302 | "addi a1, zero, 0x135\n" 303 | "or a2, a0, a1\n" 304 | PRINT_A2 305 | PRINT_LN 306 | 307 | "addi a1, zero, -0x1\n" 308 | "and a2, a0, a1\n" 309 | PRINT_A2 310 | "addi a1, zero, -0x10\n" 311 | "and a2, a0, a1\n" 312 | PRINT_A2 313 | "addi a1, zero, 0x0\n" 314 | "and a2, a0, a1\n" 315 | PRINT_A2 316 | "addi a1, zero, 0x135\n" 317 | "and a2, a0, a1\n" 318 | PRINT_A2 319 | PRINT_LN 320 | :::"memory"); 321 | } 322 | 323 | 324 | int main() { 325 | test_regular_alu(); 326 | test_shifts(); 327 | test_slt(); 328 | 329 | return 0; 330 | } 331 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__alu/output.txt: -------------------------------------------------------------------------------- 1 | Value of register: 0x12345789 2 | Value of register: 0x12345567 3 | Value of register: 0x12345678 4 | Value of register: 0x12344FFF 5 | Value of register: 0x111 6 | Value of register: 0x1 7 | 8 | Value of register: 0x12345567 9 | Value of register: 0x12345789 10 | Value of register: 0x12345678 11 | Value of register: 0x12344FFF 12 | Value of register: 0x12345CF1 13 | Value of register: 0xFFFFFEEF 14 | Value of register: 0xFFFFFFFF 15 | 16 | Value of register: 0xEDCBA987 17 | Value of register: 0xEDCBA988 18 | Value of register: 0x12345678 19 | Value of register: 0x1234574D 20 | 21 | Value of register: 0xFFFFFFFF 22 | Value of register: 0xFFFFFFF8 23 | Value of register: 0x12345678 24 | Value of register: 0x1234577D 25 | 26 | Value of register: 0x12345678 27 | Value of register: 0x12345670 28 | Value of register: 0x0 29 | Value of register: 0x30 30 | 31 | Value of register: 0x12345678 32 | Value of register: 0x2468ACF0 33 | Value of register: 0x23456780 34 | Value of register: 0x468ACF00 35 | Value of register: 0x468ACF00 36 | Value of register: 0x80000000 37 | Value of register: 0x0 38 | 39 | Value of register: 0x12345678 40 | Value of register: 0x91A2B3C 41 | Value of register: 0x1234567 42 | Value of register: 0x91A2B3 43 | Value of register: 0x91A2B3 44 | Value of register: 0x1 45 | Value of register: 0x0 46 | 47 | Value of register: 0x12345678 48 | Value of register: 0x91A2B3C 49 | Value of register: 0x1234567 50 | Value of register: 0x91A2B3 51 | Value of register: 0x91A2B3 52 | Value of register: 0x1 53 | Value of register: 0x0 54 | 55 | Value of register: 0x87654321 56 | Value of register: 0xECA8642 57 | Value of register: 0x76543210 58 | Value of register: 0xECA86420 59 | Value of register: 0xECA86420 60 | Value of register: 0x10000000 61 | Value of register: 0x80000000 62 | 63 | Value of register: 0x87654321 64 | Value of register: 0x43B2A190 65 | Value of register: 0x8765432 66 | Value of register: 0x43B2A19 67 | Value of register: 0x43B2A19 68 | Value of register: 0x8 69 | Value of register: 0x1 70 | 71 | Value of register: 0x87654321 72 | Value of register: 0xC3B2A190 73 | Value of register: 0xF8765432 74 | Value of register: 0xFC3B2A19 75 | Value of register: 0xFC3B2A19 76 | Value of register: 0xFFFFFFF8 77 | Value of register: 0xFFFFFFFF 78 | 79 | Value of register: 0x0 80 | Value of register: 0x0 81 | Value of register: 0x0 82 | Value of register: 0x1 83 | 84 | Value of register: 0x0 85 | Value of register: 0x0 86 | Value of register: 0x1 87 | Value of register: 0x0 88 | Value of register: 0x1 89 | Value of register: 0x1 90 | 91 | Value of register: 0x1 92 | Value of register: 0x1 93 | Value of register: 0x1 94 | Value of register: 0x0 95 | Value of register: 0x0 96 | Value of register: 0x1 97 | Value of register: 0x0 98 | Value of register: 0x0 99 | 100 | Program exited with exit code 0x0. 101 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__alu_imm/Makefile: -------------------------------------------------------------------------------- 1 | GCC := riscv64-unknown-elf-gcc 2 | GCC_FLAGS := -O0 3 | 4 | SOURCES := $(C2FJ_SOURCES) main.c 5 | OBJECTS := $(SOURCES:.c=.o) 6 | 7 | all: | 8 | $(GCC) $(C2FJ_GCC_OPTIONS) $(GCC_FLAGS) $(SOURCES) -I $(C2FJ_INCLUDE_DIRS) -T $(C2FJ_LINKER_SCRIPT) -o $(ELF_OUT_PATH) 9 | 10 | clean: 11 | rm -r build 2>/dev/null || true 12 | 13 | .PHONY: clean all 14 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__alu_imm/input.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/tests/programs/riscv_ops__alu_imm/input.txt -------------------------------------------------------------------------------- /tests/programs/riscv_ops__alu_imm/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "c2fj_syscall.h" 3 | 4 | 5 | #define PRINT_A2 "jal a2, .+22\n" 6 | #define PRINT_LN "jal a2, .+3042\n" 7 | 8 | 9 | void test_slt() { 10 | asm volatile( 11 | "lui a0, 0x12345\n" 12 | "addi a0, a0, 0x678\n" 13 | 14 | "slti a2, a0, 0\n" 15 | PRINT_A2 16 | "slti a2, a0, -1\n" 17 | PRINT_A2 18 | "sltiu a2, a0, 0\n" 19 | PRINT_A2 20 | "sltiu a2, a0, -1\n" 21 | PRINT_A2 22 | PRINT_LN 23 | 24 | "lui a0, 0x00000\n" 25 | "addi a0, a0, 0x123\n" 26 | 27 | "slti a2, a0, 0\n" 28 | PRINT_A2 29 | "slti a2, a0, -1\n" 30 | PRINT_A2 31 | "slti a2, a0, 0x400\n" 32 | PRINT_A2 33 | "sltiu a2, a0, 0\n" 34 | PRINT_A2 35 | "sltiu a2, a0, -1\n" 36 | PRINT_A2 37 | "sltiu a2, a0, 0x400\n" 38 | PRINT_A2 39 | PRINT_LN 40 | 41 | "addi a0, zero, -0xA0\n" 42 | 43 | "slti a2, a0, 0\n" 44 | PRINT_A2 45 | "slti a2, a0, -1\n" 46 | PRINT_A2 47 | "slti a2, a0, 0x400\n" 48 | PRINT_A2 49 | "slti a2, a0, -0x400\n" 50 | PRINT_A2 51 | "sltiu a2, a0, 0\n" 52 | PRINT_A2 53 | "sltiu a2, a0, -1\n" 54 | PRINT_A2 55 | "sltiu a2, a0, 0x400\n" 56 | PRINT_A2 57 | "sltiu a2, a0, -0x400\n" 58 | PRINT_A2 59 | PRINT_LN 60 | :::"memory"); 61 | } 62 | 63 | void test_shifts() { 64 | asm volatile( 65 | "lui a0, 0x12345\n" 66 | "addi a0, a0, 0x678\n" 67 | 68 | "slli a2, a0, 0\n" 69 | PRINT_A2 70 | "slli a2, a0, 1\n" 71 | PRINT_A2 72 | "slli a2, a0, 4\n" 73 | PRINT_A2 74 | "slli a2, a0, 5\n" 75 | PRINT_A2 76 | "slli a2, a0, 28\n" 77 | PRINT_A2 78 | "slli a2, a0, 31\n" 79 | PRINT_A2 80 | PRINT_LN 81 | 82 | "srli a2, a0, 0\n" 83 | PRINT_A2 84 | "srli a2, a0, 1\n" 85 | PRINT_A2 86 | "srli a2, a0, 4\n" 87 | PRINT_A2 88 | "srli a2, a0, 5\n" 89 | PRINT_A2 90 | "srli a2, a0, 28\n" 91 | PRINT_A2 92 | "srli a2, a0, 31\n" 93 | PRINT_A2 94 | PRINT_LN 95 | 96 | "srai a2, a0, 0\n" 97 | PRINT_A2 98 | "srai a2, a0, 1\n" 99 | PRINT_A2 100 | "srai a2, a0, 4\n" 101 | PRINT_A2 102 | "srai a2, a0, 5\n" 103 | PRINT_A2 104 | "srai a2, a0, 28\n" 105 | PRINT_A2 106 | "srai a2, a0, 31\n" 107 | PRINT_A2 108 | PRINT_LN 109 | 110 | "lui a0, 0x87654\n" 111 | "addi a0, a0, 0x321\n" 112 | 113 | "slli a2, a0, 0\n" 114 | PRINT_A2 115 | "slli a2, a0, 1\n" 116 | PRINT_A2 117 | "slli a2, a0, 4\n" 118 | PRINT_A2 119 | "slli a2, a0, 5\n" 120 | PRINT_A2 121 | "slli a2, a0, 28\n" 122 | PRINT_A2 123 | "slli a2, a0, 31\n" 124 | PRINT_A2 125 | PRINT_LN 126 | 127 | "srli a2, a0, 0\n" 128 | PRINT_A2 129 | "srli a2, a0, 1\n" 130 | PRINT_A2 131 | "srli a2, a0, 4\n" 132 | PRINT_A2 133 | "srli a2, a0, 5\n" 134 | PRINT_A2 135 | "srli a2, a0, 28\n" 136 | PRINT_A2 137 | "srli a2, a0, 31\n" 138 | PRINT_A2 139 | PRINT_LN 140 | 141 | "srai a2, a0, 0\n" 142 | PRINT_A2 143 | "srai a2, a0, 1\n" 144 | PRINT_A2 145 | "srai a2, a0, 4\n" 146 | PRINT_A2 147 | "srai a2, a0, 5\n" 148 | PRINT_A2 149 | "srai a2, a0, 28\n" 150 | PRINT_A2 151 | "srai a2, a0, 31\n" 152 | PRINT_A2 153 | PRINT_LN 154 | :::"memory"); 155 | } 156 | 157 | void test_regular_alu() { 158 | asm volatile( 159 | "lui a0, 0x12345\n" 160 | "addi a0, a0, 0x678\n" 161 | 162 | "addi a2, a0, 0x111\n" 163 | PRINT_A2 164 | "addi a2, a0, -0x111\n" 165 | PRINT_A2 166 | "addi a2, a0, 0\n" 167 | PRINT_A2 168 | "addi a2, a0, -0x679\n" 169 | PRINT_A2 170 | "addi a2, zero, 0x111\n" 171 | PRINT_A2 172 | PRINT_LN 173 | 174 | "xori a2, a0, -0x1\n" 175 | PRINT_A2 176 | "xori a2, a0, -0x10\n" 177 | PRINT_A2 178 | "xori a2, a0, 0x0\n" 179 | PRINT_A2 180 | "xori a2, a0, 0x135\n" 181 | PRINT_A2 182 | PRINT_LN 183 | 184 | "ori a2, a0, -0x1\n" 185 | PRINT_A2 186 | "ori a2, a0, -0x10\n" 187 | PRINT_A2 188 | "ori a2, a0, 0x0\n" 189 | PRINT_A2 190 | "ori a2, a0, 0x135\n" 191 | PRINT_A2 192 | PRINT_LN 193 | 194 | "andi a2, a0, -0x1\n" 195 | PRINT_A2 196 | "andi a2, a0, -0x10\n" 197 | PRINT_A2 198 | "andi a2, a0, 0x0\n" 199 | PRINT_A2 200 | "andi a2, a0, 0x135\n" 201 | PRINT_A2 202 | PRINT_LN 203 | :::"memory"); 204 | } 205 | 206 | 207 | int main() { 208 | test_regular_alu(); 209 | test_shifts(); 210 | test_slt(); 211 | 212 | return 0; 213 | } 214 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__alu_imm/output.txt: -------------------------------------------------------------------------------- 1 | Value of register: 0x12345789 2 | Value of register: 0x12345567 3 | Value of register: 0x12345678 4 | Value of register: 0x12344FFF 5 | Value of register: 0x111 6 | 7 | Value of register: 0xEDCBA987 8 | Value of register: 0xEDCBA988 9 | Value of register: 0x12345678 10 | Value of register: 0x1234574D 11 | 12 | Value of register: 0xFFFFFFFF 13 | Value of register: 0xFFFFFFF8 14 | Value of register: 0x12345678 15 | Value of register: 0x1234577D 16 | 17 | Value of register: 0x12345678 18 | Value of register: 0x12345670 19 | Value of register: 0x0 20 | Value of register: 0x30 21 | 22 | Value of register: 0x12345678 23 | Value of register: 0x2468ACF0 24 | Value of register: 0x23456780 25 | Value of register: 0x468ACF00 26 | Value of register: 0x80000000 27 | Value of register: 0x0 28 | 29 | Value of register: 0x12345678 30 | Value of register: 0x91A2B3C 31 | Value of register: 0x1234567 32 | Value of register: 0x91A2B3 33 | Value of register: 0x1 34 | Value of register: 0x0 35 | 36 | Value of register: 0x12345678 37 | Value of register: 0x91A2B3C 38 | Value of register: 0x1234567 39 | Value of register: 0x91A2B3 40 | Value of register: 0x1 41 | Value of register: 0x0 42 | 43 | Value of register: 0x87654321 44 | Value of register: 0xECA8642 45 | Value of register: 0x76543210 46 | Value of register: 0xECA86420 47 | Value of register: 0x10000000 48 | Value of register: 0x80000000 49 | 50 | Value of register: 0x87654321 51 | Value of register: 0x43B2A190 52 | Value of register: 0x8765432 53 | Value of register: 0x43B2A19 54 | Value of register: 0x8 55 | Value of register: 0x1 56 | 57 | Value of register: 0x87654321 58 | Value of register: 0xC3B2A190 59 | Value of register: 0xF8765432 60 | Value of register: 0xFC3B2A19 61 | Value of register: 0xFFFFFFF8 62 | Value of register: 0xFFFFFFFF 63 | 64 | Value of register: 0x0 65 | Value of register: 0x0 66 | Value of register: 0x0 67 | Value of register: 0x1 68 | 69 | Value of register: 0x0 70 | Value of register: 0x0 71 | Value of register: 0x1 72 | Value of register: 0x0 73 | Value of register: 0x1 74 | Value of register: 0x1 75 | 76 | Value of register: 0x1 77 | Value of register: 0x1 78 | Value of register: 0x1 79 | Value of register: 0x0 80 | Value of register: 0x0 81 | Value of register: 0x1 82 | Value of register: 0x0 83 | Value of register: 0x0 84 | 85 | Program exited with exit code 0x0. 86 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__jumps/Makefile: -------------------------------------------------------------------------------- 1 | GCC := riscv64-unknown-elf-gcc 2 | GCC_FLAGS := -O0 3 | 4 | SOURCES := $(C2FJ_SOURCES) main.c 5 | OBJECTS := $(SOURCES:.c=.o) 6 | 7 | all: | 8 | $(GCC) $(C2FJ_GCC_OPTIONS) $(GCC_FLAGS) $(SOURCES) -I $(C2FJ_INCLUDE_DIRS) -T $(C2FJ_LINKER_SCRIPT) -o $(ELF_OUT_PATH) 9 | 10 | clean: 11 | rm -r build 2>/dev/null || true 12 | 13 | .PHONY: clean all 14 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__jumps/input.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/tests/programs/riscv_ops__jumps/input.txt -------------------------------------------------------------------------------- /tests/programs/riscv_ops__jumps/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "c2fj_syscall.h" 3 | 4 | 5 | #define PRINT_A2 "jal a2, .+22\n" 6 | 7 | 8 | /// Compares in this order beq,bne, blt,bge, bltu,bgeu 9 | /// Foreach: print 1 if taken, 0 if not taken. 10 | void compare_with_numbers(int32_t a0, int32_t a1) { 11 | asm volatile ( 12 | "mv a0, %0\n" 13 | "mv a1, %1\n" 14 | 15 | "addi a2, zero, 0x1\n" 16 | "beq a0, a1, 1f\n" 17 | "xor a2, a2, a2\n" 18 | "1: " PRINT_A2 19 | 20 | "addi a2, zero, 0x1\n" 21 | "bne a0, a1, 2f\n" 22 | "xor a2, a2, a2\n" 23 | "2: " PRINT_A2 24 | 25 | "addi a2, zero, 0x1\n" 26 | "blt a0, a1, 3f\n" 27 | "xor a2, a2, a2\n" 28 | "3: " PRINT_A2 29 | 30 | "addi a2, zero, 0x1\n" 31 | "bge a0, a1, 4f\n" 32 | "xor a2, a2, a2\n" 33 | "4: " PRINT_A2 34 | 35 | "addi a2, zero, 0x1\n" 36 | "bltu a0, a1, 5f\n" 37 | "xor a2, a2, a2\n" 38 | "5: " PRINT_A2 39 | 40 | "addi a2, zero, 0x1\n" 41 | "bgeu a0, a1, 6f\n" 42 | "xor a2, a2, a2\n" 43 | "6: " PRINT_A2 44 | 45 | ::"r"(a0), "r"(a1) : "a0", "a1", "a2", "memory"); 46 | c2fj_print_char('\n'); 47 | } 48 | 49 | 50 | int main() { 51 | int32_t val; 52 | 53 | asm volatile ("lui %0, 0x12345\n":"=r"(val)::"memory"); 54 | c2fj_print_register(val); 55 | asm volatile ("lui %0, 0xABCDE\n":"=r"(val)::"memory"); 56 | c2fj_print_register(val); 57 | asm volatile ("lui %0, 0\n":"=r"(val)::"memory"); 58 | c2fj_print_register(val); 59 | c2fj_print_char('\n'); 60 | 61 | asm volatile ( 62 | "auipc a0, 0x123\n" 63 | "jal a1, 1f\n" 64 | "addi a0, a0, 0x400\n" 65 | "1: sub a0, a0, a1\n" 66 | "lui a1, 0x123\n" 67 | "sub a2, a1, a0\n" 68 | PRINT_A2 // prints 0x8 69 | :::"memory"); 70 | c2fj_print_char('\n'); 71 | 72 | asm volatile ( 73 | "xor a2, a2, a2\n" 74 | "auipc a0, 0x1\n" 75 | "addi a0, a0, -0x7f0\n" 76 | "addi a0, a0, -0x7f0\n" 77 | "jalr a1, a0, 8\n" 78 | "addi a2, a2, 1\n" // a1 79 | "addi a2, a2, 1\n" 80 | "addi a2, a2, 1\n" 81 | "addi a2, a2, 1\n" 82 | "addi a2, a2, 1\n" // a0 83 | "addi a2, a2, 1\n" 84 | "addi a2, a2, 1\n" // a0+8 85 | "addi a2, a2, 1\n" 86 | "addi a2, a2, 1\n" 87 | "addi a2, a2, 1\n" 88 | "addi a2, a2, 1\n" 89 | PRINT_A2 // prints 0x5 90 | "sub a2, a0, a1\n" 91 | PRINT_A2 // prints 0x10 92 | :::"memory"); 93 | c2fj_print_char('\n'); 94 | 95 | compare_with_numbers(0x123, 0x123); 96 | compare_with_numbers(0x234, 0x123); 97 | compare_with_numbers(0x123, 0x234); 98 | 99 | compare_with_numbers(-0x123, 0x234); 100 | compare_with_numbers(-0x234, 0x123); 101 | 102 | compare_with_numbers(-0x123, -0x234); 103 | compare_with_numbers(-0x234, -0x123); 104 | 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__jumps/output.txt: -------------------------------------------------------------------------------- 1 | Value of register: 0x12345000 2 | Value of register: 0xABCDE000 3 | Value of register: 0x0 4 | 5 | Value of register: 0x8 6 | 7 | Value of register: 0x5 8 | Value of register: 0x10 9 | 10 | Value of register: 0x1 11 | Value of register: 0x0 12 | Value of register: 0x0 13 | Value of register: 0x1 14 | Value of register: 0x0 15 | Value of register: 0x1 16 | 17 | Value of register: 0x0 18 | Value of register: 0x1 19 | Value of register: 0x0 20 | Value of register: 0x1 21 | Value of register: 0x0 22 | Value of register: 0x1 23 | 24 | Value of register: 0x0 25 | Value of register: 0x1 26 | Value of register: 0x1 27 | Value of register: 0x0 28 | Value of register: 0x1 29 | Value of register: 0x0 30 | 31 | Value of register: 0x0 32 | Value of register: 0x1 33 | Value of register: 0x1 34 | Value of register: 0x0 35 | Value of register: 0x0 36 | Value of register: 0x1 37 | 38 | Value of register: 0x0 39 | Value of register: 0x1 40 | Value of register: 0x1 41 | Value of register: 0x0 42 | Value of register: 0x0 43 | Value of register: 0x1 44 | 45 | Value of register: 0x0 46 | Value of register: 0x1 47 | Value of register: 0x0 48 | Value of register: 0x1 49 | Value of register: 0x0 50 | Value of register: 0x1 51 | 52 | Value of register: 0x0 53 | Value of register: 0x1 54 | Value of register: 0x1 55 | Value of register: 0x0 56 | Value of register: 0x1 57 | Value of register: 0x0 58 | 59 | Program exited with exit code 0x0. 60 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__memory/Makefile: -------------------------------------------------------------------------------- 1 | GCC := riscv64-unknown-elf-gcc 2 | GCC_FLAGS := -O0 3 | 4 | SOURCES := $(C2FJ_SOURCES) main.c 5 | OBJECTS := $(SOURCES:.c=.o) 6 | 7 | all: | 8 | $(GCC) $(C2FJ_GCC_OPTIONS) $(GCC_FLAGS) $(SOURCES) -I $(C2FJ_INCLUDE_DIRS) -T $(C2FJ_LINKER_SCRIPT) -o $(ELF_OUT_PATH) 9 | 10 | clean: 11 | rm -r build 2>/dev/null || true 12 | 13 | .PHONY: clean all 14 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__memory/input.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/tests/programs/riscv_ops__memory/input.txt -------------------------------------------------------------------------------- /tests/programs/riscv_ops__memory/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "c2fj_syscall.h" 3 | 4 | 5 | #define PRINT_A2 "jal a2, .+22\n" 6 | #define PRINT_LN "jal a2, .+3042\n" 7 | 8 | 9 | int main() { 10 | asm volatile ( 11 | "addi x2, x2, -1000\n" 12 | 13 | "lui a0, 0x12345\n" 14 | "addi a0, a0, 0x678\n" 15 | "sw a0, 4(x2)\n" 16 | "lw a2, 4(x2)\n" 17 | PRINT_A2 18 | "lh a2, 4(x2)\n" 19 | PRINT_A2 20 | "lhu a2, 4(x2)\n" 21 | PRINT_A2 22 | "lb a2, 4(x2)\n" 23 | PRINT_A2 24 | "lbu a2, 4(x2)\n" 25 | PRINT_A2 26 | PRINT_LN 27 | 28 | "lui a0, 0xDEADC\n" 29 | "addi a0, a0, 0xEEF-0x1000\n" 30 | "xor a2, a2, a2\n" 31 | "sw a0, 4(x2)\n" 32 | "lw a2, 4(x2)\n" 33 | PRINT_A2 34 | "lh a2, 4(x2)\n" 35 | PRINT_A2 36 | "lhu a2, 4(x2)\n" 37 | PRINT_A2 38 | "lb a2, 4(x2)\n" 39 | PRINT_A2 40 | "lbu a2, 4(x2)\n" 41 | PRINT_A2 42 | PRINT_LN 43 | 44 | "lui a0, 0x12345\n" 45 | "addi a0, a0, 0x678\n" 46 | "sh a0, 4(x2)\n" 47 | "lw a2, 4(x2)\n" 48 | PRINT_A2 49 | "sh a0, 6(x2)\n" 50 | "lw a2, 4(x2)\n" 51 | PRINT_A2 52 | PRINT_LN 53 | 54 | "lui a0, 0xDEADC\n" 55 | "addi a0, a0, 0xEEF-0x1000\n" 56 | "sb a0, 4(x2)\n" 57 | "lw a2, 4(x2)\n" 58 | PRINT_A2 59 | "sb a0, 7(x2)\n" 60 | "lw a2, 4(x2)\n" 61 | PRINT_A2 62 | PRINT_LN 63 | 64 | "addi x2, x2, +1000\n" 65 | :::"memory"); 66 | 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__memory/output.txt: -------------------------------------------------------------------------------- 1 | Value of register: 0x12345678 2 | Value of register: 0x5678 3 | Value of register: 0x5678 4 | Value of register: 0x78 5 | Value of register: 0x78 6 | 7 | Value of register: 0xDEADBEEF 8 | Value of register: 0xFFFFBEEF 9 | Value of register: 0xBEEF 10 | Value of register: 0xFFFFFFEF 11 | Value of register: 0xEF 12 | 13 | Value of register: 0xDEAD5678 14 | Value of register: 0x56785678 15 | 16 | Value of register: 0x567856EF 17 | Value of register: 0xEF7856EF 18 | 19 | Program exited with exit code 0x0. 20 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__rv32m/Makefile: -------------------------------------------------------------------------------- 1 | GCC := riscv64-unknown-elf-gcc 2 | GCC_FLAGS := -O0 3 | 4 | SOURCES := $(C2FJ_SOURCES) main.c 5 | OBJECTS := $(SOURCES:.c=.o) 6 | 7 | all: | 8 | $(GCC) $(C2FJ_GCC_OPTIONS) $(GCC_FLAGS) $(SOURCES) -I $(C2FJ_INCLUDE_DIRS) -T $(C2FJ_LINKER_SCRIPT) -o $(ELF_OUT_PATH) 9 | 10 | clean: 11 | rm -r build 2>/dev/null || true 12 | 13 | .PHONY: clean all 14 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__rv32m/input.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/tests/programs/riscv_ops__rv32m/input.txt -------------------------------------------------------------------------------- /tests/programs/riscv_ops__rv32m/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "c2fj_syscall.h" 3 | 4 | 5 | #define PRINT_A2 "jal a2, .+22\n" 6 | #define PRINT_LN "jal a2, .+3042\n" 7 | 8 | 9 | void test_mul(int32_t a0, int32_t a1) { 10 | asm volatile ( 11 | "mv a0, %0\n" 12 | "mv a1, %1\n" 13 | 14 | "mul a2, a0, a1\n" 15 | PRINT_A2 16 | "mulh a2, a0, a1\n" 17 | PRINT_A2 18 | "mulhsu a2, a0, a1\n" 19 | PRINT_A2 20 | "mulhu a2, a0, a1\n" 21 | PRINT_A2 22 | ::"r"(a0), "r"(a1) : "a0", "a1", "a2", "memory"); 23 | c2fj_print_char('\n'); 24 | } 25 | 26 | 27 | void test_div_rem(int32_t a0, int32_t a1) { 28 | asm volatile ( 29 | "mv a0, %0\n" 30 | "mv a1, %1\n" 31 | 32 | "div a2, a0, a1\n" 33 | PRINT_A2 34 | "rem a2, a0, a1\n" 35 | PRINT_A2 36 | "divu a2, a0, a1\n" 37 | PRINT_A2 38 | "remu a2, a0, a1\n" 39 | PRINT_A2 40 | ::"r"(a0), "r"(a1) : "a0", "a1", "a2", "memory"); 41 | c2fj_print_char('\n'); 42 | } 43 | 44 | 45 | int main() { 46 | test_mul(0x1110, 0x1112); 47 | test_mul(0x11111110, 0x11111112); 48 | test_mul(-0x11, 0x76543210); 49 | test_mul(0x76543210, -0x11); 50 | test_mul(-0x76543218, -0x76543); 51 | 52 | test_div_rem(20, 12); 53 | test_div_rem(20, -12); 54 | test_div_rem(-20, 12); 55 | test_div_rem(-20, -12); 56 | test_div_rem(0x87654321, 0x12345678); 57 | test_div_rem(0x87654321, -0x13); 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /tests/programs/riscv_ops__rv32m/output.txt: -------------------------------------------------------------------------------- 1 | Value of register: 0x1234320 2 | Value of register: 0x0 3 | Value of register: 0x0 4 | Value of register: 0x0 5 | 6 | Value of register: 0x87654320 7 | Value of register: 0x1234567 8 | Value of register: 0x1234567 9 | Value of register: 0x1234567 10 | 11 | Value of register: 0x2468ACF0 12 | Value of register: 0xFFFFFFF8 13 | Value of register: 0xFFFFFFF8 14 | Value of register: 0x76543208 15 | 16 | Value of register: 0x2468ACF0 17 | Value of register: 0xFFFFFFF8 18 | Value of register: 0x76543208 19 | Value of register: 0x76543208 20 | 21 | Value of register: 0x8E749448 22 | Value of register: 0x36B1B 23 | Value of register: 0x89AF3903 24 | Value of register: 0x89A7D3C0 25 | 26 | Value of register: 0x1 27 | Value of register: 0x8 28 | Value of register: 0x1 29 | Value of register: 0x8 30 | 31 | Value of register: 0xFFFFFFFF 32 | Value of register: 0x8 33 | Value of register: 0x0 34 | Value of register: 0x14 35 | 36 | Value of register: 0xFFFFFFFF 37 | Value of register: 0xFFFFFFF8 38 | Value of register: 0x15555553 39 | Value of register: 0x8 40 | 41 | Value of register: 0x1 42 | Value of register: 0xFFFFFFF8 43 | Value of register: 0x0 44 | Value of register: 0xFFFFFFEC 45 | 46 | Value of register: 0xFFFFFFFA 47 | Value of register: 0xF49F49F1 48 | Value of register: 0x7 49 | Value of register: 0x7F6E5D9 50 | 51 | Value of register: 0x658FC77 52 | Value of register: 0xFFFFFFF6 53 | Value of register: 0x0 54 | Value of register: 0x87654321 55 | 56 | Program exited with exit code 0x0. 57 | -------------------------------------------------------------------------------- /tests/programs/sanity/input.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomhea/c2fj/4117238be9bf5b3741cd41d31ffe9d94fe21ca88/tests/programs/sanity/input.txt -------------------------------------------------------------------------------- /tests/programs/sanity/main.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 0xAB; 3 | } 4 | -------------------------------------------------------------------------------- /tests/programs/sanity/output.txt: -------------------------------------------------------------------------------- 1 | Program exited with exit code 0xAB. 2 | -------------------------------------------------------------------------------- /tests/test_c2fj.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from tempfile import TemporaryDirectory 3 | 4 | import pytest 5 | from flipjump import run_test_output 6 | from flipjump.utils.constants import IO_BYTES_ENCODING 7 | 8 | from c2fj.c2fj_main import c2fj, FinishCompilingAfter, BuildNames 9 | 10 | PROGRAMS_DIR = Path(__file__).parent / "programs" 11 | 12 | 13 | def run_c2fj_test(file: Path, fixed_input_file: Path, expected_output_file: Path) -> None: 14 | with TemporaryDirectory() as temp_dir: 15 | build_dir = Path(temp_dir) 16 | 17 | c2fj(file, build_dir=build_dir, finish_compiling_after=FinishCompilingAfter.FJM) 18 | run_test_output( 19 | build_dir / BuildNames.FJM.value, 20 | fixed_input=fixed_input_file.read_text().encode(IO_BYTES_ENCODING), 21 | expected_output=expected_output_file.read_text().encode(IO_BYTES_ENCODING), 22 | should_raise_assertion_error=True, 23 | debugging_file=build_dir / BuildNames.FJ_DEBUG.value, 24 | ) 25 | 26 | 27 | @pytest.mark.parametrize("directory_name", [ 28 | "primes", 29 | "print_alice", 30 | "hello_float", 31 | "hello_input_number", 32 | "hello_input", 33 | "hello_math", 34 | "hello_world", 35 | "sanity", 36 | ]) 37 | def test_c2fj_c_file(directory_name: str) -> None: 38 | directory = PROGRAMS_DIR / directory_name 39 | run_c2fj_test(directory / "main.c", directory / "input.txt", directory / "output.txt") 40 | 41 | 42 | @pytest.mark.parametrize("directory_name", [ 43 | "riscv_ops__all_c_syscalls", 44 | "multiple_files", 45 | "riscv_ops__rv32m", 46 | "riscv_ops__alu", 47 | "riscv_ops__alu_imm", 48 | "riscv_ops__jumps", 49 | "riscv_ops__memory", 50 | ]) 51 | def test_c2fj_makefile(directory_name: str): 52 | directory = PROGRAMS_DIR / directory_name 53 | run_c2fj_test(directory / "Makefile", directory / "input.txt", directory / "output.txt") 54 | --------------------------------------------------------------------------------