diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c85d92..f173eb8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,8 +1,8 @@ name: CI # The compiler is written in Finch. It is built from bootstrap/.ll, its own LLVM IR (see bootstrap/README.md), -# then from itself. Seeds: Linux x86-64 and ARM64 (tested here), Windows x86-64 (tested under Wine, and here), -# macOS ARM64 and x86-64 (made with Apple's headers; only checked as IR until the macos job below passes). +# then from itself, and tested, on every system it runs on: Linux x86-64 and ARM64, macOS ARM64 and x86-64, Windows x86-64. +# A tag v* also makes the release: every package below is attached to it. on: push: @@ -75,7 +75,14 @@ jobs: fi macos: - runs-on: macos-15 + strategy: + fail-fast: false + matrix: + include: + - { os: macos-15, arch: arm64 } + - { os: macos-15-intel, arch: x64 } + name: macos-${{ matrix.arch }} + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Install LLVM 21 (libclang) @@ -92,11 +99,11 @@ jobs: - name: Package run: | mkdir -p dist/finch && cp build/finch README.md LICENSE dist/finch/ && cp -r runtime std examples dist/finch/ - tar -czf finch-macos-arm64.tar.gz -C dist finch + tar -czf finch-macos-${{ matrix.arch }}.tar.gz -C dist finch - uses: actions/upload-artifact@v4 with: - name: finch-macos-arm64 - path: finch-macos-arm64.tar.gz + name: finch-macos-${{ matrix.arch }} + path: finch-macos-${{ matrix.arch }}.tar.gz windows: runs-on: windows-2022 @@ -154,3 +161,27 @@ jobs: with: name: finch-vscode path: editors/vscode/finch-lang.vsix + + release: + if: startsWith(github.ref, 'refs/tags/v') + needs: [linux, cross, macos, windows, vscode] + runs-on: ubuntu-24.04 + permissions: + contents: write + steps: + - uses: actions/download-artifact@v4 + with: + path: dist + merge-multiple: true + - name: Attach the packages to the release (made if it isn't there yet) + uses: softprops/action-gh-release@v2 + with: + name: Finch ${{ github.ref_name }} + generate_release_notes: true + files: | + dist/finch-linux-x64.tar.gz + dist/finch-linux-arm64.tar.gz + dist/finch-macos-arm64.tar.gz + dist/finch-macos-x64.tar.gz + dist/finch-windows-x64.zip + dist/finch-lang.vsix diff --git a/README.md b/README.md index 7f62239..938d072 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ fn main() { Operator precedence that does not bite (`x & 1 == 0` means what it says). - **Practical.** Maps, methods, errors as values (`int(s) or 0`, `try read_file(p)`), modules, any C library, and a language server for your editor. Runs on Linux, macOS and Windows, on x86-64 and ARM64. +- **SIMD built in.** `f32x8`, `i8x32`, `f64x4`: numbers side by side, added and multiplied all at once. LLVM turns them + into SSE, AVX2, AVX-512 or NEON instructions, so one piece of code is fast on a desktop, a laptop and ARM. - **Errors that talk like a person:** ``` game.fch:3:13: error: 'y' must be i32, but this is int (use i32(...) to convert) @@ -48,13 +50,17 @@ fn main() { ``` - **Any C library, directly.** `import "raylib.h"` + `link "raylib"`, and you can call it, structs by value and callbacks included. Headers are read by libclang, so there is nothing to declare by hand. -- **Written in itself.** The compiler is Finch code, and nothing else: `src/*.fch`, about 8.6k lines. It is built from a - seed (its own LLVM IR, `bootstrap/`) and then from itself, and the two come out identical. Its first version, in C++, - is kept on the `before-bootstrap` branch. +- **Written in itself.** The compiler is Finch code, and nothing else: `src/*.fch`, about 12k lines. It is built from a + seed (its own LLVM IR, `bootstrap/`) and then from itself, and the two come out identical. ## Quick start -**Building it** (Linux x86-64 or ARM64; needs `clang` and libclang, LLVM 21): +**Ready builds** for Linux (x64, ARM64), macOS (Apple Silicon, Intel) and Windows (x64) are in the +[releases](https://github.com/Nexoniarz/Finch/releases). Unpack one and keep `runtime/` and `std/` next to `finch`. What +each system needs besides is in the [technicians' guide](docs/en/for-technicians.md#2-installing): clang and libclang +from LLVM 21 on Linux, `brew install llvm@21` on a Mac, llvm-mingw on Windows. + +**Building it yourself** (on Linux, macOS, or Windows with Git Bash; needs `clang` and libclang, LLVM 21): ```sh git clone https://github.com/Nexoniarz/Finch.git @@ -64,13 +70,12 @@ bootstrap/build.sh # the compiler is build/finch: built from th ./build/finch run examples/hello.fch ./build/finch build examples/tour.fch -o tour && ./tour -tests/run.sh # every program, every refusal, -g, a shared library, the language server +tests/run.sh # every program, every expected error, -g, a shared library, the language server ``` -Ready builds of the compiler are in the [releases](https://github.com/Nexoniarz/Finch/releases) (Linux x64 and ARM64: -unpack, keep `runtime/` and `std/` next to `finch`). **Programs for Windows, ARM64 and macOS** are built from Linux with -`--target windows|arm64|macos` (see the [guide](docs/en/for-technicians.md#3-the-finch-command)); the compiler itself does not run on -macOS or Windows yet (it needs a seed made with that system's headers: see [bootstrap/README.md](bootstrap/README.md)). +The same `bootstrap/build.sh` works on a Mac (with Homebrew's `llvm@21`) and on Windows (with llvm-mingw): see +[bootstrap/README.md](bootstrap/README.md). Programs for another system are built with `--target windows|linux|arm64|macos` +(see the [guide](docs/en/for-technicians.md#building-for-another-system)). Editors: **Kate** (`editors/kate/install.sh`), **VS Code** (the `finch-lang` `.vsix` from the release), or any editor with LSP support (`finch lsp`). @@ -91,13 +96,14 @@ Pick the guide that fits you: | | | |---|---| | Variables | `x := 5` (inferred) or `int x = 5`; `const W = 640` | -| Types | `int` `float` `bool` `char` `str`, sized `i8`…`i64` `u8`…`u64` `f32` `f64`, arrays `[]T`, maps `map[K]V`, pointers `ptr[T]` | +| Types | `int` `float` `bool` `char` `str`, sized `i8`…`i64` `u8`…`u64` `f32` `f64`, arrays `[]T`, maps `map[K]V`, pointers `ptr[T]`, vectors `f32x8` `i8x32`… | | Functions | `fn add(int a, int b) -> int { return a + b }` | | Structs | `struct Point { … }` with one field per line (`int x`, `int y = 0`); create with `Point(1, 2)` or `Point(x: 1)` | | Methods | `fn Point.move(int dx) { self.x += dx }` → `p.move(3)` | | Arrays | `nums := [1, 2, 3]`, `nums.push(4)`, `nums[0]`, `nums.len`, `for i, n in nums { }` | | Maps | `ages := ["ann": 31]`, `ages["bob"] = 25`, `ages.has("cy")`, `counts[w] += 1`, `for k, v in ages { }` | | Errors | `fn parse(str s) -> int! { … return error("bad") }` → `parse(s) or 0`, `parse(s) or { … }`, `try parse(s)` | +| Vectors (SIMD) | `v := f32x8(list, i)`, `v * 2.0 + w`, `v.sum()`, `v.dot(w)`, `min(v, 1.0)`, `v.store(list, i)`, `v[3]` | | Text | `"hp \(hp)"`, `"a" + "b"`, `str(42)`, `int("42")`, `s.split(",")`, `s.upper()`, `s[0]` | | Control | `if` / `else if` / `else` (also as a value), `while`, `for i in 0..10`, `break`, `continue`, `defer` | | Memory | automatic for arrays, text and structs; `new(...)` / `free(...)` for your own heap structures | @@ -108,7 +114,7 @@ Pick the guide that fits you: | Tests | `test "name" { assert(x == 3) }` next to the code, run with `finch test` | | C interop | `import "stdio.h"`, `link "glfw"`, `link "mine.c"` / `"glue.cpp"`, unions, `#define`s (also `(Color){…}`), `addr(fn)` for callbacks | | I/O | `print(...)`, `input("? ")`, `read_file`, `write_file`, `shell` | -| Tools | `finch run/build/test/fmt/ir/lsp`, `finch.toml` for a project, a build cache, `--target windows/arm64/macos/android`, `-g` for gdb/lldb, `-O0`, the chain of calls on runtime errors | +| Tools | `finch run/build/test/fmt/ir/lsp`, `finch.toml` for a project, a build cache, `--target windows/arm64/macos/android`, `--cpu native`, `-g` for gdb/lldb, `-O0`, the chain of calls on runtime errors | ## Examples @@ -122,6 +128,7 @@ Pick the guide that fits you: | [`examples/structs.fch`](examples/structs.fch) | structs, arrays of structs, a linked list with `new`/`free` | | [`examples/types.fch`](examples/types.fch) | sized numbers and pointers | | [`examples/c_import.fch`](examples/c_import.fch) | `printf`, `math.h`, `malloc`/`free`, `stderr` | +| [`examples/simd.fch`](examples/simd.fch) | vectors: a dot product eight numbers at a time, and the time it saves | | [`examples/window.fch`](examples/window.fch) | a GLFW + OpenGL window | | [`examples/opengl.fch`](examples/opengl.fch) | modern OpenGL 3.3: shaders, buffers, a spinning triangle | | [`examples/vulkan.fch`](examples/vulkan.fch) | Vulkan: list the GPUs, their memory and queues, create a device | @@ -151,14 +158,16 @@ Pick the guide that fits you: - [x] A standard library in Finch: `json`, `path`, `time`, `proc`, `fs` - [x] `const`, `if` as a value, `"\(x)"` in text, `"""` text, C unions and `#define` expressions - [x] `test` blocks and `finch test`, `finch fmt`, `finch.toml`, a build cache, the chain of calls on runtime errors -- [x] Threads (`import thread`) and atomics; C++ files; `--target android` (compiles; NDK linking and devices untried) +- [x] Threads (`import thread`) and atomics; C++ files; `--target android` +- [x] The compiler on every system it builds for: Linux, macOS (Apple Silicon and Intel), Windows; releases made by CI +- [x] Vector types (`f32x8`, `i8x32`, …) for SIMD, and `--cpu native` ## Layout ``` src/ the compiler, in Finch: lexer, parser, AST, code generation (to LLVM IR as text), C import (libclang) and the C calling conventions, the language server, the driver -std/ the standard library, in Finch: json, path, time, proc, fs +std/ the standard library, in Finch: json, path, time, proc, fs, thread runtime/ the small C runtime linked into every program bootstrap/ build.sh and the seeds: the compiler's own LLVM IR, one per system examples/ example programs diff --git a/README.pl.md b/README.pl.md index 52b438d..098f317 100644 --- a/README.pl.md +++ b/README.pl.md @@ -5,8 +5,8 @@

