├── .ci ├── check-format.sh └── check-newline.sh ├── .clang-format ├── .github └── workflows │ └── main.yml ├── .gitignore ├── AUTHORS ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── lib └── c.c ├── mk ├── arm.mk ├── common.mk └── riscv.mk ├── src ├── arm-codegen.c ├── arm.c ├── defs.h ├── elf.c ├── globals.c ├── lexer.c ├── main.c ├── parser.c ├── peephole.c ├── reg-alloc.c ├── riscv-codegen.c ├── riscv.c └── ssa.c ├── tests ├── check-snapshots.sh ├── driver.sh ├── fib.c ├── hello.c ├── snapshots │ ├── fib-arm.json │ ├── fib-riscv.json │ ├── hello-arm.json │ └── hello-riscv.json └── update-snapshots.sh └── tools └── inliner.c /.ci/check-format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SOURCES=$(find $(git rev-parse --show-toplevel) | egrep "\.(c|cxx|cpp|h|hpp)\$") 4 | 5 | set -x 6 | 7 | for file in ${SOURCES}; 8 | do 9 | clang-format-18 ${file} > expected-format 10 | diff -u -p --label="${file}" --label="expected coding style" ${file} expected-format 11 | done 12 | exit $(clang-format-18 --output-replacements-xml ${SOURCES} | egrep -c "") 13 | -------------------------------------------------------------------------------- /.ci/check-newline.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ret=0 4 | show=0 5 | # Reference: https://medium.com/@alexey.inkin/how-to-force-newline-at-end-of-files-and-why-you-should-do-it-fdf76d1d090e 6 | while IFS= read -rd '' f; do 7 | if file --mime-encoding "$f" | grep -qv binary; then 8 | tail -c1 < "$f" | read -r _ || show=1 9 | if [ $show -eq 1 ]; then 10 | echo "Warning: No newline at end of file $f" 11 | ret=1 12 | show=0 13 | fi 14 | fi 15 | done < <(git ls-files -z src tools tests) 16 | 17 | exit $ret 18 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Chromium 2 | Language: Cpp 3 | MaxEmptyLinesToKeep: 3 4 | IndentCaseLabels: false 5 | AllowShortIfStatementsOnASingleLine: false 6 | AllowShortCaseLabelsOnASingleLine: false 7 | AllowShortLoopsOnASingleLine: false 8 | DerivePointerAlignment: false 9 | PointerAlignment: Right 10 | SpaceAfterCStyleCast: true 11 | TabWidth: 4 12 | UseTab: Never 13 | IndentWidth: 4 14 | BreakBeforeBraces: Linux 15 | AccessModifierOffset: -4 16 | ForEachMacros: 17 | - foreach 18 | - list_for_each 19 | - list_for_each_safe 20 | - list_for_each_entry 21 | - list_for_each_entry_safe 22 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Github Actions 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | host-x86: 7 | runs-on: ubuntu-24.04 8 | strategy: 9 | matrix: 10 | compiler: [gcc, clang] 11 | architecture: [arm, riscv] 12 | steps: 13 | - name: checkout code 14 | uses: actions/checkout@v4 15 | - name: build artifact 16 | env: 17 | CC: ${{ matrix.compiler }} 18 | run: | 19 | sudo apt-get update -q -y 20 | sudo apt-get install -q -y graphviz jq 21 | sudo apt-get install -q -y qemu-user 22 | sudo apt-get install -q -y build-essential 23 | make distclean config ARCH=${{ matrix.architecture }} 24 | make check-snapshot || exit 1 25 | make check || exit 1 26 | 27 | host-arm: 28 | runs-on: ubuntu-24.04 29 | steps: 30 | - name: checkout code 31 | uses: actions/checkout@v4 32 | - name: build artifact 33 | # The GitHub Action for non-x86 CPU 34 | # https://github.com/uraimo/run-on-arch-action 35 | uses: uraimo/run-on-arch-action@v3 36 | with: 37 | arch: armv7 38 | distro: ubuntu24.04 39 | githubToken: ${{ github.token }} 40 | install: | 41 | apt-get update -qq -y 42 | apt-get install -yqq build-essential 43 | run: | 44 | make config ARCH=arm 45 | make check || exit 1 46 | 47 | coding-style: 48 | runs-on: ubuntu-24.04 49 | steps: 50 | - uses: actions/checkout@v4 51 | - name: coding convention 52 | run: | 53 | sudo apt-get install -q -y clang-format-18 54 | .ci/check-newline.sh 55 | .ci/check-format.sh 56 | shell: bash 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | a.out 3 | *.o 4 | *.elf 5 | *.log 6 | *.lst 7 | *.dot 8 | config 9 | src/codegen.c 10 | .session.mk 11 | 12 | # vscode C/C++ plugin generated files 13 | .vscode 14 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | shecc is written by: 2 | Jim Huang 3 | Yu-Cheng Cheng 4 | Lecopzer Chen 5 | Vacantron Chen 6 | Matt Jan 7 | Alex Lai 8 | Chin Yik Ming 9 | Hank Wang 10 | Kyle Lin 11 | Yu En Siao 12 | Meng-Zong Tsai 13 | Kuan-Wei Chiu 14 | Yu-Hui Wu 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-2021, 2023-2025 National Cheng Kung University, Taiwan. 2 | 3 | All rights reserved. 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 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | * 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" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC ?= gcc 2 | CFLAGS := -O -g \ 3 | -std=c99 -pedantic \ 4 | -fwrapv \ 5 | -Wall -Wextra \ 6 | -Wno-unused-but-set-variable \ 7 | -Wno-unused-parameter \ 8 | -Wno-unused-function \ 9 | -Wshadow \ 10 | -Wno-variadic-macros \ 11 | -Wno-uninitialized \ 12 | -Wno-strict-prototypes \ 13 | -Wno-declaration-after-statement \ 14 | -Wno-format \ 15 | -Wno-format-pedantic 16 | 17 | BUILD_SESSION := .session.mk 18 | 19 | -include $(BUILD_SESSION) 20 | 21 | STAGE0 := shecc 22 | STAGE1 := shecc-stage1.elf 23 | STAGE2 := shecc-stage2.elf 24 | 25 | OUT ?= out 26 | ARCHS = arm riscv 27 | ARCH ?= $(firstword $(ARCHS)) 28 | HOST_ARCH = $(shell arch 2>/dev/null) 29 | SRCDIR := $(shell find src -type d) 30 | LIBDIR := $(shell find lib -type d) 31 | 32 | SRCS := $(wildcard $(patsubst %,%/main.c, $(SRCDIR))) 33 | OBJS := $(SRCS:%.c=$(OUT)/%.o) 34 | deps := $(OBJS:%.o=%.o.d) 35 | TESTS := $(wildcard tests/*.c) 36 | TESTBINS := $(TESTS:%.c=$(OUT)/%.elf) 37 | SNAPSHOTS := $(foreach SNAPSHOT_ARCH,$(ARCHS), $(patsubst tests/%.c, tests/snapshots/%-$(SNAPSHOT_ARCH).json, $(TESTS))) 38 | 39 | all: config bootstrap 40 | 41 | ifeq (,$(filter $(ARCH),$(ARCHS))) 42 | $(error Support ARM and RISC-V only. Select the target with "ARCH=arm" or "ARCH=riscv") 43 | endif 44 | include mk/$(ARCH).mk 45 | include mk/common.mk 46 | 47 | config: 48 | $(Q)ln -s $(PWD)/$(SRCDIR)/$(ARCH)-codegen.c $(SRCDIR)/codegen.c 49 | $(Q)$(PRINTF) $(ARCH_DEFS) > $@ 50 | $(VECHO) "Target machine code switch to %s\n" $(ARCH) 51 | $(Q)$(MAKE) $(BUILD_SESSION) --silent 52 | $(Q)$(CONFIG_CHECK_CMD) 53 | 54 | $(OUT)/tests/%.elf: tests/%.c $(OUT)/$(STAGE0) 55 | $(VECHO) " SHECC\t$@\n" 56 | $(Q)$(OUT)/$(STAGE0) --dump-ir -o $@ $< > $(basename $@).log ; \ 57 | chmod +x $@ ; $(PRINTF) "Running $@ ...\n" 58 | $(Q)$(TARGET_EXEC) $@ && $(call pass) 59 | 60 | check: check-stage0 check-stage2 61 | 62 | check-stage0: $(OUT)/$(STAGE0) $(TESTBINS) tests/driver.sh 63 | $(VECHO) " TEST STAGE 0\n" 64 | tests/driver.sh 0 65 | 66 | check-stage2: $(OUT)/$(STAGE2) $(TESTBINS) tests/driver.sh 67 | $(VECHO) " TEST STAGE 2\n" 68 | tests/driver.sh 2 69 | 70 | check-snapshots: $(OUT)/$(STAGE0) $(SNAPSHOTS) tests/check-snapshots.sh 71 | $(Q)$(foreach SNAPSHOT_ARCH, $(ARCHS), $(MAKE) distclean config check-snapshot ARCH=$(SNAPSHOT_ARCH) --silent;) 72 | $(VECHO) "Switching backend back to %s\n" $(ARCH) 73 | $(Q)$(MAKE) distclean config ARCH=$(ARCH) --silent 74 | 75 | check-snapshot: $(OUT)/$(STAGE0) tests/check-snapshots.sh 76 | $(VECHO) "Checking snapshot for %s\n" $(ARCH) 77 | tests/check-snapshots.sh $(ARCH) 78 | $(VECHO) " OK\n" 79 | 80 | update-snapshots: tests/update-snapshots.sh 81 | $(Q)$(foreach SNAPSHOT_ARCH, $(ARCHS), $(MAKE) distclean config update-snapshot ARCH=$(SNAPSHOT_ARCH) --silent;) 82 | $(VECHO) "Switching backend back to %s\n" $(ARCH) 83 | $(Q)$(MAKE) distclean config ARCH=$(ARCH) --silent 84 | 85 | update-snapshot: $(OUT)/$(STAGE0) tests/update-snapshots.sh 86 | $(VECHO) "Updating snapshot for %s\n" $(ARCH) 87 | tests/update-snapshots.sh $(ARCH) 88 | $(VECHO) " OK\n" 89 | 90 | $(OUT)/%.o: %.c 91 | $(VECHO) " CC\t$@\n" 92 | $(Q)$(CC) -o $@ $(CFLAGS) -c -MMD -MF $@.d $< 93 | 94 | SHELL_HACK := $(shell mkdir -p $(OUT) $(OUT)/$(SRCDIR) $(OUT)/tests) 95 | $(OUT)/libc.inc: $(OUT)/inliner $(LIBDIR)/c.c 96 | $(VECHO) " GEN\t$@\n" 97 | $(Q)$(OUT)/inliner $(LIBDIR)/c.c $@ 98 | 99 | $(OUT)/inliner: tools/inliner.c 100 | $(VECHO) " CC+LD\t$@\n" 101 | $(Q)$(CC) $(CFLAGS) -o $@ $^ 102 | 103 | $(OUT)/$(STAGE0): $(OUT)/libc.inc $(OBJS) 104 | $(VECHO) " LD\t$@\n" 105 | $(Q)$(CC) $(OBJS) -o $@ 106 | 107 | $(OUT)/$(STAGE1): $(OUT)/$(STAGE0) 108 | $(Q)$(STAGE1_CHECK_CMD) 109 | $(VECHO) " SHECC\t$@\n" 110 | $(Q)$(OUT)/$(STAGE0) --dump-ir -o $@ $(SRCDIR)/main.c > $(OUT)/shecc-stage1.log 111 | $(Q)chmod a+x $@ 112 | 113 | $(OUT)/$(STAGE2): $(OUT)/$(STAGE1) 114 | $(VECHO) " SHECC\t$@\n" 115 | $(Q)$(TARGET_EXEC) $(OUT)/$(STAGE1) -o $@ $(SRCDIR)/main.c 116 | 117 | bootstrap: $(OUT)/$(STAGE2) 118 | $(Q)chmod 775 $(OUT)/$(STAGE2) 119 | $(Q)if ! diff -q $(OUT)/$(STAGE1) $(OUT)/$(STAGE2); then \ 120 | echo "Unable to bootstrap. Aborting"; false; \ 121 | fi 122 | 123 | $(BUILD_SESSION): 124 | $(PRINTF) "ARCH=$(ARCH)" > $@ 125 | 126 | .PHONY: clean 127 | clean: 128 | -$(RM) $(OUT)/$(STAGE0) $(OUT)/$(STAGE1) $(OUT)/$(STAGE2) 129 | -$(RM) $(OBJS) $(deps) 130 | -$(RM) $(TESTBINS) $(OUT)/tests/*.log $(OUT)/tests/*.lst 131 | -$(RM) $(OUT)/shecc*.log 132 | -$(RM) $(OUT)/libc.inc 133 | 134 | distclean: clean 135 | -$(RM) $(OUT)/inliner $(OUT)/target $(SRCDIR)/codegen.c config $(BUILD_SESSION) 136 | -$(RM) DOM.dot CFG.dot 137 | 138 | -include $(deps) 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # shecc : self-hosting and educational C optimizing compiler 2 | 3 |

logo image

