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
6 changes: 3 additions & 3 deletions .github/workflows/extended_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ jobs:
# No sccache launcher: this lane is nightly-only and runs uncached.
cmake --no-warn-unused-cli -B./build -G "${{ matrix.cmake_generator }}" -DCMAKE_BUILD_TYPE:STRING=Release \
$ACTIVE_MODULES -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"
cmake --build ./build --config Release --target daslang daslang_static --parallel
cmake --build ./build --config Release --target daslang daslang_static dasbind_probe --parallel
;;
linux)
echo "BIN=./bin" >> $GITHUB_ENV
Expand All @@ -245,7 +245,7 @@ jobs:
-DDAS_CLANG_BIND_DISABLED=ON \
-DDAS_GLFW_DISABLED=OFF \
-G "${{ matrix.cmake_generator }}" $ACTIVE_MODULES
cmake --build ./build --config Release --target daslang daslang_static
cmake --build ./build --config Release --target daslang daslang_static dasbind_probe
;;
*)
echo "BIN=./bin" >> $GITHUB_ENV
Expand All @@ -254,7 +254,7 @@ jobs:
-DCMAKE_OSX_ARCHITECTURES="${{ matrix.architecture_string }}" \
-DDAS_GLFW_DISABLED=OFF \
-G "${{ matrix.cmake_generator }}" $ACTIVE_MODULES
cmake --build ./build --config Release --target daslang daslang_static
cmake --build ./build --config Release --target daslang daslang_static dasbind_probe
;;
esac

Expand Down
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1634,6 +1634,9 @@ if (NOT ${DAS_TOOLS_DISABLED})
# test time + dasModuleSQLITE; both must already be defined as targets).
add_subdirectory(tests/exe-paths)

# The probe library tests/dasbind binds through [extern] (lands in lib/).
add_subdirectory(tests/dasbind)

# Build daslang utilities as standalone executables (-exe mode)
add_subdirectory(utils)

Expand Down
50 changes: 50 additions & 0 deletions src/builtin/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,53 @@ set; `0` disables eviction), never the record just written. `install` touches th
reads, so a record in use is the newest and a stale variant the oldest. Only the default
directory is pruned - an explicit `-module-cache <path>` is the user's - and the limit variable
is the one `DAS*` name the record key skips, since it decides nothing about a compile.

## 3. The interpreter's `[extern]` call

An `[extern]` function (`module_builtin_dasbind.cpp`) is called from the interpreter through
a wrapper `vec4f (*)(void * fn, vec4f * args)`: the node evaluates every argument into a
`vec4f` lane and the wrapper calls `fn` through a C prototype. The wrappers are generated -
`generate_x86_64_calls.das` writes `win_x86_64_wrapper.inc`, `systemV_64_wrapper.inc` and
`systemV_64_extra_wrapper.inc` - one per argument count, result class and float mask of the
first register-class arguments, indexed from 0: four positions on Windows, six on SystemV,
with every integer-class argument spelled `int64_t`, every float-class one `double`, and the
result `int64_t` or `vec4f`. Float class is a `float` or `double` by value; a ref, whatever it
points to, is a pointer and so integer class, on the table and the layout alike. A SystemV call with more than six arguments and a float at index
6 or later needs a prototype from the extra table, keyed by argument count, result class (1 for
a float or double result, as the main table indexes it) and the full mask; its entries are the
hand-listed `systemV_extra` rows in the generator - a new shape is added there and the
generator re-run - and a shape the list lacks is a compile error naming the mask.

