You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 2a968b3
Browse filesBrowse the repository at this point in the historyBrowse files
examples: the module surface, and one header kept for contrast
The payload lane and the boundary lane are separate, and each example now
shows one of them.
`vulkan` reaches its SPIR-V through a MODULE. `src/vulkan/saxpy.cpp` opened
with `#include "scale_comp.h"` -- a name no line in the project produced and
no reader could derive without opening the rule -- and now imports
`vulkan_saxpy.shaders`, whose name and whose `scale_comp()` accessor are both
derived from names the project already wrote. The accessor answers with the
address and the byte count together, which is what makes `sizeof` the wrong
question rather than merely an awkward one: the rule may put the words in an
object, and there is then no array to take the size of.
`cuda` and `sycl` GENERATE their `extern "C"` boundary. Each entry point is
marked with `MCPP_EXPORT_C` where it is defined, and `mcpp.tools.island`
writes the header the island's compiler reads and the module the seam
imports. `include/saxpy/saxpy.h` is gone from both. Both halves are handed to
the scan, so a signature that drifted between the island and the CPU
fallback is refused while the boundary is generated -- the one point at which
both texts exist at once, and the only check available at a boundary where C
language linkage does not mangle and the two halves are never in one link.
The island names nothing and the host half writes one `#include`, and that
asymmetry is measured rather than incidental. The island is compiled by a
driver mcpp did not invoke, so its rule takes forced-include flags for that
one command line. The host half is ordinary project C++, and the only
project-wide channel would force the header into every C++ translation unit
including the seam, where declarations ahead of `export module` are
ill-formed.
`hip` keeps its hand-written header, and its README says why. It computes the
same thing through the same seam as `cuda`, so the difference between the two
examples is exactly this one thing, and the hand-written form is what every C
boundary looks like today. Neither is deprecated; the generated form is the
default because the copy it removes is the one that goes wrong quietly.
`multi-backend` reaches its SPIR-V as `opkit.shaders` too, and keeps
`include/opkit/opkit.h` written by hand -- which is the distinction this example
is for. That header is a LIBRARY boundary: consumers outside this tree compile
against it, so it is not an intermediate and generating it would not remove a
copy, it would move one. The shader payload is an intermediate, and it moves.
`examples/10-graphics/offscreen` moves too, and it is the one example CI both
builds AND RUNS on two platforms -- so the surface is exercised there rather
than merely compiled. Two includes become one import, and the two stages are
`offscreen::shaders::triangle_vert()` and `::triangle_frag()`.
`cann` is unchanged. Its boundary has a different shape -- `saxpy_device`
between the seam and a host glue file, `saxpy_launch` between that glue and
the kernel -- and both halves of the first one are compiled by mcpp, so the
forced-include asymmetry above does not apply. Its README already records
that the example does not build yet.
Verified on this host: cuda and sycl build and run on an RTX 4080 with the
generated boundary on the island's own command line, vulkan runs on the same
device with its shader reached as a module, multi-backend runs on lavapipe with
`--accel "vulkan1.2"`, the offscreen triangle renders on lavapipe at centre
pixel `(124, 70, 62, 255)` -- the same pixel its CPU leg produces, which is the
assertion CI makes -- and each CPU leg builds and runs under `--no-accel`.
`docs/20`'s two-lanes section said the seam is written by hand and the shader
lane's equivalent is generated. Half of that still holds: the seam is a design
decision. What sits under it is each signature written a second time, and that
half is now generated too. The section says so, and gains the table that maps a
file name to the call it arrives as -- which is the question a reader of that
document actually has.
Every example under `examples/09-heterogeneous` and `examples/10-graphics`
pins 0.3.0 now, `hip` and `cann` included. Neither uses anything new, and
leaving them behind would be defensible in isolation -- but `hip` exists to
be read beside `cuda`, and a reader comparing the two would find a second
difference the text never explains. The floor sentences move with them.
The six plugin pins in `docs/05` and `docs/20` move too, in both languages.
A version in a document is the line someone pastes, and 0.2.1 and 0.2.4
predate every declaration those sections now describe -- a reader who copied
one would get a build in which the rules never route.
The Windows graphics run step comes back, as a measurement. It was written
once, failed, and came out with the wrong cause recorded; the cause has since
been found in `xim:mesa-lavapipe`, which wrote its rewritten `library_path`
into the ICD's JSON string unescaped -- `C:\Users\...` carries `\U`, which is
not a JSON escape, so the loader's cJSON parser rejected the manifest and
skipped the ICD silently. Fixed in openxlings/xim-pkgindex#781.
The step removes the lavapipe store entry before rebuilding, and that is not
tidiness: `install()` does not re-run over an existing payload and `~/.mcpp`
is cached across runs, so a runner that installed 26.2.0 before the fix would
keep the manifest it was given and the step would measure the cache. It also
prints the manifest and asserts it PARSES, so a failure is read against what
the loader was actually handed.
Its first attempt failed on its own preparation, and the correction is the
half that is easy to forget: removing the payload is not enough. mcpp records
a provisioned package set under `<mcpp home>/provisioned/`, and with the
marker present and the payload deleted the rebuild SKIPS provisioning -- it
printed no `Provisioning` line at all and the step then reported
`no lavapipe ICD in either store`, which reads as a missing package rather
than as a skipped step. Both halves go, and the step now asserts that
provisioning ran, so a later failure separates "the fix did not apply" from
"it applied and the device still did not appear".
The corrected run then showed the fix reaching the runner. Provisioning ran,
and the manifest it produced is
"library_path": "C:/Users/runneradmin/.mcpp/.../lib/vulkan_lvp.dll"
forward slashes, and `the ICD manifest parses as JSON`. The step still exited
1, on `error: unknown option: --toolchain` -- `mcpp run` has no such option,
and I had added it to that line. Removed; the job's earlier step sets the
project default, so `run` replays what `build` selected.
If it is red the cause is somewhere else again, and it comes back out with
what it showed recorded.
Requires mcpp:plugins 0.3.0.
Copy file name to clipboardExpand all lines: .agents/docs/2026-09-07-module-first-heterogeneous-surface.md
+49Lines changed: 49 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -846,6 +846,55 @@ and a newer one moves a project to the module surface with nothing declared.
846
846
| Seamless upgrade | A project that writes nothing new keeps its behaviour | Header storage stays the default under `modules = false`; the default surface follows `[language] modules` |
847
847
| Test coverage | Each surface and each storage has a consumer that runs | P7 |
848
848
849
+
### 13.3.1 What the self-review found after the surface was working
850
+
851
+
Four findings, three of them in names the surface generates. Every one was found
852
+
by giving the generator an input nobody had tried, and none of them by reading
853
+
the code -- which is the reason they are recorded together.
854
+
855
+
**The user-facing name came from the wrong question.** The module a consumer
856
+
imports was derived from the leaf of `MCPP_MANIFEST_DIR` -- a directory name --
857
+
because nothing in the build-program contract answered "what package am I
Copy file name to clipboardExpand all lines: docs/01-examples.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -32,10 +32,10 @@ examples.
32
32
| 07 |[`examples/07-project-subos`](../examples/07-project-subos/)| A build program that finds its tools in the environment the project declared |`[xlings] subos`, `[xlings.workspace]`, a build program whose `PATH` is the environment the project named |
33
33
| 08 |[`examples/08-build-rules`](../examples/08-build-rules/)| Two rule packages and a project that uses both |`host-module = true`, `[build-dependencies]`, `mcpp::action` with `role = "check"`|
34
34
| 09 |[`examples/09-heterogeneous`](../examples/09-heterogeneous/)| One computation on a device, in several programming models, with a CPU fallback in each; plus one artifact carrying several backends at once |`accel`, constrained source globs, the seam module, rule packages from `mcpp:plugins`, `cfg(accelerator = …)`|
35
-
| 09a |[`…/cuda`](../examples/09-heterogeneous/cuda/)| A CUDA kernel behind a seam module|`mcpp.rules.cuda`, `mcpp::action` with `role = "object"`, the driver stated as a fact and a floor |
36
-
| 09b |[`…/vulkan`](../examples/09-heterogeneous/vulkan/)| The same computation as a Vulkan compute shader, on a GPU or on the CPU |`mcpp.rules.spirv`, `mcpp::action` with `role = "source"`, generated headers, a software driver as a payload |
37
-
| 09c |[`…/sycl`](../examples/09-heterogeneous/sycl/)| The same computation as a SYCL kernel, compiled by a second compiler |`mcpp.rules.sycl`, the `.sycl` device extension, a chained `mcpp::action` for the device link, `compat:sycl-runtime`|
38
-
| 09d |[`…/hip`](../examples/09-heterogeneous/hip/)| The same computation in HIP, reaching an NVIDIA device|`mcpp.rules.hip`, HIP as a header layer over the CUDA runtime, a two-chunk `accel`|
35
+
| 09a |[`…/cuda`](../examples/09-heterogeneous/cuda/)| A CUDA kernel behind a seam module, with the `extern "C"` boundary GENERATED |`mcpp.rules.cuda`, `mcpp.tools.island`, `mcpp::action` with `role = "object"`, the driver stated as a fact and a floor |
36
+
| 09b |[`…/vulkan`](../examples/09-heterogeneous/vulkan/)| The same computation as a Vulkan compute shader, with the SPIR-V payload reached as a MODULE |`mcpp.rules.spirv`, the module surface, `mcpp::action` with `role = "source"`, a software driver as a payload |
37
+
| 09c |[`…/sycl`](../examples/09-heterogeneous/sycl/)| The same computation as a SYCL kernel, compiled by a second compiler |`mcpp.rules.sycl`, `mcpp.tools.island`, the `.sycl` device extension, a chained `mcpp::action` for the device link, `compat:sycl-runtime`|
38
+
| 09d |[`…/hip`](../examples/09-heterogeneous/hip/)| The same computation in HIP, with the boundary WRITTEN BY HAND — the contrast against 09a|`mcpp.rules.hip`, HIP as a header layer over the CUDA runtime, a two-chunk `accel`|
39
39
| 09e |[`…/multi-backend`](../examples/09-heterogeneous/multi-backend/)| Several backends in ONE artifact, chosen at run time — the library shape, not the program shape |`accel` as a set, `cfg(accelerator = "none")` and its negation, a dispatch chain, a module seam over a C island boundary |
40
40
| 09f |[`…/cann`](../examples/09-heterogeneous/cann/)| An Ascend C kernel behind the same seam. **Does not build yet** — its README names the two missing pieces | the `.asc` device extension, `op_kernel`/`op_host` as an island CANN already has, `accelerator = "none"` for the fallback |
41
41
| 10 |[`examples/10-graphics`](../examples/10-graphics/)| Graphics rather than compute: a rendering pipeline whose result is pixels |`mcpp.rules.spirv` for the vertex and fragment stages, offscreen rendering as the assertable form |
0 commit comments