mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
410 lines
12 KiB
Rust
410 lines
12 KiB
Rust
use crate::llvm::bitcode::{call_bitcode_fn, call_void_bitcode_fn};
|
|
use crate::llvm::build::{complex_bitcast, Env, Scope};
|
|
use crate::llvm::build_list::{allocate_list, call_bitcode_fn_returns_list, store_list};
|
|
use inkwell::builder::Builder;
|
|
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
|
use inkwell::AddressSpace;
|
|
use roc_builtins::bitcode;
|
|
use roc_module::symbol::Symbol;
|
|
use roc_mono::layout::{Builtin, Layout};
|
|
|
|
use super::build::load_symbol;
|
|
|
|
pub static CHAR_LAYOUT: Layout = Layout::Builtin(Builtin::Int8);
|
|
|
|
/// Str.repeat : Str, Nat -> Str
|
|
pub fn str_repeat<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
scope: &Scope<'a, 'ctx>,
|
|
str_symbol: Symbol,
|
|
count_symbol: Symbol,
|
|
) -> BasicValueEnum<'ctx> {
|
|
let str_c_abi = str_symbol_to_c_abi(env, scope, str_symbol);
|
|
let count = load_symbol(scope, &count_symbol);
|
|
call_bitcode_fn(env, &[str_c_abi.into(), count], bitcode::STR_REPEAT)
|
|
}
|
|
|
|
/// Str.split : Str, Str -> List Str
|
|
pub fn str_split<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
scope: &Scope<'a, 'ctx>,
|
|
str_symbol: Symbol,
|
|
delimiter_symbol: Symbol,
|
|
) -> BasicValueEnum<'ctx> {
|
|
let builder = env.builder;
|
|
|
|
let str_c_abi = str_symbol_to_c_abi(env, scope, str_symbol);
|
|
let delim_c_abi = str_symbol_to_c_abi(env, scope, delimiter_symbol);
|
|
|
|
let segment_count = call_bitcode_fn(
|
|
env,
|
|
&[str_c_abi.into(), delim_c_abi.into()],
|
|
bitcode::STR_COUNT_SEGMENTS,
|
|
)
|
|
.into_int_value();
|
|
|
|
// a pointer to the elements
|
|
let ret_list_ptr = allocate_list(env, &Layout::Builtin(Builtin::Str), segment_count);
|
|
|
|
// get the RocStr type defined by zig
|
|
let roc_str_type = env.module.get_struct_type("str.RocStr").unwrap();
|
|
|
|
// convert `*mut { *mut u8, i64 }` to `*mut RocStr`
|
|
let ret_list_ptr_zig_rocstr = builder.build_bitcast(
|
|
ret_list_ptr,
|
|
roc_str_type.ptr_type(AddressSpace::Generic),
|
|
"convert_to_zig_rocstr",
|
|
);
|
|
|
|
call_void_bitcode_fn(
|
|
env,
|
|
&[
|
|
ret_list_ptr_zig_rocstr,
|
|
str_c_abi.into(),
|
|
delim_c_abi.into(),
|
|
],
|
|
bitcode::STR_STR_SPLIT_IN_PLACE,
|
|
);
|
|
|
|
store_list(env, ret_list_ptr, segment_count)
|
|
}
|
|
|
|
fn str_symbol_to_c_abi<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
scope: &Scope<'a, 'ctx>,
|
|
symbol: Symbol,
|
|
) -> IntValue<'ctx> {
|
|
let string = load_symbol(scope, &symbol);
|
|
|
|
let target_type = match env.ptr_bytes {
|
|
8 => env.context.i128_type().into(),
|
|
4 => env.context.i64_type().into(),
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
complex_bitcast(env.builder, string, target_type, "str_to_c_abi").into_int_value()
|
|
}
|
|
|
|
pub fn str_to_c_abi<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
value: BasicValueEnum<'ctx>,
|
|
) -> IntValue<'ctx> {
|
|
let cell = env.builder.build_alloca(value.get_type(), "cell");
|
|
|
|
env.builder.build_store(cell, value);
|
|
|
|
let target_type = match env.ptr_bytes {
|
|
8 => env.context.i128_type(),
|
|
4 => env.context.i64_type(),
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
let target_type_ptr = env
|
|
.builder
|
|
.build_bitcast(cell, target_type.ptr_type(AddressSpace::Generic), "cast")
|
|
.into_pointer_value();
|
|
|
|
env.builder
|
|
.build_load(target_type_ptr, "load_as_c_abi")
|
|
.into_int_value()
|
|
}
|
|
|
|
pub fn destructure<'ctx>(
|
|
builder: &Builder<'ctx>,
|
|
wrapper_struct: StructValue<'ctx>,
|
|
) -> (PointerValue<'ctx>, IntValue<'ctx>) {
|
|
let length = builder
|
|
.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "list_len")
|
|
.unwrap()
|
|
.into_int_value();
|
|
|
|
// a `*mut u8` pointer
|
|
let generic_ptr = builder
|
|
.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "read_list_ptr")
|
|
.unwrap()
|
|
.into_pointer_value();
|
|
|
|
(generic_ptr, length)
|
|
}
|
|
|
|
/// Str.concat : Str, Str -> Str
|
|
pub fn str_concat<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
scope: &Scope<'a, 'ctx>,
|
|
str1_symbol: Symbol,
|
|
str2_symbol: Symbol,
|
|
) -> BasicValueEnum<'ctx> {
|
|
// swap the arguments; second argument comes before the second in the output string
|
|
let str1_c_abi = str_symbol_to_c_abi(env, scope, str1_symbol);
|
|
let str2_c_abi = str_symbol_to_c_abi(env, scope, str2_symbol);
|
|
|
|
call_bitcode_fn(
|
|
env,
|
|
&[str1_c_abi.into(), str2_c_abi.into()],
|
|
bitcode::STR_CONCAT,
|
|
)
|
|
}
|
|
|
|
/// Str.join : List Str, Str -> Str
|
|
pub fn str_join_with<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
scope: &Scope<'a, 'ctx>,
|
|
list_symbol: Symbol,
|
|
str_symbol: Symbol,
|
|
) -> BasicValueEnum<'ctx> {
|
|
// dirty hack; pretend a `list` is a `str` that works because
|
|
// they have the same stack layout `{ u8*, usize }`
|
|
let list_i128 = str_symbol_to_c_abi(env, scope, list_symbol);
|
|
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
|
|
|
call_bitcode_fn(
|
|
env,
|
|
&[list_i128.into(), str_i128.into()],
|
|
bitcode::STR_JOIN_WITH,
|
|
)
|
|
}
|
|
|
|
pub fn str_number_of_bytes<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
scope: &Scope<'a, 'ctx>,
|
|
str_symbol: Symbol,
|
|
) -> IntValue<'ctx> {
|
|
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
|
|
|
// the builtin will always return an u64
|
|
let length =
|
|
call_bitcode_fn(env, &[str_i128.into()], bitcode::STR_NUMBER_OF_BYTES).into_int_value();
|
|
|
|
// cast to the appropriate usize of the current build
|
|
env.builder
|
|
.build_int_cast(length, env.ptr_int(), "len_as_usize")
|
|
}
|
|
|
|
/// Str.startsWith : Str, Str -> Bool
|
|
pub fn str_starts_with<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
scope: &Scope<'a, 'ctx>,
|
|
str_symbol: Symbol,
|
|
prefix_symbol: Symbol,
|
|
) -> BasicValueEnum<'ctx> {
|
|
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
|
let prefix_i128 = str_symbol_to_c_abi(env, scope, prefix_symbol);
|
|
|
|
call_bitcode_fn(
|
|
env,
|
|
&[str_i128.into(), prefix_i128.into()],
|
|
bitcode::STR_STARTS_WITH,
|
|
)
|
|
}
|
|
|
|
/// Str.startsWithCodePt : Str, U32 -> Bool
|
|
pub fn str_starts_with_code_point<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
scope: &Scope<'a, 'ctx>,
|
|
str_symbol: Symbol,
|
|
prefix_symbol: Symbol,
|
|
) -> BasicValueEnum<'ctx> {
|
|
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
|
let prefix = load_symbol(scope, &prefix_symbol);
|
|
|
|
call_bitcode_fn(
|
|
env,
|
|
&[str_i128.into(), prefix],
|
|
bitcode::STR_STARTS_WITH_CODE_PT,
|
|
)
|
|
}
|
|
|
|
/// Str.endsWith : Str, Str -> Bool
|
|
pub fn str_ends_with<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
scope: &Scope<'a, 'ctx>,
|
|
str_symbol: Symbol,
|
|
prefix_symbol: Symbol,
|
|
) -> BasicValueEnum<'ctx> {
|
|
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
|
let prefix_i128 = str_symbol_to_c_abi(env, scope, prefix_symbol);
|
|
|
|
call_bitcode_fn(
|
|
env,
|
|
&[str_i128.into(), prefix_i128.into()],
|
|
bitcode::STR_ENDS_WITH,
|
|
)
|
|
}
|
|
|
|
/// Str.countGraphemes : Str -> Int
|
|
pub fn str_count_graphemes<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
scope: &Scope<'a, 'ctx>,
|
|
str_symbol: Symbol,
|
|
) -> BasicValueEnum<'ctx> {
|
|
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
|
|
|
call_bitcode_fn(
|
|
env,
|
|
&[str_i128.into()],
|
|
bitcode::STR_COUNT_GRAPEHEME_CLUSTERS,
|
|
)
|
|
}
|
|
|
|
/// Str.fromInt : Int -> Str
|
|
pub fn str_from_int<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
scope: &Scope<'a, 'ctx>,
|
|
int_symbol: Symbol,
|
|
) -> BasicValueEnum<'ctx> {
|
|
let int = load_symbol(scope, &int_symbol);
|
|
|
|
call_bitcode_fn(env, &[int], bitcode::STR_FROM_INT)
|
|
}
|
|
|
|
/// Str.toUtf8 : Str -> List U8
|
|
pub fn str_to_utf8<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
original_wrapper: StructValue<'ctx>,
|
|
) -> BasicValueEnum<'ctx> {
|
|
let string = complex_bitcast(
|
|
env.builder,
|
|
original_wrapper.into(),
|
|
env.str_list_c_abi().into(),
|
|
"to_utf8",
|
|
);
|
|
|
|
call_bitcode_fn_returns_list(env, &[string], bitcode::STR_TO_UTF8)
|
|
}
|
|
|
|
fn decode_from_utf8_result<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
pointer: PointerValue<'ctx>,
|
|
) -> StructValue<'ctx> {
|
|
let builder = env.builder;
|
|
let ctx = env.context;
|
|
|
|
let fields = match env.ptr_bytes {
|
|
8 | 4 => [
|
|
env.ptr_int().into(),
|
|
super::convert::zig_str_type(env).into(),
|
|
env.context.bool_type().into(),
|
|
ctx.i8_type().into(),
|
|
],
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
let record_type = env.context.struct_type(&fields, false);
|
|
|
|
match env.ptr_bytes {
|
|
8 | 4 => {
|
|
let result_ptr_cast = env
|
|
.builder
|
|
.build_bitcast(
|
|
pointer,
|
|
record_type.ptr_type(AddressSpace::Generic),
|
|
"to_unnamed",
|
|
)
|
|
.into_pointer_value();
|
|
|
|
builder
|
|
.build_load(result_ptr_cast, "load_utf8_validate_bytes_result")
|
|
.into_struct_value()
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
/// Str.fromUtf8 : List U8, { count : Nat, start : Nat } -> { a : Bool, b : Str, c : Nat, d : I8 }
|
|
pub fn str_from_utf8_range<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
_parent: FunctionValue<'ctx>,
|
|
list_wrapper: StructValue<'ctx>,
|
|
count_and_start: StructValue<'ctx>,
|
|
) -> BasicValueEnum<'ctx> {
|
|
let builder = env.builder;
|
|
|
|
let result_type = env.module.get_struct_type("str.FromUtf8Result").unwrap();
|
|
let result_ptr = builder.build_alloca(result_type, "alloca_utf8_validate_bytes_result");
|
|
|
|
call_void_bitcode_fn(
|
|
env,
|
|
&[
|
|
complex_bitcast(
|
|
env.builder,
|
|
list_wrapper.into(),
|
|
env.str_list_c_abi().into(),
|
|
"to_i128",
|
|
),
|
|
complex_bitcast(
|
|
env.builder,
|
|
count_and_start.into(),
|
|
env.str_list_c_abi().into(),
|
|
"to_i128",
|
|
),
|
|
result_ptr.into(),
|
|
],
|
|
bitcode::STR_FROM_UTF8_RANGE,
|
|
);
|
|
|
|
decode_from_utf8_result(env, result_ptr).into()
|
|
}
|
|
|
|
/// Str.fromUtf8 : List U8 -> { a : Bool, b : Str, c : Nat, d : I8 }
|
|
pub fn str_from_utf8<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
_parent: FunctionValue<'ctx>,
|
|
original_wrapper: StructValue<'ctx>,
|
|
) -> BasicValueEnum<'ctx> {
|
|
let builder = env.builder;
|
|
|
|
let result_type = env.module.get_struct_type("str.FromUtf8Result").unwrap();
|
|
let result_ptr = builder.build_alloca(result_type, "alloca_utf8_validate_bytes_result");
|
|
|
|
call_void_bitcode_fn(
|
|
env,
|
|
&[
|
|
complex_bitcast(
|
|
env.builder,
|
|
original_wrapper.into(),
|
|
env.str_list_c_abi().into(),
|
|
"to_i128",
|
|
),
|
|
result_ptr.into(),
|
|
],
|
|
bitcode::STR_FROM_UTF8,
|
|
);
|
|
|
|
decode_from_utf8_result(env, result_ptr).into()
|
|
}
|
|
|
|
/// Str.fromInt : Int -> Str
|
|
pub fn str_from_float<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
scope: &Scope<'a, 'ctx>,
|
|
int_symbol: Symbol,
|
|
) -> BasicValueEnum<'ctx> {
|
|
let float = load_symbol(scope, &int_symbol);
|
|
|
|
call_bitcode_fn(env, &[float], bitcode::STR_FROM_FLOAT)
|
|
}
|
|
|
|
/// Str.equal : Str, Str -> Bool
|
|
pub fn str_equal<'a, 'ctx, 'env>(
|
|
env: &Env<'a, 'ctx, 'env>,
|
|
value1: BasicValueEnum<'ctx>,
|
|
value2: BasicValueEnum<'ctx>,
|
|
) -> BasicValueEnum<'ctx> {
|
|
let str1_i128 = str_to_c_abi(env, value1);
|
|
let str2_i128 = str_to_c_abi(env, value2);
|
|
|
|
call_bitcode_fn(
|
|
env,
|
|
&[str1_i128.into(), str2_i128.into()],
|
|
bitcode::STR_EQUAL,
|
|
)
|
|
}
|
|
|
|
// TODO investigate: does this cause problems when the layout is known? this value is now not refcounted!
|
|
pub fn empty_str<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'ctx> {
|
|
let struct_type = super::convert::zig_str_type(env);
|
|
|
|
// The pointer should be null (aka zero) and the length should be zero,
|
|
// so the whole struct should be a const_zero
|
|
BasicValueEnum::StructValue(struct_type.const_zero())
|
|
}
|