Compiler and docs on every system: macOS Intel in CI, releases made by CI, docs for vectors and --cpu

- CI: macOS on Apple Silicon and Intel (macos-15, macos-15-intel); a tag v* attaches every package
  (Linux x64/ARM64, macOS ARM64/x64, Windows x64, the VS Code extension) to its release
- vectors: fma is llvm.fmuladd (llvm.fma was a libm call per lane without FMA hardware: 3.5x slower)
- examples/simd.fch: a dot product eight at a time, timed against the plain loop
- README and the English guides: installing on Linux, macOS and Windows; vectors (SIMD); --cpu;
  -g is DWARF on Windows too (lldb from llvm-mingw); no more mention of the C++ compiler
- comments left from the C++ compiler (src/target.cpp, src/codegen.cpp, "never frees") brought up to date

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Psc42moPLwP43Cmb9oZqSR
This commit is contained in:
Claude 2026-11-01 13:57:28 +00:00
commit 0bf66740df
No known key found for this signature in database
15 changed files with 463 additions and 171 deletions

View file

@ -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

View file

@ -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]).")
]
}

View file

@ -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)

View file

@ -1,7 +1,4 @@
// finch-boot: the Finch compiler written in Finch.
//
// finch-boot build <file.fch> [-o name] compile a program
// finch-boot ir <file.fch> 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.

View file

@ -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"