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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,7 @@ include/daScript/misc/platform.h
include/daScript/misc/vectypes.h
include/daScript/misc/arraytype.h
include/daScript/misc/rangetype.h
include/daScript/misc/float2string.h
include/daScript/misc/string_writer.h
include/daScript/misc/type_name.h
include/daScript/misc/memory_model.h
Expand All @@ -1043,6 +1044,7 @@ src/misc/globals.cpp
src/misc/hal.cpp
src/misc/sysos.cpp
src/misc/string_writer.cpp
src/misc/luau_float2string.cpp
src/misc/memory_model.cpp
src/misc/job_que.cpp
src/misc/handle_registry.cpp
Expand Down Expand Up @@ -1695,6 +1697,7 @@ SET(DAS_RELEASE_MISC_INCLUDE
include/daScript/misc/runtime_table_utils.h
include/daScript/misc/safebox.h
include/daScript/misc/smart_ptr.h
include/daScript/misc/float2string.h
include/daScript/misc/string_writer.h
include/daScript/misc/sysos.h
include/daScript/misc/type_name.h
Expand Down Expand Up @@ -1933,6 +1936,7 @@ install(FILES ${PROJECT_SOURCE_DIR}/include/dag_noise/LICENSE DESTINATION ${DAS_
install(FILES ${PROJECT_SOURCE_DIR}/include/vecmath/LICENSE DESTINATION ${DAS_INSTALL_DOCDIR} RENAME VEC_MATH.LICENSE)
install(FILES ${PROJECT_SOURCE_DIR}/3rdparty/fmt/LICENSE DESTINATION ${DAS_INSTALL_DOCDIR} RENAME FMT.LICENSE)
install(FILES ${PROJECT_SOURCE_DIR}/include/fast_float/LICENSE DESTINATION ${DAS_INSTALL_DOCDIR} RENAME FAST_FLOAT.LICENSE)
install(FILES ${PROJECT_SOURCE_DIR}/src/misc/LUAU.LICENSE DESTINATION ${DAS_INSTALL_DOCDIR} RENAME LUAU.LICENSE)

# utils/internal/ never installs — the folder is the audience decision
# (machine-checked by utils/REVIEW.das).
Expand Down
1 change: 1 addition & 0 deletions benchmarks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Every `.das` benchmark file in this directory tree is listed below, grouped by s
|---|---|
| `horizontal_reduce.das` | `hmin`/`hmax`/`hadd` over a hot float4 array - measures the JIT `llvm.vector.reduce.*` lowering vs the extern-call fallback (and interp); non-constant inputs + a global sink defeat const-fold / DCE |
| `exp.das` | scalar `exp` (vecmath polynomial in interp/AOT, `@llvm.exp` in JIT) against `exp_std` (the C library `expf`) and `exp_est` (the 4th-order vecmath estimate) over a hot float array; same DCE defeats |
| `scalar_crt.das` | the scalar float family (log, log2, exp2, pow, sin, cos, tan) against the CRT twins `UnitTest` binds (`crt_logf`...); log/sin/cos/tan still race the vecmath lane, while the exp2/log2/pow rows read as builtin-node-vs-extern overhead over the same CRT since the sim_policy switch; same shape as `exp.das` |

## audio/

Expand Down
137 changes: 137 additions & 0 deletions benchmarks/core/math/scalar_crt.das
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
options gen2
options persistent_heap

require dastest/testing_boost
require math
require UnitTest

let N = 65536

var g_sink = 0.

def fill(var xs : array<float>; lo : float; hi : float) {
xs |> resize(N)
var seed = 12345u
for (i in range(N)) {
seed = seed * 1664525u + 1013904223u
xs[i] = lo + float(seed >> 8u) / float(1 << 24) * (hi - lo)
}
}

[benchmark]
def log_scalar(b : B?) {
var xs : array<float>
fill(xs, 0.001, 100.)
b |> run("log/{N}", N) {
var acc = 0.
for (x in xs) {
acc += log(x)
}
g_sink += acc
}
b |> run("crt_logf/{N}", N) {
var acc = 0.
for (x in xs) {
acc += crt_logf(x)
}
g_sink += acc
}
b |> run("log2/{N}", N) {
var acc = 0.
for (x in xs) {
acc += log2(x)
}
g_sink += acc
}
b |> run("crt_log2f/{N}", N) {
var acc = 0.
for (x in xs) {
acc += crt_log2f(x)
}
g_sink += acc
}
b |> run("exp2/{N}", N) {
var acc = 0.
for (x in xs) {
acc += exp2(x * 0.05 - 3.)
}
g_sink += acc
}
b |> run("crt_exp2f/{N}", N) {
var acc = 0.
for (x in xs) {
acc += crt_exp2f(x * 0.05 - 3.)
}
g_sink += acc
}
}

[benchmark]
def pow_scalar(b : B?) {
var xs : array<float>
var ys : array<float>
fill(xs, 0.1, 10.)
fill(ys, -2., 2.)
b |> run("pow/{N}", N) {
var acc = 0.
for (x, y in xs, ys) {
acc += pow(x, y)
}
g_sink += acc
}
b |> run("crt_powf/{N}", N) {
var acc = 0.
for (x, y in xs, ys) {
acc += crt_powf(x, y)
}
g_sink += acc
}
}

[benchmark]
def trig_scalar(b : B?) {
var xs : array<float>
fill(xs, -1.4, 1.4)
b |> run("sin/{N}", N) {
var acc = 0.
for (x in xs) {
acc += sin(x)
}
g_sink += acc
}
b |> run("crt_sinf/{N}", N) {
var acc = 0.
for (x in xs) {
acc += crt_sinf(x)
}
g_sink += acc
}
b |> run("cos/{N}", N) {
var acc = 0.
for (x in xs) {
acc += cos(x)
}
g_sink += acc
}
b |> run("crt_cosf/{N}", N) {
var acc = 0.
for (x in xs) {
acc += crt_cosf(x)
}
g_sink += acc
}
b |> run("tan/{N}", N) {
var acc = 0.
for (x in xs) {
acc += tan(x)
}
g_sink += acc
}
b |> run("crt_tanf/{N}", N) {
var acc = 0.
for (x in xs) {
acc += crt_tanf(x)
}
g_sink += acc
}
}
6 changes: 3 additions & 3 deletions ci/REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
**Read `REVIEW_COMMON.md` (repo root) first - its contract binds this checklist.** Architecture
doc: `CLAUDE.md` (repo root).

**A diff that shrinks what `smoke_test_bundle.sh` - the script that checks a built release
bundle - rejects is a defect**: every bundle it failed before the diff still fails. A new
exemption may name only a file no existing check matched.
**A diff that shrinks what the bundle gate rejects - `smoke_test_bundle.sh` and the checkers
it runs (`check_shipped_skills.py`) - is a defect**: every bundle it failed before the diff
still fails. A new `--exclude` or skip may name only a file no check flagged before the diff.
2 changes: 1 addition & 1 deletion ci/smoke_test_bundle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ fi
# mode that left shipped binaries without their notices.
printf ' %-30s ' "third-party licenses present"
MISSING_LICENSES=""
for lic in URIPARSER DAG_NOISE VEC_MATH FMT FAST_FLOAT GLTF_SAMPLE_ASSETS \
for lic in URIPARSER DAG_NOISE VEC_MATH FMT FAST_FLOAT LUAU GLTF_SAMPLE_ASSETS \
HV OPENSSL LLVM Z3 MINIAUDIO OPENMPT CIPIC PUGIXML GLFW \
IMGUI FREETYPE MD4C JETBRAINS_MONO KHRONOS_GL \
VULKAN_HEADERS VOLK TREE_SITTER TREE_SITTER_ICU TREE_SITTER_C \
Expand Down
12 changes: 12 additions & 0 deletions include/daScript/misc/float2string.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include "daScript/misc/platform.h"

namespace das {
enum { DAS_F2S_BUFFER_SIZE = 48 };
// shortest round-trip float printing, spelled byte-for-byte as fmt's default "{}";
// returns the end of the written text, which is NOT null-terminated; the buffer is at
// least DAS_F2S_BUFFER_SIZE bytes - the emitter's fixed-length copies overshoot the text
DAS_API char * float2string ( char * buf, float value );
DAS_API char * double2string ( char * buf, double value );
}
14 changes: 12 additions & 2 deletions include/daScript/simulate/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,22 @@ the hot set; its cost is judged against the allocate/copy/rehash it rides.
The ledger the checklist's hot-path rule routes to. Each entry: what was added, where, why
correctness required it, and the alternative that was rejected.

- **CRT scalar transcendentals** (`sim_policy.h`) - the scalar float arms of `Exp`, `Exp2`,
`Log2` and `Pow` call the CRT; the `vec4f` arms stay on the vecmath polynomials, where
four lanes amortize the setup. The lane trick's `v_set_x`/`v_extract_x` round-trip is a
partial-register dependency chain scalar codegen does not break, so for these arms the
CRT call is cheaper than the inlined polynomial - that is the rejected alternative. `Log2`
is the one arm the swap does not speed up: it trades the `v_log2_est_p5` estimate for the
exact answer the JIT already computes, so interp, AOT and JIT agree. `log`, `sin`, `cos`
and `tan` stay on the lane, which is cheaper for them. The measurements behind the split:
`plans/benchmark_followups.md` (repo root), the scalar-exp section.

- **`das_ordered2`** (`aot.h`) - a two-member aggregate the AOT emitter wraps around any
binary op whose operands are not both side-effect-free, because braced aggregate init is
the C++ construct that guarantees left-to-right evaluation; a plain call argument list or
binary operator is unsequenced, and the interpreter and JIT both evaluate left-then-right.
Optimized builds flatten the wrapper to nothing (full-corpus A/B: regen + compile of all
AOT TUs is timing-neutral); an unoptimized AOT build pays a copy of both operand values
Optimized builds flatten the wrapper to nothing; an unoptimized AOT build pays a copy of
both operand values
plus an immediately-invoked lambda frame per wrapped op. Ops whose policy operands need a
ref cast decline the wrapper and keep the plain unordered emission. Rejected alternative:
hoisting operands to named temporaries in the emitter, which needs statement-position
Expand Down
8 changes: 4 additions & 4 deletions include/daScript/simulate/sim_policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,11 @@ namespace das {
static __forceinline int Floori ( float a, Context &, LineInfo * ) { return v_extract_xi(v_cvt_floori(v_set_x(a))); }
static __forceinline int Ceili ( float a, Context &, LineInfo * ) { return v_extract_xi(v_cvt_ceili(v_set_x(a))); }

static __forceinline float Exp ( float a, Context &, LineInfo * ) { return v_extract_x(v_exp(v_set_x(a))); }
static __forceinline float Exp ( float a, Context &, LineInfo * ) { return expf(a); }
static __forceinline float Log ( float a, Context &, LineInfo * ) { return v_extract_x(v_log(v_set_x(a))); }
static __forceinline float Exp2 ( float a, Context &, LineInfo * ) { return v_extract_x(v_exp2(v_set_x(a))); }
static __forceinline float Log2 ( float a, Context &, LineInfo * ) { return v_extract_x(v_log2_est_p5(v_set_x(a))); }
static __forceinline float Pow ( float a, float b, Context &, LineInfo * ) { return v_extract_x(v_pow(v_set_x(a), v_set_x(b))); }
static __forceinline float Exp2 ( float a, Context &, LineInfo * ) { return exp2f(a); }
static __forceinline float Log2 ( float a, Context &, LineInfo * ) { return log2f(a); }
static __forceinline float Pow ( float a, float b, Context &, LineInfo * ) { return powf(a, b); }
static __forceinline float Rcp ( float a, Context &, LineInfo * ) { return v_extract_x(v_rcp_x(v_set_x(a))); }
static __forceinline float RcpEst( float a, Context &, LineInfo * ) { return v_extract_x(v_rcp_est_x(v_set_x(a))); }

Expand Down
10 changes: 7 additions & 3 deletions modules/dasMetal/REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@ the diff puts it.** An emitted-text fixture answers to `tests/msl/REVIEW.md` (re
macro declares for it - a module-level global holding the kernel's MSL text or a compile
option - or the difference it makes to the emitted text.

- **A new construct the MSL emitter rejects at compile time ships a `tests/msl/_fail_closed/`
(repo root) fixture in the same change.** The same change asserts that construct's error
- **A diff that makes the MSL emitter reject at compile time a construct it accepted before -
a new rejection, or a widened condition on an existing one - ships a
`tests/msl/_fail_closed/` (repo root) fixture for that construct in the same change.** The
same change asserts that construct's error
needle in `tests/msl/test_msl_fail_closed.das`. An error needle is the substring of the
compile error that names the rejected construct. A diff that stops rejecting a construct
deletes that construct's `_fail_closed/` fixture and its needle assertion in
`test_msl_fail_closed.das`, in the same change.

- **A kernel behavioral change ships a CPU-oracle test under `tests/metal/` (repo root).** A
CPU-oracle test compares the GPU result against a CPU-computed expectation.
kernel behavioral change is a change to what a kernel computes - its emitted arithmetic,
indexing, or synchronization; a CPU-oracle test compares the GPU result against a
CPU-computed expectation.

- **A change visible only in the emitted text ships a `tests/msl/` (repo root) fixture.** The
fixture asserts the emitted text that the change alters.
Expand Down
4 changes: 2 additions & 2 deletions modules/dasMetal/metal/msl_emit.das
Original file line number Diff line number Diff line change
Expand Up @@ -1010,11 +1010,11 @@ def private msl_uint_literal(v : uint) : string {
// das prints integral floats bare ("{2f}" is "2"), and bare-int + suffix is not a valid MSL
// literal; `suffix` is "f" for float and "h" for half (the das float16 literal's MSL spelling)
def private msl_float_literal(var ctx : MslCtx; at : LineInfo; v : float; suffix : string) : string {
let s = "{v}"
if (s == "inf" || s == "-inf" || s == "nan") {
if (!is_finite(v)) {
err(ctx, at, "non-finite float literal has no MSL form")
return "0.0{suffix}"
}
let s = "{v}"
if ((s |> find(".")) < 0 && (s |> find("e")) < 0) {
return "{s}.0{suffix}"
}
Expand Down
1 change: 1 addition & 0 deletions modules/dasUnitTest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ IF ((NOT DAS_UNIT_TEST_INCLUDED) AND ((NOT ${DAS_UNIT_TEST_DISABLED}) OR (NOT DE
${DAS_UNIT_TEST_DIR}/bytecode.h
${DAS_UNIT_TEST_DIR}/test_handles.cpp
${DAS_UNIT_TEST_DIR}/test_enum.cpp
${DAS_UNIT_TEST_DIR}/test_crt_math.cpp
${DAS_UNIT_TEST_DIR}/module_unitTest.h
${DAS_UNIT_TEST_DIR}/unitTest.h
${DAS_UNIT_TEST_DIR}/unit_test.das.inc
Expand Down
1 change: 1 addition & 0 deletions modules/dasUnitTest/module_unitTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class Module_UnitTest : public Module {
public:
Module_UnitTest();
void addEnumTest(ModuleLibrary &);
void addCrtMath(ModuleLibrary &);
virtual ModuleAotType aotRequire ( TextWriter & tw ) const override;
bool appendCompiledFunctions();
};
17 changes: 17 additions & 0 deletions modules/dasUnitTest/test_crt_math.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include "daScript/misc/platform.h"

#include "daScript/ast/ast_interop.h"

#include "module_unitTest.h"
#include "unitTest.h"

void Module_UnitTest::addCrtMath(ModuleLibrary & lib) {
addExternInline<DAS_BIND_FUN(crt_expf)>(*this, lib, "crt_expf", SideEffects::none, "crt_expf")->arg("x");
addExternInline<DAS_BIND_FUN(crt_exp2f)>(*this, lib, "crt_exp2f", SideEffects::none, "crt_exp2f")->arg("x");
addExternInline<DAS_BIND_FUN(crt_logf)>(*this, lib, "crt_logf", SideEffects::none, "crt_logf")->arg("x");
addExternInline<DAS_BIND_FUN(crt_log2f)>(*this, lib, "crt_log2f", SideEffects::none, "crt_log2f")->arg("x");
addExternInline<DAS_BIND_FUN(crt_powf)>(*this, lib, "crt_powf", SideEffects::none, "crt_powf")->args({"x","y"});
addExternInline<DAS_BIND_FUN(crt_sinf)>(*this, lib, "crt_sinf", SideEffects::none, "crt_sinf")->arg("x");
addExternInline<DAS_BIND_FUN(crt_cosf)>(*this, lib, "crt_cosf", SideEffects::none, "crt_cosf")->arg("x");
addExternInline<DAS_BIND_FUN(crt_tanf)>(*this, lib, "crt_tanf", SideEffects::none, "crt_tanf")->arg("x");
}
1 change: 1 addition & 0 deletions modules/dasUnitTest/test_handles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,7 @@ Module_UnitTest::Module_UnitTest() : Module("UnitTest") {
lib.addBuiltInModule();
addBuiltinDependency(lib, Module::require("math"));
addEnumTest(lib);
addCrtMath(lib);
// options
options["unit_test"] = Type::tFloat;
// constant
Expand Down
12 changes: 11 additions & 1 deletion modules/dasUnitTest/unitTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -446,4 +446,14 @@ DAS_MOD_API inline void deleteFancyClassDummy(FancyClass& ) { } // this one in

DAS_MOD_API void test_abi_lambda_and_function ( das::Lambda lambda, das::Func fn, int32_t lambdaSize, das::Context * context, das::LineInfoArg * lineinfo );

DAS_MOD_API bool testBindEnumFunction ( das::Context * context, das::LineInfoArg * at );
DAS_MOD_API bool testBindEnumFunction ( das::Context * context, das::LineInfoArg * at );

//! CRT reference twins - benchmarks/core/math A/Bs these against the math builtins.
inline float crt_expf ( float a ) { return expf(a); }
inline float crt_exp2f ( float a ) { return exp2f(a); }
inline float crt_logf ( float a ) { return logf(a); }
inline float crt_log2f ( float a ) { return log2f(a); }
inline float crt_powf ( float a, float b ) { return powf(a, b); }
inline float crt_sinf ( float a ) { return sinf(a); }
inline float crt_cosf ( float a ) { return cosf(a); }
inline float crt_tanf ( float a ) { return tanf(a); }
15 changes: 15 additions & 0 deletions plans/benchmark_followups.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,18 @@ proper benchmarks - bind the CRT twins of every scalar math builtin in the dasTe
the test cycle is short (no core rebuild), compare side by side in all three tiers (AOT,
interpreter, JIT), and keep the winners; `benchmarks/core/math/exp.das` and the two bound
functions stay.

## the M1 AOT exp-loop residual after the CRT switch

The 2026-08-30 re-profile: zen2 AOT exp loop 9215 -> 3412 us (level with JIT 3383 and C++ 3240),
but the M1's went only 3592 -> 3414 while its JIT and C++ sit at ~1650. The AOT TU verifiably
recompiled against the switched header, so exp is the CRT there - the residual is the AOT loop
shape itself: the `das_iterator<range>` machinery and the `rcp_est` lane round-trip are the
suspects, and neither shows on zen2 because MSVC's codegen was the bottleneck there. Wants its
own probe on the M1 (the family-probe pattern, loop shapes instead of math). The zen2 dictionary
JIT row swung +43% in the same capture - the known bimodal per-process lane, not a change.

The switch leaves a scalar/vector split: scalar `log2` and `pow` are exact CRT while the
`float4` arms keep the vecmath estimates (`v_log2_est_p5`, `v_pow`), so `float` and `float4`
results diverge for those two. The JIT was already exact for the scalars; the vec4f arms are
the follow-up if the divergence ever bites.
27 changes: 27 additions & 0 deletions src/misc/LUAU.LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
The shortest-float presentation emitter in src/misc/luau_float2string.cpp is borrowed
from the Luau programming language (https://github.com/luau-lang/luau, VM/src/lnumprint.cpp),
licensed under the MIT License. The Luau license carries the Lua.org copyright for the
Lua 5.x code it derives from:

MIT License

Copyright (c) 2019-2025 Roblox Corporation
Copyright (c) 1994-2019 Lua.org, PUC-Rio.

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading
Loading