Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ jobs:

- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@beta
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}

# Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4)
# model: "claude-opus-4-20250514"

# Direct prompt for automated review (no @claude mention needed)
direct_prompt: |
prompt: |
Please review this pull request and provide feedback on:
- Code quality and best practices
- Potential bugs or issues
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/claude.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: recursive

- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@beta
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}

Expand Down
9 changes: 4 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ cargo run -- file.c -c # Emit object file only
cargo run -- file.c -o binary # Custom output name
cargo run -- file.c --static # Static linking (Linux only)
cargo run -- file.c --external-linker # Use system ld instead of libwild
cargo run -- file.c --no-iced # Use deprecated text-based assembler
```

## Compiler Architecture
Expand Down Expand Up @@ -110,9 +109,7 @@ Returns `(NameGenerator, SymbolTable)` needed by subsequent passes. Exit code 30

**Codegen** (`codegen.rs`): Lowers TACKY to x86-64 assembly AST. Assigns pseudo-registers to stack slots, fixes invalid instruction operands (x86 restrictions), implements System V AMD64 calling convention (arguments in RDI, RSI, RDX, RCX, R8, R9, then stack).

**Emitter** (`emit_iced.rs`): Primary emitter using [iced-x86](https://github.com/icedland/iced) to encode instructions to machine code and [object](https://github.com/gimli-rs/object) crate to write ELF (Linux) or Mach-O (macOS) object files. No external assembler needed.

Alternative: `emit.rs` (deprecated `--no-iced` flag) generates text assembly for `as`.
**Emitter** (`emit_iced.rs`): Encodes instructions to machine code using [iced-x86](https://github.com/icedland/iced) and writes ELF (Linux) or Mach-O (macOS) object files via the [object](https://github.com/gimli-rs/object) crate. No external assembler needed.

**Linker** (`main.rs`): On Linux, uses [wild](https://github.com/wild-linker/wild) for in-process linking. On macOS, shells out to system `ld`. Locates CRT files and libc via the `cc` compiler.

Expand Down Expand Up @@ -151,10 +148,12 @@ Tests validate both successful compilation and error handling:

## Language Implementation Notes

**Type System**: Currently supports `int`/`unsigned int` (32-bit) and `long`/`unsigned long` (64-bit), with the usual arithmetic conversions. Narrowing truncates (two's complement); widening sign-extends signed sources and zero-extends unsigned ones; same-width signed/unsigned conversions reinterpret the bits.
**Type System**: Currently supports `int`/`unsigned int` (32-bit), `long`/`unsigned long` (64-bit), and `double` (64-bit IEEE-754), with the usual arithmetic conversions. Narrowing truncates (two's complement); widening sign-extends signed sources and zero-extends unsigned ones; same-width signed/unsigned conversions reinterpret the bits.

**Integer Arithmetic**: Signed overflow wraps deterministically (non-standard C extension; standard C makes it UB); unsigned wraps mod 2^N (standard). Shift amounts are masked to prevent undefined behavior. The `-Woverflow` warning fires only for signed overflow.

**Floating Point**: `double` arithmetic/comparisons use SSE2 (`addsd`/`comisd`/etc.); constants live in a `.rodata` pool. Comparisons follow IEEE-754 ordering — a `NaN` operand is unordered (relationals and `==` are false, `!=` true, `NaN` is truthy in conditions), implemented via the parity flag. `double`→integer truncates toward zero; out-of-range or `NaN` yields the x86 `cvttsd2si` "integer indefinite" value (target MIN). Unsigned↔`double` conversions use SSE2 workarounds (no native unsigned convert pre-AVX-512). An out-of-range floating constant rounds to ±infinity or zero (`-Woverflow`).

**Evaluation Order**: Left-to-right (non-standard, eliminates UB).

**Constant Expressions**: Evaluated at compile time using the same rules as runtime expressions. Used for static initializers and case labels.
Expand Down
41 changes: 33 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A (**N**ot **C**ompletely) **C** compiler written in Rust, inspired by Sandler's
NCC is a full pipeline compiler, going from lexing all the way down to x86-64 machine code emission and linking.
Machine code is encoded directly using [iced-x86](https://github.com/icedland/iced) and emitted to ELF/Mach-O
object files via the [object](https://github.com/gimli-rs/object) crate—no external assembler required.
A substantial subset of C is supported, including `int`, `long`, `unsigned int`, and `unsigned long` types, functions, static variables, all control
A substantial subset of C is supported, including `int`, `long`, `unsigned int`, `unsigned long`, and `double` types, functions, static variables, all control
flow statements, and bitwise operations. Additionally, NCC supports developer-friendly warnings and pretty-printing
of each compiler pass.
Runs on Linux and macOS.
Expand Down Expand Up @@ -145,7 +145,8 @@ ncc [OPTIONS] <FILENAMES>...

### Arguments

`<FILENAMES>...` Input files (required). Supports multiple C and assembly files.
`<FILENAMES>...` Input files (required). Supports multiple C (`.c`), assembly (`.s`), and
pre-built object (`.o`) files; objects are passed straight through to the linker.

### Options

Expand All @@ -161,7 +162,7 @@ ncc [OPTIONS] <FILENAMES>...
| `-c` | Emit object file only (no linking) |
| `--external-linker` | Use system linker (`ld`) instead of built-in libwild |
| `--static` | Link statically (no runtime dependencies) - Linux only |
| `--no-iced` | Use text-based asm building instead of iced (deprecated) |
| `-l <LIB>` | Link against a library, e.g. `-lm` (forwarded to linker) |
| `-o`, `--output <OUTPUT>` | Override output file location |
| `-h`, `--help` | Print help |

Expand Down Expand Up @@ -213,7 +214,7 @@ The compiler currently implements a subset of C with the following grammar:
<variable-declaration> ::= { <specifier> }+ <identifier> [ "=" <exp> ] ";"
<function-declaration> ::= { <specifier> }+ <identifier> "(" <param-list> ")" ( <block> | ";" )
<param-list> ::= "void" | <type> <identifier> { "," <type> <identifier> }
<type> ::= { "int" | "long" | "signed" | "unsigned" }+
<type> ::= { "int" | "long" | "signed" | "unsigned" }+ | "double"
<specifier> ::= <type> | "static" | "extern"
<block> ::= "{" { <block-item> } "}"
<block-item> ::= <statement> | <declaration>
Expand All @@ -235,7 +236,7 @@ The compiler currently implements a subset of C with the following grammar:
| ";"
<exp> ::= <factor> | <exp> <binop> <exp> | <exp> <assign-op> <exp>
| <exp> "?" <exp> ":" <exp> | <exp> "++" | <exp> "--"
<factor> ::= <int> | <long> | <uint> | <ulong> | <identifier> | <unop> <factor> | "++" <factor> | "--" <factor>
<factor> ::= <int> | <long> | <uint> | <ulong> | <double> | <identifier> | <unop> <factor> | "++" <factor> | "--" <factor>
| "(" <type> ")" <factor> | "(" <exp> ")"
| <identifier> "(" [ <argument-list> ] ")"
<argument-list> ::= <exp> { "," <exp> }
Expand All @@ -248,8 +249,22 @@ The compiler currently implements a subset of C with the following grammar:
<long> ::= ? A long integer constant token (suffix 'l' or 'L') ?
<uint> ::= ? An unsigned int constant token (suffix 'u' or 'U') ?
<ulong> ::= ? An unsigned long constant token (suffix combining 'u'/'U' and 'l'/'L') ?
<double> ::= ? A floating-point constant token (decimal point and/or exponent) ?
```

