aboutsummaryrefslogtreecommitdiff
path: root/AGENTS
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-22 14:02:47 +0000
committer_ov4 <ov4@ov4.org>2026-10-22 14:02:47 +0000
commite57e63d0d2a0e4c38460d399a0c62d140a633706 (patch)
treeb2f0cea741b0f87bf35002e48a1aa66fcba62030 /AGENTS
parentf694cc443ccc17a494cd195151882ab5e491bbb2 (diff)
docs: document C-struct records for EXTERN
Describe which TYPE records count as C structs, passing them by value and by pointer as parameters, RETURNS and variadic arguments, and the recursive pointer types EXTERN accepts. Note the clangCodeGen dependency and the CABI sources in the architecture notes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'AGENTS')
-rw-r--r--AGENTS/ARCH/DEPENDENCIES.md3
-rw-r--r--AGENTS/ARCH/LAYOUT.md1
-rw-r--r--AGENTS/ARCH/LINKING.md7
-rw-r--r--AGENTS/PITFALLS/exceptions-and-c-interop.md10
-rw-r--r--AGENTS/SUBSYSTEMS/EXTERN.md51
5 files changed, 65 insertions, 7 deletions
diff --git a/AGENTS/ARCH/DEPENDENCIES.md b/AGENTS/ARCH/DEPENDENCIES.md
index 75a165e..24a34a6 100644
--- a/AGENTS/ARCH/DEPENDENCIES.md
+++ b/AGENTS/ARCH/DEPENDENCIES.md
@@ -32,3 +32,6 @@ Build pin: **LLVM 23 and upstream Clang 23** (any 23.x release); 23.1.3 is teste
by nightly CI on Linux, Windows and macOS. LLVM 23 uses `CreateGlobalString` for opaque string pointers,
`hasTerminator()` for block termination checks (its `getTerminator()` requires an
existing terminator), and an explicit `llvm::Triple` for the embedded target.
+The static Clang archives used are `clangDriver` (native linking) and `clangCodeGen`
+(the C ABI for EXTERN, through `clang/CodeGen/CodeGenABITypes.h`); CMake checks that
+both are static.
diff --git a/AGENTS/ARCH/LAYOUT.md b/AGENTS/ARCH/LAYOUT.md
index 422d4bb..805f1e1 100644
--- a/AGENTS/ARCH/LAYOUT.md
+++ b/AGENTS/ARCH/LAYOUT.md
@@ -25,6 +25,7 @@
| `lib/CodeGen/MemoryHandler.cc` | Explicit raw memory allocation, resizing and release with a live-address tracking list |
| `lib/CodeGen/FileHandler.cc`, `FileRuntimeIO.cc` | File runtime setup/lookup/open and call entry points; read/write/close/EOF helper bodies |
| `lib/CodeGen/ArrayHandler.cc`, `ArrayParameters.cc`, `ArrayOutput.cc` | Array allocation/indexing, parameter binding/arguments and whole/subarray output |
+| `include/cps/CABI.h`, `lib/CodeGen/CABI/ClangABI.cc`, `lib/CodeGen/CABI/Lowering.cc` | EXTERN C ABI: the ABI description, the only file that includes clang CodeGen headers, and the generic lowering of signatures, arguments and returns |
| `lib/CodeGen/*Handler.cc`, `FunctionGen.cc`, `RuntimeCheck.cc`, `TypeSystem.cc` | Helpers owned by CodeGen (including exception emission) |
| `CMakeLists.txt` | 3 static libs (CPSLexer, CPSParser, CPSCodeGen) + `cpsc`. **New .cc files must be added to the source lists here** |
| `tools/driver/NativeCompiler.cc`, `cmake/NativeCompiler.cmake` | Host object generation, embedded Clang/LLD linking and compressed link resources |
diff --git a/AGENTS/ARCH/LINKING.md b/AGENTS/ARCH/LINKING.md
index 7bf1ecd..f43d5c3 100644
--- a/AGENTS/ARCH/LINKING.md
+++ b/AGENTS/ARCH/LINKING.md
@@ -1,6 +1,6 @@
# Static linking and native compilation
-All modes require static LLVM, Clang Driver and LLD archives. `cmake/EmbeddedLLVM.cmake` also selects
+All modes require static LLVM, Clang Driver, Clang CodeGen and LLD archives. `cmake/EmbeddedLLVM.cmake` also selects
static compression/terminal support archives when LLVM uses them. Linux Clang
builds link the C++ runtime statically; MinGW uses `-static`. The tested Debian
LLVM 23 executable has only libc, libm and the system loader as dynamic dependencies.
@@ -12,6 +12,11 @@ plus the system bcrypt library. XML definitions are loaded with `--whole-archive
to satisfy dllimport references in the prebuilt LLVM archive. `-static` alone
does not replace an imported target's explicit `.dll.a` path.
+`CPSCodeGen` links `clangCodeGen` and `clangDriver` (for `createInvocation`) with
+Clang's include directories: EXTERN signatures use clang's C ABI code
+([SUBSYSTEMS/EXTERN.md](../SUBSYSTEMS/EXTERN.md#c-structs-and-the-c-abi)).
+`cmake/NativeCompiler.cmake` rejects a non-static `clangCodeGen` like `clangDriver`.
+
`NativeCompiler` clones the module, sets the host triple/DataLayout and emits a PIC
object. Clang Driver constructs one platform linker job, which is dispatched to
embedded LLD without launching a process. `cmake/NativeCompiler.cmake` runs
diff --git a/AGENTS/PITFALLS/exceptions-and-c-interop.md b/AGENTS/PITFALLS/exceptions-and-c-interop.md
index 511c7e1..104be41 100644
--- a/AGENTS/PITFALLS/exceptions-and-c-interop.md
+++ b/AGENTS/PITFALLS/exceptions-and-c-interop.md
@@ -31,8 +31,14 @@ Part of the [pitfalls index](../PITFALLS.md).
## EXTERN "C" extension
-- Top-level, body-less; parameters/results are C types only, no BYREF/OVERLOAD/structs.
- Variadic arguments must be C values (default promotions).
+- Top-level, body-less; parameters/results are C types, C-struct records or pointer
+ types finally pointing to either (not `^INTEGER`, not `TYPE P = ^P`); no
+ BYREF/OVERLOAD. Variadic arguments must be C values (default promotions), C-struct
+ records or pointers (never promoted); `^x` of a C value passes as `void*`.
+- A C-struct record's fields must have those types too: one INTEGER, REAL, CHAR,
+ BOOLEAN, STRING, enum or CLASS field rejects the whole record (`... uses record R,
+ which is not a C struct: field F of R has type T`). cpsc never sees the C
+ declaration: field order and types must match it by hand.
- The driver checks every EXTERN symbol against INCLUDE_OBJ inputs before CodeGen;
`"__libc"` must be included explicitly even though the runtime always links libc.
- Only `.o` user objects; INCLUDE_OBJ is global across inputs. `-S` does not link them.
diff --git a/AGENTS/SUBSYSTEMS/EXTERN.md b/AGENTS/SUBSYSTEMS/EXTERN.md
index 4d0616a..f71bfd7 100644
--- a/AGENTS/SUBSYSTEMS/EXTERN.md
+++ b/AGENTS/SUBSYSTEMS/EXTERN.md
@@ -8,14 +8,57 @@ Part of the [subsystem notes](../SUBSYSTEMS.md). Related: [functions](FUNCTIONS.
- The parser builds a body-less `PrototypeAST` (IsExternal, IsVariadic, unnamed `argN`
params).
-- `FunctionGen::emitExternPrototype` validates C types, records a `FuncSig` with
- `IsExternal`, the declared `FunctionType` and sext/zext attributes for sub-32-bit
- integers, and `getOrInsertFunction`s the exact symbol.
+- `FunctionGen::emitExternPrototype` admits types with `TypeSystem::isExternType`
+ (after `pointerBase` follows pointer types: a non-void C type or a C struct;
+ `^INTEGER` and cycles such as `TYPE P = ^P` fail), records a `FuncSig` with
+ `IsExternal` and the clang-arranged `FuncSig::ExternABI` (see below), and
+ `getOrInsertFunction`s the exact symbol.
- Identical redeclarations append a FuncSig per declaring unit (visibility unchanged)
and `resolveCall` takes the first visible one; mismatched signatures are errors.
- Calls go through `FunctionGen::emitCall(const FuncSig &, ...)` with that type, so a
runtime declaration of the same libc symbol with another type is harmless.
-- `CodeGen::emitVariadicArgument` applies C default promotions.
+- `CodeGen::emitVariadicArgument` applies C default promotions to C scalars and
+ passes C-struct records and pointers to them unchanged (C11 §6.5.2.2p6–7,
+ §7.16.1.1p2). `^x` of a C value or C struct passes as `void*`, like C's `&x`; a
+ pseudocode-typed `^x` keeps "must be a C value; cast it to a C type". Each variadic
+ call is arranged separately from the fixed-parameter count and the promoted types.
+
+## C structs and the C ABI
+
+- `TypeSystem::isCStruct`: a TYPE record (not a CLASS) whose fields, after
+ `pointerBase`, are non-void C types or C structs. A seen list stops recursion, so
+ self-referential lists terminate. Any pseudocode field type (INTEGER, REAL, CHAR,
+ BOOLEAN, STRING, enum, CLASS) fails: `<What> uses record <Rec>, which is not a C
+ struct: field <Field> of <Owner> has type <Type>`, where `<What>` is `EXTERN
+ parameter N of F`, `The return type of EXTERN_FUNCTION F` or `Variadic argument N
+ to F`. Other unsupported types keep `EXTERN parameter N of F must be a non-void C
+ type (got X)`.
+- Records already have C layout (`TypeSystem::defineRecord`: non-packed named
+ struct), so no conversion is needed to pass a pointer.
+- Every EXTERN signature, scalar-only ones included, is arranged by clang through the
+ FunctionGen member `ClangABI ABI`: `ClangABI::arrange` (`lib/CodeGen/CABI/ClangABI.cc`)
+ calls `clang::CodeGen::arrangeFreeFunctionCall` and translates each `ABIArgInfo`
+ into a `CABISignature` of `CABIArg`s (`include/cps/CABI.h`; Direct, Extend,
+ Indirect, Ignore; any other kind is an internal error).
+ - The lazy `ClangABI::State`, created on the first EXTERN, owns the clang
+ CodeGenerator. It targets `llvm::sys::getProcessTriple()` with options from
+ `clang::createInvocation`, so darwinpcs and char signedness match the C compiler.
+ - Scalars map by name, all pointers to `void*`, and each record to a cached
+ `RecordDecl`. A size mismatch is an internal error: `Internal: cannot arrange
+ EXTERN F: record R has N bytes in cpsc but M in C` (`cannot arrange the call to
+ F` for a variadic call).
+ - Keep clang code in this file: `clang::CodeGen` clashes with `cps::CodeGen`, clang
+ classes cannot be subclassed across the RTTI mismatch, and the CodeGenerator must
+ be destroyed before the ASTContext.
+- `lib/CodeGen/CABI/Lowering.cc` applies the description without architecture
+ branches: `getCABIFunctionType`, `getCABIAttributes` (sret/byval/align/signext/
+ zeroext), `lowerCABIArguments` (coercion through a temporary
+ `alloca {RecordTy, CoerceTy}`, flattening, copies for Indirect) and
+ `lowerCABIResult` (sret, or a coerced return stored back into a record).
+- The result matches the platform C compiler on Linux x86-64, Windows x86-64 (MinGW)
+ and macOS arm64. The latter two pass large records as a pointer to a copy
+ (non-byval Indirect); only nightly CI tests that path. MinGW `long double` should
+ now pass by reference, as in C, but is untested.
## Driver and object files