The C prototype passes a stack argument as an 8-byte slot. That fits Windows x64, where the
mask covers four positions and everything past the fourth argument is an 8-byte stack slot,
float included. It fits SystemV x86-64 too: its float registers past index 5 are exactly the
extra list's cases. It does not fit arm64: eight float registers, so a float at index 6 or 7
belongs in d6 or d7, and on Apple the stack is packed at natural size and alignment - a 32-bit
`int` in 4 bytes, a `bool` in 1 - where Linux and Android keep 8-byte slots. So on arm64
(Apple, Linux, Android; a Windows arm64 build keeps the table, since `DAS_BIND_ARM64_LAYOUT`
turns on for `__APPLE__` and `__linux__` only while `DAS_BIND_EXTERNAL` is on for every 64-bit
Windows) a call with more than six arguments leaves the table: at bind time the
function's das types produce an `Arm64Layout` - which argument rides in x0-x7, which in d0-d7,
and at which byte offset of a stack image the rest sit, packed on Apple and slotted elsewhere -
and the node calls `das_arm64_call`, an assembly trampoline that copies the image below its
own frame, loads the sixteen argument registers, calls, and stores x0 and d0 for the node to
return. A call of six or fewer arguments has no stack argument and no float past the mask
under any of these ABIs, so it keeps the table there too. A packed width comes from the das
Comment thread
borisbat marked this conversation as resolved.
type: a ref, pointer or string is 8 bytes, a scalar its `getBaseSizeOf`, and anything else (an
aggregate by value, which the binder does not support) rides as 8 bytes, as the table would
pass it. A layout lives for the process - `computeArm64Layout` keeps every layout in a static
vector it never frees - because a re-applied `[extern]` replaces the module's function object
while an earlier context's call node, which carries the layout pointer, may still run.

The JIT uses neither: `modules/dasLLVM/daslib/llvm_jit.das` (repo root) emits an extern call
with the function's own LLVM types, so a compiled call is right on every platform. The wrapper
table and the layout are the interpreter's alone, which is why an ABI defect there shows only
in interpreted code - a macro context, a `[no_jit]` function, a plain run. `tests/dasbind`
probes both paths against `tests/dasbind/probe`, a library whose functions sum their arguments
under distinct multipliers, so one argument in the wrong register or slot changes the total.
`tests/dasbind/CMakeLists.txt` builds the probe on a 64-bit host that builds and dlopens a
shared library at test time - not wasm, Android or iOS, none of which walks `tests/` - and
`tests/.das_test` skips the suite only on a 32-bit host, where the probe is not built, and under
dastest's `--ser`/`--deser` sweep, because a deserialized program never applies `[extern]` and
so never manufactures the `__dasbind__` function it names in the `dasbind` module; a 64-bit
desktop tree without the library fails the suite instead of skipping it.
19 changes: 12 additions & 7 deletions src/builtin/REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
`strings` and `jit`. What the scan enforces, and which shared generic helpers it exempts, is
read from the scan itself.

- **A diff that adds or changes a bind in a module the scan covers rebuilds the binary from
that diff before the folder's gate runs** - the scan reads the binds compiled into the running
binary, so a stale binary is a false green.
- **A diff that adds or changes a bind - any `addExtern*` call - in a module the scan covers
rebuilds the binary from that diff before the folder's gate runs** - the scan reads the binds
compiled into the running binary, so a stale binary is a false green.

- **A diff that adds a module under this folder adds it to `review_nttp.das`'s `require` list in
the same change - directly, or through the daslib wrapper that requires it.** A module the list
Expand Down Expand Up @@ -47,13 +47,18 @@
in the same change.** A replayed start never runs the descriptor, so an effect the recorder
does not see is an effect every warm start silently lacks.

- **A diff that moves a bind between modules - an `addExtern` or `addExternInline` call whose
module changes, or a builtin whose `vector<T>` functions follow a type to another module -
bumps `LLVM_JIT_CODEGEN_VERSION` in `modules/dasLLVM/daslib/llvm_jit_plan.das` (repo root), in
the same change.** The JIT's DLL cache key folds the codegen version and each function's AST
- **A diff that moves a bind between modules - any `addExtern*` call whose module changes, or a
builtin whose `vector<T>` functions follow a type to another module - bumps
`LLVM_JIT_CODEGEN_VERSION` in `modules/dasLLVM/daslib/llvm_jit_plan.das` (repo root), in the
same change.** The JIT's DLL cache key folds the codegen version and each function's AST
hash, never the module an extern lives in, so a cached DLL binds the old name and crashes on
the hit.

- **A diff that changes the wrapper tables, the `systemV_extra` list, the arm64 layout or the
`das_arm64_call` trampoline updates section 3 of `ARCHITECTURE.md` in the same change.** Two
comments in `module_builtin_dasbind.cpp` cite that section instead of restating it, so a
stale section is what the next reader trusts.