### Data Types

| Type | Size | Representation | Notes |
|-----------------|--------|--------------------------|-----------------------------------|
| `int` | 32-bit | two's complement signed | |
| `unsigned int` | 32-bit | unsigned | wraps mod 2³² |
| `long` | 64-bit | two's complement signed | LP64 — 64-bit, per System V AMD64 |
| `unsigned long` | 64-bit | unsigned | LP64; wraps mod 2⁶⁴ |
| `double` | 64-bit | IEEE-754 binary64 | |

Not yet supported: `char`, `short`, `float`, pointers, arrays, structs. See [Safer C](#safer-c) for
arithmetic, conversion, and overflow semantics.

### Supported Features

The compiler supports:
Expand All @@ -263,8 +278,9 @@ The compiler supports:
functions
- **Compound statements (blocks)**: `{ ... }` with proper scoping
- **Variable scoping**: Block-local variables with shadowing support
- **Type system**: `int`/`unsigned int` (32-bit) and `long`/`unsigned long` (64-bit), with the usual arithmetic conversions, implicit conversions, and explicit casts
- **Type system**: the integer and floating-point types above (see [Data Types](#data-types)), with the usual arithmetic conversions, implicit conversions, and explicit casts
- **Integer arithmetic**: addition, subtraction, multiplication, division, modulo
- **Floating-point arithmetic**: `double` addition, subtraction, multiplication, division, negation, and comparisons (SSE2), with conversions to and from every integer type; comparisons follow IEEE-754 ordering, so a `NaN` operand compares unordered (every relational and `==` is false, `!=` is true, and `NaN` is truthy in a condition)
- **Bitwise operations**: AND (`&`), OR (`|`), XOR (`^`), complement (`~`), left/right shift (`<<`, `>>`)
- **Logical operations**: AND (`&&`), OR (`||`), NOT (`!`) with short-circuit evaluation
- **Comparison operators**: `==`, `!=`, `<`, `>`, `<=`, `>=`
Expand Down Expand Up @@ -302,8 +318,17 @@ NCC provides several safety features and guarantees to help developers write mor
- **Shift masking**: Left and right shifts mask the shift amount to prevent undefined behavior. For `int` types,
the shift amount is masked with `& 31` (modulo 32); for `long` types, masked with `& 63` (modulo 64).
For example, `1 << 32` evaluates to `1 << 0 = 1`, matching x86 hardware behavior.
- **Consistent compile-time and runtime behavior**: Constant expressions (static initializers, case labels) follow
the same arithmetic and type conversion rules as runtime expressions, ensuring predictable behavior.
- **Deterministic floating-point edges**: Cases C leaves undefined are given defined results. A floating-point
constant too large for `double` rounds to ±infinity and one too small rounds to zero (both flagged by
`-Woverflow`), where C §6.4.4.2 leaves an out-of-range floating constant undefined. Converting a `double` to an
integer truncates toward zero; out-of-range or NaN values produce the x86 `cvttsd2si` "integer indefinite" result
(the target type's minimum, e.g. `INT_MIN`), where C §6.3.1.4 leaves the conversion undefined.
- **Consistent compile-time and runtime behavior**: Constant expressions (static initializers, case labels) are
folded with the *same* arithmetic and type-conversion rules as runtime expressions — including all of the
deterministic resolutions above. Where standard C would make an overflowing or out-of-range constant expression a
constraint violation (a required diagnostic), NCC instead folds it to the value equivalent runtime code would
produce: `static int x = 2147483647 + 1;` wraps to `INT_MIN`, and a constant `double`→`int` cast yields the same
`cvttsd2si` result the runtime conversion would.

#### Compile-Time Warnings

Expand Down
Loading
Loading