Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/probe/okimage/hostconst/neg/extra.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
cxxflags = ["-DSHAPE_B"]
3 changes: 3 additions & 0 deletions .github/probe/okimage/hostconst/neg/mcpp.base.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[package]
name = "hostconst-neg"
version = "0.1.0"
5 changes: 5 additions & 0 deletions .github/probe/okimage/hostconst/neg/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "hostconst-neg"
version = "0.1.0"
[build]
cxxflags = ["-DSHAPE_B"]
9 changes: 9 additions & 0 deletions .github/probe/okimage/hostconst/neg/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "../../shape.hpp"
#if !defined(SHAPE_B)
platform runtime_host() noexcept { return PLATFORM; }
#else
platform host_ok() noexcept { return PLATFORM; }
#endif
// Must be refused: on the ok target the host is not known when compiling.
static_assert(host_ok() == PLATFORM);
int main() { return 0; }
2 changes: 2 additions & 0 deletions .github/probe/okimage/hostconst/pos/extra.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
cxxflags = ["-DSHAPE_B"]
3 changes: 3 additions & 0 deletions .github/probe/okimage/hostconst/pos/mcpp.base.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[package]
name = "hostconst-pos"
version = "0.1.0"
5 changes: 5 additions & 0 deletions .github/probe/okimage/hostconst/pos/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "hostconst-pos"
version = "0.1.0"
[build]
cxxflags = ["-DSHAPE_B"]
15 changes: 15 additions & 0 deletions .github/probe/okimage/hostconst/pos/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "../../shape.hpp"
#include <cstdio>
#if !defined(SHAPE_B)
platform runtime_host() noexcept { return PLATFORM; }
#else
platform host_ok() noexcept { return PLATFORM; }
#endif
static_assert(host() == PLATFORM); // usable at compile time
int main() {
if constexpr (host() == platform::windows) std::puts("if constexpr (host() == windows): taken");
else std::puts("if constexpr (host() == windows): not taken");
const platform h = host_ok(); // run time: asks
std::printf("host_ok() at run time = %d (PLATFORM %d)\n", (int)h, (int)PLATFORM);
return h == PLATFORM ? 0 : 1;
}
32 changes: 32 additions & 0 deletions .github/probe/okimage/hostconst/shape.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// The two shapes os::host() takes (design §6.1 R3, §8.6), reduced to what the
// compilers must accept.
// native target: host() is the compile target, usable in constant expressions;
// ok target: host() is a constexpr function whose constant evaluation is
// ill-formed (it is not known when compiling) and whose call at
// run time asks the environment.
enum class platform { linux, macos, windows, openkal };
#if defined(__MCPP_TARGET_WINDOWS__) || defined(_WIN32)
inline constexpr platform PLATFORM = platform::windows;
#elif defined(__APPLE__)
inline constexpr platform PLATFORM = platform::macos;
#else
inline constexpr platform PLATFORM = platform::linux;
#endif
constexpr platform host() noexcept { return PLATFORM; }

// The ok-target form, compiled here on a native target to see what each
// compiler says about it. Two shapes:
// A: a constexpr function that branches on `if consteval` and calls a
// non-constexpr marker in the constant branch (C++23 P2448 makes such a
// function valid while it is not used in a constant expression);
// B: a plain (non-constexpr) declaration, defined where the host is asked.
#if !defined(SHAPE_B)
platform runtime_host() noexcept;
platform not_known_when_compiling() noexcept;
constexpr platform host_ok() noexcept {
if consteval { return not_known_when_compiling(); }
else { return runtime_host(); }
}
#else
platform host_ok() noexcept;
#endif
119 changes: 119 additions & 0 deletions .github/probe/okimage/image/app.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/* An ok-format image for the probe: position-independent ELF, compiled for no
* operating system, importing kal_* and nothing else. The same bytes are run on
* every system of one architecture by the prototype runner (../runner).
*
* What it exercises:
* - calls into the runner's openkal implementation with this architecture's
* fixed convention (SysV on x86_64, AAPCS64 on aarch64);
* - a callback the implementation makes into the image (kal_task_start);
* - thread_local through -femulated-tls, served by __emutls_get_address below
* on nothing but kal_* (kal_task_current, kal_alloc);
* - arguments, a by-value structure (kal_stream), a 64-bit return. */
typedef unsigned long kal_uintptr;
typedef long kal_intptr;
typedef unsigned int kal_u32;
struct kal_stream { kal_uintptr h; };
struct kal_task { kal_uintptr h; };

struct kal_stream kal_stdout(void);
kal_intptr kal_stream_write(struct kal_stream, const void*, kal_uintptr);
kal_uintptr kal_env_arg_count(void);
kal_intptr kal_env_arg(kal_uintptr, char*, kal_uintptr);
int kal_task_start(void (*entry)(void*), void* arg, struct kal_task* out);
int kal_task_join(struct kal_task);
kal_uintptr kal_task_current(void);
void* kal_alloc(kal_uintptr size, kal_uintptr align);

/* The compiler may emit these for struct copies and zeroing. */
void* memcpy(void* d, const void* s, unsigned long n) {
unsigned char* a = d; const unsigned char* b = s;
while (n--) *a++ = *b++;
return d;
}
void* memset(void* d, int c, unsigned long n) {
unsigned char* a = d;
while (n--) *a++ = (unsigned char)c;
return d;
}

static unsigned long len(const char* s) { unsigned long n = 0; while (s[n]) ++n; return n; }
static void put(const char* s) { kal_stream_write(kal_stdout(), s, len(s)); }
static void put_num(long v) {
char b[24]; int i = 23; b[i] = 0;
int neg = v < 0; unsigned long u = neg ? -(unsigned long)v : (unsigned long)v;
do { b[--i] = (char)('0' + u % 10); u /= 10; } while (u);
if (neg) b[--i] = '-';
put(b + i);
}