- **A diff that changes what `ModuleFileCache::defaultPath` folds into the module-cache key -
the binary, the command line, the environment names, or which script arguments count - updates
the cache-key paragraph of `ARCHITECTURE.md` in the same change.** The key is what stops a
Expand Down
37 changes: 27 additions & 10 deletions src/builtin/generate_x86_64_calls.das
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,17 @@ let ARG12_D = (1 << 12)
let ARG13_D = (1 << 13)
let ARG14_D = (1 << 14)
let ARG15_D = (1 << 15)
let ARG16_D = (1 << 16)
let ARG17_D = (1 << 17)
let ARG18_D = (1 << 18)
let ARG19_D = (1 << 19)
let ARG20_D = (1 << 20)
let ARG21_D = (1 << 21)
let ARG22_D = (1 << 22)
let ARG23_D = (1 << 23)

let RES_D = 0
let RES_X = 1
let RES_FLOAT = 1
let RES_INT = 0

struct WrapperFn {
nargs : int
Expand All @@ -110,18 +118,27 @@ def genExtraWrapper(name : string; extra : array<WrapperFn>) {

let systemV_extra <- [WrapperFn(
// glPrimitiveBoundingBoxARB
nargs=8, res=1, perm=0 | ARG0_D | ARG1_D | ARG2_D | ARG3_D | ARG4_D | ARG5_D | ARG6_D | ARG7_D), WrapperFn(
nargs=8, res=RES_INT, perm=0 | ARG0_D | ARG1_D | ARG2_D | ARG3_D | ARG4_D | ARG5_D | ARG6_D | ARG7_D), WrapperFn(
// glDrawVkImageNV
nargs=11, res=1, perm=0 | ARG2_D | ARG3_D | ARG4_D | ARG5_D | ARG6_D | ARG7_D | ARG8_D | ARG9_D | ARG10_D), WrapperFn(
nargs=11, res=RES_INT, perm=0 | ARG2_D | ARG3_D | ARG4_D | ARG5_D | ARG6_D | ARG7_D | ARG8_D | ARG9_D | ARG10_D), WrapperFn(
// glPathGlyphsNV
nargs=10, res=1, perm=0 | ARG9_D), WrapperFn(
nargs=10, res=RES_INT, perm=0 | ARG9_D), WrapperFn(
// glPathGlyphRangeNV, glPathMemoryGlyphIndexArrayNV
nargs=9, res=1, perm=0 | ARG8_D), WrapperFn(
nargs=9, res=RES_INT, perm=0 | ARG8_D), WrapperFn(
// glPathGlyphIndexArrayNV
nargs=8, res=1, perm=0 | ARG7_D), WrapperFn(
// DUMMY
nargs = 13, res = RES_X, perm=ARG0_D | ARG13_D), WrapperFn(
nargs = 13, res = RES_D, perm=ARG0_D | ARG13_D
nargs=8, res=RES_INT, perm=0 | ARG7_D), WrapperFn(
// glMatrixFrustumEXT, glMatrixOrthoEXT
nargs=7, res=RES_INT, perm=0 | ARG1_D | ARG2_D | ARG3_D | ARG4_D | ARG5_D | ARG6_D), WrapperFn(
// glNamedProgramLocalParameter4dEXT, glNamedProgramLocalParameter4fEXT
nargs=7, res=RES_INT, perm=0 | ARG3_D | ARG4_D | ARG5_D | ARG6_D), WrapperFn(
// glGetPathSpacingNV
nargs=9, res=RES_INT, perm=0 | ARG5_D | ARG6_D), WrapperFn(
Comment thread
borisbat marked this conversation as resolved.
// tests/dasbind probe_dbl_at6
nargs=8, res=RES_FLOAT, perm=0 | ARG6_D), WrapperFn(
// tests/dasbind probe_dbl_at67
nargs=8, res=RES_FLOAT, perm=0 | ARG6_D | ARG7_D), WrapperFn(
// tests/dasbind probe_stack_narrow
nargs=20, res=RES_FLOAT, perm=0 | ARG0_D | ARG1_D | ARG2_D | ARG3_D | ARG4_D | ARG5_D | ARG6_D | ARG7_D | ARG18_D
)];

[export]
Expand Down
Loading
Loading