From c08bf1204f49207454edb82f0328d42d64a6bc05 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 26 Sep 2023 12:21:43 +0100 Subject: [PATCH] Add `cstring` specific comparison procedures to fix comparisons like `cstring("") != cstring(nil)` --- core/runtime/internal.odin | 42 ++++++++++++++++++++++++++++++++++++++ src/check_expr.cpp | 16 ++++++++++++--- src/llvm_backend_expr.cpp | 23 ++++++++++++++++++++- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 4d166bef0..5e4805153 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -410,6 +410,48 @@ cstring_to_string :: proc "contextless" (s: cstring) -> string { } +cstring_eq :: proc "contextless" (lhs, rhs: cstring) -> bool { + x := ([^]byte)(lhs) + y := ([^]byte)(rhs) + if x == y { + return true + } + if (x == nil) ~ (y == nil) { + return false + } + xn := cstring_len(lhs) + yn := cstring_len(rhs) + if xn != yn { + return false + } + return #force_inline memory_equal(x, y, xn) +} + +cstring_cmp :: proc "contextless" (lhs, rhs: cstring) -> int { + x := ([^]byte)(lhs) + y := ([^]byte)(rhs) + if x == y { + return 0 + } + if (x == nil) ~ (y == nil) { + return -1 if x == nil else +1 + } + xn := cstring_len(lhs) + yn := cstring_len(rhs) + ret := memory_compare(x, y, min(xn, yn)) + if ret == 0 && xn != yn { + return -1 if xn < yn else +1 + } + return ret +} + +cstring_ne :: #force_inline proc "contextless" (a, b: cstring) -> bool { return !cstring_eq(a, b) } +cstring_lt :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) < 0 } +cstring_gt :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) > 0 } +cstring_le :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) <= 0 } +cstring_ge :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) >= 0 } + + complex32_eq :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) == real(b) && imag(a) == imag(b) } complex32_ne :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) != real(b) || imag(a) != imag(b) } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index abcb7fd72..5fab87a7d 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2462,8 +2462,9 @@ gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t add_package_dependency(c, "runtime", "quaternion256_ne"); break; case Basic_cstring: - add_package_dependency(c, "runtime", "cstring_to_string"); - /*fallthrough*/ + add_package_dependency(c, "runtime", "cstring_eq"); + add_package_dependency(c, "runtime", "cstring_ne"); + break; case Basic_string: add_package_dependency(c, "runtime", "string_eq"); add_package_dependency(c, "runtime", "string_ne"); @@ -2621,7 +2622,16 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper if (!is_type_untyped(x->type)) size = gb_max(size, type_size_of(x->type)); if (!is_type_untyped(y->type)) size = gb_max(size, type_size_of(y->type)); - if (is_type_string(x->type) || is_type_string(y->type)) { + if (is_type_cstring(x->type) && is_type_cstring(y->type)) { + switch (op) { + case Token_CmpEq: add_package_dependency(c, "runtime", "cstring_eq"); break; + case Token_NotEq: add_package_dependency(c, "runtime", "cstring_ne"); break; + case Token_Lt: add_package_dependency(c, "runtime", "cstring_lt"); break; + case Token_Gt: add_package_dependency(c, "runtime", "cstring_gt"); break; + case Token_LtEq: add_package_dependency(c, "runtime", "cstring_le"); break; + case Token_GtEq: add_package_dependency(c, "runtime", "cstring_gt"); break; + } + } else if (is_type_string(x->type) || is_type_string(y->type)) { switch (op) { case Token_CmpEq: add_package_dependency(c, "runtime", "string_eq"); break; case Token_NotEq: add_package_dependency(c, "runtime", "string_ne"); break; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 33768cc12..e0e36619b 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2414,7 +2414,28 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left } if (is_type_string(a)) { - if (is_type_cstring(a)) { + if (is_type_cstring(a) && is_type_cstring(b)) { + left = lb_emit_conv(p, left, t_cstring); + right = lb_emit_conv(p, right, t_cstring); + char const *runtime_procedure = nullptr; + switch (op_kind) { + case Token_CmpEq: runtime_procedure = "cstring_eq"; break; + case Token_NotEq: runtime_procedure = "cstring_ne"; break; + case Token_Lt: runtime_procedure = "cstring_lt"; break; + case Token_Gt: runtime_procedure = "cstring_gt"; break; + case Token_LtEq: runtime_procedure = "cstring_le"; break; + case Token_GtEq: runtime_procedure = "cstring_gt"; break; + } + GB_ASSERT(runtime_procedure != nullptr); + + auto args = array_make(permanent_allocator(), 2); + args[0] = left; + args[1] = right; + return lb_emit_runtime_call(p, runtime_procedure, args); + } + + + if (is_type_cstring(a) ^ is_type_cstring(b)) { left = lb_emit_conv(p, left, t_string); right = lb_emit_conv(p, right, t_string); }