iter 23.3.1: runtime ail_str_compare + IR header declaration
This commit is contained in:
@@ -9,6 +9,7 @@ declare i32 @puts(ptr)
|
||||
declare ptr @GC_malloc(i64)
|
||||
declare i32 @strcmp(ptr, ptr)
|
||||
declare zeroext i1 @ail_str_eq(ptr, ptr)
|
||||
declare i32 @ail_str_compare(ptr, ptr)
|
||||
declare i64 @llvm.fptosi.sat.i64.f64(double)
|
||||
|
||||
@ail_hello_main_clos = private unnamed_addr constant { ptr, ptr } { ptr @ail_hello_main_adapter, ptr null }
|
||||
|
||||
@@ -9,6 +9,7 @@ declare i32 @puts(ptr)
|
||||
declare ptr @GC_malloc(i64)
|
||||
declare i32 @strcmp(ptr, ptr)
|
||||
declare zeroext i1 @ail_str_eq(ptr, ptr)
|
||||
declare i32 @ail_str_compare(ptr, ptr)
|
||||
declare i64 @llvm.fptosi.sat.i64.f64(double)
|
||||
|
||||
@ail_list_sum_list_clos = private unnamed_addr constant { ptr, ptr } { ptr @ail_list_sum_list_adapter, ptr null }
|
||||
|
||||
@@ -9,6 +9,7 @@ declare i32 @puts(ptr)
|
||||
declare ptr @GC_malloc(i64)
|
||||
declare i32 @strcmp(ptr, ptr)
|
||||
declare zeroext i1 @ail_str_eq(ptr, ptr)
|
||||
declare i32 @ail_str_compare(ptr, ptr)
|
||||
declare i64 @llvm.fptosi.sat.i64.f64(double)
|
||||
|
||||
@ail_max3_max_clos = private unnamed_addr constant { ptr, ptr } { ptr @ail_max3_max_adapter, ptr null }
|
||||
|
||||
@@ -9,6 +9,7 @@ declare i32 @puts(ptr)
|
||||
declare ptr @GC_malloc(i64)
|
||||
declare i32 @strcmp(ptr, ptr)
|
||||
declare zeroext i1 @ail_str_eq(ptr, ptr)
|
||||
declare i32 @ail_str_compare(ptr, ptr)
|
||||
declare i64 @llvm.fptosi.sat.i64.f64(double)
|
||||
|
||||
@ail_sum_sum_clos = private unnamed_addr constant { ptr, ptr } { ptr @ail_sum_sum_adapter, ptr null }
|
||||
|
||||
@@ -9,6 +9,7 @@ declare i32 @puts(ptr)
|
||||
declare ptr @GC_malloc(i64)
|
||||
declare i32 @strcmp(ptr, ptr)
|
||||
declare zeroext i1 @ail_str_eq(ptr, ptr)
|
||||
declare i32 @ail_str_compare(ptr, ptr)
|
||||
declare i64 @llvm.fptosi.sat.i64.f64(double)
|
||||
|
||||
@ail_ws_lib_add_clos = private unnamed_addr constant { ptr, ptr } { ptr @ail_ws_lib_add_adapter, ptr null }
|
||||
|
||||
@@ -495,6 +495,15 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy) -> Result<String>
|
||||
// by `ail build` (see `crates/ail/src/main.rs::build_to`). The
|
||||
// `zeroext i1` return matches clang's lowering of C `_Bool`.
|
||||
out.push_str("declare zeroext i1 @ail_str_eq(ptr, ptr)\n");
|
||||
// Iter 23.3: `runtime/str.c::ail_str_compare` backs the prelude's
|
||||
// `compare__Str` mono symbol (see `try_emit_primitive_instance_body`
|
||||
// `compare__Str` arm below). Declared unconditionally on the same
|
||||
// rationale as `@ail_str_eq` — the .o is supplied by the
|
||||
// unconditionally-linked `runtime/str.c`, and clang -O2 dead-
|
||||
// strips the symbol when no caller exists. Returns i32 normalised
|
||||
// to {-1, 0, +1} so the branch ladder in the intercept can compare
|
||||
// against constant 0 directly.
|
||||
out.push_str("declare i32 @ail_str_compare(ptr, ptr)\n");
|
||||
// Floats iter 4.4: saturating fp-to-int intrinsic for
|
||||
// float_to_int_truncate. NaN → 0, +Inf → i64::MAX, -Inf →
|
||||
// i64::MIN, finite-out-of-range saturates, finite-in-range
|
||||
@@ -3653,4 +3662,38 @@ mod tests {
|
||||
"eq__Str body must call @ail_str_eq; ir was:\n{ir}"
|
||||
);
|
||||
}
|
||||
|
||||
/// Iter 23.3: `runtime/str.c::ail_str_compare` backs the prelude's
|
||||
/// `compare__Str` mono symbol (see `try_emit_primitive_instance_body`).
|
||||
/// The declaration is unconditional in the IR header alongside
|
||||
/// `@ail_str_eq`; this test pins the header line so a regression
|
||||
/// that drops it would fail at link time only on programs that
|
||||
/// actually call `compare` on a Str.
|
||||
#[test]
|
||||
fn ir_header_declares_ail_str_compare() {
|
||||
let m = Module {
|
||||
schema: SCHEMA.into(),
|
||||
name: "t".into(),
|
||||
imports: vec![],
|
||||
defs: vec![Def::Fn(FnDef {
|
||||
name: "main".into(),
|
||||
ty: Type::Fn {
|
||||
params: vec![],
|
||||
ret: Box::new(Type::unit()),
|
||||
effects: vec![],
|
||||
param_modes: vec![],
|
||||
ret_mode: ParamMode::Implicit,
|
||||
},
|
||||
params: vec![],
|
||||
body: Term::Lit { lit: Literal::Unit },
|
||||
suppress: vec![],
|
||||
doc: None,
|
||||
})],
|
||||
};
|
||||
let ir = emit_ir(&m).unwrap();
|
||||
assert!(
|
||||
ir.contains("declare i32 @ail_str_compare(ptr, ptr)"),
|
||||
"header missing @ail_str_compare declaration; ir was:\n{ir}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,3 +24,17 @@
|
||||
bool ail_str_eq(const char *a, const char *b) {
|
||||
return strcmp(a, b) == 0;
|
||||
}
|
||||
|
||||
/* Three-way comparison of NUL-terminated strings. Normalises libc
|
||||
* strcmp's signed-int output to exactly -1 / 0 / +1 so the codegen
|
||||
* caller can branch on `icmp slt i32 result, 0` and
|
||||
* `icmp eq i32 result, 0` against constants directly without
|
||||
* worrying about strcmp implementations that return values outside
|
||||
* {-1, 0, +1}. Heap-Str ABI milestone will replace the body with
|
||||
* length-prefixed comparison without changing the signature. */
|
||||
int ail_str_compare(const char *a, const char *b) {
|
||||
int r = strcmp(a, b);
|
||||
if (r < 0) return -1;
|
||||
if (r > 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user