4 | 5 | ## Introduction 6 | 7 | `shecc` is built from scratch, targeting both 32-bit Arm and RISC-V architectures, 8 | as a self-compiling compiler for a subset of the C language. 9 | Despite its simplistic nature, it is capable of performing basic optimization strategies as a standalone optimizing compiler. 10 | 11 | ### Features 12 | 13 | * Generate executable Linux ELF binaries for ARMv7-A and RV32IM. 14 | * Provide a minimal C standard library for basic I/O on GNU/Linux. 15 | * The cross-compiler is written in ANSI C, making it compatible with most platforms. 16 | * Include a self-contained C front-end with an integrated machine code generator; no external assembler or linker needed. 17 | * Utilize a two-pass compilation process: the first pass checks syntax and breaks down complex statements into basic operations, 18 | while the second pass translates these operations into Arm/RISC-V machine code. 19 | * Develop a register allocation system that is compatible with RISC-style architectures. 20 | * Implement an architecture-independent, [static single assignment](https://en.wikipedia.org/wiki/Static_single-assignment_form) (SSA)-based middle-end for enhanced optimizations. 21 | 22 | ## Compatibility 23 | 24 | `shecc` is capable of compiling C source files written in the following 25 | syntax: 26 | * data types: char, int, struct, and pointer 27 | * condition statements: if, while, for, switch, case, break, return, and 28 | general expressions 29 | * compound assignments: `+=`, `-=`, `*=` 30 | * global/local variable initializations for supported data types 31 | - e.g. `int i = [expr]` 32 | * limited support for preprocessor directives: `#define`, `#ifdef`, `#elif`, `#endif`, `#undef`, and `#error` 33 | * non-nested variadic macros with `__VA_ARGS__` identifier 34 | 35 | The backend targets armv7hf with Linux ABI, verified on Raspberry Pi 3, 36 | and also supports RISC-V 32-bit architecture, verified with QEMU. 37 | 38 | ## Bootstrapping 39 | 40 | The steps to validate `shecc` bootstrapping: 41 | 1. `stage0`: `shecc` source code is initially compiled using an ordinary compiler 42 | which generates a native executable. The generated compiler can be used as a 43 | cross-compiler. 44 | 2. `stage1`: The built binary reads its own source code as input and generates an 45 | ARMv7-A/RV32IM binary. 46 | 3. `stage2`: The generated ARMv7-A/RV32IM binary is invoked (via QEMU or running on 47 | Arm and RISC-V devices) with its own source code as input and generates another 48 | ARMv7-A/RV32IM binary. 49 | 4. `bootstrap`: Build the `stage1` and `stage2` compilers, and verify that they are 50 | byte-wise identical. If so, `shecc` can compile its own source code and produce 51 | new versions of that same program. 52 | 53 | ## Prerequisites 54 | 55 | Code generator in `shecc` does not rely on external utilities. You only need 56 | ordinary C compilers such as `gcc` and `clang`. However, `shecc` would bootstrap 57 | itself, and Arm/RISC-V ISA emulation is required. Install QEMU for Arm/RISC-V user 58 | emulation on GNU/Linux: 59 | ```shell 60 | $ sudo apt-get install qemu-user 61 | ``` 62 | 63 | It is still possible to build `shecc` on macOS or Microsoft Windows. However, 64 | the second stage bootstrapping would fail due to `qemu-arm` absence. 65 | 66 | To execute the snapshot test, install the packages below: 67 | ```shell 68 | $ sudo apt-get install graphviz jq 69 | ``` 70 | 71 | ## Build and Verify 72 | 73 | Configure which backend you want, `shecc` supports ARMv7-A and RV32IM backend: 74 | ``` 75 | $ make config ARCH=arm 76 | # Target machine code switch to Arm 77 | 78 | $ make config ARCH=riscv 79 | # Target machine code switch to RISC-V 80 | ``` 81 | 82 | Run `make` and you should see this: 83 | ``` 84 | CC+LD out/inliner 85 | GEN out/libc.inc 86 | CC out/src/main.o 87 | LD out/shecc 88 | SHECC out/shecc-stage1.elf 89 | SHECC out/shecc-stage2.elf 90 | ``` 91 | 92 | File `out/shecc` is the first stage compiler. Its usage: 93 | ```shell 94 | $ shecc [-o output] [+m] [--no-libc] [--dump-ir] 95 | ``` 96 | 97 | Compiler options: 98 | - `-o` : Specify output file name (default: `out.elf`) 99 | - `+m` : Use hardware multiplication/division instructions (default: disabled) 100 | - `--no-libc` : Exclude embedded C library (default: embedded) 101 | - `--dump-ir` : Dump intermediate representation (IR) 102 | 103 | Example: 104 | ```shell 105 | $ out/shecc -o fib tests/fib.c 106 | $ chmod +x fib 107 | $ qemu-arm fib 108 | ``` 109 | 110 | ### IR Regression Tests 111 | 112 | To ensure the consistency of frontend (lexer, parser) behavior when working on it, the snapshot test is introduced. 113 | The snapshot test dumps IRs from the executable and compares the structural identity with the provided snapshots. 114 | 115 | Verify the emitted IRs by specifying `check-snapshots` target when invoking `make`: 116 | ```shell 117 | $ make check-snapshots 118 | ``` 119 | 120 | If the compiler frontend is updated, the emitted IRs might be changed. 121 | Thus, you can update snapshots by specifying `update-snapshots` target when invoking `make`: 122 | ```shell 123 | $ make update-snapshots 124 | ``` 125 | 126 | Notice that the above 2 targets will update all backend snapshots at once, to update/check current backend's snapshot, 127 | use `update-snapshot` / `check-snapshot` instead. 128 | 129 | ### Unit Tests 130 | 131 | `shecc` comes with unit tests. To run the tests, give `check` as an argument: 132 | ```shell 133 | $ make check 134 | ``` 135 | 136 | Reference output: 137 | ``` 138 | TEST STAGE 0 139 | ... 140 | int main(int argc, int argv) { exit(sizeof(char)); } => 1 141 | int main(int argc, int argv) { int a; a = 0; switch (3) { case 0: return 2; case 3: a = 10; break; case 1: return 0; } exit(a); } => 10 142 | int main(int argc, int argv) { int a; a = 0; switch (3) { case 0: return 2; default: a = 10; break; } exit(a); } => 10 143 | OK 144 | TEST STAGE 2 145 | ... 146 | int main(int argc, int argv) { exit(sizeof(char*)); } 147 | exit code => 4 148 | output => 149 | int main(int argc, int argv) { exit(sizeof(int*)); } 150 | exit code => 4 151 | output => 152 | OK 153 | ``` 154 | 155 | To clean up the generated compiler files, execute the command `make clean`. 156 | For resetting architecture configurations, use the command `make distclean`. 157 | 158 | ## Intermediate Representation 159 | 160 | Once the option `--dump-ir` is passed to `shecc`, the intermediate representation (IR) 161 | will be generated. Take the file `tests/fib.c` for example. It consists of a recursive 162 | Fibonacci sequence function. 163 | ```c 164 | int fib(int n) 165 | { 166 | if (n == 0) 167 | return 0; 168 | else if (n == 1) 169 | return 1; 170 | return fib(n - 1) + fib(n - 2); 171 | } 172 | ``` 173 | 174 | Execute the following to generate IR: 175 | ```shell 176 | $ out/shecc --dump-ir -o fib tests/fib.c 177 | ``` 178 | 179 | Line-by-line explanation between C source and IR (variable and label numbering may differ): 180 | ```c 181 | C Source IR Explanation 182 | -------------------+--------------------------------------+-------------------------------------------------------------------------------------- 183 | int fib(int n) def int @fib(int %n) 184 | { { 185 | if (n == 0) const %.t871, 0 Load constant 0 into a temporary variable ".t871" 186 | %.t872 = eq %n, %.t871 Test if "n" is equal to ".t871", store result in ".t872" 187 | br %.t872, .label.1430, .label.1431 If ".t872" is non-zero, branch to label ".label.1430", otherwise to ".label.1431" 188 | .label.1430: 189 | return 0; const %.t873, 0 Load constant 0 into a temporary variable ".t873" 190 | ret %.t873 Return ".t873" 191 | .label.1431: 192 | else if (n == 1) const %.t874, 1 Load constant 1 into a temporary variable ".t874" 193 | %.t875 = eq %n, %.t874 Test if "n" is equal to ".t874", store result in ".t875" 194 | br %.t875, .label.1434, .label.1435 If ".t875" is true, branch to ".label.1434", otherwise to ".label.1435" 195 | .label.1434: 196 | return 1; const %.t876, 1 Load constant 1 into a temporary variable ".t876" 197 | ret %.t876 Return ".t876" 198 | .label.1435: 199 | return fib(n - 1) const %.t877, 1 Load constant 1 into ".t877" 200 | %.t878 = sub %n, %.t877 Subtract ".t877" from "n", store in ".t878" 201 | push %.t878 Prepare argument ".t878" for function call 202 | call @fib, 1 Call function "@fib" with 1 argument 203 | + retval %.t879 Store the return value in ".t879" 204 | fib(n - 2); const %.t880, 2 Load constant 2 into ".t880" 205 | %.t881 = sub %n, %.t880 Subtract ".t880" from "n", store in ".t881" 206 | push %.t881 Prepare argument ".t881" for function call 207 | call @fib, 1 Call function "@fib" with 1 argument 208 | retval %.t882 Store the return value in ".t882" 209 | %.t883 = add %.t879, %.t882 Add ".t879" and ".t882", store in ".t883" 210 | ret %.t883 Return ".t883" 211 | } } 212 | ``` 213 | 214 | ## Known Issues 215 | 216 | 1. The generated ELF lacks of .bss and .rodata section 217 | 2. The support of varying number of function arguments is incomplete. No `` can be used. 218 | Alternatively, check the implementation `printf` in source `lib/c.c` for `var_arg`. 219 | 3. The C front-end is a bit dirty because there is no effective AST. 220 | 221 | ## License 222 | 223 | `shecc` is freely redistributable under the BSD 2 clause license. 224 | Use of this source code is governed by a BSD-style license that can be found in the `LICENSE` file. 225 | -------------------------------------------------------------------------------- /lib/c.c: -------------------------------------------------------------------------------- 1 | /* 2 | * shecc - Self-Hosting and Educational C Compiler. 3 | * 4 | * shecc is freely redistributable under the BSD 2 clause license. See the 5 | * file "LICENSE" for information on usage and redistribution of this file. 6 | */ 7 | 8 | /* minimal libc implementation */ 9 | 10 | #define NULL 0 11 | 12 | #define bool _Bool 13 | #define true 1 14 | #define false 0 15 | 16 | #define INT_MAX 0x7fffffff 17 | #define INT_MIN 0x80000000 18 | 19 | #if defined(__arm__) 20 | #define __SIZEOF_POINTER__ 4 21 | #define __syscall_exit 1 22 | #define __syscall_read 3 23 | #define __syscall_write 4 24 | #define __syscall_close 6 25 | #define __syscall_open 5 26 | #define __syscall_mmap2 192 27 | #define __syscall_munmap 91 28 | 29 | #elif defined(__riscv) 30 | #define __SIZEOF_POINTER__ 4 31 | #define __syscall_exit 93 32 | #define __syscall_read 63 33 | #define __syscall_write 64 34 | #define __syscall_close 57 35 | #define __syscall_open 1024 36 | #define __syscall_openat 56 37 | #define __syscall_mmap2 222 38 | #define __syscall_munmap 215 39 | 40 | #else /* Only Arm32 and RV32 are supported */ 41 | #error "Unsupported architecture" 42 | #endif 43 | 44 | #define INT_BUF_LEN 16 45 | 46 | typedef int FILE; 47 | 48 | void abort(void); 49 | 50 | int strlen(char *str) 51 | { 52 | int i = 0; 53 | while (str[i]) 54 | i++; 55 | return i; 56 | } 57 | 58 | int strcmp(char *s1, char *s2) 59 | { 60 | int i = 0; 61 | while (s1[i] && s2[i]) { 62 | if (s1[i] < s2[i]) 63 | return -1; 64 | if (s1[i] > s2[i]) 65 | return 1; 66 | i++; 67 | } 68 | return s1[i] - s2[i]; 69 | } 70 | 71 | int strncmp(char *s1, char *s2, int len) 72 | { 73 | int i = 0; 74 | while (i < len) { 75 | if (s1[i] < s2[i]) 76 | return -1; 77 | if (s1[i] > s2[i]) 78 | return 1; 79 | if (!s1[i]) 80 | return 0; 81 | i++; 82 | } 83 | return 0; 84 | } 85 | 86 | char *strcpy(char *dest, char *src) 87 | { 88 | int i = 0; 89 | while (src[i]) { 90 | dest[i] = src[i]; 91 | i++; 92 | } 93 | dest[i] = 0; 94 | return dest; 95 | } 96 | 97 | char *strncpy(char *dest, char *src, int len) 98 | { 99 | int i = 0; 100 | int beyond = 0; 101 | while (i < len) { 102 | if (beyond == 0) { 103 | dest[i] = src[i]; 104 | if (src[i] == 0) 105 | beyond = 1; 106 | } else { 107 | dest[i] = 0; 108 | } 109 | i++; 110 | } 111 | return dest; 112 | } 113 | 114 | char *memcpy(char *dest, char *src, int count) 115 | { 116 | while (count > 0) { 117 | count--; 118 | dest[count] = src[count]; 119 | } 120 | return dest; 121 | } 122 | 123 | /* 124 | * set 10 digits (32bit) without div 125 | * 126 | * This function converts a given integer value to its string representation 127 | * in base-10 without using division operations. The method involves calculating 128 | * the approximate quotient and remainder using bitwise operations, which are 129 | * then used to derive each digit of the result. 130 | * 131 | * The logic is based on an efficient method of dividing by constants, as 132 | * detailed in the reference link: 133 | * http://web.archive.org/web/20180517023231/http://www.hackersdelight.org/divcMore.pdf. 134 | * This approach avoids expensive division instructions by using a series of 135 | * bitwise shifts and additions to calculate the quotient and remainder. 136 | */ 137 | void __str_base10(char *pb, int val) 138 | { 139 | int neg = 0; 140 | int q, r, t; 141 | int i = INT_BUF_LEN - 1; 142 | 143 | if (val == -2147483648) { 144 | strncpy(pb + INT_BUF_LEN - 11, "-2147483648", 11); 145 | return; 146 | } 147 | if (val < 0) { 148 | neg = 1; 149 | val = -val; 150 | } 151 | 152 | while (val) { 153 | q = (val >> 1) + (val >> 2); 154 | q += (q >> 4); 155 | q += (q >> 8); 156 | q += (q >> 16); 157 | q >>= 3; 158 | r = val - (((q << 2) + q) << 1); 159 | t = ((r + 6) >> 4); 160 | q += t; 161 | r -= (((t << 2) + t) << 1); 162 | 163 | pb[i] += r; 164 | val = q; 165 | i--; 166 | } 167 | 168 | if (neg == 1) 169 | pb[i] = '-'; 170 | } 171 | 172 | void __str_base8(char *pb, int val) 173 | { 174 | int c = INT_BUF_LEN - 1, v; 175 | /* 176 | * Because every 3 binary digits can be converted 177 | * to 1 octal digit, here performs the conversion 178 | * 10 times, derived from 32 divided by 3. 179 | * 180 | * Finally, the remaining 2 bits are processed after 181 | * the loop. 182 | * */ 183 | int times = (sizeof(int) << 3) / 3; 184 | for (int i = 0; i < times; i++) { 185 | v = val & 0x7; 186 | pb[c] = '0' + v; 187 | val >>= 3; 188 | c--; 189 | } 190 | v = val & 0x3; 191 | pb[c] = '0' + v; 192 | } 193 | 194 | void __str_base16(char *pb, int val) 195 | { 196 | int c = INT_BUF_LEN - 1; 197 | int times = sizeof(int) << 1; 198 | for (int i = 0; i < times; i++) { 199 | int v = val & 0xf; 200 | if (v < 10) 201 | pb[c] = '0' + v; 202 | else if (v < 16) 203 | pb[c] = 'a' + v - 10; 204 | else { 205 | abort(); 206 | break; 207 | } 208 | val >>= 4; 209 | c--; 210 | } 211 | } 212 | 213 | /* 214 | * The specification of snprintf() is defined in C99 7.19.6.5, 215 | * and its behavior and return value should comply with the 216 | * following description: 217 | * 218 | * - If n is zero, nothing is written. 219 | * - Writes at most n bytes, including the null character. 220 | * - On success, the return value should be the length of the 221 | * entire converted string even if n is insufficient to store it. 222 | * 223 | * Therefore, the following code defines a structure called fmtbuf_t 224 | * to implement formatted output conversion for the functions in the 225 | * printf() family. 226 | * 227 | * @buf: the current position of the buffer. 228 | * @n : the remaining space of the buffer. 229 | * @len: the number of characters that would have been written 230 | * had n been sufficiently large. 231 | * 232 | * Once a write operation is performed, buf and n will be 233 | * respectively incremented and decremented by the actual written 234 | * size if n is sufficient, and len must be incremented to store 235 | * the length of the entire converted string. 236 | */ 237 | typedef struct { 238 | char *buf; 239 | int n; 240 | int len; 241 | } fmtbuf_t; 242 | 243 | void __fmtbuf_write_char(fmtbuf_t *fmtbuf, int val) 244 | { 245 | fmtbuf->len += 1; 246 | 247 | /* 248 | * Write the given character when n is greater than 1. 249 | * This means preserving one position for the null character. 250 | */ 251 | if (fmtbuf->n <= 1) 252 | return; 253 | 254 | char ch = val & 0xFF; 255 | fmtbuf->buf[0] = ch; 256 | fmtbuf->buf += 1; 257 | fmtbuf->n -= 1; 258 | } 259 | 260 | void __fmtbuf_write_str(fmtbuf_t *fmtbuf, char *str, int l) 261 | { 262 | fmtbuf->len += l; 263 | 264 | /* 265 | * Write the given string when n is greater than 1. 266 | * This means preserving one position for the null character. 267 | */ 268 | if (fmtbuf->n <= 1) 269 | return; 270 | 271 | /* 272 | * If the remaining space is less than the length of the string, 273 | * write only n - 1 bytes. 274 | */ 275 | int sz = fmtbuf->n - 1; 276 | l = l <= sz ? l : sz; 277 | strncpy(fmtbuf->buf, str, l); 278 | fmtbuf->buf += l; 279 | fmtbuf->n -= l; 280 | } 281 | 282 | void __format(fmtbuf_t *fmtbuf, 283 | int val, 284 | int width, 285 | int zeropad, 286 | int base, 287 | int alternate_form) 288 | { 289 | char pb[INT_BUF_LEN], ch; 290 | int pbi; 291 | 292 | /* set to zeroes */ 293 | for (pbi = 0; pbi < INT_BUF_LEN; pbi++) 294 | pb[pbi] = '0'; 295 | 296 | pbi = 0; 297 | 298 | switch (base) { 299 | case 8: 300 | __str_base8(pb, val); 301 | break; 302 | case 10: 303 | __str_base10(pb, val); 304 | break; 305 | case 16: 306 | __str_base16(pb, val); 307 | break; 308 | default: 309 | abort(); 310 | break; 311 | } 312 | 313 | while (pb[pbi] == '0' && pbi < INT_BUF_LEN - 1) 314 | pbi++; 315 | 316 | switch (base) { 317 | case 8: 318 | if (alternate_form) { 319 | if (width && zeropad && pb[pbi] != '0') { 320 | __fmtbuf_write_char(fmtbuf, '0'); 321 | width -= 1; 322 | } else if (pb[pbi] != '0') 323 | pb[--pbi] = '0'; 324 | } 325 | break; 326 | case 10: 327 | if (width && zeropad && pb[pbi] == '-') { 328 | __fmtbuf_write_char(fmtbuf, '-'); 329 | pbi++; 330 | width--; 331 | } 332 | break; 333 | case 16: 334 | if (alternate_form) { 335 | if (width && zeropad && pb[pbi] != '0') { 336 | __fmtbuf_write_char(fmtbuf, '0'); 337 | __fmtbuf_write_char(fmtbuf, 'x'); 338 | width -= 2; 339 | } else if (pb[pbi] != '0') { 340 | pb[--pbi] = 'x'; 341 | pb[--pbi] = '0'; 342 | } 343 | } 344 | break; 345 | } 346 | 347 | width -= (INT_BUF_LEN - pbi); 348 | if (width < 0) 349 | width = 0; 350 | 351 | ch = zeropad ? '0' : ' '; 352 | while (width) { 353 | __fmtbuf_write_char(fmtbuf, ch); 354 | width--; 355 | } 356 | 357 | __fmtbuf_write_str(fmtbuf, pb + pbi, INT_BUF_LEN - pbi); 358 | } 359 | 360 | void __format_to_buf(fmtbuf_t *fmtbuf, char *format, int *var_args) 361 | { 362 | int si = 0, pi = 0; 363 | 364 | while (format[si]) { 365 | if (format[si] != '%') { 366 | __fmtbuf_write_char(fmtbuf, format[si]); 367 | si++; 368 | } else { 369 | int w = 0, zp = 0, pp = 0, v = var_args[pi], l; 370 | 371 | si++; 372 | if (format[si] == '#') { 373 | pp = 1; 374 | si++; 375 | } 376 | if (format[si] == '0') { 377 | zp = 1; 378 | si++; 379 | } 380 | if (format[si] >= '1' && format[si] <= '9') { 381 | w = format[si] - '0'; 382 | si++; 383 | while (format[si] >= '0' && format[si] <= '9') { 384 | w *= 10; 385 | w += format[si] - '0'; 386 | si++; 387 | } 388 | } 389 | switch (format[si]) { 390 | case 's': 391 | /* append param pi as string */ 392 | l = strlen(v); 393 | __fmtbuf_write_str(fmtbuf, v, l); 394 | break; 395 | case 'c': 396 | /* append param pi as char */ 397 | __fmtbuf_write_char(fmtbuf, v); 398 | break; 399 | case 'o': 400 | /* append param as octal */ 401 | __format(fmtbuf, v, w, zp, 8, pp); 402 | break; 403 | case 'd': 404 | /* append param as decimal */ 405 | __format(fmtbuf, v, w, zp, 10, 0); 406 | break; 407 | case 'x': 408 | /* append param as hex */ 409 | __format(fmtbuf, v, w, zp, 16, pp); 410 | break; 411 | case '%': 412 | /* append literal '%' character */ 413 | __fmtbuf_write_char(fmtbuf, '%'); 414 | si++; 415 | continue; 416 | } 417 | pi++; 418 | si++; 419 | } 420 | } 421 | 422 | /* If n is still greater than 0, set the null character. */ 423 | if (fmtbuf->n) 424 | fmtbuf->buf[0] = 0; 425 | } 426 | 427 | int printf(char *str, ...) 428 | { 429 | char buffer[200]; 430 | fmtbuf_t fmtbuf; 431 | 432 | fmtbuf.buf = buffer; 433 | fmtbuf.n = INT_MAX; 434 | fmtbuf.len = 0; 435 | __format_to_buf(&fmtbuf, str, &str + 4); 436 | return __syscall(__syscall_write, 1, buffer, fmtbuf.len); 437 | } 438 | 439 | int sprintf(char *buffer, char *str, ...) 440 | { 441 | fmtbuf_t fmtbuf; 442 | 443 | fmtbuf.buf = buffer; 444 | fmtbuf.n = INT_MAX; 445 | fmtbuf.len = 0; 446 | __format_to_buf(&fmtbuf, str, &str + 4); 447 | return fmtbuf.len; 448 | } 449 | 450 | int snprintf(char *buffer, int n, char *str, ...) 451 | { 452 | fmtbuf_t fmtbuf; 453 | 454 | fmtbuf.buf = buffer; 455 | fmtbuf.n = n; 456 | fmtbuf.len = 0; 457 | __format_to_buf(&fmtbuf, str, &str + 4); 458 | return fmtbuf.len; 459 | } 460 | 461 | int __free_all(void); 462 | 463 | void exit(int exit_code) 464 | { 465 | __free_all(); 466 | __syscall(__syscall_exit, exit_code); 467 | } 468 | 469 | void abort(void) 470 | { 471 | printf("Abnormal program termination\n"); 472 | exit(-1); 473 | } 474 | 475 | FILE *fopen(char *filename, char *mode) 476 | { 477 | if (!strcmp(mode, "wb")) { 478 | #if defined(__arm__) 479 | return __syscall(__syscall_open, filename, 65, 0x1fd); 480 | #elif defined(__riscv) 481 | /* FIXME: mode not work currently in RISC-V */ 482 | return __syscall(__syscall_openat, -100, filename, 65, 0x1fd); 483 | #endif 484 | } 485 | if (!strcmp(mode, "rb")) { 486 | #if defined(__arm__) 487 | return __syscall(__syscall_open, filename, 0, 0); 488 | #elif defined(__riscv) 489 | return __syscall(__syscall_openat, -100, filename, 0, 0); 490 | #endif 491 | } 492 | return NULL; 493 | } 494 | 495 | int fclose(FILE *stream) 496 | { 497 | __syscall(__syscall_close, stream); 498 | return 0; 499 | } 500 | 501 | /* Read a byte from file descriptor. So the return value is either in the range 502 | * of 0 to 127 for the character, or -1 on the end of file. */ 503 | int fgetc(FILE *stream) 504 | { 505 | int buf = 0, r = __syscall(__syscall_read, stream, &buf, 1); 506 | if (r < 1) 507 | return -1; 508 | return buf; 509 | } 510 | 511 | char *fgets(char *str, int n, FILE *stream) 512 | { 513 | int i; 514 | for (i = 0; i < n - 1; i++) { 515 | int c = fgetc(stream); 516 | if (c == -1) { 517 | if (i == 0) 518 | /* EOF on first char */ 519 | return NULL; 520 | /* EOF in the middle */ 521 | str[i] = 0; 522 | return str; 523 | } 524 | /* Not support casting yet. Simply assign it. */ 525 | str[i] = c; 526 | 527 | if (c == '\n') { 528 | str[i + 1] = 0; 529 | return str; 530 | } 531 | } 532 | str[i] = 0; 533 | return str; 534 | } 535 | 536 | int fputc(int c, FILE *stream) 537 | { 538 | if (__syscall(__syscall_write, stream, &c, 1) < 0) 539 | return -1; 540 | return c; 541 | } 542 | 543 | /* Non-portable: Assume page size is 4KiB */ 544 | #define PAGESIZE 4096 545 | 546 | #define CHUNK_SIZE_FREED_MASK 1 547 | #define CHUNK_SIZE_SZ_MASK 0xFFFFFFFE 548 | #define CHUNK_GET_SIZE(size) (size & CHUNK_SIZE_SZ_MASK) 549 | #define IS_CHUNK_GET_FREED(size) (size & CHUNK_SIZE_FREED_MASK) 550 | 551 | typedef struct chunk { 552 | struct chunk *next; 553 | struct chunk *prev; 554 | int size; 555 | } chunk_t; 556 | 557 | void chunk_set_freed(chunk_t *chunk) 558 | { 559 | chunk->size |= CHUNK_SIZE_FREED_MASK; 560 | } 561 | 562 | void chunk_clear_freed(chunk_t *chunk) 563 | { 564 | chunk->size &= CHUNK_SIZE_SZ_MASK; 565 | } 566 | 567 | int __align_up(int size) 568 | { 569 | int mask = PAGESIZE - 1; 570 | return ((size - 1) | mask) + 1; 571 | } 572 | 573 | chunk_t *__alloc_head; 574 | chunk_t *__alloc_tail; 575 | chunk_t *__freelist_head; 576 | 577 | void *malloc(int size) 578 | { 579 | if (size <= 0) 580 | return NULL; 581 | 582 | int flags = 34; /* MAP_PRIVATE (0x02) | MAP_ANONYMOUS (0x20) */ 583 | int prot = 3; /* PROT_READ (0x01) | PROT_WRITE (0x02) */ 584 | 585 | if (!__alloc_head) { 586 | chunk_t *tmp = 587 | __syscall(__syscall_mmap2, NULL, __align_up(sizeof(chunk_t)), prot, 588 | flags, -1, 0); 589 | __alloc_head = tmp; 590 | __alloc_tail = tmp; 591 | __alloc_head->next = NULL; 592 | __alloc_head->prev = NULL; 593 | __alloc_head->size = 0; 594 | } 595 | 596 | if (!__freelist_head) { 597 | chunk_t *tmp = 598 | __syscall(__syscall_mmap2, NULL, __align_up(sizeof(chunk_t)), prot, 599 | flags, -1, 0); 600 | __freelist_head = tmp; 601 | __freelist_head->next = NULL; 602 | __freelist_head->prev = NULL; 603 | __freelist_head->size = -1; 604 | } 605 | 606 | /* to search the best chunk */ 607 | chunk_t *best_fit_chunk = NULL; 608 | chunk_t *allocated; 609 | 610 | if (!__freelist_head->next) { 611 | /* If no more chunks in the free chunk list, allocate best_fit_chunk 612 | * as NULL. 613 | */ 614 | allocated = best_fit_chunk; 615 | } else { 616 | /* record the size of the chunk */ 617 | int bsize = 0; 618 | 619 | for (chunk_t *fh = __freelist_head; fh->next; fh = fh->next) { 620 | int fh_size = CHUNK_GET_SIZE(fh->size); 621 | if (fh_size >= size && !best_fit_chunk) { 622 | /* first time setting fh as best_fit_chunk */ 623 | best_fit_chunk = fh; 624 | bsize = fh_size; 625 | } else if ((fh_size >= size) && best_fit_chunk && 626 | (fh_size < bsize)) { 627 | /* If there is a smaller chunk available, replace it. */ 628 | best_fit_chunk = fh; 629 | bsize = fh_size; 630 | } 631 | } 632 | 633 | /* a suitable chunk has been found */ 634 | if (best_fit_chunk) { 635 | /* remove the chunk from the freelist */ 636 | if (best_fit_chunk->prev) { 637 | chunk_t *tmp = best_fit_chunk->prev; 638 | tmp->next = best_fit_chunk->next; 639 | } else 640 | __freelist_head = best_fit_chunk->next; 641 | 642 | if (best_fit_chunk->next) { 643 | chunk_t *tmp = best_fit_chunk->next; 644 | tmp->prev = best_fit_chunk->prev; 645 | } 646 | } 647 | allocated = best_fit_chunk; 648 | } 649 | 650 | if (!allocated) { 651 | allocated = 652 | __syscall(__syscall_mmap2, NULL, __align_up(sizeof(chunk_t) + size), 653 | prot, flags, -1, 0); 654 | allocated->size = __align_up(sizeof(chunk_t) + size); 655 | } 656 | 657 | __alloc_tail->next = allocated; 658 | allocated->prev = __alloc_tail; 659 | 660 | __alloc_tail = allocated; 661 | __alloc_tail->next = NULL; 662 | __alloc_tail->size = allocated->size; 663 | chunk_clear_freed(__alloc_tail); 664 | void *ptr = __alloc_tail + 1; 665 | return ptr; 666 | } 667 | 668 | void *calloc(int n, int size) 669 | { 670 | int total = n * size; 671 | char *p = malloc(total); 672 | 673 | if (!p) 674 | return NULL; 675 | 676 | /* TODO: Replace the byte buffer clearing algorithm with memset once 677 | * implemented. 678 | */ 679 | 680 | /* Currently malloc uses mmap(2) to request allocation, which guarantees 681 | * memory to be page-aligned 682 | */ 683 | int *pi = p, num_words = total >> 2, offset = num_words << 2; 684 | 685 | for (int i = 0; i < num_words; i++) 686 | pi[i] = 0; 687 | 688 | while (offset < total) 689 | p[offset++] = 0; 690 | 691 | return p; 692 | } 693 | 694 | void __rfree(void *ptr, int size) 695 | { 696 | if (!ptr) 697 | return; 698 | __syscall(__syscall_munmap, ptr, size); 699 | } 700 | 701 | int __free_all(void) 702 | { 703 | if (!__freelist_head && !__alloc_head) 704 | return 0; 705 | 706 | chunk_t *cur = __freelist_head; 707 | chunk_t *rel; 708 | int size; 709 | 710 | /* release freelist */ 711 | while (cur->next) { 712 | rel = cur; 713 | cur = cur->next; 714 | rel->next = NULL; 715 | rel->prev = NULL; 716 | size = CHUNK_GET_SIZE(rel->size); 717 | __rfree(rel, size); 718 | } 719 | 720 | if (__alloc_head->next) { 721 | cur = __alloc_head->next; 722 | /* release chunks which not be free */ 723 | while (cur) { 724 | rel = cur; 725 | cur = cur->next; 726 | rel->next = NULL; 727 | rel->prev = NULL; 728 | size = CHUNK_GET_SIZE(rel->size); 729 | __rfree(rel, size); 730 | } 731 | } 732 | return 0; 733 | } 734 | 735 | void free(void *ptr) 736 | { 737 | if (!ptr) 738 | return; 739 | 740 | char *__ptr = ptr; 741 | chunk_t *cur = __ptr - sizeof(chunk_t); 742 | if (IS_CHUNK_GET_FREED(cur->size)) { 743 | printf("free(): double free detected\n"); 744 | abort(); 745 | } 746 | 747 | chunk_t *prev; 748 | if (cur->prev) { 749 | prev = cur->prev; 750 | prev->next = cur->next; 751 | } else 752 | __alloc_head = cur->next; 753 | 754 | if (cur->next) { 755 | chunk_t *next = cur->next; 756 | next->prev = cur->prev; 757 | } else { 758 | prev->next = NULL; 759 | __alloc_tail = prev; 760 | } 761 | 762 | /* Insert head in __freelist_head */ 763 | cur->next = __freelist_head; 764 | cur->prev = NULL; 765 | chunk_set_freed(cur); 766 | __freelist_head->prev = cur; 767 | __freelist_head = cur; 768 | } 769 | -------------------------------------------------------------------------------- /mk/arm.mk: -------------------------------------------------------------------------------- 1 | ARCH_NAME = armv7l 2 | ARCH_RUNNER = qemu-arm 3 | ARCH_DEFS = \ 4 | "/* target: ARM */\n$\ 5 | \#pragma once\n$\ 6 | \#define ARCH_PREDEFINED \"__arm__\" /* defined by GNU C and RealView */\n$\ 7 | \#define ELF_MACHINE 0x28 /* up to ARMv7/Aarch32 */\n$\ 8 | \#define ELF_FLAGS 0x5000200\n$\ 9 | " 10 | -------------------------------------------------------------------------------- /mk/common.mk: -------------------------------------------------------------------------------- 1 | UNAME_S := $(shell uname -s) 2 | ifeq ($(UNAME_S),Darwin) 3 | PRINTF = printf 4 | else 5 | PRINTF = env printf 6 | endif 7 | 8 | # Control the build verbosity 9 | ifeq ("$(VERBOSE)","1") 10 | Q := 11 | VECHO = @true 12 | REDIR = 13 | else 14 | Q := @ 15 | VECHO = @$(PRINTF) 16 | REDIR = >/dev/null 17 | endif 18 | 19 | # Test suite 20 | PASS_COLOR = \e[32;01m 21 | NO_COLOR = \e[0m 22 | 23 | pass = $(PRINTF) "$(PASS_COLOR)$1 Passed$(NO_COLOR)\n" 24 | 25 | # Check the prerequisites 26 | PREREQ_LIST := dot jq 27 | TARGET_EXEC ?= 28 | ifneq ($(HOST_ARCH),$(ARCH_NAME)) 29 | # Add qemu to the list if the host and target architectures differ 30 | PREREQ_LIST += $(ARCH_RUNNER) 31 | ifeq ($(filter $(ARCH_RUNNER),$(notdir $(shell which $(ARCH_RUNNER)))),) 32 | STAGE1_WARN_MSG := "Warning: failed to build the stage 1 and $\ 33 | stage 2 compilers due to missing $(ARCH_RUNNER)\n" 34 | STAGE1_CHECK_CMD := $(VECHO) $(STAGE1_WARN_MSG) && exit 1 35 | endif 36 | 37 | # Generate the path to the architecture-specific qemu 38 | TARGET_EXEC = $(shell which $(ARCH_RUNNER)) 39 | endif 40 | export TARGET_EXEC 41 | 42 | PREREQ_EXEC := $(shell which $(PREREQ_LIST)) 43 | PREREQ_MISSING := $(filter-out $(notdir $(PREREQ_EXEC)),$(PREREQ_LIST)) 44 | 45 | ifdef PREREQ_MISSING 46 | CONFIG_WARN_MSG := "Warning: missing packages: $(PREREQ_MISSING)\n$\ 47 | Warning: Please check package installation\n" 48 | CONFIG_CHECK_CMD := $(VECHO) $(CONFIG_WARN_MSG) 49 | endif 50 | -------------------------------------------------------------------------------- /mk/riscv.mk: -------------------------------------------------------------------------------- 1 | # Enforce the use qemu of by setting the ARCH_NAME variable to empty 2 | ARCH_NAME = 3 | ARCH_RUNNER = qemu-riscv32 4 | ARCH_DEFS = \ 5 | "/* target: RISCV */\n$\ 6 | \#pragma once\n$\ 7 | \#define ARCH_PREDEFINED \"__riscv\" /* Older versions of the GCC toolchain defined __riscv__ */\n$\ 8 | \#define ELF_MACHINE 0xf3\n$\ 9 | \#define ELF_FLAGS 0\n$\ 10 | " 11 | -------------------------------------------------------------------------------- /src/arm-codegen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * shecc - Self-Hosting and Educational C Compiler. 3 | * 4 | * shecc is freely redistributable under the BSD 2 clause license. See the 5 | * file "LICENSE" for information on usage and redistribution of this file. 6 | */ 7 | 8 | /* Translate IR to target machine code */ 9 | 10 | #include "arm.c" 11 | #include "defs.h" 12 | #include "globals.c" 13 | 14 | void update_elf_offset(ph2_ir_t *ph2_ir) 15 | { 16 | switch (ph2_ir->op) { 17 | case OP_load_constant: 18 | /* ARMv7 uses 12 bits to encode immediate value, but the higher 4 bits 19 | * are for rotation. See A5.2.4 "Modified immediate constants in ARM 20 | * instructions" in ARMv7-A manual. 21 | */ 22 | if (ph2_ir->src0 < 0) 23 | elf_offset += 12; 24 | else if (ph2_ir->src0 > 255) 25 | elf_offset += 8; 26 | else 27 | elf_offset += 4; 28 | return; 29 | case OP_address_of: 30 | case OP_global_address_of: 31 | /* ARMv7 uses 12 bits to encode immediate value, but the higher 4 bits 32 | * are for rotation. See A5.2.4 "Modified immediate constants in ARM 33 | * instructions" in ARMv7-A manual. 34 | */ 35 | if (ph2_ir->src0 > 255) 36 | elf_offset += 12; 37 | else if (ph2_ir->src0 >= 0) 38 | elf_offset += 4; 39 | else 40 | abort(); 41 | return; 42 | case OP_assign: 43 | if (ph2_ir->dest != ph2_ir->src0) 44 | elf_offset += 4; 45 | return; 46 | case OP_load: 47 | case OP_global_load: 48 | /* ARMv7 straight uses 12 bits to encode the offset of load instruction 49 | * (no rotation). 50 | */ 51 | if (ph2_ir->src0 > 4095) 52 | elf_offset += 16; 53 | else if (ph2_ir->src0 >= 0) 54 | elf_offset += 4; 55 | else 56 | abort(); 57 | return; 58 | case OP_store: 59 | case OP_global_store: 60 | /* ARMv7 straight uses 12 bits to encode the offset of store instruction 61 | * (no rotation). 62 | */ 63 | if (ph2_ir->src1 > 4095) 64 | elf_offset += 16; 65 | else if (ph2_ir->src1 >= 0) 66 | elf_offset += 4; 67 | else 68 | abort(); 69 | return; 70 | case OP_read: 71 | case OP_write: 72 | case OP_jump: 73 | case OP_call: 74 | case OP_load_func: 75 | case OP_indirect: 76 | case OP_add: 77 | case OP_sub: 78 | case OP_mul: 79 | case OP_lshift: 80 | case OP_rshift: 81 | case OP_bit_and: 82 | case OP_bit_or: 83 | case OP_bit_xor: 84 | case OP_negate: 85 | case OP_bit_not: 86 | elf_offset += 4; 87 | return; 88 | case OP_div: 89 | case OP_mod: 90 | if (hard_mul_div) { 91 | if (ph2_ir->op == OP_div) 92 | elf_offset += 4; 93 | else 94 | elf_offset += 12; 95 | return; 96 | } 97 | /* div/mod emulation's offset */ 98 | elf_offset += 116; 99 | return; 100 | case OP_load_data_address: 101 | elf_offset += 8; 102 | return; 103 | case OP_address_of_func: 104 | case OP_eq: 105 | case OP_neq: 106 | case OP_gt: 107 | case OP_lt: 108 | case OP_geq: 109 | case OP_leq: 110 | case OP_log_not: 111 | elf_offset += 12; 112 | return; 113 | case OP_branch: 114 | if (ph2_ir->is_branch_detached) 115 | elf_offset += 12; 116 | else 117 | elf_offset += 8; 118 | return; 119 | case OP_return: 120 | elf_offset += 24; 121 | return; 122 | case OP_trunc: 123 | elf_offset += 4; 124 | return; 125 | case OP_sign_ext: 126 | elf_offset += 4; 127 | return; 128 | default: 129 | fatal("Unknown opcode"); 130 | } 131 | } 132 | 133 | void cfg_flatten(void) 134 | { 135 | func_t *func = find_func("__syscall"); 136 | func->bbs->elf_offset = 44; /* offset of start + exit in codegen */ 137 | 138 | elf_offset = 80; /* offset of start + exit + syscall in codegen */ 139 | GLOBAL_FUNC->bbs->elf_offset = elf_offset; 140 | 141 | for (ph2_ir_t *ph2_ir = GLOBAL_FUNC->bbs->ph2_ir_list.head; ph2_ir; 142 | ph2_ir = ph2_ir->next) { 143 | update_elf_offset(ph2_ir); 144 | } 145 | 146 | /* prepare 'argc' and 'argv', then proceed to 'main' function */ 147 | elf_offset += 24; 148 | 149 | for (func = FUNC_LIST.head; func; func = func->next) { 150 | ph2_ir_t *flatten_ir; 151 | 152 | /* reserve stack */ 153 | flatten_ir = add_ph2_ir(OP_define); 154 | flatten_ir->src0 = func->stack_size; 155 | 156 | for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) { 157 | bb->elf_offset = elf_offset; 158 | 159 | if (bb == func->bbs) { 160 | /* save ra, sp */ 161 | elf_offset += 16; 162 | } 163 | 164 | for (ph2_ir_t *insn = bb->ph2_ir_list.head; insn; 165 | insn = insn->next) { 166 | flatten_ir = add_existed_ph2_ir(insn); 167 | 168 | if (insn->op == OP_return) { 169 | /* restore sp */ 170 | flatten_ir->src1 = bb->belong_to->stack_size; 171 | } 172 | 173 | if (insn->op == OP_branch) { 174 | /* In SSA, we index 'else_bb' first, and then 'then_bb' */ 175 | if (insn->else_bb != bb->rpo_next) 176 | flatten_ir->is_branch_detached = true; 177 | } 178 | 179 | update_elf_offset(flatten_ir); 180 | } 181 | } 182 | } 183 | } 184 | 185 | void emit(int code) 186 | { 187 | elf_write_int(elf_code, code); 188 | } 189 | 190 | void emit_ph2_ir(ph2_ir_t *ph2_ir) 191 | { 192 | func_t *func; 193 | int rd = ph2_ir->dest; 194 | int rn = ph2_ir->src0; 195 | int rm = ph2_ir->src1; 196 | int ofs; 197 | 198 | /* Prepare this variable to reuse the same code for 199 | * the instruction sequence of 200 | * 1. division and modulo. 201 | * 2. load and store operations. 202 | * 3. address-of operations. 203 | */ 204 | arm_reg interm; 205 | 206 | switch (ph2_ir->op) { 207 | case OP_define: 208 | emit(__sw(__AL, __lr, __sp, -4)); 209 | emit(__movw(__AL, __r8, ph2_ir->src0 + 4)); 210 | emit(__movt(__AL, __r8, ph2_ir->src0 + 4)); 211 | emit(__sub_r(__AL, __sp, __sp, __r8)); 212 | return; 213 | case OP_load_constant: 214 | if (ph2_ir->src0 < 0) { 215 | emit(__movw(__AL, __r8, -ph2_ir->src0)); 216 | emit(__movt(__AL, __r8, -ph2_ir->src0)); 217 | emit(__rsb_i(__AL, rd, 0, __r8)); 218 | } else if (ph2_ir->src0 > 255) { 219 | emit(__movw(__AL, rd, ph2_ir->src0)); 220 | emit(__movt(__AL, rd, ph2_ir->src0)); 221 | } else 222 | emit(__mov_i(__AL, rd, ph2_ir->src0)); 223 | return; 224 | case OP_address_of: 225 | case OP_global_address_of: 226 | interm = ph2_ir->op == OP_address_of ? __sp : __r12; 227 | if (ph2_ir->src0 > 255) { 228 | emit(__movw(__AL, __r8, ph2_ir->src0)); 229 | emit(__movt(__AL, __r8, ph2_ir->src0)); 230 | emit(__add_r(__AL, rd, interm, __r8)); 231 | } else 232 | emit(__add_i(__AL, rd, interm, ph2_ir->src0)); 233 | return; 234 | case OP_assign: 235 | emit(__mov_r(__AL, rd, rn)); 236 | return; 237 | case OP_load: 238 | case OP_global_load: 239 | interm = ph2_ir->op == OP_load ? __sp : __r12; 240 | if (ph2_ir->src0 > 4095) { 241 | emit(__movw(__AL, __r8, ph2_ir->src0)); 242 | emit(__movt(__AL, __r8, ph2_ir->src0)); 243 | emit(__add_r(__AL, __r8, interm, __r8)); 244 | emit(__lw(__AL, rd, __r8, 0)); 245 | } else 246 | emit(__lw(__AL, rd, interm, ph2_ir->src0)); 247 | return; 248 | case OP_store: 249 | case OP_global_store: 250 | interm = ph2_ir->op == OP_store ? __sp : __r12; 251 | if (ph2_ir->src1 > 4095) { 252 | emit(__movw(__AL, __r8, ph2_ir->src1)); 253 | emit(__movt(__AL, __r8, ph2_ir->src1)); 254 | emit(__add_r(__AL, __r8, interm, __r8)); 255 | emit(__sw(__AL, rn, __r8, 0)); 256 | } else 257 | emit(__sw(__AL, rn, interm, ph2_ir->src1)); 258 | return; 259 | case OP_read: 260 | if (ph2_ir->src1 == 1) 261 | emit(__lb(__AL, rd, rn, 0)); 262 | else if (ph2_ir->src1 == 4) 263 | emit(__lw(__AL, rd, rn, 0)); 264 | else 265 | abort(); 266 | return; 267 | case OP_write: 268 | if (ph2_ir->dest == 1) 269 | emit(__sb(__AL, rm, rn, 0)); 270 | else if (ph2_ir->dest == 4) 271 | emit(__sw(__AL, rm, rn, 0)); 272 | else 273 | abort(); 274 | return; 275 | case OP_branch: 276 | emit(__teq(rn)); 277 | if (ph2_ir->is_branch_detached) { 278 | emit(__b(__NE, 8)); 279 | emit(__b(__AL, ph2_ir->else_bb->elf_offset - elf_code->size)); 280 | } else 281 | emit(__b(__NE, ph2_ir->then_bb->elf_offset - elf_code->size)); 282 | return; 283 | case OP_jump: 284 | emit(__b(__AL, ph2_ir->next_bb->elf_offset - elf_code->size)); 285 | return; 286 | case OP_call: 287 | func = find_func(ph2_ir->func_name); 288 | emit(__bl(__AL, func->bbs->elf_offset - elf_code->size)); 289 | return; 290 | case OP_load_data_address: 291 | emit(__movw(__AL, rd, ph2_ir->src0 + elf_data_start)); 292 | emit(__movt(__AL, rd, ph2_ir->src0 + elf_data_start)); 293 | return; 294 | case OP_address_of_func: 295 | func = find_func(ph2_ir->func_name); 296 | ofs = elf_code_start + func->bbs->elf_offset; 297 | emit(__movw(__AL, __r8, ofs)); 298 | emit(__movt(__AL, __r8, ofs)); 299 | emit(__sw(__AL, __r8, rn, 0)); 300 | return; 301 | case OP_load_func: 302 | emit(__mov_r(__AL, __r8, rn)); 303 | return; 304 | case OP_indirect: 305 | emit(__blx(__AL, __r8)); 306 | return; 307 | case OP_return: 308 | if (ph2_ir->src0 == -1) 309 | emit(__mov_r(__AL, __r0, __r0)); 310 | else 311 | emit(__mov_r(__AL, __r0, rn)); 312 | emit(__movw(__AL, __r8, ph2_ir->src1 + 4)); 313 | emit(__movt(__AL, __r8, ph2_ir->src1 + 4)); 314 | emit(__add_r(__AL, __sp, __sp, __r8)); 315 | emit(__lw(__AL, __lr, __sp, -4)); 316 | emit(__blx(__AL, __lr)); 317 | return; 318 | case OP_add: 319 | emit(__add_r(__AL, rd, rn, rm)); 320 | return; 321 | case OP_sub: 322 | emit(__sub_r(__AL, rd, rn, rm)); 323 | return; 324 | case OP_mul: 325 | emit(__mul(__AL, rd, rn, rm)); 326 | return; 327 | case OP_div: 328 | case OP_mod: 329 | if (hard_mul_div) { 330 | if (ph2_ir->op == OP_div) 331 | emit(__div(__AL, rd, rm, rn)); 332 | else { 333 | emit(__div(__AL, __r8, rm, rn)); 334 | emit(__mul(__AL, __r8, rm, __r8)); 335 | emit(__sub_r(__AL, rd, rn, __r8)); 336 | } 337 | return; 338 | } 339 | interm = __r8; 340 | /* div/mod emulation */ 341 | /* Preserve the values of the dividend and divisor */ 342 | emit(__stmdb(__AL, 1, __sp, (1 << rn) | (1 << rm))); 343 | /* Obtain absolute values of the dividend and divisor */ 344 | emit(__srl_amt(__AL, 0, arith_rs, __r8, rn, 31)); 345 | emit(__add_r(__AL, rn, rn, __r8)); 346 | emit(__eor_r(__AL, rn, rn, __r8)); 347 | emit(__srl_amt(__AL, 0, arith_rs, __r9, rm, 31)); 348 | emit(__add_r(__AL, rm, rm, __r9)); 349 | emit(__eor_r(__AL, rm, rm, __r9)); 350 | if (ph2_ir->op == OP_div) 351 | emit(__eor_r(__AL, __r10, __r8, __r9)); 352 | else { 353 | /* If the requested operation is modulo, the result will be stored 354 | * in __r9. The sign of the divisor is irrelevant for determining 355 | * the result's sign. 356 | */ 357 | interm = __r9; 358 | emit(__mov_r(__AL, __r10, __r8)); 359 | } 360 | /* Unsigned integer division */ 361 | emit(__zero(__r8)); 362 | emit(__mov_i(__AL, __r9, 1)); 363 | emit(__cmp_i(__AL, rm, 0)); 364 | emit(__b(__EQ, 52)); 365 | emit(__cmp_i(__AL, rn, 0)); 366 | emit(__b(__EQ, 44)); 367 | emit(__cmp_r(__AL, rm, rn)); 368 | emit(__sll_amt(__CC, 0, logic_ls, rm, rm, 1)); 369 | emit(__sll_amt(__CC, 0, logic_ls, __r9, __r9, 1)); 370 | emit(__b(__CC, -12)); 371 | emit(__cmp_r(__AL, rn, rm)); 372 | emit(__sub_r(__CS, rn, rn, rm)); 373 | emit(__add_r(__CS, __r8, __r8, __r9)); 374 | emit(__srl_amt(__AL, 1, logic_rs, __r9, __r9, 1)); 375 | emit(__srl_amt(__CC, 0, logic_rs, rm, rm, 1)); 376 | emit(__b(__CC, -20)); 377 | /* After completing the emulation, the quotient and remainder 378 | * will be stored in __r8 and __r9, respectively. 379 | * 380 | * The original values of the dividend and divisor will be 381 | * restored in rn and rm. 382 | * 383 | * Finally, the result (quotient or remainder) will be stored 384 | * in rd. 385 | */ 386 | emit(__mov_r(__AL, __r9, rn)); 387 | emit(__ldm(__AL, 1, __sp, (1 << rn) | (1 << rm))); 388 | emit(__mov_r(__AL, rd, interm)); 389 | /* Handle the correct sign for the quotient or remainder */ 390 | emit(__cmp_i(__AL, __r10, 0)); 391 | emit(__rsb_i(__NE, rd, 0, rd)); 392 | return; 393 | case OP_lshift: 394 | emit(__sll(__AL, rd, rn, rm)); 395 | return; 396 | case OP_rshift: 397 | emit(__sra(__AL, rd, rn, rm)); 398 | return; 399 | case OP_eq: 400 | case OP_neq: 401 | case OP_gt: 402 | case OP_lt: 403 | case OP_geq: 404 | case OP_leq: 405 | emit(__cmp_r(__AL, rn, rm)); 406 | emit(__zero(rd)); 407 | emit(__mov_i(arm_get_cond(ph2_ir->op), rd, 1)); 408 | return; 409 | case OP_negate: 410 | emit(__rsb_i(__AL, rd, 0, rn)); 411 | return; 412 | case OP_bit_not: 413 | emit(__mvn_r(__AL, rd, rn)); 414 | return; 415 | case OP_bit_and: 416 | emit(__and_r(__AL, rd, rn, rm)); 417 | return; 418 | case OP_bit_or: 419 | emit(__or_r(__AL, rd, rn, rm)); 420 | return; 421 | case OP_bit_xor: 422 | emit(__eor_r(__AL, rd, rn, rm)); 423 | return; 424 | case OP_log_not: 425 | emit(__teq(rn)); 426 | emit(__mov_i(__NE, rd, 0)); 427 | emit(__mov_i(__EQ, rd, 1)); 428 | return; 429 | case OP_trunc: 430 | if (rm == 1) 431 | rm = 0xFF; 432 | else if (rm == 2) 433 | rm = 0xFFFF; 434 | else if (rm == 4) 435 | rm = 0xFFFFFFFF; 436 | else 437 | fatal("Unsupported truncation operation with invalid target size"); 438 | 439 | emit(__and_i(__AL, rd, rn, rm)); 440 | return; 441 | case OP_sign_ext: 442 | /* TODO: Allow to sign extends to other types */ 443 | emit(__sxtb(__AL, rd, rn, 0)); 444 | return; 445 | default: 446 | fatal("Unknown opcode"); 447 | } 448 | } 449 | 450 | void code_generate(void) 451 | { 452 | elf_data_start = elf_code_start + elf_offset; 453 | 454 | /* start */ 455 | emit(__movw(__AL, __r8, GLOBAL_FUNC->stack_size)); 456 | emit(__movt(__AL, __r8, GLOBAL_FUNC->stack_size)); 457 | emit(__sub_r(__AL, __sp, __sp, __r8)); 458 | emit(__mov_r(__AL, __r12, __sp)); 459 | emit(__bl(__AL, GLOBAL_FUNC->bbs->elf_offset - elf_code->size)); 460 | 461 | /* exit */ 462 | emit(__movw(__AL, __r8, GLOBAL_FUNC->stack_size)); 463 | emit(__movt(__AL, __r8, GLOBAL_FUNC->stack_size)); 464 | emit(__add_r(__AL, __sp, __sp, __r8)); 465 | emit(__mov_r(__AL, __r0, __r0)); 466 | emit(__mov_i(__AL, __r7, 1)); 467 | emit(__svc()); 468 | 469 | /* syscall */ 470 | emit(__mov_r(__AL, __r7, __r0)); 471 | emit(__mov_r(__AL, __r0, __r1)); 472 | emit(__mov_r(__AL, __r1, __r2)); 473 | emit(__mov_r(__AL, __r2, __r3)); 474 | emit(__mov_r(__AL, __r3, __r4)); 475 | emit(__mov_r(__AL, __r4, __r5)); 476 | emit(__mov_r(__AL, __r5, __r6)); 477 | emit(__svc()); 478 | emit(__mov_r(__AL, __pc, __lr)); 479 | 480 | ph2_ir_t *ph2_ir; 481 | for (ph2_ir = GLOBAL_FUNC->bbs->ph2_ir_list.head; ph2_ir; 482 | ph2_ir = ph2_ir->next) 483 | emit_ph2_ir(ph2_ir); 484 | 485 | /* prepare 'argc' and 'argv', then proceed to 'main' function */ 486 | emit(__movw(__AL, __r8, GLOBAL_FUNC->stack_size)); 487 | emit(__movt(__AL, __r8, GLOBAL_FUNC->stack_size)); 488 | emit(__add_r(__AL, __r8, __r12, __r8)); 489 | emit(__lw(__AL, __r0, __r8, 0)); 490 | emit(__add_i(__AL, __r1, __r8, 4)); 491 | emit(__b(__AL, MAIN_BB->elf_offset - elf_code->size)); 492 | 493 | for (int i = 0; i < ph2_ir_idx; i++) { 494 | ph2_ir = PH2_IR_FLATTEN[i]; 495 | emit_ph2_ir(ph2_ir); 496 | } 497 | } 498 | -------------------------------------------------------------------------------- /src/arm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * shecc - Self-Hosting and Educational C Compiler. 3 | * 4 | * shecc is freely redistributable under the BSD 2 clause license. See the 5 | * file "LICENSE" for information on usage and redistribution of this file. 6 | */ 7 | 8 | /* ARMv7-A instruction encoding */ 9 | 10 | /* Identifier naming conventions 11 | * - prefix arm_ : Arm instruction encoding. 12 | * - prefix __ : mnemonic symbols for Arm instruction, condition code, 13 | * registers, etc. 14 | * 15 | * An example of usage in src/codegen.c: (unconditional jump) 16 | * 17 | * +---------------- write specified instruction into ELF 18 | * | 19 | * emit(__b(__AL, ofs)); 20 | * | | | 21 | * | | +--- to PC-relative expression 22 | * | +------- always 23 | * +------------ branch 24 | * 25 | * Machine-level "b" instructions have restricted ranges from the address of 26 | * the current instruction. 27 | */ 28 | 29 | #include "defs.h" 30 | 31 | /* opcode */ 32 | typedef enum { 33 | arm_and = 0, 34 | arm_eor = 1, 35 | arm_sub = 2, 36 | arm_rsb = 3, 37 | arm_add = 4, 38 | arm_ldm = 9, 39 | arm_teq = 9, 40 | arm_cmp = 10, 41 | arm_orr = 12, 42 | arm_mov = 13, 43 | arm_mvn = 15, 44 | arm_stmdb = 16 45 | } arm_op_t; 46 | 47 | /* Condition code 48 | * Reference: 49 | * https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/condition-codes-1-condition-flags-and-codes 50 | */ 51 | typedef enum { 52 | __EQ = 0, /* Equal */ 53 | __NE = 1, /* Not equal */ 54 | __CS = 2, /* Unsigned higher or same */ 55 | __CC = 3, /* Unsigned lower */ 56 | __LS = 9, /* Unsigned lower or same */ 57 | __GE = 10, /* Signed greater than or equal */ 58 | __LT = 11, /* Signed less than */ 59 | __GT = 12, /* Signed greater than */ 60 | __LE = 13, /* Signed less than or equal */ 61 | __AL = 14 /* Always executed */ 62 | } arm_cond_t; 63 | 64 | /* Registers */ 65 | typedef enum { 66 | __r0 = 0, 67 | __r1 = 1, 68 | __r2 = 2, 69 | __r3 = 3, 70 | __r4 = 4, 71 | __r5 = 5, 72 | __r6 = 6, 73 | __r7 = 7, 74 | __r8 = 8, 75 | __r9 = 9, 76 | __r10 = 10, 77 | __r11 = 11, 78 | __r12 = 12, 79 | __sp = 13, /* stack pointer, r13 */ 80 | __lr = 14, /* link register, r14 */ 81 | __pc = 15 /* program counter, r15 */ 82 | } arm_reg; 83 | 84 | typedef enum { 85 | logic_ls = 0, /* Logical left shift */ 86 | logic_rs = 1, /* Logical right shift */ 87 | arith_rs = 2, /* Arithmetic right shift */ 88 | rotat_rs = 3 /* Rotate right shift */ 89 | } shift_type; 90 | 91 | arm_cond_t arm_get_cond(opcode_t op) 92 | { 93 | switch (op) { 94 | case OP_eq: 95 | return __EQ; 96 | case OP_neq: 97 | return __NE; 98 | case OP_lt: 99 | return __LT; 100 | case OP_geq: 101 | return __GE; 102 | case OP_gt: 103 | return __GT; 104 | case OP_leq: 105 | return __LE; 106 | default: 107 | error("Unsupported condition IR opcode"); 108 | } 109 | return __AL; 110 | } 111 | 112 | int arm_extract_bits(int imm, int i_start, int i_end, int d_start, int d_end) 113 | { 114 | if (((d_end - d_start) != (i_end - i_start)) || (i_start > i_end) || 115 | (d_start > d_end)) 116 | error("Invalid bit copy"); 117 | 118 | int v = imm >> i_start; 119 | v &= ((2 << (i_end - i_start)) - 1); 120 | v <<= d_start; 121 | return v; 122 | } 123 | 124 | int arm_encode(arm_cond_t cond, int opcode, int rn, int rd, int op2) 125 | { 126 | return (cond << 28) + (opcode << 20) + (rn << 16) + (rd << 12) + op2; 127 | } 128 | 129 | int __svc(void) 130 | { 131 | return arm_encode(__AL, 240, 0, 0, 0); 132 | } 133 | 134 | int __mov(arm_cond_t cond, int io, int opcode, int s, int rn, int rd, int op2) 135 | { 136 | int shift = 0; 137 | if (op2 > 255) { 138 | shift = 16; /* full rotation */ 139 | while ((op2 & 3) == 0) { 140 | /* we can shift by two bits */ 141 | op2 >>= 2; 142 | shift -= 1; 143 | } 144 | if (op2 > 255) 145 | /* value spans more than 8 bits */ 146 | error("Unable to represent value"); 147 | } 148 | return arm_encode(cond, s + (opcode << 1) + (io << 5), rn, rd, 149 | (shift << 8) + (op2 & 255)); 150 | } 151 | 152 | int __and_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg rm) 153 | { 154 | return __mov(cond, 0, arm_and, 0, rs, rd, rm); 155 | } 156 | 157 | int __or_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg rm) 158 | { 159 | return __mov(cond, 0, arm_orr, 0, rs, rd, rm); 160 | } 161 | 162 | int __eor_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg rm) 163 | { 164 | return __mov(cond, 0, arm_eor, 0, rs, rd, rm); 165 | } 166 | 167 | int __mvn_r(arm_cond_t cond, arm_reg rd, arm_reg rm) 168 | { 169 | return __mov(cond, 0, arm_mvn, 0, 0, rd, rm); 170 | } 171 | 172 | int __movw(arm_cond_t cond, arm_reg rd, int imm) 173 | { 174 | return arm_encode(cond, 48, 0, rd, 0) + 175 | arm_extract_bits(imm, 0, 11, 0, 11) + 176 | arm_extract_bits(imm, 12, 15, 16, 19); 177 | } 178 | 179 | int __movt(arm_cond_t cond, arm_reg rd, int imm) 180 | { 181 | imm >>= 16; 182 | return arm_encode(cond, 52, 0, rd, 0) + 183 | arm_extract_bits(imm, 0, 11, 0, 11) + 184 | arm_extract_bits(imm, 12, 15, 16, 19); 185 | } 186 | 187 | int __mov_i(arm_cond_t cond, arm_reg rd, int imm) 188 | { 189 | return __mov(cond, 1, arm_mov, 0, 0, rd, imm); 190 | } 191 | 192 | int __mov_r(arm_cond_t cond, arm_reg rd, arm_reg rs) 193 | { 194 | return __mov(cond, 0, arm_mov, 0, 0, rd, rs); 195 | } 196 | 197 | int __srl(arm_cond_t cond, arm_reg rd, arm_reg rm, arm_reg rs) 198 | { 199 | return arm_encode(cond, 0 + (arm_mov << 1) + (0 << 5), 0, rd, 200 | rm + (1 << 4) + (1 << 5) + (rs << 8)); 201 | } 202 | 203 | int __srl_amt(arm_cond_t cond, 204 | int s, 205 | shift_type shift, 206 | arm_reg rd, 207 | arm_reg rm, 208 | int amt) 209 | { 210 | return arm_encode(cond, s + (arm_mov << 1) + (0 << 5), 0, rd, 211 | rm + (0 << 4) + (shift << 5) + (amt << 7)); 212 | } 213 | 214 | int __sll(arm_cond_t cond, arm_reg rd, arm_reg rm, arm_reg rs) 215 | { 216 | return arm_encode(cond, 0 + (arm_mov << 1) + (0 << 5), 0, rd, 217 | rm + (1 << 4) + (0 << 5) + (rs << 8)); 218 | } 219 | 220 | int __sll_amt(arm_cond_t cond, 221 | int s, 222 | shift_type shift, 223 | arm_reg rd, 224 | arm_reg rm, 225 | int amt) 226 | { 227 | return arm_encode(cond, s + (arm_mov << 1) + (0 << 5), 0, rd, 228 | rm + (0 << 4) + (shift << 5) + (amt << 7)); 229 | } 230 | 231 | int __sra(arm_cond_t cond, arm_reg rd, arm_reg rm, arm_reg rs) 232 | { 233 | return arm_encode(cond, 0 + (arm_mov << 1) + (0 << 5), 0, rd, 234 | rm + (5 << 4) + (rs << 8)); 235 | } 236 | 237 | int __add_i(arm_cond_t cond, arm_reg rd, arm_reg rs, int imm) 238 | { 239 | if (imm >= 0) 240 | return __mov(cond, 1, arm_add, 0, rs, rd, imm); 241 | return __mov(cond, 1, arm_sub, 0, rs, rd, -imm); 242 | } 243 | 244 | int __add_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg ro) 245 | { 246 | return __mov(cond, 0, arm_add, 0, rs, rd, ro); 247 | } 248 | 249 | int __sub_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg ro) 250 | { 251 | return __mov(cond, 0, arm_sub, 0, rs, rd, ro); 252 | } 253 | 254 | int __and_i(arm_cond_t cond, arm_reg rd, arm_reg rs, int imm) 255 | { 256 | return __mov(cond, 1, arm_and, 0, rs, rd, imm); 257 | } 258 | 259 | int __zero(int rd) 260 | { 261 | return __mov_i(__AL, rd, 0); 262 | } 263 | 264 | int arm_transfer(arm_cond_t cond, 265 | int l, 266 | int size, 267 | arm_reg rn, 268 | arm_reg rd, 269 | int ofs) 270 | { 271 | int opcode = 64 + 16 + 8 + l; 272 | if (size == 1) 273 | opcode += 4; 274 | if (ofs < 0) { 275 | opcode -= 8; 276 | ofs = -ofs; 277 | } 278 | return arm_encode(cond, opcode, rn, rd, ofs & 4095); 279 | } 280 | 281 | int __lw(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs) 282 | { 283 | return arm_transfer(cond, 1, 4, rn, rd, ofs); 284 | } 285 | 286 | int __lb(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs) 287 | { 288 | return arm_transfer(cond, 1, 1, rn, rd, ofs); 289 | } 290 | 291 | int __sw(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs) 292 | { 293 | return arm_transfer(cond, 0, 4, rn, rd, ofs); 294 | } 295 | 296 | int __sb(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs) 297 | { 298 | return arm_transfer(cond, 0, 1, rn, rd, ofs); 299 | } 300 | 301 | int __stmdb(arm_cond_t cond, int w, arm_reg rn, int reg_list) 302 | { 303 | return arm_encode(cond, arm_stmdb + (0x2 << 6) + (w << 1), rn, 0, reg_list); 304 | } 305 | 306 | int __ldm(arm_cond_t cond, int w, arm_reg rn, int reg_list) 307 | { 308 | return arm_encode(cond, arm_ldm + (0x2 << 6) + (w << 1), rn, 0, reg_list); 309 | } 310 | 311 | int __b(arm_cond_t cond, int ofs) 312 | { 313 | int o = (ofs - 8) >> 2; 314 | return arm_encode(cond, 160, 0, 0, 0) + (o & 16777215); 315 | } 316 | 317 | int __bl(arm_cond_t cond, int ofs) 318 | { 319 | int o = (ofs - 8) >> 2; 320 | return arm_encode(cond, 176, 0, 0, 0) + (o & 16777215); 321 | } 322 | 323 | int __blx(arm_cond_t cond, arm_reg rd) 324 | { 325 | return arm_encode(cond, 18, 15, 15, rd + 3888); 326 | } 327 | 328 | int __mul(arm_cond_t cond, arm_reg rd, arm_reg r1, arm_reg r2) 329 | { 330 | return arm_encode(cond, 0, rd, 0, (r1 << 8) + 144 + r2); 331 | } 332 | 333 | int __div(arm_cond_t cond, arm_reg rd, arm_reg r1, arm_reg r2) 334 | { 335 | return arm_encode(cond, 113, rd, 15, (r1 << 8) + 16 + r2); 336 | } 337 | 338 | int __rsb_i(arm_cond_t cond, arm_reg rd, int imm, arm_reg rn) 339 | { 340 | return __mov(cond, 1, arm_rsb, 0, rn, rd, imm); 341 | } 342 | 343 | int __cmp_r(arm_cond_t cond, arm_reg r1, arm_reg r2) 344 | { 345 | return __mov(cond, 0, arm_cmp, 1, r1, 0, r2); 346 | } 347 | 348 | int __cmp_i(arm_cond_t cond, arm_reg rn, int imm) 349 | { 350 | return __mov(cond, 1, arm_cmp, 1, rn, 0, imm); 351 | } 352 | 353 | int __teq(arm_reg rd) 354 | { 355 | return __mov(__AL, 1, arm_teq, 1, rd, 0, 0); 356 | } 357 | 358 | int __sxtb(arm_cond_t cond, arm_reg rd, arm_reg rm, int rotation) 359 | { 360 | if (rotation != 0 && rotation != 8 && rotation != 16 && rotation != 24) 361 | fatal("SXTB rotation must be 0, 8, 16, or 24"); 362 | 363 | return arm_encode(cond, 106, 0xF, rd, 364 | rm | ((rotation >> 3) << 10) | (0x7 << 4)); 365 | } 366 | -------------------------------------------------------------------------------- /src/defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * shecc - Self-Hosting and Educational C Compiler. 3 | * 4 | * shecc is freely redistributable under the BSD 2 clause license. See the 5 | * file "LICENSE" for information on usage and redistribution of this file. 6 | */ 7 | 8 | #pragma once 9 | #include 10 | 11 | /* definitions */ 12 | 13 | /* Limitations */ 14 | #define MAX_TOKEN_LEN 256 15 | #define MAX_ID_LEN 64 16 | #define MAX_LINE_LEN 256 17 | #define MAX_VAR_LEN 32 18 | #define MAX_TYPE_LEN 32 19 | #define MAX_PARAMS 8 20 | #define MAX_LOCALS 1600 21 | #define MAX_FIELDS 64 22 | #define MAX_TYPES 64 23 | #define MAX_IR_INSTR 60000 24 | #define MAX_BB_PRED 128 25 | #define MAX_BB_DOM_SUCC 64 26 | #define MAX_BB_RDOM_SUCC 256 27 | #define MAX_GLOBAL_IR 256 28 | #define MAX_SOURCE 524288 29 | #define MAX_CODE 262144 30 | #define MAX_DATA 262144 31 | #define MAX_SYMTAB 65536 32 | #define MAX_STRTAB 65536 33 | #define MAX_HEADER 1024 34 | #define MAX_SECTION 1024 35 | #define MAX_ALIASES 128 36 | #define MAX_CONSTANTS 1024 37 | #define MAX_CASES 128 38 | #define MAX_NESTING 128 39 | #define MAX_OPERAND_STACK_SIZE 32 40 | #define MAX_ANALYSIS_STACK_SIZE 800 41 | 42 | /* Default capacities for common data structures */ 43 | /* Default arena size is initialized with 256 KiB */ 44 | #define DEFAULT_ARENA_SIZE 262144 45 | #define DEFAULT_FUNCS_SIZE 64 46 | #define DEFAULT_INCLUSIONS_SIZE 16 47 | 48 | #define ELF_START 0x10000 49 | #define PTR_SIZE 4 50 | 51 | /* Number of the available registers. Either 7 or 8 is accepted now. */ 52 | #define REG_CNT 8 53 | 54 | /* This macro will be automatically defined at shecc run-time. */ 55 | #ifdef __SHECC__ 56 | /* use do-while as a substitution for nop */ 57 | #define UNUSED(x) \ 58 | do { \ 59 | ; \ 60 | } while (0) 61 | #define HOST_PTR_SIZE 4 62 | #else 63 | /* suppress GCC/Clang warnings */ 64 | #define UNUSED(x) (void) (x) 65 | /* configure host data model when using 'memcpy'. */ 66 | #define HOST_PTR_SIZE __SIZEOF_POINTER__ 67 | #endif 68 | 69 | /* Common data structures */ 70 | typedef struct arena_block { 71 | char *memory; 72 | int capacity; 73 | int offset; 74 | struct arena_block *next; 75 | } arena_block_t; 76 | 77 | typedef struct { 78 | arena_block_t *head; 79 | } arena_t; 80 | 81 | /* string-based hash map definitions */ 82 | 83 | typedef struct hashmap_node { 84 | char *key; 85 | void *val; 86 | struct hashmap_node *next; 87 | } hashmap_node_t; 88 | 89 | typedef struct { 90 | int size; 91 | int cap; 92 | hashmap_node_t **buckets; 93 | } hashmap_t; 94 | 95 | /* lexer tokens */ 96 | typedef enum { 97 | T_start, /* FIXME: it was intended to start the state machine. */ 98 | T_numeric, 99 | T_identifier, 100 | T_comma, /* , */ 101 | T_string, /* null-terminated string */ 102 | T_char, 103 | T_open_bracket, /* ( */ 104 | T_close_bracket, /* ) */ 105 | T_open_curly, /* { */ 106 | T_close_curly, /* } */ 107 | T_open_square, /* [ */ 108 | T_close_square, /* ] */ 109 | T_asterisk, /* '*' */ 110 | T_divide, /* / */ 111 | T_mod, /* % */ 112 | T_bit_or, /* | */ 113 | T_bit_xor, /* ^ */ 114 | T_bit_not, /* ~ */ 115 | T_log_and, /* && */ 116 | T_log_or, /* || */ 117 | T_log_not, /* ! */ 118 | T_lt, /* < */ 119 | T_gt, /* > */ 120 | T_le, /* <= */ 121 | T_ge, /* >= */ 122 | T_lshift, /* << */ 123 | T_rshift, /* >> */ 124 | T_dot, /* . */ 125 | T_arrow, /* -> */ 126 | T_plus, /* + */ 127 | T_minus, /* - */ 128 | T_minuseq, /* -= */ 129 | T_pluseq, /* += */ 130 | T_asteriskeq, /* *= */ 131 | T_divideeq, /* /= */ 132 | T_modeq, /* %= */ 133 | T_lshifteq, /* <<= */ 134 | T_rshifteq, /* >>= */ 135 | T_xoreq, /* ^= */ 136 | T_oreq, /* |= */ 137 | T_andeq, /* &= */ 138 | T_eq, /* == */ 139 | T_noteq, /* != */ 140 | T_assign, /* = */ 141 | T_increment, /* ++ */ 142 | T_decrement, /* -- */ 143 | T_question, /* ? */ 144 | T_colon, /* : */ 145 | T_semicolon, /* ; */ 146 | T_eof, /* end-of-file (EOF) */ 147 | T_ampersand, /* & */ 148 | T_return, 149 | T_if, 150 | T_else, 151 | T_while, 152 | T_for, 153 | T_do, 154 | T_typedef, 155 | T_enum, 156 | T_struct, 157 | T_sizeof, 158 | T_elipsis, /* ... */ 159 | T_switch, 160 | T_case, 161 | T_break, 162 | T_default, 163 | T_continue, 164 | /* C pre-processor directives */ 165 | T_cppd_include, 166 | T_cppd_define, 167 | T_cppd_undef, 168 | T_cppd_error, 169 | T_cppd_if, 170 | T_cppd_elif, 171 | T_cppd_else, 172 | T_cppd_endif, 173 | T_cppd_ifdef, 174 | T_cppd_ifndef, 175 | T_cppd_pragma 176 | } token_t; 177 | 178 | /* builtin types */ 179 | typedef enum { 180 | TYPE_void = 0, 181 | TYPE_int, 182 | TYPE_char, 183 | TYPE_struct, 184 | TYPE_typedef 185 | } base_type_t; 186 | 187 | /* IR opcode */ 188 | typedef enum { 189 | /* intermediate use in front-end. No code generation */ 190 | OP_generic, 191 | 192 | OP_phi, 193 | OP_unwound_phi, /* work like address_of + store */ 194 | 195 | /* calling convention */ 196 | OP_define, /* function entry point */ 197 | OP_push, /* prepare arguments */ 198 | OP_call, /* function call */ 199 | OP_indirect, /* indirect call with function pointer */ 200 | OP_return, /* explicit return */ 201 | 202 | OP_allocat, /* allocate space on stack */ 203 | OP_assign, 204 | OP_load_constant, /* load constant */ 205 | OP_load_data_address, /* lookup address of a constant in data section */ 206 | 207 | /* control flow */ 208 | OP_branch, /* conditional jump */ 209 | OP_jump, /* unconditional jump */ 210 | OP_func_ret, /* returned value */ 211 | 212 | /* function pointer */ 213 | OP_address_of_func, /* resolve function entry */ 214 | OP_load_func, /* prepare indirective call */ 215 | OP_global_load_func, 216 | 217 | /* memory address operations */ 218 | OP_address_of, /* lookup variable's address */ 219 | OP_global_address_of, 220 | OP_load, /* load a word from stack */ 221 | OP_global_load, 222 | OP_store, /* store a word to stack */ 223 | OP_global_store, 224 | OP_read, /* read from memory address */ 225 | OP_write, /* write to memory address */ 226 | 227 | /* arithmetic operators */ 228 | OP_add, 229 | OP_sub, 230 | OP_mul, 231 | OP_div, /* signed division */ 232 | OP_mod, /* modulo */ 233 | OP_ternary, /* ? : */ 234 | OP_lshift, 235 | OP_rshift, 236 | OP_log_and, 237 | OP_log_or, 238 | OP_log_not, 239 | OP_eq, /* equal */ 240 | OP_neq, /* not equal */ 241 | OP_lt, /* less than */ 242 | OP_leq, /* less than or equal */ 243 | OP_gt, /* greater than */ 244 | OP_geq, /* greater than or equal */ 245 | OP_bit_or, 246 | OP_bit_and, 247 | OP_bit_xor, 248 | OP_bit_not, 249 | OP_negate, 250 | 251 | /* data type conversion */ 252 | OP_trunc, 253 | OP_sign_ext, 254 | 255 | /* entry point of the state machine */ 256 | OP_start 257 | } opcode_t; 258 | 259 | /* variable definition */ 260 | typedef struct { 261 | int counter; 262 | int stack[64]; 263 | int stack_idx; 264 | } rename_t; 265 | 266 | typedef struct ref_block ref_block_t; 267 | 268 | struct ref_block_list { 269 | ref_block_t *head; 270 | ref_block_t *tail; 271 | }; 272 | 273 | typedef struct ref_block_list ref_block_list_t; 274 | 275 | typedef struct insn insn_t; 276 | 277 | typedef struct use_chain_node { 278 | insn_t *insn; 279 | struct use_chain_node *next; 280 | struct use_chain_node *prev; 281 | } use_chain_t; 282 | 283 | typedef struct var var_t; 284 | typedef struct type type_t; 285 | 286 | typedef struct var_list { 287 | int capacity; 288 | int size; 289 | var_t **elements; 290 | } var_list_t; 291 | 292 | struct var { 293 | type_t *type; 294 | char var_name[MAX_VAR_LEN]; 295 | int is_ptr; 296 | bool is_func; 297 | bool is_global; 298 | int array_size; 299 | int offset; /* offset from stack or frame, index 0 is reserved */ 300 | int init_val; /* for global initialization */ 301 | int liveness; /* live range */ 302 | int in_loop; 303 | struct var *base; 304 | int subscript; 305 | struct var *subscripts[64]; 306 | int subscripts_idx; 307 | rename_t rename; 308 | ref_block_list_t ref_block_list; /* blocks which kill variable */ 309 | use_chain_t *users_head; 310 | use_chain_t *users_tail; 311 | struct insn *last_assign; 312 | int consumed; 313 | bool is_ternary_ret; 314 | bool is_logical_ret; 315 | bool is_const; /* whether a constant representaion or not */ 316 | }; 317 | 318 | typedef struct { 319 | char name[MAX_VAR_LEN]; 320 | bool is_variadic; 321 | int start_source_idx; 322 | var_t param_defs[MAX_PARAMS]; 323 | int num_param_defs; 324 | int params[MAX_PARAMS]; 325 | int num_params; 326 | bool disabled; 327 | } macro_t; 328 | 329 | typedef struct func func_t; 330 | 331 | /* block definition */ 332 | struct block { 333 | var_list_t locals; 334 | struct block *parent; 335 | func_t *func; 336 | macro_t *macro; 337 | struct block *next; 338 | }; 339 | 340 | typedef struct block block_t; 341 | typedef struct basic_block basic_block_t; 342 | 343 | /* Definition of a growable buffer for a mutable null-terminated string 344 | * size: Current number of elements in the array 345 | * capacity: Number of elements that can be stored without resizing 346 | * elements: Pointer to the array of characters 347 | */ 348 | typedef struct { 349 | int size; 350 | int capacity; 351 | char *elements; 352 | } strbuf_t; 353 | 354 | /* phase-2 IR definition */ 355 | struct ph2_ir { 356 | opcode_t op; 357 | int src0; 358 | int src1; 359 | int dest; 360 | char func_name[MAX_VAR_LEN]; 361 | basic_block_t *next_bb; 362 | basic_block_t *then_bb; 363 | basic_block_t *else_bb; 364 | struct ph2_ir *next; 365 | bool is_branch_detached; 366 | }; 367 | 368 | typedef struct ph2_ir ph2_ir_t; 369 | 370 | /* type definition */ 371 | struct type { 372 | char type_name[MAX_TYPE_LEN]; 373 | base_type_t base_type; 374 | struct type *base_struct; 375 | int size; 376 | var_t fields[MAX_FIELDS]; 377 | int num_fields; 378 | }; 379 | 380 | /* lvalue details */ 381 | typedef struct { 382 | int size; 383 | int is_ptr; 384 | bool is_func; 385 | bool is_reference; 386 | type_t *type; 387 | } lvalue_t; 388 | 389 | /* alias for #defines */ 390 | typedef struct { 391 | char alias[MAX_VAR_LEN]; 392 | char value[MAX_VAR_LEN]; 393 | bool disabled; 394 | } alias_t; 395 | 396 | /* constants for enums */ 397 | typedef struct { 398 | char alias[MAX_VAR_LEN]; 399 | int value; 400 | } constant_t; 401 | 402 | struct phi_operand { 403 | var_t *var; 404 | basic_block_t *from; 405 | struct phi_operand *next; 406 | }; 407 | 408 | typedef struct phi_operand phi_operand_t; 409 | 410 | struct insn { 411 | struct insn *next; 412 | struct insn *prev; 413 | int idx; 414 | opcode_t opcode; 415 | var_t *rd; 416 | var_t *rs1; 417 | var_t *rs2; 418 | int sz; 419 | bool useful; /* Used in DCE process. Set true if instruction is useful. */ 420 | basic_block_t *belong_to; 421 | phi_operand_t *phi_ops; 422 | char str[64]; 423 | }; 424 | 425 | typedef struct { 426 | insn_t *head; 427 | insn_t *tail; 428 | } insn_list_t; 429 | 430 | typedef struct { 431 | ph2_ir_t *head; 432 | ph2_ir_t *tail; 433 | } ph2_ir_list_t; 434 | 435 | typedef enum { NEXT, ELSE, THEN } bb_connection_type_t; 436 | 437 | typedef struct { 438 | basic_block_t *bb; 439 | bb_connection_type_t type; 440 | } bb_connection_t; 441 | 442 | struct symbol { 443 | var_t *var; 444 | int index; 445 | struct symbol *next; 446 | }; 447 | 448 | typedef struct symbol symbol_t; 449 | 450 | typedef struct { 451 | symbol_t *head; 452 | symbol_t *tail; 453 | } symbol_list_t; 454 | 455 | struct basic_block { 456 | insn_list_t insn_list; 457 | ph2_ir_list_t ph2_ir_list; 458 | bb_connection_t prev[MAX_BB_PRED]; 459 | char bb_label_name[MAX_VAR_LEN]; /* Used in instruction dumping when ir_dump 460 | is enabled. */ 461 | struct basic_block *next; /* normal BB */ 462 | struct basic_block *then_; /* conditional BB */ 463 | struct basic_block *else_; 464 | struct basic_block *idom; 465 | struct basic_block *r_idom; 466 | struct basic_block *rpo_next; 467 | struct basic_block *rpo_r_next; 468 | var_t *live_gen[MAX_ANALYSIS_STACK_SIZE]; 469 | int live_gen_idx; 470 | var_t *live_kill[MAX_ANALYSIS_STACK_SIZE]; 471 | int live_kill_idx; 472 | var_t *live_in[MAX_ANALYSIS_STACK_SIZE]; 473 | int live_in_idx; 474 | var_t *live_out[MAX_ANALYSIS_STACK_SIZE]; 475 | int live_out_idx; 476 | int rpo; 477 | int rpo_r; 478 | struct basic_block *DF[64]; 479 | struct basic_block *RDF[64]; 480 | int df_idx; 481 | int rdf_idx; 482 | int visited; 483 | bool useful; /* indicate whether this BB contains useful instructions */ 484 | struct basic_block *dom_next[64]; 485 | struct basic_block *dom_prev; 486 | struct basic_block *rdom_next[256]; 487 | struct basic_block *rdom_prev; 488 | func_t *belong_to; 489 | block_t *scope; 490 | symbol_list_t symbol_list; /* variable declaration */ 491 | int elf_offset; 492 | }; 493 | 494 | struct ref_block { 495 | basic_block_t *bb; 496 | struct ref_block *next; 497 | }; 498 | 499 | /** 500 | * Syntatic representation of func, combines syntactic details 501 | * (e.g., return type, parameters) with SSA-related information 502 | * (e.g., basic blocks, control flow) to support parsing, 503 | * analysis, optimization, and code generation. 504 | */ 505 | struct func { 506 | /* Syntatic info */ 507 | var_t return_def; 508 | var_t param_defs[MAX_PARAMS]; 509 | int num_params; 510 | int va_args; 511 | int stack_size; /* stack always starts at offset 4 for convenience */ 512 | 513 | /* SSA info */ 514 | basic_block_t *bbs; 515 | basic_block_t *exit; 516 | symbol_list_t global_sym_list; 517 | int bb_cnt; 518 | int visited; 519 | 520 | struct func *next; 521 | }; 522 | 523 | typedef struct { 524 | func_t *head; 525 | func_t *tail; 526 | } func_list_t; 527 | 528 | typedef struct { 529 | func_t *func; 530 | basic_block_t *bb; 531 | void (*preorder_cb)(func_t *, basic_block_t *); 532 | void (*postorder_cb)(func_t *, basic_block_t *); 533 | } bb_traversal_args_t; 534 | 535 | typedef struct { 536 | var_t *var; 537 | int polluted; 538 | } regfile_t; 539 | 540 | /* FIXME: replace char[2] with a short data type in ELF header structures */ 541 | /* ELF header */ 542 | typedef struct { 543 | char e_ident[16]; 544 | char e_type[2]; 545 | char e_machine[2]; 546 | int e_version; 547 | int e_entry; 548 | int e_phoff; 549 | int e_shoff; 550 | int e_flags; 551 | char e_ehsize[2]; 552 | char e_phentsize[2]; 553 | char e_phnum[2]; 554 | char e_shentsize[2]; 555 | char e_shnum[2]; 556 | char e_shstrndx[2]; 557 | } elf32_hdr_t; 558 | 559 | /* ELF program header */ 560 | typedef struct { 561 | int p_type; 562 | int p_offset; 563 | int p_vaddr; 564 | int p_paddr; 565 | int p_filesz; 566 | int p_memsz; 567 | int p_flags; 568 | int p_align; 569 | } elf32_phdr_t; 570 | 571 | /* ELF section header */ 572 | typedef struct { 573 | int sh_name; 574 | int sh_type; 575 | int sh_flags; 576 | int sh_addr; 577 | int sh_offset; 578 | int sh_size; 579 | int sh_link; 580 | int sh_info; 581 | int sh_addralign; 582 | int sh_entsize; 583 | } elf32_shdr_t; 584 | -------------------------------------------------------------------------------- /src/elf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * shecc - Self-Hosting and Educational C Compiler. 3 | * 4 | * shecc is freely redistributable under the BSD 2 clause license. See the 5 | * file "LICENSE" for information on usage and redistribution of this file. 6 | */ 7 | 8 | /* ELF file manipulation */ 9 | 10 | #include "../config" 11 | #include "defs.h" 12 | #include "globals.c" 13 | 14 | int elf_symbol_index; 15 | 16 | void elf_write_str(strbuf_t *elf_array, char *vals) 17 | { 18 | /* 19 | * Note that strbuf_puts() does not push the null character. 20 | * 21 | * If necessary, use elf_write_byte() to append the null character 22 | * after calling elf_write_str(). 23 | */ 24 | strbuf_puts(elf_array, vals); 25 | } 26 | 27 | void elf_write_byte(strbuf_t *elf_array, int val) 28 | { 29 | strbuf_putc(elf_array, val); 30 | } 31 | 32 | char e_extract_byte(int v, int b) 33 | { 34 | return (v >> (b << 3)) & 0xFF; 35 | } 36 | 37 | void elf_write_int(strbuf_t *elf_array, int val) 38 | { 39 | for (int i = 0; i < 4; i++) 40 | strbuf_putc(elf_array, e_extract_byte(val, i)); 41 | } 42 | 43 | void elf_write_blk(strbuf_t *elf_array, void *blk, int sz) 44 | { 45 | char *ptr = blk; 46 | for (int i = 0; i < sz; i++) 47 | strbuf_putc(elf_array, ptr[i]); 48 | } 49 | 50 | void elf_generate_header(void) 51 | { 52 | elf32_hdr_t hdr; 53 | /* 54 | * The following table explains the meaning of each field in the 55 | * ELF32 file header. 56 | * 57 | * Notice that the following values are hexadecimal. 58 | * 59 | * | File | | 60 | * & | Header bytes | Explanation | 61 | * ---+----------------+-------------------------------------------------+ 62 | * 00 | 7F 45 4C 46 | e_ident[0] - e_ident[3]: ELF magic number. | 63 | * | 01 | e_ident[4]: 1 -> 32-bit, 2 -> 64-bit. | 64 | * | 01 | e_ident[5]: 1 -> little-endian. 2 -> big-endian.| 65 | * | 01 | e_ident[6]: 1 -> ELF header version; must be 1. | 66 | * | 00 | e_ident[7]: Target OS ABI; be 1 for Linux. | 67 | * | 00 | e_ident[8]: ABI version; should be 1 for Linux. | 68 | * | 00 00 00 | e_ident[9] - e_ident[16]: Padding; Unused; | 69 | * | 00 00 00 00 | should be 0. | 70 | * ---+----------------+-------------------------------------------------+ 71 | * | 02 00 | e_type: Object file type; 2 -> executable | 72 | * | 28 00 | e_machine: Instruction Set Architecture. | 73 | * | | 0x28 -> ARMv7 | 74 | * | | 0xF3 -> RISC-V | 75 | * | 01 00 00 00 | e_version: ELF identification version; | 76 | * | | must be 1. | 77 | * | 54 00 01 00 | e_entry: Memory address of entry point. | 78 | * | | (where process starts). | 79 | * | 34 00 00 00 | e_phoff: File offset of program headers. | 80 | * | | 0x34 -> 32-bit, 0x40 -> 64-bit. | 81 | * | d7 8a 03 00 | e_shoff: File offset of section headers. | 82 | * ---+----------------+-------------------------------------------------+ 83 | * | 00 02 00 50 | e_flags: 0x50000200 -> ARM Version5 EABI, | 84 | * | | soft-float ABI | 85 | * | | 0x00000000 -> RISC-V | 86 | * | 34 00 | e_ehsize: Size of this header. | 87 | * | | 0x34 -> 32-bit, 0x40 -> 64-bit. | 88 | * | 20 00 | e_phentsize: Size of each program header. | 89 | * | | 0x20 -> 32-bit, 0x38 -> 64-bit. | 90 | * | 01 00 | e_phnum: Number of program headers. | 91 | * | 28 00 | e_shentsize: Size of each section header. | 92 | * | | 0x28 -> 32-bit, 0x40 -> 64-bit. | 93 | * | 06 00 | e_shnum: Number of section headers. | 94 | * | 05 00 | e_shstrndx: Index of section header containing | 95 | * | | section names. | 96 | * ---+----------------+-------------------------------------------------+ 97 | * 34 | | | 98 | */ 99 | /* ELF file header */ 100 | hdr.e_ident[0] = 0x7F; /* ELF magic number */ 101 | hdr.e_ident[1] = 'E'; 102 | hdr.e_ident[2] = 'L'; 103 | hdr.e_ident[3] = 'F'; 104 | hdr.e_ident[4] = 1; /* 32-bit */ 105 | hdr.e_ident[5] = 1; /* little-endian */ 106 | hdr.e_ident[6] = 1; /* ELF header version */ 107 | hdr.e_ident[7] = 0; /* Target OS ABI */ 108 | hdr.e_ident[8] = 0; /* ABI version */ 109 | hdr.e_ident[9] = 0; /* Padding */ 110 | hdr.e_ident[10] = 0; 111 | hdr.e_ident[11] = 0; 112 | hdr.e_ident[12] = 0; 113 | hdr.e_ident[13] = 0; 114 | hdr.e_ident[14] = 0; 115 | hdr.e_ident[15] = 0; 116 | hdr.e_type[0] = 2; /* Object file type */ 117 | hdr.e_type[1] = 0; 118 | hdr.e_machine[0] = ELF_MACHINE; /* Instruction Set Architecture */ 119 | hdr.e_machine[1] = 0; 120 | hdr.e_version = 1; /* ELF version */ 121 | hdr.e_entry = ELF_START + elf_header_len; /* entry point */ 122 | hdr.e_phoff = 0x34; /* program header offset */ 123 | hdr.e_shoff = elf_header_len + elf_code->size + elf_data->size + 39 + 124 | elf_symtab->size + 125 | elf_strtab->size; /* section header offset */ 126 | hdr.e_flags = ELF_FLAGS; /* flags */ 127 | hdr.e_ehsize[0] = 0x34; /* header size */ 128 | hdr.e_ehsize[1] = 0; 129 | hdr.e_phentsize[0] = 0x20; /* program header size */ 130 | hdr.e_phentsize[1] = 0; 131 | hdr.e_phnum[0] = 1; /* number of program headers */ 132 | hdr.e_phnum[1] = 0; 133 | hdr.e_shentsize[0] = 0x28; /* section header size */ 134 | hdr.e_shentsize[1] = 0; 135 | hdr.e_shnum[0] = 6; /* number of section headers */ 136 | hdr.e_shnum[1] = 0; 137 | hdr.e_shstrndx[0] = 5; /* section index with names */ 138 | hdr.e_shstrndx[1] = 0; 139 | elf_write_blk(elf_header, &hdr, sizeof(elf32_hdr_t)); 140 | 141 | /* 142 | * Explain the meaning of each field in the ELF32 program header. 143 | * 144 | * | Program | | 145 | * & | Header bytes | Explanation | 146 | * ---+----------------+-------------------------------------------------+ 147 | * 34 | 01 00 00 00 | p_type: Segment type; 1 -> loadable. | 148 | * | 54 00 00 00 | p_offset: Offset of segment in the file. | 149 | * | 54 00 01 00 | p_vaddr: Virtual address of loaded segment. | 150 | * | 54 00 01 00 | p_paddr: Only used on systems where physical | 151 | * | | address is relevant. | 152 | * | 48 8a 03 00 | p_filesz: Size of the segment in the file image.| 153 | * | 48 8a 03 00 | p_memsz: Size of the segment in memory. | 154 | * | | This value should be greater than or | 155 | * | | equal to p_filesz. | 156 | * | 07 00 00 00 | p_flags: Segment-wise permissions; | 157 | * | | 0x1 -> execute, 0x2 -> write, | 158 | * | | 0x4 -> read | 159 | * | 04 00 00 00 | p_align: Align segment to the specified value. | 160 | * ---+----------------+-------------------------------------------------+ 161 | * 54 | | | 162 | */ 163 | /* program header - code and data combined */ 164 | elf32_phdr_t phdr; 165 | phdr.p_type = 1; /* PT_LOAD */ 166 | phdr.p_offset = elf_header_len; /* offset of segment */ 167 | phdr.p_vaddr = ELF_START + elf_header_len; /* virtual address */ 168 | phdr.p_paddr = ELF_START + elf_header_len; /* physical address */ 169 | phdr.p_filesz = elf_code->size + elf_data->size; /* size in file */ 170 | phdr.p_memsz = elf_code->size + elf_data->size; /* size in memory */ 171 | phdr.p_flags = 7; /* flags */ 172 | phdr.p_align = 4; /* alignment */ 173 | elf_write_blk(elf_header, &phdr, sizeof(elf32_phdr_t)); 174 | } 175 | 176 | void elf_generate_sections(void) 177 | { 178 | /* symtab section */ 179 | for (int b = 0; b < elf_symtab->size; b++) 180 | elf_write_byte(elf_section, elf_symtab->elements[b]); 181 | 182 | /* strtab section */ 183 | for (int b = 0; b < elf_strtab->size; b++) 184 | elf_write_byte(elf_section, elf_strtab->elements[b]); 185 | 186 | /* shstr section; len = 39 */ 187 | elf_write_byte(elf_section, 0); 188 | elf_write_str(elf_section, ".shstrtab"); 189 | elf_write_byte(elf_section, 0); 190 | elf_write_str(elf_section, ".text"); 191 | elf_write_byte(elf_section, 0); 192 | elf_write_str(elf_section, ".data"); 193 | elf_write_byte(elf_section, 0); 194 | elf_write_str(elf_section, ".symtab"); 195 | elf_write_byte(elf_section, 0); 196 | elf_write_str(elf_section, ".strtab"); 197 | elf_write_byte(elf_section, 0); 198 | 199 | /* section header table */ 200 | elf32_shdr_t shdr; 201 | int ofs = elf_header_len; 202 | 203 | /* 204 | * The following table uses the text section header as an example 205 | * to explain the ELF32 section header. 206 | * 207 | * | Section | | 208 | * & | Header bytes | Explanation | 209 | * ---+----------------+-------------------------------------------------+ 210 | * | 0b 00 00 00 | sh_name: Name of the section. Giving the | 211 | * | | location of a null-terminated string. | 212 | * | 01 00 00 00 | sh_type: Type of the section's contents | 213 | * | | and semantics. | 214 | * | | 1 -> holds the program-defined | 215 | * | | information | 216 | * | 07 00 00 00 | sh_flags: Miscellaneous attributes. | 217 | * | | 0x1 -> writable, 0x2 -> allocatable | 218 | * | | 0x4 -> executable. | 219 | * | 54 00 01 00 | sh_addr: Starting address of the section | 220 | * | | in the memory image of a process. | 221 | * | 54 00 00 00 | sh_offset: Offset of the section in the file. | 222 | * | 0b 30 03 00 | sh_size: Size of the section. | 223 | * | 00 00 00 00 | sh_link: Section header table index link. | 224 | * | 00 00 00 00 | sh_info: Extra information. | 225 | * | 04 00 00 00 | sh_addralign: Address alignment constraints. | 226 | * | 00 00 00 00 | sh_entsize: Size of each entry. | 227 | * ---+----------------+-------------------------------------------------+ 228 | * | | | 229 | */ 230 | /* NULL section */ 231 | shdr.sh_name = 0; 232 | shdr.sh_type = 0; 233 | shdr.sh_flags = 0; 234 | shdr.sh_addr = 0; 235 | shdr.sh_offset = 0; 236 | shdr.sh_size = 0; 237 | shdr.sh_link = 0; 238 | shdr.sh_info = 0; 239 | shdr.sh_addralign = 0; 240 | shdr.sh_entsize = 0; 241 | elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); 242 | 243 | /* .text */ 244 | shdr.sh_name = 0xb; 245 | shdr.sh_type = 1; 246 | shdr.sh_flags = 7; 247 | shdr.sh_addr = ELF_START + elf_header_len; 248 | shdr.sh_offset = ofs; 249 | shdr.sh_size = elf_code->size; 250 | shdr.sh_link = 0; 251 | shdr.sh_info = 0; 252 | shdr.sh_addralign = 4; 253 | shdr.sh_entsize = 0; 254 | elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); 255 | ofs += elf_code->size; 256 | 257 | /* .data */ 258 | shdr.sh_name = 0x11; 259 | shdr.sh_type = 1; 260 | shdr.sh_flags = 3; 261 | shdr.sh_addr = elf_code_start + elf_code->size; 262 | shdr.sh_offset = ofs; 263 | shdr.sh_size = elf_data->size; 264 | shdr.sh_link = 0; 265 | shdr.sh_info = 0; 266 | shdr.sh_addralign = 4; 267 | shdr.sh_entsize = 0; 268 | elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); 269 | ofs += elf_data->size; 270 | 271 | /* .symtab */ 272 | shdr.sh_name = 0x17; 273 | shdr.sh_type = 2; 274 | shdr.sh_flags = 0; 275 | shdr.sh_addr = 0; 276 | shdr.sh_offset = ofs; 277 | shdr.sh_size = elf_symtab->size; 278 | shdr.sh_link = 4; 279 | shdr.sh_info = elf_symbol_index; 280 | shdr.sh_addralign = 4; 281 | shdr.sh_entsize = 16; 282 | elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); 283 | ofs += elf_symtab->size; 284 | 285 | /* .strtab */ 286 | shdr.sh_name = 0x1f; 287 | shdr.sh_type = 3; 288 | shdr.sh_flags = 0; 289 | shdr.sh_addr = 0; 290 | shdr.sh_offset = ofs; 291 | shdr.sh_size = elf_strtab->size; 292 | shdr.sh_link = 0; 293 | shdr.sh_info = 0; 294 | shdr.sh_addralign = 1; 295 | shdr.sh_entsize = 0; 296 | elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); 297 | ofs += elf_strtab->size; 298 | 299 | /* .shstr */ 300 | shdr.sh_name = 1; 301 | shdr.sh_type = 3; 302 | shdr.sh_flags = 0; 303 | shdr.sh_addr = 0; 304 | shdr.sh_offset = ofs; 305 | shdr.sh_size = 39; 306 | shdr.sh_link = 0; 307 | shdr.sh_info = 0; 308 | shdr.sh_addralign = 1; 309 | shdr.sh_entsize = 0; 310 | elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); 311 | } 312 | 313 | void elf_align(void) 314 | { 315 | while (elf_data->size & 3) 316 | elf_write_byte(elf_data, 0); 317 | 318 | while (elf_symtab->size & 3) 319 | elf_write_byte(elf_symtab, 0); 320 | 321 | while (elf_strtab->size & 3) 322 | elf_write_byte(elf_strtab, 0); 323 | } 324 | 325 | void elf_add_symbol(char *symbol, int pc) 326 | { 327 | elf_write_int(elf_symtab, elf_strtab->size); 328 | elf_write_int(elf_symtab, pc); 329 | elf_write_int(elf_symtab, 0); 330 | elf_write_int(elf_symtab, pc == 0 ? 0 : 1 << 16); 331 | 332 | elf_write_str(elf_strtab, symbol); 333 | elf_write_byte(elf_strtab, 0); 334 | elf_symbol_index++; 335 | } 336 | 337 | void elf_generate(char *outfile) 338 | { 339 | elf_align(); 340 | elf_generate_header(); 341 | elf_generate_sections(); 342 | 343 | if (!outfile) 344 | outfile = "a.out"; 345 | 346 | FILE *fp = fopen(outfile, "wb"); 347 | for (int i = 0; i < elf_header->size; i++) 348 | fputc(elf_header->elements[i], fp); 349 | for (int i = 0; i < elf_code->size; i++) 350 | fputc(elf_code->elements[i], fp); 351 | for (int i = 0; i < elf_data->size; i++) 352 | fputc(elf_data->elements[i], fp); 353 | for (int i = 0; i < elf_section->size; i++) 354 | fputc(elf_section->elements[i], fp); 355 | fclose(fp); 356 | } 357 | -------------------------------------------------------------------------------- /src/lexer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * shecc - Self-Hosting and Educational C Compiler. 3 | * 4 | * shecc is freely redistributable under the BSD 2 clause license. See the 5 | * file "LICENSE" for information on usage and redistribution of this file. 6 | */ 7 | 8 | #include 9 | 10 | #include "defs.h" 11 | #include "globals.c" 12 | 13 | bool is_whitespace(char c) 14 | { 15 | return c == ' ' || c == '\t'; 16 | } 17 | 18 | char peek_char(int offset); 19 | 20 | /* is it backslash-newline? */ 21 | bool is_linebreak(char c) 22 | { 23 | return c == '\\' && peek_char(1) == '\n'; 24 | } 25 | 26 | bool is_newline(char c) 27 | { 28 | return c == '\r' || c == '\n'; 29 | } 30 | 31 | /* is it alphabet, number or '_'? */ 32 | bool is_alnum(char c) 33 | { 34 | return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || 35 | (c >= '0' && c <= '9') || (c == '_')); 36 | } 37 | 38 | bool is_digit(char c) 39 | { 40 | return c >= '0' && c <= '9'; 41 | } 42 | 43 | bool is_hex(char c) 44 | { 45 | return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || c == 'x' || 46 | (c >= 'A' && c <= 'F')); 47 | } 48 | 49 | bool is_numeric(char buffer[]) 50 | { 51 | bool hex = false; 52 | int size = strlen(buffer); 53 | 54 | if (size > 2) 55 | hex = !strncmp(buffer, "0x", 2); 56 | 57 | for (int i = 0; i < size; i++) { 58 | if (hex && !is_hex(buffer[i])) 59 | return false; 60 | if (!hex && !is_digit(buffer[i])) 61 | return false; 62 | } 63 | return true; 64 | } 65 | 66 | void skip_whitespace(void) 67 | { 68 | while (true) { 69 | if (is_linebreak(next_char)) { 70 | SOURCE->size += 2; 71 | next_char = SOURCE->elements[SOURCE->size]; 72 | continue; 73 | } 74 | if (is_whitespace(next_char) || 75 | (skip_newline && is_newline(next_char))) { 76 | SOURCE->size++; 77 | next_char = SOURCE->elements[SOURCE->size]; 78 | continue; 79 | } 80 | break; 81 | } 82 | } 83 | 84 | char read_char(bool is_skip_space) 85 | { 86 | SOURCE->size++; 87 | next_char = SOURCE->elements[SOURCE->size]; 88 | if (is_skip_space) 89 | skip_whitespace(); 90 | return next_char; 91 | } 92 | 93 | char peek_char(int offset) 94 | { 95 | return SOURCE->elements[SOURCE->size + offset]; 96 | } 97 | 98 | /* Lex next token and returns its token type. Parameter 'aliasing' is used for 99 | * disable preprocessor aliasing on identifier tokens. 100 | */ 101 | token_t lex_token_internal(bool aliasing) 102 | { 103 | token_str[0] = 0; 104 | 105 | /* partial preprocessor */ 106 | if (next_char == '#') { 107 | int i = 0; 108 | 109 | do { 110 | token_str[i++] = next_char; 111 | } while (is_alnum(read_char(false))); 112 | token_str[i] = 0; 113 | skip_whitespace(); 114 | 115 | if (!strcmp(token_str, "#include")) 116 | return T_cppd_include; 117 | if (!strcmp(token_str, "#define")) 118 | return T_cppd_define; 119 | if (!strcmp(token_str, "#undef")) 120 | return T_cppd_undef; 121 | if (!strcmp(token_str, "#error")) 122 | return T_cppd_error; 123 | if (!strcmp(token_str, "#if")) 124 | return T_cppd_if; 125 | if (!strcmp(token_str, "#elif")) 126 | return T_cppd_elif; 127 | if (!strcmp(token_str, "#ifdef")) 128 | return T_cppd_ifdef; 129 | if (!strcmp(token_str, "#ifndef")) 130 | return T_cppd_ifndef; 131 | if (!strcmp(token_str, "#else")) 132 | return T_cppd_else; 133 | if (!strcmp(token_str, "#endif")) 134 | return T_cppd_endif; 135 | if (!strcmp(token_str, "#pragma")) 136 | return T_cppd_pragma; 137 | error("Unknown directive"); 138 | } 139 | 140 | if (next_char == '/') { 141 | read_char(true); 142 | 143 | /* C-style comments */ 144 | if (next_char == '*') { 145 | /* in a comment, skip until end */ 146 | do { 147 | read_char(false); 148 | if (next_char == '*') { 149 | read_char(false); 150 | if (next_char == '/') { 151 | read_char(true); 152 | return lex_token_internal(aliasing); 153 | } 154 | } 155 | } while (next_char); 156 | 157 | if (!next_char) 158 | error("Unenclosed C-style comment"); 159 | return lex_token_internal(aliasing); 160 | } 161 | 162 | /* C++-style comments */ 163 | if (next_char == '/') { 164 | do { 165 | read_char(false); 166 | } while (next_char && !is_newline(next_char)); 167 | return lex_token_internal(aliasing); 168 | } 169 | 170 | if (next_char == '=') { 171 | read_char(true); 172 | return T_divideeq; 173 | } 174 | 175 | return T_divide; 176 | } 177 | 178 | if (is_digit(next_char)) { 179 | int i = 0; 180 | do { 181 | token_str[i++] = next_char; 182 | } while (is_hex(read_char(false))); 183 | token_str[i] = 0; 184 | skip_whitespace(); 185 | return T_numeric; 186 | } 187 | if (next_char == '(') { 188 | read_char(true); 189 | return T_open_bracket; 190 | } 191 | if (next_char == ')') { 192 | read_char(true); 193 | return T_close_bracket; 194 | } 195 | if (next_char == '{') { 196 | read_char(true); 197 | return T_open_curly; 198 | } 199 | if (next_char == '}') { 200 | read_char(true); 201 | return T_close_curly; 202 | } 203 | if (next_char == '[') { 204 | read_char(true); 205 | return T_open_square; 206 | } 207 | if (next_char == ']') { 208 | read_char(true); 209 | return T_close_square; 210 | } 211 | if (next_char == ',') { 212 | read_char(true); 213 | return T_comma; 214 | } 215 | if (next_char == '^') { 216 | read_char(true); 217 | 218 | if (next_char == '=') { 219 | read_char(true); 220 | return T_xoreq; 221 | } 222 | 223 | return T_bit_xor; 224 | } 225 | if (next_char == '~') { 226 | read_char(true); 227 | return T_bit_not; 228 | } 229 | if (next_char == '"') { 230 | int i = 0; 231 | int special = 0; 232 | 233 | while ((read_char(false) != '"') || special) { 234 | if ((i > 0) && (token_str[i - 1] == '\\')) { 235 | if (next_char == 'n') 236 | token_str[i - 1] = '\n'; 237 | else if (next_char == '"') 238 | token_str[i - 1] = '"'; 239 | else if (next_char == 'r') 240 | token_str[i - 1] = '\r'; 241 | else if (next_char == '\'') 242 | token_str[i - 1] = '\''; 243 | else if (next_char == 't') 244 | token_str[i - 1] = '\t'; 245 | else if (next_char == '\\') 246 | token_str[i - 1] = '\\'; 247 | else if (next_char == '0') 248 | token_str[i - 1] = '\0'; 249 | else 250 | abort(); 251 | } else { 252 | token_str[i++] = next_char; 253 | } 254 | if (next_char == '\\') 255 | special = 1; 256 | else 257 | special = 0; 258 | } 259 | token_str[i] = 0; 260 | read_char(true); 261 | return T_string; 262 | } 263 | if (next_char == '\'') { 264 | read_char(false); 265 | if (next_char == '\\') { 266 | read_char(false); 267 | if (next_char == 'n') 268 | token_str[0] = '\n'; 269 | else if (next_char == 'r') 270 | token_str[0] = '\r'; 271 | else if (next_char == '\'') 272 | token_str[0] = '\''; 273 | else if (next_char == '"') 274 | token_str[0] = '"'; 275 | else if (next_char == 't') 276 | token_str[0] = '\t'; 277 | else if (next_char == '\\') 278 | token_str[0] = '\\'; 279 | else if (next_char == '0') 280 | token_str[0] = '\0'; 281 | else 282 | abort(); 283 | } else { 284 | token_str[0] = next_char; 285 | } 286 | token_str[1] = 0; 287 | if (read_char(true) != '\'') 288 | abort(); 289 | read_char(true); 290 | return T_char; 291 | } 292 | if (next_char == '*') { 293 | read_char(true); 294 | 295 | if (next_char == '=') { 296 | read_char(true); 297 | return T_asteriskeq; 298 | } 299 | 300 | return T_asterisk; 301 | } 302 | if (next_char == '&') { 303 | read_char(false); 304 | if (next_char == '&') { 305 | read_char(true); 306 | return T_log_and; 307 | } 308 | if (next_char == '=') { 309 | read_char(true); 310 | return T_andeq; 311 | } 312 | skip_whitespace(); 313 | return T_ampersand; 314 | } 315 | if (next_char == '|') { 316 | read_char(false); 317 | if (next_char == '|') { 318 | read_char(true); 319 | return T_log_or; 320 | } 321 | if (next_char == '=') { 322 | read_char(true); 323 | return T_oreq; 324 | } 325 | skip_whitespace(); 326 | return T_bit_or; 327 | } 328 | if (next_char == '<') { 329 | read_char(false); 330 | if (next_char == '=') { 331 | read_char(true); 332 | return T_le; 333 | } 334 | if (next_char == '<') { 335 | read_char(true); 336 | 337 | if (next_char == '=') { 338 | read_char(true); 339 | return T_lshifteq; 340 | } 341 | 342 | return T_lshift; 343 | } 344 | skip_whitespace(); 345 | return T_lt; 346 | } 347 | if (next_char == '%') { 348 | read_char(true); 349 | 350 | if (next_char == '=') { 351 | read_char(true); 352 | return T_modeq; 353 | } 354 | 355 | return T_mod; 356 | } 357 | if (next_char == '>') { 358 | read_char(false); 359 | if (next_char == '=') { 360 | read_char(true); 361 | return T_ge; 362 | } 363 | if (next_char == '>') { 364 | read_char(true); 365 | 366 | if (next_char == '=') { 367 | read_char(true); 368 | return T_rshifteq; 369 | } 370 | 371 | return T_rshift; 372 | } 373 | skip_whitespace(); 374 | return T_gt; 375 | } 376 | if (next_char == '!') { 377 | read_char(false); 378 | if (next_char == '=') { 379 | read_char(true); 380 | return T_noteq; 381 | } 382 | skip_whitespace(); 383 | return T_log_not; 384 | } 385 | if (next_char == '.') { 386 | read_char(false); 387 | if (next_char == '.') { 388 | read_char(false); 389 | if (next_char == '.') { 390 | read_char(true); 391 | return T_elipsis; 392 | } 393 | abort(); 394 | } 395 | skip_whitespace(); 396 | return T_dot; 397 | } 398 | if (next_char == '-') { 399 | read_char(true); 400 | if (next_char == '>') { 401 | read_char(true); 402 | return T_arrow; 403 | } 404 | if (next_char == '-') { 405 | read_char(true); 406 | return T_decrement; 407 | } 408 | if (next_char == '=') { 409 | read_char(true); 410 | return T_minuseq; 411 | } 412 | skip_whitespace(); 413 | return T_minus; 414 | } 415 | if (next_char == '+') { 416 | read_char(false); 417 | if (next_char == '+') { 418 | read_char(true); 419 | return T_increment; 420 | } 421 | if (next_char == '=') { 422 | read_char(true); 423 | return T_pluseq; 424 | } 425 | skip_whitespace(); 426 | return T_plus; 427 | } 428 | if (next_char == ';') { 429 | read_char(true); 430 | return T_semicolon; 431 | } 432 | if (next_char == '?') { 433 | read_char(true); 434 | return T_question; 435 | } 436 | if (next_char == ':') { 437 | read_char(true); 438 | return T_colon; 439 | } 440 | if (next_char == '=') { 441 | read_char(false); 442 | if (next_char == '=') { 443 | read_char(true); 444 | return T_eq; 445 | } 446 | skip_whitespace(); 447 | return T_assign; 448 | } 449 | 450 | if (is_alnum(next_char)) { 451 | char *alias; 452 | int i = 0; 453 | do { 454 | token_str[i++] = next_char; 455 | } while (is_alnum(read_char(false))); 456 | token_str[i] = 0; 457 | skip_whitespace(); 458 | 459 | if (!strcmp(token_str, "if")) 460 | return T_if; 461 | if (!strcmp(token_str, "while")) 462 | return T_while; 463 | if (!strcmp(token_str, "for")) 464 | return T_for; 465 | if (!strcmp(token_str, "do")) 466 | return T_do; 467 | if (!strcmp(token_str, "else")) 468 | return T_else; 469 | if (!strcmp(token_str, "return")) 470 | return T_return; 471 | if (!strcmp(token_str, "typedef")) 472 | return T_typedef; 473 | if (!strcmp(token_str, "enum")) 474 | return T_enum; 475 | if (!strcmp(token_str, "struct")) 476 | return T_struct; 477 | if (!strcmp(token_str, "sizeof")) 478 | return T_sizeof; 479 | if (!strcmp(token_str, "switch")) 480 | return T_switch; 481 | if (!strcmp(token_str, "case")) 482 | return T_case; 483 | if (!strcmp(token_str, "break")) 484 | return T_break; 485 | if (!strcmp(token_str, "default")) 486 | return T_default; 487 | if (!strcmp(token_str, "continue")) 488 | return T_continue; 489 | 490 | if (aliasing) { 491 | alias = find_alias(token_str); 492 | if (alias) { 493 | /* FIXME: comparison with string "bool" is a temporary hack */ 494 | token_t t; 495 | 496 | if (is_numeric(alias)) { 497 | t = T_numeric; 498 | } else if (!strcmp(alias, "_Bool")) { 499 | t = T_identifier; 500 | } else { 501 | t = T_string; 502 | } 503 | 504 | strcpy(token_str, alias); 505 | return t; 506 | } 507 | } 508 | 509 | return T_identifier; 510 | } 511 | 512 | /* This only happens when parsing a macro. Move to the token after the 513 | * macro definition or return to where the macro has been called. 514 | */ 515 | if (next_char == '\n') { 516 | if (macro_return_idx) { 517 | SOURCE->size = macro_return_idx; 518 | next_char = SOURCE->elements[SOURCE->size]; 519 | } else 520 | next_char = read_char(true); 521 | return lex_token_internal(aliasing); 522 | } 523 | 524 | if (next_char == 0) 525 | return T_eof; 526 | 527 | error("Unrecognized input"); 528 | 529 | /* Unreachable, but we need an explicit return for non-void method. */ 530 | return T_eof; 531 | } 532 | 533 | /* Lex next token and returns its token type. To disable aliasing on next 534 | * token, use 'lex_token_internal'. 535 | */ 536 | token_t lex_token(void) 537 | { 538 | return lex_token_internal(true); 539 | } 540 | 541 | /* Skip the content. We only need the index where the macro body begins. */ 542 | void skip_macro_body(void) 543 | { 544 | while (!is_newline(next_char)) 545 | next_token = lex_token(); 546 | 547 | skip_newline = true; 548 | next_token = lex_token(); 549 | } 550 | 551 | /* Accepts next token if token types are matched. */ 552 | bool lex_accept_internal(token_t token, bool aliasing) 553 | { 554 | if (next_token == token) { 555 | next_token = lex_token_internal(aliasing); 556 | return true; 557 | } 558 | 559 | return false; 560 | } 561 | 562 | /* Accepts next token if token types are matched. To disable aliasing on next 563 | * token, use 'lex_accept_internal'. 564 | */ 565 | bool lex_accept(token_t token) 566 | { 567 | return lex_accept_internal(token, 1); 568 | } 569 | 570 | /* Peeks next token and copy token's literal to value if token types are 571 | * matched. 572 | */ 573 | bool lex_peek(token_t token, char *value) 574 | { 575 | if (next_token == token) { 576 | if (!value) 577 | return true; 578 | strcpy(value, token_str); 579 | return true; 580 | } 581 | return false; 582 | } 583 | 584 | /* Strictly match next token with given token type and copy token's literal to 585 | * value. 586 | */ 587 | void lex_ident_internal(token_t token, char *value, bool aliasing) 588 | { 589 | if (next_token != token) 590 | error("Unexpected token"); 591 | strcpy(value, token_str); 592 | next_token = lex_token_internal(aliasing); 593 | } 594 | 595 | /* Strictly match next token with given token type and copy token's literal to 596 | * value. To disable aliasing on next token, use 'lex_ident_internal'. 597 | */ 598 | void lex_ident(token_t token, char *value) 599 | { 600 | lex_ident_internal(token, value, true); 601 | } 602 | 603 | /* Strictly match next token with given token type. */ 604 | void lex_expect_internal(token_t token, bool aliasing) 605 | { 606 | if (next_token != token) 607 | error("Unexpected token"); 608 | next_token = lex_token_internal(aliasing); 609 | } 610 | 611 | /* Strictly match next token with given token type. To disable aliasing on next 612 | * token, use 'lex_expect_internal'. 613 | */ 614 | void lex_expect(token_t token) 615 | { 616 | lex_expect_internal(token, true); 617 | } 618 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * shecc - Self-Hosting and Educational C Compiler. 3 | * 4 | * shecc is freely redistributable under the BSD 2 clause license. See the 5 | * file "LICENSE" for information on usage and redistribution of this file. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | /* Define target machine */ 14 | #include "../config" 15 | 16 | /* The inclusion must follow the fixed order, otherwise it fails to build. */ 17 | #include "defs.h" 18 | 19 | /* Initialize global objects */ 20 | #include "globals.c" 21 | 22 | /* ELF manipulation */ 23 | #include "elf.c" 24 | 25 | /* C language lexical analyzer */ 26 | #include "lexer.c" 27 | 28 | /* C language syntactic analyzer */ 29 | #include "parser.c" 30 | 31 | /* architecture-independent middle-end */ 32 | #include "ssa.c" 33 | 34 | /* Register allocator */ 35 | #include "reg-alloc.c" 36 | 37 | /* Peephole optimization */ 38 | #include "peephole.c" 39 | 40 | /* Machine code generation. support ARMv7-A and RV32I */ 41 | #include "codegen.c" 42 | 43 | /* inlined libc */ 44 | #include "../out/libc.inc" 45 | 46 | int main(int argc, char *argv[]) 47 | { 48 | int libc = 1; 49 | char *out = NULL, *in = NULL; 50 | 51 | for (int i = 1; i < argc; i++) { 52 | if (!strcmp(argv[i], "--dump-ir")) 53 | dump_ir = 1; 54 | else if (!strcmp(argv[i], "+m")) 55 | hard_mul_div = 1; 56 | else if (!strcmp(argv[i], "--no-libc")) 57 | libc = 0; 58 | else if (!strcmp(argv[i], "-o")) { 59 | if (i < argc + 1) { 60 | out = argv[i + 1]; 61 | i++; 62 | } else 63 | /* unsupported options */ 64 | abort(); 65 | } else if (argv[i][0] == '-') { 66 | fatal("Unidentified option"); 67 | } else 68 | in = argv[i]; 69 | } 70 | 71 | if (!in) { 72 | printf("Missing source file!\n"); 73 | printf( 74 | "Usage: shecc [-o output] [+m] [--dump-ir] [--no-libc] " 75 | "\n"); 76 | return -1; 77 | } 78 | 79 | /* initialize global objects */ 80 | global_init(); 81 | 82 | /* include libc */ 83 | if (libc) 84 | libc_generate(); 85 | 86 | /* load and parse source code into IR */ 87 | parse(in); 88 | 89 | ssa_build(); 90 | 91 | /* dump first phase IR */ 92 | if (dump_ir) 93 | dump_insn(); 94 | 95 | /* SSA-based optimization */ 96 | optimize(); 97 | 98 | /* SSA-based liveness analyses */ 99 | liveness_analysis(); 100 | 101 | /* allocate register from IR */ 102 | reg_alloc(); 103 | 104 | peephole(); 105 | 106 | /* flatten CFG to linear instruction */ 107 | cfg_flatten(); 108 | 109 | /* dump second phase IR */ 110 | if (dump_ir) 111 | dump_ph2_ir(); 112 | 113 | /* generate code from IR */ 114 | code_generate(); 115 | 116 | /* output code in ELF */ 117 | elf_generate(out); 118 | 119 | /* release allocated objects */ 120 | global_release(); 121 | 122 | exit(0); 123 | } 124 | -------------------------------------------------------------------------------- /src/peephole.c: -------------------------------------------------------------------------------- 1 | /* 2 | * shecc - Self-Hosting and Educational C Compiler. 3 | * 4 | * shecc is freely redistributable under the BSD 2 clause license. See the 5 | * file "LICENSE" for information on usage and redistribution of this file. 6 | */ 7 | 8 | #include 9 | 10 | #include "defs.h" 11 | #include "globals.c" 12 | 13 | bool is_fusible_insn(ph2_ir_t *ph2_ir) 14 | { 15 | switch (ph2_ir->op) { 16 | case OP_add: 17 | case OP_sub: 18 | case OP_mul: 19 | case OP_div: 20 | case OP_mod: 21 | case OP_lshift: 22 | case OP_rshift: 23 | case OP_bit_and: 24 | case OP_bit_or: 25 | case OP_bit_xor: 26 | case OP_log_and: 27 | case OP_log_or: 28 | case OP_log_not: 29 | case OP_negate: 30 | case OP_load: 31 | case OP_global_load: 32 | case OP_load_data_address: 33 | return true; 34 | default: 35 | return false; 36 | } 37 | } 38 | 39 | bool insn_fusion(ph2_ir_t *ph2_ir) 40 | { 41 | ph2_ir_t *next = ph2_ir->next; 42 | if (!next) 43 | return false; 44 | 45 | if (next->op == OP_assign) { 46 | if (is_fusible_insn(ph2_ir) && ph2_ir->dest == next->src0) { 47 | /* eliminates: 48 | * {ALU rn, rs1, rs2; mv rd, rn;} 49 | * reduces to: 50 | * {ALU rd, rs1, rs2;} 51 | */ 52 | ph2_ir->dest = next->dest; 53 | ph2_ir->next = next->next; 54 | return true; 55 | } 56 | } 57 | 58 | if (ph2_ir->op == OP_load_constant && ph2_ir->src0 == 0) { 59 | if (next->op == OP_add && 60 | (ph2_ir->dest == next->src0 || ph2_ir->dest == next->src1)) { 61 | /* eliminates: 62 | * {li rn, 0; add rd, rs1, rn;} or 63 | * {li rn, 0; add rd, rn, rs1;} 64 | * reduces to: 65 | * {mv rd, rs1;}, based on identity property of addition 66 | */ 67 | /* Determine the non-zero source operand */ 68 | int non_zero_src = 69 | (ph2_ir->dest == next->src0) ? next->src1 : next->src0; 70 | 71 | /* Transform instruction sequence from addition with zero to move */ 72 | ph2_ir->op = OP_assign; 73 | ph2_ir->src0 = non_zero_src; 74 | ph2_ir->dest = next->dest; 75 | ph2_ir->next = next->next; 76 | return true; 77 | } 78 | 79 | if (next->op == OP_sub) { 80 | if (ph2_ir->dest == next->src1) { 81 | /* eliminates: 82 | * {li rn, 0; sub rd, rs1, rn;} 83 | * reduces to: 84 | * {mv rd, rs1;} 85 | */ 86 | ph2_ir->op = OP_assign; 87 | ph2_ir->src0 = next->src0; 88 | ph2_ir->dest = next->dest; 89 | ph2_ir->next = next->next; 90 | return true; 91 | } 92 | 93 | if (ph2_ir->dest == next->src0) { 94 | /* eliminates: 95 | * {li rn, 0; sub rd, rn, rs1;} 96 | * reduces to: 97 | * {negate rd, rs1;} 98 | */ 99 | ph2_ir->op = OP_negate; 100 | ph2_ir->src0 = next->src1; 101 | ph2_ir->dest = next->dest; 102 | ph2_ir->next = next->next; 103 | return true; 104 | } 105 | } 106 | 107 | if (next->op == OP_mul && 108 | (ph2_ir->dest == next->src0 || ph2_ir->dest == next->src1)) { 109 | /* eliminates: 110 | * {li rn, 0; mul rd, rs1, rn;} or 111 | * {li rn, 0; mul rd, rn, rs1;} 112 | * reduces to: 113 | * {li rd, 0}, based on zero property of multiplication 114 | */ 115 | ph2_ir->op = OP_load_constant; 116 | ph2_ir->src0 = 0; 117 | ph2_ir->dest = next->dest; 118 | ph2_ir->next = next->next; 119 | return true; 120 | } 121 | } 122 | 123 | if (ph2_ir->op == OP_load_constant && ph2_ir->src0 == 1) { 124 | if (next->op == OP_mul && 125 | (ph2_ir->dest == next->src0 || ph2_ir->dest == next->src1)) { 126 | /* eliminates: 127 | * {li rn, 1; mul rd, rs1, rn;} or 128 | * {li rn, 1; mul rd, rn, rs1;} 129 | * reduces to: 130 | * {li rd, rs1}, based on identity property of multiplication 131 | */ 132 | ph2_ir->op = OP_assign; 133 | ph2_ir->src0 = ph2_ir->dest == next->src0 ? next->src1 : next->src0; 134 | ph2_ir->dest = next->dest; 135 | ph2_ir->next = next->next; 136 | return true; 137 | } 138 | } 139 | 140 | /* Other instruction fusion should be done here, and for any success fusion, 141 | * it should return true. This meant to allow peephole optimization to do 142 | * multiple passes over the IR list to maximize optimization as much as 143 | * possbile. 144 | */ 145 | 146 | return false; 147 | } 148 | 149 | void peephole(void) 150 | { 151 | for (func_t *func = FUNC_LIST.head; func; func = func->next) { 152 | for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) { 153 | for (ph2_ir_t *ir = bb->ph2_ir_list.head; ir; ir = ir->next) { 154 | ph2_ir_t *next = ir->next; 155 | if (!next) 156 | continue; 157 | if (next->op == OP_assign && next->dest == next->src0) { 158 | ir->next = next->next; 159 | continue; 160 | } 161 | insn_fusion(ir); 162 | } 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/reg-alloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * shecc - Self-Hosting and Educational C Compiler. 3 | * 4 | * shecc is freely redistributable under the BSD 2 clause license. See the 5 | * file "LICENSE" for information on usage and redistribution of this file. 6 | */ 7 | 8 | /* Allocate registers from IR. The linear-scan algorithm now expects a minimum 9 | * of 7 available registers (typical for RISC-style architectures). 10 | * 11 | * TODO: Implement the "-O level" option. This allocator now always drops the 12 | * dead variable and does NOT wrtie it back to the stack. 13 | */ 14 | 15 | #include 16 | 17 | #include "defs.h" 18 | #include "globals.c" 19 | 20 | /* Aligns size to nearest multiple of 4, this meets 21 | * ARMv7's alignment requirement. 22 | * 23 | * This function should 24 | * be called whenever handling with user-defined type's 25 | * size. 26 | */ 27 | int align_size(int i) 28 | { 29 | return i <= 4 ? 4 : (i + 3) & ~3; 30 | } 31 | 32 | bool check_live_out(basic_block_t *bb, var_t *var) 33 | { 34 | for (int i = 0; i < bb->live_out_idx; i++) { 35 | if (bb->live_out[i] == var) 36 | return true; 37 | } 38 | return false; 39 | } 40 | 41 | void refresh(basic_block_t *bb, insn_t *insn) 42 | { 43 | for (int i = 0; i < REG_CNT; i++) { 44 | if (!REGS[i].var) 45 | continue; 46 | if (check_live_out(bb, REGS[i].var)) 47 | continue; 48 | if (REGS[i].var->consumed < insn->idx) { 49 | REGS[i].var = NULL; 50 | REGS[i].polluted = 0; 51 | } 52 | } 53 | } 54 | 55 | ph2_ir_t *bb_add_ph2_ir(basic_block_t *bb, opcode_t op) 56 | { 57 | ph2_ir_t *n = arena_alloc(BB_ARENA, sizeof(ph2_ir_t)); 58 | n->op = op; 59 | 60 | if (!bb->ph2_ir_list.head) 61 | bb->ph2_ir_list.head = n; 62 | else 63 | bb->ph2_ir_list.tail->next = n; 64 | 65 | bb->ph2_ir_list.tail = n; 66 | return n; 67 | } 68 | 69 | /* Priority of spilling: 70 | * - live_out variable 71 | * - farthest local variable 72 | */ 73 | void spill_var(basic_block_t *bb, var_t *var, int idx) 74 | { 75 | if (!REGS[idx].polluted) { 76 | REGS[idx].var = NULL; 77 | return; 78 | } 79 | 80 | if (!var->offset) { 81 | var->offset = bb->belong_to->stack_size; 82 | bb->belong_to->stack_size += 4; 83 | } 84 | ph2_ir_t *ir = var->is_global ? bb_add_ph2_ir(bb, OP_global_store) 85 | : bb_add_ph2_ir(bb, OP_store); 86 | ir->src0 = idx; 87 | ir->src1 = var->offset; 88 | REGS[idx].var = NULL; 89 | REGS[idx].polluted = 0; 90 | } 91 | 92 | /* Return the index of register for given variable. Otherwise, return -1. */ 93 | int find_in_regs(var_t *var) 94 | { 95 | for (int i = 0; i < REG_CNT; i++) { 96 | if (REGS[i].var == var) 97 | return i; 98 | } 99 | return -1; 100 | } 101 | 102 | void load_var(basic_block_t *bb, var_t *var, int idx) 103 | { 104 | ph2_ir_t *ir = var->is_global ? bb_add_ph2_ir(bb, OP_global_load) 105 | : bb_add_ph2_ir(bb, OP_load); 106 | ir->src0 = var->offset; 107 | ir->dest = idx; 108 | REGS[idx].var = var; 109 | REGS[idx].polluted = 0; 110 | } 111 | 112 | int prepare_operand(basic_block_t *bb, var_t *var, int operand_0) 113 | { 114 | int i = find_in_regs(var); 115 | if (i > -1) 116 | return i; 117 | 118 | for (i = 0; i < REG_CNT; i++) { 119 | if (!REGS[i].var) { 120 | load_var(bb, var, i); 121 | return i; 122 | } 123 | } 124 | 125 | for (i = 0; i < REG_CNT; i++) { 126 | if (i == operand_0) 127 | continue; 128 | if (check_live_out(bb, REGS[i].var)) { 129 | spill_var(bb, REGS[i].var, i); 130 | load_var(bb, var, i); 131 | return i; 132 | } 133 | } 134 | 135 | /* spill farthest local */ 136 | int spilled = 0; 137 | for (i = 0; i < REG_CNT; i++) { 138 | if (!REGS[i].var) 139 | continue; 140 | if (REGS[i].var->consumed > REGS[spilled].var->consumed) 141 | spilled = i; 142 | } 143 | 144 | spill_var(bb, REGS[spilled].var, spilled); 145 | load_var(bb, var, spilled); 146 | 147 | return spilled; 148 | } 149 | 150 | int prepare_dest(basic_block_t *bb, var_t *var, int operand_0, int operand_1) 151 | { 152 | int i = find_in_regs(var); 153 | if (i > -1) { 154 | REGS[i].polluted = 1; 155 | return i; 156 | } 157 | 158 | for (i = 0; i < REG_CNT; i++) { 159 | if (!REGS[i].var) { 160 | REGS[i].var = var; 161 | REGS[i].polluted = 1; 162 | return i; 163 | } 164 | } 165 | 166 | for (i = 0; i < REG_CNT; i++) { 167 | if (i == operand_0) 168 | continue; 169 | if (i == operand_1) 170 | continue; 171 | if (check_live_out(bb, REGS[i].var)) { 172 | spill_var(bb, REGS[i].var, i); 173 | REGS[i].var = var; 174 | REGS[i].polluted = 1; 175 | return i; 176 | } 177 | } 178 | 179 | /* spill farthest local */ 180 | int spilled = 0; 181 | for (i = 0; i < REG_CNT; i++) { 182 | if (i == operand_0) 183 | continue; 184 | if (i == operand_1) 185 | continue; 186 | if (!REGS[i].var) 187 | continue; 188 | if (REGS[i].var->consumed > REGS[spilled].var->consumed) 189 | spilled = i; 190 | } 191 | 192 | spill_var(bb, REGS[spilled].var, spilled); 193 | REGS[spilled].var = var; 194 | REGS[spilled].polluted = 1; 195 | 196 | return spilled; 197 | } 198 | 199 | void spill_alive(basic_block_t *bb, insn_t *insn) 200 | { 201 | for (int i = 0; i < REG_CNT; i++) { 202 | if (!REGS[i].var) 203 | continue; 204 | if (check_live_out(bb, REGS[i].var)) { 205 | spill_var(bb, REGS[i].var, i); 206 | continue; 207 | } 208 | if (REGS[i].var->consumed > insn->idx) { 209 | spill_var(bb, REGS[i].var, i); 210 | continue; 211 | } 212 | } 213 | } 214 | 215 | void spill_live_out(basic_block_t *bb) 216 | { 217 | for (int i = 0; i < REG_CNT; i++) { 218 | if (!REGS[i].var) 219 | continue; 220 | if (!check_live_out(bb, REGS[i].var)) { 221 | REGS[i].var = NULL; 222 | REGS[i].polluted = 0; 223 | continue; 224 | } 225 | if (!var_check_killed(REGS[i].var, bb)) { 226 | REGS[i].var = NULL; 227 | REGS[i].polluted = 0; 228 | continue; 229 | } 230 | spill_var(bb, REGS[i].var, i); 231 | } 232 | } 233 | 234 | /* The operand of 'OP_push' should not been killed until function called. */ 235 | void extend_liveness(basic_block_t *bb, insn_t *insn, var_t *var, int offset) 236 | { 237 | if (check_live_out(bb, var)) 238 | return; 239 | if (insn->idx + offset > var->consumed) 240 | var->consumed = insn->idx + offset; 241 | } 242 | 243 | void reg_alloc(void) 244 | { 245 | /* TODO: .bss and .data section */ 246 | for (insn_t *global_insn = GLOBAL_FUNC->bbs->insn_list.head; global_insn; 247 | global_insn = global_insn->next) { 248 | ph2_ir_t *ir; 249 | int dest, src0; 250 | 251 | switch (global_insn->opcode) { 252 | case OP_allocat: 253 | if (global_insn->rd->array_size) { 254 | global_insn->rd->offset = GLOBAL_FUNC->stack_size; 255 | GLOBAL_FUNC->stack_size += PTR_SIZE; 256 | src0 = GLOBAL_FUNC->stack_size; 257 | if (global_insn->rd->is_ptr) 258 | GLOBAL_FUNC->stack_size += 259 | align_size(PTR_SIZE * global_insn->rd->array_size); 260 | else { 261 | GLOBAL_FUNC->stack_size += 262 | align_size(global_insn->rd->array_size * 263 | global_insn->rd->type->size); 264 | } 265 | 266 | dest = prepare_dest(GLOBAL_FUNC->bbs, global_insn->rd, -1, -1); 267 | ir = bb_add_ph2_ir(GLOBAL_FUNC->bbs, OP_global_address_of); 268 | ir->src0 = src0; 269 | ir->dest = dest; 270 | spill_var(GLOBAL_FUNC->bbs, global_insn->rd, dest); 271 | } else { 272 | global_insn->rd->offset = GLOBAL_FUNC->stack_size; 273 | if (global_insn->rd->is_ptr) 274 | GLOBAL_FUNC->stack_size += PTR_SIZE; 275 | else if (global_insn->rd->type != TY_int && 276 | global_insn->rd->type != TY_char && 277 | global_insn->rd->type != TY_bool) { 278 | GLOBAL_FUNC->stack_size += 279 | align_size(global_insn->rd->type->size); 280 | } else 281 | /* 'char' is aligned to one byte for the convenience */ 282 | GLOBAL_FUNC->stack_size += 4; 283 | } 284 | break; 285 | case OP_load_constant: 286 | case OP_load_data_address: 287 | dest = prepare_dest(GLOBAL_FUNC->bbs, global_insn->rd, -1, -1); 288 | ir = bb_add_ph2_ir(GLOBAL_FUNC->bbs, global_insn->opcode); 289 | ir->src0 = global_insn->rd->init_val; 290 | ir->dest = dest; 291 | break; 292 | case OP_assign: 293 | src0 = prepare_operand(GLOBAL_FUNC->bbs, global_insn->rs1, -1); 294 | dest = prepare_dest(GLOBAL_FUNC->bbs, global_insn->rd, src0, -1); 295 | ir = bb_add_ph2_ir(GLOBAL_FUNC->bbs, OP_assign); 296 | ir->src0 = src0; 297 | ir->dest = dest; 298 | spill_var(GLOBAL_FUNC->bbs, global_insn->rd, dest); 299 | /* release the unused constant number in register manually */ 300 | REGS[src0].polluted = 0; 301 | REGS[src0].var = NULL; 302 | break; 303 | default: 304 | printf("Unsupported global operation\n"); 305 | abort(); 306 | } 307 | } 308 | 309 | for (func_t *func = FUNC_LIST.head; func; func = func->next) { 310 | func->visited++; 311 | 312 | if (!strcmp(func->return_def.var_name, "main")) 313 | MAIN_BB = func->bbs; 314 | 315 | for (int i = 0; i < REG_CNT; i++) 316 | REGS[i].var = NULL; 317 | 318 | /* set arguments available */ 319 | for (int i = 0; i < func->num_params; i++) { 320 | REGS[i].var = func->param_defs[i].subscripts[0]; 321 | REGS[i].polluted = 1; 322 | } 323 | 324 | /* variadic function implementation */ 325 | if (func->va_args) { 326 | for (int i = 0; i < MAX_PARAMS; i++) { 327 | ph2_ir_t *ir = bb_add_ph2_ir(func->bbs, OP_store); 328 | 329 | if (i < func->num_params) 330 | func->param_defs[i].subscripts[0]->offset = 331 | func->stack_size; 332 | 333 | ir->src0 = i; 334 | ir->src1 = func->stack_size; 335 | func->stack_size += 4; 336 | } 337 | } 338 | 339 | for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) { 340 | int is_pushing_args = 0, args = 0; 341 | 342 | bb->visited++; 343 | 344 | for (insn_t *insn = bb->insn_list.head; insn; insn = insn->next) { 345 | func_t *callee_func; 346 | ph2_ir_t *ir; 347 | int dest, src0, src1; 348 | int sz, clear_reg; 349 | 350 | refresh(bb, insn); 351 | 352 | switch (insn->opcode) { 353 | case OP_unwound_phi: 354 | src0 = prepare_operand(bb, insn->rs1, -1); 355 | 356 | if (!insn->rd->offset) { 357 | insn->rd->offset = bb->belong_to->stack_size; 358 | bb->belong_to->stack_size += 4; 359 | } 360 | 361 | ir = bb_add_ph2_ir(bb, OP_store); 362 | ir->src0 = src0; 363 | ir->src1 = insn->rd->offset; 364 | break; 365 | case OP_allocat: 366 | if ((insn->rd->type == TY_void || 367 | insn->rd->type == TY_int || 368 | insn->rd->type == TY_char || 369 | insn->rd->type == TY_bool) && 370 | insn->rd->array_size == 0) 371 | break; 372 | 373 | insn->rd->offset = func->stack_size; 374 | func->stack_size += PTR_SIZE; 375 | src0 = func->stack_size; 376 | 377 | if (insn->rd->is_ptr) 378 | sz = PTR_SIZE; 379 | else { 380 | sz = insn->rd->type->size; 381 | } 382 | 383 | if (insn->rd->array_size) 384 | func->stack_size += 385 | align_size(insn->rd->array_size * sz); 386 | else 387 | func->stack_size += align_size(sz); 388 | 389 | dest = prepare_dest(bb, insn->rd, -1, -1); 390 | ir = bb_add_ph2_ir(bb, OP_address_of); 391 | ir->src0 = src0; 392 | ir->dest = dest; 393 | break; 394 | case OP_load_constant: 395 | case OP_load_data_address: 396 | if (insn->rd->consumed == -1) 397 | break; 398 | 399 | dest = prepare_dest(bb, insn->rd, -1, -1); 400 | ir = bb_add_ph2_ir(bb, insn->opcode); 401 | ir->src0 = insn->rd->init_val; 402 | ir->dest = dest; 403 | 404 | /* store global variable immediately after assignment */ 405 | if (insn->rd->is_global) { 406 | ir = bb_add_ph2_ir(bb, OP_global_store); 407 | ir->src0 = dest; 408 | ir->src1 = insn->rd->offset; 409 | REGS[dest].polluted = 0; 410 | } 411 | 412 | break; 413 | case OP_address_of: 414 | /* make sure variable is on stack */ 415 | if (!insn->rs1->offset) { 416 | insn->rs1->offset = bb->belong_to->stack_size; 417 | bb->belong_to->stack_size += 4; 418 | 419 | for (int i = 0; i < REG_CNT; i++) 420 | if (REGS[i].var == insn->rs1) { 421 | ir = bb_add_ph2_ir(bb, OP_store); 422 | ir->src0 = i; 423 | ir->src1 = insn->rs1->offset; 424 | } 425 | } 426 | 427 | dest = prepare_dest(bb, insn->rd, -1, -1); 428 | if (insn->rs1->is_global) 429 | ir = bb_add_ph2_ir(bb, OP_global_address_of); 430 | else 431 | ir = bb_add_ph2_ir(bb, OP_address_of); 432 | ir->src0 = insn->rs1->offset; 433 | ir->dest = dest; 434 | break; 435 | case OP_assign: 436 | if (insn->rd->consumed == -1) 437 | break; 438 | 439 | src0 = find_in_regs(insn->rs1); 440 | 441 | /* If operand is loaded from stack, clear the original slot 442 | * after moving. 443 | */ 444 | if (src0 > -1) 445 | clear_reg = 0; 446 | else { 447 | clear_reg = 1; 448 | src0 = prepare_operand(bb, insn->rs1, -1); 449 | } 450 | dest = prepare_dest(bb, insn->rd, src0, -1); 451 | ir = bb_add_ph2_ir(bb, OP_assign); 452 | ir->src0 = src0; 453 | ir->dest = dest; 454 | 455 | /* store global variable immediately after assignment */ 456 | if (insn->rd->is_global) { 457 | ir = bb_add_ph2_ir(bb, OP_global_store); 458 | ir->src0 = dest; 459 | ir->src1 = insn->rd->offset; 460 | REGS[dest].polluted = 0; 461 | } 462 | 463 | if (clear_reg) 464 | REGS[src0].var = NULL; 465 | 466 | break; 467 | case OP_read: 468 | src0 = prepare_operand(bb, insn->rs1, -1); 469 | dest = prepare_dest(bb, insn->rd, src0, -1); 470 | ir = bb_add_ph2_ir(bb, OP_read); 471 | ir->src0 = src0; 472 | ir->src1 = insn->sz; 473 | ir->dest = dest; 474 | break; 475 | case OP_write: 476 | if (insn->rs2->is_func) { 477 | src0 = prepare_operand(bb, insn->rs1, -1); 478 | ir = bb_add_ph2_ir(bb, OP_address_of_func); 479 | ir->src0 = src0; 480 | strcpy(ir->func_name, insn->rs2->var_name); 481 | } else { 482 | /* FIXME: Avoid outdated content in register after 483 | * storing, but causing some redundant spilling. 484 | */ 485 | spill_alive(bb, insn); 486 | src0 = prepare_operand(bb, insn->rs1, -1); 487 | src1 = prepare_operand(bb, insn->rs2, src0); 488 | ir = bb_add_ph2_ir(bb, OP_write); 489 | ir->src0 = src0; 490 | ir->src1 = src1; 491 | ir->dest = insn->sz; 492 | } 493 | break; 494 | case OP_branch: 495 | src0 = prepare_operand(bb, insn->rs1, -1); 496 | 497 | /* REGS[src0].var had been set to NULL, but the actual 498 | * content is still holded in the register. 499 | */ 500 | spill_live_out(bb); 501 | 502 | ir = bb_add_ph2_ir(bb, OP_branch); 503 | ir->src0 = src0; 504 | ir->then_bb = bb->then_; 505 | ir->else_bb = bb->else_; 506 | break; 507 | case OP_push: 508 | extend_liveness(bb, insn, insn->rs1, insn->sz); 509 | 510 | if (!is_pushing_args) { 511 | spill_alive(bb, insn); 512 | is_pushing_args = 1; 513 | } 514 | 515 | src0 = prepare_operand(bb, insn->rs1, -1); 516 | ir = bb_add_ph2_ir(bb, OP_assign); 517 | ir->src0 = src0; 518 | ir->dest = args++; 519 | REGS[ir->dest].var = insn->rs1; 520 | REGS[ir->dest].polluted = 0; 521 | break; 522 | case OP_call: 523 | callee_func = find_func(insn->str); 524 | if (!callee_func->num_params) 525 | spill_alive(bb, insn); 526 | 527 | ir = bb_add_ph2_ir(bb, OP_call); 528 | strcpy(ir->func_name, insn->str); 529 | 530 | is_pushing_args = 0; 531 | args = 0; 532 | 533 | for (int i = 0; i < REG_CNT; i++) 534 | REGS[i].var = NULL; 535 | 536 | break; 537 | case OP_indirect: 538 | if (!args) 539 | spill_alive(bb, insn); 540 | 541 | src0 = prepare_operand(bb, insn->rs1, -1); 542 | ir = bb_add_ph2_ir(bb, OP_load_func); 543 | ir->src0 = src0; 544 | 545 | bb_add_ph2_ir(bb, OP_indirect); 546 | 547 | is_pushing_args = 0; 548 | args = 0; 549 | break; 550 | case OP_func_ret: 551 | dest = prepare_dest(bb, insn->rd, -1, -1); 552 | ir = bb_add_ph2_ir(bb, OP_assign); 553 | ir->src0 = 0; 554 | ir->dest = dest; 555 | break; 556 | case OP_return: 557 | if (insn->rs1) 558 | src0 = prepare_operand(bb, insn->rs1, -1); 559 | else 560 | src0 = -1; 561 | 562 | ir = bb_add_ph2_ir(bb, OP_return); 563 | ir->src0 = src0; 564 | break; 565 | case OP_add: 566 | case OP_sub: 567 | case OP_mul: 568 | case OP_div: 569 | case OP_mod: 570 | case OP_lshift: 571 | case OP_rshift: 572 | case OP_eq: 573 | case OP_neq: 574 | case OP_gt: 575 | case OP_geq: 576 | case OP_lt: 577 | case OP_leq: 578 | case OP_bit_and: 579 | case OP_bit_or: 580 | case OP_bit_xor: 581 | src0 = prepare_operand(bb, insn->rs1, -1); 582 | src1 = prepare_operand(bb, insn->rs2, src0); 583 | dest = prepare_dest(bb, insn->rd, src0, src1); 584 | ir = bb_add_ph2_ir(bb, insn->opcode); 585 | ir->src0 = src0; 586 | ir->src1 = src1; 587 | ir->dest = dest; 588 | break; 589 | case OP_negate: 590 | case OP_bit_not: 591 | case OP_log_not: 592 | src0 = prepare_operand(bb, insn->rs1, -1); 593 | dest = prepare_dest(bb, insn->rd, src0, -1); 594 | ir = bb_add_ph2_ir(bb, insn->opcode); 595 | ir->src0 = src0; 596 | ir->dest = dest; 597 | break; 598 | case OP_trunc: 599 | case OP_sign_ext: 600 | src0 = prepare_operand(bb, insn->rs1, -1); 601 | dest = prepare_dest(bb, insn->rd, src0, -1); 602 | ir = bb_add_ph2_ir(bb, insn->opcode); 603 | ir->src1 = insn->sz; 604 | ir->src0 = src0; 605 | ir->dest = dest; 606 | break; 607 | default: 608 | printf("Unknown opcode\n"); 609 | abort(); 610 | } 611 | } 612 | 613 | if (bb->next) 614 | spill_live_out(bb); 615 | 616 | if (bb == func->exit) 617 | continue; 618 | 619 | /* append jump instruction for the normal block only */ 620 | if (!bb->next) 621 | continue; 622 | 623 | if (bb->next == func->exit) 624 | continue; 625 | 626 | /* jump to the beginning of loop or over the else block */ 627 | if (bb->next->visited == func->visited || 628 | bb->next->rpo != bb->rpo + 1) { 629 | ph2_ir_t *ir = bb_add_ph2_ir(bb, OP_jump); 630 | ir->next_bb = bb->next; 631 | } 632 | } 633 | 634 | /* handle implicit return */ 635 | for (int i = 0; i < MAX_BB_PRED; i++) { 636 | basic_block_t *bb = func->exit->prev[i].bb; 637 | if (!bb) 638 | continue; 639 | 640 | if (func->return_def.type != TY_void) 641 | continue; 642 | 643 | if (bb->insn_list.tail) 644 | if (bb->insn_list.tail->opcode == OP_return) 645 | continue; 646 | 647 | ph2_ir_t *ir = bb_add_ph2_ir(bb, OP_return); 648 | ir->src0 = -1; 649 | } 650 | } 651 | } 652 | 653 | void dump_ph2_ir(void) 654 | { 655 | for (int i = 0; i < ph2_ir_idx; i++) { 656 | ph2_ir_t *ph2_ir = PH2_IR_FLATTEN[i]; 657 | 658 | int rd = ph2_ir->dest + 48; 659 | int rs1 = ph2_ir->src0 + 48; 660 | int rs2 = ph2_ir->src1 + 48; 661 | 662 | switch (ph2_ir->op) { 663 | case OP_define: 664 | printf("%s:", ph2_ir->func_name); 665 | break; 666 | case OP_allocat: 667 | continue; 668 | case OP_assign: 669 | printf("\t%%x%c = %%x%c", rd, rs1); 670 | break; 671 | case OP_load_constant: 672 | printf("\tli %%x%c, $%d", rd, ph2_ir->src0); 673 | break; 674 | case OP_load_data_address: 675 | printf("\t%%x%c = .data(%d)", rd, ph2_ir->src0); 676 | break; 677 | case OP_address_of: 678 | printf("\t%%x%c = %%sp + %d", rd, ph2_ir->src0); 679 | break; 680 | case OP_global_address_of: 681 | printf("\t%%x%c = %%gp + %d", rd, ph2_ir->src0); 682 | break; 683 | case OP_branch: 684 | printf("\tbr %%x%c", rs1); 685 | break; 686 | case OP_jump: 687 | printf("\tj %s", ph2_ir->func_name); 688 | break; 689 | case OP_call: 690 | printf("\tcall @%s", ph2_ir->func_name); 691 | break; 692 | case OP_return: 693 | if (ph2_ir->src0 == -1) 694 | printf("\tret"); 695 | else 696 | printf("\tret %%x%c", rs1); 697 | break; 698 | case OP_load: 699 | printf("\tload %%x%c, %d(sp)", rd, ph2_ir->src0); 700 | break; 701 | case OP_store: 702 | printf("\tstore %%x%c, %d(sp)", rs1, ph2_ir->src1); 703 | break; 704 | case OP_global_load: 705 | printf("\tload %%x%c, %d(gp)", rd, ph2_ir->src0); 706 | break; 707 | case OP_global_store: 708 | printf("\tstore %%x%c, %d(gp)", rs1, ph2_ir->src1); 709 | break; 710 | case OP_read: 711 | printf("\t%%x%c = (%%x%c)", rd, rs1); 712 | break; 713 | case OP_write: 714 | printf("\t(%%x%c) = %%x%c", rs2, rs1); 715 | break; 716 | case OP_address_of_func: 717 | printf("\t(%%x%c) = @%s", rs1, ph2_ir->func_name); 718 | break; 719 | case OP_load_func: 720 | printf("\tload %%t0, %d(sp)", ph2_ir->src0); 721 | break; 722 | case OP_global_load_func: 723 | printf("\tload %%t0, %d(gp)", ph2_ir->src0); 724 | break; 725 | case OP_indirect: 726 | printf("\tindirect call @(%%t0)"); 727 | break; 728 | case OP_negate: 729 | printf("\tneg %%x%c, %%x%c", rd, rs1); 730 | break; 731 | case OP_add: 732 | printf("\t%%x%c = add %%x%c, %%x%c", rd, rs1, rs2); 733 | break; 734 | case OP_sub: 735 | printf("\t%%x%c = sub %%x%c, %%x%c", rd, rs1, rs2); 736 | break; 737 | case OP_mul: 738 | printf("\t%%x%c = mul %%x%c, %%x%c", rd, rs1, rs2); 739 | break; 740 | case OP_div: 741 | printf("\t%%x%c = div %%x%c, %%x%c", rd, rs1, rs2); 742 | break; 743 | case OP_mod: 744 | printf("\t%%x%c = mod %%x%c, %%x%c", rd, rs1, rs2); 745 | break; 746 | case OP_eq: 747 | printf("\t%%x%c = eq %%x%c, %%x%c", rd, rs1, rs2); 748 | break; 749 | case OP_neq: 750 | printf("\t%%x%c = neq %%x%c, %%x%c", rd, rs1, rs2); 751 | break; 752 | case OP_gt: 753 | printf("\t%%x%c = gt %%x%c, %%x%c", rd, rs1, rs2); 754 | break; 755 | case OP_lt: 756 | printf("\t%%x%c = lt %%x%c, %%x%c", rd, rs1, rs2); 757 | break; 758 | case OP_geq: 759 | printf("\t%%x%c = geq %%x%c, %%x%c", rd, rs1, rs2); 760 | break; 761 | case OP_leq: 762 | printf("\t%%x%c = leq %%x%c, %%x%c", rd, rs1, rs2); 763 | break; 764 | case OP_bit_and: 765 | printf("\t%%x%c = and %%x%c, %%x%c", rd, rs1, rs2); 766 | break; 767 | case OP_bit_or: 768 | printf("\t%%x%c = or %%x%c, %%x%c", rd, rs1, rs2); 769 | break; 770 | case OP_bit_not: 771 | printf("\t%%x%c = not %%x%c", rd, rs1); 772 | break; 773 | case OP_bit_xor: 774 | printf("\t%%x%c = xor %%x%c, %%x%c", rd, rs1, rs2); 775 | break; 776 | case OP_log_not: 777 | printf("\t%%x%c = not %%x%c", rd, rs1); 778 | break; 779 | case OP_rshift: 780 | printf("\t%%x%c = rshift %%x%c, %%x%c", rd, rs1, rs2); 781 | break; 782 | case OP_lshift: 783 | printf("\t%%x%c = lshift %%x%c, %%x%c", rd, rs1, rs2); 784 | break; 785 | case OP_trunc: 786 | printf("\t%%x%c = trunc %%x%c, %d", rd, rs1, ph2_ir->src1); 787 | break; 788 | case OP_sign_ext: 789 | printf("\t%%x%c = sign_ext %%x%c, %d", rd, rs1, ph2_ir->src1); 790 | break; 791 | default: 792 | break; 793 | } 794 | printf("\n"); 795 | } 796 | } 797 | -------------------------------------------------------------------------------- /src/riscv-codegen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * shecc - Self-Hosting and Educational C Compiler. 3 | * 4 | * shecc is freely redistributable under the BSD 2 clause license. See the 5 | * file "LICENSE" for information on usage and redistribution of this file. 6 | */ 7 | 8 | /* Translate IR to target machine code */ 9 | 10 | #include "defs.h" 11 | #include "globals.c" 12 | #include "riscv.c" 13 | 14 | void update_elf_offset(ph2_ir_t *ph2_ir) 15 | { 16 | switch (ph2_ir->op) { 17 | case OP_load_constant: 18 | if (ph2_ir->src0 < -2048 || ph2_ir->src0 > 2047) 19 | elf_offset += 8; 20 | else 21 | elf_offset += 4; 22 | return; 23 | case OP_address_of: 24 | case OP_global_address_of: 25 | if (ph2_ir->src0 < -2048 || ph2_ir->src0 > 2047) 26 | elf_offset += 12; 27 | else 28 | elf_offset += 4; 29 | return; 30 | case OP_assign: 31 | elf_offset += 4; 32 | return; 33 | case OP_load: 34 | case OP_global_load: 35 | if (ph2_ir->src0 < -2048 || ph2_ir->src0 > 2047) 36 | elf_offset += 16; 37 | else 38 | elf_offset += 4; 39 | return; 40 | case OP_store: 41 | case OP_global_store: 42 | if (ph2_ir->src1 < -2048 || ph2_ir->src1 > 2047) 43 | elf_offset += 16; 44 | else 45 | elf_offset += 4; 46 | return; 47 | case OP_read: 48 | case OP_write: 49 | case OP_jump: 50 | case OP_call: 51 | case OP_load_func: 52 | case OP_indirect: 53 | case OP_add: 54 | case OP_sub: 55 | case OP_lshift: 56 | case OP_rshift: 57 | case OP_gt: 58 | case OP_lt: 59 | case OP_bit_and: 60 | case OP_bit_or: 61 | case OP_bit_xor: 62 | case OP_negate: 63 | case OP_bit_not: 64 | elf_offset += 4; 65 | return; 66 | case OP_mul: 67 | if (hard_mul_div) 68 | elf_offset += 4; 69 | else 70 | elf_offset += 52; 71 | return; 72 | case OP_div: 73 | case OP_mod: 74 | if (hard_mul_div) 75 | elf_offset += 4; 76 | else 77 | elf_offset += 108; 78 | return; 79 | case OP_load_data_address: 80 | case OP_neq: 81 | case OP_geq: 82 | case OP_leq: 83 | case OP_log_not: 84 | elf_offset += 8; 85 | return; 86 | case OP_address_of_func: 87 | case OP_eq: 88 | elf_offset += 12; 89 | return; 90 | case OP_branch: 91 | elf_offset += 20; 92 | return; 93 | case OP_return: 94 | elf_offset += 24; 95 | return; 96 | case OP_trunc: 97 | elf_offset += 4; 98 | return; 99 | case OP_sign_ext: 100 | elf_offset += 12; 101 | return; 102 | default: 103 | fatal("Unknown opcode"); 104 | } 105 | } 106 | 107 | void cfg_flatten(void) 108 | { 109 | func_t *func = find_func("__syscall"); 110 | func->bbs->elf_offset = 48; /* offset of start + exit in codegen */ 111 | 112 | elf_offset = 84; /* offset of start + exit + syscall in codegen */ 113 | GLOBAL_FUNC->bbs->elf_offset = elf_offset; 114 | 115 | for (ph2_ir_t *ph2_ir = GLOBAL_FUNC->bbs->ph2_ir_list.head; ph2_ir; 116 | ph2_ir = ph2_ir->next) { 117 | update_elf_offset(ph2_ir); 118 | } 119 | 120 | /* prepare 'argc' and 'argv', then proceed to 'main' function */ 121 | elf_offset += 24; 122 | 123 | for (func = FUNC_LIST.head; func; func = func->next) { 124 | /* reserve stack */ 125 | ph2_ir_t *flatten_ir = add_ph2_ir(OP_define); 126 | flatten_ir->src0 = func->stack_size; 127 | 128 | for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) { 129 | bb->elf_offset = elf_offset; 130 | 131 | if (bb == func->bbs) { 132 | /* save ra, sp */ 133 | elf_offset += 16; 134 | } 135 | 136 | for (ph2_ir_t *insn = bb->ph2_ir_list.head; insn; 137 | insn = insn->next) { 138 | flatten_ir = add_existed_ph2_ir(insn); 139 | 140 | if (insn->op == OP_return) { 141 | /* restore sp */ 142 | flatten_ir->src1 = bb->belong_to->stack_size; 143 | } 144 | 145 | update_elf_offset(flatten_ir); 146 | } 147 | } 148 | } 149 | } 150 | 151 | void emit(int code) 152 | { 153 | elf_write_int(elf_code, code); 154 | } 155 | 156 | void emit_ph2_ir(ph2_ir_t *ph2_ir) 157 | { 158 | func_t *func; 159 | int rd = ph2_ir->dest + 10; 160 | int rs1 = ph2_ir->src0 + 10; 161 | int rs2 = ph2_ir->src1 + 10; 162 | int ofs; 163 | 164 | /* Prepare the variables to reuse the same code for 165 | * the instruction sequence of 166 | * 1. division and modulo. 167 | * 2. load and store operations. 168 | * 3. address-of operations. 169 | */ 170 | rv_reg interm, divisor_mask = __t1; 171 | 172 | switch (ph2_ir->op) { 173 | case OP_define: 174 | emit(__lui(__t0, rv_hi(ph2_ir->src0 + 4))); 175 | emit(__addi(__t0, __t0, rv_lo(ph2_ir->src0 + 4))); 176 | emit(__sub(__sp, __sp, __t0)); 177 | emit(__sw(__ra, __sp, 0)); 178 | return; 179 | case OP_load_constant: 180 | if (ph2_ir->src0 < -2048 || ph2_ir->src0 > 2047) { 181 | emit(__lui(rd, rv_hi(ph2_ir->src0))); 182 | emit(__addi(rd, rd, rv_lo(ph2_ir->src0))); 183 | 184 | } else 185 | emit(__addi(rd, __zero, ph2_ir->src0)); 186 | return; 187 | case OP_address_of: 188 | case OP_global_address_of: 189 | interm = ph2_ir->op == OP_address_of ? __sp : __gp; 190 | if (ph2_ir->src0 < -2048 || ph2_ir->src0 > 2047) { 191 | emit(__lui(__t0, rv_hi(ph2_ir->src0))); 192 | emit(__addi(__t0, __t0, rv_lo(ph2_ir->src0))); 193 | emit(__add(rd, interm, __t0)); 194 | } else 195 | emit(__addi(rd, interm, ph2_ir->src0)); 196 | return; 197 | case OP_assign: 198 | emit(__addi(rd, rs1, 0)); 199 | return; 200 | case OP_load: 201 | case OP_global_load: 202 | interm = ph2_ir->op == OP_load ? __sp : __gp; 203 | if (ph2_ir->src0 < -2048 || ph2_ir->src0 > 2047) { 204 | emit(__lui(__t0, rv_hi(ph2_ir->src0))); 205 | emit(__addi(__t0, __t0, rv_lo(ph2_ir->src0))); 206 | emit(__add(__t0, interm, __t0)); 207 | emit(__lw(rd, __t0, 0)); 208 | } else 209 | emit(__lw(rd, interm, ph2_ir->src0)); 210 | return; 211 | case OP_store: 212 | case OP_global_store: 213 | interm = ph2_ir->op == OP_store ? __sp : __gp; 214 | if (ph2_ir->src1 < -2048 || ph2_ir->src1 > 2047) { 215 | emit(__lui(__t0, rv_hi(ph2_ir->src1))); 216 | emit(__addi(__t0, __t0, rv_lo(ph2_ir->src1))); 217 | emit(__add(__t0, interm, __t0)); 218 | emit(__sw(rs1, __t0, 0)); 219 | } else 220 | emit(__sw(rs1, interm, ph2_ir->src1)); 221 | return; 222 | case OP_read: 223 | if (ph2_ir->src1 == 1) 224 | emit(__lb(rd, rs1, 0)); 225 | else if (ph2_ir->src1 == 4) 226 | emit(__lw(rd, rs1, 0)); 227 | else 228 | abort(); 229 | return; 230 | case OP_write: 231 | if (ph2_ir->dest == 1) 232 | emit(__sb(rs2, rs1, 0)); 233 | else if (ph2_ir->dest == 4) 234 | emit(__sw(rs2, rs1, 0)); 235 | else 236 | abort(); 237 | return; 238 | case OP_branch: 239 | ofs = elf_code_start + ph2_ir->then_bb->elf_offset; 240 | emit(__lui(__t0, rv_hi(ofs))); 241 | emit(__addi(__t0, __t0, rv_lo(ofs))); 242 | emit(__beq(rs1, __zero, 8)); 243 | emit(__jalr(__zero, __t0, 0)); 244 | emit(__jal(__zero, ph2_ir->else_bb->elf_offset - elf_code->size)); 245 | return; 246 | case OP_jump: 247 | emit(__jal(__zero, ph2_ir->next_bb->elf_offset - elf_code->size)); 248 | return; 249 | case OP_call: 250 | func = find_func(ph2_ir->func_name); 251 | emit(__jal(__ra, func->bbs->elf_offset - elf_code->size)); 252 | return; 253 | case OP_load_data_address: 254 | emit(__lui(rd, rv_hi(elf_data_start + ph2_ir->src0))); 255 | emit(__addi(rd, rd, rv_lo(elf_data_start + ph2_ir->src0))); 256 | return; 257 | case OP_address_of_func: 258 | func = find_func(ph2_ir->func_name); 259 | ofs = elf_code_start + func->bbs->elf_offset; 260 | emit(__lui(__t0, rv_hi(ofs))); 261 | emit(__addi(__t0, __t0, rv_lo(ofs))); 262 | emit(__sw(__t0, rs1, 0)); 263 | return; 264 | case OP_load_func: 265 | emit(__addi(__t0, rs1, 0)); 266 | return; 267 | case OP_indirect: 268 | emit(__jalr(__ra, __t0, 0)); 269 | return; 270 | case OP_return: 271 | if (ph2_ir->src0 == -1) 272 | emit(__addi(__zero, __zero, 0)); 273 | else 274 | emit(__addi(__a0, rs1, 0)); 275 | emit(__lw(__ra, __sp, 0)); 276 | emit(__lui(__t0, rv_hi(ph2_ir->src1 + 4))); 277 | emit(__addi(__t0, __t0, rv_lo(ph2_ir->src1 + 4))); 278 | emit(__add(__sp, __sp, __t0)); 279 | emit(__jalr(__zero, __ra, 0)); 280 | return; 281 | case OP_add: 282 | emit(__add(rd, rs1, rs2)); 283 | return; 284 | case OP_sub: 285 | emit(__sub(rd, rs1, rs2)); 286 | return; 287 | case OP_mul: 288 | if (hard_mul_div) 289 | emit(__mul(rd, rs1, rs2)); 290 | else { 291 | emit(__addi(__t0, __zero, 0)); 292 | emit(__addi(__t1, __zero, 0)); 293 | emit(__addi(__t3, rs1, 0)); 294 | emit(__addi(__t4, rs2, 0)); 295 | emit(__beq(__t3, __zero, 32)); 296 | emit(__beq(__t4, __zero, 28)); 297 | emit(__andi(__t1, __t4, 1)); 298 | emit(__beq(__t1, __zero, 8)); 299 | emit(__add(__t0, __t0, __t3)); 300 | emit(__slli(__t3, __t3, 1)); 301 | emit(__srli(__t4, __t4, 1)); 302 | emit(__jal(__zero, -28)); 303 | emit(__addi(rd, __t0, 0)); 304 | } 305 | return; 306 | case OP_div: 307 | case OP_mod: 308 | if (hard_mul_div) { 309 | if (ph2_ir->op == OP_div) 310 | emit(__div(rd, rs1, rs2)); 311 | else 312 | emit(__mod(rd, rs1, rs2)); 313 | return; 314 | } 315 | interm = __t0; 316 | /* div/mod emulation */ 317 | if (ph2_ir->op == OP_mod) { 318 | /* If the requested operation is modulo, the result will be stored 319 | * in __t2. The sign of the divisor is irrelevant for determining 320 | * the result's sign. 321 | */ 322 | interm = __t2; 323 | divisor_mask = __zero; 324 | } 325 | /* Obtain absolute values of the dividend and divisor */ 326 | emit(__addi(__t2, rs1, 0)); 327 | emit(__addi(__t3, rs2, 0)); 328 | emit(__srai(__t0, __t2, 31)); 329 | emit(__add(__t2, __t2, __t0)); 330 | emit(__xor(__t2, __t2, __t0)); 331 | emit(__srai(__t1, __t3, 31)); 332 | emit(__add(__t3, __t3, __t1)); 333 | emit(__xor(__t3, __t3, __t1)); 334 | emit(__xor(__t5, __t0, divisor_mask)); 335 | /* Unsigned integer division */ 336 | emit(__addi(__t0, __zero, 0)); 337 | emit(__addi(__t1, __zero, 1)); 338 | emit(__beq(__t3, __zero, 52)); 339 | emit(__beq(__t2, __zero, 48)); 340 | emit(__beq(__t2, __t3, 20)); 341 | emit(__bltu(__t2, __t3, 16)); 342 | emit(__slli(__t3, __t3, 1)); 343 | emit(__slli(__t1, __t1, 1)); 344 | emit(__jal(__zero, -16)); 345 | emit(__bltu(__t2, __t3, 12)); 346 | emit(__sub(__t2, __t2, __t3)); 347 | emit(__add(__t0, __t0, __t1)); 348 | emit(__srli(__t1, __t1, 1)); 349 | emit(__srli(__t3, __t3, 1)); 350 | emit(__bne(__t1, __zero, -20)); 351 | emit(__addi(rd, interm, 0)); 352 | /* Handle the correct sign for the quotient or remainder */ 353 | emit(__beq(__t5, __zero, 8)); 354 | emit(__sub(rd, __zero, rd)); 355 | return; 356 | case OP_lshift: 357 | emit(__sll(rd, rs1, rs2)); 358 | return; 359 | case OP_rshift: 360 | emit(__sra(rd, rs1, rs2)); 361 | return; 362 | case OP_eq: 363 | emit(__sub(rd, rs1, rs2)); 364 | emit(__sltu(rd, __zero, rd)); 365 | emit(__xori(rd, rd, 1)); 366 | return; 367 | case OP_neq: 368 | emit(__sub(rd, rs1, rs2)); 369 | emit(__sltu(rd, __zero, rd)); 370 | return; 371 | case OP_gt: 372 | emit(__slt(rd, rs2, rs1)); 373 | return; 374 | case OP_geq: 375 | emit(__slt(rd, rs1, rs2)); 376 | emit(__xori(rd, rd, 1)); 377 | return; 378 | case OP_lt: 379 | emit(__slt(rd, rs1, rs2)); 380 | return; 381 | case OP_leq: 382 | emit(__slt(rd, rs2, rs1)); 383 | emit(__xori(rd, rd, 1)); 384 | return; 385 | case OP_negate: 386 | emit(__sub(rd, __zero, rs1)); 387 | return; 388 | case OP_bit_not: 389 | emit(__xori(rd, rs1, -1)); 390 | return; 391 | case OP_bit_and: 392 | emit(__and(rd, rs1, rs2)); 393 | return; 394 | case OP_bit_or: 395 | emit(__or(rd, rs1, rs2)); 396 | return; 397 | case OP_bit_xor: 398 | emit(__xor(rd, rs1, rs2)); 399 | return; 400 | case OP_log_not: 401 | emit(__sltu(rd, __zero, rs1)); 402 | emit(__xori(rd, rd, 1)); 403 | return; 404 | case OP_trunc: 405 | if (ph2_ir->src1 == 1) 406 | rs2 = 0xFF; 407 | else if (ph2_ir->src1 == 2) 408 | rs2 = 0xFFFF; 409 | else if (ph2_ir->src1 == 4) 410 | rs2 = 0xFFFFFFFF; 411 | else 412 | fatal("Unsupported truncation operation with invalid target size"); 413 | 414 | emit(__andi(rd, rs1, rs2)); 415 | return; 416 | case OP_sign_ext: 417 | /* TODO: Allow to sign extends to other types */ 418 | emit(__andi(rd, rs1, 0xFF)); 419 | emit(__slli(rd, rd, 24)); 420 | emit(__srai(rd, rd, 24)); 421 | /* TODO: Allow user to switch to Zbb extension if needed */ 422 | /* emit(__sext_b(rd, rs1)); */ 423 | return; 424 | default: 425 | fatal("Unknown opcode"); 426 | } 427 | } 428 | 429 | void code_generate(void) 430 | { 431 | elf_data_start = elf_code_start + elf_offset; 432 | 433 | /* start */ 434 | emit(__lui(__t0, rv_hi(GLOBAL_FUNC->stack_size))); 435 | emit(__addi(__t0, __t0, rv_lo(GLOBAL_FUNC->stack_size))); 436 | emit(__sub(__sp, __sp, __t0)); 437 | emit(__addi(__gp, __sp, 0)); 438 | emit(__jal(__ra, GLOBAL_FUNC->bbs->elf_offset - elf_code->size)); 439 | 440 | /* exit */ 441 | emit(__lui(__t0, rv_hi(GLOBAL_FUNC->stack_size))); 442 | emit(__addi(__t0, __t0, rv_lo(GLOBAL_FUNC->stack_size))); 443 | emit(__add(__gp, __gp, __t0)); 444 | emit(__addi(__sp, __gp, 0)); 445 | emit(__addi(__a0, __a0, 0)); 446 | emit(__addi(__a7, __zero, 93)); 447 | emit(__ecall()); 448 | 449 | /* syscall */ 450 | emit(__addi(__a7, __a0, 0)); 451 | emit(__addi(__a0, __a1, 0)); 452 | emit(__addi(__a1, __a2, 0)); 453 | emit(__addi(__a2, __a3, 0)); 454 | emit(__addi(__a3, __a4, 0)); 455 | emit(__addi(__a4, __a5, 0)); 456 | emit(__addi(__a5, __a6, 0)); 457 | emit(__ecall()); 458 | emit(__jalr(__zero, __ra, 0)); 459 | 460 | ph2_ir_t *ph2_ir; 461 | for (ph2_ir = GLOBAL_FUNC->bbs->ph2_ir_list.head; ph2_ir; 462 | ph2_ir = ph2_ir->next) 463 | emit_ph2_ir(ph2_ir); 464 | 465 | /* prepare 'argc' and 'argv', then proceed to 'main' function */ 466 | emit(__lui(__t0, rv_hi(GLOBAL_FUNC->stack_size))); 467 | emit(__addi(__t0, __t0, rv_lo(GLOBAL_FUNC->stack_size))); 468 | emit(__add(__t0, __gp, __t0)); 469 | emit(__lw(__a0, __t0, 0)); 470 | emit(__addi(__a1, __t0, 4)); 471 | emit(__jal(__zero, MAIN_BB->elf_offset - elf_code->size)); 472 | 473 | for (int i = 0; i < ph2_ir_idx; i++) { 474 | ph2_ir = PH2_IR_FLATTEN[i]; 475 | emit_ph2_ir(ph2_ir); 476 | } 477 | } 478 | -------------------------------------------------------------------------------- /src/riscv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * shecc - Self-Hosting and Educational C Compiler. 3 | * 4 | * shecc is freely redistributable under the BSD 2 clause license. See the 5 | * file "LICENSE" for information on usage and redistribution of this file. 6 | */ 7 | 8 | /* RISC-V instruction encoding */ 9 | 10 | /* opcodes */ 11 | typedef enum { 12 | /* R type */ 13 | rv_add = 51 /* 0b110011 + (0 << 12) */, 14 | rv_sub = 1073741875 /* 0b110011 + (0 << 12) + (0x20 << 25) */, 15 | rv_xor = 16435 /* 0b110011 + (4 << 12) */, 16 | rv_or = 24627 /* 0b110011 + (6 << 12) */, 17 | rv_and = 28723 /* 0b110011 + (7 << 12) */, 18 | rv_sll = 4147 /* 0b110011 + (1 << 12) */, 19 | rv_srl = 20531 /* 0b110011 + (5 << 12) */, 20 | rv_sra = 1073762355 /* 0b110011 + (5 << 12) + (0x20 << 25) */, 21 | rv_slt = 8243 /* 0b110011 + (2 << 12) */, 22 | rv_sltu = 12339 /* 0b110011 + (3 << 12) */, 23 | /* I type */ 24 | rv_addi = 19 /* 0b0010011 */, 25 | rv_xori = 16403 /* 0b0010011 + (4 << 12) */, 26 | rv_ori = 24595 /* 0b0010011 + (6 << 12) */, 27 | rv_andi = 28691 /* 0b0010011 + (7 << 12) */, 28 | rv_slli = 4115 /* 0b0010011 + (1 << 12) */, 29 | rv_srli = 20499 /* 0b0010011 + (5 << 12) */, 30 | rv_srai = 1073762323 /* 0b0010011 + (5 << 12) + (0x20 << 25) */, 31 | rv_slti = 8211 /* 0b0010011 + (2 << 12) */, 32 | rv_sltiu = 12307 /* 0b0010011 + (3 << 12) */, 33 | rv_sext_b = 34 | 1614811155 /* 0b0010011 + (1 << 12) + (0x604 << 20) (imm included)*/, 35 | /* load/store */ 36 | rv_lb = 3 /* 0b11 */, 37 | rv_lh = 4099 /* 0b11 + (1 << 12) */, 38 | rv_lw = 8195 /* 0b11 + (2 << 12) */, 39 | rv_lbu = 16387 /* 0b11 + (4 << 12) */, 40 | rv_lhu = 20483 /* 0b11 + (5 << 12) */, 41 | rv_sb = 35 /* 0b0100011 */, 42 | rv_sh = 4131 /* 0b0100011 + (1 << 12) */, 43 | rv_sw = 8227 /* 0b0100011 + (2 << 12) */, 44 | /* branch */ 45 | rv_beq = 99 /* 0b1100011 */, 46 | rv_bne = 4195 /* 0b1100011 + (1 << 12) */, 47 | rv_blt = 16483 /* 0b1100011 + (4 << 12) */, 48 | rv_bge = 20579 /* 0b1100011 + (5 << 12) */, 49 | rv_bltu = 24675 /* 0b1100011 + (6 << 12) */, 50 | rv_bgeu = 28771 /* 0b1100011 + (7 << 12) */, 51 | /* jumps */ 52 | rv_jal = 111 /* 0b1101111 */, 53 | rv_jalr = 103 /* 0b1100111 */, 54 | /* misc */ 55 | rv_lui = 55 /* 0b0110111 */, 56 | rv_auipc = 23 /* 0b0010111 */, 57 | rv_ecall = 115 /* 0b1110011 */, 58 | rv_ebreak = 1048691 /* 0b1110011 + (1 << 20) */, 59 | /* m */ 60 | rv_mul = 33554483 /* 0b0110011 + (1 << 25) */, 61 | rv_div = 33570867 /* 0b0110011 + (1 << 25) + (4 << 12) */, 62 | rv_mod = 33579059 /* 0b0110011 + (1 << 25) + (6 << 12) */ 63 | } rv_op; 64 | 65 | /* registers */ 66 | typedef enum { 67 | __zero = 0, 68 | __ra = 1, 69 | __sp = 2, 70 | __gp = 3, 71 | __tp = 4, 72 | __t0 = 5, 73 | __t1 = 6, 74 | __t2 = 7, 75 | __s0 = 8, 76 | __s1 = 9, 77 | __a0 = 10, 78 | __a1 = 11, 79 | __a2 = 12, 80 | __a3 = 13, 81 | __a4 = 14, 82 | __a5 = 15, 83 | __a6 = 16, 84 | __a7 = 17, 85 | __s2 = 18, 86 | __s3 = 19, 87 | __s4 = 20, 88 | __s5 = 21, 89 | __s6 = 22, 90 | __s7 = 23, 91 | __s8 = 24, 92 | __s9 = 25, 93 | __s10 = 26, 94 | __s11 = 27, 95 | __t3 = 28, 96 | __t4 = 29, 97 | __t5 = 30, 98 | __t6 = 31 99 | } rv_reg; 100 | 101 | int rv_extract_bits(int imm, int i_start, int i_end, int d_start, int d_end) 102 | { 103 | int v; 104 | 105 | if (d_end - d_start != i_end - i_start || i_start > i_end || 106 | d_start > d_end) 107 | error("Invalid bit copy"); 108 | 109 | v = imm >> i_start; 110 | v &= ((2 << (i_end - i_start)) - 1); 111 | v <<= d_start; 112 | return v; 113 | } 114 | 115 | int rv_hi(int val) 116 | { 117 | return val + ((val & (1 << 11)) << 1); 118 | } 119 | 120 | int rv_lo(int val) 121 | { 122 | return (val & 0xFFF) - ((val & (1 << 11)) << 1); 123 | } 124 | 125 | int rv_encode_R(rv_op op, rv_reg rd, rv_reg rs1, rv_reg rs2) 126 | { 127 | return op + (rd << 7) + (rs1 << 15) + (rs2 << 20); 128 | } 129 | 130 | int rv_encode_I(rv_op op, rv_reg rd, rv_reg rs1, int imm) 131 | { 132 | if (imm > 2047 || imm < -2048) 133 | error("Offset too large"); 134 | 135 | if (imm < 0) { 136 | imm += 4096; 137 | imm &= (1 << 13) - 1; 138 | } 139 | return op + (rd << 7) + (rs1 << 15) + (imm << 20); 140 | } 141 | 142 | int rv_encode_S(rv_op op, rv_reg rs1, rv_reg rs2, int imm) 143 | { 144 | if (imm > 2047 || imm < -2048) 145 | error("Offset too large"); 146 | 147 | if (imm < 0) { 148 | imm += 4096; 149 | imm &= (1 << 13) - 1; 150 | } 151 | return op + (rs1 << 15) + (rs2 << 20) + rv_extract_bits(imm, 0, 4, 7, 11) + 152 | rv_extract_bits(imm, 5, 11, 25, 31); 153 | } 154 | 155 | int rv_encode_B(rv_op op, rv_reg rs1, rv_reg rs2, int imm) 156 | { 157 | int sign = 0; 158 | 159 | /* 13 signed bits, with bit zero ignored */ 160 | if (imm > 4095 || imm < -4096) 161 | error("Offset too large"); 162 | 163 | if (imm < 0) 164 | sign = 1; 165 | 166 | return op + (rs1 << 15) + (rs2 << 20) + rv_extract_bits(imm, 11, 11, 7, 7) + 167 | rv_extract_bits(imm, 1, 4, 8, 11) + 168 | rv_extract_bits(imm, 5, 10, 25, 30) + (sign << 31); 169 | } 170 | 171 | int rv_encode_J(rv_op op, rv_reg rd, int imm) 172 | { 173 | int sign = 0; 174 | 175 | if (imm < 0) { 176 | sign = 1; 177 | imm = -imm; 178 | imm = (1 << 21) - imm; 179 | } 180 | return op + (rd << 7) + rv_extract_bits(imm, 1, 10, 21, 30) + 181 | rv_extract_bits(imm, 11, 11, 20, 20) + 182 | rv_extract_bits(imm, 12, 19, 12, 19) + (sign << 31); 183 | } 184 | 185 | int rv_encode_U(rv_op op, rv_reg rd, int imm) 186 | { 187 | return op + (rd << 7) + rv_extract_bits(imm, 12, 31, 12, 31); 188 | } 189 | 190 | int __add(rv_reg rd, rv_reg rs1, rv_reg rs2) 191 | { 192 | return rv_encode_R(rv_add, rd, rs1, rs2); 193 | } 194 | 195 | int __sub(rv_reg rd, rv_reg rs1, rv_reg rs2) 196 | { 197 | return rv_encode_R(rv_sub, rd, rs1, rs2); 198 | } 199 | 200 | int __xor(rv_reg rd, rv_reg rs1, rv_reg rs2) 201 | { 202 | return rv_encode_R(rv_xor, rd, rs1, rs2); 203 | } 204 | 205 | int __or(rv_reg rd, rv_reg rs1, rv_reg rs2) 206 | { 207 | return rv_encode_R(rv_or, rd, rs1, rs2); 208 | } 209 | 210 | int __and(rv_reg rd, rv_reg rs1, rv_reg rs2) 211 | { 212 | return rv_encode_R(rv_and, rd, rs1, rs2); 213 | } 214 | 215 | int __sll(rv_reg rd, rv_reg rs1, rv_reg rs2) 216 | { 217 | return rv_encode_R(rv_sll, rd, rs1, rs2); 218 | } 219 | 220 | int __srl(rv_reg rd, rv_reg rs1, rv_reg rs2) 221 | { 222 | return rv_encode_R(rv_srl, rd, rs1, rs2); 223 | } 224 | 225 | int __sra(rv_reg rd, rv_reg rs1, rv_reg rs2) 226 | { 227 | return rv_encode_R(rv_sra, rd, rs1, rs2); 228 | } 229 | 230 | int __slt(rv_reg rd, rv_reg rs1, rv_reg rs2) 231 | { 232 | return rv_encode_R(rv_slt, rd, rs1, rs2); 233 | } 234 | 235 | int __sltu(rv_reg rd, rv_reg rs1, rv_reg rs2) 236 | { 237 | return rv_encode_R(rv_sltu, rd, rs1, rs2); 238 | } 239 | 240 | int __addi(rv_reg rd, rv_reg rs1, int imm) 241 | { 242 | return rv_encode_I(rv_addi, rd, rs1, imm); 243 | } 244 | 245 | int __xori(rv_reg rd, rv_reg rs1, int imm) 246 | { 247 | return rv_encode_I(rv_xori, rd, rs1, imm); 248 | } 249 | 250 | int __ori(rv_reg rd, rv_reg rs1, int imm) 251 | { 252 | return rv_encode_I(rv_ori, rd, rs1, imm); 253 | } 254 | 255 | int __andi(rv_reg rd, rv_reg rs1, int imm) 256 | { 257 | return rv_encode_I(rv_andi, rd, rs1, imm); 258 | } 259 | 260 | int __slli(rv_reg rd, rv_reg rs1, int imm) 261 | { 262 | return rv_encode_I(rv_slli, rd, rs1, imm); 263 | } 264 | 265 | int __srli(rv_reg rd, rv_reg rs1, int imm) 266 | { 267 | return rv_encode_I(rv_srli, rd, rs1, imm); 268 | } 269 | 270 | int __srai(rv_reg rd, rv_reg rs1, int imm) 271 | { 272 | return rv_encode_I(rv_srai, rd, rs1, imm); 273 | } 274 | 275 | int __slti(rv_reg rd, rv_reg rs1, int imm) 276 | { 277 | return rv_encode_I(rv_slti, rd, rs1, imm); 278 | } 279 | 280 | int __sltiu(rv_reg rd, rv_reg rs1, int imm) 281 | { 282 | return rv_encode_I(rv_sltiu, rd, rs1, imm); 283 | } 284 | 285 | int __lb(rv_reg rd, rv_reg rs1, int imm) 286 | { 287 | return rv_encode_I(rv_lb, rd, rs1, imm); 288 | } 289 | 290 | int __lh(rv_reg rd, rv_reg rs1, int imm) 291 | { 292 | return rv_encode_I(rv_lh, rd, rs1, imm); 293 | } 294 | 295 | int __lw(rv_reg rd, rv_reg rs1, int imm) 296 | { 297 | return rv_encode_I(rv_lw, rd, rs1, imm); 298 | } 299 | 300 | int __lbu(rv_reg rd, rv_reg rs1, int imm) 301 | { 302 | return rv_encode_I(rv_lbu, rd, rs1, imm); 303 | } 304 | 305 | int __lhu(rv_reg rd, rv_reg rs1, int imm) 306 | { 307 | return rv_encode_I(rv_lhu, rd, rs1, imm); 308 | } 309 | 310 | int __sb(rv_reg rd, rv_reg rs1, int imm) 311 | { 312 | return rv_encode_S(rv_sb, rs1, rd, imm); 313 | } 314 | 315 | int __sh(rv_reg rd, rv_reg rs1, int imm) 316 | { 317 | return rv_encode_S(rv_sh, rs1, rd, imm); 318 | } 319 | 320 | int __sw(rv_reg rd, rv_reg rs1, int imm) 321 | { 322 | return rv_encode_S(rv_sw, rs1, rd, imm); 323 | } 324 | 325 | int __beq(rv_reg rs1, rv_reg rs2, int imm) 326 | { 327 | return rv_encode_B(rv_beq, rs1, rs2, imm); 328 | } 329 | 330 | int __bne(rv_reg rs1, rv_reg rs2, int imm) 331 | { 332 | return rv_encode_B(rv_bne, rs1, rs2, imm); 333 | } 334 | 335 | int __blt(rv_reg rs1, rv_reg rs2, int imm) 336 | { 337 | return rv_encode_B(rv_blt, rs1, rs2, imm); 338 | } 339 | 340 | int __bge(rv_reg rs1, rv_reg rs2, int imm) 341 | { 342 | return rv_encode_B(rv_bge, rs1, rs2, imm); 343 | } 344 | 345 | int __bltu(rv_reg rs1, rv_reg rs2, int imm) 346 | { 347 | return rv_encode_B(rv_bltu, rs1, rs2, imm); 348 | } 349 | 350 | int __bgeu(rv_reg rs1, rv_reg rs2, int imm) 351 | { 352 | return rv_encode_B(rv_bgeu, rs1, rs2, imm); 353 | } 354 | 355 | int __jal(rv_reg rd, int imm) 356 | { 357 | return rv_encode_J(rv_jal, rd, imm); 358 | } 359 | 360 | int __jalr(rv_reg rd, rv_reg rs1, int imm) 361 | { 362 | return rv_encode_I(rv_jalr, rd, rs1, imm); 363 | } 364 | 365 | int __lui(rv_reg rd, int imm) 366 | { 367 | return rv_encode_U(rv_lui, rd, imm); 368 | } 369 | 370 | int __auipc(rv_reg rd, int imm) 371 | { 372 | return rv_encode_U(rv_auipc, rd, imm); 373 | } 374 | 375 | int __ecall(void) 376 | { 377 | return rv_encode_I(rv_ecall, __zero, __zero, 0); 378 | } 379 | 380 | int __ebreak(void) 381 | { 382 | return rv_encode_I(rv_ebreak, __zero, __zero, 1); 383 | } 384 | 385 | int __nop(void) 386 | { 387 | return __addi(__zero, __zero, 0); 388 | } 389 | 390 | int __mul(rv_reg rd, rv_reg rs1, rv_reg rs2) 391 | { 392 | return rv_encode_R(rv_mul, rd, rs1, rs2); 393 | } 394 | 395 | int __div(rv_reg rd, rv_reg rs1, rv_reg rs2) 396 | { 397 | return rv_encode_R(rv_div, rd, rs1, rs2); 398 | } 399 | 400 | int __mod(rv_reg rd, rv_reg rs1, rv_reg rs2) 401 | { 402 | return rv_encode_R(rv_mod, rd, rs1, rs2); 403 | } 404 | 405 | int __sext_b(rv_reg rd, rv_reg rs) 406 | { 407 | return rv_encode_I(rv_sext_b, rd, rs, 0); 408 | } 409 | -------------------------------------------------------------------------------- /tests/check-snapshots.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -u 4 | 5 | readonly SHECC="$PWD/out/shecc" 6 | 7 | if [ "$#" != 1 ]; then 8 | echo "Usage: $0 " 9 | exit 1 10 | fi 11 | 12 | readonly ARCH="$1" 13 | 14 | function check_snapshot() { 15 | local source="$1" 16 | local ref="tests/snapshots/$(basename $source .c)-$ARCH.json" 17 | local temp_exe=$(mktemp) 18 | local temp_json=$(mktemp --suffix .json) 19 | 20 | $SHECC --dump-ir -o $temp_exe $source &>/dev/null 21 | dot -Tdot_json -o $temp_json CFG.dot 22 | diff -q <(cat $ref) \ 23 | <(sed -E "/0x[0-9a-f]+/d" $temp_json | \ 24 | jq -S -c '.edges |= sort_by(._gvid) | .objects |= sort_by(._gvid) | 25 | .objects |= map_values(.edges |= (. // [] | sort)) | 26 | .objects |= map_values(.nodes |= (. // [] | sort)) | 27 | .objects |= map_values(.subgraphs |= (. // [] | sort))') 28 | } 29 | 30 | for file in tests/*.c; do 31 | check_snapshot "$file" 32 | done 33 | -------------------------------------------------------------------------------- /tests/fib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int fib(int n) 4 | { 5 | if (n == 0) 6 | return 0; 7 | else if (n == 1) 8 | return 1; 9 | return fib(n - 1) + fib(n - 2); 10 | } 11 | 12 | int main() 13 | { 14 | printf("F(10) = %d\n", fib(10)); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /tests/hello.c: -------------------------------------------------------------------------------- 1 | int main(int argc, char *argv[]) 2 | { 3 | printf("%d\n", argc); 4 | printf("Hello World\n"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/update-snapshots.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -u 4 | 5 | readonly SHECC="$PWD/out/shecc" 6 | 7 | if [ "$#" != 1 ]; then 8 | echo "Usage: $0 " 9 | exit 1 10 | fi 11 | 12 | readonly ARCH="$1" 13 | 14 | function update_snapshot() { 15 | local source="$1" 16 | local dest="tests/snapshots/$(basename $source .c)-$ARCH.json" 17 | local temp_exe=$(mktemp) 18 | local temp_json=$(mktemp --suffix .json) 19 | 20 | $SHECC --dump-ir -o $temp_exe $source &>/dev/null 21 | dot -Tdot_json -o $temp_json CFG.dot 22 | sed -i -E "/0x[0-9a-f]+/d" $temp_json 23 | jq -S -c '.edges |= sort_by(._gvid) | .objects |= sort_by(._gvid) | 24 | .objects |= map_values(.edges |= (. // [] | sort)) | 25 | .objects |= map_values(.nodes |= (. // [] | sort)) | 26 | .objects |= map_values(.subgraphs |= (. // [] | sort))' $temp_json > $dest 27 | } 28 | 29 | for file in tests/*.c; do 30 | update_snapshot "$file" 31 | done 32 | -------------------------------------------------------------------------------- /tools/inliner.c: -------------------------------------------------------------------------------- 1 | /* 2 | * shecc - Self-Hosting and Educational C Compiler. 3 | * 4 | * shecc is freely redistributable under the BSD 2 clause license. See the 5 | * file "LICENSE" for information on usage and redistribution of this file. 6 | */ 7 | 8 | /* inliner - inline libc source into C file. 9 | * 10 | * The inliner is used at build-time, and developers can use the 11 | * "inline C" feature to implement target-specific parts such as 12 | * C runtime and essential libraries. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #define MAX_LINE_LEN 200 21 | #define DEFAULT_SOURCE_SIZE 65536 22 | 23 | #define write_char(c) strbuf_putc(SOURCE, c) 24 | #define write_str(s) strbuf_puts(SOURCE, s) 25 | 26 | typedef struct { 27 | int size; 28 | int capacity; 29 | char *elements; 30 | } strbuf_t; 31 | 32 | strbuf_t *SOURCE; 33 | 34 | strbuf_t *strbuf_create(int init_capacity) 35 | { 36 | strbuf_t *array = malloc(sizeof(strbuf_t)); 37 | if (!array) 38 | return NULL; 39 | 40 | array->size = 0; 41 | array->capacity = init_capacity; 42 | array->elements = malloc(array->capacity * sizeof(char)); 43 | if (!array->elements) { 44 | free(array); 45 | return NULL; 46 | } 47 | 48 | return array; 49 | } 50 | 51 | bool strbuf_extend(strbuf_t *src, int len) 52 | { 53 | int new_size = src->size + len; 54 | 55 | if (new_size < src->capacity) 56 | return true; 57 | 58 | if (new_size > src->capacity << 1) 59 | src->capacity = new_size; 60 | else 61 | src->capacity <<= 1; 62 | 63 | char *new_arr = malloc(src->capacity * sizeof(char)); 64 | 65 | if (!new_arr) 66 | return false; 67 | 68 | memcpy(new_arr, src->elements, src->size * sizeof(char)); 69 | 70 | free(src->elements); 71 | src->elements = new_arr; 72 | 73 | return true; 74 | } 75 | 76 | bool strbuf_putc(strbuf_t *src, char value) 77 | { 78 | if (!strbuf_extend(src, 1)) 79 | return false; 80 | 81 | src->elements[src->size] = value; 82 | src->size++; 83 | 84 | return true; 85 | } 86 | 87 | bool strbuf_puts(strbuf_t *src, char *value) 88 | { 89 | int len = strlen(value); 90 | 91 | if (!strbuf_extend(src, len)) 92 | return false; 93 | 94 | strncpy(src->elements + src->size, value, len); 95 | src->size += len; 96 | 97 | return true; 98 | } 99 | 100 | void strbuf_free(strbuf_t *src) 101 | { 102 | if (!src) 103 | return; 104 | 105 | free(src->elements); 106 | free(src); 107 | } 108 | 109 | void write_line(char *src) 110 | { 111 | write_str(" __c(\""); 112 | for (int i = 0; src[i]; i++) { 113 | if (src[i] == '\"') { 114 | write_char('\\'); 115 | write_char('\"'); 116 | } else if (src[i] != '\n') { 117 | write_char(src[i]); 118 | } 119 | } 120 | 121 | write_char('\\'); 122 | write_char('n'); 123 | write_str("\");\n"); 124 | } 125 | 126 | void load_from(char *file) 127 | { 128 | char buffer[MAX_LINE_LEN]; 129 | FILE *f = fopen(file, "rb"); 130 | for (;;) { 131 | if (!fgets(buffer, MAX_LINE_LEN, f)) { 132 | fclose(f); 133 | return; 134 | } 135 | write_line(buffer); 136 | } 137 | fclose(f); 138 | } 139 | 140 | void save_to(char *file) 141 | { 142 | FILE *f = fopen(file, "wb"); 143 | for (int i = 0; i < SOURCE->size; i++) 144 | fputc(SOURCE->elements[i], f); 145 | fclose(f); 146 | } 147 | 148 | int main(int argc, char *argv[]) 149 | { 150 | if (argc <= 2) { 151 | printf("Usage: inliner \n"); 152 | return -1; 153 | } 154 | 155 | SOURCE = strbuf_create(DEFAULT_SOURCE_SIZE); 156 | 157 | write_str("/* Created by tools/inliner - DO NOT EDIT. */\n"); 158 | 159 | /* The __c construct is inspired by the __asm keyword, which is used to 160 | * invoke the inline assembler. In a similar vein, __c aims to "inline C 161 | * code," allowing for the emission of specified C code as needed. e.g., 162 | * __c("int strlen(char *str) {\n"); 163 | * __c(" int i = 0;\n"); 164 | * __c(" while (str[i])\n"); 165 | * __c(" i++;\n"); 166 | * __c(" return i;\n"); 167 | * __c("}\n"); 168 | */ 169 | write_str("void __c(char *src) {\n"); 170 | write_str(" strbuf_puts(SOURCE, src);\n"); 171 | write_str("}\n"); 172 | 173 | write_str("void libc_generate() {\n"); 174 | load_from(argv[1]); 175 | write_str("}\n"); 176 | save_to(argv[2]); 177 | strbuf_free(SOURCE); 178 | 179 | return 0; 180 | } 181 | --------------------------------------------------------------------------------