Skip to content
Merged
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
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ env:
# 0.20.0: 2026.11.9.1, for the tests on the mtest library (a test file of
# mtest's objects has no `main' from 2026.11.8.1, and `mcpp test' keeps its
# runs in mtest's store from 2026.11.9.1).
MCPP_VERSION: 2026.11.9.1
# 0.20.3: 2026.11.13.1, which selects by `cfg(object-format)' and `cfg(interface)'.
MCPP_VERSION: 2026.11.13.1
XLINGS_VERSION: v2026.10.26.1
# okrun is built from openkal-image, whose openkal-run states its own floor.
OKRUN_MCPP_VERSION: 2026.11.10.1
OKRUN_MCPP_VERSION: 2026.11.13.1
XLINGS_NON_INTERACTIVE: '1'

jobs:
Expand Down
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ C library.

```toml
[dependencies]
openkal-llvm-runtime = "0.20.2"
openkal-llvm-runtime = "0.20.3"
```

> **Engine floor (mcpp 2026.11.2.1):** 0.17.0 is built over openkal-musl 0.26.0,
Expand Down Expand Up @@ -328,6 +328,46 @@ openkal-musl 0.27.3 selects openkal-linux 0.24.1 on Linux and openkal-ld 0.4.3
for an image. Linux confinement requires Landlock ABI 3 to enforce read-only
grants against raw file truncation. The runtime implementation is unchanged.

## An image asks for the suspension primitive (0.20.3)

In an ok image `std::atomic::wait` and `notify` import `kal_task_wait` and
`kal_task_wake` weakly (`OPENKAL_HAS_TASK=2`, SPEC 12.4): an image is run by a
loader on the implementation of whatever machine runs it, a firmware among them.
Where the two names are bound the wait suspends as before; where they are not,
the wait returns and the caller polls, as in the configuration without
`openkal.task`. The image's requirements then name no `openkal.task` on this
library's account. With openkal-musl 0.29.6, which does the same for its own
optional interfaces, an image that uses neither files nor processes nor threads
requires only `openkal.abort`, `openkal.env`, `openkal.memory`, `openkal.stream`
and `openkal.time`. The libraries built for a known implementation are unchanged.

## UEFI, and the PE container (0.20.3)

Every question this package asked of Windows was a question about PE: the
compiler-rt sources of the container, emulated TLS, libunwind's register record
and its search for the unwind tables. They are now selected by
`cfg(object-format = "pe")` (mcpp 2026.11.13.1) and the private define
`OPENKAL_TARGET_PE`, so `x86_64-uefi`, a platform's PE row, is served as Windows
is. `__libunwind_config.h`, which a consumer's compile reads without this
package's defines, learns the container from `port/include-pe/__openkal_target_pe.h`,
a directory on the PE rows' include line alone. Where the implementation beneath
states its interfaces and `openkal.task` is not among them (openkal-uefi),
`OPENKAL_HAS_TASK` is 0 by `cfg(not(interface = "openkal.task"))`. This release
requires mcpp 2026.11.13.1.

## DOS (0.20.3; technical experiment only)

`i686-dos-musl`, a platform's row over openkal-dos, takes the Linux selection of
compiler-rt, the allocator of a bare machine (`malloc-mallocng`) and two
differences. compiler-rt's `emutls.c` is built: the row compiles with
`-femulated-tls`, there being no thread register the toolchain can address, and
nothing else answers `__emutls_get_address` (on Linux the access is the thread
register's, and in an ok image openkal-musl answers it). And the `__bf16`
conversions stay out: the row's processor is the 486 (`-march=i486`; DOSBox
Staging executes no CMPXCHG8B), which has no SSE2. The same holds for `i686-openkal`, the ok image of i386, which
openkal-platform 0.28.0 compiles for the 486 too; `i686-linux-musl` keeps the
toolchain's processor and the conversions.

## i686 (0.20.2)

