Add a target#
A target is one row of hawk.targets.TARGETS. The build, the package step and the compile drivers read the row, and
none of them compares a target’s name, so a new target is a new row and the code it points to. A test fails if
"cuda" or "host" appears as a string anywhere outside hawk/targets.
Three jobs, smallest first:
You want |
You add |
|---|---|
another file layout for the same language (a second export) |
a row that reuses a Writer |
a new execution model for a language hawk already writes (HIP) |
a Wrapper, a row, and a compile entry |
a new language (OpenCL C, MSL) |
a Writer, then the same as above |
The row#
Target(id="cuda", about="builds for the GPU", writer=AETHER, wrapper=CUDA, compile=GPU,
entry=ENTRY_DEVICE, format="ptx", artifact=".ptx", source=".cu", features=frozenset(FEATURES))
writer says how the language spells each construct and wrapper how a kernel is wrapped for one execution model.
wrapper=None means the Writer produces the whole file (the c row). compile is the toolchain, GPU, CPU or
None: with None the row is exported, not built, and export names the module that renders and checks it.
entry is how eagle enters a built kernel ("device" or "host", or None when eagle never runs it), and
format is the manifest’s artifact format. features are the names from the shared table in
hawk/targets/features.py that the row builds; a kernel that uses one the row lacks is refused by name before
anything renders.
A row that reuses a Writer#
The smallest target is a copy of an existing row. This one exports the same C under new suffixes, with no change to the build:
from dataclasses import replace
import hawk
from hawk import Mutable, Param, Scalar
from hawk.targets import TARGETS
@hawk.kernel
def drag(v: Scalar, k: Param, a: Mutable[Scalar]):
a = -k * v
TARGETS["toy"] = replace(TARGETS["c"], id="toy", about="a toy export", artifact=".toyh", source=".toy")
bundle = hawk.build([drag], "out", targets=("toy",)) # out/drag.toy and out/drag.toyh
Remove the row and the same call is refused: unknown target 'toy', with the valid ones listed.
A new execution model: the Wrapper#
A Wrapper is the part of the unit that depends on how the kernel runs. It has one attribute and six methods:
Member |
It writes |
|---|---|
|
the prefix of the helper functions ( |
|
the entry’s declaration: the arguments, then the partition triple |
|
the one place that binds the body’s lane index (a CUDA block and thread, a serial host loop) |
|
the closing half of that seam |
|
the lines that rebuild each argument’s view from what the entry receives |
|
the frame around a body with a reduction over lanes |
|
a per-lane branch between formulas |
A Wrapper may also supply optional hooks (simd_safe, lane_guards, interchange), which the renderer reads with
getattr. See hawk/emit/cuda.py and hawk/emit/host.py for the two that exist. The launch geometry is eagle’s:
a Wrapper names no stream and no occupancy.
Then add the row. A HIP target that reuses the aether C++ Writer would be (a sketch; hawk builds no HIP today):
Target("hip", "builds for AMD GPUs", AETHER, HipWrapper(), HIP, ENTRY_DEVICE, "hsaco",
".hsaco", ".hip.cpp", frozenset(FEATURES))
A toolchain#
compile=GPU and compile=CPU name the two toolchains hawk drives: the device compiler and the host compiler.
A row that uses one of them needs nothing more. A new compiler (hiprtc) is a new value and a branch in
hawk.compile.drivers.compile_source, the one function that forks on the row’s toolchain.
A new language: the Writer#
A Writer answers the renderer’s questions in one language: declare a local, write an operator, store to a sink,
write a sum over lanes. hawk.emit.writer.WRITER_MEMBERS lists them, and each member’s docstring gives the aether
writer’s text. hawk.embedded.writer.CWriter is the second one. A language that cannot compile aether’s C++ templates
also needs its own core for the numerics contract: see the numerics contract.
The family side#
A built row also reaches the other repos:
entryandformatare written to the manifest. A new format word is added toraptor.schema.manifest.MANIFEST_FORMATSand to eagle’splugin/roles.h; a test in each repo pins the two lists against each other.eagle runs the kernel through an execution backend for that device kind. See Add an execution backend.
Renamed#
Was |
Now |
|---|---|
|
|
|
|
|
|
|
the target ids |
|
|