-**Mały, zwinny, skuteczny.** Język programowania na LLVM ze składnią podobną do C, -na tyle prosty, że nauczysz się go w godzinę, i tak szybki jak C. +**Mały, szybki, celny.** Język programowania oparty na LLVM, ze składnią w stylu C. Nauczysz się go w godzinę, +a programy działają tak szybko jak w C. > 🇬🇧 [English version](README.md) @@ -19,7 +19,7 @@ fn przeciwprostokatna(float a, float b) -> float { fn main() { imie := "Finch" - print("Cześć od", imie, "!") + print("Cześć, tu", imie, "!") for i in 1..4 { print(i, "→", przeciwprostokatna(float(i), 1.0)) @@ -29,51 +29,58 @@ fn main() { ## Dlaczego Finch -- **Prosty.** Bez średników, bez plików nagłówkowych, bez preprocesora. Każdą rzecz robi się na jeden sposób. -- **Szybki.** Kompiluje się przez potok `-O2` z LLVM do kodu natywnego, bez garbage collectora. - Rekurencyjne `fib(40)` trwa ok. 0,19 s (`clang -O2` na tym samym kodzie C: ok. 0,23 s), a kod na tablicach - z kontrolą zakresu dorównuje C. -- **Pamięć bez bólu.** Listy, teksty i struktury są wartościami: przypisanie kopiuje, a koniec bloku je zwalnia. - Nie ma `free`, o którym można zapomnieć, nie ma użycia po zwolnieniu, a każdy test jest czysty pod valgrindem. -- **Bezpieczniejszy niż C tam, gdzie to tanie.** Nie ma cichego zwężania typów ani mieszania liczb ze znakiem - i bez znaku. Indeks poza zakresem, dzielenie przez zero i null zatrzymują program czytelnym błędem, zamiast - powodować niezdefiniowane zachowanie. Priorytety operatorów nie gryzą (`x & 1 == 0` znaczy to, co widać). -- **Praktyczny.** Mapy, metody, błędy jako wartości (`int(s) or 0`, `try read_file(p)`), moduły, każda - biblioteka C i serwer języka dla twojego edytora. Działa na Linuksie, macOS i Windowsie, na x86-64 i ARM64. -- **Błędy mówią po ludzku:** +- **Prosty.** Nie ma średników, plików nagłówkowych ani preprocesora. Na każdą rzecz jest jeden sposób. +- **Szybki.** Kod przechodzi przez optymalizacje LLVM (`-O2`) i staje się zwykłym programem, bez garbage collectora. + Rekurencyjne `fib(40)` liczy się ok. 0,19 s (ten sam kod w C z `clang -O2`: ok. 0,23 s), a pętle po tablicach + ze sprawdzaniem zakresu są tak samo szybkie jak w C. +- **Pamięć bez kłopotów.** Listy, teksty i struktury są wartościami: przypisanie robi kopię, a koniec bloku sprząta. + Nie ma `free`, o którym da się zapomnieć, ani odwołań do zwolnionej pamięci. Valgrind nie znajduje błędów w żadnym teście. +- **Bezpieczniejszy niż C tam, gdzie nic to nie kosztuje.** Liczby nie zmieniają po cichu typu, a liczb ze znakiem + i bez znaku nie da się przypadkiem pomieszać. Indeks poza tablicą, dzielenie przez zero czy `null` kończą program + jasnym komunikatem, a nie niezdefiniowanym zachowaniem. Kolejność działań jest taka, jakiej się spodziewasz + (`x & 1 == 0` znaczy dokładnie to, co widać). +- **Praktyczny.** Mapy, metody, błędy jako wartości (`int(s) or 0`, `try read_file(p)`), moduły, dowolna biblioteka C + i serwer języka dla edytora. Działa na Linuksie, macOS i Windowsie, na procesorach x86-64 i ARM64. +- **SIMD w języku.** `f32x8`, `i8x32`, `f64x4`: kilka liczb obok siebie, dodawanych i mnożonych naraz. LLVM zamienia je + na instrukcje SSE, AVX2, AVX-512 albo NEON, więc ten sam kod jest szybki na komputerze stacjonarnym, laptopie i ARM-ie. +- **Komunikaty, które da się zrozumieć:** ``` gra.fch:3:13: error: 'y' must be i32, but this is int (use i32(...) to convert) 3 | i32 y = x | ^ ``` -- **Każda biblioteka C, bezpośrednio.** Piszesz `import "raylib.h"` i `link "raylib"`, i możesz z niej korzystać, - także ze struktur przekazywanych przez wartość i z callbacków. Nagłówki czyta libclang, więc nie trzeba niczego deklarować ręcznie. -- **Napisany w sobie.** Kompilator to kod w Finchu i nic więcej: `src/*.fch`, ok. 8,6 tys. linii. Buduje się go z ziarna - (własny kod LLVM IR, `bootstrap/`), potem z samego siebie, a oba wyniki są identyczne. Pierwsza wersja, w C++, - jest w gałęzi `before-bootstrap`. +- **Biblioteki C bez pośredników.** Wystarczy `import "raylib.h"` i `link "raylib"`, żeby z niej korzystać, łącznie + ze strukturami przekazywanymi przez wartość i funkcjami zwrotnymi. Nagłówki czyta libclang, więc niczego nie trzeba + deklarować ręcznie. +- **Napisany w samym sobie.** Kompilator to wyłącznie kod w Finchu: `src/*.fch`, ok. 12 tys. linii. Buduje się go + z pliku startowego (jego własnego kodu LLVM IR, w `bootstrap/`), a potem jeszcze raz nim samym. Oba wyniki są identyczne. -## Szybki start +## Na start -**Budowanie** (Linux x86-64 lub ARM64; potrzebny `clang` i libclang, LLVM 21): +**Gotowe kompilatory** na Linuksa (x64, ARM64), macOS (Apple Silicon, Intel) i Windowsa (x64) znajdziesz w +[wydaniach](https://github.com/Nexoniarz/Finch/releases). Rozpakuj archiwum i trzymaj katalogi `runtime/` i `std/` obok +pliku `finch`. Co jeszcze trzeba doinstalować, opisuje [przewodnik dla techników](docs/pl/dla-technikow.md#2-instalacja): +na Linuksie clang i libclang z LLVM 21, na Macu `brew install llvm@21`, na Windowsie llvm-mingw. + +**Samodzielne budowanie** (na Linuksie, macOS albo Windowsie w Git Bash; potrzebne są `clang` i libclang z LLVM 21): ```sh git clone https://github.com/Nexoniarz/Finch.git cd Finch -nix-shell # albo zainstaluj samodzielnie clang + libclang (LLVM 21) + pkg-config -bootstrap/build.sh # kompilator to build/finch: z ziarna, potem z samego siebie +nix-shell # albo zainstaluj clang, libclang (LLVM 21) i pkg-config po swojemu +bootstrap/build.sh # gotowy kompilator to build/finch ./build/finch run examples/hello.fch ./build/finch build examples/tour.fch -o tour && ./tour -tests/run.sh # każdy program, każda odmowa, -g, biblioteka współdzielona, serwer języka +tests/run.sh # wszystkie programy testowe, oczekiwane błędy, -g, biblioteka współdzielona, serwer języka ``` -Gotowe kompilatory są w [wydaniach](https://github.com/Nexoniarz/Finch/releases) (Linux x64 i ARM64: rozpakuj, zostaw -`runtime/` i `std/` obok `finch`). **Programy dla Windowsa, ARM64 i macOS** buduje się z Linuksa przez -`--target windows|arm64|macos`; sam kompilator nie działa jeszcze na macOS ani Windowsie (potrzebuje ziarna -zrobionego z nagłówkami tego systemu: patrz [bootstrap/README.md](bootstrap/README.md)). +Ten sam `bootstrap/build.sh` działa na Macu (z `llvm@21` z Homebrew) i na Windowsie (z llvm-mingw); szczegóły są w +[bootstrap/README.md](bootstrap/README.md). Program na inny system zbudujesz opcją `--target windows|linux|arm64|macos` +(zob. [przewodnik](docs/pl/dla-technikow.md#budowanie-na-inny-system)). -Edytory: **Kate** (`editors/kate/install.sh`), **VS Code** (plik `.vsix` `finch-lang` z wydania) albo dowolny -edytor z obsługą LSP (`finch lsp`). +Edytory: **Kate** (`editors/kate/install.sh`), **VS Code** (plik `.vsix` `finch-lang` z wydania) albo każdy edytor, +który obsługuje LSP (`finch lsp`). ## Dokumentacja @@ -81,90 +88,94 @@ Wybierz przewodnik dla siebie: | Przewodnik | Dla kogo | Polski | English | |---|---|---|---| -| **Początkujący** | Nigdy nie programowałeś: uczniowie, seniorzy, ciekawscy | [dla-poczatkujacych.md](docs/pl/dla-poczatkujacych.md) | [for-beginners.md](docs/en/for-beginners.md) | -| **Technicy** | Znasz komputery, programowanie jest nowe: instalacja, cały język, biblioteki C, problemy | [dla-technikow.md](docs/pl/dla-technikow.md) | [for-technicians.md](docs/en/for-technicians.md) | -| **Inżynierowie** | Wszystko: lekser, gramatyka, AST, reguły typów, tłumaczenie na IR, import przez libclang, ABI, wnętrze | [dla-inzynierow.md](docs/pl/dla-inzynierow.md) | [for-engineers.md](docs/en/for-engineers.md) | -| **Ściąga** | Każda funkcja, metoda, operator i polecenie na jednej stronie | [sciaga.md](docs/pl/sciaga.md) | [cheatsheet.md](docs/en/cheatsheet.md) | +| **Dla początkujących** | Dla tych, którzy nigdy nie programowali: uczniów, seniorów, ciekawych świata | [dla-poczatkujacych.md](docs/pl/dla-poczatkujacych.md) | [for-beginners.md](docs/en/for-beginners.md) | +| **Dla techników** | Znasz komputery, ale programowanie jest dla ciebie nowe: instalacja, cały język, biblioteki C, rozwiązywanie problemów | [dla-technikow.md](docs/pl/dla-technikow.md) | [for-technicians.md](docs/en/for-technicians.md) | +| **Dla inżynierów** | Wszystko: lekser, gramatyka, AST, reguły typów, generowanie IR, import przez libclang, ABI, budowa kompilatora | [dla-inzynierow.md](docs/pl/dla-inzynierow.md) | [for-engineers.md](docs/en/for-engineers.md) | +| **Ściąga** | Wszystkie funkcje, metody, operatory i polecenia na jednej stronie | [sciaga.md](docs/pl/sciaga.md) | [cheatsheet.md](docs/en/cheatsheet.md) | ## Język w pigułce | | | |---|---| -| Zmienne | `x := 5` (typ zgadnięty) albo `int x = 5`; `const W = 640` | -| Typy | `int` `float` `bool` `char` `str`, z rozmiarem `i8`…`i64` `u8`…`u64` `f32` `f64`, tablice `[]T`, mapy `map[K]V`, wskaźniki `ptr[T]` | -| Funkcje | `fn add(int a, int b) -> int { return a + b }` | -| Struktury | `struct Punkt { … }` z jednym polem na linijkę (`int x`, `int y = 0`); tworzenie przez `Punkt(1, 2)` albo `Punkt(x: 1)` | +| Zmienne | `x := 5` (typ wynika z wartości) albo `int x = 5`; `const W = 640` | +| Typy | `int` `float` `bool` `char` `str`, o podanym rozmiarze: `i8`…`i64` `u8`…`u64` `f32` `f64`, tablice `[]T`, mapy `map[K]V`, wskaźniki `ptr[T]`, wektory `f32x8` `i8x32`… | +| Funkcje | `fn dodaj(int a, int b) -> int { return a + b }` | +| Struktury | `struct Punkt { … }`, jedno pole w linii (`int x`, `int y = 0`); tworzy się je przez `Punkt(1, 2)` albo `Punkt(x: 1)` | | Metody | `fn Punkt.przesun(int dx) { self.x += dx }` → `p.przesun(3)` | | Tablice | `liczby := [1, 2, 3]`, `liczby.push(4)`, `liczby[0]`, `liczby.len`, `for i, n in liczby { }` | -| Mapy | `wiek := ["ania": 31]`, `wiek["bob"] = 25`, `wiek.has("cy")`, `ile[s] += 1`, `for k, v in wiek { }` | -| Błędy | `fn parsuj(str s) -> int! { … return error("zły") }` → `parsuj(s) or 0`, `parsuj(s) or { … }`, `try parsuj(s)` | -| Tekst | `"hp \(hp)"`, `"a" + "b"`, `str(42)`, `int("42")`, `s.split(",")`, `s.upper()`, `s[0]` | -| Sterowanie | `if` / `else if` / `else` (też jako wartość), `while`, `for i in 0..10`, `break`, `continue`, `defer` | -| Pamięć | automatyczna dla tablic, tekstów i struktur; `new(...)` / `free(...)` dla własnych struktur na stercie | -| Enumy, match | `enum Kolor { Czerwony, Zielony }`, `match k { Czerwony -> ... else -> ... }` (także jako wartość) | -| Generyki | `fn max[T](T a, T b) -> T`, `struct Stos[T]`, `Stos[int]()` | +| Mapy | `wiek := ["ania": 31]`, `wiek["bob"] = 25`, `wiek.has("cyryl")`, `ile[slowo] += 1`, `for k, v in wiek { }` | +| Błędy | `fn parsuj(str s) -> int! { … return error("zła liczba") }` → `parsuj(s) or 0`, `parsuj(s) or { … }`, `try parsuj(s)` | +| Wektory (SIMD) | `v := f32x8(lista, i)`, `v * 2.0 + w`, `v.sum()`, `v.dot(w)`, `min(v, 1.0)`, `v.store(lista, i)`, `v[3]` | +| Tekst | `"hp: \(hp)"`, `"a" + "b"`, `str(42)`, `int("42")`, `s.split(",")`, `s.upper()`, `s[0]` | +| Sterowanie | `if` / `else if` / `else` (również jako wartość), `while`, `for i in 0..10`, `break`, `continue`, `defer` | +| Pamięć | tablice, teksty i struktury zwalniają się same; `new(...)` / `free(...)` dla własnych struktur na stercie | +| Enumy i `match` | `enum Kolor { Czerwony, Zielony }`, `match k { Czerwony -> ... else -> ... }` (również jako wartość) | +| Typy generyczne | `fn max[T](T a, T b) -> T`, `struct Stos[T]`, `Stos[int]()` | | Moduły | `import ksztalty` → `ksztalty.pole(p)`, `import ui/button as btn`; biblioteka standardowa: `json`, `path`, `time`, `proc`, `fs`, `thread` | | Wątki | `thread.spawn(addr(praca), addr(dane))`, `t.join()`, `thread.mutex()`, `atomic_add(addr(n), 1)` | -| Testy | `test "nazwa" { assert(x == 3) }` obok kodu, uruchamiane przez `finch test` | -| Współpraca z C | `import "stdio.h"`, `link "glfw"`, `link "moje.c"` / `"glue.cpp"`, unie, `#define` (też `(Color){…}`), `addr(fn)` dla callbacków | -| Wejście/wyjście | `print(...)`, `input("? ")`, `read_file`, `write_file`, `shell` | -| Narzędzia | `finch run/build/test/fmt/ir/lsp`, `finch.toml` dla projektu, cache budowania, `--target windows/arm64/macos/android`, `-g` dla gdb/lldb, `-O0`, łańcuch wywołań przy błędach w czasie działania | +| Testy | `test "nazwa" { assert(x == 3) }` tuż obok kodu, uruchamiane przez `finch test` | +| Współpraca z C | `import "stdio.h"`, `link "glfw"`, `link "moje.c"` / `"glue.cpp"`, unie, `#define` (także `(Color){…}`), `addr(fn)` jako funkcja zwrotna | +| Wejście i wyjście | `print(...)`, `input("? ")`, `read_file`, `write_file`, `shell` | +| Narzędzia | `finch run/build/test/fmt/ir/lsp`, `finch.toml` dla projektu, pamięć podręczna kompilacji, `--target windows/arm64/macos/android`, `--cpu native`, `-g` dla gdb i lldb, `-O0`, ścieżka wywołań przy błędzie w trakcie działania | ## Przykłady -| Plik | Pokazuje | +| Plik | Co pokazuje | |---|---| -| [`examples/hello.fch`](examples/hello.fch) | najmniejszy program | +| [`examples/hello.fch`](examples/hello.fch) | najkrótszy program | | [`examples/tour.fch`](examples/tour.fch) | zmienne, funkcje, warunki, pętle | -| [`examples/guess.fch`](examples/guess.fch) | gra w zgadywanie: wejście, pętle, `rand` z C | +| [`examples/guess.fch`](examples/guess.fch) | gra w zgadywanie liczby: wczytywanie, pętle, `rand` z C | | [`examples/todo.fch`](examples/todo.fch) | struktury, tablice, teksty i pliki | | [`examples/wordcount.fch`](examples/wordcount.fch) | mapy, metody i błędy jako wartości: najczęstsze słowa w pliku | -| [`examples/structs.fch`](examples/structs.fch) | struktury, tablice struktur, lista wiązana z `new`/`free` | -| [`examples/types.fch`](examples/types.fch) | liczby z rozmiarem i wskaźniki | +| [`examples/structs.fch`](examples/structs.fch) | struktury, tablice struktur, lista jednokierunkowa z `new`/`free` | +| [`examples/types.fch`](examples/types.fch) | liczby o podanym rozmiarze i wskaźniki | | [`examples/c_import.fch`](examples/c_import.fch) | `printf`, `math.h`, `malloc`/`free`, `stderr` | -| [`examples/window.fch`](examples/window.fch) | okno GLFW + OpenGL | +| [`examples/simd.fch`](examples/simd.fch) | wektory: iloczyn skalarny liczony po osiem liczb naraz i zysk na czasie | +| [`examples/window.fch`](examples/window.fch) | okno GLFW z OpenGL | | [`examples/opengl.fch`](examples/opengl.fch) | nowoczesny OpenGL 3.3: shadery, bufory, obracający się trójkąt | -| [`examples/vulkan.fch`](examples/vulkan.fch) | Vulkan: lista kart graficznych, ich pamięć i kolejki, tworzenie urządzenia | -| [`examples/raylib.fch`](examples/raylib.fch) | raylib ze strukturami C przez wartość | -| [`examples/llvm.fch`](examples/llvm.fch) | Finch budujący LLVM IR przez API LLVM-C | -| [`src/`](src/) | kompilator napisany w Finchu (ziarno do jego zbudowania jest w [`bootstrap/`](bootstrap/)) | +| [`examples/vulkan.fch`](examples/vulkan.fch) | Vulkan: lista kart graficznych z ich pamięcią i kolejkami, utworzenie urządzenia | +| [`examples/raylib.fch`](examples/raylib.fch) | raylib i struktury C przekazywane przez wartość | +| [`examples/llvm.fch`](examples/llvm.fch) | Finch, który buduje LLVM IR przez API LLVM-C | +| [`src/`](src/) | kompilator napisany w Finchu (plik startowy do jego zbudowania leży w [`bootstrap/`](bootstrap/)) | ## Plan rozwoju - [x] Lekser, parser, generowanie kodu LLVM, optymalizacja O2 -- [x] Typy, w tym liczby z rozmiarem, wskaźniki, konwersje; funkcje; `if`/`while`/`for` -- [x] Czytelne błędy kompilacji; kontrole w czasie działania (dzielenie, null, zakres) -- [x] Operatory bitowe z rozsądnymi priorytetami -- [x] Nagłówki C przez libclang; `link` z pkg-config; porady przy błędach linkowania -- [x] `struct`, także struktury C przez wartość (ABI System V) i callbacki -- [x] Tablice z `.len`, kontrolą zakresu i metodami; `for x in lista` -- [x] Pamięć: zwalnianie na końcu bloku, `defer`, `new`/`free` +- [x] Typy, w tym liczby o podanym rozmiarze, wskaźniki i konwersje; funkcje; `if`/`while`/`for` +- [x] Czytelne błędy kompilacji; sprawdzanie w trakcie działania (dzielenie, `null`, zakres tablicy) +- [x] Operatory bitowe z rozsądną kolejnością działań +- [x] Nagłówki C przez libclang; `link` z pkg-config; podpowiedzi przy błędach linkera +- [x] `struct`, w tym struktury C przekazywane przez wartość (ABI System V) i funkcje zwrotne +- [x] Tablice z `.len`, sprawdzaniem zakresu i metodami; `for x in lista` +- [x] Pamięć: wartości zwalniane na końcu bloku, `defer`, `new`/`free` - [x] Teksty: `+`, `.len`, indeksowanie, metody, `str(x)`, `int(s)`; `input()`; pliki - [x] Moduły Fincha (`import nazwa`) - [x] Informacje dla debuggera (`-g`) -- [x] Bootstrap: kompilator Fincha napisany w Finchu, który buduje sam siebie +- [x] Bootstrap: kompilator Fincha napisany w Finchu, który kompiluje sam siebie - [x] Windows: `finch.exe`, programy `.exe`, ABI Microsoft x64; budowanie na Windowsa z Linuksa (`--target windows`) -- [x] Serwer języka (`finch lsp`) i rozszerzenie VS Code +- [x] Serwer języka (`finch lsp`) i rozszerzenie do VS Code - [x] macOS i ARM64 (Linux i Apple Silicon) z ich konwencjami wywołań C; obsługa Kate -- [x] Metody w strukturach, mapy, błędy jako wartości (`!`, `or`, `try`) +- [x] Metody struktur, mapy, błędy jako wartości (`!`, `or`, `try`) - [x] Typy generyczne (`fn max[T]`, `struct Stos[T]`), `match`, enumy - [x] Biblioteka standardowa w Finchu: `json`, `path`, `time`, `proc`, `fs` -- [x] `const`, `if` jako wartość, `"\(x)"` w tekście, tekst `"""`, unie C i wyrażenia `#define` -- [x] Bloki `test` i `finch test`, `finch fmt`, `finch.toml`, cache budowania, łańcuch wywołań przy błędach -- [x] Wątki (`import thread`) i operacje atomowe; pliki C++; `--target android` (kompiluje się; linkowanie z NDK i urządzenia niesprawdzone) +- [x] `const`, `if` jako wartość, `"\(x)"` w tekście, tekst `"""`, unie C i wyrażenia w `#define` +- [x] Bloki `test` i `finch test`, `finch fmt`, `finch.toml`, pamięć podręczna kompilacji, ścieżka wywołań przy błędach +- [x] Wątki (`import thread`) i operacje atomowe; pliki C++; `--target android` +- [x] Kompilator na każdym systemie, na który buduje: Linux, macOS (Apple Silicon i Intel), Windows; wydania robi CI +- [x] Typy wektorowe (`f32x8`, `i8x32`, …) dla SIMD i `--cpu native` -## Struktura +## Co gdzie leży ``` -src/ kompilator, w Finchu: lekser, parser, AST, generowanie kodu (do LLVM IR jako tekst), import C (libclang) - i konwencje wywołań C, serwer języka, sterownik -std/ biblioteka standardowa, w Finchu: json, path, time, proc, fs -runtime/ mały runtime w C dołączany do każdego programu -bootstrap/ build.sh i ziarna: własny LLVM IR kompilatora, po jednym na system +src/ kompilator w Finchu: lekser, parser, AST, generowanie kodu (LLVM IR jako tekst), import C (libclang) + i konwencje wywołań C, serwer języka, polecenie finch +std/ biblioteka standardowa w Finchu: json, path, time, proc, fs, thread +runtime/ mały runtime w C, dołączany do każdego programu +bootstrap/ build.sh i pliki startowe: własny LLVM IR kompilatora, osobno dla każdego systemu examples/ przykładowe programy -editors/ rozszerzenie VS Code, podświetlanie i konfiguracja LSP dla Kate -tests/ run/ new/ (wzorcowe wyjście), fail/ new/fail/ (oczekiwane błędy), run.sh, cross.sh, shared.sh, lsp_test.py -docs/ przewodniki en/ i pl/ dla trzech grup odbiorców +editors/ rozszerzenie do VS Code, podświetlanie składni i konfiguracja LSP dla Kate +tests/ run/ new/ (wzorcowe wyniki), fail/ new/fail/ (oczekiwane błędy), run.sh, cross.sh, shared.sh, lsp_test.py +docs/ przewodniki po angielsku (en/) i po polsku (pl/) dla trzech grup czytelników ``` ## Licencja diff --git a/bootstrap/README.md b/bootstrap/README.md index f4825d8..3ea5dbe 100644 --- a/bootstrap/README.md +++ b/bootstrap/README.md @@ -3,12 +3,16 @@ Finch's compiler is written in Finch. To build it you need something that already runs Finch: the **seed**, which is the compiler's own LLVM IR, one file per system: -| File | System | Checked | +| File | System | Built and tested in CI on | |---|---|---| -| `linux-x86_64.ll` | Linux x86-64 | built and tested here and in CI | -| `linux-aarch64.ll` | Linux ARM64 | made with the ARM64 cross compiler; CI builds and tests it on an ARM64 runner | -| `windows-x86_64.ll` | Windows x86-64 (MinGW: llvm-mingw's clang + libclang.dll) | built and run under Wine: it compiles itself, and every test program runs | -| `macos-arm64.ll`, `macos-x86_64.ll` | macOS | made on Linux with the macOS 11.3 SDK; checked as IR only (nobody has run them yet) | +| `linux-x86_64.ll` | Linux x86-64 | ubuntu-24.04 | +| `linux-aarch64.ll` | Linux ARM64 | ubuntu-24.04-arm | +| `macos-arm64.ll` | macOS, Apple Silicon | macos-15 | +| `macos-x86_64.ll` | macOS, Intel | macos-15-intel | +| `windows-x86_64.ll` | Windows x86-64 (MinGW: llvm-mingw's clang + libclang.dll) | windows-2022 | + +Each CI job builds the compiler from its seed, then from itself, runs every test, and packs the result; a tag `v*` attaches +the packages to the release. `bootstrap/build.sh` compiles the seed with `clang` (and the runtime, and libclang) into `build/finch0`; `finch0` builds `src/` into `finch1`; `finch1` builds `src/` again into `build/finch`; the two must make the same IR, or the script stops. @@ -41,5 +45,6 @@ official LLVM for Windows); `bootstrap/winbuild.sh` turns it into `finch.exe` fo `bootstrap/build.sh` does the same from Git Bash: put llvm-mingw's `bin` in PATH and set `FINCH_LIBCLANG` (the path of `libclang.dll`), `FINCH_LLVM_INCLUDE` (its `include` folder) and `FINCH_LDFLAGS=-L`. -macOS: the seeds were made with the macOS 11.3 SDK; `brew install llvm@21` gives clang and libclang; the CI job sets -`FINCH_CLANG`, `FINCH_LIBCLANG` and `FINCH_LLVM_INCLUDE`. If the macOS build fails, that job is where it shows first. +macOS: the seeds were made with the macOS 11.3 SDK. `brew install llvm@21` +gives clang and libclang; set `FINCH_CLANG`, `FINCH_LIBCLANG` and `FINCH_LLVM_INCLUDE` as the CI job does (the +[technicians' guide](../docs/en/for-technicians.md#option-b-build-it-yourself) has the lines). diff --git a/docs/en/cheatsheet.md b/docs/en/cheatsheet.md index d27262c..615856d 100644 --- a/docs/en/cheatsheet.md +++ b/docs/en/cheatsheet.md @@ -21,8 +21,9 @@ Everything in Finch on one page. For explanations, see the [guides](../../README | `finch version` | version | | `-l lib` | link a C library (like `link "lib"`) | | `--target windows` / `linux` / `arm64` / `macos` / `android` / triple | build for another system (`android`: the NDK's clang from `ANDROID_NDK_HOME`, usually with `--shared`) | -| `-g` | debug info (gdb, lldb, Visual Studio) | +| `-g` | debug info (gdb, lldb) | | `-O0` | no optimizations | +| `--cpu native` / `x86-64-v3` / `apple-m1` … | use every instruction of that processor (AVX2, AVX-512, NEON); the program then needs one like it | | `--trace` / `--no-trace` | runtime errors show the chain of calls; on for `run`, `test`, `-g`, `-O0`, off for a plain `build` (it costs speed) | Environment: `FINCH_PATH` (folders with modules), `FINCH_CC` (the C compiler that links), `FINCH_CXX` (for `.cpp` files). @@ -39,6 +40,7 @@ links = ["SDL3", "native/glue.cpp"] # libraries and your own .c / .cpp / .o / .a libdirs = ["vendor/SDL3/lib"] cflags = ["-DFAST"] # for your .c / .cpp files ldflags = ["-Wl,-rpath,."] +cpu = "native" # the same as --cpu native [target.windows] # only when building for that system (also linux, macos, android, linux-arm64) links = ["ws2_32"] @@ -96,6 +98,7 @@ One statement per line, no semicolons, `{` on the same line, no brackets around | `[]T` | array: `[1, 2, 3]`, `[]str names` | | `map[K]V` | map: `["a": 1]`, `map[str]int counts`, `[:]` | | `ptr[T]`, `ptr` | pointer, untyped pointer | +| `f32x8`, `i8x32`, `f64x4`, `u16x16` … | vector: N numbers worked on at once (SIMD), see below | | `Name` / `module.Name` | a struct | Escapes: `\n \t \r \0 \\ \" \'`. @@ -240,6 +243,27 @@ Fallible built-ins: `int(s)`, `float(s)`, `read_file`, `write_file`, `delete_fil --- +## Vectors `f32x8` (SIMD) + +Types: `f32x4/8/16`, `f64x2/4/8`, `i8x16/32/64`, `i16x8/16/32`, `i32x4/8/16`, `i64x2/4/8`, and `u…` the same; at most 512 bits. + +| Code | Gives | +|---|---| +| `f32x8(x)` | `x` in every lane | +| `f32x8(a, b, …, h)` | one number per lane | +| `f32x8(list, i)` | `list[i]` … `list[i+7]` (a `[]f32` or `ptr[f32]`) | +| `f32x8(v)` | an 8-lane vector converted lane by lane (`f32x8(ints)`, `i32x8(floats)`) | +| `v + w`, `v * 2.0`, `-v` | lane by lane; a plain number counts for every lane (`/` only on floats) | +| `& \| ^ << >> ~` | lane by lane, whole-number lanes | +| `v == w`, `v != w` | bool: every lane equal / not | +| `v[i]`, `v[i] = x`, `v.len` | a lane; the number of lanes | +| `min(v, w)`, `max`, `abs(v)`, `clamp(v, lo, hi)` | lane by lane | +| `v.sum()`, `v.min()`, `v.max()`, `v.dot(w)` | one number from all the lanes | +| `v.sqrt()`, `v.floor()`, `v.ceil()`, `v.round()`, `v.fma(b, c)` | every lane (float lanes; `fma` = `v * b + c`) | +| `v.store(list, i)` | lanes into `list[i]` … | + +--- + ## Text `str` | Code | Gives | diff --git a/docs/en/for-beginners.md b/docs/en/for-beginners.md index fd3bedc..935e0b6 100644 --- a/docs/en/for-beginners.md +++ b/docs/en/for-beginners.md @@ -53,8 +53,9 @@ The computer reads your program from top to bottom, line by line. ## 2. Getting Finch ready -Finch runs on **Linux** computers. If a teacher, parent or friend has already set it up -for you, skip to the next section. +Finch runs on **Linux**, **macOS** and **Windows** computers. If a teacher, parent or friend has already set it up +for you, skip to the next section. (On a Mac or on Windows, ask someone to follow the +[guide for technicians](for-technicians.md#2-installing): it has a ready-made Finch for both.) Otherwise, open the **Terminal** program (a window where you type commands) and type these lines one by one, pressing **Enter** after each: @@ -76,7 +77,7 @@ To check it works, type: ./build/finch version ``` -You should see something like `Finch 2.5.0`. +You should see something like `Finch 2.6.0`. --- diff --git a/docs/en/for-engineers.md b/docs/en/for-engineers.md index 56ce0a9..e018e5a 100644 --- a/docs/en/for-engineers.md +++ b/docs/en/for-engineers.md @@ -1,13 +1,11 @@ # Finch for Engineers -**The internals of Finch 2.5, whose compiler is written in Finch.** How source becomes tokens, an AST and LLVM IR *as text*, +**The internals of Finch 2.6, whose compiler is written in Finch.** How source becomes tokens, an AST and LLVM IR *as text*, which `clang` compiles and links with a small C runtime; how C headers are read with libclang from Finch itself and how the C calling conventions are reproduced by hand; the type and ownership rules; how the compiler builds itself from a seed; and the language server. Assumed: you can read Finch (see the [technicians' guide](for-technicians.md)) and have at least seen LLVM IR. -The compiler's first version, in C++ on LLVM's C++ API, is kept on the `before-bootstrap` branch; this guide describes -the Finch one. > 🇵🇱 Polska wersja: [dla-inzynierow.md](../pl/dla-inzynierow.md) @@ -34,7 +32,7 @@ the Finch one. ## 1. Architecture The compiler has no LLVM library inside it. It writes LLVM IR as **text** and lets `clang` do optimisation, code -generation and linking. That is what makes it small (about 8k lines) and what lets it be written in Finch itself. +generation and linking. That is what makes it small (about 12k lines) and what lets it be written in Finch itself. ``` main.fch ─► loader ── lex ── parse ──► Program (+ every imported module, recursively) @@ -60,7 +58,7 @@ Design decisions that shape the rest: Finch type (a string kind: `int`, `f32`, `str`, `array`, `map`, `struct`, `enum`, `named`…) and flags. - **Let clang do the work.** Locals are `alloca`s promoted by `mem2reg`. Optimisation is clang's `-O2` (or `-O0` with `-g`). - **Values, not references.** Arrays, strings and structs have value semantics; ownership is decided at compile time (§8). -- **A tiny C runtime** (`runtime/finch_rt.c`, ~760 lines) does what is tedious in IR: strings, array growth, maps, input, +- **A small C runtime** (`runtime/finch_rt.c`, ~1150 lines) does what is tedious in IR: strings, array growth, maps, input, files, panics. It is compiled once and cached in `~/.cache/finch`. --- @@ -70,17 +68,17 @@ Design decisions that shape the rest: | File | Lines | Responsibility | |---|---|---| | `src/lexer.fch` | 470 | Tokens, positions, `"""` text, `"\(x)"` turned into `("…" + str(x) + "…")` tokens | -| `src/ast.fch` | 120 | One `Node` struct with a `kind` string, children, type names, positions (`line`, `endLine`) | +| `src/ast.fch` | 170 | One `Node` struct with a `kind` string, children, type names, positions (`line`, `endLine`) | | `src/parser.fch` | 910 | Recursive descent: declarations, statements, expressions, generics (`[T]`), `enum`, `match`, `export`, `link`, `const`, `test "…" { }` | -| `src/gen.fch` | 6450 | Type check + IR: structs, functions, methods, scopes and drops, statements, expressions, built-ins, maps, errors as values, generics (monomorphisation), enums and `match`, `-g` debug metadata, the `index` mode | +| `src/gen.fch` | 7030 | Type check + IR: structs, functions, methods, scopes and drops, statements, expressions, built-ins, maps, errors as values, generics (monomorphisation), enums and `match`, vectors (SIMD), `-g` debug metadata, the `index` mode | | `src/cimport.fch` | 790 | libclang: functions, structs and unions (fields, offsets, padding), enums, constants, globals, macros (also struct-valued) | -| `src/platform.fch` | 90 | `Target`: triple, data layout, C compiler, signedness of `char` per system | -| `src/linker.fch` | 430 | The cache (runtime, program object, C/C++ files), libraries (`link`, pkg-config), link-error advice | -| `src/main.fch` | 515 | Driver: CLI, loader with module lookup and overlays, `build/run/test/fmt/ir/index/lsp` | +| `src/platform.fch` | 120 | `Target`: triple, data layout, C compiler, signedness of `char` per system | +| `src/linker.fch` | 480 | The cache (runtime, program object, C/C++ files), libraries (`link`, pkg-config), link-error advice | +| `src/main.fch` | 530 | Driver: CLI, loader with module lookup and overlays, `build/run/test/fmt/ir/index/lsp` | | `src/manifest.fch` | 240 | `finch.toml`: a small TOML reader, per-target lists | | `src/fmt.fch` | 225 | `finch fmt` and LSP formatting | -| `src/lsp.fch` | 910 | The language server (§11) | -| `src/docs.fch` | 110 | One description of the built-ins, shared by hover and completion | +| `src/lsp.fch` | 950 | The language server (§11) | +| `src/docs.fch` | 140 | One description of the built-ins, shared by hover and completion | | `std/*.fch` | 1040 | `json`, `path`, `time` (+`_windows`), `proc` (+`_windows`), `fs`, `thread` | | `runtime/finch_rt.c` | 1150 | The C runtime: text, arrays, maps, files, float printing, fs, threads, the test runner, the call chain | | `bootstrap/` | | `build.sh`, `install.sh`, the seeds (§10) | @@ -168,6 +166,9 @@ offset 0, and a field is read through memory. They can't be passed by value to C `ir` prints the IR; `index` prints the records the language server uses (§11); `--shared` makes a shared library of the `export fn`s. +`--cpu ` (or `cpu` in `finch.toml`) adds `-march=` on x86-64 or `-mcpu=` on ARM64 to the clang that +compiles the IR (not the runtime); it is part of the cache key. + `--target windows|linux|arm64|macos|android|` changes the triple and data layout, the C `char` signedness, the ABI rules above, the OS variant of modules (`name_windows.fch`), and the C compiler: `FINCH_CC` or `clang --target=…`. Linking asks `pkg-config` for flags for `link "lib"`; when the linker fails the output is analysed and the likely missing @@ -225,6 +226,7 @@ statement, so their shape (and the `"""` dedent) is kept. | `map[K]V` | `{ ptr, i64, i64, i64, ptr, i64 }` (entries, len, used, cap, index, icap; see src/gen.fch) | | `ptr`, `ptr[T]`, `null` | `ptr` (opaque) | | `[N]T` | `[N x T]` | +| `f32x8`, `i8x32`, … | `<8 x float>`, `<32 x i8>` (§7, Vectors) | | Finch struct | named struct type, natural layout | | a method's `self` | `ptr` (the first parameter) | | the result of `-> T!` | `{ i1 ok, T value, str error }` (`i8` in place of `T` for `-> !`) | @@ -235,11 +237,16 @@ Signedness lives only in the Finch type and selects `sdiv/udiv`, `ashr/lshr`, `i ### Values -```cpp -struct Value_ { Value *v; FType type; bool literal; bool fresh; }; +```c +struct Val { + str v // the IR operand: %t12, 42, @.str.3, zeroinitializer + ast.Type ty + bool fresh + bool lit +} ``` -- **`literal`**: a number written in the code or an imported C constant. It may become any integer type +- **`lit`**: a number written in the code or an imported C constant. It may become any integer type it fits (`fits()`), or `f32`. It survives unary minus and literal-only arithmetic that folds to a constant; explicit conversions clear it (`u8(250)` is a typed constant, so `u8(250) + 10` wraps to 4). - **`fresh`**: an owning value (str/array/owning struct) that nobody holds yet: the result of a call, @@ -281,6 +288,38 @@ Pointers/null: `icmp eq/ne ptr` only. `str`: `finch_str_eq` (length + `memcmp`) `finch_str_cmp` for ordering. Floats: `oeq olt ole ogt oge` and `une` for `!=`. Integers/char by signedness (char unsigned). Bool: equality only. Arrays/structs: error. +### Vectors + +`ast.vecType(name)` recognises `x` (lanes `i8`…`u64`, `f32`, `f64`; N = 2…64; at most 512 bits) and gives +`Type(kind: "vec", elem: [lane], count: N)`, which is an LLVM vector ``. The parser takes such a name wherever a +type goes; a call `f32x8(…)` goes to `vecMake()`. + +| Finch | IR | +|---|---| +| `f32x8(x)` | `insertelement` into lane 0 + `shufflevector … zeroinitializer` (a splat) | +| `f32x8(a, …, h)` | a chain of `insertelement` | +| `f32x8(list, i)` | bounds check of `i … i+7`, then `load <8 x float>, ptr …, align 4` (the lane's alignment) | +| `f32x8(i32x8 v)` | `sitofp`, `fptosi`, `sext`, `trunc`… on the whole vector | +| `v + w`, `v * 2.0` | `fadd`/`add`… on vectors; a scalar operand is coerced to the lane type, then splatted | +| `v[i]` | a place: `getelementptr , ptr %v, i64 i` after a bounds check; or `extractelement` on a temporary | +| `v == w` | `fcmp oeq`/`icmp eq` → `` → `llvm.vector.reduce.and` | +| `min`, `max`, `abs` | `llvm.minnum`/`maxnum`, `llvm.smin`/`umin`…, `llvm.fabs`/`llvm.abs` | +| `v.sum()`, `.min()`, `.max()` | `llvm.vector.reduce.fadd` (with `reassoc`), `.add`, `.fmin`, `.smin`… | +| `v.sqrt()`, `.floor()`, `.ceil()`, `.round()` | the `llvm.*` intrinsic of that name | +| `v.fma(b, c)` | `llvm.fmuladd` (fused where the target has FMA; `llvm.fma` would be a libm call per lane without it) | +| `v.store(list, i)` | bounds check, `store <8 x float> …, align 4` | + +Integer `/` and `%` are refused: neither x86 nor ARM has a lane-wise integer divide, and LLVM would scalarise it. +Ordering comparisons are refused because their result would be a mask, which has no Finch type. + +**Alignment.** LLVM gives `<8 x float>` an ABI alignment of 32 (its size), but `malloc` and Finch's `alloca … align 16` +promise only 16. `llty()` records every vector wider than 16 bytes in `g.wide`, and `resolveStructBody` records a struct +that holds one; `load()` and `store()` add `, align 16` for those types. Without it LLVM may emit `vmovaps` on a 16-byte +aligned address and crash. + +**Which instructions** is clang's choice: `--cpu ` adds `-march=` (x86-64) or `-mcpu=` (ARM64) to the +IR's compile, so the same IR becomes SSE2, AVX2 + FMA, AVX-512 or NEON code. Lanes wider than the registers are split. + --- --- @@ -428,8 +467,9 @@ and stops unless `finch1` and `finch` produce byte-identical IR (a fixpoint: the whatever built it). `bootstrap/build.sh seed` rewrites the seed from the current compiler; do it whenever the compiler's own source starts to use a feature the committed seed lacks. -A seed is per system because the C calling convention for the libclang calls is baked into it (§5). -Linux x86-64 and Linux ARM64 have seeds. macOS and Windows would need seeds made with Apple's and Microsoft's headers. +A seed is per system because the C calling convention for the libclang calls is baked into it (§5). There are seeds +for Linux x86-64 and ARM64, macOS ARM64 and x86-64 (made with Apple's SDK headers), and Windows x86-64 (made with +llvm-mingw's headers). CI builds the compiler from each one on that system and runs the tests there. --- @@ -518,13 +558,10 @@ evaluated twice and temporaries are never pointed to. ## 13. Known limitations -- The compiler runs on Linux x86-64 and ARM64 only (no seed for macOS or Windows yet). Programs can be built for all three. -- macOS output is only checked as IR; Windows and ARM64 are run under Wine and qemu in CI. - One error per run, no recovery. - `-g` debug info is best with `-O0`. -- The driver uses `sh` utilities (`rm`, `mkdir`), so it needs a POSIX shell. - Generics are monomorphised (code size grows with instantiations); there are no trait bounds, a wrong use is an error inside the instantiated body. - A failed test's owned memory is not freed (the test runner `longjmp`s out of it). -- `--target android` is checked to the object file only; linking needs the NDK and has not been run on a device. +- Vectors have no lane masks (`v < w`), shuffles or integer division. diff --git a/docs/en/for-technicians.md b/docs/en/for-technicians.md index 9e41283..712dea1 100644 --- a/docs/en/for-technicians.md +++ b/docs/en/for-technicians.md @@ -41,6 +41,7 @@ compiler works inside. For that, see the [guide for engineers](for-engineers.md) 28. [A project: finch.toml](#28-a-project-finchtoml) 29. [Threads](#29-threads) 30. [C++, Rust and Android](#30-c-rust-and-android) +31. [Vectors (SIMD)](#31-vectors-simd) --- @@ -67,19 +68,24 @@ messages that tell you how to fix the problem. ## 2. Installing -The Finch compiler is written in Finch (see the [engineers' guide](for-engineers.md)). It runs on **Linux** -(x86-64 and ARM64) and builds programs for **Linux, Windows and macOS**. It needs **clang 21** and **libclang** -(LLVM 21) to read C headers and to compile its output. +The Finch compiler is written in Finch (see the [engineers' guide](for-engineers.md)). It runs on **Linux** (x86-64 and +ARM64), **macOS** (Apple Silicon and Intel) and **Windows** (x86-64), and builds programs for all of them. On every +system it needs **clang** (it compiles each program with it) and **libclang** from LLVM 21 (it reads C headers with it). -The compiler does not run on macOS or Windows yet; it needs a seed made with that system's headers -([bootstrap/README.md](../../bootstrap/README.md) says why). Until then, build your Windows and macOS programs from Linux -with `--target` (below), or use the older C++ compiler from the `before-bootstrap` branch. +### Option A: a ready build -### Option A: a ready build (Linux) +Download the archive for your system from the [releases](https://github.com/Nexoniarz/Finch/releases) and unpack it. +Keep `runtime/` and `std/` next to the `finch` file (or install it, below). -Download `finch-linux-x64.tar.gz` or `finch-linux-arm64.tar.gz` from the -[releases](https://github.com/Nexoniarz/Finch/releases) and unpack it. Keep `runtime/` and `std/` next to the `finch` file -(or install it, below). You also need `clang` 21 and its libclang library: +| System | Archive | Also install | +|---|---|---| +| Linux x86-64 | `finch-linux-x64.tar.gz` | clang 21 and libclang (table below) | +| Linux ARM64 (Raspberry Pi 4/5, Graviton…) | `finch-linux-arm64.tar.gz` | the same | +| macOS, Apple Silicon | `finch-macos-arm64.tar.gz` | `xcode-select --install` (clang and the system headers), `brew install llvm@21` (libclang) | +| macOS, Intel | `finch-macos-x64.tar.gz` | the same | +| Windows x64 | `finch-windows-x64.zip` (`finch.exe` with `libclang.dll`) | [llvm-mingw](https://github.com/mstorsjo/llvm-mingw/releases) (the `ucrt-x86_64` zip): unpack it and put its `bin` folder on your PATH | + +On Linux: | Distribution | Packages | |---|---| @@ -88,12 +94,15 @@ Download `finch-linux-x64.tar.gz` or `finch-linux-arm64.tar.gz` from the | Arch | `clang pkgconf` | | Nix | `nix-shell` in the repository gives all of it | +On Windows, llvm-mingw gives Finch `clang`, the C headers and the linker in one folder; Windows programs built with it +need nothing else to run. Keep `libclang.dll` next to `finch.exe`. + ### Option B: build it yourself ```sh git clone https://github.com/Nexoniarz/Finch.git cd Finch -nix-shell # clang, libclang, pkg-config (or install them with your distribution) +nix-shell # Linux: clang, libclang, pkg-config (or install them with your distribution) bootstrap/build.sh # build/finch ./build/finch version ``` @@ -102,6 +111,26 @@ bootstrap/build.sh # build/finch `src/` with it, builds `src/` again with the result and checks the two are identical. If libclang isn't where the linker looks, tell it: `FINCH_LIBCLANG="-L/usr/lib/llvm-21/lib -lclang-21" bootstrap/build.sh`. +On a Mac, with Homebrew's LLVM: + +```sh +brew install llvm@21 +export FINCH_CLANG="$(brew --prefix llvm@21)/bin/clang" +export FINCH_LIBCLANG="-L$(brew --prefix llvm@21)/lib -lclang" +export FINCH_LLVM_INCLUDE="$(brew --prefix llvm@21)/include" +bootstrap/build.sh +``` + +On Windows, in Git Bash, with llvm-mingw on the PATH and the official LLVM for Windows (for `libclang.dll` and its headers) +unpacked to `C:/llvm`: + +```sh +export FINCH_LIBCLANG="C:/llvm/bin/libclang.dll" +export FINCH_LLVM_INCLUDE="C:/llvm/include" +export FINCH_LDFLAGS="-LC:/llvm/bin" +bootstrap/build.sh # build/finch.exe, with libclang.dll copied next to it +``` + ### Building for another system `--target` builds a program for another system. Finch needs that system's C compiler to link: @@ -151,7 +180,9 @@ options: -l link a C library, same as link "lib" in the file -I look for C headers there too --target build for another system: windows, linux, arm64, macos, android, or an LLVM triple - -g add debug info (for gdb / lldb / Visual Studio) + --cpu use every instruction of a processor: native (this one), x86-64-v3 (AVX2), + x86-64-v4 (AVX-512), apple-m1, ... (section 31) + -g add debug info (for gdb and lldb) -O0 skip optimizations --trace runtime errors show the chain of calls (on anyway for run, test, -g and -O0) --no-trace they don't (a plain finch build doesn't: keeping the chain makes calls slower) @@ -946,8 +977,7 @@ gdb ./game ``` `-g` adds debug info (lines, functions, variables, struct fields). `-O0` keeps every variable -visible; without it the optimizer may remove some. On Windows, `-g` writes CodeView, which the -Visual Studio debugger and WinDbg read. +visible; without it the optimizer may remove some. On Windows the debug info is DWARF as well: use the `lldb` that comes with llvm-mingw, or gdb. --- @@ -959,10 +989,11 @@ Visual Studio debugger and WinDbg read. | `… come from "x.h", but its library isn't linked` | Add `link "name"` at the top of the file. | | `the library 'x' wasn't found` | Not installed, or a wrong name. On Nix, add it to `shell.nix` and run inside `nix-shell`. | | `the C function 'f' can't be used from Finch yet` | It uses a union, bit fields or another unsupported type by value. Look for a variant that takes pointers. | +| `--cpu native`: the program stops with `Illegal instruction` on another computer | It was built for the processor that built it. Build without `--cpu`, or with an older one (`x86-64-v2`). | | `can't find the module 'x'` | Put `x.fch` next to the importing file, or set `FINCH_PATH`. | | `couldn't build the Finch runtime` | No working C compiler. Install gcc or clang, or set `CC`. | | A library is not found when the program starts | It was linked from a folder the system doesn't search. Run inside the same `nix-shell`, or install it system-wide. | -| Windows: `finch` can't link (`LNK…` errors about `libcmt`, `kernel32`) | Install Visual Studio Build Tools with *Desktop development with C++*. | +| Windows: `no C compiler found to link with ('clang')` | Put llvm-mingw's `bin` folder on your PATH (section 2). | | Windows: `finch.exe` doesn't start (missing `libclang.dll`) | Keep `libclang.dll` next to `finch.exe`. | | macOS: `finch` doesn't start (`Library not loaded: …libLLVM…`) | `brew install llvm@21`. | | macOS: `can't find the C header 'stdio.h'` | Install the command line tools: `xcode-select --install`. | @@ -1134,7 +1165,7 @@ Not there yet: - Interfaces. - Unicode-aware text: `.len`, `s[i]` and `upper()` work on bytes / ASCII. - Checks that stop two threads from changing the same array at once (use a mutex). -- Windows on ARM64, 32-bit systems, WebAssembly. `--target android` makes code for Android, but linking with the NDK and running on a device are untried. +- Windows on ARM64, 32-bit systems, WebAssembly. ### Warnings about big copies @@ -1193,6 +1224,7 @@ links = ["SDL3", "native/glue.cpp"] # libraries, and your own .c / .cpp / .o / libdirs = ["vendor/SDL3/lib"] # folders with libraries cflags = ["-DFAST"] # for your .c / .cpp files ldflags = ["-Wl,-rpath,$ORIGIN"] # for the linker +cpu = "native" # the same as --cpu native (section 31) [target.windows] # added only when building for Windows links = ["ws2_32"] @@ -1292,6 +1324,82 @@ Write the header (`float path_len(float x, float y);`, or let `cbindgen` write i **Android**: `finch build game.fch --target android --shared` makes `libgame.so` for 64-bit ARM Android (API 24 and up) with the NDK's clang, found through `ANDROID_NDK_HOME` (or `FINCH_CC`). Load it from an app, for example through -SDL3's Android project (the library's `SDL_main` is an `export fn`). This is new and only partly checked: Finch's -output compiles for Android (thread-local data goes through the emulated TLS older Android needs), but linking with the -NDK and running on a device have not been tried yet. +SDL3's Android project (the library's `SDL_main` is an `export fn`). Thread-local data goes through the emulated TLS +that older Android versions need. + +--- + +## 31. Vectors (SIMD) + +A processor can add or multiply several numbers with one instruction: 4 floats at once with SSE, 8 with AVX2, 16 with +AVX-512, 4 with ARM's NEON. That is SIMD (single instruction, multiple data), and it is how image filters, audio, physics +and neural networks get their speed. In Finch it is a type: **`f32x8` is 8 `f32` side by side**. + +```c +a := f32x4(1.0, 2.0, 3.0, 4.0) +b := f32x4(0.5) // 0.5 in every lane +print(a + b) // f32x4(1.5, 2.5, 3.5, 4.5) +print(a * 2.0) // a plain number counts for every lane: f32x4(2, 4, 6, 8) +print(a.sum(), a[2], a.len) // 10 3 4 +``` + +**The types** are a number type, `x`, and how many lanes: `f32x4` `f32x8` `f32x16`, `f64x2` `f64x4` `f64x8`, +`i8x16` `i8x32` `i8x64`, `i16x8` `i16x16`, `i32x4` `i32x8` `i32x16`, `i64x2` `i64x4`, and the same with `u` (unsigned). +2 to 64 lanes, at most 512 bits in all. A vector is a plain value like an `int`: it goes into variables, struct fields, +arrays, maps and function parameters, and `print` and `str` show it. + +**Making one:** + +| | | +|---|---| +| `f32x8(x)` | `x` in every lane | +| `f32x8(a, b, c, d, e, f, g, h)` | one number per lane | +| `f32x8(list, i)` | `list[i]` … `list[i+7]` from a `[]f32` (or through a `ptr[f32]`); stops the program if they aren't all in the list | +| `f32x8(v)` | another 8-lane vector converted lane by lane: `f32x8(ints)`, `i32x8(floats)` (cuts the fractions), `i16x8(i32s)` | + +**Working with it:** + +| | | +|---|---| +| `+ - * /` | lane by lane (`/` on floats only: x86 and ARM can't divide whole numbers lane by lane) | +| `& \| ^ << >> ~` | lane by lane, on whole-number lanes | +| `-v` | every lane negated | +| `v == w`, `v != w` | `true` if every lane is equal / if any differs | +| `v[i]`, `v[i] = x`, `v.len` | one lane; the number of lanes | +| `min(v, w)`, `max(v, w)`, `abs(v)`, `clamp(v, lo, hi)` | lane by lane; `lo`, `hi` and `w` can be plain numbers | +| `v.sum()`, `v.min()`, `v.max()` | all the lanes made into one number | +| `v.dot(w)` | `(v * w).sum()` | +| `v.sqrt()`, `v.floor()`, `v.ceil()`, `v.round()` | every lane (float lanes) | +| `v.fma(b, c)` | `v * b + c`, as one instruction where the processor has one | +| `v.store(list, i)` | writes the lanes to `list[i]` … (or through a `ptr`) | + +`<` and the other comparisons would give an answer per lane, so they aren't there; use `min`, `max` or `v[i] < w[i]`. + +**A real loop.** The usual shape: as many whole vectors as fit, then the rest one by one. + +```c +fn dot([]f32 a, []f32 b) -> f32 { + acc := f32x8(0.0) + i := 0 + while i + 8 <= a.len { + acc = f32x8(a, i).fma(f32x8(b, i), acc) + i += 8 + } + total := acc.sum() + while i < a.len { + total += a[i] * b[i] + i += 1 + } + return total +} +``` + +`examples/simd.fch` times this against the plain loop. + +**Which instructions.** By default a program runs on every processor of its system (on x86-64 that means SSE2, so an +`f32x8` is done as two halves). `--cpu native` (or `cpu = "native"` in `finch.toml`) lets LLVM use everything the processor +that builds it has: AVX2 and FMA, AVX-512, or NEON's newer parts on ARM. Such a program needs a processor like that one to +run. To pick a level instead: `--cpu x86-64-v3` (AVX2, most PCs since 2015), `--cpu x86-64-v4` (AVX-512), `--cpu apple-m1`. + +Lanes wider than the processor's registers are split by LLVM, and the same code builds for x86-64 and ARM64 alike. +`v.sum()` on float lanes adds them in whatever order is fastest, so the last digits can differ from adding them one by one. diff --git a/examples/simd.fch b/examples/simd.fch new file mode 100644 index 0000000..691227f --- /dev/null +++ b/examples/simd.fch @@ -0,0 +1,69 @@ +// Vectors (SIMD): eight f32 at once instead of one. +// +// finch run examples/simd.fch runs on any x86-64 or ARM64 processor +// finch run examples/simd.fch --cpu native uses all of this one's instructions (AVX2, AVX-512, NEON) +import time + +// one number at a time +fn dot_plain([]f32 a, []f32 b) -> f32 { + f32 total = 0.0 + for i in 0..a.len { + total += a[i] * b[i] + } + return total +} + +// eight at a time: f32x8(list, i) takes list[i] ... list[i+7], fma multiplies and adds in one step +fn dot_simd([]f32 a, []f32 b) -> f32 { + acc := f32x8(0.0) + i := 0 + while i + 8 <= a.len { + acc = f32x8(a, i).fma(f32x8(b, i), acc) + i += 8 + } + total := acc.sum() + while i < a.len { // the few that are left + total += a[i] * b[i] + i += 1 + } + return total +} + +// brighten every color: the same work on every lane, with min keeping it below 1 +fn brighten([]f32 pixels, f32 amount) { + i := 0 + while i + 8 <= pixels.len { + v := min(f32x8(pixels, i) * (1.0 + amount), 1.0) + v.store(pixels, i) + i += 8 + } +} + +fn main() { + []f32 a + []f32 b + for i in 0..4_000_000 { + a.push(f32(i % 100) / 100.0) + b.push(0.5) + } + + start := time.millis() + x := 0.0 + for round in 0..20 { + x = float(dot_plain(a, b)) + } + plain := time.millis() - start + + start = time.millis() + y := 0.0 + for round in 0..20 { + y = float(dot_simd(a, b)) + } + simd := time.millis() - start + + print("one at a time: ", str(x, 1), "in", plain, "ms") + print("eight at a time:", str(y, 1), "in", simd, "ms") + + brighten(a, 0.5) + print("brightened:", a[10], a[90], a.len) +} diff --git a/runtime/finch_rt.c b/runtime/finch_rt.c index ab806a3..e9a63fb 100644 --- a/runtime/finch_rt.c +++ b/runtime/finch_rt.c @@ -2,7 +2,7 @@ // It is embedded in the compiler, compiled once into a cache folder, and linked // statically into every Finch program. Everything here is plain C with no state. // -// Layout shared with the compiler (src/codegen.cpp): +// Layout shared with the compiler (src/gen.fch): // str = { char *ptr; int64 len; int64 cap } ptr is always NUL-terminated // cap > 0 : heap memory owned by the value (cap bytes allocated) // cap == 0 : a string literal, valid forever, never written to diff --git a/src/cimport.fch b/src/cimport.fch index 8d1701f..5d8fa14 100644 --- a/src/cimport.fch +++ b/src/cimport.fch @@ -1,10 +1,9 @@ // C headers, read through libclang, written in Finch. // // What `import "stdio.h"` teaches the compiler: the C functions, the constants (enum values and -// plain #define numbers), the extern variables (stdout) and the structs. The same job as -// src/cimport.cpp, done by calling libclang's C API through Finch's own C import -- which is -// what makes this the part that closes the bootstrap: to compile this file the self-hosted -// compiler has to read libclang's headers with this very code. +// plain #define numbers), the extern variables (stdout) and the structs. It calls libclang's C API +// through Finch's own C import -- so to compile this file, the compiler reads libclang's headers +// with this very code. // // C structs by value appear in types as the named type `module "C"`. import ast diff --git a/src/docs.fch b/src/docs.fch index 04dc13e..271660a 100644 --- a/src/docs.fch +++ b/src/docs.fch @@ -107,7 +107,7 @@ fn vecMethods() -> []Doc { doc("ceil", "ceil() -> V", "Every lane rounded up (f32/f64 lanes)."), doc("round", "round() -> V", "Every lane rounded to the nearest whole number, halves away from 0 (f32/f64 lanes)."), doc("abs", "abs() -> V", "Every lane without its minus sign."), - doc("fma", "fma(V b, V c) -> V", "v * b + c in one step, with one rounding (f32/f64 lanes)."), + doc("fma", "fma(V b, V c) -> V", "v * b + c, as one instruction where the processor has one (f32/f64 lanes)."), doc("store", "store([]T list, int i)", "Writes the lanes into list[i], list[i+1], ... (or through a ptr[T]).") ] } diff --git a/src/gen.fch b/src/gen.fch index 7aba27c..d3b3eb3 100644 --- a/src/gen.fch +++ b/src/gen.fch @@ -1,8 +1,7 @@ // Type checking and LLVM IR generation (as text), written in Finch. // -// The output is a .ll file that clang turns into a program, linked with the same -// C runtime the main compiler uses. This compiler copies values like the main one -// (so programs behave the same) but never frees them: it is the bootstrap compiler. +// The output is a .ll file that clang turns into a program, linked with Finch's C runtime +// (runtime/finch_rt.c). Values are copied when assigned and freed when their block ends. import ast import docs import cimport @@ -477,7 +476,7 @@ fn T(str kind) -> ast.Type { return ast.simple(kind) } -// `int` is i64 and `float` is f64, as in the C++ compiler: the everyday names +// `int` is i64 and `float` is f64: the everyday names // for the two default sizes, and the only way they are ever written back. fn isSigned(ast.Type t) -> bool { k := t.kind @@ -4642,11 +4641,12 @@ fn convert(ptr[Gen] g, ast.Node c, ast.Type to) -> Val { return v } -// ---------- the C calling convention for structs passed by value (System V x86-64) ---------- +// ---------- the C calling convention for structs passed by value ---------- // -// A struct of up to 16 bytes travels in registers, one INTEGER or SSE class per eightbyte; +// System V x86-64: a struct of up to 16 bytes travels in registers, one INTEGER or SSE class per eightbyte; // bigger ones are copied to the stack (byval) and returned through a hidden pointer (sret). -// Other targets (Windows, ARM64) have their own rules and are not done here yet. +// ARM64 (AAPCS64, and Apple's variant) and Windows x64 have their own rules: classifyArm64, and the +// windows branch of classify. struct Abi { str kind // "direct", "expand" (in registers: `parts`) or "memory" @@ -6992,7 +6992,9 @@ fn vecMethod(ptr[Gen] g, ast.Node m, Val v) -> Val { need(g, m, 2) b := asVec(g, expr(g, m.kids[1]), vt, "fma", m.kids[1]) c := asVec(g, expr(g, m.kids[2]), vt, "fma", m.kids[2]) - return Val(v: vecIntrinsic(g, lt, "fma", vt, [lt, lt, lt], [v.v, b.v, c.v], ""), ty: vt) + // fmuladd: one fused instruction where the processor has one (FMA, NEON), a multiply and an add where + // it doesn't (llvm.fma would call a slow library function there, to round only once) + return Val(v: vecIntrinsic(g, lt, "fmuladd", vt, [lt, lt, lt], [v.v, b.v, c.v], ""), ty: vt) } need(g, m, 0) return Val(v: vecIntrinsic(g, lt, n, vt, [lt], [v.v], ""), ty: vt) diff --git a/src/main.fch b/src/main.fch index 7c59c14..8bf35bd 100644 --- a/src/main.fch +++ b/src/main.fch @@ -1,7 +1,4 @@ -// finch-boot: the Finch compiler written in Finch. -// -// finch-boot build [-o name] compile a program -// finch-boot ir print the LLVM IR +// finch: the Finch compiler, written in Finch (`finch` with no arguments lists the commands). // // It reads the program and the modules it imports, checks it, writes LLVM IR as text // and lets clang turn that into a program, linked with Finch's C runtime. diff --git a/src/platform.fch b/src/platform.fch index 44a678f..2cbb0c2 100644 --- a/src/platform.fch +++ b/src/platform.fch @@ -1,6 +1,5 @@ // The system a program is built for, and what depends on it: the C compiler that links, the file // name of the program, how it is run here, and (through the flags) the C calling convention. -// The same as src/target.cpp. import host import "stdlib.h"