7.4.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
7.4.13.1. Prerequisites
Tutorial 12 completed (tutorial_integration_cpp_smart_pointers).
Understanding of the compile → simulate → eval workflow.
7.4.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.
7.4.13.3. The AOT workflow
7.4.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.
7.4.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.
7.4.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.
7.4.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.
7.4.13.5. Key policies
|
Enable AOT linking during simulate |
|
Error if a function lacks AOT (default
|
|
This is a module AOT (not entry point) |
7.4.13.6. Building and running
7.4.13.6.1. From the source tree
Build using the in-tree CMake target:
cmake --build build --config Release --target integration_cpp_13
bin\Release\integration_cpp_13.exe
7.4.13.6.2. From the installed SDK
The installed SDK includes a standalone CMakeLists.txt for all C++
integration tutorials. Configure and build against the SDK:
mkdir build_cpp && cd build_cpp
cmake -DCMAKE_PREFIX_PATH=/path/to/daslang /path/to/daslang/tutorials/integration/cpp
cmake --build . --config Release
On Windows:
mkdir build_cpp; cd build_cpp
cmake -DCMAKE_PREFIX_PATH=D:\daslang D:\daslang\tutorials\integration\cpp
cmake --build . --config Release
The standalone CMakeLists.txt handles AOT code generation automatically.
It defines a DAS_TUTORIAL_AOT macro that runs daslang with the AOT
tool script during the build:
set(AOT_13_SRC)
DAS_TUTORIAL_AOT("${TUT_DIR}/13_aot.das" -aot AOT_13_SRC integration_cpp_13)
DAS_CPP_TUTORIAL(integration_cpp_13
"${TUT_DIR}/13_aot.cpp"
"${AOT_13_SRC}"
)
The generated .cpp is compiled alongside the tutorial source — no manual
steps involved. See tutorial_building_from_sdk for the full guide on
building your own projects with AOT support.
7.4.13.6.3. 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.
7.4.13.7. AOT in your own projects
To add AOT to your own CMake project built against the SDK, define a code
generation macro using the DAS::daslang imported target:
get_filename_component(DAS_SDK_ROOT "${DAS_DIR}/../../.." ABSOLUTE)
macro(MY_AOT input out_var target_name)
get_filename_component(_abs "${input}" ABSOLUTE)
get_filename_component(_name "${input}" NAME)
set(_out_dir "${CMAKE_CURRENT_BINARY_DIR}/_aot_generated")
set(_out_src "${_out_dir}/${target_name}_${_name}.cpp")
file(MAKE_DIRECTORY "${_out_dir}")
add_custom_command(
OUTPUT "${_out_src}"
DEPENDS "${_abs}"
COMMENT "AOT: ${_name}"
COMMAND $<TARGET_FILE:DAS::daslang>
"${DAS_SDK_ROOT}/utils/aot/main.das"
-- -aot "${_abs}" "${_out_src}"
)
set(${out_var} "${_out_src}")
set_source_files_properties("${_out_src}" PROPERTIES GENERATED TRUE)
endmacro()
Then link the generated source into your executable:
set(AOT_SRC)
MY_AOT("my_script.das" AOT_SRC my_app)
add_executable(my_app main.cpp "${AOT_SRC}")
target_compile_definitions(my_app PRIVATE DAS_MOD_EXPORTS)
target_link_libraries(my_app PRIVATE DAS::libDaScriptDyn Threads::Threads)
And enable AOT in your host code:
CodeOfPolicies policies;
policies.aot = true;
auto program = compileDaScript(scriptPath, fAccess, tout, libGroup, policies);
See tutorial_building_from_sdk for the full SDK build guide.
See also
Full source:
13_aot.cpp,
13_aot.das
Previous tutorial: tutorial_integration_cpp_smart_pointers
Next tutorial: tutorial_integration_cpp_serialization
Building from the SDK: tutorial_building_from_sdk