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 ptr @GC_malloc(i64)
|
||||||
declare i32 @strcmp(ptr, ptr)
|
declare i32 @strcmp(ptr, ptr)
|
||||||
declare zeroext i1 @ail_str_eq(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)
|
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 }
|
@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 ptr @GC_malloc(i64)
|
||||||
declare i32 @strcmp(ptr, ptr)
|
declare i32 @strcmp(ptr, ptr)
|
||||||
declare zeroext i1 @ail_str_eq(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)
|
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 }
|
@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 ptr @GC_malloc(i64)
|
||||||
declare i32 @strcmp(ptr, ptr)
|
declare i32 @strcmp(ptr, ptr)
|
||||||
declare zeroext i1 @ail_str_eq(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)
|
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 }
|
@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 ptr @GC_malloc(i64)
|
||||||
declare i32 @strcmp(ptr, ptr)
|
declare i32 @strcmp(ptr, ptr)
|
||||||
declare zeroext i1 @ail_str_eq(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)
|
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 }
|
@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 ptr @GC_malloc(i64)
|
||||||
declare i32 @strcmp(ptr, ptr)
|
declare i32 @strcmp(ptr, ptr)
|
||||||
declare zeroext i1 @ail_str_eq(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)
|
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 }
|
@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
|
// by `ail build` (see `crates/ail/src/main.rs::build_to`). The
|
||||||
// `zeroext i1` return matches clang's lowering of C `_Bool`.
|
// `zeroext i1` return matches clang's lowering of C `_Bool`.
|
||||||
out.push_str("declare zeroext i1 @ail_str_eq(ptr, ptr)\n");
|
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
|
// Floats iter 4.4: saturating fp-to-int intrinsic for
|
||||||
// float_to_int_truncate. NaN → 0, +Inf → i64::MAX, -Inf →
|
// float_to_int_truncate. NaN → 0, +Inf → i64::MAX, -Inf →
|
||||||
// i64::MIN, finite-out-of-range saturates, finite-in-range
|
// 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}"
|
"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) {
|
bool ail_str_eq(const char *a, const char *b) {
|
||||||
return strcmp(a, b) == 0;
|
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