5.3.13. C++ Integration: AOT Compilation
This tutorial explains daslang’s ahead-of-time (AOT) compilation system, which transpiles daslang functions into C++ source code for near-native performance. Topics covered:
The AOT workflow — generate, compile, link
CodeOfPolicies::aot— enabling AOT at runtimeSimFunction::aot— checking if a function is AOT-linkedSelf-registration via
AotListBase
5.3.13.1. Prerequisites
Tutorial 12 completed (tutorial_integration_cpp_smart_pointers).
Understanding of the compile → simulate → eval workflow.
5.3.13.2. What AOT does
AOT in daslang does not JIT-compile to machine code at runtime. Instead, it follows a two-stage build pattern:
Generate — the
daslangtool transpiles daslang functions into C++ source codeCompile — the generated
.cppfile is compiled by your C++ compiler and linked into the host executableLink — at runtime,
simulate()replaces interpreted simulation nodes with direct calls to the pre-compiled C++ functions
This gives near-C++ performance while keeping the convenience of scripting during development.
5.3.13.3. The AOT workflow
5.3.13.3.1. Stage 1 — Generate C++ from daslang
The project’s CMakeLists.txt uses the DAS_AOT macro,
which runs daslang.exe with the AOT tool script at build time:
daslang.exe utils/aot/main.das -- -aot script.das output.cpp
This produces a .cpp file containing C++ implementations of all
daslang functions, plus a self-registration block:
// Generated AOT code (simplified)
namespace das {
namespace _anon_XXX {
inline bool test(Context * __context__) {
// ... translated daslang code ...
}
static void registerAotFunctions(AotLibrary & aotLib) {
aotLib[0x92da443ef141abbb] = +[](Context & ctx) -> SimNode* {
return ctx.code->makeNode<SimNode_Aot<...>>();
};
}
}
AotListBase impl(registerAotFunctions); // self-registers
}
The AotListBase constructor runs at program startup, inserting the
registration function into a global linked list.
5.3.13.3.2. Stage 2 — Compile into the host
The CMakeLists.txt for this tutorial uses the DAS_AOT macro
to automate both generation and compilation:
# 1. Create a custom target and accumulator variable
SET(INTEGRATION_13_AOT_GENERATED_SRC)
add_custom_target(integration_cpp_13_dasAotStub)
# 2. Generate AOT C++ from the .das file using daslang as the tool
DAS_AOT("tutorials/integration/cpp/13_aot.das"
INTEGRATION_13_AOT_GENERATED_SRC
integration_cpp_13_dasAotStub daslang)
# 3. Build the executable with both hand-written AND generated sources
add_executable(integration_cpp_13
13_aot.cpp
${INTEGRATION_13_AOT_GENERATED_SRC}
)
TARGET_LINK_LIBRARIES(integration_cpp_13 libDaScript Threads::Threads)
ADD_DEPENDENCIES(integration_cpp_13 libDaScript
integration_cpp_13_dasAotStub)
The DAS_AOT macro:
Runs
daslang.exe utils/aot/main.das -- -aot 13_aot.das output.cppas a custom build commandPuts the generated file in an
_aot_generated/subdirectoryCreates a dependency on
daslangso it’s built first
The generated .cpp is compiled alongside 13_aot.cpp —
no manual steps required.
5.3.13.3.3. Stage 3 — Enable AOT in the host
CodeOfPolicies policies;
policies.aot = true; // enable AOT linking
policies.fail_on_no_aot = true; // error if any function lacks AOT
auto program = compileDaScript(scriptPath, fAccess, tout,
libGroup, policies);
Context ctx(program->getContextStackSize());
program->simulate(ctx, tout); // AOT functions linked here
auto fn = ctx.findFunction("test");
// Check if AOT-linked:
if (fn->aot) { /* running native C++ */ }
During simulate(), the engine computes a semantic hash for each
function, looks it up in the global AOT library, and replaces the
simulation node if a match is found.
5.3.13.4. Checking AOT status
SimFunction (returned by ctx.findFunction()) has an aot
flag:
auto fn = ctx.findFunction("test");
printf("AOT: %s\n", fn->aot ? "yes" : "no");
Without the generated .cpp linked in, aot is false and
the function runs through the interpreter. With it linked in,
aot is true.
5.3.13.5. Key policies
|
Enable AOT linking during simulate |
|
Error if a function lacks AOT (default
|
|
This is a module AOT (not entry point) |
5.3.13.6. Building and running
cmake --build build --config Release --target integration_cpp_13
bin\Release\integration_cpp_13.exe
Expected output:
=== Interpreter mode ===
test() is AOT: no
=== AOT Tutorial ===
fib(0) = 0
fib(1) = 1
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
fib(6) = 8
fib(7) = 13
fib(8) = 21
fib(9) = 34
sin(pi/4) = 0.70710677
cos(pi/4) = 0.70710677
sqrt(2) = 1.4142135
=== AOT mode (policies.aot = true) ===
test() is AOT: yes
=== AOT Tutorial ===
fib(0) = 0
fib(1) = 1
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
fib(6) = 8
fib(7) = 13
fib(8) = 21
fib(9) = 34
sin(pi/4) = 0.70710677
cos(pi/4) = 0.70710677
sqrt(2) = 1.4142135
=== AOT workflow summary ===
1. daslang.exe -aot script.das script.das.cpp
-> generates C++ source from daslang functions
2. Compile the .cpp into your executable
-> functions self-register via static AotListBase
3. Set CodeOfPolicies::aot = true before compileDaScript
-> simulate() links AOT functions automatically
4. Functions run as native C++ — no interpreter overhead
Without policies.aot, the function runs through the interpreter
(test() is AOT: no). With it set, the pre-compiled C++ code
is linked in (test() is AOT: yes) for near-native performance.
See also
Full source:
13_aot.cpp,
13_aot.das
Previous tutorial: tutorial_integration_cpp_smart_pointers
Next tutorial: tutorial_integration_cpp_serialization