Full Code of jserv/shecc for AI

master 0d07d85f1cc4 cached
47 files
2.4 MB
640.5k tokens
538 symbols
1 requests
Download .txt
Showing preview only (2,562K chars total). Download the full file or copy to clipboard to get everything.
Repository: jserv/shecc
Branch: master
Commit: 0d07d85f1cc4
Files: 47
Total size: 2.4 MB

Directory structure:
gitextract_2uml3qup/

├── .ci/
│   ├── check-format.sh
│   └── check-newline.sh
├── .clang-format
├── .github/
│   └── workflows/
│       └── main.yml
├── .gitignore
├── AUTHORS
├── COMPLIANCE.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── docs/
│   └── dynamic-linking.md
├── lib/
│   ├── c.c
│   └── c.h
├── mk/
│   ├── arm.mk
│   ├── common.mk
│   └── riscv.mk
├── src/
│   ├── arch-lower.c
│   ├── arm-codegen.c
│   ├── arm.c
│   ├── defs.h
│   ├── elf.c
│   ├── globals.c
│   ├── lexer.c
│   ├── main.c
│   ├── opt-sccp.c
│   ├── parser.c
│   ├── peephole.c
│   ├── preprocessor.c
│   ├── reg-alloc.c
│   ├── riscv-codegen.c
│   ├── riscv.c
│   └── ssa.c
├── tests/
│   ├── arm-abi.sh
│   ├── check-snapshots.sh
│   ├── driver.sh
│   ├── fib.c
│   ├── hello.c
│   ├── snapshots/
│   │   ├── fib-arm-dynamic.json
│   │   ├── fib-arm-static.json
│   │   ├── fib-riscv-static.json
│   │   ├── hello-arm-dynamic.json
│   │   ├── hello-arm-static.json
│   │   └── hello-riscv-static.json
│   └── update-snapshots.sh
└── tools/
    ├── inliner.c
    └── norm-lf.c

================================================
FILE CONTENTS
================================================

================================================
FILE: .ci/check-format.sh
================================================
#!/usr/bin/env bash

SOURCES=$(find $(git rev-parse --show-toplevel) | egrep "\.(c|cxx|cpp|h|hpp)\$")

set -x

for file in ${SOURCES};
do
    clang-format-18 ${file} > expected-format
    diff -u -p --label="${file}" --label="expected coding style" ${file} expected-format
done
exit $(clang-format-18 --output-replacements-xml ${SOURCES} | egrep -c "</replacement>")


================================================
FILE: .ci/check-newline.sh
================================================
#!/usr/bin/env bash

ret=0
show=0
# Reference: https://medium.com/@alexey.inkin/how-to-force-newline-at-end-of-files-and-why-you-should-do-it-fdf76d1d090e
while IFS= read -rd '' f; do
    if file --mime-encoding "$f" | grep -qv binary; then
        tail -c1 < "$f" | read -r _ || show=1
        if [ $show -eq 1 ]; then
            echo "Warning: No newline at end of file $f"
            ret=1
            show=0
        fi
    fi
done < <(git ls-files -z src tools tests)

exit $ret


================================================
FILE: .clang-format
================================================
BasedOnStyle: Chromium
Language: Cpp
MaxEmptyLinesToKeep: 3
IndentCaseLabels: false
AllowShortIfStatementsOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
DerivePointerAlignment: false
PointerAlignment: Right
SpaceAfterCStyleCast: true
TabWidth: 4
UseTab: Never
IndentWidth: 4
BreakBeforeBraces: Linux
AccessModifierOffset: -4
ForEachMacros:
  - foreach
  - list_for_each
  - list_for_each_safe
  - list_for_each_entry
  - list_for_each_entry_safe


================================================
FILE: .github/workflows/main.yml
================================================
name: Github Actions

on: [push, pull_request]

jobs:
  host-x86:
    runs-on: ubuntu-24.04
    strategy:
      matrix:
        compiler: [gcc, clang]
        architecture: [arm, riscv]
        link_mode: [static]
        include:
          - compiler: gcc
            architecture: arm
            link_mode: dynamic
          - compiler: clang
            architecture: arm
            link_mode: dynamic
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Download dependencies
        run: |
          sudo apt-get update -q -y
          sudo apt-get install -q -y graphviz jq
          sudo apt-get install -q -y qemu-user
          sudo apt-get install -q -y build-essential
          sudo apt-get install -q -y gcc-arm-linux-gnueabihf
      - name: Determine static or dynamic linking mode
        id: determine-mode
        run: |
          if [ "${{ matrix.link_mode }}" = "dynamic" ]; then
            echo "Use dynamic linking mode"
            echo "DYNLINK=1" >> "$GITHUB_OUTPUT"
          else
            echo "Use static linking mode"
            echo "DYNLINK=0" >> "$GITHUB_OUTPUT"
          fi
      - name: Build artifacts
        env:
          CC: ${{ matrix.compiler }}
        run: |
          make ARCH=${{ matrix.architecture }} DYNLINK=${{ steps.determine-mode.outputs.DYNLINK }}
      - name: IR regression tests
        run: |
          make check-snapshot DYNLINK=${{ steps.determine-mode.outputs.DYNLINK }} || exit 1
      - name: Sanitizer-enabled stage 0 tests
        env:
          CC: ${{ matrix.compiler }}
        run: |
          make check-sanitizer DYNLINK=${{ steps.determine-mode.outputs.DYNLINK }} || exit 1
      - name: Unit tests
        run: |
          make check DYNLINK=${{ steps.determine-mode.outputs.DYNLINK }} || exit 1

  host-arm:
    runs-on: ubuntu-24.04-arm
    strategy:
      matrix:
        link_mode: [static, dynamic]
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Download dependencies
        run: |
          sudo dpkg --add-architecture armhf
          sudo apt-get update -q -y
          sudo apt-get install -q -y graphviz jq
          sudo apt-get install -q -y build-essential libc6:armhf
          sudo wget https://github.com/fastfetch-cli/fastfetch/releases/download/2.58.0/fastfetch-linux-aarch64.deb
          sudo dpkg -i fastfetch-linux-aarch64.deb

      - name: Determine static or dynamic linking mode
        id: determine-mode
        run: |
          if [ "${{ matrix.link_mode }}" = "dynamic" ]; then
            echo "Use dynamic linking mode"
            echo "DYNLINK=1" >> "$GITHUB_OUTPUT"
          else
            echo "Use static linking mode"
            echo "DYNLINK=0" >> "$GITHUB_OUTPUT"
          fi

      - name: Build artifacts
        run: |
          make ARCH=arm DYNLINK=${{ steps.determine-mode.outputs.DYNLINK }}

      - name: Sanitizer-enabled stage 0 tests
        run: |
          make check-sanitizer DYNLINK=${{ steps.determine-mode.outputs.DYNLINK }} || exit 1

      - name: Unit tests
        run: |
          make check DYNLINK=${{ steps.determine-mode.outputs.DYNLINK }} || exit 1

  preprocessor-host:
    runs-on: ubuntu-24.04
    strategy:
      matrix:
        compiler: [gcc, clang]
        architecture: [arm, riscv]
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Download dependencies
        run: |
          sudo apt-get update -q -y
          sudo apt-get install -q -y graphviz jq
          sudo apt-get install -q -y qemu-user
          sudo apt-get install -q -y build-essential
      - name: Configurate config
        run: |
          make distclean config ARCH=${{ matrix.architecture }}
      - name: Preprocess stage 1 source code
        env:
          CC: ${{ matrix.compiler }}
        run: |
          make out/shecc
          ./out/shecc -E src/main.c > ./out/out.c
      - name: Build stage 1 artifact
        run: |
          ./out/shecc --no-libc -o out/shecc-stage1.elf ./out/out.c

  coding-style:
    runs-on: ubuntu-24.04
    steps:
    - uses: actions/checkout@v4
    - name: Coding convention
      run: |
            sudo apt-get install -q -y clang-format-18
            .ci/check-newline.sh
            .ci/check-format.sh
      shell: bash


================================================
FILE: .gitignore
================================================
out
a.out
*.o
*.elf
*.log
*.lst
*.dot
config
src/codegen.c
.session.mk

# vscode C/C++ plugin generated files
.vscode


================================================
FILE: AUTHORS
================================================
shecc is written by:
  Jim Huang <jserv.tw@gmail.com>
  Yu-Cheng Cheng <yucheng871011@gmail.com>
  Lecopzer Chen <lecopzer@gmail.com>
  Vacantron Chen <e24081030@gs.ncku.edu.tw>
  Matt Jan <zoo868e@gmail.com>
  Alex Lai <alexabc722@gmail.com>
  Chin Yik Ming <yikming2222@gmail.com>
  Hank Wang <wanghanchi2000@gmail.com>
  Kyle Lin <minecraft.kyle.train@gmail.com>
  Yu En Siao <drxiao6399@gmail.com>
  Meng-Zong Tsai <hwahwa649@gmail.com>
  Kuan-Wei Chiu <visitorckw@gmail.com>
  Yu-Hui Wu <nosba0957@gmail.com>


================================================
FILE: COMPLIANCE.md
================================================
# C99 Compliance Status

shecc implements a subset of C99 suitable for self-hosting and systems programming,
prioritizing simplicity, educational value, and minimal dependencies over full standard compliance.
This document tracks compliance gaps and non-standard behaviors.

## Implemented Features

### Core Language
- Basic types: `int`, `short`, `char`, `void`, `_Bool`
- Structures and unions with nested definitions
- Enumerations with automatic value assignment
- Function definitions and declarations
- Arrays (single and multi-dimensional)
- Pointers and pointer arithmetic (fully C99-compliant)
- Type definitions (`typedef`)

### Control Flow
- `if`/`else` statements
- `goto` and label statements
- `while`, `do-while`, `for` loops
- `switch`/`case`/`default` statements
- `break`, `continue`, `return` statements

### Operators
- Arithmetic: `+`, `-`, `*`, `/`, `%`
- Bitwise: `&`, `|`, `^`, `~`, `<<`, `>>`
- Logical: `&&`, `||`, `!`
- Relational: `<`, `>`, `<=`, `>=`, `==`, `!=`
- Assignment: `=`, `+=`, `-=`, `*=`, `/=`, `%=`, `<<=`, `>>=`, `&=`, `|=`, `^=`
- Increment/decrement: `++`, `--` (prefix and postfix)
- Conditional: `? :`
- Member access: `.`, `->`
- Address/dereference: `&`, `*`

### Preprocessor (Partial)
- `#define` for object-like and function-like macros
- `#ifdef`, `#ifndef`, `#if`, `#elif`, `#else`, `#endif`
- `#undef` for macro removal
- `#pragma once`, other `#pragma` options will be ignored
- `defined()` operator
- `__VA_ARGS__` for variadic macros
- `__FILE__`, `__LINE__` built-in macros

## Missing Features

### Storage Classes & Qualifiers

| Feature | Status | Impact |
|---------|--------|--------|
| `static` | Not implemented | No internal linkage or persistent local variables |
| `extern` | Not implemented | No external linkage declarations |
| `register` | Not implemented | No register hint optimization |
| `auto` | Not implemented | Default storage class (implicit) |
| `const` | Parsed but ignored | No read-only enforcement |
| `volatile` | Not implemented | No volatile semantics |
| `restrict` | Not implemented | No pointer aliasing optimization |
| `inline` | Not implemented | No function inlining |

### Type System

| Feature | Status | Notes |
|---------|--------|-------|
| `long` | Missing | Only 4-byte integers |
| `long long` | Missing | No 64-bit integers |
| `unsigned` | Missing | All integers are signed |
| `signed` | Missing | Implicit for integers |
| `float` | Missing | No floating-point support |
| `double` | Missing | No floating-point support |
| `long double` | Missing | No floating-point support |
| Bit-fields | Missing | Cannot pack struct members |

### Literals & Constants

| Feature | Status | Current Behavior |
|---------|--------|-----------------|
| Integer suffixes (`u`, `l`, `ll`) | Not parsed | All literals are `int` |
| Wide characters (`L'c'`) | Not supported | Single-byte only |
| Wide strings (`L"..."`) | Not supported | Single-byte only |
| Multi-character constants | Not supported | Single character only |
| Universal characters (`\u`, `\U`) | Not supported | ASCII only |
| Hex escapes (`\x...`) | Limited | Max 2 hex digits |

### Preprocessor Gaps

| Feature | Status | Description |
|---------|--------|-------------|
| `#include` | Partial | Local file inclusion is supported, but lack of capability to include system files |
| Token pasting (`##`) | Missing | Cannot concatenate tokens |
| Stringizing (`#`) | Missing | Cannot convert to string |
| `__DATE__` | Missing | No compile date |
| `__TIME__` | Missing | No compile time |
| `__STDC__` | Missing | No standard compliance indicator |

### Advanced Features

| Feature | Status | Description |
|---------|--------|-------------|
| Designated initializers | Missing | No `.field = value` syntax |
| Compound literals | Partial | Limited support |
| Flexible array members | Missing | No `[]` at struct end |
| Variable-length arrays | Missing | No runtime-sized arrays |
| `_Complex` | Missing | No complex numbers |
| `_Imaginary` | Missing | No imaginary numbers |
| `_Static_assert` | Missing | No compile-time assertions |
| `_Alignof` | Missing | No alignment queries |
| `_Alignas` | Missing | No alignment specification |
| `_Generic` | Missing | No generic selection |

## Non-Standard Behaviors

### GNU Extensions
- Binary literals: `0b101010`
- Escape sequence: `\e` for ESC character
- `void*` arithmetic (treated as `char*`)
- `sizeof(void)` returns 0 (should be error)
- Computed goto

### Implementation-Specific
- Array compound literals in scalar context use first element
- String literals are modifiable (stored in `.data`, not `.rodata`)
- No strict aliasing rules
- Left-to-right evaluation order (not always guaranteed in C99)


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to `shecc`

:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:

