Finish first pass of Str.fromUtf8

This commit is contained in:
Jared Ramirez 2021-02-07 16:18:34 -08:00
parent 815f633a58
commit f3d9c2f2bb
12 changed files with 591 additions and 159 deletions

View file

@ -1,21 +0,0 @@
use inkwell::types::BasicTypeEnum;
use roc_module::low_level::LowLevel;
pub fn call_bitcode_fn<'a, 'ctx, 'env>(
op: LowLevel,
env: &Env<'a, 'ctx, 'env>,
args: &[BasicValueEnum<'ctx>],
fn_name: &str,
) -> BasicValueEnum<'ctx> {
let fn_val = env
.module
.get_function(fn_name)
.unwrap_or_else(|| panic!("Unrecognized builtin function: {:?} - if you're working on the Roc compiler, do you need to rebuild the bitcode? See compiler/builtins/bitcode/README.md", fn_name));
let call = env.builder.build_call(fn_val, args, "call_builtin");
call.set_call_convention(fn_val.get_call_conventions());
call.try_as_basic_value()
.left()
.unwrap_or_else(|| panic!("LLVM error: Invalid call for low-level op {:?}", op))
}

View file

@ -3516,7 +3516,9 @@ fn run_low_level<'a, 'ctx, 'env>(
// Str.fromInt : Int -> Str
debug_assert_eq!(args.len(), 1);
str_from_utf8(env, scope, args[0])
let original_wrapper = load_symbol(env, scope, &args[0]).into_struct_value();
str_from_utf8(env, parent, original_wrapper)
}
StrSplit => {
// Str.split : Str, Str -> List Str

View file

@ -1,11 +1,14 @@
use crate::llvm::build::{
call_bitcode_fn, call_void_bitcode_fn, complex_bitcast, Env, InPlace, Scope,
};
use crate::llvm::build_list::{allocate_list, store_list};
use crate::llvm::convert::collection;
use inkwell::types::BasicTypeEnum;
use inkwell::values::{BasicValueEnum, IntValue, StructValue};
use inkwell::AddressSpace;
use crate::llvm::build_list::{
allocate_list, build_basic_phi2, empty_polymorphic_list, list_len, load_list_ptr, store_list,
};
use crate::llvm::convert::{collection, get_ptr_type};
use inkwell::builder::Builder;
use inkwell::types::{BasicTypeEnum, StructType};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, StructValue};
use inkwell::{AddressSpace, IntPredicate};
use roc_builtins::bitcode;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout};
@ -255,19 +258,128 @@ pub fn str_from_int<'a, 'ctx, 'env>(
zig_str_to_struct(env, zig_result).into()
}
/// Str.fromUtf8 : List U8 -> Result Str [ BadUtf8 Utf8Problem ]*
/// 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>,
scope: &Scope<'a, 'ctx>,
bytes_symbol: Symbol,
parent: FunctionValue<'ctx>,
original_wrapper: StructValue<'ctx>,
) -> BasicValueEnum<'ctx> {
let bytes = load_symbol(env, scope, &bytes_symbol);
let builder = env.builder;
let ctx = env.context;
// TODO fromUtf8:
// let zig_result = call_bitcode_fn(env, &[int], &bitcode::STR_FROM_INT).into_struct_value();
// zig_str_to_struct(env, zig_result).into()
let list_len = list_len(builder, original_wrapper);
let ptr_type = get_ptr_type(&ctx.i8_type().into(), AddressSpace::Generic);
let list_ptr = load_list_ptr(builder, original_wrapper, ptr_type);
panic!("TODO fromUtf8")
let result_type = env
.module
.get_struct_type("str.ValidateUtf8BytesResult")
.unwrap();
let result_ptr = builder.build_alloca(result_type, "alloca_utf8_validate_bytes_result");
call_void_bitcode_fn(
env,
&[result_ptr.into(), list_ptr.into(), list_len.into()],
&bitcode::STR_VALIDATE_UTF_BYTES,
);
let utf8_validate_bytes_result = builder
.build_load(result_ptr, "load_utf8_validate_bytes_result")
.into_struct_value();
let is_ok = builder
.build_extract_value(utf8_validate_bytes_result, 0, "extract_extract_is_ok")
.unwrap()
.into_int_value();
let byte_index = builder
.build_extract_value(utf8_validate_bytes_result, 1, "extract_byte_index")
.unwrap()
.into_int_value();
let problem_code = builder
.build_extract_value(utf8_validate_bytes_result, 2, "extract_problem_code")
.unwrap()
.into_int_value();
let record_type = env.context.struct_type(
&[
env.ptr_int().into(),
collection(env.context, env.ptr_bytes).into(),
env.context.bool_type().into(),
ctx.i8_type().into(),
],
false,
);
let comparison = builder.build_int_compare(
IntPredicate::EQ,
is_ok,
ctx.bool_type().const_int(1, false),
"compare_is_ok",
);
build_basic_phi2(
env,
parent,
comparison,
|| {
// We have a valid utf8 byte sequence
// TODO: Should we do something different here if we're doing this in place?
let zig_str =
call_bitcode_fn(env, &[list_ptr.into(), list_len.into()], &bitcode::STR_INIT)
.into_struct_value();
build_struct(
builder,
record_type,
vec![
(
env.ptr_int().const_int(0 as u64, false).into(),
"insert_zeroed_byte_index",
),
(zig_str_to_struct(env, zig_str).into(), "insert_str"),
(
ctx.bool_type().const_int(1 as u64, false).into(),
"insert_is_ok",
),
(
ctx.i8_type().const_int(0 as u64, false).into(),
"insert_zeroed_problem",
),
],
)
.into()
},
|| {
// We do not have a valid utf8 byte sequence
build_struct(
builder,
record_type,
vec![
(byte_index.into(), "insert_byte_index"),
(empty_polymorphic_list(env).into(), "insert_zeroed_str"),
(
ctx.bool_type().const_int(0 as u64, false).into(),
"insert_is_ok",
),
(problem_code.into(), "insert_problem"),
],
)
.into()
},
BasicTypeEnum::StructType(record_type),
)
}
fn build_struct<'env, 'ctx>(
builder: &'env Builder<'ctx>,
struct_type: StructType<'ctx>,
values: Vec<(BasicValueEnum<'ctx>, &str)>,
) -> StructValue<'ctx> {
let mut val = struct_type.get_undef().into();
for (index, (value, name)) in values.iter().enumerate() {
val = builder
.build_insert_value(val, *value, index as u32, name)
.unwrap();
}
val.into_struct_value()
}
/// Str.equal : Str, Str -> Bool

View file

@ -47,6 +47,7 @@ pub fn build_eq<'a, 'ctx, 'env>(
(Builtin::Int16, Builtin::Int16) => int_cmp(IntPredicate::EQ, "eq_i16"),
(Builtin::Int8, Builtin::Int8) => int_cmp(IntPredicate::EQ, "eq_i8"),
(Builtin::Int1, Builtin::Int1) => int_cmp(IntPredicate::EQ, "eq_i1"),
(Builtin::Usize, Builtin::Usize) => int_cmp(IntPredicate::EQ, "eq_usize"),
(Builtin::Float64, Builtin::Float64) => float_cmp(FloatPredicate::OEQ, "eq_f64"),
(Builtin::Float32, Builtin::Float32) => float_cmp(FloatPredicate::OEQ, "eq_f32"),
(Builtin::Str, Builtin::Str) => str_equal(env, lhs_val, rhs_val),