Skip to content

Using the DQ Compiler

The main tools are:

Tool Purpose
dq-comp compile DQ modules and link executables
dq-run compile a single DQ file and run it
dq-atrun run compiler autotests

For instructions on building these tools from source, see Getting the DQ Compiler.

dq-comp

Basic use:

dq-comp app.dq
./app

Useful options:

Option Meaning
-o <file> set output filename
-c compile only; do not link
--ifgen generate a standalone .dqm_if interface file
--ifdump dump a standalone .dqm_if interface
--no-use-sys disable implicit merged sys import
--target=<name> select a compiler target
--sysroot=<path> select the target system root used for linking
--cpu-features=<features> append LLVM CPU features to the target defaults
--targets list canonical targets and their LLVM/default settings
--exceptions, --no-exceptions enable or disable DQ exception handling
--dynstrings, --no-dynstrings enable or disable heap-backed dynamic strings
--pkg-path <path> add a package search root; repeatable
--build <tag> select .dqbuild/<tag>
--build-suffix <suffix> append to the selected build tag
--build-root <path> select the local artifact build root
--package-build-root <path> select a separate package artifact build root
--lto[=full|off] control full LTO bitcode sidecars
-Dname define a compile-time symbol as true
-Dname=value define an integer or boolean symbol
-O0..-O3, -Os, -Oz optimization level
-g generate debug info
-v, -vv, -vvv increasing compiler verbosity
-ir print generated LLVM IR

The default optimization level is currently -O1.

The rtl/sys prelude is implicitly merged for hosted and bare targets. Use --no-use-sys only for low-level bootstrap or RTL work that must opt out. Exception handling defaults to enabled on hosted targets and disabled on bare targets; when enabled, #ifdef EXCEPTIONS selects exception-aware source code. Runtime checks remain active when exception handling is disabled. Dynamic strings follow the same target defaults; when enabled, #ifdef DYNSTRINGS selects str-dependent source. With dynamic strings disabled, strslice, rostr, string literals, and bounded embstr remain available, but owning str operations are rejected at compile time.

Compiler Targets

Run dq-comp --targets to print the canonical target names together with their architecture, platform, LLVM triple, CPU/features, feature defaults, and default link mode. In addition to the current host and Cortex-M targets, the compiler supports:

Target Behavior
x64-windows Windows x64 GNU/UCRT executable; also accepts x86_64-win, x64-win, and x86_64-windows
armhf-linux ARMv7-A hard-float Linux executable
arm64-linux AArch64 Linux executable
rv64g-linux RV64G/LP64D Linux executable
wasm32-wasi Hosted WASI command module; links automatically and defaults to a .wasm output
wasm32-bare Bare WebAssembly object generation; compile-only by default
rv32imac-bare Bare RV32IMAC ELF object generation; compile-only by default

wasm32-wasi requires Clang/LLD and a WASI sysroot containing libc at link time. DQ exceptions are not supported on this target: --exceptions and exceptions = true are rejected, while dynamic strings remain enabled by default. The resulting command module uses WASI libc startup and can be run by a WASI runtime such as Wasmtime.

The foreign Linux targets require a target sysroot at link time. Select it with --sysroot=/path/to/sysroot or the sysroot project property. DQ passes this to its Clang linker driver as --sysroot; the sysroot must come from the target distribution or SDK and match the deployed C library and development packages.

For example, an ARM64 Debian sysroot is selected per build:

dq-comp --target=arm64-linux --sysroot=/path/to/debian-arm64-sysroot app.dq

The two bare targets do not bundle startup code, linker scripts, libc, or compiler-runtime libraries. They emit objects without linking unless --link is requested. A forced bare link must supply the appropriate objects and linker arguments and normally sets compiler_runtime = 'none' and c_runtime = 'none'.

Target selection defines architecture and ABI markers as applicable: 32-bit x86 uses X86_FAMILY and X86, while 64-bit x86 uses X86_FAMILY and X64. Arm targets use ARM plus ARM_M or ARM_A; the hosted hard-float target also defines ARMHF. AArch64 defines ARM_A, ARM64, and AARCH64. RISC-V targets define RISCV plus RV32 or RV64. Bare targets also define BARE, and WASM/WASI, TARGET_32BIT/TARGET_64BIT, EXCEPTIONS, and DYNSTRINGS continue to reflect the selected target and feature settings.

Windows cross-compilation from Linux

When built with LLVM's X86 backend, the Linux compiler can produce Windows x64 executables using a Linux-hosted llvm-mingw UCRT toolchain. With the toolchain extracted under sysroots/llvm-mingw-x86_64, source the setup script in Bash before compiling and running:

source tools/dqcrosswinsetup.sh
build/dq-comp --target=x64-windows app.dq
wine app.exe