Built for `i686-linux-musl` above openkal-musl and openkal-linux on i386
Expand Down
6 changes: 3 additions & 3 deletions llvm/PATCHES.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ openkal-musl 的 `[c-abi] presents = "posix"` 把它和 `_WIN32`、`__MINGW32__`
分支,保存例程仍旧往 `%rdi` 写,而 Win64 调用约定下调用者实际把指针传在 `%rcx`。表现
只在运行期:第一次 `throw` 就在 `unw_getcontext` 里对一个由错误寄存器读出的地址(测得
是空指针)写内存。已按 `libunwind` 已有的同一判据处理:内部四处(两个 `.S`、
`Registers.hpp`)换成 `OPENKAL_TARGET_WINDOWS`;`__libunwind_config.h` 已安装(经
`Registers.hpp`)换成 `OPENKAL_TARGET_PE`;`__libunwind_config.h` 已安装(经
`unwind.h` / `libunwind.h` 公开可达),换成 `__CYGWIN__`。下一次改这棵树,完整的检查是
`grep -rn "_WIN32\|_WIN64\|__MINGW32__\|__MINGW64__"`,不是只 grep 第一个。

Expand All @@ -197,7 +197,7 @@ openkal-musl 的 `[c-abi] presents = "posix"` 把它和 `_WIN32`、`__MINGW32__`
三支全部落空,直接 `#error`。

它**不是**已安装的头,只由本包自己的构建编译,所以按本文件的规矩用私有 define
`OPENKAL_TARGET_WINDOWS`——而 `__libunwind_config.h` 反过来用 `__MCPP_TARGET_WINDOWS__`,
`OPENKAL_TARGET_PE`——而 `__libunwind_config.h` 反过来用 `__MCPP_TARGET_WINDOWS__`,
因为它**是**已安装的。两者读不同的宏,回答同一个关于同一个目标的问题,这正是下面那一节
要求的性质。

Expand All @@ -223,7 +223,7 @@ extern "C" { int __cxa_thread_atexit(Dtor, void*, void*) throw(); }
`doctest` 与 `spdlog` 两个成员在 `x86_64-windows-gnu` 上停在
`ld.lld: error: undefined symbol: __cxa_thread_atexit`。

这个文件**不是**已安装的头,所以按本文的规矩用私有 define `OPENKAL_TARGET_WINDOWS`。
这个文件**不是**已安装的头,所以按本文的规矩用私有 define `OPENKAL_TARGET_PE`。

### 第二层:`__thread` 在 emutls 下活不过 key 析构

Expand Down
6 changes: 3 additions & 3 deletions llvm/compiler-rt/lib/builtins/int_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
#define SYMBOL_NAME(name) XSTR(__USER_LABEL_PREFIX__) #name

