iter 23.2.1: runtime/str.c with ail_str_eq + unconditional link
This commit is contained in:
@@ -1970,6 +1970,37 @@ fn locate_rc_runtime() -> Result<PathBuf> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Locate the workspace-root `runtime/str.c` file relative to the
|
||||
/// `ail` binary, mirroring [`locate_rc_runtime`]. Iter 23.2:
|
||||
/// `runtime/str.c` houses Str-runtime primitives (`ail_str_eq`,
|
||||
/// later `ail_str_compare`) and is linked unconditionally for every
|
||||
/// alloc strategy — `@ail_str_eq` is declared in the emitted IR
|
||||
/// header (codegen) without an alloc-strategy guard, so the symbol
|
||||
/// must always be resolvable at link time.
|
||||
fn locate_str_runtime() -> Result<PathBuf> {
|
||||
let candidates = [
|
||||
std::env::current_exe().ok(),
|
||||
std::env::current_dir().ok(),
|
||||
];
|
||||
for start in candidates.iter().flatten() {
|
||||
let mut cur: &Path = start.as_path();
|
||||
loop {
|
||||
let candidate = cur.join("runtime").join("str.c");
|
||||
if candidate.exists() {
|
||||
return Ok(candidate);
|
||||
}
|
||||
match cur.parent() {
|
||||
Some(p) => cur = p,
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
anyhow::bail!(
|
||||
"could not locate `runtime/str.c` (linked unconditionally). \
|
||||
Run `ail` from inside the AILang workspace."
|
||||
)
|
||||
}
|
||||
|
||||
/// Iter 9b: shared build helper for `Cmd::Build` and `Cmd::Run`.
|
||||
/// Loads the workspace, runs the typechecker, emits IR, and links via
|
||||
/// clang. On typecheck failure, prints diagnostics to stderr and exits
|
||||
@@ -2060,6 +2091,29 @@ fn build_to(
|
||||
let out_bin = out.unwrap_or_else(|| Path::new(".").join(&ws.entry).with_extension(""));
|
||||
let mut clang = std::process::Command::new("clang");
|
||||
clang.arg(opt).arg("-o").arg(&out_bin).arg(&ll_path);
|
||||
// Iter 23.2: compile and link `runtime/str.c` unconditionally —
|
||||
// `@ail_str_eq` is emitted in the IR header without an alloc-strategy
|
||||
// guard (it backs the prelude `eq__Str` mono symbol). The .o is
|
||||
// cached at <tmpdir>/str.o per build invocation; if a program never
|
||||
// monomorphises `eq__Str`, clang -O2 dead-strips the symbol so the
|
||||
// linker overhead is fixed and negligible.
|
||||
let str_src = locate_str_runtime()?;
|
||||
let str_obj = tmpdir.join("str.o");
|
||||
let cstatus = std::process::Command::new("clang")
|
||||
.arg("-O2")
|
||||
.arg("-c")
|
||||
.arg(&str_src)
|
||||
.arg("-o")
|
||||
.arg(&str_obj)
|
||||
.status()
|
||||
.context("compiling runtime/str.c")?;
|
||||
if !cstatus.success() {
|
||||
anyhow::bail!(
|
||||
"clang failed compiling str.c (status {})",
|
||||
cstatus
|
||||
);
|
||||
}
|
||||
clang.arg(&str_obj);
|
||||
match alloc {
|
||||
ailang_codegen::AllocStrategy::Gc => {
|
||||
// Boehm conservative GC (Decision 9 / Iter 14f). The lowered
|
||||
@@ -2139,6 +2193,17 @@ fn build_to(
|
||||
mod tests {
|
||||
use super::compose_merge_prose_prompt;
|
||||
|
||||
#[test]
|
||||
fn locate_str_runtime_finds_workspace_str_c() {
|
||||
let p = super::locate_str_runtime().expect("locate runtime/str.c");
|
||||
assert!(
|
||||
p.ends_with("runtime/str.c"),
|
||||
"expected path ending in runtime/str.c, got {}",
|
||||
p.display()
|
||||
);
|
||||
assert!(p.exists(), "located path must exist on disk: {}", p.display());
|
||||
}
|
||||
|
||||
/// The composed prompt must contain all three payloads
|
||||
/// byte-for-byte — the LLM has to see exactly what the user wrote,
|
||||
/// the original module's Form-A bytes, and the embedded spec, with
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* AILang Str runtime primitives.
|
||||
*
|
||||
* Provides ABI-stable string operations against the existing
|
||||
* constant-Str layout (NUL-terminated byte buffer, passed as
|
||||
* `ptr` in LLVM IR). Lives separately from runtime/rc.c so that
|
||||
* a future heap-Str ABI milestone can swap the implementations
|
||||
* without touching the RC runtime.
|
||||
*
|
||||
* Linked unconditionally by `ail build` (Gc, Bump, Rc) so that
|
||||
* any program that monomorphises `eq__Str` (auto-loaded from
|
||||
* the prelude) finds the symbol. Programs that never call
|
||||
* `eq` on Str leave @ail_str_eq as a dead-stripped no-op at
|
||||
* link time under clang -O2.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Byte-equality on NUL-terminated strings. Mirrors strcmp(...) == 0
|
||||
* but exposed under a stable AILang-namespaced ABI so the codegen
|
||||
* caller does not assume libc's strcmp shape. Heap-Str ABI milestone
|
||||
* will replace the body with length-prefixed comparison without
|
||||
* changing the signature. */
|
||||
bool ail_str_eq(const char *a, const char *b) {
|
||||
return strcmp(a, b) == 0;
|
||||
}
|
||||
Reference in New Issue
Block a user