The following is a set of guidelines for contributing to [shecc](https://github.com/sysprog21/shecc)
hosted on GitHub. These are mostly guidelines, not rules.
Use your best judgment, and feel free to propose changes to this document in a pull request.

## Issues

This project uses GitHub Issues to track ongoing development, discuss project plans, and keep track of bugs.
Be sure to search for existing issues before you create another one.

Initially, it is advisable to create an issue on GitHub for bug reports, feature requests,
or substantial pull requests, as this offers a platform for discussion with both the community and project maintainers.

Engaging in a conversation through a GitHub issue before making a contribution is crucial to ensure the acceptance of your work.
We aim to prevent situations where significant effort is expended on a pull request that might not align with the project's design principles.
For example, it might turn out that the feature you propose is more suited as an independent module that complements this project,
in which case we would recommend that direction.

For minor corrections, such as typo fixes, small refactoring, or updates to documentation/comments,
filing an issue is not typically necessary.
What constitutes a "minor" fix involves discretion; however, examples include:
- Correcting spelling mistakes
- Minor code refactoring
- Updating or editing documentation and comments

Nevertheless, there may be instances where, upon reviewing your pull requests,
we might request an issue to be filed to facilitate discussion on broader design considerations.

Visit our [Issues page on GitHub](https://github.com/sysprog21/shecc/issues) to search and submit.

## Language Compliance

Before contributing new language features or modifications to the compiler frontend,
please review [COMPLIANCE.md](COMPLIANCE.md) to understand shecc's current C99 compliance status,
supported features, and known limitations. This document helps ensure that contributions
align with the project's subset implementation philosophy.

## Coding Convention

Contributions from developers across corporations, academia, and individuals are welcome.
However, participation requires adherence to fundamental ground rules:
* Code must strictly adhere to the established C coding style (refer to the guidelines below).
  While there is some flexibility in basic style, it is crucial to stick to the current coding standards.
  Complex algorithmic constructs without proper comments will not be accepted.
* External pull requests should include thorough documentation in the pull request comments for consideration.
* When composing documentation, code comments, and other materials in English,
  please adhere to the American English (`en_US`) dialect.
  This variant should be considered the standard for all documentation efforts.
  For instance, opt for "initialize" over "initialise" and "color" rather than "colour".

Software requirement: [clang-format](https://clang.llvm.org/docs/ClangFormat.html) version 18 or later.

This repository consistently contains an up-to-date `.clang-format` file with rules that match the explained ones.
For maintaining a uniform coding style, execute the command `clang-format -i *.{c,h}`.

## Coding Style for Modern C

This coding style is a variant of the [K&R style](https://en.wikipedia.org/wiki/Indentation_style#K&R).
Adhere to established practices while being open to innovation.
Maintain consistency, adopt the latest C standards,
and embrace modern compilers along with their advanced static analysis capabilities and sanitizers.

### Indentation

In this coding style guide, the use of 4 spaces for indentation instead of tabs is strongly enforced to ensure consistency.
Always apply a single space before and after comparison and assignment operators to maintain readable code.
Additionally, it is crucial to include a single space after every comma.
e.g.,
```c
for (int i = 0; i < 10; i++) {
    printf("%d\n", i);
    /* some operations */
}
```

The tab character (ASCII 0x9) should never appear within any source code file.
When indentation is needed in the source code, align using spaces instead.
The width of the tab character varies by text editor and programmer preference,
making consistent visual layout a continual challenge during code reviews and maintenance.

### Line length

All lines should typically remain within 80 characters, with longer lines wrapped as needed.
This practice is supported by several valid rationales:
* It encourages developers to write concise code.
* Smaller portions of information are easier for humans to process.
* It assists users of vi/vim (and potentially other editors) who use vertical splits.
* It is especially helpful for those who may want to print code on paper.

### Comments

Multi-line comments should have the opening and closing characters on separate lines,
with the content lines prefixed by a space and an asterisk (`*`) for alignment, e.g.,
```c
/*
 * This is a multi-line comment.
 */

/* One line comment. */
```

Use multi-line comments for more elaborate descriptions or before significant logical blocks of code.

Single-line comments should be written in C89 style:
```c
    return (uintptr_t) val;  /* return a bitfield */
```

Leave two spaces between the statement and the inline comment.
Avoid commenting out code directly.
Instead, use `#if 0` ... `#endif` when it is intentional.

All assumptions should be clearly explained in comments.
Use the following markers to highlight issues and make them searchable:
* `WARNING`: Alerts a maintainer to the risk of changing this code.
  e.g., a delay loop counter's terminal value was determined empirically and may need adjustment when the code is ported or the optimization level is tweaked.
* `NOTE`: Provides descriptive comments about the "why" of a chunk of code,
  as opposed to the "how" usually included in comments.
  e.g., a chunk of driver code may deviate from the datasheet due to a known erratum in the chip,
  or an assumption made by the original programmer is explained.
* `TODO`: Indicates an area of the code that is still under construction and explains what remains to be done.
  When appropriate, include an all-caps programmer name or set of initials before the word `TODO`.

Keep the documentation as close to the code as possible.

### Spacing and brackets

Ensure that the keywords `if`, `while`, `for`, `switch`, and `return` are always followed by a single space when there is additional code on the same line.
Follow these spacing guidelines:
* Place one space after the keyword in a conditional or loop.
* Do not use spaces around the parentheses in conditionals or loops.
* Insert one space before the opening curly bracket.

For example:
```c
do {
    /* some operations */
} while (condition);
```

Functions (their declarations or calls), `sizeof` operator or similar
macros shall not have a space after their name/keyword or around the
brackets, e.g.,
```c
unsigned total_len = offsetof(obj_t, items[n]);
unsigned obj_len = sizeof(obj_t);
```

Use brackets to avoid ambiguity and with operators such as `sizeof`,
but otherwise avoid redundant or excessive brackets.

Assignment operators (`=`, `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `~=`, and `!=`) should always have a space before and after them.
For example:
```c
count += 1;
```

Binary operators (`+`, `-`, `*`, `/`, `%`, `<`, `<=`, `>`, `>=`, `==`, `!=`, `<<`, `>>`, `&`, `|`, `^`, `&&`, and `||`) should also be surrounded by spaces.
For example:
```c
current_conf = prev_conf | (1 << START_BIT);
```

Unary operators (`++`, `--`, `!`, and `~`) should be written without spaces between the operator and the operand.
For example:
```c
bonus++;
if (!play)
    return STATE_QUITE;
```

The ternary operator (`?` and `:`) should have spaces on both sides.
For example:
```c
uint32_t max(uint32_t a, uint32_t b)
{
    return (a > b) ? a : b;
}
```

Structure pointer (`->`) and member (`.`) operators should not have surrounding spaces.
Similarly, array subscript operators (`[` and `]`) and function call parentheses should be written without spaces around them.

### Parentheses

Avoid relying on C’s operator precedence rules, as they may not be immediately clear to those maintaining the code.
To ensure clarity, always use parentheses to enforce the correct execution order within a sequence of operations,
or break long statements into multiple lines if necessary.

When using logical AND (`&&`) and logical OR (`||`) operators, each operand should be enclosed in parentheses,
unless it is a single identifier or constant.
For example:
```c
if ((count > 100) && (detected == false)) {
    character = C_ASSASSIN;
}
```

### Variable names and declarations

Ensure that functions, variables, and comments are consistently named using English words.
Global variables should have descriptive names, while local variables can have shorter names.
It is important to strike a balance between being descriptive and concise.
Each variable's name should clearly reflect its purpose.

Use [snake_case](https://en.wikipedia.org/wiki/Snake_case) for naming conventions,
and avoid using "camelCase."
Additionally, do not use Hungarian notation or any other unnecessary prefixes or suffixes.

When declaring pointers, follow these spacing conventions:
```c
const char *name;  /* Const pointer; '*' with the name and a space before it */
conf_t * const cfg;  /* Pointer to const data; spaces around 'const' */
const uint8_t * const charmap;  /* Const pointer and const data */
const void * restrict key;  /* Const pointer that does not alias */
```

Local variables of the same type should be declared on the same line.
For example:
```c
void func(void)
{
    char a, b;  /* OK */

    char a;
    char b;     /* Incorrect: a variable with char type already exists. */
}
```

Always include a trailing comma in the last element of a structure initialization,
including its nested elements, to help `clang-format` correctly format the structure.
However, this comma can be omitted in very simple and short structures.
```c
typedef struct {
    int width, height;
} screen_t;

screen_t s = {
    .width = 640,
    .height = 480,   /* comma here */
}
```

### Type definitions

Declarations shall be on the same line, e.g.,
```c
typedef void (*dir_iter_t)(void *, const char *, struct dirent *);
```

_Typedef_ structures rather than pointers.  Note that structures can be kept
opaque if they are not dereferenced outside the translation unit where they
are defined.  Pointers can be _typedefed_ only if there is a very compelling
reason.

New types may be suffixed with `_t`.  Structure name, when used within the
translation unit, may be omitted, e.g.:

```c
typedef struct {
    unsigned if_index;
    unsigned addr_len;
    addr_t next_hop;
} route_info_t;
```

### Initialization

Do not initialize static and global variables to `0`; the compiler will do this.
When a variable is declared inside a function, it is not automatically initialized.

```c
static uint8_t a;  /* Global variable 'a' is set to 0 by the compiler */

void foo()
{
    /* 'b' is uninitialized and set to whatever happens to be in memory */
    uint8_t b;
    ...
}
```

Embrace C99 structure initialization where reasonable, e.g.,
```c
static const crypto_ops_t openssl_ops = {
    .create = openssl_crypto_create,
    .destroy = openssl_crypto_destroy,
    .encrypt = openssl_crypto_encrypt,
    .decrypt = openssl_crypto_decrypt,
    .hmac = openssl_crypto_hmac,
};
```

Embrace C99 array initialization, especially for the state machines, e.g.,
```c
static const uint8_t tcp_fsm[TCP_NSTATES][2][TCPFC_COUNT] = {
    [TCPS_CLOSED] = {
        [FLOW_FORW] = {
            /* Handshake (1): initial SYN. */
            [TCPFC_SYN] = TCPS_SYN_SENT,
        },
    },
    ...
}
```

Any pointer variable that does not have an initial address should be explicitly initialized to `NULL`.
This practice helps prevent undefined behavior caused by dereferencing uninitialized pointers.

In accordance with modern C standards (such as C99 and later), it is preferable to define local variables as needed,
rather than declaring them all at the beginning of a function.
Declaring variables close to their first use enhances code readability and helps maintain a clear, logical flow within the function.

Additionally, static analysis tools can be employed to scan the entire source code before each build,
providing warnings about variables that are used before being properly initialized.
This helps catch potential bugs early and ensures code quality and safety.

### Control structures

Try to make the control flow easy to follow.  Avoid long convoluted logic
expressions; try to split them where possible (into inline functions,
separate if-statements, etc).

The control structure keyword and the expression in the brackets should be
separated by a single space.  The opening curly bracket shall be in the
same line, also separated by a single space.  Example:

```c
    for (;;) {
        obj = get_first();
        while ((obj = get_next(obj))) {
            ...
        }
        if (done)
            break;
    }
```

Do not add inner spaces around the brackets. There should be one space after
the semicolon when `for` has expressions:
```c
    for (unsigned i = 0; i < __arraycount(items); i++) {
        ...
    }
```

#### Avoid unnecessary nesting levels

It is generally preferred to place the shorter clause (measured in lines of code) first in `if` and `else if` statements.
Long clauses can distract the reader from the core decision-making logic, making the code harder to follow.
By placing the shorter clause first, the decision path becomes clearer and easier to understand, which can help reduce bugs.

Avoid nesting `if`-`else` statements deeper than two levels.
Instead, consider using function calls or `switch` statements to simplify the logic and enhance readability.
Deeply nested `if`-`else` statements often indicate a complex and fragile state machine implementation,
which can be refactored into a safer and more maintainable structure.

For example, avoid this:
```c
int inspect(obj_t *obj)
{
    if (cond) {
        ...
        /* long code block */
        ...
        return 0;
    }
    return -1;
}
```

Instead, consider this approach:
```c
int inspect(obj_t *obj)
{
    if (!cond)
        return -1;
    ...
    return 0;
}
```

However, be careful not to make the logic more convoluted in an attempt to simplify nesting.

### `if` statements

Curly brackets and spacing follow the K&R style:
```c
    if (a == b) {
        ..
    } else if (a < b) {
        ...
    } else {
        ...
    }
```

Simple and succinct one-line if-statements may omit curly brackets:
```c
    if (!valid)
        return -1;
```

However, do prefer curly brackets with multi-line or more complex statements.
If one branch uses curly brackets, then all other branches shall use the
curly brackets too.

Wrap long conditions to the if-statement indentation adding extra 4 spaces:
```c
    if (some_long_expression &&
        another_expression) {
        ...
    }
```

#### Avoid redundant `else`

Avoid:
```c
    if (flag & F_FEATURE_X) {
        ...
        return 0;
    } else {
        return -1;
    }
```

Consider:
```c
    if (flag & F_FEATURE_X) {
        ...
        return 0;
    }
    return -1;
```

### `switch` statements

Switch statements should have the `case` blocks at the same indentation
level, e.g.:
```c
    switch (expr) {
    case A:
        ...
        break;
    case B:
        /* fallthrough */
    case C:
        ...
        break;
    }
```

If the case block does not break, then it is strongly recommended to add a
comment containing "fallthrough" to indicate it.  Modern compilers can also
be configured to require such comment (see gcc `-Wimplicit-fallthrough`).

### Function definitions

The opening and closing curly brackets shall also be in the separate lines (K&R style).

```c
ssize_t hex_write(FILE *stream, const void *buf, size_t len)
{
    ...
}
```

Do not use old style K&R style C definitions.

Introduced in C99, `restrict` is a pointer qualifier that informs the compiler no other pointer will access the same object during its lifetime,
enabling optimizations such as vectorization. Violating this assumption leads to undefined behavior.
Use `restrict` judiciously.

For function parameters, place one space after each comma, except at the end of a line.

### Function-like Macros

When using function-like macros (parameterized macros), adhere to the following guidelines:
- Enclose the entire macro body in parentheses.
- Surround each parameter usage with parentheses.
- Limit the use of each parameter to no more than once within the macro to avoid unintended side effects.
- Never include control flow statements (e.g., `return`) within a macro.
- If the macro involves multiple statements, encapsulate them within a `do`-`while (0)` construct.

For example:
```c
#define SET_POINT(p, x, y)      \
    do {                        \
        (p)->px = (x);          \
        (p)->py = (y);          \
    } while (0)
```

While the extensive use of parentheses, as shown above, helps minimize some risks,
it cannot prevent issues like unintended double increments from calls such as `MAX(i++, j++)`.

Other risks associated with macros include comparing signed and unsigned data or testing floating-point values.
Additionally, macros are not visible at runtime, making them impossible to step into with a debugger.
Therefore, use them with caution.

In general, macro names are typically written in all capitals, except in cases where readability is improved by using lowercase.
For example:
```
#define countof(a)   (size)(sizeof(a) / sizeof(*(a)))
#define lengthof(s)  (countof(s) - 1)
```

Although all capitals are generally preferred for constants,
lowercase can be used for function-like macros to improve readability.
These function-like macros do not share the same namespace concerns as other macros.

For example, consider the implementation of a simple memory allocator.
An arena can be represented by a memory buffer and an offset that begins at zero.
To allocate an object, record the pointer at the current offset,
advance the offset by the size of the object, and return the pointer.
Additional considerations, such as alignment and checking for available space, are also required.
```c
#define new(a, n, t)  alloc(a, n, sizeof(t), _Alignof(t))

typedef struct {
    char *begin, *end;
} arena_t;

void *alloc(arena_t *a, ptrdiff_t count, ptrdiff_t size, ptrdiff_t align)
{
    ptrdiff_t pad = -(uintptr_t)a->begin & (align - 1);
    assert(count < (a->end - a->begin - pad) / size);

    void *result = a->begin + pad;
    a->begin += pad + (count * size);
    return memset(result, 0, count * size);
}
```

Using the `new` macro helps prevent several common errors in C programs.
If types are mixed up, the compiler generates errors or warnings.
Moreover, naming a macro `new()` does not conflict with variables or fields named `new`,
because the macro form does not resemble a function call.

### Use `const` and `static` effectively

The `static` keyword should be used for any variables that do not need to be accessible outside the module where they are declared.
This is particularly important for global variables defined in C files.
Declaring variables and functions as `static` at the module level protects them from external access, reducing coupling between modules and improving encapsulation.

For functions that do not need to be accessible outside the module, use the `static` keyword.
This is especially important for private functions, where `static` should always be applied.

For example:
```c
static bool verify_range(uint16_t x, uint16_t y);
```

The `const` keyword is essential for several key purposes:
- Declaring variables that should not change after initialization.
- Defining fields within a `struct` that must remain immutable, such as those in memory-mapped I/O peripheral registers.
- Serving as a strongly typed alternative to `#define` for numerical constants.

For example, instead of using:
```c
#define MAX_SKILL_LEVEL (100U)
```

Use:
```c
const uint8_t max_skill_level = 100;
```

Maximizing the use of `const` provides the advantage of compiler-enforced protection against unintended modifications to data that should be read-only,
thereby enhancing code reliability and safety.

Additionally, when one of your function arguments is a pointer to data that will not be modified within the function,
you should use the `const` keyword.
This is particularly useful when comparing a character array with predefined strings without altering the array’s contents.

For example:
```c
static bool is_valid_cmd(const char *cmd);
```

### Object abstraction

Objects are often "simulated" by the C programmers with a `struct` and
its "public API".  To enforce the information hiding principle, it is a
good idea to define the structure in the source file (translation unit)
and provide only the _declaration_ in the header.  For example, `obj.c`:

```c
#include "obj.h"

struct obj {
    int value;
}

obj_t *obj_create(void)
{
    return calloc(1, sizeof(obj_t));
}

void obj_destroy(obj_t *obj)
{
    free(obj);
}
```

With an example `obj.h`:
```c
#ifndef _OBJ_H_
#define _OBJ_H_

typedef struct obj;

obj_t *obj_create(void);
void obj_destroy(obj_t *);

#endif
```

Such structuring will prevent direct access of the `obj_t` members outside
the `obj.c` source file.  The implementation (of such "class" or "module")
may be large and abstracted within separate source files.  In such case,
consider separating structures and "methods" into separate headers (think of
different visibility), for example `obj_impl.h` (private) and `obj.h` (public).

Consider `crypto_impl.h`:
```c
#ifndef _CRYPTO_IMPL_H_
#define _CRYPTO_IMPL_H_

#if !defined(__CRYPTO_PRIVATE)
#error "only to be used by the crypto modules"
#endif

#include "crypto.h"

typedef struct crypto {
    crypto_cipher_t cipher;
    void *key;
    size_t key_len;
    ...
}
...

#endif
```

And `crypto.h` (public API):

```c
#ifndef _CRYPTO_H_
#define _CRYPTO_H_

typedef struct crypto crypto_t;

crypto_t *crypto_create(crypto_cipher_t);
void crypto_destroy(crypto_t *);
...

#endif
```

### Use reasonable types

Use `unsigned` for general iterators; use `size_t` for general sizes; use
`ssize_t` to return a size which may include an error.  Of course, consider
possible overflows.

Avoid using fixed-width types like `uint8_t`, `uint16_t`, or other smaller integer types for general iterators or similar cases unless there is a specific need for size-constrained operations,
such as in fixed-width data processing or resource-limited environments.

C has rather peculiar _type promotion rules_ and unnecessary use of sub-word
types might contribute to a bug once in a while.

Boolean variables should be declared using the `bool` type.
Non-Boolean values should be converted to Boolean by using relational operators (e.g., `<` or `!=`) rather than by casting.

For example:
```c
#include <stdbool.h>
...
bool inside = (value < expected_range);
```

### Embrace portability

#### Byte-order

Do not assume x86 or little-endian architecture.  Use endian conversion
functions for operating the on-disk and on-the-wire structures or other
cases where it is appropriate.

#### Types

Do not assume a particular 32-bit or 64-bit architecture; for example, do not assume the size of `long` or `unsigned long`.
Instead, use `int64_t` or `uint64_t` for 8-byte integers.

Fixed-width types, such as `uint32_t`, are particularly useful when memory size is critical,
as in embedded systems, communication protocols requiring specific data sizes,
or when interacting with hardware registers that require precise bit-width operations.
In these scenarios, fixed-width types ensure consistent behavior across different platforms and compilers.

Do not assume `char` is signed; for example, on Arm architectures, it is unsigned by default.

Avoid defining bit-fields within signed integer types.
Additionally, do not use bitwise operators (such as `&`, `|`, `~`, `^`, `<<`, and `>>`) on signed integer data.
Refrain from combining signed and unsigned integers in comparisons or expressions, as this can lead to unpredictable results.

When using `#define` to declare decimal constants, append a `U` to ensure they are treated as unsigned.
For example:
```c
#define SOME_CONSTANT (6U)

uint16_t unsigned_a = 6;
int16_t  signed_b = -9;
if (unsigned_a + signed_b < 4) {
    /* This block might appear logically correct, as -9 + 6 is -3 */
    ...
}
/* but compilers with 16-bit int may legally interpret it as (0xFFFF – 9) + 6. */
```

It is important to note that certain aspects of manipulating binary data within signed integer containers are implementation-defined behaviors according to ISO C standards.
Additionally, mixing signed and unsigned integers can lead to data-dependent results, as demonstrated in the example above.

Use C99 macros for constant prefixes or formatting of the fixed-width types.

Use:
```c
#define SOME_CONSTANT (UINT64_C(1) << 48)
printf("val %" PRIu64 "\n", SOME_CONSTANT);
```

Do not use:
```c
#define SOME_CONSTANT (1ULL << 48)
printf("val %lld\n", SOME_CONSTANT);
```

#### Avoid unaligned access

Avoid assuming that unaligned access is safe.
It is not secure on architectures like Arm, POWER, and others.
Additionally, even on x86, unaligned access can be slower.

#### Structures and Unions

Care should be taken to prevent the compiler from inserting padding bytes within `struct` or `union` types,
as this can affect memory layout and portability.
To control padding and alignment, consider using structure packing techniques specific to your compiler.

Additionally, take precautions to ensure that the compiler does not alter the intended order of bits within bit-fields.
This is particularly important when working with hardware registers or communication protocols where bit order is crucial.

According to the C standard, the layout of structures, including padding and bit-field ordering,
is implementation-defined, meaning it can vary between different compilers and platforms.
Therefore, it is essential to verify that the structure's layout meets your expectations, especially when writing portable code.

For example:
```c
typedef struct {
    uint16_t count;         /* offset 0 */
    uint16_t max_count;     /* offset 2 */
    uint16_t unused0;       /* offset 4 */
    uint16_t enable    : 2; /* offset 6 bits 15-14 */
    uint16_t interrupt : 1; /* offset 6 bit 13     */
    uint16_t unused1   : 7; /* offset 6 bits 12-6  */
    uint16_t complete  : 1; /* offset 6 bit 5      */
    uint16_t unused2   : 4; /* offset 6 bits 4-1   */
    uint16_t periodic  : 1; /* offset 6 bit 0      */
} mytimer_t;

_Static_assert(sizeof(mytimer_t) == 8,
               "mytimer_t struct size incorrect (expected 8 bytes)");
```

To enhance portability, use standard-defined types (e.g., `uint16_t`, `uint32_t`) and avoid relying on compiler-specific behavior.
Where precise control over memory layout is required, such as in embedded systems or when interfacing with hardware,
always verify the structure size and layout using static assertions.

#### Avoid extreme portability

Unless programming for micro-controllers or exotic CPU architectures,
focus on the common denominator of the modern CPU architectures, avoiding
the very maximum portability which can make the code unnecessarily cumbersome.

Some examples:
- It is fair to assume `sizeof(int) == 4` since it is the case on all modern
mainstream architectures.  PDP-11 era is long gone.
- Using `1U` instead of `UINT32_C(1)` or `(uint32_t) 1` is also fine.
- It is fair to assume that `NULL` is matching `(uintptr_t) 0` and it is fair
to `memset()` structures with zero.  Non-zero `NULL` is for retro computing.

## Git Commit Style

Effective version control is critical to modern software development.
Git's powerful features—such as granular commits, branching,
and a versatile staging area—offer unparalleled flexibility.
However, this flexibility can sometimes lead to disorganized commit histories and
merge conflicts if not managed with clear, consistent practices.

By committing often, writing clear messages, and adhering to a common workflow,
developers can not only reduce the potential for errors but also simplify collaboration and future maintenance.
We encourage every team to tailor these best practices to their specific needs while striving
for a shared standard that promotes efficiency and code quality.

Below are the detailed guidelines that build on these principles.
* Group Related Changes Together:
  Each commit should encapsulate a single, coherent change.
  e.g., if you are addressing two separate bugs, create two distinct commits.
  This approach produces focused, small commits that simplify understanding, enable quick rollbacks,
  and foster efficient peer reviews.
  By taking advantage of Git’s staging area and selective file staging,
  you can craft granular commits that make collaboration smoother and more transparent.
* Commit Frequently:
  Making commits often ensures that your changes remain concise and logically grouped.
  Frequent commits not only help maintain a clean history but also allow you to share your progress with your teammates regularly.
  This regular sharing keeps everyone in sync,
  minimizes merge conflicts, and promotes a collaborative environment where integration happens seamlessly.
* Avoid Committing Work in Progress:
  Only commit code when a logical component is in a stable, ready-to-integrate state.
  Break your feature's development into manageable segments that reach a functional milestone quickly,
  so you can commit regularly without compromising quality.
  If you feel the urge to commit merely to clear your working directory for actions like switching branches or pulling changes,
  use Git's stash feature instead.
  This practice helps maintain a stable repository and ensures that your team reviews well-tested, coherent code.
* Test Your Code Before Committing:
  Before committing, ensure that your code has been thoroughly tested.
  Rather than assuming your changes are ready, run comprehensive tests to confirm they work as intended without unintended side effects.
  Testing is especially critical when sharing your code with others,
  as it maintains the overall stability of the project and builds trust among collaborators.
* Utilize Branches for Parallel Development:
  Branches are a powerful tool that enables developers to isolate different lines of work—whether you are developing new features,
  fixing bugs, or exploring innovative ideas.
  By using branches extensively, you can work on your tasks independently and merge only after careful review and testing.
  This not only keeps the main branch stable but also encourages collaborative code reviews and a more organized integration process.

Clear and descriptive commit messages are crucial for maintaining a transparent history of changes and for facilitating effective debugging and tracking.
Please adhere to the guidelines outlined in [How to Write a Git Commit Message](https://cbea.ms/git-commit/).
1. Separate the subject from the body with a blank line.
2. Limit the subject line to 50 characters.
3. Capitalize the subject line.
4. Do not end the subject line with a period.
5. Use the imperative mood in the subject line.
6. Wrap the body at 72 characters.
7. Use the body to explain what and why, not how.

An example (derived from Chris' blog post) looks like the following:
```text
Summarize changes in around 50 characters or less

More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of the commit and the rest of the text as the body. The
blank line separating the summary from the body is critical (unless
you omit the body entirely); various tools like `log`, `shortlog`
and `rebase` can get confused if you run the two together.

Explain the problem that this commit is solving. Focus on why you
are making this change as opposed to how (the code explains that).
Are there side effects or other unintuitive consequences of this
change? Here's the place to explain them.

Further paragraphs come after blank lines.

- Bullet points are okay, too

- Typically a hyphen or asterisk is used for the bullet, preceded
  by a single space, with blank lines in between, but conventions
  vary here

If you use an issue tracker, put references to them at the bottom,
like this:
Close #123
```

Another illustration of effective practice.
```text
commit f1775422bb5a1aa6e79a685dfa7cb54a852b567b
Author: Jim Huang <jserv@ccns.ncku.edu.tw>
Date:   Mon Feb 24 13:08:32 2025 +0800

    Introduce CPU architecture filtering in scheduler
    
    In environments with mixed CPU architectures, it is crucial to ensure
    that an instance runs only on a host with a compatible CPU
    type—preventing, for example, a RISC-V instance from being scheduled on
    an Arm host.
    
    This new scheduler filter enforces that requirement by comparing an
    instance's architecture against the host's allowed architectures. For
    the libvirt driver, the host's guest capabilities are queried, and the
    permitted architectures are recorded in the permitted_instances_types
    list within the host's cpu_info dictionary.
    
    The filter systematically excludes hosts that do not support the
    instance's CPU architecture. Additionally, RISC-V has been added to the
    set of acceptable architectures for scheduling.
    
    Note that the CPU architecture filter is disabled by default.
```

The above is a clear, unformatted description provided in plain text.

In addition, this project expects contributors to follow these additional rules:
* If there is important, useful, or essential conversation or information,
  include a reference or copy it.
* Do not write single-word commits. Provide a descriptive subject.
* Avoid using abusive words.
* Avoid using backticks in commit subjects.
  Backticks can be easily confused with single quotes on some terminals,
  reducing readability. Plain text or single quotes provide sufficient clarity and emphasis.
* Avoid using parentheses in commit subjects.
  Excessive use of parentheses "()" can clutter the subject line,
  making it harder to quickly grasp the essential message.

Some conventions are automatically enforced by the [githooks](https://git-scm.com/docs/githooks).

## References
- [Linux kernel coding style](https://www.kernel.org/doc/html/latest/process/coding-style.html)
- 1999, Brian W. Kernighan and Rob Pike, The Practice of Programming, Addison–Wesley.
- 1993, Bill Shannon, [C Style and Coding Standards for SunOS](https://devnull-cz.github.io/unix-linux-prog-in-c/cstyle.ms.pdf)


================================================
FILE: LICENSE
================================================
Copyright (c) 2020-2021, 2023-2026 National Cheng Kung University, Taiwan.

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: Makefile
================================================
CFLAGS := -O -g \
	-std=c99 -pedantic

CFLAGS_TO_CHECK := \
	-fwrapv \
	-Wall -Wextra \
	-Wno-unused-but-set-variable \
	-Wno-unused-parameter \
	-Wno-unused-function \
	-Wshadow \
	-Wno-variadic-macros \
	-Wno-uninitialized \
	-Wno-strict-prototypes \
	-Wno-declaration-after-statement \
	-Wno-format \
	-Wno-format-pedantic \
	-Wno-overflow

SUPPORTED_CFLAGS :=
# Check if a specific compiler flag is supported, attempting a dummy compilation
# with flags. If successful, it returns the flag string; otherwise, it returns
# an empty string.
# Usage: $(call check_flag, -some-flag)
check_flag = $(shell $(CC) $(1) -S -o /dev/null -xc /dev/null 2>/dev/null; \
              if test $$? -eq 0; then echo "$(1)"; fi)

# Iterate through the list of all potential flags, effectively filtering out all
# unsupported flags.
$(foreach flag, $(CFLAGS_TO_CHECK), $(eval CFLAGS += $(call check_flag, $(flag))))

BUILD_SESSION := .session.mk

-include $(BUILD_SESSION)

STAGE0 := shecc
STAGE1 := shecc-stage1.elf
STAGE2 := shecc-stage2.elf

USE_QEMU ?= 1
OUT ?= out
ARCHS = arm riscv
ARCH ?= $(firstword $(ARCHS))
SRCDIR := $(shell find src -type d)
LIBDIR := $(shell find lib -type d)

BUILTIN_LIBC_SOURCE ?= c.c
BUILTIN_LIBC_HEADER := c.h
STAGE0_FLAGS ?= --dump-ir
STAGE1_FLAGS ?=
DYNLINK ?= 0
ifeq ($(DYNLINK),1)
    ifeq ($(ARCH),riscv)
        # TODO: implement dynamic linking for RISC-V.
        $(error "Dynamic linking mode is not implemented for RISC-V")
    endif
    STAGE0_FLAGS += --dynlink
    STAGE1_FLAGS += --dynlink
endif

SRCS := $(wildcard $(patsubst %,%/main.c, $(SRCDIR)))
OBJS := $(SRCS:%.c=$(OUT)/%.o)
deps := $(OBJS:%.o=%.o.d)
TESTS := $(wildcard tests/*.c)
TESTBINS := $(TESTS:%.c=$(OUT)/%.elf)
SNAPSHOTS = $(foreach SNAPSHOT_ARCH,$(ARCHS), $(patsubst tests/%.c, tests/snapshots/%-$(SNAPSHOT_ARCH)-static.json, $(TESTS)))
SNAPSHOTS += $(patsubst tests/%.c, tests/snapshots/%-arm-dynamic.json, $(TESTS))

all: config bootstrap

sanitizer: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer -O0
sanitizer: LDFLAGS += -fsanitize=address -fsanitize=undefined
sanitizer: config $(OUT)/$(STAGE0)-sanitizer
	$(VECHO) "  Built stage 0 compiler with sanitizers\n"

ifeq (,$(filter $(ARCH),$(ARCHS)))
$(error Support ARM and RISC-V only. Select the target with "ARCH=arm" or "ARCH=riscv")
endif
include mk/$(ARCH).mk
include mk/common.mk

config:
	$(Q)ln -s $(PWD)/$(SRCDIR)/$(ARCH)-codegen.c $(SRCDIR)/codegen.c
	$(Q)$(PRINTF) $(ARCH_DEFS) > $@
	$(VECHO) "Target machine code switch to %s\n" $(ARCH)
	$(Q)$(MAKE) $(BUILD_SESSION) --silent
	$(Q)$(CONFIG_CHECK_CMD)

$(OUT)/tests/%.elf: tests/%.c $(OUT)/$(STAGE0)
	$(VECHO) "  SHECC\t$@\n"
	$(Q)$(OUT)/$(STAGE0) $(STAGE0_FLAGS) -o $@ $< > $(basename $@).log ; \
	chmod +x $@ ; $(PRINTF) "Running $@ ...\n"
	$(Q)$(TARGET_EXEC) $@ && $(call pass)

check: check-stage0 check-stage2 check-abi-stage0 check-abi-stage2

check-stage0: $(OUT)/$(STAGE0) $(TESTBINS) tests/driver.sh
	$(VECHO) "  TEST STAGE 0\n"
	tests/driver.sh 0 $(DYNLINK)

check-stage2: $(OUT)/$(STAGE2) $(TESTBINS) tests/driver.sh
	$(VECHO) "  TEST STAGE 2\n"
	tests/driver.sh 2 $(DYNLINK)

check-sanitizer: $(OUT)/$(STAGE0)-sanitizer tests/driver.sh
	$(VECHO) "  TEST STAGE 0 (with sanitizers)\n"
	$(Q)cp $(OUT)/$(STAGE0)-sanitizer $(OUT)/shecc
	tests/driver.sh 0 $(DYNLINK)
	$(Q)rm $(OUT)/shecc

check-snapshots: $(OUT)/$(STAGE0) $(SNAPSHOTS) tests/check-snapshots.sh
	$(Q)$(foreach SNAPSHOT_ARCH, $(ARCHS), $(MAKE) distclean config check-snapshot ARCH=$(SNAPSHOT_ARCH) DYNLINK=0 --silent;)
	$(Q)$(MAKE) distclean config check-snapshot ARCH=arm DYNLINK=1 --silent
	$(VECHO) "Switching backend back to %s (DYNLINK=0)\n" arm
	$(Q)$(MAKE) distclean config ARCH=arm DYNLINK=0 --silent

check-snapshot: $(OUT)/$(STAGE0) tests/check-snapshots.sh
	$(VECHO) "Checking snapshot for %s (DYNLINK=%s)\n" $(ARCH) $(DYNLINK)
	tests/check-snapshots.sh $(ARCH) $(DYNLINK)
	$(VECHO) "  OK\n"

# TODO: Add an ABI conformance test suite for the RISC-V architecture
check-abi-stage0: $(OUT)/$(STAGE0)
	$(Q)if [ "$(ARCH)" = "arm" ]; then \
		tests/$(ARCH)-abi.sh 0 $(DYNLINK); \
	else \
		echo "Skip ABI compliance validation"; \
	fi

check-abi-stage2: $(OUT)/$(STAGE2)
	$(Q)if [ "$(ARCH)" = "arm" ]; then \
		tests/$(ARCH)-abi.sh 2 $(DYNLINK); \
	else \
		echo "Skip ABI compliance validation"; \
	fi

update-snapshots: tests/update-snapshots.sh
	$(Q)$(foreach SNAPSHOT_ARCH, $(ARCHS), $(MAKE) distclean config update-snapshot ARCH=$(SNAPSHOT_ARCH) DYNLINK=0 --silent;)
	$(Q)$(MAKE) distclean config update-snapshot ARCH=arm DYNLINK=1 --silent
	$(VECHO) "Switching backend back to %s (DYNLINK=0)\n" arm
	$(Q)$(MAKE) distclean config ARCH=arm DYNLINK=0 --silent

update-snapshot: $(OUT)/$(STAGE0) tests/update-snapshots.sh
	$(VECHO) "Updating snapshot for %s (DYNLINK=%s)\n" $(ARCH) $(DYNLINK)
	tests/update-snapshots.sh $(ARCH) $(DYNLINK)
	$(VECHO) "  OK\n"

$(OUT)/%.o: %.c
	$(VECHO) "  CC\t$@\n"
	$(Q)$(CC) -o $@ $(CFLAGS) -c -MMD -MF $@.d $<

SHELL_HACK := $(shell mkdir -p $(OUT) $(OUT)/$(SRCDIR) $(OUT)/tests)

$(OUT)/norm-lf: tools/norm-lf.c
	$(VECHO) "  CC+LD\t$@\n"
	$(Q)$(CC) $(CFLAGS) -o $@ $^

$(OUT)/libc.inc: $(OUT)/inliner $(OUT)/norm-lf $(LIBDIR)/$(BUILTIN_LIBC_SOURCE) $(LIBDIR)/$(BUILTIN_LIBC_HEADER)
	$(VECHO) "  GEN\t$@\n"
	$(Q)$(OUT)/norm-lf $(LIBDIR)/$(BUILTIN_LIBC_SOURCE) $(OUT)/c.normalized.c
	$(Q)$(OUT)/norm-lf $(LIBDIR)/$(BUILTIN_LIBC_HEADER) $(OUT)/c.normalized.h
	$(Q)$(OUT)/inliner $(OUT)/c.normalized.c $(OUT)/c.normalized.h $@
	$(Q)$(RM) $(OUT)/c.normalized.c $(OUT)/c.normalized.h

$(OUT)/inliner: tools/inliner.c
	$(VECHO) "  CC+LD\t$@\n"
	$(Q)$(CC) $(CFLAGS) -o $@ $^

$(OUT)/$(STAGE0): $(OUT)/libc.inc $(OBJS)
	$(VECHO) "  LD\t$@\n"
	$(Q)$(CC) $(OBJS) $(LDFLAGS) -o $@

$(OUT)/$(STAGE0)-sanitizer: $(OUT)/libc.inc $(OBJS)
	$(VECHO) "  LD\t$@ (with sanitizers)\n"
	$(Q)$(CC) $(OBJS) $(LDFLAGS) -o $@

$(OUT)/$(STAGE1): $(OUT)/$(STAGE0)
	$(Q)$(STAGE1_CHECK_CMD)
	$(VECHO) "  SHECC\t$@\n"
	$(Q)$(OUT)/$(STAGE0) $(STAGE0_FLAGS) -o $@ $(SRCDIR)/main.c > $(OUT)/shecc-stage1.log
	$(Q)chmod a+x $@

$(OUT)/$(STAGE2): $(OUT)/$(STAGE1)
	$(VECHO) "  SHECC\t$@\n"
	$(Q)$(TARGET_EXEC) $(OUT)/$(STAGE1) $(STAGE1_FLAGS) -o $@ $(SRCDIR)/main.c

bootstrap: $(OUT)/$(STAGE2)
	$(Q)chmod 775 $(OUT)/$(STAGE2)
	$(Q)if ! diff -q $(OUT)/$(STAGE1) $(OUT)/$(STAGE2); then \
	echo "Unable to bootstrap. Aborting"; false; \
	fi

$(BUILD_SESSION):
	$(PRINTF) "ARCH=$(ARCH)" > $@

.PHONY: clean
clean:
	-$(RM) $(OUT)/$(STAGE0) $(OUT)/$(STAGE1) $(OUT)/$(STAGE2)
	-$(RM) $(OBJS) $(deps)
	-$(RM) $(TESTBINS) $(OUT)/tests/*.log $(OUT)/tests/*.lst
	-$(RM) $(OUT)/shecc*.log
	-$(RM) $(OUT)/libc.inc

distclean: clean
	-$(RM) $(OUT)/inliner $(OUT)/norm-lf $(OUT)/target $(SRCDIR)/codegen.c config $(BUILD_SESSION)
	-$(RM) DOM.dot CFG.dot

-include $(deps)


================================================
FILE: README.md
================================================
# shecc : self-hosting and educational C optimizing compiler

<p align="center"><img src="https://user-images.githubusercontent.com/18013815/91671374-b2f0db00-eb58-11ea-8d55-858e9fb160c0.png" alt="logo image" width=40%></p>

## Introduction

`shecc` is built from scratch, targeting both 32-bit Arm and RISC-V architectures,
as a self-compiling compiler for a subset of the C language.
Despite its simplistic nature, it is capable of performing basic optimization strategies as a standalone optimizing compiler.

### Features

* Generate executable Linux ELF binaries for ARMv7-A and RV32IM.
* Provide a minimal C standard library for basic I/O on GNU/Linux.
* The cross-compiler is written in ANSI C, making it compatible with most platforms.
* Include a self-contained C front-end with an integrated machine code generator; no external assembler or linker needed.
* Utilize a two-pass compilation process: the first pass checks syntax and breaks down complex statements into basic operations,
  while the second pass translates these operations into Arm/RISC-V machine code.
* Develop a register allocation system that is compatible with RISC-style architectures.
* Implement an architecture-independent, [static single assignment](https://en.wikipedia.org/wiki/Static_single-assignment_form) (SSA)-based middle-end for enhanced optimizations.

## Compatibility

`shecc` is capable of compiling C source files written in the following
syntax:
* data types: `char`, `int`, `struct`, `enum`, `typedef`, and pointer types
* condition statements: `if`, `else`, `while`, `for`, `do-while`, `switch`, `case`, `default`, `break`, `continue`, `return`, and
                        general expressions
* operators: all arithmetic, logical, bitwise, and assignment operators including compound assignments
  (`+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, `>>=`)
* arrays: global/local arrays with initializers, multi-dimensional arrays
* functions: function declarations, definitions, and calls with fixed arguments
* variadic functions: basic support via direct pointer arithmetic (no `<stdarg.h>`)
* typedef: type aliasing including typedef pointers (`typedef int *ptr_t;`)
* pointers: full pointer arithmetic, multi-level pointer dereference (`***ptr`)
* global/local variable initializations for all supported data types
    - e.g. `int i = [expr];`, `int arr[] = {1, 2, 3};`
* preprocessor directives: `#define`, `#ifdef`, `#ifndef`, `#elif`, `#else`, `#endif`, `#undef`, `#error`, and `#include`
* function-like macros with parameters and `__VA_ARGS__` support

The backend targets armv7hf with Linux ABI, verified on Raspberry Pi 3,
and also supports RISC-V 32-bit architecture, verified with QEMU.

## Bootstrapping

The steps to validate `shecc` bootstrapping:
1. `stage0`: `shecc` source code is initially compiled using an ordinary compiler
   which generates a native executable. The generated compiler can be used as a
   cross-compiler.
2. `stage1`: The built binary reads its own source code as input and generates an
   ARMv7-A/RV32IM  binary.
3. `stage2`: The generated ARMv7-A/RV32IM binary is invoked (via QEMU or running on
   Arm and RISC-V devices) with its own source code as input and generates another
   ARMv7-A/RV32IM binary.
4. `bootstrap`: Build the `stage1` and `stage2` compilers, and verify that they are
   byte-wise identical. If so, `shecc` can compile its own source code and produce
   new versions of that same program.

## Prerequisites

Code generator in `shecc` does not rely on external utilities. You only need
ordinary C compilers such as `gcc` and `clang`. However, `shecc` would bootstrap
itself, and Arm/RISC-V ISA emulation is required. Install QEMU for Arm/RISC-V user
emulation on GNU/Linux:
```shell
$ sudo apt-get install qemu-user
```

The build system is able to verify whether the running machine can perform native
execution without QEMU. The host machine may install the prebuilt
[fastfetch](https://github.com/fastfetch-cli/fastfetch/), which allows the build
system to determine whether native execution can be enabled.

It is still possible to build `shecc` on macOS or Microsoft Windows. However,
the second stage bootstrapping would fail due to `qemu-arm` absence.

To execute the snapshot test, install the packages below:
```shell
$ sudo apt-get install graphviz jq
```

Additionally, because `shecc` supports the dynamic linking mode for the Arm architecture,
it needs to install the ARM GNU toolchain to obtain the ELF interpreter and other dependencies:
```shell
$ sudo apt-get install gcc-arm-linux-gnueabihf
```
Another approach is to manually download and install the toolchain from [ARM Developer website](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads).

Select "x86_64 Linux hosted cross toolchains" - "AArch32 GNU/Linux target with hard float (arm-none-linux-gnueabihf)" to download the toolchain.

## Build and Verify

Configure which backend you want, `shecc` supports ARMv7-A and RV32IM backend:
```shell
$ make config ARCH=arm
# Target machine code switch to Arm

$ make config ARCH=riscv
# Target machine code switch to RISC-V
```

Run `make` and you should see this:
```shell
$ make
  CC+LD	out/inliner
  GEN	out/libc.inc
  CC	out/src/main.o
  LD	out/shecc
  SHECC	out/shecc-stage1.elf
  SHECC	out/shecc-stage2.elf
```

Run `make DYNLINK=1` to use the dynamic linking mode and generate the dynamically linked compiler:
```shell
# If using the dynamic linking mode, you should add 'DYNLINK=1' for each 'make' command.
$ make DYNLINK=1
  CC+LD	out/inliner
  GEN	out/libc.inc
  CC	out/src/main.o
  LD	out/shecc
  SHECC	out/shecc-stage1.elf
  SHECC	out/shecc-stage2.elf

$ file out/shecc-stage2.elf
out/shecc-stage2.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, not stripped
```

For development builds with memory safety checks:
```shell
$ make sanitizer
$ make check-sanitizer
```

File `out/shecc` is the first stage compiler. Its usage:
```shell
$ shecc [-o output] [+m] [--no-libc] [--dump-ir] [--dynlink] <infile.c>
```

Compiler options:
- `-o` : Specify output file name (default: `out.elf`)
- `+m` : Use hardware multiplication/division instructions (default: disabled)
- `--no-libc` : Exclude embedded C library (default: embedded)
- `--dump-ir` : Dump intermediate representation (IR)
- `--dynlink` : Use dynamic linking (default: disabled)

Example 1: static linking mode
```shell
$ out/shecc -o fib tests/fib.c
$ chmod +x fib
$ qemu-arm fib
```

Example 2: dynamic linking mode

Notice that `/usr/arm-linux-gnueabihf` is the ELF interpreter prefix. Since the path may be different if you manually install the ARM GNU toolchain instead of using `apt-get`, you should set the prefix to the actual path.
```shell
$ out/shecc --dynlink -o fib tests/fib.c
$ chmod +x fib
$ qemu-arm -L /usr/arm-linux-gnueabihf fib
```

### IR Regression Tests

To ensure the consistency of frontend (lexer, parser) behavior when working on it, the snapshot test is introduced.
The snapshot test dumps IRs from the executable and compares the structural identity with the provided snapshots.

Verify the emitted IRs by specifying `check-snapshots` target when invoking `make`:
```shell
$ make check-snapshots
```

If the compiler frontend is updated, the emitted IRs might be changed.
Thus, you can update snapshots by specifying `update-snapshots` target when invoking `make`:
```shell
$ make update-snapshots
```

Notice that the above 2 targets will update all backend snapshots at once, to update/check current backend's snapshot, 
use `update-snapshot` / `check-snapshot` instead.

### Unit Tests

`shecc` comes with a comprehensive test suite (200+ test cases). To run the tests:
```shell
# Add 'DYNLINK=1' if using the dynamic linking mode.
$ make check          # Run all tests (stage 0 and stage 2)
$ make check-stage0   # Test stage 0 compiler only
$ make check-stage2   # Test stage 2 compiler only
$ make check-sanitizer # Test with AddressSanitizer and UBSan
```

The test suite covers:
* Basic data types and operators
* Control flow statements
* Arrays and pointers (including multi-level dereference)
* Structs, enums, and typedefs
* Variadic functions
* Preprocessor directives and macros
* Self-hosting validation

Reference output:
```
  TEST STAGE 0
...
int main(int argc, int argv) { exit(sizeof(char)); } => 1
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
int main(int argc, int argv) { int a; a = 0; switch (3) { case 0: return 2; default: a = 10; break; } exit(a); } => 10
OK
  TEST STAGE 2
...
int main(int argc, int argv) { exit(sizeof(char*)); }
exit code => 4
output => 
int main(int argc, int argv) { exit(sizeof(int*)); }
exit code => 4
output => 
OK
```

To clean up the generated compiler files, execute the command `make clean`.
For resetting architecture configurations, use the command `make distclean`.

## Intermediate Representation

Once the option `--dump-ir` is passed to `shecc`, the intermediate representation (IR)
will be generated. Take the file `tests/fib.c` for example. It consists of a recursive
Fibonacci sequence function.
```c
int fib(int n)
{
    if (n == 0)
        return 0;
    else if (n == 1)
        return 1;
    return fib(n - 1) + fib(n - 2);
}
```

Execute the following to generate IR:
```shell
$ out/shecc --dump-ir -o fib tests/fib.c
```

Line-by-line explanation between C source and IR (variable and label numbering may differ):
```c
C Source                  IR                                         Explanation
-------------------+--------------------------------------+--------------------------------------------------------------------------------------
int fib(int n)       def int @fib(int %n)
{                    {
  if (n == 0)          const %.t871, 0                      Load constant 0 into a temporary variable ".t871"
                       %.t872 = eq %n, %.t871               Test if "n" is equal to ".t871", store result in ".t872"
                       br %.t872, .label.1430, .label.1431  If ".t872" is non-zero, branch to label ".label.1430", otherwise to ".label.1431"
                     .label.1430:
    return 0;          const %.t873, 0                      Load constant 0 into a temporary variable ".t873"
                       ret %.t873                           Return ".t873"
                     .label.1431:
  else if (n == 1)     const %.t874, 1                      Load constant 1 into a temporary variable ".t874"
                       %.t875 = eq %n, %.t874               Test if "n" is equal to ".t874", store result in ".t875"
                       br %.t875, .label.1434, .label.1435  If ".t875" is true, branch to ".label.1434", otherwise to ".label.1435"
                     .label.1434:
    return 1;          const %.t876, 1                      Load constant 1 into a temporary variable ".t876"
                       ret %.t876                           Return ".t876"
                     .label.1435:
  return fib(n - 1)    const %.t877, 1                      Load constant 1 into ".t877"
                       %.t878 = sub %n, %.t877              Subtract ".t877" from "n", store in ".t878"
                       push %.t878                          Prepare argument ".t878" for function call
                       call @fib, 1                         Call function "@fib" with 1 argument
         +             retval %.t879                        Store the return value in ".t879"
         fib(n - 2);   const %.t880, 2                      Load constant 2 into ".t880"
                       %.t881 = sub %n, %.t880              Subtract ".t880" from "n", store in ".t881"
                       push %.t881                          Prepare argument ".t881" for function call
                       call @fib, 1                         Call function "@fib" with 1 argument
                       retval %.t882                        Store the return value in ".t882"
                       %.t883 = add %.t879, %.t882          Add ".t879" and ".t882", store in ".t883"
                       ret %.t883                           Return ".t883"
}                    }
```

## C99 Compliance

shecc implements a subset of C99 suitable for self-hosting and systems programming.
For detailed information about supported features, missing functionality, and non-standard behaviors,
see [COMPLIANCE.md](COMPLIANCE.md).

## Known Issues

2. Full `<stdarg.h>` support is not available. Variadic functions work via direct pointer arithmetic.
   See the `printf` implementation in `lib/c.c` for the supported approach.
3. The C front-end operates directly on token streams without building a full AST.
4. Complex pointer arithmetic expressions like `*(p + offset)` have limited support.

## License

`shecc` is freely redistributable under the BSD 2 clause license.
Use of this source code is governed by a BSD-style license that can be found in the `LICENSE` file.


================================================
FILE: docs/dynamic-linking.md
================================================
# Dynamic Linking

## Build dynamically linked shecc and programs

Build the dynamically linked version of shecc, but notice that shecc currently doesn't support dynamic linking for the RISC-V architecture:

```shell
$ make ARCH=arm DYNLINK=1
```

Next, you can use shecc to build dynamically linked programs by adding the `--dynlink` flag:

```shell
# Use the stage 0 compiler
$ out/shecc --dynlink -o <output> <input.c>
# Use the stage 1 or stage 2 compiler
$ qemu-arm -L <LD_PREFIX> out/shecc-stage2.elf --dynlink -o <output> <input.c>

# Execute the compiled program
$ qemu-arm -L <LD_PREFIX> <output>
```

When executing a dynamically linked program, you should set the ELF interpreter prefix so that `ld.so` can be invoked. Generally, it should be `/usr/arm-linux-gnueabihf` if you have installed the ARM GNU toolchain by `apt`. Otherwise, you should find and specify the correct path if you manually installed the toolchain.

## Stack frame layout

### Arm32

In both static and dynamic linking modes, the stack frame layout for each function can be illustrated as follows:

```
High Address
+------------------+
| incoming args    |
+------------------+ <- sp + total_size
| saved lr         |
+------------------+
| saved r11        |
+------------------+
| saved r10        |
+------------------+
| saved r9         |
+------------------+
| saved r8         |
+------------------+
| saved r7         |
+------------------+
| saved r6         |
+------------------+
| saved r5         |
+------------------+
| saved r4         |
+------------------+
| (padding)        |
+------------------+
| local variables  |
+------------------+ <- sp + (MAX_PARAMS - MAX_ARGS_IN_REG) * 4
| outgoing args    |
+------------------+ <- sp (MUST be aligned to 8 bytes)
Low Address
```

* `total_size`: includes the size of the following elements:
  * `outgoing args`: a fixed size - `(MAX_PARAMS - MAX_ARGS_IN_REG) * 4` bytes
  * `local variables`
  * `saved r4-r11 and lr`: a fixed size - 36 bytes

* Note that the space for `incoming args` belongs to the caller's stack frame, while the remaining space belongs to the callee's stack frame.

### RISC-V

(Currently not supported)

## Calling Convention

### Arm32

Regardless of which mode is used, the caller performs the following operations to comply with the Arm Architecture Procedure Call Standard (AAPCS) when calling a function.

* The first four arguments are put into registers `r0` - `r3`
* Any additional arguments are passed on the stack. Arguments are pushed onto the stack starting from the last argument, so the fifth argument resides at a lower address and the last argument at a higher address.
* Align the stack pointer to 8 bytes, as external functions may access 8-byte objects that require such alignment.

Then, the callee will perform these operations:

- Preserve the contents of registers `r4` - `r11` on the stack upon function entry.
  - The callee also pushes the content of `lr` onto the stack to preserve the return address; however, this operation is not required by the AAPCS.

- Restore these registers from the stack upon returning.

### RISC-V

In the RISC-V architecture, registers `a0` - `a7` are used as argument registers; that is, the first eight arguments are passed into these registers.

Since the current implementation of shecc supports up to 8 arguments, no argument needs to be passed onto the stack.

## Runtime execution flow of a dynamically linked program

```
          |                                                                     +---------------------------+
          |                                                                     |  program                  |
          | +-------------+                             +----------------+      |                           |
          | | shell       |                             | Dynamic linker |      |  +--------+ +----------+  |
userspace | |             |                             |                +------+->| entry  | | main     |  |
          | | $ ./program |                             | (ld.so)        |      |  | point  | | function |  |
program   | +-----+-------+                             +----------------+      |  +-+------+ +-----+----+  |
          |       |                                             ^               |    |         ^    |       |
          |       |                                             |               +----+---------+----+-------+
          |       |                                             |                    |         |    |
          |       |                                             |                    |         |    |
----------+-------+---------------------------------------------+--------------------+---------+----+----------------------
          |       |                                             |                    |         |    |
          |       v                                             |                    v         |    v
          |   +-------+ (It may be another                      |                +-------------+-----+    +------+
glibc     |   | execl |                                         |                | __libc_start_main +--->| exit |
          |   +---+---+  equivalent call)                       |                +-------------------+    +---+--+
          |       |                                             |                                             |
----------+-------+---------------------------------------------+---------------------------------------------+------------
system    |       |                                             |                                             |
          |       v                                             |                                             v
call      |   +------+  (It may be another                      |                                         +-------+
          |   | exec |                                          |                                         | _exit |
interface |   +---+--+   equivalent syscall)                    |                                         +---+---+
          |       |                                             |                                             |
----------+-------+---------------------------------------------+---------------------------------------------+------------
          |       |                                             |                                             |
          |       v                                             |                                             v
          |   +--------------+    +---------------+    +--------+-------------+                        +---------------+
          |   | Validate the |    | Create a new  |    | Startup the kernel's |                        | Delete the    |
kernel    |   |              +--->|               +--->|                      |                        |               |
          |   | executable   |    | process image |    | program loader       |                        | process image |
          |   +--------------+    +---------------+    +----------------------+                        +---------------+
```

1. A running process (e.g.: a shell) executes the specified program (`program`), which is dynamically linked.
2. Kernel validates the executable and creates a process image if the validation passes.
3. Dynamic linker (`ld.so`) is invoked by the kernel's program loader.
   * For the Arm architecture, the dynamic linker is `/lib/ld-linux-armhf.so.3`.
4. Linker loads shared libraries such as `libc.so`.
5. Linker resolves symbols and fills global offset table (GOT).
6. Control transfers to the program, which starts at the entry point.
7. Program executes `__libc_start_main` at the beginning.
8. `__libc_start_main` calls the *main wrapper*, which pushes registers r4-r11 and lr onto the stack, sets up a global stack for all global variables (excluding read-only variables), and initializes them.
9. Execute the *main wrapper*, and then invoke the main function.
10. After the `main` function returns, the *main wrapper* restores the necessary registers and passes control back to  `__libc_start_main`, which implicitly calls `exit(3)` to terminate the program.
       * Or, the `main` function can also call `exit(3)` or `_exit(2)` to directly terminate itself.

## Dynamic sections

When using dynamic linking, the following sections are generated for compiled programs:

1. `.interp` - Path to dynamic linker
2. `.dynsym` - Dynamic symbol table
3. `.dynstr` - Dynamic string table
4. `.rel.plt` - PLT relocations
5. `.plt` - Procedure Linkage Table
6. `.got` - Global Offset Table
7. `.dynamic` - Dynamic linking information

### Initialization of all GOT entries

* `GOT[0]` is set to the starting address of the `.dynamic` section.
* `GOT[1]` and `GOT[2]` are initialized to zero and reserved for the `link_map` and the resolver (`__dl_runtimer_resolve`).
  * The dynamic linker modifies them to point to the actual addresses at runtime.
* `GOT[3]` - `GOT[N]` are initially set to the address of `PLT[0]` at compile time, causing the first call to an external function to invoke the resolver at runtime.

### Explanation for PLT stubs (Arm32)

Under the Arm architecture, the resolver assumes that the following three conditions are met:

* `[sp]` contains the return address from the original function call.
* `ip` stores the address of the callee's GOT entry.
* `lr` stores the address of `GOT[2]`.

Therefore, the first entry (`PLT[0]`) contains the following instructions to satisfy the first and third requirements, and then to invoke the resolver.

```
push	{lr}		@ (str lr, [sp, #-4]!)
movw	sl, #:lower16:(&GOT[2])
movt	sl, #:upper16:(&GOT[2])
mov	lr, sl
ldr	pc, [lr]
```

1. Push register `lr` onto the stack.
2. Set register `sl` to the address of `GOT[2]`.
3. Move the value of `sl` to `lr`.
4. Load the value located at `[lr]` into the program counter (`pc`)

The remaining PLT entries correspond to all external functions, and each entry includes the following instructions to fulfill the second requirement:

```
movw ip, #:lower16:(&GOT[x])
movt ip, #:upper16:(&GOT[x])
ldr  pc, [ip]
```

1. Set register `ip` to the address of `GOT[x]`. 
2. Assign register `pc` to the value of `GOT[x]`. That is, set `pc` to the address of the callee.

## PLT execution path and performance overhead

Since calling an external function needs a PLT stub for indirect invocation, the execution path of the first call is as follows:

1. Call the corresponding PLT stub of the external function.
2. The PLT stub reads the GOT entry.
3. Since the GOT entry is initially set to point to the first PLT entry, the call jumps to `PLT[0]`, which in turn calls the resolver.
4. The resolver handles the symbol and updates the GOT entry.
5. Jump to the actual function to continue execution.

For subsequent calls, the execution path only performs steps 1, 2 and 5. Regardless of whether it is the first call or a subsequent call, calling an external function requires executing additional instructions. It is evident that the overhead accounts to 3-8 instructions compared to a direct call.

For a bootstrapping compiler, this overhead is acceptable.

## Binding

Each external function must perform relocation via the resolver; in other words, each "symbol" needs to **bind** to its actual address.

There are two types of binding:

### Lazy binding

The dynamic linker defers function call resolution until the function is called at runtime.

### Immediate handling

The dynamic linker resolves all symbols when the program is started, or when the shared library is loaded via `dlopen`.

## Limitations

For the current implementation of dynamic linking, note the following:

* GOT is located in a writable segment (`.data` segment).
* The `PT_GNU_RELRO` program header has not yet been implemented.
* `DT_BIND_NOW` (force immediate binding) is not set.

This implies that:

* GOT entries can be modified at runtime, which may create a potential ROP (Return-Oriented Programming) attack vector.
* Function pointers (GOT entries) might be hijacked due to the absence of full RELRO protection.

## Reference

* man page: `ld(1)`
* man page: `ld.so(8)`
* glibc - [`__dl_runtime_resolve`](https://elixir.bootlin.com/glibc/glibc-2.41.9000/source/sysdeps/arm/dl-trampoline.S#L30) implementation (for Arm32)
* Application Binary Interface for the Arm Architecture - [`abi-aa`](https://github.com/ARM-software/abi-aa)
  * `aaelf32`
  * `aapcs32`


================================================
FILE: lib/c.c
================================================
/*
 * shecc - Self-Hosting and Educational C Compiler.
 *
 * shecc is freely redistributable under the BSD 2 clause license. See the
 * file "LICENSE" for information on usage and redistribution of this file.
 */

/* minimal libc implementation */
#include "c.h"
#define INT_BUF_LEN 16

#define __is_alpha(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
#define __is_digit(c) ((c >= '0' && c <= '9'))
#define __is_hex(c) \
    (__is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))

int isdigit(int c)
{
    return __is_digit(c);
}

int isalpha(int c)
{
    return __is_alpha(c);
}

int isalnum(int c)
{
    return __is_alpha(c) || __is_digit(c);
}

int isxdigit(int c)
{
    return __is_hex(c);
}

int isblank(int c)
{
    return c == ' ' || c == '\t';
}

int strlen(char *str)
{
    /* process the string by checking 4 characters (a 32-bit word) at a time */
    int i = 0;
    for (;; i += 4) {
        if (!str[i])
            return i;
        if (!str[i + 1])
            return i + 1;
        if (!str[i + 2])
            return i + 2;
        if (!str[i + 3])
            return i + 3;
    }
}

int strcmp(char *s1, char *s2)
{
    int i = 0;
    while (s1[i] && s2[i]) {
        if (s1[i] < s2[i])
            return -1;
        if (s1[i] > s2[i])
            return 1;
        i++;
    }
    return s1[i] - s2[i];
}

int strncmp(char *s1, char *s2, int len)
{
    int i = 0;
    while (i < len) {
        if (s1[i] < s2[i])
            return -1;
        if (s1[i] > s2[i])
            return 1;
        if (!s1[i])
            return 0;
        i++;
    }
    return 0;
}

char *strcpy(char *dest, char *src)
{
    int i = 0;
    while (src[i]) {
        dest[i] = src[i];
        i++;
    }
    dest[i] = 0;
    return dest;
}

char *strncpy(char *dest, char *src, int len)
{
    int i = 0;
    int beyond = 0;
    while (i < len) {
        if (beyond == 0) {
            dest[i] = src[i];
            if (src[i] == 0)
                beyond = 1;
        } else {
            dest[i] = 0;
        }
        i++;
    }
    return dest;
}

char *memcpy(char *dest, char *src, int count)
{
    int i = 0;

    /* Continues as long as there are at least 4 bytes remaining to copy. */
    for (; i + 4 <= count; i += 4) {
        dest[i] = src[i];
        dest[i + 1] = src[i + 1];
        dest[i + 2] = src[i + 2];
        dest[i + 3] = src[i + 3];
    }

    /* Ensure all @count bytes are copied, even if @count is not a multiple of
     * 4, or if @count was less than 4 initially.
     */
    for (; i < count; i++)
        dest[i] = src[i];

    return dest;
}

int memcmp(void *s1, void *s2, int n)
{
    char *p1 = (char *) s1, *p2 = (char *) s2;

    for (int i = 0; i < n; i++) {
        if (p1[i] < p2[i])
            return -1;
        if (p1[i] > p2[i])
            return 1;
    }
    return 0;
}

void *memset(void *s, int c, int n)
{
    int i = 0;
    char *ptr = (char *) s;
    char byte_val = (char) c;
    for (; i + 4 <= n; i += 4) {
        ptr[i] = byte_val;
        ptr[i + 1] = byte_val;
        ptr[i + 2] = byte_val;
        ptr[i + 3] = byte_val;
    }

    for (; i < n; i++)
        ptr[i] = byte_val;

    return s;
}

/* set 10 digits (32bit) without div
 *
 * This function converts a given integer value to its string representation
 * in base-10 without using division operations. The method involves calculating
 * the approximate quotient and remainder using bitwise operations, which are
 * then used to derive each digit of the result.
 *
 * The logic is based on an efficient method of dividing by constants, as
 * detailed in the reference link:
 * http://web.archive.org/web/20180517023231/http://www.hackersdelight.org/divcMore.pdf.
 * This approach avoids expensive division instructions by using a series of
 * bitwise shifts and additions to calculate the quotient and remainder.
 */
void __str_base10(char *pb, int val)
{
    int neg = 0;
    int q, r, t;
    int i = INT_BUF_LEN - 1;

    if (val == -2147483648) {
        strncpy(pb + INT_BUF_LEN - 11, "-2147483648", 11);
        return;
    }
    if (val < 0) {
        neg = 1;
        val = -val;
    }

    while (val) {
        q = (val >> 1) + (val >> 2);
        q += (q >> 4);
        q += (q >> 8);
        q += (q >> 16);
        q >>= 3;
        r = val - (((q << 2) + q) << 1);
        t = ((r + 6) >> 4);
        q += t;
        r -= (((t << 2) + t) << 1);

        pb[i] += r;
        val = q;
        i--;
    }

    if (neg)
        pb[i] = '-';
}

void __str_base8(char *pb, int val)
{
    int c = INT_BUF_LEN - 1, v;

    /* Because every 3 binary digits can be converted to 1 octal digit, here
     * performs the conversion 10 times, derived from 32 divided by 3.
     *
     * Finally, the remaining 2 bits are processed after the loop.
     */
    int times = (sizeof(int) << 3) / 3;
    for (int i = 0; i < times; i++) {
        v = val & 0x7;
        pb[c] = '0' + v;
        val >>= 3;
        c--;
    }
    v = val & 0x3;
    pb[c] = '0' + v;
}

void __str_base16(char *pb, int val)
{
    int c = INT_BUF_LEN - 1;
    int times = sizeof(int) << 1;
    for (int i = 0; i < times; i++) {
        int v = val & 0xf;
        if (v < 10)
            pb[c] = '0' + v;
        else if (v < 16)
            pb[c] = 'a' + v - 10;
        else {
            abort();
            break;
        }
        val >>= 4;
        c--;
    }
}

/* The specification of snprintf() is defined in C99 7.19.6.5, and its behavior
 * and return value should comply with the following description:
 * - If n is zero, nothing is written.
 * - Writes at most n bytes, including the null character.
 * - On success, the return value should be the length of the entire converted
 *   string even if n is insufficient to store it.
 *
 * Thus, a structure fmtbuf_t is defined for formatted output conversion for
 * the functions in the printf() family.
 * @buf: the current position of the buffer.
 * @n  : the remaining space of the buffer.
 * @len: the number of characters that would have been written (excluding the
 * null terminator) had n been sufficiently large.
 *
 * Once a write operation is performed, buf and n will be respectively
 * incremented and decremented by the actual written size if n is sufficient,
 * and len must be incremented to store the length of the entire converted
 * string.
 */
typedef struct {
    char *buf;
    int n;
    int len;
} fmtbuf_t;

void __fmtbuf_write_char(fmtbuf_t *fmtbuf, int val)
{
    fmtbuf->len += 1;

    /* Write the given character when n is greater than 1.
     * This means preserving one position for the null character.
     */
    if (fmtbuf->n <= 1)
        return;

    char ch = (char) (val & 0xFF);
    fmtbuf->buf[0] = ch;
    fmtbuf->buf += 1;
    fmtbuf->n -= 1;
}

void __fmtbuf_write_str(fmtbuf_t *fmtbuf, char *str, int l)
{
    fmtbuf->len += l;

    /* Write the given string when n is greater than 1.
     * This means preserving one position for the null character.
     */
    if (fmtbuf->n <= 1)
        return;

    /* If the remaining space is less than the length of the string, write only
     * n - 1 bytes.
     */
    int sz = fmtbuf->n - 1;
    l = l <= sz ? l : sz;
    strncpy(fmtbuf->buf, str, l);
    fmtbuf->buf += l;
    fmtbuf->n -= l;
}

void __format(fmtbuf_t *fmtbuf,
              int val,
              int width,
              int zeropad,
              int base,
              int alternate_form)
{
    char pb[INT_BUF_LEN], ch;
    int pbi;

    /* set to zeroes */
    for (pbi = 0; pbi < INT_BUF_LEN; pbi++)
        pb[pbi] = '0';

    pbi = 0;

    switch (base) {
    case 8:
        __str_base8(pb, val);
        break;
    case 10:
        __str_base10(pb, val);
        break;
    case 16:
        __str_base16(pb, val);
        break;
    default:
        abort();
        break;
    }

    while (pb[pbi] == '0' && pbi < INT_BUF_LEN - 1)
        pbi++;

    switch (base) {
    case 8:
        if (alternate_form) {
            if (width && zeropad && pb[pbi] != '0') {
                __fmtbuf_write_char(fmtbuf, '0');
                width -= 1;
            } else if (pb[pbi] != '0')
                pb[--pbi] = '0';
        }
        break;
    case 10:
        if (width && zeropad && pb[pbi] == '-') {
            __fmtbuf_write_char(fmtbuf, '-');
            pbi++;
            width--;
        }
        break;
    case 16:
        if (alternate_form) {
            if (width && zeropad && pb[pbi] != '0') {
                __fmtbuf_write_char(fmtbuf, '0');
                __fmtbuf_write_char(fmtbuf, 'x');
                width -= 2;
            } else if (pb[pbi] != '0') {
                pb[--pbi] = 'x';
                pb[--pbi] = '0';
            }
        }
        break;
    }

    width -= (INT_BUF_LEN - pbi);
    if (width < 0)
        width = 0;

    ch = zeropad ? '0' : ' ';
    while (width) {
        __fmtbuf_write_char(fmtbuf, ch);
        width--;
    }

    __fmtbuf_write_str(fmtbuf, pb + pbi, INT_BUF_LEN - pbi);
}

void __format_to_buf(fmtbuf_t *fmtbuf, char *format, int *var_args)
{
    int si = 0, pi = 0;

    while (format[si]) {
        if (format[si] != '%') {
            __fmtbuf_write_char(fmtbuf, format[si]);
            si++;
        } else {
            int w = 0, zp = 0, pp = 0, v = var_args[pi], l;

            si++;
            if (format[si] == '#') {
                pp = 1;
                si++;
            }
            if (format[si] == '0') {
                zp = 1;
                si++;
            }
            if (format[si] >= '1' && format[si] <= '9') {
                w = format[si] - '0';
                si++;
                while (format[si] >= '0' && format[si] <= '9') {
                    w *= 10;
                    w += format[si] - '0';
                    si++;
                }
            }
            switch (format[si]) {
            case 's':
                /* append param pi as string */
                l = strlen((char *) v);
                __fmtbuf_write_str(fmtbuf, (char *) v, l);
                break;
            case 'c':
                /* append param pi as char */
                __fmtbuf_write_char(fmtbuf, (char) v);
                break;
            case 'o':
                /* append param as octal */
                __format(fmtbuf, v, w, zp, 8, pp);
                break;
            case 'd':
                /* append param as decimal */
                __format(fmtbuf, v, w, zp, 10, 0);
                break;
            case 'x':
                /* append param as hex */
                __format(fmtbuf, v, w, zp, 16, pp);
                break;
            case '%':
                /* append literal '%' character */
                __fmtbuf_write_char(fmtbuf, '%');
                si++;
                continue;
            }
            pi++;
            si++;
        }
    }

    /* If n is still greater than 0, set the null character. */
    if (fmtbuf->n)
        fmtbuf->buf[0] = 0;
}

int printf(char *str, ...)
{
    char buffer[200];
    fmtbuf_t fmtbuf;

    fmtbuf.buf = buffer;
    fmtbuf.n = INT_MAX;
    fmtbuf.len = 0;
    __format_to_buf(&fmtbuf, str, &str + 4);
    return __syscall(__syscall_write, 1, buffer, fmtbuf.len);
}

int sprintf(char *buffer, char *str, ...)
{
    fmtbuf_t fmtbuf;

    fmtbuf.buf = buffer;
    fmtbuf.n = INT_MAX;
    fmtbuf.len = 0;
    __format_to_buf(&fmtbuf, str, &str + 4);
    return fmtbuf.len;
}

int snprintf(char *buffer, int n, char *str, ...)
{
    fmtbuf_t fmtbuf;

    fmtbuf.buf = buffer;
    fmtbuf.n = n;
    fmtbuf.len = 0;
    __format_to_buf(&fmtbuf, str, &str + 4);
    return fmtbuf.len;
}

int __free_all(void);

void exit(int exit_code)
{
    __free_all();
    __syscall(__syscall_exit, exit_code);
}

void abort(void)
{
    printf("Abnormal program termination\n");
    exit(-1);
}

FILE *fopen(char *filename, char *mode)
{
    if (!strcmp(mode, "wb")) {
#if defined(__arm__)
        return __syscall(__syscall_open, filename, 65, 0x1fd);
#elif defined(__riscv)
        /* FIXME: mode not work currently in RISC-V */
        return __syscall(__syscall_openat, -100, filename, 65, 0x1fd);
#endif
    }
    if (!strcmp(mode, "rb")) {
#if defined(__arm__)
        return __syscall(__syscall_open, filename, 0, 0);
#elif defined(__riscv)
        return __syscall(__syscall_openat, -100, filename, 0, 0);
#endif
    }
    return NULL;
}

int fclose(FILE *stream)
{
    __syscall(__syscall_close, stream);
    return 0;
}

/* Read a byte from file descriptor. So the return value is either in the range
 * of 0 to 127 for the character, or -1 on the end of file.
 */
int fgetc(FILE *stream)
{
    int buf = 0, r = __syscall(__syscall_read, stream, &buf, 1);
    if (r < 1)
        return -1;
    return buf;
}

char *fgets(char *str, int n, FILE *stream)
{
    int i;
    for (i = 0; i < n - 1; i++) {
        int c = fgetc(stream);
        if (c == -1) {
            if (i == 0)
                /* EOF on first char */
                return NULL;
            /* EOF in the middle */
            str[i] = 0;
            return str;
        }
        /* Use explicit cast for clarity */
        str[i] = (char) c;

        if (c == '\n') {
            str[i + 1] = 0;
            return str;
        }
    }
    str[i] = 0;
    return str;
}

int fputc(int c, FILE *stream)
{
    if (__syscall(__syscall_write, stream, &c, 1) < 0)
        return -1;
    return c;
}

int fseek(FILE *stream, int offset, int whence)
{
    int result;
#if defined(__arm__)
    result = __syscall(__syscall_lseek, stream, offset, whence);
#elif defined(__riscv)
    /* No need to offset */
    result = __syscall(__syscall_lseek, stream, 0, offset, NULL, whence);
#else
#error "Unsupported fseek support for current platform"
#endif
    return result == -1;
}

int ftell(FILE *stream)
{
#if defined(__arm__)
    return __syscall(__syscall_lseek, stream, 0, SEEK_CUR);
#elif defined(__riscv)
    int result;
    __syscall(__syscall_lseek, stream, 0, 0, &result, SEEK_CUR);
    return result;
#else
#error "Unsupported ftell support for current platform"
#endif
}

#define CHUNK_SIZE_FREED_MASK 1
#define CHUNK_SIZE_SZ_MASK 0xFFFFFFFE
#define CHUNK_GET_SIZE(size) (size & CHUNK_SIZE_SZ_MASK)
#define IS_CHUNK_GET_FREED(size) (size & CHUNK_SIZE_FREED_MASK)

typedef struct chunk {
    struct chunk *next, *prev;
    int size;
} chunk_t;

void chunk_set_freed(chunk_t *chunk)
{
    chunk->size |= CHUNK_SIZE_FREED_MASK;
}

void chunk_clear_freed(chunk_t *chunk)
{
    chunk->size &= CHUNK_SIZE_SZ_MASK;
}

int __align_up(int size)
{
    return ALIGN_UP(size, PAGESIZE);
}

chunk_t *__alloc_head;
chunk_t *__alloc_tail;
chunk_t *__freelist_head;

void *malloc(int size)
{
    if (size <= 0)
        return NULL;

    int flags = 34; /* MAP_PRIVATE (0x02) | MAP_ANONYMOUS (0x20) */
    int prot = 3;   /* PROT_READ (0x01) | PROT_WRITE (0x02) */

    /* Align size to MIN_ALIGNMENT */
    size = ALIGN_UP(size, MIN_ALIGNMENT);

    if (!__alloc_head) {
        chunk_t *tmp =
            __syscall(__syscall_mmap2, NULL, __align_up(sizeof(chunk_t)), prot,
                      flags, -1, 0);
        __alloc_head = tmp;
        __alloc_tail = tmp;
        __alloc_head->next = NULL;
        __alloc_head->prev = NULL;
        __alloc_head->size = 0;
    }

    if (!__freelist_head) {
        chunk_t *tmp =
            __syscall(__syscall_mmap2, NULL, __align_up(sizeof(chunk_t)), prot,
                      flags, -1, 0);
        __freelist_head = tmp;
        __freelist_head->next = NULL;
        __freelist_head->prev = NULL;
        __freelist_head->size = -1;
    }

    /* Search for the best fit chunk in the free list */
    chunk_t *best_fit_chunk = NULL;
    chunk_t *allocated;
    int best_size = 0;

    if (!__freelist_head->next) {
        allocated = NULL;
    } else {
        for (chunk_t *fh = __freelist_head; fh->next; fh = fh->next) {
            int fh_size = CHUNK_GET_SIZE(fh->size);
            if (fh_size >= size && (!best_fit_chunk || fh_size < best_size)) {
                best_fit_chunk = fh;
                best_size = fh_size;
            }
        }

        if (best_fit_chunk) {
            /* Remove from freelist */
            if (best_fit_chunk->prev) {
                best_fit_chunk->prev->next = best_fit_chunk->next;
            } else {
                __freelist_head = best_fit_chunk->next;
            }
            if (best_fit_chunk->next) {
                best_fit_chunk->next->prev = best_fit_chunk->prev;
            }
        }
        allocated = best_fit_chunk;
    }

    if (!allocated) {
        allocated =
            __syscall(__syscall_mmap2, NULL, __align_up(sizeof(chunk_t) + size),
                      prot, flags, -1, 0);
        allocated->size = __align_up(sizeof(chunk_t) + size);
    }

    /* Add to allocation list */
    __alloc_tail->next = allocated;
    allocated->prev = __alloc_tail;
    __alloc_tail = allocated;
    __alloc_tail->next = NULL;
    __alloc_tail->size = allocated->size;
    chunk_clear_freed(__alloc_tail);

    void *ptr = (void *) (__alloc_tail + 1);
    return ptr;
}

void *calloc(int n, int size)
{
    /* Check for overflow before multiplication */
    if (!n || !size)
        return NULL;
    if (n > INT_MAX / size)
        return NULL; /* Overflow protection */

    int total = n * size;
    char *p = malloc(total);

    if (!p)
        return NULL;

    return memset(p, 0, total);
}

void __rfree(void *ptr, int size)
{
    if (!ptr)
        return;
    __syscall(__syscall_munmap, ptr, size);
}

int __free_all(void)
{
    if (!__freelist_head && !__alloc_head)
        return 0;

    chunk_t *cur = __freelist_head;
    chunk_t *rel;
    int size;

    /* release freelist */
    while (cur && cur->next) {
        rel = cur;
        cur = cur->next;
        rel->next = NULL;
        rel->prev = NULL;
        size = CHUNK_GET_SIZE(rel->size);
        __rfree(rel, size);
    }

    if (__alloc_head && __alloc_head->next) {
        cur = __alloc_head->next;
        /* release chunks which not be free */
        while (cur) {
            rel = cur;
            cur = cur->next;
            rel->next = NULL;
            rel->prev = NULL;
            size = CHUNK_GET_SIZE(rel->size);
            __rfree(rel, size);
        }
    }
    return 0;
}

void free(void *ptr)
{
    if (!ptr)
        return;

    char *__ptr = (char *) ptr;
    chunk_t *cur = (chunk_t *) (__ptr - sizeof(chunk_t));
    if (IS_CHUNK_GET_FREED(cur->size)) {
        printf("free(): double free detected\n");
        abort();
    }

    chunk_t *prev = NULL;
    if (cur->prev) {
        prev = cur->prev;
        prev->next = cur->next;
    } else {
        __alloc_head = cur->next;
    }

    if (cur->next) {
        chunk_t *next = cur->next;
        next->prev = cur->prev;
    } else if (prev) {
        prev->next = NULL;
        __alloc_tail = prev;
    }

    /* Insert head in __freelist_head */
    cur->next = __freelist_head;
    cur->prev = NULL;
    chunk_set_freed(cur);
    if (__freelist_head)
        __freelist_head->prev = cur;
    __freelist_head = cur;
}


================================================
FILE: lib/c.h
================================================
/*
 * shecc - Self-Hosting and Educational C Compiler.
 *
 * shecc is freely redistributable under the BSD 2 clause license. See the
 * file "LICENSE" for information on usage and redistribution of this file.
 */

#pragma once
/* Declarations of C standard library functions */

#define NULL 0

#define bool _Bool
#define true 1
#define false 0

#define INT_MAX 0x7fffffff
#define INT_MIN 0x80000000

#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2

#if defined(__arm__)
#define __SIZEOF_POINTER__ 4
#define __syscall_exit 1
#define __syscall_read 3
#define __syscall_write 4
#define __syscall_close 6
#define __syscall_open 5
#define __syscall_lseek 19
#define __syscall_mmap2 192
#define __syscall_munmap 91

#elif defined(__riscv)
#define __SIZEOF_POINTER__ 4
#define __syscall_exit 93
#define __syscall_read 63
#define __syscall_write 64
#define __syscall_close 57
#define __syscall_open 1024
#define __syscall_openat 56
#define __syscall_lseek 62
#define __syscall_mmap2 222
#define __syscall_munmap 215

#else /* Only Arm32 and RV32 are supported */
#error "Unsupported architecture"
#endif

/* Non-portable: Assume page size is 4KiB */
#define PAGESIZE 4096

/* Minimum alignment for all memory allocations. */
#define MIN_ALIGNMENT 8
#define ALIGN_UP(val, align) (((val) + (align) - 1) & ~((align) - 1))

/* va_list support for variadic functions */
typedef int *va_list;

/* Character predicate functions */
int isdigit(int c);
int isalpha(int c);
int isalnum(int c);
int isxdigit(int c);
int isblank(int c);

/* File I/O */
typedef int FILE;
FILE *fopen(char *filename, char *mode);
int fclose(FILE *stream);
int fgetc(FILE *stream);
char *fgets(char *str, int n, FILE *stream);
int fputc(int c, FILE *stream);
int fseek(FILE *stream, int offset, int whence);
int ftell(FILE *stream);

/* string-related functions */
int strlen(char *str);
int strcmp(char *s1, char *s2);
int strncmp(char *s1, char *s2, int len);
char *strcpy(char *dest, char *src);
char *strncpy(char *dest, char *src, int len);
char *memcpy(char *dest, char *src, int count);
int memcmp(void *s1, void *s2, int n);
void *memset(void *s, int c, int n);

/* formatted output string */
int printf(char *str, ...);
int sprintf(char *buffer, char *str, ...);
int snprintf(char *buffer, int n, char *str, ...);

/* Terminating program */
void exit(int exit_code);
void abort(void);

/* Dynamic memory allocation/deallocation functions */
void *malloc(int size);
void *calloc(int n, int size);
void free(void *ptr);


================================================
FILE: mk/arm.mk
================================================
# Allow the following machines to use native execution
#
# - Beaglebone Black (Cortex-A8)
# - Raspberry Pi 3 (Cortex-A53)
# - Raspberry Pi 4 (Cortex-A72)
# - Raspberry Pi 5 (Cortex-A76)
ALLOW_MACHINES = BeagleBone-Black Raspberry-Pi-3 Raspberry-Pi-4 Raspberry-Pi-5
ARCH_RUNNER = qemu-arm
ARCH_DEFS = \
    "/* target: ARM */\n$\
    \#pragma once\n$\
    \#define ARCH_PREDEFINED \"__arm__\" /* defined by GNU C and RealView */\n$\
    \#define ELF_MACHINE 0x28 /* up to ARMv7/Aarch32 */\n$\
    \#define ELF_FLAGS 0x5000400\n$\
    \#define DYN_LINKER \"/lib/ld-linux-armhf.so.3\"\n$\
    \#define LIBC_SO \"libc.so.6\"\n$\
    \#define PLT_FIXUP_SIZE 20\n$\
    \#define PLT_ENT_SIZE 12\n$\
    \#define R_ARCH_JUMP_SLOT 0x16\n$\
    \#define MAX_ARGS_IN_REG 4\n$\
    "

# If the running machine has the "fastfetch" tool installed, the build
# system will verify whether native execution can be performed.
ifneq ($(shell which fastfetch),)
    # 1. Replace whitespaces with hyphens after retrieving the host
    #    machine name via the "fastfetch" tool.
    #
    # 2. If at least one machine name in the allowlist is found in
    #    the host machine name, it can perform native execution.
    #
    #    Therefore, set USE_QEMU to 0.
    HOST_MACHINE = $(shell fastfetch --logo none --structure Host | sed 's/ /-/g')
    USE_QEMU = $(if $(strip $(foreach MACHINE, $(ALLOW_MACHINES), $(findstring $(MACHINE),$(HOST_MACHINE)))),0,1)

    # Special case: GitHub workflows on Arm64 runners
    #
    # When an Arm-hosted runner executes "fastfetch --logo none --structure Host",
    # it produces the following output:
    # 
    #     Host: Virtual Machine (Hyper-V UEFI Release v4.1)
    #
    # Arm-hosted runners are also capable of performing native execution. However,
    # directly adding "Virtual-Machine" to the allowlist would be ambiguous.
    # Therefore, the build system instead checks the CPU name using the
    # "fastfetch --logo none --structure CPU" command.
    #
    # If the detected CPU is "Neoverse-N2", the build system treats the running
    # machine as an Arm-hosted runner and enable native execution.
    ifeq ($(USE_QEMU),1)
        HOST_CPU = $(shell fastfetch --logo none --structure CPU | sed 's/ /-/g')
        USE_QEMU = $(if $(strip $(findstring Neoverse-N2,$(HOST_CPU))),0,1)
    endif
endif

# Find the sysroot of the ARM GNU toolchain if using dynamic linking.
#
# Since developers may install the toolchain manually instead of
# using a package manager such as apt, we cannot assume that the
# path of ld-linux is always "/usr/arm-linux-gnueabihf".
#
# Therefore, the following process first locates find the correct
# sysroot of the toolchain, and then generate the ELF interpreter
# prefix for later use.
ifeq ($(USE_QEMU),1)
    ifeq ($(DYNLINK),1)
        CROSS_COMPILE = arm-none-linux-gnueabihf-
        ARM_CC = $(CROSS_COMPILE)gcc
        ARM_CC := $(shell which $(ARM_CC))
        ifndef ARM_CC
            CROSS_COMPILE = arm-linux-gnueabihf-
            ARM_CC = $(CROSS_COMPILE)gcc
            ARM_CC := $(shell which $(ARM_CC))
            ifndef ARM_CC
                $(error "Unable to find ARM GNU toolchain.")
            endif
        endif

        LD_LINUX_PATH := $(shell cd $(shell $(ARM_CC) --print-sysroot) 2>/dev/null && pwd)
        ifeq ("$(LD_LINUX_PATH)","/")
            LD_LINUX_PATH := $(shell dirname "$(shell which $(ARM_CC))")/..
            LD_LINUX_PATH := $(shell cd $(LD_LINUX_PATH) 2>/dev/null && pwd)
            LD_LINUX_PATH := $(LD_LINUX_PATH)/$(shell echo $(CROSS_COMPILE) | sed s'/.$$//')/libc
            LD_LINUX_PATH := $(shell cd $(LD_LINUX_PATH) 2>/dev/null && pwd)
            ifndef LD_LINUX_PATH
                LD_LINUX_PATH = /usr/$(shell echo $(CROSS_COMPILE) | sed s'/.$$//')
                LD_LINUX_PATH := $(shell cd $(LD_LINUX_PATH) 2>/dev/null && pwd)
            endif
        endif

        ifndef LD_LINUX_PATH
            $(error "Dynamic linking mode requires ld-linux.so")
        endif

        RUNNER_LD_PREFIX = -L $(LD_LINUX_PATH)
    endif
endif


================================================
FILE: mk/common.mk
================================================
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
    PRINTF = printf
else
    PRINTF = env printf
endif

# Control the build verbosity
ifeq ("$(VERBOSE)","1")
    Q :=
    VECHO = @true
    REDIR =
else
    Q := @
    VECHO = @$(PRINTF)
    REDIR = >/dev/null
endif

# Test suite
PASS_COLOR = \e[32;01m
NO_COLOR = \e[0m

pass = $(PRINTF) "$(PASS_COLOR)$1 Passed$(NO_COLOR)\n"

# Check the prerequisites
PREREQ_LIST := dot jq
TARGET_EXEC ?=
ifeq ($(USE_QEMU),1)
    # Add qemu to the list if the host and target architectures differ
    PREREQ_LIST += $(ARCH_RUNNER)
    ifeq ($(filter $(ARCH_RUNNER),$(notdir $(shell which $(ARCH_RUNNER)))),)
        STAGE1_WARN_MSG := "Warning: failed to build the stage 1 and $\
                               stage 2 compilers due to missing $(ARCH_RUNNER)\n"
        STAGE1_CHECK_CMD := $(VECHO) $(STAGE1_WARN_MSG) && exit 1
    endif

    # Generate the path to the architecture-specific qemu
    TARGET_EXEC = $(shell which $(ARCH_RUNNER))
    ifeq ($(DYNLINK),1)
        TARGET_EXEC += $(RUNNER_LD_PREFIX)
    endif
endif
export TARGET_EXEC

PREREQ_EXEC := $(shell which $(PREREQ_LIST))
PREREQ_MISSING := $(filter-out $(notdir $(PREREQ_EXEC)),$(PREREQ_LIST))

ifdef PREREQ_MISSING
    CONFIG_WARN_MSG := "Warning: missing packages: $(PREREQ_MISSING)\n$\
                            Warning: Please check package installation\n"
    CONFIG_CHECK_CMD := $(VECHO) $(CONFIG_WARN_MSG)
endif


================================================
FILE: mk/riscv.mk
================================================
# Enforce the use qemu of by setting the ALLOW_MACHINES variable to empty
ALLOW_MACHINES =
ARCH_RUNNER = qemu-riscv32
ARCH_DEFS = \
    "/* target: RISCV */\n$\
    \#pragma once\n$\
    \#define ARCH_PREDEFINED \"__riscv\" /* Older versions of the GCC toolchain defined __riscv__ */\n$\
    \#define ELF_MACHINE 0xf3\n$\
    \#define ELF_FLAGS 0\n$\
    \#define DYN_LINKER \"/lib/ld-linux.so.3\"\n$\
    \#define LIBC_SO \"libc.so.6\"\n$\
    \#define PLT_FIXUP_SIZE 20\n$\
    \#define PLT_ENT_SIZE 12\n$\
    \#define R_ARCH_JUMP_SLOT 0x5\n$\
    \#define MAX_ARGS_IN_REG 8\n$\
    "

# TODO: Set this variable for RISC-V architecture
RUNNER_LD_PREFIX=


================================================
FILE: src/arch-lower.c
================================================
/*
 * shecc - Architecture-specific IR lowering stage
 *
 * Introduces a minimal arch-lowering boundary that applies target-specific
 * tweaks to phase-2 IR (ph2_ir) before final code generation. This keeps
 * backends simpler by moving decisions that depend on CFG shape or target
 * quirks out of emit-time where possible.
 */

#include "../config"
#include "defs.h"

/* ARM-specific lowering:
 * - Mark detached conditional branches so codegen can decide between
 *   short/long forms without re-deriving CFG shape.
 */
void arm_lower(void)
{
    for (func_t *func = FUNC_LIST.head; func; func = func->next) {
        /* Skip function declarations without bodies */
        if (!func->bbs)
            continue;

        for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) {
            for (ph2_ir_t *insn = bb->ph2_ir_list.head; insn;
                 insn = insn->next) {
                /* Mark branches that don't fall through to next block */
                if (insn->op == OP_branch) {
                    /* In SSA, we index 'else_bb' first, and then 'then_bb' */
                    insn->is_branch_detached = (insn->else_bb != bb->rpo_next);
                }
            }
        }
    }
}

/* RISC-V-specific lowering:
 * - Mark detached conditional branches
 * - Future: prepare for RISC-V specific patterns
 */
void riscv_lower(void)
{
    for (func_t *func = FUNC_LIST.head; func; func = func->next) {
        /* Skip function declarations without bodies */
        if (!func->bbs)
            continue;

        for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) {
            for (ph2_ir_t *insn = bb->ph2_ir_list.head; insn;
                 insn = insn->next) {
                /* Mark branches that don't fall through to next block */
                if (insn->op == OP_branch)
                    insn->is_branch_detached = (insn->else_bb != bb->rpo_next);
            }
        }
    }
}

/* Entry point: dispatch to the active architecture. */
void arch_lower(void)
{
#if ELF_MACHINE == 0x28 /* ARM */
    arm_lower();
#elif ELF_MACHINE == 0xf3 /* RISC-V */
    riscv_lower();
#else
    /* Unknown architecture: keep behavior as-is. */
    (void) 0;
#endif
}


================================================
FILE: src/arm-codegen.c
================================================
/*
 * shecc - Self-Hosting and Educational C Compiler.
 *
 * shecc is freely redistributable under the BSD 2 clause license. See the
 * file "LICENSE" for information on usage and redistribution of this file.
 */

/* Translate IR to target machine code */

#include "arm.c"
#include "defs.h"
#include "globals.c"

void update_elf_offset(ph2_ir_t *ph2_ir)
{
    func_t *func;
    switch (ph2_ir->op) {
    case OP_load_constant:
        /* ARMv7 uses 12 bits to encode immediate value, but the higher 4 bits
         * are for rotation. See A5.2.4 "Modified immediate constants in ARM
         * instructions" in ARMv7-A manual.
         */
        if (ph2_ir->src0 < 0)
            elf_offset += 12;
        else if (ph2_ir->src0 > 255)
            elf_offset += 8;
        else
            elf_offset += 4;
        return;
    case OP_address_of:
    case OP_global_address_of:
        /* ARMv7 uses 12 bits to encode immediate value, but the higher 4 bits
         * are for rotation. See A5.2.4 "Modified immediate constants in ARM
         * instructions" in ARMv7-A manual.
         */
        if (ph2_ir->src0 > 255)
            elf_offset += 12;
        else if (ph2_ir->src0 >= 0)
            elf_offset += 4;
        else
            abort();
        return;
    case OP_assign:
        if (ph2_ir->dest != ph2_ir->src0)
            elf_offset += 4;
        return;
    case OP_load:
    case OP_global_load:
        /* ARMv7 straight uses 12 bits to encode the offset of load instruction
         * (no rotation).
         */
        if (ph2_ir->src0 > 4095)
            elf_offset += 16;
        else if (ph2_ir->src0 >= 0)
            elf_offset += 4;
        else
            abort();
        return;
    case OP_store:
    case OP_global_store:
        /* ARMv7 straight uses 12 bits to encode the offset of store instruction
         * (no rotation).
         */
        if (ph2_ir->src1 > 4095)
            elf_offset += 16;
        else if (ph2_ir->src1 >= 0)
            elf_offset += 4;
        else
            abort();
        return;
    case OP_read:
    case OP_write:
    case OP_jump:
    case OP_load_func:
    case OP_indirect:
    case OP_add:
    case OP_sub:
    case OP_mul:
    case OP_lshift:
    case OP_rshift:
    case OP_bit_and:
    case OP_bit_or:
    case OP_bit_xor:
    case OP_negate:
    case OP_bit_not:
        elf_offset += 4;
        return;
    case OP_call:
        func = find_func(ph2_ir->func_name);
        if (func->bbs)
            elf_offset += 4;
        else if (dynlink) {
            /* When calling external functions in dynamic linking mode,
             * the following instructions are required:
             * - movw + movt: set r8 to 'elf_data_start'
             * - ldr: load a word from the address 'elf_data_start' into r12.
             *        (restore the global stack pointer.)
             *
             * Therefore, the total offset is 16 bytes (4 instructions).
             */
            elf_offset += 16;
        } else {
            printf("The '%s' function is not implemented\n", ph2_ir->func_name);
            abort();
        }
        return;
    case OP_div:
    case OP_mod:
        if (hard_mul_div) {
            if (ph2_ir->op == OP_div)
                elf_offset += 4;
            else
                elf_offset += 12;
            return;
        }
        /* div/mod emulation's offset */
        elf_offset += 116;
        return;
    case OP_load_data_address:
    case OP_load_rodata_address:
        elf_offset += 8;
        return;
    case OP_address_of_func:
    case OP_eq:
    case OP_neq:
    case OP_gt:
    case OP_lt:
    case OP_geq:
    case OP_leq:
    case OP_log_not:
        elf_offset += 12;
        return;
    case OP_branch:
        if (ph2_ir->is_branch_detached)
            elf_offset += 12;
        else
            elf_offset += 8;
        return;
    case OP_return:
        elf_offset += 24;
        return;
    case OP_trunc:
        if (ph2_ir->src1 == 2)
            elf_offset += 8;
        else
            elf_offset += 4;
        return;
    case OP_sign_ext:
        elf_offset += 4;
        return;
    case OP_cast:
        elf_offset += 4;
        return;
    default:
        fatal("Unknown opcode");
    }
}

void cfg_flatten(void)
{
    func_t *func;

    if (dynlink)
        elf_offset =
            100; /* offset of __libc_start_main + main_wrapper in codegen */
    else {
        func = find_func("__syscall");
        func->bbs->elf_offset = 32; /* offset of start + branch in codegen */
        elf_offset = 92; /* offset of start + branch + syscall in codegen */
    }

    GLOBAL_FUNC->bbs->elf_offset = elf_offset;

    for (ph2_ir_t *ph2_ir = GLOBAL_FUNC->bbs->ph2_ir_list.head; ph2_ir;
         ph2_ir = ph2_ir->next) {
        update_elf_offset(ph2_ir);
    }

    /* prepare 'argc' and 'argv', then proceed to 'main' function */
    if (dynlink)
        elf_offset += 28;
    else
        elf_offset += 32; /* 6 insns for main call + 2 for exit */

    for (func = FUNC_LIST.head; func; func = func->next) {
        /* Skip function declarations without bodies */
        if (!func->bbs)
            continue;

        /* reserve stack */
        ph2_ir_t *flatten_ir = add_ph2_ir(OP_define);
        flatten_ir->src0 = func->stack_size;
        strncpy(flatten_ir->func_name, func->return_def.var_name, MAX_VAR_LEN);

        /* The actual offset of the top of the local stack is the sum of:
         * - 36 bytes (pushing registers r4-r11 and lr onto the stack)
         * - 4 bytes
         *   (to ensure 8-byte alignment after pushing the 9 registers)
         * - ALIGN_UP(func->stack_size, 8)
         *
         * Note that func->stack_size does not include the 36 + 4 bytes,
         * so an additional 40 bytes should be added to
         * ALIGN_UP(func->stack_size, 8).
         */
        int stack_top_ofs = ALIGN_UP(func->stack_size, MIN_ALIGNMENT) + 40;

        for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) {
            bb->elf_offset = elf_offset;

            if (bb == func->bbs) {
                /* retrieve the global stack pointer and save ra, sp */
                elf_offset += 28;
            }

            for (ph2_ir_t *insn = bb->ph2_ir_list.head; insn;
                 insn = insn->next) {
                /* For the instructions whose ofs_based_on_stack_top is set,
                 * recalculate the operand's offset by adding stack_top_ofs.
                 */
                if (insn->ofs_based_on_stack_top) {
                    switch (insn->op) {
                    case OP_load:
                    case OP_address_of:
                        insn->src0 = insn->src0 + stack_top_ofs;
                        break;
                    case OP_store:
                        insn->src1 = insn->src1 + stack_top_ofs;
                        break;
                    default:
                        /* Ignore opcodes with the ofs_based_on_stack_top
                         * flag set since only the three opcodes above needs
                         * to access a variable's address.
                         */
                        break;
                    }
                }
                flatten_ir = add_existed_ph2_ir(insn);

                if (insn->op == OP_return) {
                    /* restore sp */
                    flatten_ir->src1 = bb->belong_to->stack_size;
                }

                /* Branch detachment is determined in the arch-lowering stage */

                update_elf_offset(flatten_ir);
            }
        }
    }
}

void emit(int code)
{
    elf_write_int(elf_code, code);
}

void emit_ph2_ir(ph2_ir_t *ph2_ir)
{
    func_t *func;
    const int rd = ph2_ir->dest;
    const int rn = ph2_ir->src0;
    int rm = ph2_ir->src1; /* Not const because OP_trunc modifies it */
    int ofs;
    bool is_external_call = false;

    /* Prepare this variable to reuse code for:
     * 1. division and modulo operations
     * 2. load and store operations
     * 3. address-of operations
     */
    arm_reg interm;

    switch (ph2_ir->op) {
    case OP_define:
        /* We should handle the function entry point carefully due to the
         * following constraints:
         * - according to AAPCS, the callee must preserve r4-r11 for the caller,
         *   and the stack must always be 8-byte aligned.
         * - lr must be pushed because it may be modified when the function
         *   calls another function.
         * - since external functions may call internal functions, r12 may not
         *   hold the global stack pointer.
         *
         * Therefore, we perform the following operations:
         * 1. use a __stmdb instruction to push r4-r11 and lr onto the stack
         *    first.
         * 2. retrieve the global stack pointer from the 4-byte global object
         *    located at 'elf_data_start' to ensure correct access to the global
         *    stack.
         * 3. set ofs to align(ph2_ir->src0, 8) + 4, and prepare a local
         *    stack for the callee by subtracting ofs from sp.
         */
        emit(__stmdb(__AL, 1, __sp, 0x4FF0));
        emit(__movw(__AL, __r8, elf_data_start));
        emit(__movt(__AL, __r8, elf_data_start));
        emit(__lw(__AL, __r12, __r8, 0));
        ofs = ALIGN_UP(ph2_ir->src0, MIN_ALIGNMENT) + 4;
        emit(__movw(__AL, __r8, ofs));
        emit(__movt(__AL, __r8, ofs));
        emit(__sub_r(__AL, __sp, __sp, __r8));
        return;
    case OP_load_constant:
        if (ph2_ir->src0 < 0) {
            emit(__movw(__AL, __r8, -ph2_ir->src0));
            emit(__movt(__AL, __r8, -ph2_ir->src0));
            emit(__rsb_i(__AL, rd, 0, __r8));
        } else if (ph2_ir->src0 > 255) {
            emit(__movw(__AL, rd, ph2_ir->src0));
            emit(__movt(__AL, rd, ph2_ir->src0));
        } else
            emit(__mov_i(__AL, rd, ph2_ir->src0));
        return;
    case OP_address_of:
    case OP_global_address_of:
        interm = ph2_ir->op == OP_address_of ? __sp : __r12;
        if (ph2_ir->src0 > 255) {
            emit(__movw(__AL, __r8, ph2_ir->src0));
            emit(__movt(__AL, __r8, ph2_ir->src0));
            emit(__add_r(__AL, rd, interm, __r8));
        } else
            emit(__add_i(__AL, rd, interm, ph2_ir->src0));
        return;
    case OP_assign:
        emit(__mov_r(__AL, rd, rn));
        return;
    case OP_load:
    case OP_global_load:
        interm = ph2_ir->op == OP_load ? __sp : __r12;
        if (ph2_ir->src0 > 4095) {
            emit(__movw(__AL, __r8, ph2_ir->src0));
            emit(__movt(__AL, __r8, ph2_ir->src0));
            emit(__add_r(__AL, __r8, interm, __r8));
            emit(__lw(__AL, rd, __r8, 0));
        } else
            emit(__lw(__AL, rd, interm, ph2_ir->src0));
        return;
    case OP_store:
    case OP_global_store:
        interm = ph2_ir->op == OP_store ? __sp : __r12;
        if (ph2_ir->src1 > 4095) {
            emit(__movw(__AL, __r8, ph2_ir->src1));
            emit(__movt(__AL, __r8, ph2_ir->src1));
            emit(__add_r(__AL, __r8, interm, __r8));
            emit(__sw(__AL, rn, __r8, 0));
        } else
            emit(__sw(__AL, rn, interm, ph2_ir->src1));
        return;
    case OP_read:
        if (ph2_ir->src1 == 1)
            emit(__lb(__AL, rd, rn, 0));
        else if (ph2_ir->src1 == 2)
            emit(__lh(__AL, rd, rn, 0));
        else if (ph2_ir->src1 == 4)
            emit(__lw(__AL, rd, rn, 0));
        else
            abort();
        return;
    case OP_write:
        if (ph2_ir->dest == 1)
            emit(__sb(__AL, rm, rn, 0));
        else if (ph2_ir->dest == 2)
            emit(__sh(__AL, rm, rn, 0));
        else if (ph2_ir->dest == 4)
            emit(__sw(__AL, rm, rn, 0));
        else
            abort();
        return;
    case OP_branch:
        emit(__teq(rn));
        if (ph2_ir->is_branch_detached) {
            emit(__b(__NE, 8));
            emit(__b(__AL, ph2_ir->else_bb->elf_offset - elf_code->size));
        } else
            emit(__b(__NE, ph2_ir->then_bb->elf_offset - elf_code->size));
        return;
    case OP_jump:
        emit(__b(__AL, ph2_ir->next_bb->elf_offset - elf_code->size));
        return;
    case OP_call:
        func = find_func(ph2_ir->func_name);
        if (func->bbs)
            ofs = func->bbs->elf_offset - elf_code->size;
        else if (dynlink) {
            ofs = (dynamic_sections.elf_plt_start + func->plt_offset) -
                  (elf_code_start + elf_code->size);
            is_external_call = true;
        } else {
            printf("The '%s' function is not implemented\n", ph2_ir->func_name);
            abort();
        }

        /* When calling external functions in dynamic linking mode,
         * the following instructions are required:
         * - movw + movt: set r8 to 'elf_data_start'
         * - ldr: load a word from the address 'elf_data_start' to r12.
         *        (restore the global stack pointer.)
         *
         * Since shecc uses r12 to store a global stack pointer and external
         * functions can freely modify r12, causing internal functions to
         * access global variables incorrectly, additional instructions are
         * needed to restore r12 from the global object after the external
         * function returns.
         *
         * Otherwise, only a 'bl' instruction is generated to call internal
         * functions because shecc guarantees they do not modify r12.
         */
        emit(__bl(__AL, ofs));
        if (is_external_call) {
            emit(__movw(__AL, __r8, elf_data_start));
            emit(__movt(__AL, __r8, elf_data_start));
            emit(__lw(__AL, __r12, __r8, 0));
        }
        return;
    case OP_load_data_address:
        emit(__movw(__AL, rd, ph2_ir->src0 + elf_data_start));
        emit(__movt(__AL, rd, ph2_ir->src0 + elf_data_start));
        return;
    case OP_load_rodata_address:
        emit(__movw(__AL, rd, ph2_ir->src0 + elf_rodata_start));
        emit(__movt(__AL, rd, ph2_ir->src0 + elf_rodata_start));
        return;
    case OP_address_of_func:
        func = find_func(ph2_ir->func_name);
        if (func->bbs)
            ofs = elf_code_start + func->bbs->elf_offset;
        else if (dynlink)
            ofs = dynamic_sections.elf_plt_start + func->plt_offset;
        else {
            printf("The '%s' function is not implemented\n", ph2_ir->func_name);
            abort();
        }
        emit(__movw(__AL, __r8, ofs));
        emit(__movt(__AL, __r8, ofs));
        emit(__sw(__AL, __r8, rn, 0));
        return;
    case OP_load_func:
        emit(__mov_r(__AL, __r8, rn));
        return;
    case OP_indirect:
        emit(__blx(__AL, __r8));
        return;
    case OP_return:
        if (ph2_ir->src0 == -1)
            emit(__mov_r(__AL, __r0, __r0));
        else
            emit(__mov_r(__AL, __r0, rn));

        /* When calling a function, the following operations are performed:
         * 1. push r4-r11 and lr onto the stack.
         * 2. retrieve the global stack pointer from the 4-byte global object.
         * 3. decrement the stack by ALIGN_UP(stack_size, 8) + 4.
         *
         * Except for step 2, the reversed operations should be performed to
         * upon returning to restore the stack and the contents of r4-r11 and
         * lr.
         */
        ofs = ALIGN_UP(ph2_ir->src1, MIN_ALIGNMENT) + 4;
        emit(__movw(__AL, __r8, ofs));
        emit(__movt(__AL, __r8, ofs));
        emit(__add_r(__AL, __sp, __sp, __r8));
        emit(__ldm(__AL, 1, __sp, 0x4FF0));
        emit(__bx(__AL, __lr));
        return;
    case OP_add:
        emit(__add_r(__AL, rd, rn, rm));
        return;
    case OP_sub:
        emit(__sub_r(__AL, rd, rn, rm));
        return;
    case OP_mul:
        emit(__mul(__AL, rd, rn, rm));
        return;
    case OP_div:
    case OP_mod:
        if (hard_mul_div) {
            if (ph2_ir->op == OP_div)
                emit(__div(__AL, rd, rm, rn));
            else {
                emit(__div(__AL, __r8, rm, rn));
                emit(__mul(__AL, __r8, rm, __r8));
                emit(__sub_r(__AL, rd, rn, __r8));
            }
            return;
        }
        interm = __r8;
        /* div/mod emulation */
        /* Preserve the values of the dividend and divisor */
        emit(__stmdb(__AL, 1, __sp, (1 << rn) | (1 << rm)));
        /* Obtain absolute values of the dividend and divisor */
        emit(__srl_amt(__AL, 0, arith_rs, __r8, rn, 31));
        emit(__add_r(__AL, rn, rn, __r8));
        emit(__eor_r(__AL, rn, rn, __r8));
        emit(__srl_amt(__AL, 0, arith_rs, __r9, rm, 31));
        emit(__add_r(__AL, rm, rm, __r9));
        emit(__eor_r(__AL, rm, rm, __r9));
        if (ph2_ir->op == OP_div)
            emit(__eor_r(__AL, __r10, __r8, __r9));
        else {
            /* If the requested operation is modulo, the result will be stored
             * in __r9. The sign of the divisor is irrelevant for determining
             * the result's sign.
             */
            interm = __r9;
            emit(__mov_r(__AL, __r10, __r8));
        }
        /* Unsigned integer division */
        emit(__zero(__r8));
        emit(__mov_i(__AL, __r9, 1));
        emit(__cmp_i(__AL, rm, 0));
        emit(__b(__EQ, 52));
        emit(__cmp_i(__AL, rn, 0));
        emit(__b(__EQ, 44));
        emit(__cmp_r(__AL, rm, rn));
        emit(__sll_amt(__CC, 0, logic_ls, rm, rm, 1));
        emit(__sll_amt(__CC, 0, logic_ls, __r9, __r9, 1));
        emit(__b(__CC, -12));
        emit(__cmp_r(__AL, rn, rm));
        emit(__sub_r(__CS, rn, rn, rm));
        emit(__add_r(__CS, __r8, __r8, __r9));
        emit(__srl_amt(__AL, 1, logic_rs, __r9, __r9, 1));
        emit(__srl_amt(__CC, 0, logic_rs, rm, rm, 1));
        emit(__b(__CC, -20));
        /* After completing the emulation, the quotient and remainder will be
         * stored in __r8 and __r9, respectively.
         *
         * The original values of the dividend and divisor will be restored in
         * rn and rm.
         *
         * Finally, the result (quotient or remainder) will be stored in rd.
         */
        emit(__mov_r(__AL, __r9, rn));
        emit(__ldm(__AL, 1, __sp, (1 << rn) | (1 << rm)));
        emit(__mov_r(__AL, rd, interm));
        /* Handle the correct sign for the quotient or remainder */
        emit(__cmp_i(__AL, __r10, 0));
        emit(__rsb_i(__NE, rd, 0, rd));
        return;
    case OP_lshift:
        emit(__sll(__AL, rd, rn, rm));
        return;
    case OP_rshift:
        emit(__sra(__AL, rd, rn, rm));
        return;
    case OP_eq:
    case OP_neq:
    case OP_gt:
    case OP_lt:
    case OP_geq:
    case OP_leq:
        emit(__cmp_r(__AL, rn, rm));
        emit(__zero(rd));
        emit(__mov_i(arm_get_cond(ph2_ir->op), rd, 1));
        return;
    case OP_negate:
        emit(__rsb_i(__AL, rd, 0, rn));
        return;
    case OP_bit_not:
        emit(__mvn_r(__AL, rd, rn));
        return;
    case OP_bit_and:
        emit(__and_r(__AL, rd, rn, rm));
        return;
    case OP_bit_or:
        emit(__or_r(__AL, rd, rn, rm));
        return;
    case OP_bit_xor:
        emit(__eor_r(__AL, rd, rn, rm));
        return;
    case OP_log_not:
        emit(__cmp_i(__AL, rn, 0));
        emit(__mov_i(__NE, rd, 0));
        emit(__mov_i(__EQ, rd, 1));
        return;
    case OP_trunc:
        if (rm == 1) {
            emit(__and_i(__AL, rd, rn, 0xFF));
        } else if (rm == 2) {
            emit(__sll_amt(__AL, 0, logic_ls, rd, rn, 16));
            emit(__sll_amt(__AL, 0, logic_rs, rd, rd, 16));
        } else if (rm == 4) {
            emit(__mov_r(__AL, rd, rn));
        } else {
            fatal("Unsupported truncation operation with invalid target size");
        }
        return;
    case OP_sign_ext: {
        /* Decode source size from upper 16 bits */
        int source_size = (rm >> 16) & 0xFFFF;
        if (source_size == 2) {
            emit(__sxth(__AL, rd, rn, 0));
        } else {
            /* For other cases, use byte extension (original behavior) */
            emit(__sxtb(__AL, rd, rn, 0));
        }
    }
        return;
    case OP_cast:
        /* Generic cast operation - for now, just move the value */
        emit(__mov_r(__AL, rd, rn));
        return;
    default:
        fatal("Unknown opcode");
    }
}

void plt_generate(void);
void code_generate(void)
{
    int ofs;

    if (dynlink) {
        plt_generate();
        /* Call __libc_start_main() */
        emit(__mov_i(__AL, __r11, 0));
        emit(__mov_i(__AL, __lr, 0));
        emit(__pop_word(__AL, __r1));
        emit(__mov_r(__AL, __r2, __sp));
        emit(__push_reg(__AL, __r2));
        emit(__push_reg(__AL, __r0));
        emit(__mov_i(__AL, __r12, 0));
        emit(__push_reg(__AL, __r12));

        int main_wrapper_offset = elf_code->size + 28;
        emit(__movw(__AL, __r0, elf_code_start + main_wrapper_offset));
        emit(__movt(__AL, __r0, elf_code_start + main_wrapper_offset));
        emit(__mov_i(__AL, __r3, 0));
        emit(__bl(__AL, (dynamic_sections.elf_plt_start + PLT_FIXUP_SIZE) -
                            (elf_code_start + elf_code->size)));
        /* Call '_exit' (syscall) to terminate the program if __libc_start_main
         * returns. */
        emit(__mov_i(__AL, __r0, 127));
        emit(__mov_i(__AL, __r7, 1));
        emit(__svc());

        /* If the compiled program is dynamic linking, the starting
         * point of 'main_wrapper' is located here.
         *
         * Push the contents of r4-r11 and lr onto stack.
         * Preserve 'argc' and 'argv' for the 'main' function.
         */
        emit(__stmdb(__AL, 1, __sp, 0x4FF0));
        emit(__mov_r(__AL, __r9, __r0));
        emit(__mov_r(__AL, __r10, __r1));
    }
    /* For both static and dynamic linking, we need to set up the stack
     * and call the main function.
     *
     * To ensure that the stack remains 8-byte aligned after adjustment,
     * 'ofs' is to align(GLOBAL_FUNC->stack_size, 8) to allocate space
     * for the global stack.
     *
     * In dynamic linking mode, since the preceding __stmdb instruction
     * pushes 9 registers onto stack, 'ofs' must be increased by 4 to
     * prevent the stack from becoming misaligned.
     */
    ofs = ALIGN_UP(GLOBAL_FUNC->stack_size, MIN_ALIGNMENT);
    if (dynlink)
        ofs += 4;
    emit(__movw(__AL, __r8, ofs));
    emit(__movt(__AL, __r8, ofs));
    emit(__sub_r(__AL, __sp, __sp, __r8));
    emit(__mov_r(__AL, __r12, __sp));
    /* The first object in the .data section is used to store the global
     * stack pointer. Therefore, store r12 at the address 'elf_data_start'
     * after the global stack has been prepared.
     */
    emit(__movw(__AL, __r8, elf_data_start));
    emit(__movt(__AL, __r8, elf_data_start));
    emit(__sw(__AL, __r12, __r8, 0));

    if (!dynlink) {
        /* Jump directly to the main preparation and then execute the
         * main function.
         *
         * In static linking mode, when the main function completes its
         * execution, it will invoke the '_exit' syscall to terminate
         * the program.
         *
         * That is, the execution flow is:
         *
         *               +------------------+
         *               | movw r8 <ofs>    |
         * 'start'       | ...              |
         *               | b <global init>  | (1) jump to global init --+
         *               +------------------+                           |
         *               | push {r4 ... r7} |                           |
         * '__syscall'   | ...              |                           |
         *               | bx lr            |                           |
         *               +------------------+                           |
         *               | ...              | (2) global init    <------+
         *               | (global init)    |
         *               | ...              |
         * global init   | movw r8 <ofs>    |
         *     +         | movt r8 <ofs>    |
         * call main()   | ...              |
         *               | bl <main func>   | (3) call main()
         *               | mov r7 #1        |
         *               | svc 0x00000000   | (4) call '_exit' after main()
         *               +------------------+     returns
         */
        emit(__b(__AL, GLOBAL_FUNC->bbs->elf_offset - elf_code->size));

        /* __syscall - only for static linking
         *
         * If the number of arguments is greater than 4, the additional
         * arguments need to be retrieved from the stack. However, this
         * process must modify the contents of registers r4-r7.
         *
         * Therefore, __syscall needs to preserve the contents of these
         * registers before invoking a syscall, and restore them after
         * the syscall has completed.
         */
        emit(__stmdb(__AL, 1, __sp, 0x00F0));
        emit(__lw(__AL, __r4, __sp, 16));
        emit(__lw(__AL, __r5, __sp, 20));
        emit(__lw(__AL, __r6, __sp, 24));
        emit(__lw(__AL, __r7, __sp, 28));
        emit(__mov_r(__AL, __r7, __r0));
        emit(__mov_r(__AL, __r0, __r1));
        emit(__mov_r(__AL, __r1, __r2));
        emit(__mov_r(__AL, __r2, __r3));
        emit(__mov_r(__AL, __r3, __r4));
        emit(__mov_r(__AL, __r4, __r5));
        emit(__mov_r(__AL, __r5, __r6));
        emit(__svc());
        emit(__ldm(__AL, 1, __sp, 0x00F0));
        emit(__bx(__AL, __lr));
    }

    ph2_ir_t *ph2_ir;
    for (ph2_ir = GLOBAL_FUNC->bbs->ph2_ir_list.head; ph2_ir;
         ph2_ir = ph2_ir->next)
        emit_ph2_ir(ph2_ir);

    /* prepare 'argc' and 'argv', then proceed to 'main' function */
    if (MAIN_BB) {
        if (dynlink) {
            emit(__mov_r(__AL, __r0, __r9));
            emit(__mov_r(__AL, __r1, __r10));
            /* Call the main function.
             *
             * After the main function returns, the following
             * instructions restore the registers r4-r11 and
             * return control to __libc_start_main via the
             * preserved lr.
             */
            emit(__bl(__AL, MAIN_BB->elf_offset - elf_code->size));
            emit(__movw(__AL, __r8, ofs));
            emit(__movt(__AL, __r8, ofs));
            emit(__add_r(__AL, __sp, __sp, __r8));
            emit(__ldm(__AL, 1, __sp, 0x8FF0));
        } else {
            emit(__movw(__AL, __r8, ofs));
            emit(__movt(__AL, __r8, ofs));
            emit(__add_r(__AL, __r8, __r12, __r8));
            emit(__lw(__AL, __r0, __r8, 0));
            emit(__add_i(__AL, __r1, __r8, 4));

            /* Call main function, and call '_exit' syscall to
             * terminate the program. */
            emit(__bl(__AL, MAIN_BB->elf_offset - elf_code->size));

            /* exit with main's return value - r0 already has the
             * return value */
            emit(__mov_i(__AL, __r7, 1));
            emit(__svc());
        }
    }

    for (int i = 0; i < ph2_ir_idx; i++) {
        ph2_ir = PH2_IR_FLATTEN[i];
        emit_ph2_ir(ph2_ir);
    }
}

void plt_generate(void)
{
    /* - PLT code generation explanation -
     *
     * As described in ARM's Platform Standard, PLT code should make register
     * ip address the corresponding GOT entry on SVr4-like (Linux-like)
     * platforms.
     *
     * Therefore, PLT[1] ~ PLT[N] use r12 (ip) to load the address of the
     * GOT entry and jump to the function entry via the GOT value.
     *
     * PLT[0] is used to call the resolver, which requires:
     * - [sp] contains the return address from the original function call.
     * - ip contains the address of the GOT entry.
     * - lr points to the address of GOT[2].
     *
     * The second requirement is alreadly handled by PLT[1] - PLT[N], so
     * PLT[0] must take care of the other two. The first one can be achieved
     * by a 'push' instruction; for the third, we use r10 to store the address
     * of GOT[2] and then move the value to lr.
     *
     * - Reason for using r10 in PLT[0] -
     *
     * The register allocation assumes 8 available registers, so the ARM code
     * generator primarily uses r0-r7 for code generation. These registers
     * cannot be modified arbitrarily; otherwise, the program may fail if any
     * of them are changed by PLT[0].
     *
     * However, r8-r11 can be freely used as temporary registers during code
     * generation, so PLT[0] arbitrarily chooses r10 to perform the required
     * operation.
     */
    int addr_of_got = dynamic_sections.elf_got_start + PTR_SIZE * 2;
    int end = dynamic_sections.plt_size - PLT_FIXUP_SIZE;
    elf_write_int(dynamic_sections.elf_plt, __push_reg(__AL, __lr));
    elf_write_int(dynamic_sections.elf_plt, __movw(__AL, __r10, addr_of_got));
    elf_write_int(dynamic_sections.elf_plt, __movt(__AL, __r10, addr_of_got));
    elf_write_int(dynamic_sections.elf_plt, __mov_r(__AL, __lr, __r10));
    elf_write_int(dynamic_sections.elf_plt, __lw(__AL, __pc, __lr, 0));
    for (int i = 0; i * PLT_ENT_SIZE < end; i++) {
        addr_of_got = dynamic_sections.elf_got_start + PTR_SIZE * (i + 3);
        elf_write_int(dynamic_sections.elf_plt,
                      __movw(__AL, __r12, addr_of_got));
        elf_write_int(dynamic_sections.elf_plt,
                      __movt(__AL, __r12, addr_of_got));
        elf_write_int(dynamic_sections.elf_plt, __lw(__AL, __pc, __r12, 0));
    }
}


================================================
FILE: src/arm.c
================================================
/*
 * shecc - Self-Hosting and Educational C Compiler.
 *
 * shecc is freely redistributable under the BSD 2 clause license. See the
 * file "LICENSE" for information on usage and redistribution of this file.
 */

/* ARMv7-A instruction encoding */

/* Identifier naming conventions
 *   - prefix arm_ : Arm instruction encoding.
 *   - prefix __ : mnemonic symbols for Arm instruction, condition code,
 *                 registers, etc.
 *
 * An example of usage in src/codegen.c: (unconditional jump)
 *
 *         +---------------- write specified instruction into ELF
 *         |
 *      emit(__b(__AL, ofs));
 *             |    |   |
 *             |    |   +--- to PC-relative expression
 *             |    +------- always
 *             +------------ branch
 *
 * Machine-level "b" instructions have restricted ranges from the address of
 * the current instruction.
 */

#include "defs.h"

/* opcode */
typedef enum {
    arm_and = 0,
    arm_eor = 1,
    arm_sub = 2,
    arm_rsb = 3,
    arm_add = 4,
    arm_ldm = 9,
    arm_teq = 9,
    arm_cmp = 10,
    arm_orr = 12,
    arm_mov = 13,
    arm_mvn = 15,
    arm_stmdb = 16
} arm_op_t;

/* Condition code
 * Reference:
 * https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/condition-codes-1-condition-flags-and-codes
 */
typedef enum {
    __EQ = 0,  /* Equal */
    __NE = 1,  /* Not equal */
    __CS = 2,  /* Unsigned higher or same */
    __CC = 3,  /* Unsigned lower */
    __LS = 9,  /* Unsigned lower or same */
    __GE = 10, /* Signed greater than or equal */
    __LT = 11, /* Signed less than */
    __GT = 12, /* Signed greater than */
    __LE = 13, /* Signed less than or equal */
    __AL = 14  /* Always executed */
} arm_cond_t;

/* Registers */
typedef enum {
    __r0 = 0,
    __r1 = 1,
    __r2 = 2,
    __r3 = 3,
    __r4 = 4,
    __r5 = 5,
    __r6 = 6,
    __r7 = 7,
    __r8 = 8,
    __r9 = 9,
    __r10 = 10,
    __r11 = 11,
    __r12 = 12,
    __sp = 13, /* stack pointer, r13 */
    __lr = 14, /* link register, r14 */
    __pc = 15  /* program counter, r15 */
} arm_reg;

typedef enum {
    logic_ls = 0, /* Logical left shift */
    logic_rs = 1, /* Logical right shift */
    arith_rs = 2, /* Arithmetic right shift */
    rotat_rs = 3  /* Rotate right shift */
} shift_type;

arm_cond_t arm_get_cond(opcode_t op)
{
    switch (op) {
    case OP_eq:
        return __EQ;
    case OP_neq:
        return __NE;
    case OP_lt:
        return __LT;
    case OP_geq:
        return __GE;
    case OP_gt:
        return __GT;
    case OP_leq:
        return __LE;
    default:
        fatal("Unsupported condition IR opcode");
    }
    return __AL;
}

int arm_extract_bits(int imm, int i_start, int i_end, int d_start, int d_end)
{
    if (((d_end - d_start) != (i_end - i_start)) || (i_start > i_end) ||
        (d_start > d_end))
        fatal("Invalid bit copy");

    int v = imm >> i_start;
    v &= ((2 << (i_end - i_start)) - 1);
    v <<= d_start;
    return v;
}

int arm_encode(arm_cond_t cond, int opcode, int rn, int rd, int op2)
{
    return (cond << 28) + (opcode << 20) + (rn << 16) + (rd << 12) + op2;
}

int __svc(void)
{
    return arm_encode(__AL, 240, 0, 0, 0);
}

int __mov(arm_cond_t cond, int io, int opcode, int s, int rn, int rd, int op2)
{
    int shift = 0;
    if (op2 > 255) {
        shift = 16; /* full rotation */
        while ((op2 & 3) == 0) {
            /* we can shift by two bits */
            op2 >>= 2;
            shift -= 1;
        }
        if (op2 > 255)
            /* value spans more than 8 bits */
            fatal("Unable to represent value");
    }
    return arm_encode(cond, s + (opcode << 1) + (io << 5), rn, rd,
                      (shift << 8) + (op2 & 255));
}

int __and_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg rm)
{
    return __mov(cond, 0, arm_and, 0, rs, rd, rm);
}

int __or_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg rm)
{
    return __mov(cond, 0, arm_orr, 0, rs, rd, rm);
}

int __eor_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg rm)
{
    return __mov(cond, 0, arm_eor, 0, rs, rd, rm);
}

int __mvn_r(arm_cond_t cond, arm_reg rd, arm_reg rm)
{
    return __mov(cond, 0, arm_mvn, 0, 0, rd, rm);
}

int __movw(arm_cond_t cond, arm_reg rd, int imm)
{
    return arm_encode(cond, 48, 0, rd, 0) +
           arm_extract_bits(imm, 0, 11, 0, 11) +
           arm_extract_bits(imm, 12, 15, 16, 19);
}

int __movt(arm_cond_t cond, arm_reg rd, int imm)
{
    imm >>= 16;
    return arm_encode(cond, 52, 0, rd, 0) +
           arm_extract_bits(imm, 0, 11, 0, 11) +
           arm_extract_bits(imm, 12, 15, 16, 19);
}

int __mov_i(arm_cond_t cond, arm_reg rd, int imm)
{
    return __mov(cond, 1, arm_mov, 0, 0, rd, imm);
}

int __mov_r(arm_cond_t cond, arm_reg rd, arm_reg rs)
{
    return __mov(cond, 0, arm_mov, 0, 0, rd, rs);
}

int __srl(arm_cond_t cond, arm_reg rd, arm_reg rm, arm_reg rs)
{
    return arm_encode(cond, 0 + (arm_mov << 1) + (0 << 5), 0, rd,
                      rm + (1 << 4) + (1 << 5) + (rs << 8));
}

int __srl_amt(arm_cond_t cond,
              int s,
              shift_type shift,
              arm_reg rd,
              arm_reg rm,
              int amt)
{
    return arm_encode(cond, s + (arm_mov << 1) + (0 << 5), 0, rd,
                      rm + (0 << 4) + (shift << 5) + (amt << 7));
}

int __sll(arm_cond_t cond, arm_reg rd, arm_reg rm, arm_reg rs)
{
    return arm_encode(cond, 0 + (arm_mov << 1) + (0 << 5), 0, rd,
                      rm + (1 << 4) + (0 << 5) + (rs << 8));
}

int __sll_amt(arm_cond_t cond,
              int s,
              shift_type shift,
              arm_reg rd,
              arm_reg rm,
              int amt)
{
    return arm_encode(cond, s + (arm_mov << 1) + (0 << 5), 0, rd,
                      rm + (0 << 4) + (shift << 5) + (amt << 7));
}

int __sra(arm_cond_t cond, arm_reg rd, arm_reg rm, arm_reg rs)
{
    /* Arithmetic right shift with register
     * Bit 4 = 1 (register-specified shift)
     * Bits 5-6 = arith_rs (2) for arithmetic right shift
     */
    return arm_encode(cond, 0 + (arm_mov << 1) + (0 << 5), 0, rd,
                      rm + (1 << 4) + (arith_rs << 5) + (rs << 8));
}

int __add_i(arm_cond_t cond, arm_reg rd, arm_reg rs, int imm)
{
    if (imm >= 0)
        return __mov(cond, 1, arm_add, 0, rs, rd, imm);
    return __mov(cond, 1, arm_sub, 0, rs, rd, -imm);
}

int __add_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg ro)
{
    return __mov(cond, 0, arm_add, 0, rs, rd, ro);
}

int __sub_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg ro)
{
    return __mov(cond, 0, arm_sub, 0, rs, rd, ro);
}

int __and_i(arm_cond_t cond, arm_reg rd, arm_reg rs, int imm)
{
    return __mov(cond, 1, arm_and, 0, rs, rd, imm);
}

int __zero(int rd)
{
    return __mov_i(__AL, rd, 0);
}

/* ARM halfword transfer (immediate offset) using special encoding
 * For halfword: bits[11:8] = imm4H, bits[7:4] = encoding, bits[3:0] = imm4L
 * imm4H: upper 4 bits of offset
 * imm4L: lower 4 bits of offset
 * encoding: 0b1011 for unsigned halfword, 0b1111 for signed halfword
 */
int arm_halfword_transfer(arm_cond_t cond,
                          int l,
                          arm_reg rn,
                          arm_reg rd,
                          int ofs,
                          int signed_op)
{
    int opcode = 16 + 8 + 4 + l;

    if (ofs < 0) {
        opcode -= 8;
        ofs = -ofs;
    }

    if (ofs > 255)
        fatal("Halfword offset too large");

    /* Halfword encoding: split offset into 4-bit high and low parts */
    int imm4H = ((ofs >> 4) & 0xF) << 8;
    int imm4L = ofs & 0xF;

    /* Encode lower 8 bits: 1011xxxx for unsigned, 1111xxxx for signed */
    int encoded_ofs = imm4H | 0xB0 | imm4L | (signed_op << 6);

    return arm_encode(cond, opcode, rn, rd, encoded_ofs);
}

int arm_transfer(arm_cond_t cond,
                 int l,
                 int size,
                 arm_reg rn,
                 arm_reg rd,
                 int ofs)
{
    int opcode = 64 + 16 + 8 + l;
    if (size == 1)
        opcode += 4;
    if (ofs < 0) {
        opcode -= 8;
        ofs = -ofs;
    }
    return arm_encode(cond, opcode, rn, rd, ofs & 4095);
}

int __lw(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs)
{
    return arm_transfer(cond, 1, 4, rn, rd, ofs);
}

int __lb(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs)
{
    return arm_transfer(cond, 1, 1, rn, rd, ofs);
}

int __sw(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs)
{
    return arm_transfer(cond, 0, 4, rn, rd, ofs);
}

int __sb(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs)
{
    return arm_transfer(cond, 0, 1, rn, rd, ofs);
}

/* ARM signed halfword load (LDRSH) */
int __lh(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs)
{
    return arm_halfword_transfer(cond, 1, rn, rd, ofs, 1);
}

/* ARM halfword store (STRH) */
int __sh(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs)
{
    return arm_halfword_transfer(cond, 0, rn, rd, ofs, 0);
}

int __stmdb(arm_cond_t cond, int w, arm_reg rn, int reg_list)
{
    return arm_encode(cond, arm_stmdb + (0x2 << 6) + (w << 1), rn, 0, reg_list);
}

int __ldm(arm_cond_t cond, int w, arm_reg rn, int reg_list)
{
    return arm_encode(cond, arm_ldm + (0x2 << 6) + (w << 1), rn, 0, reg_list);
}

int __push_reg(arm_cond_t cond, arm_reg rt)
{
    return arm_encode(cond, (0x5 << 4) | 0x2, 0xd, rt, 0x4);
}

int __pop_word(arm_cond_t cond, arm_reg rt)
{
    return arm_encode(cond, (0x4 << 4) | 0x9, 0xd, rt, 0x4);
}

int __b(arm_cond_t cond, int ofs)
{
    int o = (ofs - 8) >> 2;
    return arm_encode(cond, 160, 0, 0, 0) + (o & 16777215);
}

int __bl(arm_cond_t cond, int ofs)
{
    int o = (ofs - 8) >> 2;
    return arm_encode(cond, 176, 0, 0, 0) + (o & 16777215);
}

int __bx(arm_cond_t cond, arm_reg rm)
{
    /* BX: Branch and Exchange */
    return (cond << 28) | 0x012FFF10 | rm;
}

int __blx(arm_cond_t cond, arm_reg rd)
{
    return arm_encode(cond, 18, 15, 15, rd + 3888);
}

int __mul(arm_cond_t cond, arm_reg rd, arm_reg r1, arm_reg r2)
{
    return arm_encode(cond, 0, rd, 0, (r1 << 8) + 144 + r2);
}

int __div(arm_cond_t cond, arm_reg rd, arm_reg r1, arm_reg r2)
{
    return arm_encode(cond, 113, rd, 15, (r1 << 8) + 16 + r2);
}

int __rsb_i(arm_cond_t cond, arm_reg rd, int imm, arm_reg rn)
{
    return __mov(cond, 1, arm_rsb, 0, rn, rd, imm);
}

int __cmp_r(arm_cond_t cond, arm_reg r1, arm_reg r2)
{
    return __mov(cond, 0, arm_cmp, 1, r1, 0, r2);
}

int __cmp_i(arm_cond_t cond, arm_reg rn, int imm)
{
    return __mov(cond, 1, arm_cmp, 1, rn, 0, imm);
}

int __teq(arm_reg rd)
{
    return __mov(__AL, 1, arm_teq, 1, rd, 0, 0);
}

int __sxtb(arm_cond_t cond, arm_reg rd, arm_reg rm, int rotation)
{
    if (rotation != 0 && rotation != 8 && rotation != 16 && rotation != 24)
        fatal("SXTB rotation must be 0, 8, 16, or 24");

    return arm_encode(cond, 106, 0xF, rd,
                      rm | ((rotation >> 3) << 10) | (0x7 << 4));
}

int __sxth(arm_cond_t cond, arm_reg rd, arm_reg rm, int rotation)
{
    if (rotation != 0 && rotation != 8 && rotation != 16 && rotation != 24)
        fatal("SXTH rotation must be 0, 8, 16, or 24");

    return arm_encode(cond, 107, 0xF, rd,
                      rm | ((rotation >> 3) << 10) | (0x7 << 4));
}


================================================
FILE: src/defs.h
================================================
/*
 * shecc - Self-Hosting and Educational C Compiler.
 *
 * shecc is freely redistributable under the BSD 2 clause license. See the
 * file "LICENSE" for information on usage and redistribution of this file.
 */

#pragma once
#include <stdbool.h>

/* definitions */

/* Common macro functions */
#define is_newline(c) (c == '\r' || c == '\n')

/* Limitations */
#define MAX_TOKEN_LEN 256
#define MAX_ID_LEN 64
#define MAX_LINE_LEN 256
#define MAX_VAR_LEN 128
#define MAX_TYPE_LEN 32
#define MAX_PARAMS 8
#define MAX_LOCALS 1600
#define MAX_FIELDS 64
#define MAX_TYPES 256
#define MAX_LABELS 256
#define MAX_IR_INSTR 80000
#define MAX_BB_PRED 128
#define MAX_BB_DOM_SUCC 64
#define MAX_BB_RDOM_SUCC 256
#define MAX_GLOBAL_IR 256
#define MAX_CODE 262144
#define MAX_DATA 262144
#define MAX_SYMTAB 65536
#define MAX_STRTAB 65536
#define MAX_HEADER 1024
#define MAX_PROGRAM_HEADER 1024
#define MAX_SECTION 1024
#define MAX_SECTION_HEADER 1024
#define MAX_SHSTR 1024
#define MAX_INTERP 1024
#define MAX_DYNAMIC 1024
#define MAX_DYNSYM 1024
#define MAX_DYNSTR 1024
#define MAX_RELPLT 1024
#define MAX_PLT 1024
#define MAX_GOTPLT 1024
#define MAX_CONSTANTS 1024
#define MAX_CASES 128
#define MAX_NESTING 128
#define MAX_OPERAND_STACK_SIZE 32
#define MAX_ANALYSIS_STACK_SIZE 800

/* Default capacities for common data structures */
/* Arena sizes optimized based on typical usage patterns */
#define DEFAULT_ARENA_SIZE 262144 /* 256 KiB - standard default */
#define SMALL_ARENA_SIZE 65536    /* 64 KiB - for small allocations */
#define LARGE_ARENA_SIZE 524288   /* 512 KiB - for instruction arena */
#define DEFAULT_FUNCS_SIZE 64
#define DEFAULT_SRC_FILE_COUNT 8

/* Arena compaction bitmask flags for selective memory reclamation */
#define COMPACT_ARENA_BLOCK 0x01   /* BLOCK_ARENA - variables/blocks */
#define COMPACT_ARENA_INSN 0x02    /* INSN_ARENA - instructions */
#define COMPACT_ARENA_BB 0x04      /* BB_ARENA - basic blocks */
#define COMPACT_ARENA_HASHMAP 0x08 /* HASHMAP_ARENA - hash nodes */
#define COMPACT_ARENA_GENERAL 0x10 /* GENERAL_ARENA - misc allocations */
#define COMPACT_ARENA_ALL 0x1F     /* All arenas */

/* Common arena compaction combinations for different compilation phases */
#define COMPACT_PHASE_PARSING (COMPACT_ARENA_BLOCK | COMPACT_ARENA_GENERAL)
#define COMPACT_PHASE_SSA (COMPACT_ARENA_INSN | COMPACT_ARENA_BB)
#define COMPACT_PHASE_BACKEND (COMPACT_ARENA_BB | COMPACT_ARENA_GENERAL)

#define ELF_START 0x10000
#define PTR_SIZE 4

/* Number of the available registers. Either 7 or 8 is accepted now. */
#define REG_CNT 8

/* This macro will be automatically defined at shecc run-time. */
#ifdef __SHECC__
/* use do-while as a substitution for nop */
#define UNUSED(x) \
    do {          \
        ;         \
    } while (0)
#define HOST_PTR_SIZE 4
#else
/* suppress GCC/Clang warnings */
#define UNUSED(x) (void) (x)
/* configure host data model when using 'memcpy'. */
#define HOST_PTR_SIZE __SIZEOF_POINTER__
#endif

#ifndef MIN_ALIGNMENT
#define MIN_ALIGNMENT 8
#endif

#ifndef ALIGN_UP
#define ALIGN_UP(val, align) (((val) + (align) - 1) & ~((align) - 1))
#endif

/* Common data structures */
typedef struct arena_block {
    char *memory;
    int capacity;
    int offset;
    struct arena_block *next;
} arena_block_t;

typedef struct {
    arena_block_t *head;
    int total_bytes; /* Track total allocation for profiling */
    int block_size;  /* Default block size for new blocks */
} arena_t;

/* string-based hash map definitions */

typedef struct hashmap_node {
    char *key;
    void *val;
    bool occupied;
} hashmap_node_t;

typedef struct {
    int size;
    int cap;
    hashmap_node_t *table;
} hashmap_t;

/* lexer tokens */
typedef enum {
    T_start, /* FIXME: Unused, intended for lexer state machine init */
    T_eof,   /* end-of-file (EOF) */
    T_numeric,
    T_identifier,
    T_comma,  /* , */
    T_string, /* null-terminated string */
    T_char,
    T_open_bracket,  /* ( */
    T_close_bracket, /* ) */
    T_open_curly,    /* { */
    T_close_curly,   /* } */
    T_open_square,   /* [ */
    T_close_square,  /* ] */
    T_asterisk,      /* '*' */
    T_divide,        /* / */
    T_mod,           /* % */
    T_bit_or,        /* | */
    T_bit_xor,       /* ^ */
    T_bit_not,       /* ~ */
    T_log_and,       /* && */
    T_log_or,        /* || */
    T_log_not,       /* ! */
    T_lt,            /* < */
    T_gt,            /* > */
    T_le,            /* <= */
    T_ge,            /* >= */
    T_lshift,        /* << */
    T_rshift,        /* >> */
    T_dot,           /* . */
    T_arrow,         /* -> */
    T_plus,          /* + */
    T_minus,         /* - */
    T_minuseq,       /* -= */
    T_pluseq,        /* += */
    T_asteriskeq,    /* *= */
    T_divideeq,      /* /= */
    T_modeq,         /* %= */
    T_lshifteq,      /* <<= */
    T_rshifteq,      /* >>= */
    T_xoreq,         /* ^= */
    T_oreq,          /* |= */
    T_andeq,         /* &= */
    T_eq,            /* == */
    T_noteq,         /* != */
    T_assign,        /* = */
    T_increment,     /* ++ */
    T_decrement,     /* -- */
    T_question,      /* ? */
    T_colon,         /* : */
    T_semicolon,     /* ; */
    T_ampersand,     /* & */
    T_return,
    T_if,
    T_else,
    T_while,
    T_for,
    T_do,
    T_typedef,
    T_enum,
    T_struct,
    T_union,
    T_sizeof,
    T_elipsis, /* ... */
    T_switch,
    T_case,
    T_break,
    T_default,
    T_continue,
    T_goto,
    T_const, /* const qualifier */
    /* C pre-processor directives */
    T_cppd_include,
    T_cppd_define,
    T_cppd_undef,
    T_cppd_error,
    T_cppd_if,
    T_cppd_elif,
    T_cppd_else,
    T_cppd_endif,
    T_cppd_ifdef,
    T_cppd_ifndef,
    T_cppd_pragma,
    /* C pre-processor specific, these kinds
     * will be removed after pre-processing is done.
     */
    T_newline,
    T_backslash,
    T_whitespace,
    T_tab
} token_kind_t;

/* Source location tracking for better error reporting */
typedef struct {
    int pos; /* raw source file position */
    int len; /* length of token */
    int line;
    int column;
    char *filename;
} source_location_t;

typedef struct token {
    token_kind_t kind;
    char *literal;
    source_location_t location;
    struct token *next;
} token_t;

typedef struct token_stream {
    token_t *head;
    token_t *tail;
} token_stream_t;

/* String pool for identifier deduplication */
typedef struct {
    hashmap_t *strings; /* Map string -> interned string */
} string_pool_t;

/* String literal pool for deduplicating string constants */
typedef struct {
    hashmap_t *literals; /* Map string literal -> ELF data offset */
} string_literal_pool_t;

/* builtin types */
typedef enum {
    TYPE_void = 0,
    TYPE_int,
    TYPE_char,
    TYPE_short,
    TYPE_struct,
    TYPE_union,
    TYPE_typedef
} base_type_t;

/* IR opcode */
typedef enum {
    /* intermediate use in front-end. No code generation */
    OP_generic,

    OP_phi,
    OP_unwound_phi, /* work like address_of + store */

    /* calling convention */
    OP_define,   /* function entry point */
    OP_push,     /* prepare arguments */
    OP_call,     /* function call */
    OP_indirect, /* indirect call with function pointer */
    OP_return,   /* explicit return */

    OP_allocat, /* allocate space on stack */
    OP_assign,
    OP_load_constant,       /* load constant */
    OP_load_data_address,   /* lookup address of a constant in data section */
    OP_load_rodata_address, /* lookup address of a constant in rodata section */

    /* control flow */
    OP_branch,   /* conditional jump */
    OP_jump,     /* unconditional jump */
    OP_func_ret, /* returned value */
    OP_label,    /* for goto label */

    /* function pointer */
    OP_address_of_func, /* resolve function entry */
    OP_load_func,       /* prepare indirective call */
    OP_global_load_func,

    /* memory address operations */
    OP_address_of, /* lookup variable's address */
    OP_global_address_of,
    OP_load, /* load a word from stack */
    OP_global_load,
    OP_store, /* store a word to stack */
    OP_global_store,
    OP_read,  /* read from memory address */
    OP_write, /* write to memory address */

    /* arithmetic operators */
    OP_add,
    OP_sub,
    OP_mul,
    OP_div,     /* signed division */
    OP_mod,     /* modulo */
    OP_ternary, /* ? : */
    OP_lshift,
    OP_rshift,
    OP_log_and,
    OP_log_or,
    OP_log_not,
    OP_eq,  /* equal */
    OP_neq, /* not equal */
    OP_lt,  /* less than */
    OP_leq, /* less than or equal */
    OP_gt,  /* greater than */
    OP_geq, /* greater than or equal */
    OP_bit_or,
    OP_bit_and,
    OP_bit_xor,
    OP_bit_not,
    OP_negate,

    /* data type conversion */
    OP_trunc,
    OP_sign_ext,
    OP_cast,

    /* entry point of the state machine */
    OP_start
} opcode_t;

/* variable definition */
typedef struct {
    int counter;
    int stack[64];
    int stack_idx;
} rename_t;

typedef struct ref_block ref_block_t;

struct ref_block_list {
    ref_block_t *head, *tail;
};

typedef struct ref_block_list ref_block_list_t;

typedef struct insn insn_t;

typedef struct use_chain_node {
    insn_t *insn;
    struct use_chain_node *next, *prev;
} use_chain_t;

typedef struct var var_t;
typedef struct type type_t;

typedef struct var_list {
    int capacity;
    int size;
    var_t **elements;
} var_list_t;

struct var {
    type_t *type;
    char var_name[MAX_VAR_LEN];
    int ptr_level;
    bool is_func;
    bool is_global;
    bool is_const_qualified; /* true if variable has const qualifier */
    bool address_taken;      /* true if variable address was taken (&var) */
    int array_size;
    int array_dim1, array_dim2; /* first/second dimension size for 2D arrays */
    int offset;   /* offset from stack or frame, index 0 is reserved */
    int init_val; /* for global initialization */
    int liveness; /* live range */
    int in_loop;
    struct var *base;
    int subscript;
    struct var *subscripts[128];
    int subscripts_idx;
    rename_t rename;
    ref_block_list_t ref_block_list; /* blocks which kill variable */
    use_chain_t *users_head, *users_tail;
    struct insn *last_assign;
    int consumed;
    bool is_ternary_ret;
    bool is_logical_ret;
    bool is_const;  /* whether a constant representaion or not */
    int vreg_id;    /* Virtual register ID */
    int phys_reg;   /* Physical register assignment (-1 if unassigned) */
    int vreg_flags; /* VReg flags */
    int first_use;  /* First instruction index where variable is used */
    int last_use;   /* Last instruction index where variable is used */
    int loop_depth; /* Nesting depth if variable is in a loop */
    int use_count;  /* Number of times variable is used */
    bool space_is_allocated; /* whether space is allocated for this variable */

    /* This flag is used to indicate to the compiler that the offset of
     * the variable is based on the top of the local stack.
     */
    bool ofs_based_on_stack_top;

    /* True when this variable was synthesized to hold a compound literal
     * (e.g., array or struct literal temporaries).
     */
    bool is_compound_literal;
};

typedef struct func func_t;

/* block definition */
struct block {
    var_list_t locals;
    struct block *parent;
    func_t *func;
    struct block *next;
};

typedef struct block block_t;
typedef struct basic_block basic_block_t;

/* Definition of a growable buffer for a mutable null-terminated string
 * @size:     Current number of elements in the array
 * @capacity: Number of elements that can be stored without resizing
 * @elements: Pointer to the array of characters
 */
typedef struct {
    int size;
    int capacity;
    char *elements;
} strbuf_t;

/* phase-2 IR definition */
struct ph2_ir {
    opcode_t op;
    int src0;
    int src1;
    int dest;
    char func_name[MAX_VAR_LEN];
    basic_block_t *next_bb;
    basic_block_t *then_bb;
    basic_block_t *else_bb;
    struct ph2_ir *next;
    bool is_branch_detached;

    /* When an instruction uses a variable that its offset is based on
     * the top of the stack, this instruction's flag is also set to
     * indicate the compiler to recalculate the offset after the function's
     * stack size has been determined.
     *
     * Currently, only OP_load, OP_store and OP_address_of need this flag
     * to recompute the offset.
     */
    bool ofs_based_on_stack_top;
};

typedef struct ph2_ir ph2_ir_t;

/* type definition */
struct type {
    char type_name[MAX_TYPE_LEN];
    base_type_t base_type;
    struct type *base_struct;
    int size;
    var_t fields[MAX_FIELDS];
    int num_fields;
    int ptr_level; /* pointer level for typedef pointer types */
};

/* lvalue details */
typedef struct {
    int size;
    int ptr_level;
    bool is_func;
    bool is_reference;
    type_t *type;
} lvalue_t;

/* constants for enums */
typedef struct {
    char alias[MAX_VAR_LEN];
    int value;
} constant_t;

struct phi_operand {
    var_t *var;
    basic_block_t *from;
    struct phi_operand *next;
};

typedef struct phi_operand phi_operand_t;

struct insn {
    struct insn *next, *prev;
    int idx;
    opcode_t opcode;
    var_t *rd;
    var_t *rs1;
    var_t *rs2;
    int sz;
    bool useful; /* Used in DCE process. Set true if instruction is useful. */
    basic_block_t *belong_to;
    phi_operand_t *phi_ops;
    char str[64];
};

typedef struct {
    insn_t *head, *tail;
} insn_list_t;

typedef struct {
    ph2_ir_t *head, *tail;
} ph2_ir_list_t;

typedef enum { NEXT, ELSE, THEN } bb_connection_type_t;

typedef struct {
    basic_block_t *bb;
    bb_connection_type_t type;
} bb_connection_t;

struct symbol {
    var_t *var;
    int index;
    struct symbol *next;
};

typedef struct symbol symbol_t;

typedef struct {
    symbol_t *head, *tail;
} symbol_list_t;

struct basic_block {
    insn_list_t insn_list;
    ph2_ir_list_t ph2_ir_list;
    bb_connection_t prev[MAX_BB_PRED];
    /* Used in instruction dumping when ir_dump is enabled. */
    char bb_label_name[MAX_VAR_LEN];
    struct basic_block *next;  /* normal BB */
    struct basic_block *then_; /* conditional BB */
    struct basic_block *else_;
    struct basic_block *idom;
    struct basic_block *r_idom;
    struct basic_block *rpo_next;
    struct basic_block *rpo_r_next;
    var_list_t live_gen;
    var_list_t live_kill;
    var_list_t live_in;
    var_list_t live_out;
    int rpo;
    int rpo_r;
    struct basic_block *DF[64];
    struct basic_block *RDF[64];
    int df_idx;
    int rdf_idx;
    int visited;
    bool useful; /* indicate whether this BB contains useful instructions */
    struct basic_block *dom_next[64];
    struct basic_block *dom_prev;
    struct basic_block *rdom_next[256];
    struct basic_block *rdom_prev;
    func_t *belong_to;
    block_t *scope;
    symbol_list_t symbol_list; /* variable declaration */
    int elf_offset;
};

struct ref_block {
    basic_block_t *bb;
    struct ref_block *next;
};

/* Syntactic representation of func, combines syntactic details (e.g., return
 * type, parameters) with SSA-related information (e.g., basic blocks, control
 * flow) to support parsing, analysis, optimization, and code generation.
 */

typedef struct {
    char label_name[MAX_ID_LEN];
    basic_block_t *bb;
    bool used;
} label_t;

struct func {
    /* Syntatic info */
    var_t return_def;
    var_t param_defs[MAX_PARAMS];
    int num_params;
    int va_args;
    int stack_size;

    /* SSA info */
    basic_block_t *bbs;
    basic_block_t *exit;
    symbol_list_t global_sym_list;
    int bb_cnt;
    int visited;

    /* Information used for dynamic linking */
    bool is_used;
    int plt_offset, got_offset;

    struct func *next;
};

typedef struct {
    func_t *head, *tail;
} func_list_t;

typedef struct {
    func_t *func;
    basic_block_t *bb;
    void (*preorder_cb)(func_t *, basic_block_t *);
    void (*postorder_cb)(func_t *, basic_block_t *);
} bb_traversal_args_t;

typedef struct {
    var_t *var;
    int polluted;
} regfile_t;

/* ELF header */
typedef struct {
    char e_ident[16];
    char e_type[2];
    char e_machine[2];
    int e_version;
    int e_entry;
    int e_phoff;
    int e_shoff;
    int e_flags;
    char e_ehsize[2];
    char e_phentsize[2];
    char e_phnum[2];
    char e_shentsize[2];
    char e_shnum[2];
    char e_shstrndx[2];
} elf32_hdr_t;

/* ELF program header */
typedef struct {
    int p_type;
    int p_offset;
    int p_vaddr;
    int p_paddr;
    int p_filesz;
    int p_memsz;
    int p_flags;
    int p_align;
} elf32_phdr_t;

/* ELF section header */
typedef struct {
    int sh_name;
    int sh_type;
    int sh_flags;
    int sh_addr;
    int sh_offset;
    int sh_size;
    int sh_link;
    int sh_info;
    int sh_addralign;
    int sh_entsize;
} elf32_shdr_t;

/* Structures for dynamic linked program */
/* ELF buffers for dynamic sections */
typedef struct {
    strbuf_t *elf_interp;
    strbuf_t *elf_dynamic;
    strbuf_t *elf_dynsym;
    strbuf_t *elf_dynstr;
    strbuf_t *elf_relplt;
    strbuf_t *elf_plt;
    strbuf_t *elf_got;
    int elf_interp_start;
    int elf_relplt_start;
    int elf_plt_start;
    int elf_got_start;
    int relplt_size;
    int plt_size;
    int got_size;
} dynamic_sections_t;

/* For .dynsym section. */
typedef struct {
    int st_name;
    int st_value;
    int st_size;
    char st_info;
    char st_other;
    char st_shndx[2];
} elf32_sym_t;

/* For .rel.plt section */
typedef struct {
    int r_offset;
    int r_info;
} elf32_rel_t;

/* For .dynamic section */
typedef struct {
    int d_tag;
    int d_un;
} elf32_dyn_t;

#define ELF32_ST_INFO(b, t) (((b) << 4) + ((t) & 0xf))


================================================
FILE: src/elf.c
================================================
/*
 * shecc - Self-Hosting and Educational C Compiler.
 *
 * shecc is freely redistributable under the BSD 2 clause license. See the
 * file "LICENSE" for information on usage and redistribution of this file.
 */

/* ELF file manipulation */

#include "../config"
#include "defs.h"
#include "globals.c"

#ifndef PAGESIZE
#define PAGESIZE 4096
#endif

int elf_symbol_index = 0;

void elf_write_str(strbuf_t *elf_array, const char *vals)
{
    /* Note that strbuf_puts() does not push the null character.
     *
     * If necessary, use elf_write_byte() to append the null character
     * after calling elf_write_str().
     */
    if (!elf_array || !vals)
        return;
    strbuf_puts(elf_array, vals);
}

void elf_write_byte(strbuf_t *elf_array, int val)
{
    if (!elf_array)
        return;
    strbuf_putc(elf_array, val);
}

char e_extract_byte(int v, int b)
{
    return (char) ((v >> (b << 3)) & 0xFF);
}

void elf_write_int(strbuf_t *elf_array, int val)
{
    if (!elf_array)
        return;
    for (int i = 0; i < 4; i++)
        strbuf_putc(elf_array, e_extract_byte(val, i));
}

void elf_write_blk(strbuf_t *elf_array, void *blk, int sz)
{
    if (!elf_array || !blk || sz <= 0)
        return;
    char *ptr = blk;
    for (int i = 0; i < sz; i++)
        strbuf_putc(elf_array, ptr[i]);
}

void elf_generate_header(void)
{
    /* Check for null pointers to prevent crashes */
    if (!elf_code || !elf_data || !elf_symtab || !elf_strtab || !elf_header) {
        fatal("ELF buffers not initialized");
        return;
    }

    elf32_hdr_t hdr;
    int phnum, shnum, shstrndx, shoff;

    if (dynlink) {
        /* In dynamic linking mode:
         * - number of program headers = 4
         * - number of section headers = 15
         * - section header index of .shstrtab = 14
         */
        phnum = 4;
        shnum = 15;
        shstrndx = 14;
        shoff =
            elf_header_len + elf_code->size + elf_data->size +
            elf_rodata->size + elf_symtab->size + elf_strtab->size +
            elf_shstrtab->size + dynamic_sections.elf_interp->size +
            dynamic_sections.elf_relplt->size + dynamic_sections.elf_plt->size +
            dynamic_sections.elf_got->size + dynamic_sections.elf_dynstr->size +
            dynamic_sections.elf_dynsym->size +
            dynamic_sections.elf_dynamic->size;
    } else {
        /* In static linking mode:
         * - number of program headers = 2
         * - number of section headers = 8
         * - section header index of .shstrtab = 7
         */
        phnum = 2;
        shnum = 8;
        shstrndx = 7;
        shoff = elf_header_len + elf_code->size + elf_data->size +
                elf_rodata->size + elf_symtab->size + elf_strtab->size +
                elf_shstrtab->size;
    }
    /* The following table explains the meaning of each field in the
     * ELF32 file header.
     *
     * Notice that the following values are hexadecimal.
     *
     *    |  File          |                                                 |
     *  & |  Header bytes  | Explanation                                     |
     * ---+----------------+-------------------------------------------------+
     * 00 | 7F  45  4C  46 | e_ident[0] - e_ident[3]: ELF magic number.      |
     *    | 01             | e_ident[4]: 1 -> 32-bit, 2 -> 64-bit.           |
     *    |     01         | e_ident[5]: 1 -> little-endian. 2 -> big-endian.|
     *    |         01     | e_ident[6]: 1 -> ELF header version; must be 1. |
     *    |             00 | e_ident[7]: Target OS ABI; be 1 for Linux.      |
     *    | 00             | e_ident[8]: ABI version; should be 1 for Linux. |
     *    |     00  00  00 | e_ident[9] - e_ident[16]: Padding; Unused;      |
     *    | 00  00  00  00 |                           should be 0.          |
     * ---+----------------+-------------------------------------------------+
     *    | 02  00         | e_type: Object file type; 2 -> executable       |
     *    |         28  00 | e_machine: Instruction Set Architecture.        |
     *    |                |            0x28 -> ARMv7                        |
     *    |                |            0xF3 -> RISC-V                       |
     *    | 01  00  00  00 | e_version: ELF identification version;          |
     *    |                |            must be 1.                           |
     *    | 54  00  01  00 | e_entry: Memory address of entry point.         |
     *    |                |          (where process starts).                |
     *    | 34  00  00  00 | e_phoff: File offset of program headers.        |
     *    |                |          0x34 -> 32-bit, 0x40 -> 64-bit.        |
     *    | d7  8a  03  00 | e_shoff: File offset of section headers.        |
     * ---+----------------+-------------------------------------------------+
     *    | 00  02  00  50 | e_flags: 0x50000200 -> ARM Version5 EABI,       |
     *    |                |                        soft-float ABI           |
     *    |                |          0x00000000 -> RISC-V                   |
     *    | 34  00         | e_ehsize: Size of this header.                  |
     *    |                |           0x34 -> 32-bit, 0x40 -> 64-bit.       |
     *    |         20  00 | e_phentsize: Size of each program header.       |
     *    |                |              0x20 -> 32-bit, 0x38 -> 64-bit.    |
     *    | 01  00         | e_phnum: Number of program headers.             |
     *    |         28  00 | e_shentsize: Size of each section header.       |
     *    |                |              0x28 -> 32-bit, 0x40 -> 64-bit.    |
     *    | 06  00         | e_shnum: Number of section headers.             |
     *    |         05  00 | e_shstrndx: Index of section header containing  |
     *    |                |             section names.                      |
     * ---+----------------+-------------------------------------------------+
     * 34 |                |                                                 |
     */
    /* ELF file header */
    hdr.e_ident[0] = (char) 0x7F; /* ELF magic number */
    hdr.e_ident[1] = 'E';
    hdr.e_ident[2] = 'L';
    hdr.e_ident[3] = 'F';
    hdr.e_ident[4] = 1; /* 32-bit */
    hdr.e_ident[5] = 1; /* little-endian */
    hdr.e_ident[6] = 1; /* ELF header version */
    hdr.e_ident[7] = 0; /* Target OS ABI */
    hdr.e_ident[8] = 0; /* ABI version */
    hdr.e_ident[9] = 0; /* Padding */
    hdr.e_ident[10] = 0;
    hdr.e_ident[11] = 0;
    hdr.e_ident[12] = 0;
    hdr.e_ident[13] = 0;
    hdr.e_ident[14] = 0;
    hdr.e_ident[15] = 0;
    hdr.e_type[0] = 2; /* Object file type */
    hdr.e_type[1] = 0;
    hdr.e_machine[0] = ELF_MACHINE; /* Instruction Set Architecture */
    hdr.e_machine[1] = 0;
    hdr.e_version = 1;                     /* ELF version */
    hdr.e_entry = elf_code_start;          /* entry point */
    hdr.e_phoff = sizeof(elf32_hdr_t);     /* program header offset */
    hdr.e_shoff = shoff;                   /* section header offset */
    hdr.e_flags = ELF_FLAGS;               /* flags */
    hdr.e_ehsize[0] = sizeof(elf32_hdr_t); /* header size */
    hdr.e_ehsize[1] = 0;
    hdr.e_phentsize[0] = sizeof(elf32_phdr_t); /* program header size */
    hdr.e_phentsize[1] = 0;
    hdr.e_phnum[0] = phnum; /* number of program headers */
    hdr.e_phnum[1] = 0;
    hdr.e_shentsize[0] = sizeof(elf32_shdr_t); /* section header size */
    hdr.e_shentsize[1] = 0;
    hdr.e_shnum[0] = shnum; /* number of section headers */
    hdr.e_shnum[1] = 0;
    hdr.e_shstrndx[0] = shstrndx; /* section index with names */
    hdr.e_shstrndx[1] = 0;
    elf_write_blk(elf_header, &hdr, sizeof(elf32_hdr_t));
}

void elf_generate_program_headers(void)
{
    if (!elf_program_header || !elf_code || !elf_data || !elf_rodata ||
        (dynlink &&
         (!dynamic_sections.elf_interp || !dynamic_sections.elf_relplt ||
          !dynamic_sections.elf_plt || !dynamic_sections.elf_got ||
          !dynamic_sections.elf_dynstr || !dynamic_sections.elf_dynsym ||
          !dynamic_sections.elf_dynamic))) {
        fatal("ELF section buffers not initialized");
        return;
    }

    elf32_phdr_t phdr;

    /* Explain the meaning of each field in the ELF32 program header.
     *
     *    |  Program       |                                                 |
     *  & |  Header bytes  | Explanation                                     |
     * ---+----------------+-------------------------------------------------+
     * 34 | 01  00  00  00 | p_type: Segment type; 1 -> loadable.            |
     *    | 54  00  00  00 | p_offset: Offset of segment in the file.        |
     *    | 54  00  01  00 | p_vaddr: Virtual address of loaded segment.     |
     *    | 54  00  01  00 | p_paddr: Only used on systems where physical    |
     *    |                |          address is relevant.                   |
     *    | 48  8a  03  00 | p_filesz: Size of the segment in the file image.|
     *    | 48  8a  03  00 | p_memsz: Size of the segment in memory.         |
     *    |                |          This value should be greater than or   |
     *    |                |          equal to p_filesz.                     |
     *    | 07  00  00  00 | p_flags: Segment-wise permissions;              |
     *    |                |          0x1 -> execute, 0x2 -> write,          |
     *    |                |          0x4 -> read                            |
     *    | 04  00  00  00 | p_align: Align segment to the specified value.  |
     * ---+----------------+-------------------------------------------------+
     * 54 |                |                                                 |
     */
    /* program header - read-only segment */
    phdr.p_type = 1;          /* PT_LOAD */
    phdr.p_offset = 0;        /* offset of segment */
    phdr.p_vaddr = ELF_START; /* virtual address */
    phdr.p_paddr = ELF_START; /* physical address */
    phdr.p_filesz =
        elf_header_len + elf_code->size + elf_rodata->size; /* size in file */
    phdr.p_memsz =
        elf_header_len + elf_code->size + elf_rodata->size; /* size in memory */
    phdr.p_flags = 5;                                       /* flags */
    phdr.p_align = PAGESIZE;                                /* alignment */
    if (dynlink) {
        phdr.p_filesz +=
            dynamic_sections.elf_relplt->size + dynamic_sections.elf_plt->size;
        phdr.p_memsz +=
            dynamic_sections.elf_relplt->size + dynamic_sections.elf_plt->size;
    }
    elf_write_blk(elf_program_header, &phdr, sizeof(elf32_phdr_t));

    /* program header - readable and writable segment */
    phdr.p_type = 1; /* PT_LOAD */
    phdr.p_offset = elf_header_len + elf_code->size +
                    elf_rodata->size;             /* offset of segment */
    phdr.p_vaddr = elf_data_start;                /* virtual address */
    phdr.p_paddr = elf_data_start;                /* physical address */
    phdr.p_filesz = elf_data->size;               /* size in file */
    phdr.p_memsz = elf_data->size + elf_bss_size; /* size in memory */
    phdr.p_flags = 6;                             /* flags */
    phdr.p_align = PAGESIZE;                      /* alignment */
    if (dynlink) {
        phdr.p_offset +=
            dynamic_sections.elf_relplt->size + dynamic_sections.elf_plt->size;
        phdr.p_vaddr = dynamic_sections.elf_interp_start;
        phdr.p_paddr = dynamic_sections.elf_interp_start;
        phdr.p_filesz += dynamic_sections.elf_interp->size +
                         dynamic_sections.elf_got->size +
                         dynamic_sections.elf_dynstr->size +
                         dynamic_sections.elf_dynsym->size +
                         dynamic_sections.elf_dynamic->size;
        phdr.p_memsz += dynamic_sections.elf_interp->size +
                        dynamic_sections.elf_got->size +
                        dynamic_sections.elf_dynstr->size +
                        dynamic_sections.elf_dynsym->size +
                        dynamic_sections.elf_dynamic->size;
    }
    elf_write_blk(elf_program_header, &phdr, sizeof(elf32_phdr_t));


    if (dynlink) {
        /* program header - program interpreter (.interp section) */
        phdr.p_type = 3; /* PT_INTERP */
        phdr.p_offset = elf_header_len + elf_code->size + elf_rodata->size +
                        dynamic_sections.elf_relplt->size +
                        dynamic_sections.elf_plt->size; /* offset of segment */
        phdr.p_vaddr = dynamic_sections.elf_interp_start; /* virtual address */
        phdr.p_paddr = dynamic_sections.elf_interp_start; /* physical address */
        phdr.p_filesz = strlen(DYN_LINKER) + 1;           /* size in file */
        phdr.p_memsz = strlen(DYN_LINKER) + 1;            /* size in memory */
        phdr.p_flags = 4;                                 /* flags */
        phdr.p_align = 1;                                 /* alignment */
        elf_write_blk(elf_program_header, &phdr, sizeof(elf32_phdr_t));

        /* program header - .dynamic section */
        phdr.p_type = 2; /* PT_DYNAMIC */
        phdr.p_offset =
            elf_header_len + elf_code->size + elf_rodata->size +
            dynamic_sections.elf_relplt->size + dynamic_sections.elf_plt->size +
            dynamic_sections.elf_interp->size + dynamic_sections.elf_got->size +
            dynamic_sections.elf_dynstr->size +
            dynamic_sections.elf_dynsym->size; /* offset of segment */
        phdr.p_vaddr = dynamic_sections.elf_got_start +
                       dynamic_sections.elf_got->size +
                       dynamic_sections.elf_dynstr->size +
                       dynamic_sections.elf_dynsym->size; /* virtual address */
        phdr.p_paddr = dynamic_sections.elf_got_start +
                       dynamic_sections.elf_got->size +
                       dynamic_sections.elf_dynstr->size +
                       dynamic_sections.elf_dynsym->size; /* physical address */
        phdr.p_filesz = dynamic_sections.elf_dynamic->size; /* size in file */
        phdr.p_memsz = dynamic_sections.elf_dynamic->size;  /* size in memory */
        phdr.p_flags = 6;                                   /* flags */
        phdr.p_align = 4;                                   /* alignment */
        elf_write_blk(elf_program_header, &phdr, sizeof(elf32_phdr_t));
    }
}

void elf_generate_section_headers(void)
{
    /* Check for null pointers to prevent crashes */
    if (!elf_section_header || !elf_code || !elf_data || !elf_rodata ||
        !elf_symtab || !elf_strtab || !elf_shstrtab ||
        (dynlink &&
         (!dynamic_sections.elf_interp || !dynamic_sections.elf_relplt ||
          !dynamic_sections.elf_plt || !dynamic_sections.elf_got ||
          !dynamic_sections.elf_dynstr || !dynamic_sections.elf_dynsym ||
          !dynamic_sections.elf_dynamic))) {
        fatal("ELF section buffers not initialized");
        return;
    }

    /* section header table */
    elf32_shdr_t shdr;
    int ofs = elf_header_len, sh_name = 0;

    /*
     * The following table uses the text section header as an example
     * to explain the ELF32 section header.
     *
     *    |  Section       |                                                 |
     *  & |  Header bytes  | Explanation                                     |
     * ---+----------------+-------------------------------------------------+
     *    | 0b  00  00  00 | sh_name: Name of the section. Giving the        |
     *    |                |          location of a null-terminated string.  |
     *    | 01  00  00  00 | sh_type: Type of the section's contents         |
     *    |                |          and semantics.                         |
     *    |                |          1 -> holds the program-defined         |
     *    |                |               information                       |
     *    | 07  00  00  00 | sh_flags: Miscellaneous attributes.             |
     *    |                |           0x1 -> writable, 0x2 -> allocatable   |
     *    |                |           0x4 -> executable.                    |
     *    | 54  00  01  00 | sh_addr: Starting address of the section        |
     *    |                |          in the memory image of a process.      |
     *    | 54  00  00  00 | sh_offset: Offset of the section in the file.   |
     *    | 0b  30  03  00 | sh_size: Size of the section.                   |
     *    | 00  00  00  00 | sh_link: Section header table index link.       |
     *    | 00  00  00  00 | sh_info: Extra information.                     |
     *    | 04  00  00  00 | sh_addralign: Address alignment constraints.    |
     *    | 00  00  00  00 | sh_entsize: Size of each entry.                 |
     * ---+----------------+-------------------------------------------------+
     *    |                |                                                 |
     */
    /* NULL section */
    shdr.sh_name = sh_name;
    shdr.sh_type = 0;
    shdr.sh_flags = 0;
    shdr.sh_addr = 0;
    shdr.sh_offset = 0;
    shdr.sh_size = 0;
    shdr.sh_link = 0;
    shdr.sh_info = 0;
    shdr.sh_addralign = 0;
    shdr.sh_entsize = 0;
    elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
    sh_name += 1;

    /* .text */
    shdr.sh_name = sh_name;
    shdr.sh_type = 1;
    shdr.sh_flags = 7;
    shdr.sh_addr = elf_code_start;
    shdr.sh_offset = ofs;
    shdr.sh_size = elf_code->size;
    shdr.sh_link = 0;
    shdr.sh_info = 0;
    shdr.sh_addralign = 4;
    shdr.sh_entsize = 0;
    elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
    ofs += elf_code->size;
    sh_name += strlen(".text") + 1;

    /* .rodata */
    shdr.sh_name = sh_name; /* Offset in shstrtab for ".rodata" */
    shdr.sh_type = 1;       /* SHT_PROGBITS */
    shdr.sh_flags = 2;      /* SHF_ALLOC only (read-only) */
    shdr.sh_addr = elf_rodata_start;
    shdr.sh_offset = ofs;
    shdr.sh_size = elf_rodata->size;
    shdr.sh_link = 0;
    shdr.sh_info = 0;
    shdr.sh_addralign = 4;
    shdr.sh_entsize = 0;
    elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
    ofs += elf_rodata->size;
    sh_name += strlen(".rodata") + 1;

    if (dynlink) {
        /* .rel.plt */
        shdr.sh_name = sh_name;
        shdr.sh_type = 9;     /* SHT_REL */
        shdr.sh_flags = 0x42; /* 0x40 | SHF_ALLOC */
        shdr.sh_addr = dynamic_sections.elf_relplt_start;
        shdr.sh_offset = ofs;
        shdr.sh_size = dynamic_sections.elf_relplt->size;
        shdr.sh_link = 8; /* The section header index of .dynsym. */
        shdr.sh_info = 6; /* The section header index of .got. */
        shdr.sh_addralign = 4;
        shdr.sh_entsize = sizeof(elf32_rel_t);
        elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
        ofs += dynamic_sections.elf_relplt->size;
        sh_name += strlen(".rel.plt") + 1;

        /* .plt */
        shdr.sh_name = sh_name;
        shdr.sh_type = 1;
        shdr.sh_flags = 0x6;
        shdr.sh_addr = dynamic_sections.elf_plt_start;
        shdr.sh_offset = ofs;
        shdr.sh_size = dynamic_sections.elf_plt->size;
        shdr.sh_link = 0;
        shdr.sh_info = 0;
        shdr.sh_addralign = 4;
        shdr.sh_entsize = 4;
        elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
        ofs += dynamic_sections.elf_plt->size;
        sh_name += strlen(".plt") + 1;

        /* .interp */
        shdr.sh_name = sh_name;
        shdr.sh_type = 1;
        shdr.sh_flags = 0x2;
        shdr.sh_addr = dynamic_sections.elf_interp_start;
        shdr.sh_offset = ofs;
        shdr.sh_size = strlen(DYN_LINKER) + 1;
        shdr.sh_link = 0;
        shdr.sh_info = 0;
        shdr.sh_addralign = 1;
        shdr.sh_entsize = 0;
        elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
        ofs += dynamic_sections.elf_interp->size;
        sh_name += strlen(".interp") + 1;

        /* .got */
        shdr.sh_name = sh_name;
        shdr.sh_type = 1;
        shdr.sh_flags = 0x3;
        shdr.sh_addr = dynamic_sections.elf_got_start;
        shdr.sh_offset = ofs;
        shdr.sh_size = dynamic_sections.elf_got->size;
        shdr.sh_link = 0;
        shdr.sh_info = 0;
        shdr.sh_addralign = 4;
        shdr.sh_entsize = PTR_SIZE;
        elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
        ofs += dynamic_sections.elf_got->size;
        sh_name += strlen(".got") + 1;

        /* .dynstr */
        shdr.sh_name = sh_name;
        shdr.sh_type = 3;
        shdr.sh_flags = 0x2;
        shdr.sh_addr =
            dynamic_sections.elf_got_start + dynamic_sections.elf_got->size;
        shdr.sh_offset = ofs;
        shdr.sh_size = dynamic_sections.elf_dynstr->size;
        shdr.sh_link = 0;
        shdr.sh_info = 0;
        shdr.sh_addralign = 1;
        shdr.sh_entsize = 0;
        elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
        ofs += dynamic_sections.elf_dynstr->size;
        sh_name += strlen(".dynstr") + 1;

        /* .dynsym */
        shdr.sh_name = sh_name;
        shdr.sh_type = 11;
        shdr.sh_flags = 0x2;
        shdr.sh_addr = dynamic_sections.elf_got_start +
                       dynamic_sections.elf_got->size +
                       dynamic_sections.elf_dynstr->size;
        shdr.sh_offset = ofs;
        shdr.sh_size = dynamic_sections.elf_dynsym->size;
        shdr.sh_link = 7; /* The section header index of .dynstr. */
        shdr.sh_info = 1; /* The index of the first non-local symbol. */
        shdr.sh_addralign = 4;
        shdr.sh_entsize = sizeof(elf32_sym_t);
        elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
        ofs += dynamic_sections.elf_dynsym->size;
        sh_name += strlen(".dynsym") + 1;

        /* .dynamic */
        shdr.sh_name = sh_name;
        shdr.sh_type = 6;
        shdr.sh_flags = 0x3;
        shdr.sh_addr = dynamic_sections.elf_got_start +
                       dynamic_sections.elf_got->size +
                       dynamic_sections.elf_dynstr->size +
                       dynamic_sections.elf_dynsym->size;
        shdr.sh_offset = ofs;
        shdr.sh_size = dynamic_sections.elf_dynamic->size;
        shdr.sh_link = 7; /* The section header index of .dynstr. */
        shdr.sh_info = 0;
        shdr.sh_addralign = 4;
        shdr.sh_entsize = 0;
        elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
        ofs += dynamic_sections.elf_dynamic->size;
        sh_name += strlen(".dynamic") + 1;
    }

    /* .data */
    shdr.sh_name = sh_name;
    shdr.sh_type = 1;
    shdr.sh_flags = 3;
    shdr.sh_addr = elf_data_start;
    shdr.sh_offset = ofs;
    shdr.sh_size = elf_data->size;
    shdr.sh_link = 0;
    shdr.sh_info = 0;
    shdr.sh_addralign = 4;
    shdr.sh_entsize = 0;
    elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
    ofs += elf_data->size;
    sh_name += strlen(".data") + 1;

    /* .bss */
    shdr.sh_name = sh_name; /* Offset in shstrtab for ".bss" */
    shdr.sh_type = 8;       /* SHT_NOBITS */
    shdr.sh_flags = 3;      /* SHF_ALLOC | SHF_WRITE */
    shdr.sh_addr = elf_bss_start;
    shdr.sh_offset = ofs; /* File offset (not actually used for NOBITS) */
    shdr.sh_size = elf_bss_size;
    shdr.sh_link = 0;
    shdr.sh_info = 0;
    shdr.sh_addralign = 4;
    shdr.sh_entsize = 0;
    elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
    sh_name += strlen(".bss") + 1;
    /* Note: .bss is not written to file (SHT_NOBITS) */

    /* .symtab */
    shdr.sh_name = sh_name;
    shdr.sh_type = 2;
    shdr.sh_flags = 0;
    shdr.sh_addr = 0;
    shdr.sh_offset = ofs;
    shdr.sh_size = elf_symtab->size;
    shdr.sh_link = dynlink ? 13 : 6; /* Link to .strtab */
    shdr.sh_info = elf_symbol_index;
    shdr.sh_addralign = 4;
    shdr.sh_entsize = 16;
    elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
    ofs += elf_symtab->size;
    sh_name += strlen(".symtab") + 1;

    /* .strtab */
    shdr.sh_name = sh_name;
    shdr.sh_type = 3;
    shdr.sh_flags = 0;
    shdr.sh_addr = 0;
    shdr.sh_offset = ofs;
    shdr.sh_size = elf_strtab->size;
    shdr.sh_link = 0;
    shdr.sh_info = 0;
    shdr.sh_addralign = 1;
    shdr.sh_entsize = 0;
    elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
    ofs += elf_strtab->size;
    sh_name += strlen(".strtab") + 1;

    /* .shstr */
    shdr.sh_name = sh_name;
    shdr.sh_type = 3;
    shdr.sh_flags = 0;
    shdr.sh_addr = 0;
    shdr.sh_offset = ofs;
    shdr.sh_size = elf_shstrtab->size;
    shdr.sh_link = 0;
    shdr.sh_info = 0;
    shdr.sh_addralign = 1;
    shdr.sh_entsize = 0;
    elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
    sh_name += strlen(".shstrtab") + 1;
}

void elf_align(strbuf_t *elf_array)
{
    /* Check for null pointers to prevent crashes */
    if (!elf_array) {
        fatal("ELF buffers not initialized for alignment");
        return;
    }

    while (elf_array->size & 3)
        elf_write_byte(elf_array, 0);
}

void elf_generate_sections(void)
{
    if (!elf_shstrtab ||
        (dynlink &&
         (!dynamic_sections.elf_interp || !dynamic_sections.elf_relplt ||
          !dynamic_sections.elf_plt || !dynamic_sections.elf_got ||
          !dynamic_sections.elf_dynstr || !dynamic_sections.elf_dynsym ||
          !dynamic_sections.elf_dynamic))) {
        fatal("ELF section buffers not initialized");
        return;
    }

    if (dynlink) {
        /* In dynamic linking mode, elf_generate_sections() also generates
         * .interp, .dynsym, .dynstr, .relplt, .got and dynamic sections.
         *
         * .plt section is generated at the code generation phase.
         *
         * TODO:
         * Define a new structure named 'elf32_rela_t' and use it to generate
         * relocation entries for RISC-V architecture.
         */
        elf32_sym_t sym;
        elf32_dyn_t dyn;
        elf32_rel_t rel;
        int dymsym_idx = 1, func_plt_ofs, func_got_ofs, st_name = 0;
        memset(&sym, 0, sizeof(elf32_sym_t));
        memset(&dyn, 0, sizeof(elf32_dyn_t));
        memset(&rel, 0, sizeof(elf32_rel_t));

        /* .interp section */
        elf_write_str(dynamic_sections.elf_interp, DYN_LINKER);
        elf_write_byte(dynamic_sections.elf_interp, 0);
        elf_align(dynamic_sections.elf_interp);

        /* Add first symbol table entry (STN_UNDEF) to .dynsym section. */
        elf_write_blk(dynamic_sections.elf_dynsym, &sym, sizeof(elf32_sym_t));

        /* Add first NULL byte to .dynstr section.  */
        elf_write_byte(dynamic_sections.elf_dynstr, 0);
        st_name += 1;

        /* Add "libc.so.6" to .dynstr section. */
        elf_write_str(dynamic_sections.elf_dynstr, LIBC_SO);
        elf_write_byte(dynamic_sections.elf_dynstr, 0);
        st_name += strlen(LIBC_SO) + 1;

        /* Perform the following steps for each external function.
         * - Add a new PLT relocation entry to .relplt section.
         * - Add a new dynamic symbol entry to .dynsym section.
         * - Append the external function name to .dynstr section.
         * - Set plt_offset and got_offset for the external function.
         *
         * Since __libc_start_main is not added to the function list,
         * it must be handled additionally first.
         */
        rel.r_offset = dynamic_sections.elf_got_start + PTR_SIZE * 3;
        rel.r_info = (dymsym_idx << 8) | R_ARCH_JUMP_SLOT;
        elf_write_blk(dynamic_sections.elf_relplt, &rel, sizeof(elf32_rel_t));

        sym.st_name = st_name;
        sym.st_info = ELF32_ST_INFO(1, 2); /* STB_GLOBAL = 1, STT_FUNC = 2 */
        elf_write_blk(dynamic_sections.elf_dynsym, &sym, sizeof(elf32_sym_t));
        dymsym_idx += 1;

        elf_write_str(dynamic_sections.elf_dynstr, "__libc_start_main");
        elf_write_byte(dynamic_sections.elf_dynstr, 0);
        st_name += strlen("__libc_start_main") + 1;

        /* Because PLT[1] and GOT[3] are reserved for __libc_start_main,
         * its plt_offset and got_offset must be PLT_FIXUP_SIZE and
         * PTR_SIZE * 3, respectively. Therefore, no offset assignment is
         * required for this function.
         */

        func_plt_ofs = PLT_FIXUP_SIZE + PLT_ENT_SIZE;
        func_got_ofs = PTR_SIZE << 2;
        for (func_t *func = FUNC_LIST.head; func; func = func->next) {
            if (func->is_used && !func->bbs) {
                rel.r_offset += PTR_SIZE;
                rel.r_info = (dymsym_idx << 8) | R_ARCH_JUMP_SLOT;
                elf_write_blk(dynamic_sections.elf_relplt, &rel,
                              sizeof(elf32_rel_t));

                sym.st_name = st_name;
                sym.st_info =
                    ELF32_ST_INFO(1, 2); /* STB_GLOBAL = 1, STT_FUNC = 2 */
                elf_write_blk(dynamic_sections.elf_dynsym, &sym,
                              sizeof(elf32_sym_t));
                dymsym_idx += 1;

                elf_write_str(dynamic_sections.elf_dynstr,
                              func->return_def.var_name);
                elf_write_byte(dynamic_sections.elf_dynstr, 0);
                st_name += strlen(func->return_def.var_name) + 1;

                func->
Download .txt
gitextract_2uml3qup/

├── .ci/
│   ├── check-format.sh
│   └── check-newline.sh
├── .clang-format
├── .github/
│   └── workflows/
│       └── main.yml
├── .gitignore
├── AUTHORS
├── COMPLIANCE.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── docs/
│   └── dynamic-linking.md
├── lib/
│   ├── c.c
│   └── c.h
├── mk/
│   ├── arm.mk
│   ├── common.mk
│   └── riscv.mk
├── src/
│   ├── arch-lower.c
│   ├── arm-codegen.c
│   ├── arm.c
│   ├── defs.h
│   ├── elf.c
│   ├── globals.c
│   ├── lexer.c
│   ├── main.c
│   ├── opt-sccp.c
│   ├── parser.c
│   ├── peephole.c
│   ├── preprocessor.c
│   ├── reg-alloc.c
│   ├── riscv-codegen.c
│   ├── riscv.c
│   └── ssa.c
├── tests/
│   ├── arm-abi.sh
│   ├── check-snapshots.sh
│   ├── driver.sh
│   ├── fib.c
│   ├── hello.c
│   ├── snapshots/
│   │   ├── fib-arm-dynamic.json
│   │   ├── fib-arm-static.json
│   │   ├── fib-riscv-static.json
│   │   ├── hello-arm-dynamic.json
│   │   ├── hello-arm-static.json
│   │   └── hello-riscv-static.json
│   └── update-snapshots.sh
└── tools/
    ├── inliner.c
    └── norm-lf.c
Download .txt
SYMBOL INDEX (538 symbols across 22 files)

FILE: lib/c.c
  function isdigit (line 17) | int isdigit(int c)
  function isalpha (line 22) | int isalpha(int c)
  function isalnum (line 27) | int isalnum(int c)
  function isxdigit (line 32) | int isxdigit(int c)
  function isblank (line 37) | int isblank(int c)
  function strlen (line 42) | int strlen(char *str)
  function strcmp (line 58) | int strcmp(char *s1, char *s2)
  function strncmp (line 71) | int strncmp(char *s1, char *s2, int len)
  function memcmp (line 135) | int memcmp(void *s1, void *s2, int n)
  function __str_base10 (line 179) | void __str_base10(char *pb, int val)
  function __str_base8 (line 214) | void __str_base8(char *pb, int val)
  function __str_base16 (line 234) | void __str_base16(char *pb, int val)
  type fmtbuf_t (line 272) | typedef struct {
  function __fmtbuf_write_char (line 278) | void __fmtbuf_write_char(fmtbuf_t *fmtbuf, int val)
  function __fmtbuf_write_str (line 294) | void __fmtbuf_write_str(fmtbuf_t *fmtbuf, char *str, int l)
  function __format (line 314) | void __format(fmtbuf_t *fmtbuf,
  function __format_to_buf (line 392) | void __format_to_buf(fmtbuf_t *fmtbuf, char *format, int *var_args)
  function printf (line 459) | int printf(char *str, ...)
  function sprintf (line 471) | int sprintf(char *buffer, char *str, ...)
  function snprintf (line 482) | int snprintf(char *buffer, int n, char *str, ...)
  function exit (line 495) | void exit(int exit_code)
  function abort (line 501) | void abort(void)
  function FILE (line 507) | FILE *fopen(char *filename, char *mode)
  function fclose (line 527) | int fclose(FILE *stream)
  function fgetc (line 536) | int fgetc(FILE *stream)
  function fputc (line 569) | int fputc(int c, FILE *stream)
  function fseek (line 576) | int fseek(FILE *stream, int offset, int whence)
  function ftell (line 590) | int ftell(FILE *stream)
  type chunk_t (line 608) | typedef struct chunk {
  function chunk_set_freed (line 613) | void chunk_set_freed(chunk_t *chunk)
  function chunk_clear_freed (line 618) | void chunk_clear_freed(chunk_t *chunk)
  function __align_up (line 623) | int __align_up(int size)
  function __rfree (line 730) | void __rfree(void *ptr, int size)
  function __free_all (line 737) | int __free_all(void)
  function free (line 771) | void free(void *ptr)

FILE: lib/c.h
  type FILE (line 69) | typedef int FILE;

FILE: src/arch-lower.c
  function arm_lower (line 17) | void arm_lower(void)
  function riscv_lower (line 41) | void riscv_lower(void)
  function arch_lower (line 60) | void arch_lower(void)

FILE: src/arm-codegen.c
  function update_elf_offset (line 14) | void update_elf_offset(ph2_ir_t *ph2_ir)
  function cfg_flatten (line 159) | void cfg_flatten(void)
  function emit (line 252) | void emit(int code)
  function emit_ph2_ir (line 257) | void emit_ph2_ir(ph2_ir_t *ph2_ir)
  function code_generate (line 607) | void code_generate(void)
  function plt_generate (line 776) | void plt_generate(void)

FILE: src/arm.c
  type arm_op_t (line 32) | typedef enum {
  type arm_cond_t (line 51) | typedef enum {
  type arm_reg (line 65) | typedef enum {
  type shift_type (line 84) | typedef enum {
  function arm_cond_t (line 91) | arm_cond_t arm_get_cond(opcode_t op)
  function arm_extract_bits (line 112) | int arm_extract_bits(int imm, int i_start, int i_end, int d_start, int d...
  function arm_encode (line 124) | int arm_encode(arm_cond_t cond, int opcode, int rn, int rd, int op2)
  function __svc (line 129) | int __svc(void)
  function __mov (line 134) | int __mov(arm_cond_t cond, int io, int opcode, int s, int rn, int rd, in...
  function __and_r (line 152) | int __and_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg rm)
  function __or_r (line 157) | int __or_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg rm)
  function __eor_r (line 162) | int __eor_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg rm)
  function __mvn_r (line 167) | int __mvn_r(arm_cond_t cond, arm_reg rd, arm_reg rm)
  function __movw (line 172) | int __movw(arm_cond_t cond, arm_reg rd, int imm)
  function __movt (line 179) | int __movt(arm_cond_t cond, arm_reg rd, int imm)
  function __mov_i (line 187) | int __mov_i(arm_cond_t cond, arm_reg rd, int imm)
  function __mov_r (line 192) | int __mov_r(arm_cond_t cond, arm_reg rd, arm_reg rs)
  function __srl (line 197) | int __srl(arm_cond_t cond, arm_reg rd, arm_reg rm, arm_reg rs)
  function __srl_amt (line 203) | int __srl_amt(arm_cond_t cond,
  function __sll (line 214) | int __sll(arm_cond_t cond, arm_reg rd, arm_reg rm, arm_reg rs)
  function __sll_amt (line 220) | int __sll_amt(arm_cond_t cond,
  function __sra (line 231) | int __sra(arm_cond_t cond, arm_reg rd, arm_reg rm, arm_reg rs)
  function __add_i (line 241) | int __add_i(arm_cond_t cond, arm_reg rd, arm_reg rs, int imm)
  function __add_r (line 248) | int __add_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg ro)
  function __sub_r (line 253) | int __sub_r(arm_cond_t cond, arm_reg rd, arm_reg rs, arm_reg ro)
  function __and_i (line 258) | int __and_i(arm_cond_t cond, arm_reg rd, arm_reg rs, int imm)
  function __zero (line 263) | int __zero(int rd)
  function arm_halfword_transfer (line 274) | int arm_halfword_transfer(arm_cond_t cond,
  function arm_transfer (line 301) | int arm_transfer(arm_cond_t cond,
  function __lw (line 318) | int __lw(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs)
  function __lb (line 323) | int __lb(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs)
  function __sw (line 328) | int __sw(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs)
  function __sb (line 333) | int __sb(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs)
  function __lh (line 339) | int __lh(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs)
  function __sh (line 345) | int __sh(arm_cond_t cond, arm_reg rd, arm_reg rn, int ofs)
  function __stmdb (line 350) | int __stmdb(arm_cond_t cond, int w, arm_reg rn, int reg_list)
  function __ldm (line 355) | int __ldm(arm_cond_t cond, int w, arm_reg rn, int reg_list)
  function __push_reg (line 360) | int __push_reg(arm_cond_t cond, arm_reg rt)
  function __pop_word (line 365) | int __pop_word(arm_cond_t cond, arm_reg rt)
  function __b (line 370) | int __b(arm_cond_t cond, int ofs)
  function __bl (line 376) | int __bl(arm_cond_t cond, int ofs)
  function __bx (line 382) | int __bx(arm_cond_t cond, arm_reg rm)
  function __blx (line 388) | int __blx(arm_cond_t cond, arm_reg rd)
  function __mul (line 393) | int __mul(arm_cond_t cond, arm_reg rd, arm_reg r1, arm_reg r2)
  function __div (line 398) | int __div(arm_cond_t cond, arm_reg rd, arm_reg r1, arm_reg r2)
  function __rsb_i (line 403) | int __rsb_i(arm_cond_t cond, arm_reg rd, int imm, arm_reg rn)
  function __cmp_r (line 408) | int __cmp_r(arm_cond_t cond, arm_reg r1, arm_reg r2)
  function __cmp_i (line 413) | int __cmp_i(arm_cond_t cond, arm_reg rn, int imm)
  function __teq (line 418) | int __teq(arm_reg rd)
  function __sxtb (line 423) | int __sxtb(arm_cond_t cond, arm_reg rd, arm_reg rm, int rotation)
  function __sxth (line 432) | int __sxth(arm_cond_t cond, arm_reg rd, arm_reg rm, int rotation)

FILE: src/defs.h
  type arena_block_t (line 105) | typedef struct arena_block {
  type arena_t (line 112) | typedef struct {
  type hashmap_node_t (line 120) | typedef struct hashmap_node {
  type hashmap_t (line 126) | typedef struct {
  type token_kind_t (line 133) | typedef enum {
  type source_location_t (line 226) | typedef struct {
  type token_t (line 234) | typedef struct token {
  type token_stream_t (line 241) | typedef struct token_stream {
  type string_pool_t (line 247) | typedef struct {
  type string_literal_pool_t (line 252) | typedef struct {
  type base_type_t (line 257) | typedef enum {
  type opcode_t (line 268) | typedef enum {
  type rename_t (line 343) | typedef struct {
  type ref_block_t (line 349) | typedef struct ref_block ref_block_t;
  type ref_block_list (line 351) | struct ref_block_list {
  type ref_block_list_t (line 355) | typedef struct ref_block_list ref_block_list_t;
  type insn_t (line 357) | typedef struct insn insn_t;
  type use_chain_t (line 359) | typedef struct use_chain_node {
  type var_t (line 364) | typedef struct var var_t;
  type type_t (line 365) | typedef struct type type_t;
  type var_list_t (line 367) | typedef struct var_list {
  type var (line 373) | struct var {
  type func_t (line 419) | typedef struct func func_t;
  type block (line 422) | struct block {
  type block_t (line 429) | typedef struct block block_t;
  type basic_block_t (line 430) | typedef struct basic_block basic_block_t;
  type strbuf_t (line 437) | typedef struct {
  type ph2_ir (line 444) | struct ph2_ir {
  type ph2_ir_t (line 467) | typedef struct ph2_ir ph2_ir_t;
  type type (line 470) | struct type {
  type lvalue_t (line 481) | typedef struct {
  type constant_t (line 490) | typedef struct {
  type phi_operand (line 495) | struct phi_operand {
  type phi_operand_t (line 501) | typedef struct phi_operand phi_operand_t;
  type insn (line 503) | struct insn {
  type insn_list_t (line 517) | typedef struct {
  type ph2_ir_list_t (line 521) | typedef struct {
  type bb_connection_type_t (line 525) | typedef enum { NEXT, ELSE, THEN } bb_connection_type_t;
  type bb_connection_t (line 527) | typedef struct {
  type symbol (line 532) | struct symbol {
  type symbol_t (line 538) | typedef struct symbol symbol_t;
  type symbol_list_t (line 540) | typedef struct {
  type basic_block (line 544) | struct basic_block {
  type ref_block (line 579) | struct ref_block {
  type label_t (line 589) | typedef struct {
  type func (line 595) | struct func {
  type func_list_t (line 617) | typedef struct {
  type bb_traversal_args_t (line 621) | typedef struct {
  type regfile_t (line 628) | typedef struct {
  type elf32_hdr_t (line 634) | typedef struct {
  type elf32_phdr_t (line 652) | typedef struct {
  type elf32_shdr_t (line 664) | typedef struct {
  type dynamic_sections_t (line 679) | typedef struct {
  type elf32_sym_t (line 697) | typedef struct {
  type elf32_rel_t (line 707) | typedef struct {
  type elf32_dyn_t (line 713) | typedef struct {

FILE: src/elf.c
  function elf_write_str (line 20) | void elf_write_str(strbuf_t *elf_array, const char *vals)
  function elf_write_byte (line 32) | void elf_write_byte(strbuf_t *elf_array, int val)
  function e_extract_byte (line 39) | char e_extract_byte(int v, int b)
  function elf_write_int (line 44) | void elf_write_int(strbuf_t *elf_array, int val)
  function elf_write_blk (line 52) | void elf_write_blk(strbuf_t *elf_array, void *blk, int sz)
  function elf_generate_header (line 61) | void elf_generate_header(void)
  function elf_generate_program_headers (line 188) | void elf_generate_program_headers(void)
  function elf_generate_section_headers (line 309) | void elf_generate_section_headers(void)
  function elf_align (line 586) | void elf_align(strbuf_t *elf_array)
  function elf_generate_sections (line 598) | void elf_generate_sections(void)
  function elf_add_symbol (line 819) | void elf_add_symbol(const char *symbol, int pc)
  function elf_preprocess (line 837) | void elf_preprocess(void)
  function elf_postprocess (line 917) | void elf_postprocess(void)
  function elf_generate (line 924) | void elf_generate(const char *outfile)

FILE: src/globals.c
  function arena_block_t (line 110) | arena_block_t *arena_block_create(int capacity)
  function arena_block_free (line 136) | void arena_block_free(arena_block_t *block)
  function arena_t (line 147) | arena_t *arena_init(int initial_capacity)
  function func_t (line 308) | func_t *arena_alloc_func(void)
  function symbol_t (line 313) | symbol_t *arena_alloc_symbol(void)
  function constant_t (line 318) | constant_t *arena_alloc_constant(void)
  function bb_traversal_args_t (line 327) | bb_traversal_args_t *arena_alloc_traversal_args(void)
  function arena_free (line 333) | void arena_free(arena_t *arena)
  function hashmap_hash_index (line 356) | int hashmap_hash_index(int size, char *key)
  function round_up_pow2 (line 372) | int round_up_pow2(int v)
  function hashmap_t (line 391) | hashmap_t *hashmap_create(int cap)
  function hashmap_rehash (line 414) | void hashmap_rehash(hashmap_t *map)
  function hashmap_put (line 467) | void hashmap_put(hashmap_t *map, char *key, void *val)
  function hashmap_node_t (line 505) | hashmap_node_t *hashmap_get_node(hashmap_t *map, char *key)
  function hashmap_contains (line 545) | bool hashmap_contains(hashmap_t *map, char *key)
  function hashmap_free (line 553) | void hashmap_free(hashmap_t *map)
  function type_t (line 571) | type_t *find_type(char *type_name, int flag)
  function ph2_ir_t (line 596) | ph2_ir_t *add_existed_ph2_ir(ph2_ir_t *ph2_ir)
  function ph2_ir_t (line 602) | ph2_ir_t *add_ph2_ir(opcode_t op)
  function set_var_liveout (line 620) | void set_var_liveout(var_t *var, int end)
  function block_t (line 627) | block_t *add_block(block_t *parent, func_t *func)
  function hex_digit_value (line 676) | int hex_digit_value(char c)
  function unescape_string (line 687) | int unescape_string(const char *input, char *output, int output_size)
  function parse_numeric_constant (line 808) | int parse_numeric_constant(char *buffer)
  function type_t (line 845) | type_t *add_type(void)
  function type_t (line 854) | type_t *add_named_type(char *name)
  function add_constant (line 862) | void add_constant(char alias[], int value)
  function constant_t (line 876) | constant_t *find_constant(char alias[])
  function var_t (line 881) | var_t *find_member(char token[], type_t *type)
  function var_t (line 896) | var_t *find_local_var(char *token, block_t *block)
  function var_t (line 917) | var_t *find_global_var(char *token)
  function var_t (line 928) | var_t *find_var(char *token, block_t *parent)
  function size_var (line 936) | int size_var(var_t *var)
  function func_t (line 966) | func_t *add_func(char *func_name, bool synthesize)
  function func_t (line 1024) | func_t *find_func(char *func_name)
  function basic_block_t (line 1030) | basic_block_t *bb_create(block_t *parent)
  function bb_connect (line 1053) | void bb_connect(basic_block_t *pred,
  function bb_disconnect (line 1090) | void bb_disconnect(basic_block_t *pred, basic_block_t *succ)
  function add_symbol (line 1115) | void add_symbol(basic_block_t *bb, var_t *var)
  function add_insn (line 1139) | void add_insn(block_t *block,
  function strbuf_t (line 1188) | strbuf_t *strbuf_create(int init_capacity)
  function strbuf_extend (line 1205) | bool strbuf_extend(strbuf_t *src, int len)
  function strbuf_putc (line 1230) | bool strbuf_putc(strbuf_t *src, char value)
  function strbuf_puts (line 1241) | bool strbuf_puts(strbuf_t *src, const char *value)
  function strbuf_free (line 1254) | void strbuf_free(strbuf_t *src)
  function global_init (line 1266) | void global_init(void)
  function arena_free_trailing_blocks (line 1330) | int arena_free_trailing_blocks(arena_t *arena)
  function compact_all_arenas (line 1371) | int compact_all_arenas(void)
  function compact_arenas_selective (line 1393) | int compact_arenas_selective(int phase_mask)
  function global_release (line 1415) | void global_release(void)
  function fatal (line 1458) | void fatal(char *msg)
  function error_at (line 1468) | void error_at(char *msg, source_location_t *loc)
  function print_indent (line 1521) | void print_indent(int indent)
  function dump_bb_insn (line 1527) | void dump_bb_insn(func_t *func, basic_block_t *bb, bool *at_func_start)
  function dump_bb_insn_by_dom (line 1757) | void dump_bb_insn_by_dom(func_t *func, basic_block_t *bb, bool *at_func_...
  function dump_insn (line 1767) | void dump_insn(void)

FILE: src/lexer.c
  type token_mapping_t (line 18) | typedef struct {
  function lex_init_directives (line 31) | void lex_init_directives()
  function lex_init_keywords (line 60) | void lex_init_keywords()
  function token_kind_t (line 101) | token_kind_t lookup_directive(char *token)
  function token_kind_t (line 114) | token_kind_t lookup_keyword(char *token)
  function lexer_cleanup (line 128) | void lexer_cleanup()
  function peek_char (line 148) | char peek_char(strbuf_t *buf, int offset)
  function read_char (line 155) | char read_char(strbuf_t *buf)
  function strbuf_t (line 163) | strbuf_t *read_file(char *filename)
  function strbuf_t (line 187) | strbuf_t *get_file_buf(char *filename)
  function token_t (line 201) | token_t *new_token(token_kind_t kind, source_location_t *loc, int len)
  function token_t (line 210) | token_t *lex_token(strbuf_t *buf, source_location_t *loc)
  function token_stream_t (line 923) | token_stream_t *gen_file_token_stream(char *filename)
  function token_stream_t (line 976) | token_stream_t *gen_libc_token_stream()
  function skip_unused_token (line 1024) | void skip_unused_token(void)
  function source_location_t (line 1037) | source_location_t *cur_token_loc()
  function source_location_t (line 1045) | source_location_t *next_token_loc()
  function token_kind_t (line 1056) | token_kind_t lex_next(void)
  function lex_accept (line 1068) | bool lex_accept(token_kind_t kind)
  function lex_peek (line 1081) | bool lex_peek(token_kind_t kind, char *value)
  function lex_ident (line 1096) | void lex_ident(token_kind_t token, char *value)
  function lex_expect (line 1111) | void lex_expect(token_kind_t token)

FILE: src/main.c
  function main (line 52) | int main(int argc, char *argv[])

FILE: src/opt-sccp.c
  function simple_sccp (line 18) | bool simple_sccp(func_t *func)
  function optimize_constant_casts (line 202) | bool optimize_constant_casts(func_t *func)

FILE: src/parser.c
  function label_t (line 51) | label_t *find_label(char *name)
  function add_label (line 60) | void add_label(char *name, basic_block_t *bb)
  function var_t (line 76) | var_t *require_var(block_t *blk)
  function var_t (line 105) | var_t *require_typed_var(block_t *blk, type_t *type)
  function var_t (line 115) | var_t *require_typed_ptr_var(block_t *blk, type_t *type, int ptr)
  function var_t (line 122) | var_t *require_ref_var(block_t *blk, type_t *type, int ptr)
  function var_t (line 132) | var_t *require_deref_var(block_t *blk, type_t *type, int ptr)
  function opstack_push (line 151) | void opstack_push(var_t *var)
  function var_t (line 156) | var_t *opstack_pop(void)
  function write_symbol (line 163) | int write_symbol(const char *data)
  function get_size (line 172) | int get_size(var_t *var)
  function get_operator_prio (line 179) | int get_operator_prio(opcode_t op)
  function get_unary_operator_prio (line 218) | int get_unary_operator_prio(opcode_t op)
  function opcode_t (line 231) | opcode_t get_operator(void)
  function var_t (line 275) | var_t *promote_unchecked(block_t *block,
  function var_t (line 298) | var_t *promote(block_t *block,
  function var_t (line 317) | var_t *truncate_unchecked(block_t *block,
  function var_t (line 330) | var_t *resize_var(block_t *block, basic_block_t **bb, var_t *from, var_t...
  function var_t (line 362) | var_t *compute_element_address(block_t *parent,
  function var_t (line 382) | var_t *compute_field_address(block_t *parent,
  function var_t (line 401) | var_t *parse_global_constant_value(block_t *parent, basic_block_t **bb)
  function consume_global_constant_syntax (line 441) | void consume_global_constant_syntax(void)
  function parse_struct_field_init (line 458) | void parse_struct_field_init(block_t *parent,
  function parse_array_literal_expr (line 508) | void parse_array_literal_expr(block_t *parent, basic_block_t **bb)
  function basic_block_t (line 549) | basic_block_t *handle_return_statement(block_t *parent, basic_block_t *bb)
  function basic_block_t (line 581) | basic_block_t *handle_if_statement(block_t *parent, basic_block_t *bb)
  function basic_block_t (line 631) | basic_block_t *handle_while_statement(block_t *parent, basic_block_t *bb)
  function basic_block_t (line 664) | basic_block_t *handle_goto_statement(block_t *parent, basic_block_t *bb)
  function basic_block_t (line 719) | basic_block_t *handle_struct_variable_decl(block_t *parent,
  function parse_array_init (line 805) | void parse_array_init(var_t *var,
  function parse_array_compound_literal (line 969) | void parse_array_compound_literal(var_t *var,
  function is_array_literal_placeholder (line 1009) | bool is_array_literal_placeholder(var_t *var)
  function is_pointer_like_value (line 1015) | bool is_pointer_like_value(var_t *var)
  function var_t (line 1026) | var_t *scalarize_array_literal(block_t *parent,
  function var_t (line 1066) | var_t *scalarize_array_literal_if_needed(block_t *parent,
  function read_inner_var_decl (line 1078) | void read_inner_var_decl(var_t *vd, bool anon, bool is_param)
  function read_full_var_decl (line 1182) | void read_full_var_decl(var_t *vd, bool anon, bool is_param)
  function read_partial_var_decl (line 1204) | void read_partial_var_decl(var_t *vd, var_t *template)
  function read_parameter_list_decl (line 1209) | void read_parameter_list_decl(func_t *func, bool anon)
  function read_literal_param (line 1249) | void read_literal_param(block_t *parent, basic_block_t *bb)
  function read_numeric_param (line 1282) | void read_numeric_param(block_t *parent, basic_block_t *bb, bool is_neg)
  function read_char_param (line 1347) | void read_char_param(block_t *parent, basic_block_t *bb)
  function read_func_parameters (line 1362) | void read_func_parameters(func_t *func, block_t *parent, basic_block_t *...
  function read_func_call (line 1406) | void read_func_call(func_t *func, block_t *parent, basic_block_t **bb)
  function read_indirect_call (line 1415) | void read_indirect_call(block_t *parent, basic_block_t **bb)
  function handle_address_of_operator (line 1433) | void handle_address_of_operator(block_t *parent, basic_block_t **bb)
  function handle_single_dereference (line 1452) | void handle_single_dereference(block_t *parent, basic_block_t **bb)
  function handle_multiple_dereference (line 1523) | void handle_multiple_dereference(block_t *parent, basic_block_t **bb)
  function handle_sizeof_operator (line 1607) | void handle_sizeof_operator(block_t *parent, basic_block_t **bb)
  function read_expr_operand (line 1653) | void read_expr_operand(block_t *parent, basic_block_t **bb)
  function is_logical (line 2103) | bool is_logical(opcode_t op)
  function get_pointer_element_size (line 2109) | int get_pointer_element_size(var_t *ptr_var)
  function handle_pointer_difference (line 2156) | void handle_pointer_difference(block_t *parent,
  function handle_pointer_arithmetic (line 2189) | void handle_pointer_arithmetic(block_t *parent,
  function is_pointer_operation (line 2354) | bool is_pointer_operation(opcode_t op, var_t *rs1, var_t *rs2)
  function is_pointer_var (line 2364) | bool is_pointer_var(var_t *v, block_t *parent)
  function read_expr (line 2383) | void read_expr(block_t *parent, basic_block_t **bb)
  function read_lvalue (line 2684) | void read_lvalue(lvalue_t *lvalue,
  function read_logical (line 3040) | void read_logical(opcode_t op, block_t *parent, basic_block_t **bb)
  function finalize_logical (line 3061) | void finalize_logical(opcode_t op,
  function read_ternary_operation (line 3198) | void read_ternary_operation(block_t *parent, basic_block_t **bb)
  function read_body_assignment (line 3275) | bool read_body_assignment(char *token,
  function read_primary_constant (line 3474) | int read_primary_constant(void)
  function eval_expression_imm (line 3499) | int eval_expression_imm(opcode_t op, int op1, int op2)
  function eval_ternary_imm (line 3563) | void eval_ternary_imm(int cond, char *token)
  function read_global_assignment (line 3580) | bool read_global_assignment(char *token)
  function perform_side_effect (line 3737) | void perform_side_effect(block_t *parent, basic_block_t *bb)
  function basic_block_t (line 3751) | basic_block_t *read_body_statement(block_t *parent, basic_block_t *bb)
  function basic_block_t (line 4598) | basic_block_t *read_code_block(func_t *func, block_t *parent, basic_bloc...
  function read_func_body (line 4615) | void read_func_body(func_t *func)
  function print_ptr_level (line 4654) | void print_ptr_level(int level)
  function print_func_decl (line 4662) | void print_func_decl(func_t *func, const char *prefix, bool newline)
  function read_global_decl (line 4698) | void read_global_decl(block_t *block, bool is_const)
  function consume_global_compound_literal (line 4811) | void consume_global_compound_literal(void)
  function initialize_struct_field (line 4842) | void initialize_struct_field(var_t *nv, var_t *v, int offset)
  function read_global_statement (line 4861) | void read_global_statement(void)
  function parse_internal (line 5189) | void parse_internal(void)
  function parse (line 5265) | void parse(token_t *tk)

FILE: src/peephole.c
  function is_fusible_insn (line 16) | bool is_fusible_insn(ph2_ir_t *ph2_ir)
  function insn_fusion (line 47) | bool insn_fusion(ph2_ir_t *ph2_ir)
  function redundant_move_elim (line 248) | bool redundant_move_elim(ph2_ir_t *ph2_ir)
  function eliminate_load_store_pairs (line 356) | bool eliminate_load_store_pairs(ph2_ir_t *ph2_ir)
  function algebraic_simplification (line 464) | bool algebraic_simplification(ph2_ir_t *ph2_ir)
  function strength_reduction (line 531) | bool strength_reduction(ph2_ir_t *ph2_ir)
  function comparison_optimization (line 602) | bool comparison_optimization(ph2_ir_t *ph2_ir)
  function bitwise_optimization (line 677) | bool bitwise_optimization(ph2_ir_t *ph2_ir)
  function triple_pattern_optimization (line 780) | bool triple_pattern_optimization(ph2_ir_t *ph2_ir)
  function peephole (line 877) | void peephole(void)

FILE: src/preprocessor.c
  function token_t (line 15) | token_t *pp_lex_skip_space(token_t *tk)
  function token_t (line 23) | token_t *pp_lex_next_token(token_t *tk, bool skip_space)
  function pp_lex_peek_token (line 30) | bool pp_lex_peek_token(token_t *tk, token_kind_t kind, bool skip_space)
  function token_t (line 37) | token_t *pp_lex_expect_token(token_t *tk, token_kind_t kind, bool skip_s...
  function token_t (line 52) | token_t *lex_ident_token(token_t *tk,
  function token_t (line 63) | token_t *copy_token(token_t *tk)
  type macro_t (line 71) | typedef struct macro {
  function is_macro_defined (line 83) | bool is_macro_defined(char *name)
  function token_t (line 93) | token_t *file_macro_handler(token_t *tk)
  function token_t (line 105) | token_t *line_macro_handler(token_t *tk)
  type hide_set_t (line 121) | typedef struct hide_set {
  function hide_set_t (line 126) | hide_set_t *new_hide_set(char *name)
  function hide_set_t (line 134) | hide_set_t *hide_set_union(hide_set_t *hs1, hide_set_t *hs2)
  function hide_set_contains (line 148) | bool hide_set_contains(hide_set_t *hs, char *name)
  function hide_set_free (line 156) | void hide_set_free(hide_set_t *hs)
  type cond_kind_t (line 165) | typedef enum { CK_if_then, CK_elif_then, CK_else_then } cond_kind_t;
  type cond_incl_t (line 171) | typedef struct cond_incl {
  function cond_incl_t (line 178) | cond_incl_t *push_cond(cond_incl_t *ci, token_t *tk, bool included)
  type preprocess_ctx_t (line 197) | typedef struct preprocess_ctx {
  function pp_get_operator_prio (line 208) | int pp_get_operator_prio(opcode_t op)
  function pp_get_unary_operator_prio (line 244) | int pp_get_unary_operator_prio(opcode_t op)
  function token_t (line 257) | token_t *pp_get_operator(token_t *tk, opcode_t *op)
  function token_t (line 332) | token_t *pp_read_constant_expr_operand(token_t *tk, int *val)
  function token_t (line 388) | token_t *pp_read_constant_infix_expr(int precedence, token_t *tk, int *val)
  function token_t (line 494) | token_t *pp_read_constant_expr(token_t *tk, int *val)
  function token_t (line 502) | token_t *pp_skip_inner_cond_incl(token_t *tk)
  function token_t (line 531) | token_t *pp_skip_cond_incl(token_t *tk)
  function token_t (line 552) | token_t *pp_preprocess_internal(token_t *tk, preprocess_ctx_t *ctx)
  function token_t (line 1034) | token_t *preprocess(token_t *tk)
  function emit_preprocessed_token (line 1276) | void emit_preprocessed_token(token_t *tk)

FILE: src/reg-alloc.c
  function vreg_map_to_phys (line 17) | void vreg_map_to_phys(var_t *var, int phys_reg)
  function vreg_get_phys (line 23) | int vreg_get_phys(var_t *var)
  function vreg_clear_phys (line 30) | void vreg_clear_phys(var_t *var)
  function align_size (line 42) | int align_size(int i)
  function check_live_out (line 47) | bool check_live_out(basic_block_t *bb, var_t *var)
  function track_var_use (line 56) | void track_var_use(var_t *var, int insn_idx)
  function refresh (line 69) | void refresh(basic_block_t *bb, insn_t *insn)
  function ph2_ir_t (line 84) | ph2_ir_t *bb_add_ph2_ir(basic_block_t *bb, opcode_t op)
  function calculate_spill_cost (line 113) | int calculate_spill_cost(var_t *var, basic_block_t *bb, int current_idx)
  function find_best_spill (line 162) | int find_best_spill(basic_block_t *bb,
  function spill_var (line 192) | void spill_var(basic_block_t *bb, var_t *var, int idx)
  function find_in_regs (line 216) | int find_in_regs(var_t *var)
  function load_var (line 225) | void load_var(basic_block_t *bb, var_t *var, int idx)
  function prepare_operand (line 246) | int prepare_operand(basic_block_t *bb, var_t *var, int operand_0)
  function prepare_dest (line 290) | int prepare_dest(basic_block_t *bb, var_t *var, int operand_0, int opera...
  function spill_alive (line 338) | void spill_alive(basic_block_t *bb, insn_t *insn)
  function spill_live_out (line 364) | void spill_live_out(basic_block_t *bb)
  function extend_liveness (line 386) | void extend_liveness(basic_block_t *bb, insn_t *insn, var_t *var, int of...
  function abi_lower_call_args (line 395) | bool abi_lower_call_args(basic_block_t *bb, insn_t *insn)
  function reg_alloc (line 421) | void reg_alloc(void)
  function dump_ph2_ir (line 1022) | void dump_ph2_ir(void)

FILE: src/riscv-codegen.c
  function update_elf_offset (line 13) | void update_elf_offset(ph2_ir_t *ph2_ir)
  function cfg_flatten (line 119) | void cfg_flatten(void)
  function emit (line 173) | void emit(int code)
  function emit_ph2_ir (line 178) | void emit_ph2_ir(ph2_ir_t *ph2_ir)
  function code_generate (line 485) | void code_generate(void)

FILE: src/riscv.c
  type rv_op (line 11) | typedef enum {
  type rv_reg (line 66) | typedef enum {
  function rv_extract_bits (line 101) | int rv_extract_bits(int imm, int i_start, int i_end, int d_start, int d_...
  function rv_hi (line 115) | int rv_hi(int val)
  function rv_lo (line 120) | int rv_lo(int val)
  function rv_encode_R (line 125) | int rv_encode_R(rv_op op, rv_reg rd, rv_reg rs1, rv_reg rs2)
  function rv_encode_I (line 130) | int rv_encode_I(rv_op op, rv_reg rd, rv_reg rs1, int imm)
  function rv_encode_S (line 142) | int rv_encode_S(rv_op op, rv_reg rs1, rv_reg rs2, int imm)
  function rv_encode_B (line 155) | int rv_encode_B(rv_op op, rv_reg rs1, rv_reg rs2, int imm)
  function rv_encode_J (line 171) | int rv_encode_J(rv_op op, rv_reg rd, int imm)
  function rv_encode_U (line 185) | int rv_encode_U(rv_op op, rv_reg rd, int imm)
  function __add (line 190) | int __add(rv_reg rd, rv_reg rs1, rv_reg rs2)
  function __sub (line 195) | int __sub(rv_reg rd, rv_reg rs1, rv_reg rs2)
  function __xor (line 200) | int __xor(rv_reg rd, rv_reg rs1, rv_reg rs2)
  function __or (line 205) | int __or(rv_reg rd, rv_reg rs1, rv_reg rs2)
  function __and (line 210) | int __and(rv_reg rd, rv_reg rs1, rv_reg rs2)
  function __sll (line 215) | int __sll(rv_reg rd, rv_reg rs1, rv_reg rs2)
  function __srl (line 220) | int __srl(rv_reg rd, rv_reg rs1, rv_reg rs2)
  function __sra (line 225) | int __sra(rv_reg rd, rv_reg rs1, rv_reg rs2)
  function __slt (line 230) | int __slt(rv_reg rd, rv_reg rs1, rv_reg rs2)
  function __sltu (line 235) | int __sltu(rv_reg rd, rv_reg rs1, rv_reg rs2)
  function __addi (line 240) | int __addi(rv_reg rd, rv_reg rs1, int imm)
  function __xori (line 245) | int __xori(rv_reg rd, rv_reg rs1, int imm)
  function __ori (line 250) | int __ori(rv_reg rd, rv_reg rs1, int imm)
  function __andi (line 255) | int __andi(rv_reg rd, rv_reg rs1, int imm)
  function __slli (line 260) | int __slli(rv_reg rd, rv_reg rs1, int imm)
  function __srli (line 265) | int __srli(rv_reg rd, rv_reg rs1, int imm)
  function __srai (line 270) | int __srai(rv_reg rd, rv_reg rs1, int imm)
  function __slti (line 275) | int __slti(rv_reg rd, rv_reg rs1, int imm)
  function __sltiu (line 280) | int __sltiu(rv_reg rd, rv_reg rs1, int imm)
  function __lb (line 285) | int __lb(rv_reg rd, rv_reg rs1, int imm)
  function __lh (line 290) | int __lh(rv_reg rd, rv_reg rs1, int imm)
  function __lw (line 295) | int __lw(rv_reg rd, rv_reg rs1, int imm)
  function __lbu (line 300) | int __lbu(rv_reg rd, rv_reg rs1, int imm)
  function __lhu (line 305) | int __lhu(rv_reg rd, rv_reg rs1, int imm)
  function __sb (line 310) | int __sb(rv_reg rd, rv_reg rs1, int imm)
  function __sh (line 315) | int __sh(rv_reg rd, rv_reg rs1, int imm)
  function __sw (line 320) | int __sw(rv_reg rd, rv_reg rs1, int imm)
  function __beq (line 325) | int __beq(rv_reg rs1, rv_reg rs2, int imm)
  function __bne (line 330) | int __bne(rv_reg rs1, rv_reg rs2, int imm)
  function __blt (line 335) | int __blt(rv_reg rs1, rv_reg rs2, int imm)
  function __bge (line 340) | int __bge(rv_reg rs1, rv_reg rs2, int imm)
  function __bltu (line 345) | int __bltu(rv_reg rs1, rv_reg rs2, int imm)
  function __bgeu (line 350) | int __bgeu(rv_reg rs1, rv_reg rs2, int imm)
  function __jal (line 355) | int __jal(rv_reg rd, int imm)
  function __jalr (line 360) | int __jalr(rv_reg rd, rv_reg rs1, int imm)
  function __lui (line 365) | int __lui(rv_reg rd, int imm)
  function __auipc (line 370) | int __auipc(rv_reg rd, int imm)
  function __ecall (line 375) | int __ecall(void)
  function __mul (line 381) | int __mul(rv_reg rd, rv_reg rs1, rv_reg rs2)
  function __div (line 386) | int __div(rv_reg rd, rv_reg rs1, rv_reg rs2)
  function __mod (line 391) | int __mod(rv_reg rd, rv_reg rs1, rv_reg rs2)
  function __sext_b (line 396) | int __sext_b(rv_reg rd, rv_reg rs)

FILE: src/ssa.c
  function var_list_ensure_capacity (line 23) | void var_list_ensure_capacity(var_list_t *list, int min_capacity)
  function var_list_add_var (line 42) | void var_list_add_var(var_list_t *list, var_t *var)
  function var_list_assign_array (line 53) | void var_list_assign_array(var_list_t *list, var_t **data, int count)
  function bb_forward_traversal (line 61) | void bb_forward_traversal(bb_traversal_args_t *args)
  function bb_backward_traversal (line 96) | void bb_backward_traversal(bb_traversal_args_t *args)
  function bb_index_rpo (line 120) | void bb_index_rpo(func_t *func, basic_block_t *bb)
  function bb_reverse_index (line 125) | void bb_reverse_index(func_t *func, basic_block_t *bb)
  function bb_build_rpo (line 130) | void bb_build_rpo(func_t *func, basic_block_t *bb)
  function build_rpo (line 151) | void build_rpo(void)
  function basic_block_t (line 176) | basic_block_t *intersect(basic_block_t *i, basic_block_t *j)
  function build_idom (line 198) | void build_idom(void)
  function dom_connect (line 242) | bool dom_connect(basic_block_t *pred, basic_block_t *succ)
  function bb_build_dom (line 263) | void bb_build_dom(func_t *func, basic_block_t *bb)
  function build_dom (line 273) | void build_dom(void)
  function bb_build_df (line 290) | void bb_build_df(func_t *func, basic_block_t *bb)
  function build_df (line 311) | void build_df(void)
  function basic_block_t (line 328) | basic_block_t *reverse_intersect(basic_block_t *i, basic_block_t *j)
  function build_r_idom (line 339) | void build_r_idom(void)
  function rdom_connect (line 380) | bool rdom_connect(basic_block_t *pred, basic_block_t *succ)
  function bb_build_rdom (line 401) | void bb_build_rdom(func_t *func, basic_block_t *bb)
  function build_rdom (line 411) | void build_rdom(void)
  function bb_build_rdf (line 428) | void bb_build_rdf(func_t *func, basic_block_t *bb)
  function build_rdf (line 459) | void build_rdf(void)
  function use_chain_add_tail (line 476) | void use_chain_add_tail(insn_t *i, var_t *var)
  function use_chain_delete (line 489) | void use_chain_delete(use_chain_t *u, var_t *var)
  function use_chain_build (line 505) | void use_chain_build(void)
  function var_check_killed (line 523) | bool var_check_killed(var_t *var, basic_block_t *bb)
  function bb_add_killed_var (line 532) | void bb_add_killed_var(basic_block_t *bb, var_t *var)
  function var_add_killed_bb (line 537) | void var_add_killed_bb(var_t *var, basic_block_t *bb)
  function fn_add_global (line 560) | void fn_add_global(func_t *func, var_t *var)
  function bb_solve_globals (line 586) | void bb_solve_globals(func_t *func, basic_block_t *bb)
  function solve_globals (line 604) | void solve_globals(void)
  function var_check_in_scope (line 621) | bool var_check_in_scope(var_t *var, block_t *block)
  function insert_phi_insn (line 641) | bool insert_phi_insn(basic_block_t *bb, var_t *var)
  function solve_phi_insertion (line 670) | void solve_phi_insertion(void)
  function new_name (line 747) | void new_name(block_t *block, var_t **var)
  function var_t (line 765) | var_t *get_stack_top_subscript_var(var_t *var)
  function rename_var (line 780) | void rename_var(var_t **var)
  function pop_name (line 791) | void pop_name(var_t *var)
  function append_phi_operand (line 798) | void append_phi_operand(insn_t *insn, var_t *var, basic_block_t *bb_from)
  function bb_solve_phi_params (line 813) | void bb_solve_phi_params(basic_block_t *bb)
  function solve_phi_params (line 866) | void solve_phi_params(void)
  function append_unwound_phi_insn (line 892) | void append_unwound_phi_insn(basic_block_t *bb, var_t *dest, var_t *rs)
  function bb_unwind_phi (line 922) | void bb_unwind_phi(func_t *func, basic_block_t *bb)
  function unwind_phi (line 943) | void unwind_phi(void)
  function is_dominate (line 960) | bool is_dominate(basic_block_t *pred, basic_block_t *succ)
  function bb_check_var_cross_init (line 982) | void bb_check_var_cross_init(func_t *func, basic_block_t *bb)
  function check_var_cross_init (line 1019) | void check_var_cross_init()
  function bb_dump_connection (line 1038) | void bb_dump_connection(FILE *fd,
  function bb_dump (line 1128) | void bb_dump(FILE *fd, func_t *func, basic_block_t *bb)
  function dump_cfg (line 1332) | void dump_cfg(char name[])
  function dom_dump (line 1353) | void dom_dump(FILE *fd, basic_block_t *bb)
  function dump_dom (line 1364) | void dump_dom(char name[])
  function ssa_build (line 1386) | void ssa_build(void)
  function is_cse_candidate (line 1412) | bool is_cse_candidate(insn_t *insn)
  function cse (line 1441) | bool cse(insn_t *insn, basic_block_t *bb)
  function mark_const (line 1547) | bool mark_const(insn_t *insn)
  function eval_const_arithmetic (line 1577) | bool eval_const_arithmetic(insn_t *insn)
  function eval_const_unary (line 1662) | bool eval_const_unary(insn_t *insn)
  function const_folding (line 1693) | bool const_folding(insn_t *insn)
  function is_block_unreachable (line 1705) | bool is_block_unreachable(basic_block_t *bb)
  function var_escapes (line 1730) | bool var_escapes(var_t *var)
  function dce_init_mark (line 1749) | int dce_init_mark(insn_t *insn, insn_t *work_list[], int work_list_idx)
  function dce_insn (line 1822) | void dce_insn(basic_block_t *bb)
  function dce_sweep (line 1905) | void dce_sweep(void)
  function optimize (line 1970) | void optimize(void)
  function bb_index_reversed_rpo (line 2446) | void bb_index_reversed_rpo(func_t *func, basic_block_t *bb)
  function bb_reverse_reversed_index (line 2451) | void bb_reverse_reversed_index(func_t *func, basic_block_t *bb)
  function bb_build_reversed_rpo (line 2456) | void bb_build_reversed_rpo(func_t *func, basic_block_t *bb)
  function build_reversed_rpo (line 2477) | void build_reversed_rpo(void)
  function bb_reset_live_kill_idx (line 2503) | void bb_reset_live_kill_idx(func_t *func, basic_block_t *bb)
  function bb_reset_and_solve_locals (line 2513) | void bb_reset_and_solve_locals(func_t *func, basic_block_t *bb)
  function add_live_gen (line 2540) | void add_live_gen(basic_block_t *bb, var_t *var)
  function update_consumed (line 2548) | void update_consumed(insn_t *insn, var_t *var)
  function bb_solve_locals (line 2554) | void bb_solve_locals(func_t *func, basic_block_t *bb)
  function add_live_in (line 2578) | void add_live_in(basic_block_t *bb, var_t *var)
  function compute_live_in (line 2583) | void compute_live_in(basic_block_t *bb)
  function merge_live_in (line 2597) | int merge_live_in(var_t *live_out[], int live_out_idx, basic_block_t *bb)
  function recompute_live_out (line 2648) | bool recompute_live_out(basic_block_t *bb)
  function liveness_analysis (line 2707) | void liveness_analysis(void)

FILE: tests/fib.c
  function fib (line 3) | int fib(int n)
  function main (line 12) | int main()

FILE: tests/hello.c
  function main (line 3) | int main(int argc, char *argv[])

FILE: tools/inliner.c
  type strbuf_t (line 29) | typedef struct {
  function strbuf_t (line 37) | strbuf_t *strbuf_create(int init_capacity)
  function strbuf_extend (line 54) | bool strbuf_extend(strbuf_t *src, int len)
  function strbuf_putc (line 79) | bool strbuf_putc(strbuf_t *src, char value)
  function strbuf_puts (line 90) | bool strbuf_puts(strbuf_t *src, char *value)
  function strbuf_free (line 103) | void strbuf_free(strbuf_t *src)
  function write_line (line 112) | void write_line(char *src)
  function load_from (line 132) | void load_from(char *file)
  function save_to (line 152) | void save_to(char *file)
  function main (line 160) | int main(int argc, char *argv[])

FILE: tools/norm-lf.c
  function main (line 12) | int main(int argc, char *argv[])
Condensed preview — 47 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,840K chars).
[
  {
    "path": ".ci/check-format.sh",
    "chars": 367,
    "preview": "#!/usr/bin/env bash\n\nSOURCES=$(find $(git rev-parse --show-toplevel) | egrep \"\\.(c|cxx|cpp|h|hpp)\\$\")\n\nset -x\n\nfor file "
  },
  {
    "path": ".ci/check-newline.sh",
    "chars": 485,
    "preview": "#!/usr/bin/env bash\n\nret=0\nshow=0\n# Reference: https://medium.com/@alexey.inkin/how-to-force-newline-at-end-of-files-and"
  },
  {
    "path": ".clang-format",
    "chars": 497,
    "preview": "BasedOnStyle: Chromium\nLanguage: Cpp\nMaxEmptyLinesToKeep: 3\nIndentCaseLabels: false\nAllowShortIfStatementsOnASingleLine:"
  },
  {
    "path": ".github/workflows/main.yml",
    "chars": 4313,
    "preview": "name: Github Actions\n\non: [push, pull_request]\n\njobs:\n  host-x86:\n    runs-on: ubuntu-24.04\n    strategy:\n      matrix:\n"
  },
  {
    "path": ".gitignore",
    "chars": 118,
    "preview": "out\na.out\n*.o\n*.elf\n*.log\n*.lst\n*.dot\nconfig\nsrc/codegen.c\n.session.mk\n\n# vscode C/C++ plugin generated files\n.vscode\n"
  },
  {
    "path": "AUTHORS",
    "chars": 514,
    "preview": "shecc is written by:\n  Jim Huang <jserv.tw@gmail.com>\n  Yu-Cheng Cheng <yucheng871011@gmail.com>\n  Lecopzer Chen <lecopz"
  },
  {
    "path": "COMPLIANCE.md",
    "chars": 4728,
    "preview": "# C99 Compliance Status\n\nshecc implements a subset of C99 suitable for self-hosting and systems programming,\nprioritizin"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 35057,
    "preview": "# Contributing to `shecc`\n\n:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:\n\nThe following is "
  },
  {
    "path": "LICENSE",
    "chars": 1337,
    "preview": "Copyright (c) 2020-2021, 2023-2026 National Cheng Kung University, Taiwan.\n\nAll rights reserved.\n\nRedistribution and use"
  },
  {
    "path": "Makefile",
    "chars": 6778,
    "preview": "CFLAGS := -O -g \\\n\t-std=c99 -pedantic\n\nCFLAGS_TO_CHECK := \\\n\t-fwrapv \\\n\t-Wall -Wextra \\\n\t-Wno-unused-but-set-variable \\\n"
  },
  {
    "path": "README.md",
    "chars": 13041,
    "preview": "# shecc : self-hosting and educational C optimizing compiler\n\n<p align=\"center\"><img src=\"https://user-images.githubuser"
  },
  {
    "path": "docs/dynamic-linking.md",
    "chars": 12539,
    "preview": "# Dynamic Linking\n\n## Build dynamically linked shecc and programs\n\nBuild the dynamically linked version of shecc, but no"
  },
  {
    "path": "lib/c.c",
    "chars": 19081,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "lib/c.h",
    "chars": 2498,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "mk/arm.mk",
    "chars": 4067,
    "preview": "# Allow the following machines to use native execution\n#\n# - Beaglebone Black (Cortex-A8)\n# - Raspberry Pi 3 (Cortex-A53"
  },
  {
    "path": "mk/common.mk",
    "chars": 1432,
    "preview": "UNAME_S := $(shell uname -s)\nifeq ($(UNAME_S),Darwin)\n    PRINTF = printf\nelse\n    PRINTF = env printf\nendif\n\n# Control "
  },
  {
    "path": "mk/riscv.mk",
    "chars": 657,
    "preview": "# Enforce the use qemu of by setting the ALLOW_MACHINES variable to empty\nALLOW_MACHINES =\nARCH_RUNNER = qemu-riscv32\nAR"
  },
  {
    "path": "src/arch-lower.c",
    "chars": 2202,
    "preview": "/*\n * shecc - Architecture-specific IR lowering stage\n *\n * Introduces a minimal arch-lowering boundary that applies tar"
  },
  {
    "path": "src/arm-codegen.c",
    "chars": 29493,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/arm.c",
    "chars": 11231,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/defs.h",
    "chars": 17699,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/elf.c",
    "chars": 41048,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/globals.c",
    "chars": 49209,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/lexer.c",
    "chars": 29088,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/main.c",
    "chars": 4389,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/opt-sccp.c",
    "chars": 9450,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/parser.c",
    "chars": 188313,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/peephole.c",
    "chars": 32184,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/preprocessor.c",
    "chars": 38747,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/reg-alloc.c",
    "chars": 40640,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/riscv-codegen.c",
    "chars": 16261,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/riscv.c",
    "chars": 8946,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "src/ssa.c",
    "chars": 84865,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "tests/arm-abi.sh",
    "chars": 14748,
    "preview": "#!/usr/bin/env bash\n\n# AAPCS (ARM Architecture Procedure Call Standard) Compliance Test Suite\n\nset -u\n\n# Test Configurat"
  },
  {
    "path": "tests/check-snapshots.sh",
    "chars": 1126,
    "preview": "#!/usr/bin/env bash\n\nset -u\n\nreadonly SHECC=\"$PWD/out/shecc\"\n\nif [ \"$#\" != 2 ]; then\n    echo \"Usage: $0 <architecture> "
  },
  {
    "path": "tests/driver.sh",
    "chars": 116370,
    "preview": "#!/usr/bin/env bash\n\nset -u\n\n# Configuration and Test Metrics\n\n# Test Configuration\nreadonly VERBOSE_MODE=\"${VERBOSE:-0}"
  },
  {
    "path": "tests/fib.c",
    "chars": 215,
    "preview": "#include <stdio.h>\n\nint fib(int n)\n{\n    if (n == 0)\n        return 0;\n    else if (n == 1)\n        return 1;\n    return"
  },
  {
    "path": "tests/hello.c",
    "chars": 126,
    "preview": "#include <stdio.h>\n\nint main(int argc, char *argv[])\n{\n    printf(\"%d\\n\", argc);\n    printf(\"Hello World\\n\");\n    return"
  },
  {
    "path": "tests/snapshots/fib-arm-dynamic.json",
    "chars": 6749,
    "preview": "{\"_subgraph_cnt\":13,\"directed\":true,\"edges\":[{\"_gvid\":0,\"head\":14,\"headport\":\"n\",\"tail\":13,\"tailport\":\"s\"},{\"_gvid\":1,\"h"
  },
  {
    "path": "tests/snapshots/fib-arm-static.json",
    "chars": 425249,
    "preview": "{\"_subgraph_cnt\":602,\"directed\":true,\"edges\":[{\"_gvid\":0,\"head\":603,\"tail\":602,\"weight\":\"100\"},{\"_gvid\":1,\"head\":604,\"ta"
  },
  {
    "path": "tests/snapshots/fib-riscv-static.json",
    "chars": 427394,
    "preview": "{\"_subgraph_cnt\":602,\"directed\":true,\"edges\":[{\"_gvid\":0,\"head\":603,\"tail\":602,\"weight\":\"100\"},{\"_gvid\":1,\"head\":604,\"ta"
  },
  {
    "path": "tests/snapshots/hello-arm-dynamic.json",
    "chars": 1692,
    "preview": "{\"_subgraph_cnt\":3,\"directed\":true,\"edges\":[{\"_gvid\":0,\"head\":4,\"tail\":3,\"weight\":\"100\"},{\"_gvid\":1,\"head\":5,\"tail\":4,\"w"
  },
  {
    "path": "tests/snapshots/hello-arm-static.json",
    "chars": 419654,
    "preview": "{\"_subgraph_cnt\":592,\"directed\":true,\"edges\":[{\"_gvid\":0,\"head\":593,\"tail\":592,\"weight\":\"100\"},{\"_gvid\":1,\"head\":594,\"ta"
  },
  {
    "path": "tests/snapshots/hello-riscv-static.json",
    "chars": 421800,
    "preview": "{\"_subgraph_cnt\":592,\"directed\":true,\"edges\":[{\"_gvid\":0,\"head\":593,\"tail\":592,\"weight\":\"100\"},{\"_gvid\":1,\"head\":594,\"ta"
  },
  {
    "path": "tests/update-snapshots.sh",
    "chars": 1062,
    "preview": "#!/usr/bin/env bash\n\nset -u\n\nreadonly SHECC=\"$PWD/out/shecc\"\n\nif [ \"$#\" != 2 ]; then\n    echo \"Usage: $0 <architecture> "
  },
  {
    "path": "tools/inliner.c",
    "chars": 4306,
    "preview": "/*\n * shecc - Self-Hosting and Educational C Compiler.\n *\n * shecc is freely redistributable under the BSD 2 clause lice"
  },
  {
    "path": "tools/norm-lf.c",
    "chars": 2169,
    "preview": "/*\n * Convert all line endings to LF (Unix style)\n *\n * This tool ensures consistent line endings before processing with"
  }
]

About this extraction

This page contains the full source code of the jserv/shecc GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 47 files (2.4 MB), approximately 640.5k tokens, and a symbol index with 538 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!