/* ── emulated TLS on kal_* alone ───────────────────────────────────────────
* Each context is found by kal_task_current() in a small table; each variable
* gets an index on first use. A real C runtime would do this once, behind
* whatever per-context word the specification settles on (design §8.3). */
struct emutls_control { unsigned long size, align; union { unsigned long index; void* address; } object; void* value; };
#define MAX_CTX 64
#define MAX_VARS 32
static struct { kal_uintptr id; void* vars[MAX_VARS]; } g_ctx[MAX_CTX];
static unsigned long g_next_index;
static volatile int g_lock;
static void lock(void) { while (__atomic_exchange_n(&g_lock, 1, __ATOMIC_ACQUIRE)) {} }
static void unlock(void) { __atomic_store_n(&g_lock, 0, __ATOMIC_RELEASE); }

void* __emutls_get_address(struct emutls_control* c) {
unsigned long index = __atomic_load_n(&c->object.index, __ATOMIC_ACQUIRE);
if (index == 0) {
lock();
if (c->object.index == 0) c->object.index = ++g_next_index;
index = c->object.index;
unlock();
}
const kal_uintptr self = kal_task_current();
lock();
int slot = -1;
for (int i = 0; i < MAX_CTX; ++i) if (g_ctx[i].id == self) { slot = i; break; }
if (slot < 0) for (int i = 0; i < MAX_CTX; ++i) if (g_ctx[i].id == 0) { g_ctx[i].id = self; slot = i; break; }
void** var = &g_ctx[slot].vars[index - 1];
if (!*var) {
unsigned char* p = kal_alloc(c->size, c->align ? c->align : 8);
if (c->value) memcpy(p, c->value, c->size); else memset(p, 0, c->size);
*var = p;
}
void* out = *var;
unlock();
return out;
}

static __thread long tl_value = 7;

struct job { long set; long seen; kal_u32 done; };
static void worker(void* arg) {
struct job* j = arg;
long before = tl_value; /* each context starts from the initial value */
tl_value = j->set;
j->seen = (before == 7 && tl_value == j->set) ? tl_value : -1;
}

/* Many arguments, mixed widths: the convention is fixed per architecture. */
__attribute__((noinline)) static long mix(long a, int b, long c, int d, long e, int f, long g, int h) {
return a + b * 2 + c * 3 + d * 4 + e * 5 + f * 6 + g * 7 + h * 8;
}

__attribute__((visibility("default"))) int ok_main(void) {
put("image: hello from the same bytes\n");
put("image: argc as kal_env_arg_count = "); put_num((long)kal_env_arg_count()); put("\n");
char a0[256]; kal_intptr n = kal_env_arg(0, a0, sizeof a0 - 1);
if (n > 0 && n < 255) { a0[n] = 0; put("image: arg0 = "); put(a0); put("\n"); }
put("image: mix = "); put_num(mix(1, 2, 3, 4, 5, 6, 7, 8)); put(" (expect 204)\n");

tl_value = 1;
struct job j1 = {10, 0, 0}, j2 = {20, 0, 0};
struct kal_task t1, t2;
int r1 = kal_task_start(worker, &j1, &t1);
int r2 = kal_task_start(worker, &j2, &t2);
if (r1 == 0) kal_task_join(t1);
if (r2 == 0) kal_task_join(t2);
put("image: tls main="); put_num(tl_value);
put(" ctx1="); put_num(j1.seen); put(" ctx2="); put_num(j2.seen);
put(" (expect 1 10 20) start="); put_num(r1); put("/"); put_num(r2); put("\n");
return (tl_value == 1 && j1.seen == 10 && j2.seen == 20 && mix(1, 2, 3, 4, 5, 6, 7, 8) == 204) ? 0 : 3;
}
18 changes: 18 additions & 0 deletions .github/probe/okimage/image/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Builds app-<arch>.oke: ELF ET_DYN, no operating system, imports kal_* only.
# usage: build.sh <llvm bin dir> <out dir>
set -euo pipefail
LLVM="$1"; OUT="$2"; HERE=$(cd "$(dirname "$0")" && pwd)
mkdir -p "$OUT"
for arch in x86_64 aarch64; do
extra=""
[ "$arch" = aarch64 ] && extra="-ffixed-x18 -mno-outline-atomics"
"$LLVM/clang" --target=$arch-unknown-none-elf -ffreestanding -fPIC -fvisibility=hidden \
-femulated-tls -fno-stack-protector -fno-builtin -O2 $extra \
-c "$HERE/app.c" -o "$OUT/app-$arch.o"
"$LLVM/ld.lld" -shared -z max-page-size=65536 -z noexecstack -z now --hash-style=both \
--no-undefined-version -o "$OUT/app-$arch.oke" "$OUT/app-$arch.o"
echo "== $arch"
"$LLVM/llvm-readelf" -h -l -d --dyn-syms "$OUT/app-$arch.oke" | grep -E "Type:|Machine:|LOAD|NEEDED|FLAGS|UND|ok_main|RELA|JMPREL" | head -30
sha256sum "$OUT/app-$arch.oke"
done
15 changes: 15 additions & 0 deletions .github/probe/okimage/runner/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "okrunner-probe"
version = "0.1.0"

[dependencies]
openkal = "0.16.0"

[target.linux.dependencies]
openkal-linux = "0.17.0"

[target.macos.dependencies]
openkal-macos = "0.14.1"

[target.windows.dependencies]
openkal-windows = "0.12.0"
Loading
Loading