Implement string equality operator using IRRT and optimise LLVM implementation #561

Merged
sb10q merged 9 commits from ramtej/nac3:feature/string-equality into master 2024-12-30 13:02:09 +08:00
2 changed files with 19 additions and 22 deletions
Showing only changes of commit 0b6a9bd89b - Show all commits

View File

@ -8,7 +8,7 @@ SizeT __nac3_str_eq_impl(const char* str1, SizeT len1, const char* str2, SizeT l
if (len1 != len2){
return 0;
}
return (__builtin_strncmp(str1, str2, static_cast<SizeT>(len1)) == 0) ? 1 : 0;
return (__builtin_memcmp(str1, str2, static_cast<SizeT>(len1)) == 0) ? 1 : 0;
Outdated
Review

Is IRRT really required at this point?

Is IRRT really required at this point?

Yes, __builtin_strncmp isn't exposed in LLVM IR so we will need clang to emit the equivalent bitcode for us.

Yes, `__builtin_strncmp` isn't exposed in LLVM IR so we will need `clang` to emit the equivalent bitcode for us.
Outdated
Review

Isn't clang also just a LLVM user?
How does it do it?

Isn't clang also just a LLVM user? How does it do it?
Outdated
Review

Also, NAC3 is just using regular Pascal-style strings, isn't it?
If so, then the correct function to use is memcmp. strncmp is intrinsically slower as it adds a check for NUL characters which is unnecessary and may even lead to incorrect behavior.

Also, NAC3 is just using regular Pascal-style strings, isn't it? If so, then the correct function to use is ``memcmp``. ``strncmp`` is intrinsically slower as it adds a check for NUL characters which is unnecessary and may even lead to incorrect behavior.
}
ramtej marked this conversation as resolved Outdated
Outdated
Review

Isn't that just memcmp but implemented inefficiently?

Isn't that just memcmp but implemented inefficiently?

LLVM has an intrinsic __builtin_memcmp for this exact purpose: https://clang.llvm.org/docs/LanguageExtensions.html#string-builtins

LLVM has an intrinsic `__builtin_memcmp` for this exact purpose: https://clang.llvm.org/docs/LanguageExtensions.html#string-builtins

In fact, LLVM has __builtin_strncmp. If that's the case, the entire IRRT can just be implemented in terms of that intrinsic function.

In fact, LLVM has `__builtin_strncmp`. If that's the case, the entire IRRT can just be implemented in terms of that intrinsic function.
} // namespace

View File

@ -1,7 +1,4 @@
use inkwell::{
values::{BasicValueEnum, CallSiteValue, IntValue, PointerValue},
AddressSpace,
};
use inkwell::values::{BasicValueEnum, CallSiteValue, IntValue, PointerValue};
use itertools::Either;
use crate::codegen::{CodeGenContext, CodeGenerator};
@ -15,27 +12,27 @@ pub fn call_string_eq<'ctx, G: CodeGenerator + ?Sized>(
str2_ptr: PointerValue<'ctx>,
str2_len: IntValue<'ctx>,
) -> IntValue<'ctx> {
let string_eq_fn = ctx.module.get_function("nac3_str_eq").unwrap_or_else(|| {
let i8_ptr_type = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
let i64_type = ctx.ctx.i64_type();
let i32_type = ctx.ctx.i32_type();
let fn_type = i32_type.fn_type(
&[i8_ptr_type.into(), i64_type.into(), i8_ptr_type.into(), i64_type.into()],
let func = ctx.module.get_function("nac3_str_eq").unwrap_or_else(|| {
ctx.module.add_function(
"nac3_str_eq",
ramtej marked this conversation as resolved Outdated
Outdated
Review

And when is nac3_str_eq64 called?

And when is ``nac3_str_eq64`` called?
ctx.ctx.i32_type().fn_type(
&[
str1_ptr.get_type().into(),
str1_len.get_type().into(),
str2_ptr.get_type().into(),
str2_len.get_type().into(),
],
false,
);
ctx.module.add_function("nac3_str_eq", fn_type, None)
),
None,
)
});
let result = ctx
.builder
.build_call(
string_eq_fn,
&[
str1_ptr.into(),
ctx.builder.build_int_z_extend(str1_len, ctx.ctx.i64_type(), "").unwrap().into(),
str2_ptr.into(),
ctx.builder.build_int_z_extend(str2_len, ctx.ctx.i64_type(), "").unwrap().into(),
],
"string_eq",
func,
&[str1_ptr.into(), str1_len.into(), str2_ptr.into(), str2_len.into()],
"str_eq_call",
)
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))