Skip to content

Commit bf81b35

Browse files
committed
tools-island: the fixture reaches the boundary through a seam module
Every example under `examples/09-heterogeneous` puts a module of the project between the consumer and the island: `app.cppm` imports the boundary and turns pointers and a count back into spans, and it is the one place a backend can be exchanged. The fixture reached the generated module from `src/main.cpp`, which is not a module unit -- so its import said nothing about the case the examples are in, where one module interface written by the project imports a module interface written into the build directory during the same build. `src/app.cppm` is that seam, and `src/main.cpp` now names no boundary symbol at all. Both properties are asserted: a consumer that named `saxpy_device` would mean the seam is not hiding which island is underneath. Both legs build and run with it.
1 parent 38549b4 commit bf81b35

5 files changed

Lines changed: 75 additions & 16 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,21 @@ jobs:
243243
# island reads the generated header through the compiler's
244244
# forced-include flag -- so a project using this generator has no
245245
# header in its source tree and no line naming one.
246-
if grep -rn '#include' src/main.cpp src/kernels/saxpy.c src/cpu/saxpy.c; then
246+
if grep -rn '#include' src/main.cpp src/app.cppm src/kernels/saxpy.c src/cpu/saxpy.c; then
247247
echo "FAIL: a source names an include; the generator exists to remove it"
248248
exit 1
249249
fi
250+
# THE SEAM IS A MODULE OF THIS PROJECT IMPORTING THE GENERATED ONE.
251+
# `main.cpp` is not a module unit, so its import said nothing about
252+
# ordering two module interfaces where one is written during the
253+
# build. `src/app.cppm` is, and it is the shape every example under
254+
# examples/09-heterogeneous has.
255+
grep -q '^import island_interface.kernels;' src/app.cppm \
256+
|| { echo "FAIL: the seam no longer imports the generated module"; exit 1; }
257+
if grep -q 'saxpy_device\|scale_device' src/main.cpp; then
258+
echo "FAIL: the consumer names a boundary symbol; the seam exists to hide it"
259+
exit 1
260+
fi
250261
# AND THE CHECK THAT INCLUDE USED TO DO IS STILL THERE. The compiler
251262
# sees the declarations, so a definition whose signature drifted from
252263
# the generated header fails where it was written rather than at the

‎README.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,13 @@ Two files come out of that one marked declaration: the `extern "C"` header the
297297
device translation unit includes, guards and `__cplusplus` dance included, and
298298
the module the C++ side imports.
299299
300+
The C++ side is usually a **seam module** of the project rather than a consumer
301+
directly: `app.cppm` imports the generated module and turns pointers and a count
302+
back into spans, and it is the one place a backend can be exchanged. That means
303+
one module interface of the project imports a module interface written into the
304+
build directory during the same build; the ordering comes from the scan seeing
305+
the import, and nothing has to be declared for it.
306+
300307
**Three layers, and each overrides the one above.** `scan` reads the marked
301308
declarations out of the island, which puts the signature beside the definition;
302309
`emit` takes a list directly, for entry points a scan cannot see; and a project

‎tests/island-interface/mcpp.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ plugins = { path = "../..", features = ["tools-island"], host-module = true }
3939
# disk in either build, and the manifest decides which one is compiled.
4040
[build]
4141
accel = "vulkan1.2"
42-
sources = ["src/*.cpp"]
42+
sources = ["src/*.cppm", "src/*.cpp"]
4343

4444
[target.'cfg(accelerator = "vulkan")'.build]
4545
sources = ["src/kernels/*.c"]
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// The seam: a module of this project that imports the GENERATED module.
2+
//
3+
// This is the shape every example under `examples/09-heterogeneous` has, and it
4+
// is the one the fixture was missing. `src/main.cpp` is not a module unit, so
5+
// its `import` said nothing about the case that matters here -- a module
6+
// interface written by this project importing a module interface written into
7+
// the build directory during the same build. The two have to be ordered, and
8+
// the ordering comes from the scan seeing the import rather than from anything
9+
// this file declares.
10+
//
11+
// It is also where the boundary stops being C. Above this line callers pass
12+
// spans; below it, pointers and a count, which is the one shape every device
13+
// API agrees on.
14+
export module island_interface.app;
15+
16+
import std;
17+
import island_interface.kernels;
18+
19+
export namespace island_interface {
20+
21+
std::optional<std::vector<float>>
22+
saxpy(float a, std::span<const float> x, std::span<const float> y) {
23+
if (x.size() != y.size()) return std::nullopt;
24+
std::vector<float> out(x.size());
25+
if (saxpy_device(a, x.data(), y.data(), out.data(),
26+
static_cast<unsigned>(x.size())) != 0)
27+
return std::nullopt;
28+
return out;
29+
}
30+
31+
bool scale(float a, std::span<float> v) {
32+
return scale_device(a, v.data(), static_cast<unsigned>(v.size())) == 0;
33+
}
34+
35+
} // namespace island_interface

‎tests/island-interface/src/main.cpp‎

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,34 @@
1-
// The C++ side. It imports the generated module and includes nothing: the
2-
// header exists for the island's compiler, which does not read modules.
1+
// The consumer. It imports the SEAM, not the generated module, and includes
2+
// nothing: the generated header exists for the island's compiler, which does
3+
// not read modules.
4+
//
5+
// Nothing here names `saxpy_device`. That is the property the seam exists for:
6+
// which island is underneath -- the device half or the host one -- is not
7+
// visible from this file, and neither is the fact that a boundary was
8+
// generated at all.
39
import std;
4-
import island_interface.kernels;
10+
import island_interface.app;
511

612
int main() {
7-
float x[4] = {1, 2, 3, 4};
8-
float y[4] = {10, 20, 30, 40};
9-
float out[4] = {};
13+
const std::vector<float> x{1, 2, 3, 4};
14+
const std::vector<float> y{10, 20, 30, 40};
1015

11-
if (saxpy_device(2.0f, x, y, out, 4) != 0) {
12-
std::cout << "BAD: saxpy_device failed\n";
16+
auto out = island_interface::saxpy(2.0f, x, y);
17+
if (!out) {
18+
std::cout << "BAD: saxpy failed\n";
1319
return 1;
1420
}
15-
if (scale_device(0.5f, out, 4) != 0) {
16-
std::cout << "BAD: scale_device failed\n";
21+
if (!island_interface::scale(0.5f, *out)) {
22+
std::cout << "BAD: scale failed\n";
1723
return 1;
1824
}
1925

2026
// (2*1+10)/2, (2*2+20)/2, (2*3+30)/2, (2*4+40)/2
2127
const float want[4] = {6, 12, 18, 24};
22-
bool ok = true;
23-
for (int i = 0; i < 4; ++i) {
24-
std::cout << std::format("out[{}]={} ", i, out[i]);
25-
if (out[i] != want[i]) ok = false;
28+
bool ok = out->size() == 4;
29+
for (std::size_t i = 0; ok && i < out->size(); ++i) {
30+
std::cout << std::format("out[{}]={} ", i, (*out)[i]);
31+
if ((*out)[i] != want[i]) ok = false;
2632
}
2733
std::cout << "\n";
2834
// Two entry points rather than one, because a generator that re-exported

0 commit comments

Comments
 (0)