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`.
|
/// Iter 9b: shared build helper for `Cmd::Build` and `Cmd::Run`.
|
||||||
/// Loads the workspace, runs the typechecker, emits IR, and links via
|
/// Loads the workspace, runs the typechecker, emits IR, and links via
|
||||||
/// clang. On typecheck failure, prints diagnostics to stderr and exits
|
/// 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 out_bin = out.unwrap_or_else(|| Path::new(".").join(&ws.entry).with_extension(""));
|
||||||
let mut clang = std::process::Command::new("clang");
|
let mut clang = std::process::Command::new("clang");
|
||||||
clang.arg(opt).arg("-o").arg(&out_bin).arg(&ll_path);
|
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 {
|
match alloc {
|
||||||
ailang_codegen::AllocStrategy::Gc => {
|
ailang_codegen::AllocStrategy::Gc => {
|
||||||
// Boehm conservative GC (Decision 9 / Iter 14f). The lowered
|
// Boehm conservative GC (Decision 9 / Iter 14f). The lowered
|
||||||
@@ -2139,6 +2193,17 @@ fn build_to(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::compose_merge_prose_prompt;
|
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
|
/// The composed prompt must contain all three payloads
|
||||||
/// byte-for-byte — the LLM has to see exactly what the user wrote,
|
/// 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
|
/// 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