// ─── openkal ─── BEGIN
// `OPENKAL_TARGET_WINDOWS` joins this guard, and the alternative was a hard
// `OPENKAL_TARGET_PE` joins this guard, and the alternative was a hard
// `#error Unsupported target` a release from now.
//
// This target is PE, and PE takes the first branch: `__attribute__((alias))`
Expand All @@ -64,12 +64,12 @@
//
// The private define is the right name here, not `__mcpp_target_windows__`:
// this file is NOT installed, it is compiled only by this package's own
// build, and `mcpp.toml` carries `OPENKAL_TARGET_WINDOWS` to it in `cflags`.
// build, and `mcpp.toml` carries `OPENKAL_TARGET_PE` to it in `cflags`.
// That is the same rule the other five patches in this tree follow, and the
// reason `__libunwind_config.h` does the opposite --- it IS installed. See
// `llvm/PATCHES.md`.
#if defined(__ELF__) || defined(__MINGW32__) || defined(__wasm__) || \
defined(_AIX) || defined(__CYGWIN__) || defined(OPENKAL_TARGET_WINDOWS)
defined(_AIX) || defined(__CYGWIN__) || defined(OPENKAL_TARGET_PE)
// ─── openkal ─── END
#define COMPILER_RT_ALIAS(name, aliasname) \
COMPILER_RT_ABI __typeof(name) aliasname __attribute__((__alias__(#name)));
Expand Down
24 changes: 24 additions & 0 deletions llvm/libcxx/src/atomic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,30 @@ extern "C" {
// the specification says they are. openkal derives them from the compiler for
// exactly this reason: a consumer can restate them without a header and cannot
// get a different type than the implementation was built with.
# if OPENKAL_HAS_TASK == 2
// ⚠️ AN OK IMAGE ASKS AT RUN TIME (OPENKAL_HAS_TASK == 2, 0.20.3). The image is run by a loader on
// the implementation of whatever machine runs it, which a firmware may be: the two names are
// imported weakly (SPEC 12.4: bound where provided, zero otherwise, and kept out of the image's
// requirements), and where they are zero the wait is the baseline's --- it returns, and the caller
// polls --- as on a machine with one execution context (the `== 0` configuration).
__attribute__((__weak__))
# endif
int kal_task_wait(const unsigned int* __word, unsigned int __expected, unsigned long long __timeout_ns);
# if OPENKAL_HAS_TASK == 2
__attribute__((__weak__))
# endif
int kal_task_wake(const unsigned int* __word, __okl_uptr __count, __okl_uptr* __woken);
}

// Whether the suspension primitive is bound: always where the implementation is known to have it.
static inline bool __okl_task_bound() {
# if OPENKAL_HAS_TASK == 2
return kal_task_wait != nullptr && kal_task_wake != nullptr;
# else
return true;
# endif
}

template <std::size_t _Size, class MaybeTimeout>
static void __platform_wait_on_address(void const* __ptr, void const* __val, MaybeTimeout maybe_timeout_ns) {
static_assert(_Size == 4, "Can only wait on 4 bytes value");
Expand All @@ -142,6 +162,8 @@ static void __platform_wait_on_address(void const* __ptr, void const* __val, May
// reason: a wait that never times out cannot notice a wake it raced with.
// Zero means "no timeout" to openkal, so the default is stated rather than
// passed through.
if (!__okl_task_bound())
return;
unsigned long long __ns = 2'000'000'000ull;
if constexpr (!is_same_v<MaybeTimeout, NoTimeout>) {
__ns = static_cast<unsigned long long>(maybe_timeout_ns);
Expand All @@ -157,6 +179,8 @@ static void __platform_wake_by_address(void const* __ptr, bool __notify_one) {
// ⚠️ `-1` for "all", which is openkal's spelling of what the other branches
// write as `INT_MAX`. The count is a `kal_uintptr`, so the maximum is stated
// as the wrap rather than as a header's constant.
if (!__okl_task_bound())
return;
__okl_uptr __woken = 0;
kal_task_wake(reinterpret_cast<const unsigned int*>(__ptr),
__notify_one ? __okl_uptr(1) : __okl_uptr(-1),
Expand Down
6 changes: 3 additions & 3 deletions llvm/libcxxabi/src/cxa_thread_atexit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ namespace {
// __cxa_thread_atexit`, measured on two members of the index's compatibility
// suite (doctest, spdlog) on `x86_64-windows-gnu`.
//
// `OPENKAL_TARGET_WINDOWS` IS THIS PACKAGE'S OWN DEFINE, not the engine's
// `OPENKAL_TARGET_PE` IS THIS PACKAGE'S OWN DEFINE, not the engine's
// `__MCPP_TARGET_WINDOWS__`, and the difference is whose compile reads the
// file: this one is a source of this package and is never installed, so a
// package-private define reaches it. The same distinction chose the define in
Expand All @@ -182,9 +182,9 @@ namespace {
//
// THE OK IMAGE IS THE FOURTH SUCH TARGET. Its triple names no operating system,
// so the guard reads `OPENKAL_TARGET_IMAGE`, which `mcpp.toml` states for
// `cfg(os = "openkal")` as it states `OPENKAL_TARGET_WINDOWS` for PE.
// `cfg(os = "openkal")` as it states `OPENKAL_TARGET_PE` for PE.
#if defined(__linux__) || defined(__Fuchsia__) \
|| defined(OPENKAL_TARGET_WINDOWS) || defined(OPENKAL_TARGET_IMAGE) \
|| defined(OPENKAL_TARGET_PE) || defined(OPENKAL_TARGET_IMAGE) \
|| defined(__APPLE__)
extern "C" {

Expand Down
14 changes: 11 additions & 3 deletions llvm/libunwind/include/__libunwind_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
// `okm_setjmp.S` sizes `jmp_buf` for, and it is gone on this target along
// with `_WIN32`. THIS HEADER IS INSTALLED (`llvm/libunwind/include/`, read
// through `unwind.h` and `libunwind.h`, both public), so it cannot read
// `OPENKAL_TARGET_WINDOWS` --- that is this package's own private build
// `OPENKAL_TARGET_PE` --- that is this package's own private build
// define (mcpp.toml) and an application calling `unw_getcontext` directly
// against `unw_context_t` does not see it. `__MCPP_TARGET_WINDOWS__` is read
// instead: mcpp states the target under its OWN name, target-wide, an
Expand All @@ -76,11 +76,19 @@
// packages doing exactly that and reaching `#include <windows.h>`. It
// answered this file's question only by accident.
// `UnwindRegistersSave.S` writes exactly the record sized here and reads
// `OPENKAL_TARGET_WINDOWS` rather than `__CYGWIN__`, because it is NOT
// `OPENKAL_TARGET_PE` rather than `__CYGWIN__`, because it is NOT
// installed --- compiled only by this package's own build, like
// `okm_setjmp.S`. The two answer the same question about the same target
// without reading the same macro, which is what has to hold; see that file.
# if defined(_WIN64) || defined(__MCPP_TARGET_WINDOWS__) || defined(__CYGWIN__)
// 0.20.3: THE CONTAINER'S MARK, NOT A SYSTEM'S NAME. Every PE row presents
// POSIX through the same substitution (Windows's and UEFI's alike), so the
// record is the Microsoft x64 convention's on each; this package puts
// `port/include-pe' on those rows' include line alone (mcpp.toml), which
// carries the mark to a consumer's compile.
# if __has_include(<__openkal_target_pe.h>)
# include <__openkal_target_pe.h>
# endif
# if defined(_WIN64) || defined(OPENKAL_TARGET_PE) || defined(__MCPP_TARGET_WINDOWS__) || defined(__CYGWIN__)
// ─── openkal ─── END
# define _LIBUNWIND_CONTEXT_SIZE 54
# ifdef __SEH__
Expand Down
6 changes: 3 additions & 3 deletions llvm/libunwind/src/AddressSpace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,11 @@ extern char __exidx_end;
// `_WIN32` on this line is the OUTER question --- "is this even the PE
// branch" --- and it is an identity question, not a platform-service one;
// the service question is the `OPENKAL` that already decides what is INSIDE
// the branch, just below. `OPENKAL_TARGET_WINDOWS` (mcpp.toml) is what
// the branch, just below. `OPENKAL_TARGET_PE` (mcpp.toml) is what
// answers the outer one now that `_WIN32` does not reach this target. See
// `config.h` for the fuller account.
#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && \
(defined(_WIN32) || defined(OPENKAL_TARGET_WINDOWS))
(defined(_WIN32) || defined(OPENKAL_TARGET_PE))
// ─── openkal ─── END

// ─── openkal ─── BEGIN
Expand Down Expand Up @@ -602,7 +602,7 @@ inline bool LocalAddressSpace::findUnwindSections(
// The same outer identity question as the declaration above, answered the
// same way.
#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && \
(defined(_WIN32) || defined(OPENKAL_TARGET_WINDOWS))
(defined(_WIN32) || defined(OPENKAL_TARGET_PE))
// ─── openkal ─── END
// ─── openkal ─── BEGIN
#if defined(OPENKAL)
Expand Down
12 changes: 6 additions & 6 deletions llvm/libunwind/src/Registers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,15 +352,15 @@ class _LIBUNWIND_HIDDEN Registers_x86_64 {
// writes and `__libunwind_config.h` sizes `unw_context_t` for --- all three
// have to agree, so all three read the target the same way. This file is
// not installed (`llvm/libunwind/src/`, compiled only by this package's own
// build), so it reads `OPENKAL_TARGET_WINDOWS` (mcpp.toml) as the assembly
// build), so it reads `OPENKAL_TARGET_PE` (mcpp.toml) as the assembly
// file does, rather than `__CYGWIN__` as the installed header does.
#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS)
#if defined(_WIN64) || defined(OPENKAL_TARGET_PE)
// ─── openkal ─── END
uint64_t __padding; // 16-byte align
#endif
};
GPRs _registers;
#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS)
#if defined(_WIN64) || defined(OPENKAL_TARGET_PE)
v128 _xmm[16];
#endif
};
Expand Down Expand Up @@ -579,7 +579,7 @@ inline bool Registers_x86_64::validVectorRegister(int regNum) const {
// ─── openkal ─── BEGIN
// Whether `_xmm` exists at all in this object, the same fact the struct
// definition above reads the same way.
#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS)
#if defined(_WIN64) || defined(OPENKAL_TARGET_PE)
// ─── openkal ─── END
if (regNum < UNW_X86_64_XMM0)
return false;
Expand All @@ -593,7 +593,7 @@ inline bool Registers_x86_64::validVectorRegister(int regNum) const {
}

inline v128 Registers_x86_64::getVectorRegister(int regNum) const {
#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS)
#if defined(_WIN64) || defined(OPENKAL_TARGET_PE)
assert(validVectorRegister(regNum));
return _xmm[regNum - UNW_X86_64_XMM0];
#else
Expand All @@ -603,7 +603,7 @@ inline v128 Registers_x86_64::getVectorRegister(int regNum) const {
}

inline void Registers_x86_64::setVectorRegister(int regNum, v128 value) {
#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS)
#if defined(_WIN64) || defined(OPENKAL_TARGET_PE)
assert(validVectorRegister(regNum));
_xmm[regNum - UNW_X86_64_XMM0] = value;
#else
Expand Down
6 changes: 3 additions & 3 deletions llvm/libunwind/src/UnwindRegistersRestore.S
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_x86_64_jumpto)
# Same target, same reason as `UnwindRegistersSave.S`: the machine code is
# still Win64, so the pointer still arrives in `%rcx`, and `_WIN64` is what
# used to say so before this target stopped defining it along with
# `_WIN32`. `OPENKAL_TARGET_WINDOWS` (mcpp.toml) is read in its place.
#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS)
# `_WIN32`. `OPENKAL_TARGET_PE` (mcpp.toml) is read in its place.
#if defined(_WIN64) || defined(OPENKAL_TARGET_PE)
# ─── openkal ─── END
# On entry, thread_state pointer is in rcx; move it into rdi
# to share restore code below. Since this routine restores and
Expand Down Expand Up @@ -128,7 +128,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_x86_64_jumpto)

# ─── openkal ─── BEGIN
# The vector registers, the second half of the same fact.
#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS)
#if defined(_WIN64) || defined(OPENKAL_TARGET_PE)
# ─── openkal ─── END
movdqu 176(%rdi),%xmm0
movdqu 192(%rdi),%xmm1
Expand Down
6 changes: 3 additions & 3 deletions llvm/libunwind/src/UnwindRegistersSave.S
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,10 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
# table for this target), so a caller here still passes `thread_state` in
# `%rcx`, and reading it out of `%rdi` instead is what a SysV callee does.
# `_WIN64` is gone from this target along with `_WIN32`;
# `OPENKAL_TARGET_WINDOWS` (mcpp.toml) is this package's own manifest
# `OPENKAL_TARGET_PE` (mcpp.toml) is this package's own manifest
# stating it, exactly as `okm_setjmp.S` states the equivalent fact for
# `jmp_buf` --- this file is not installed, so it can read it.
#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS)
#if defined(_WIN64) || defined(OPENKAL_TARGET_PE)
# ─── openkal ─── END
#define PTR %rcx
#define TMP %rdx
Expand Down Expand Up @@ -169,7 +169,7 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
# caller's to preserve. On Win64 they are not, so this file must save them;
# on SysV the callee owns none of them and there is nothing here to save.
# Same target, same answer as above.
#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS)
#if defined(_WIN64) || defined(OPENKAL_TARGET_PE)
# ─── openkal ─── END
movdqu %xmm0,176(PTR)
movdqu %xmm1,192(PTR)
Expand Down
8 changes: 4 additions & 4 deletions llvm/libunwind/src/assembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,8 @@
// Assembler directive syntax by object format, not a platform service ---
// the same identity question `config.h` asks, asked again because this file
// is included from `.S` sources rather than from C++. `_WIN32` does not
// reach this target; `OPENKAL_TARGET_WINDOWS` (mcpp.toml) does.
#elif defined(_WIN32) || defined(OPENKAL_TARGET_WINDOWS)
// reach this target; `OPENKAL_TARGET_PE` (mcpp.toml) does.
#elif defined(_WIN32) || defined(OPENKAL_TARGET_PE)
// ─── openkal ─── END

#define SYMBOL_IS_FUNC(name) \
Expand All @@ -210,10 +210,10 @@
#define HIDDEN_SYMBOL(name)

// ─── openkal ─── BEGIN
// `__MINGW32__` is also gone on this target; `OPENKAL_TARGET_WINDOWS` takes
// `__MINGW32__` is also gone on this target; `OPENKAL_TARGET_PE` takes
// this branch in its place, for the same reason and with the same
// measurement behind it as `config.h`'s `_LIBUNWIND_WEAK_ALIAS`.
#if defined(__MINGW32__) || defined(OPENKAL_TARGET_WINDOWS)
#if defined(__MINGW32__) || defined(OPENKAL_TARGET_PE)
// ─── openkal ─── END
#define WEAK_ALIAS(name, aliasname) \
.globl SYMBOL_NAME(aliasname) SEPARATOR \
Expand Down
Loading
Loading