The script detects the repository root from its own location, sets DQ_MINGW_ROOT, and uses winepath to set WINEPATH to the toolchain's target runtime DLL directory. Wine loads the DLLs directly from that directory, so you do not need to copy them beside each executable. The script also sets MESA_VK_IGNORE_CONFORMANCE_WARNING=1 to suppress Mesa's RADV Vulkan conformance warning. Wine and winepath must be installed.

You can source the script by its absolute path from any working directory. For example, add this line to ~/.bashrc to configure new Bash terminals automatically (adjust the path to your checkout):

source /lindata/workvc/dq-lang/tools/dqcrosswinsetup.sh

Source the script in the current terminal as well to apply the settings immediately. Running it as a separate process does not configure the current shell.

Windows outputs default to .exe. An explicit -o filename is used exactly as given, including filenames without an extension. Object generation with -c does not require the Windows libraries or Wine.

Set DQ_MINGW_ROOT to the extracted llvm-mingw root when it is installed elsewhere:

DQ_MINGW_ROOT=/path/to/llvm-mingw build/dq-comp --target=x64-windows app.dq

The linker search order is DQ_LINKER_DRIVER, the cross driver under DQ_MINGW_ROOT/bin, compiler-relative toolchain/llvm-mingw, llvm-mingw, and sysroots/llvm-mingw-x86_64 installations, then x86_64-w64-mingw32-clang++ on PATH. An invalid explicit DQ_MINGW_ROOT fails when linking. Native Windows compiler installations retain their Windows-hosted driver discovery. --sysroot is still passed through when set.

The toolchain must provide Clang/LLD, startup objects, UCRT and Windows import libraries, libc++, and libunwind. Full LTO also requires a toolchain with the same LLVM major version as DQ. Windows full-LTO links use LLD's default optimization pipeline rather than the ELF-specific O1 inlining pipeline.

As an alternative to setting WINEPATH, copy the toolchain's target runtime DLLs beside the executable: libc++.dll, libunwind.dll, and libwinpthread-1.dll when provided. These are under x86_64-w64-mingw32/bin in llvm-mingw.

The opt-in compatibility suite copies the runtime DLLs automatically:

make test-windows
# Custom installation:
DQ_MINGW_ROOT=/path/to/llvm-mingw make test-windows
# Direct invocation with explicit tool paths:
python3 tools/test-windows.py --compiler build/dq-comp \
    --mingw-root /path/to/llvm-mingw --wine /path/to/wine

It requires Linux, Python 3, llvm-mingw, and usable 64-bit Wine. Missing prerequisites fail the requested run; they are not silently skipped. Windows fixtures live in autotest/cross-windows; the suite also reuses the portable thread/channel autotests and the threads examples. It checks native thread lifecycle and handle cleanup, atomics, event deadlines, channel capacity and waits, producer/consumer stress, and work distribution at O0 and O3 with full LTO. Normal make test, make autotest, and make check runs do not invoke the Windows suite or require these dependencies. Executables, caches, runtime DLLs, command logs, and a dedicated Wine prefix are stored under build/test-windows. The suite leaves the user's Wine prefix untouched.

dq-run

dq-run is for experiments and single-file programs:

dq-run app.dq
dq-run -O2 app.dq -- arg1 arg2

Compiler options must come before the .dq file. Program arguments come after the file, or after --.

When no compiler options are supplied, dq-run passes -g -O0 to dq-comp.

Build Artifacts

DQ packages and the RTL are source-first. The compiler writes generated module artifacts under .dqbuild.

.dqbuild/<build-tag>/
  local/
    app.o
    app.dqm_if
  pkg/
    print/
      print.o
      print.dqm_if
    rtl/
      rtl_linux.o
      rtl_linux.dqm_if

Each module has a normal native .o object and a standalone .dqm_if interface. Both are generated artifacts, not source package contents.

By default the build root is the directory containing the main .dq file. The default build tag is <target-arch>-<target-rtl>, for example x86_64-linux.

The build tag is only a namespace. A tag named debug does not imply -g, and a tag named release does not imply -O3.

By default, local and package artifacts use the same build root. With --package-build-root <path>, package and RTL artifacts are instead written to <path>/.dqbuild/<build-tag>/pkg, while local artifacts remain under the normal build root. This allows several projects with the same build context to share compiled package modules.

Package Search Path

Bare imports such as use print or use json are resolved through package search roots. The compiler adds standard roots automatically, including:

/usr/lib/dq/stdpkg
<compiler-dir>/../lib/dq/stdpkg
<compiler-dir>/../stdpkg
/usr/lib/dq/packages
<compiler-dir>/../lib/dq/packages
~/.dq/packages

Additional roots can be appended with --pkg-path. Search uses the last matching package root first, so later --pkg-path entries can override earlier roots.

Cleaning

Clean .dqbuild/<tag> when the compiler version, target, ABI-relevant options, package sources, or important build defines change.

rm -rf .dqbuild/x86_64-linux