Merge pull request #2287 from rtfeldman/str_to_num

WASM StrToNum
This commit is contained in:
Lucas 2022-01-03 19:28:18 -05:00 committed by GitHub
commit ef4bb5e124
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 272 additions and 24 deletions

View file

@ -204,6 +204,8 @@ macro_rules! int_intrinsic {
($name:literal) => {{
let mut output = IntrinsicName::default();
// These are LLVM types which don't include
// u64 for example
output.options[4] = concat!($name, ".i8");
output.options[5] = concat!($name, ".i16");
output.options[6] = concat!($name, ".i32");

View file

@ -5321,13 +5321,15 @@ fn run_low_level<'a, 'ctx, 'env>(
let (string, _string_layout) = load_symbol_and_layout(scope, &args[0]);
if let Layout::Struct(struct_layout) = layout {
let number_layout = match layout {
Layout::Struct(fields) => fields[0], // TODO: why is it sometimes a struct?
_ => unreachable!(),
};
// match on the return layout to figure out which zig builtin we need
let intrinsic = match struct_layout[0] {
let intrinsic = match number_layout {
Layout::Builtin(Builtin::Int(int_width)) => &bitcode::STR_TO_INT[int_width],
Layout::Builtin(Builtin::Float(float_width)) => {
&bitcode::STR_TO_FLOAT[float_width]
}
Layout::Builtin(Builtin::Float(float_width)) => &bitcode::STR_TO_FLOAT[float_width],
Layout::Builtin(Builtin::Decimal) => bitcode::DEC_FROM_STR,
_ => unreachable!(),
};
@ -5336,9 +5338,6 @@ fn run_low_level<'a, 'ctx, 'env>(
complex_bitcast(env.builder, string, env.str_list_c_abi().into(), "to_utf8");
call_bitcode_fn(env, &[string], intrinsic)
} else {
unreachable!()
}
}
StrFromInt => {
// Str.fromInt : Int -> Str

View file

@ -599,6 +599,7 @@ impl<'a> WasmBackend<'a> {
arguments,
*sym,
wasm_layout,
layout,
storage,
);
}
@ -636,7 +637,7 @@ impl<'a> WasmBackend<'a> {
}
CallType::LowLevel { op: lowlevel, .. } => {
self.build_low_level(*lowlevel, arguments, *sym, wasm_layout, storage)
self.build_low_level(*lowlevel, arguments, *sym, wasm_layout, layout, storage)
}
x => todo!("call type {:?}", x),
@ -1066,14 +1067,20 @@ impl<'a> WasmBackend<'a> {
arguments: &'a [Symbol],
return_sym: Symbol,
return_layout: WasmLayout,
mono_layout: &Layout<'a>,
storage: &StoredValue,
) {
use LowLevel::*;
match lowlevel {
Eq | NotEq => {
self.build_eq_or_neq(lowlevel, arguments, return_sym, return_layout, storage)
}
Eq | NotEq => self.build_eq_or_neq(
lowlevel,
arguments,
return_sym,
return_layout,
mono_layout,
storage,
),
PtrCast => {
// Don't want Zig calling convention when casting pointers.
self.storage.load_symbols(&mut self.code_builder, arguments);
@ -1099,6 +1106,7 @@ impl<'a> WasmBackend<'a> {
lowlevel,
arguments,
&return_layout,
mono_layout,
);
// Handle the result
@ -1122,6 +1130,7 @@ impl<'a> WasmBackend<'a> {
arguments: &'a [Symbol],
return_sym: Symbol,
return_layout: WasmLayout,
mono_layout: &Layout<'a>,
storage: &StoredValue,
) {
let arg_layout = self.storage.symbol_layouts[&arguments[0]];
@ -1134,7 +1143,7 @@ impl<'a> WasmBackend<'a> {
match arg_layout {
Layout::Builtin(
Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal,
) => self.build_eq_or_neq_number(lowlevel, arguments, return_layout),
) => self.build_eq_or_neq_number(lowlevel, arguments, return_layout, mono_layout),
Layout::Builtin(Builtin::Str) => {
let (param_types, ret_type) = self.storage.load_symbols_for_call(
@ -1189,6 +1198,7 @@ impl<'a> WasmBackend<'a> {
lowlevel: LowLevel,
arguments: &'a [Symbol],
return_layout: WasmLayout,
mono_layout: &Layout<'a>,
) {
use StoredValue::*;
match self.storage.get(&arguments[0]).to_owned() {
@ -1220,7 +1230,13 @@ impl<'a> WasmBackend<'a> {
..
} = self.storage.get(&arguments[1]).to_owned()
{
self.build_eq_num128(format, [location0, location1], arguments, return_layout);
self.build_eq_num128(
format,
[location0, location1],
arguments,
return_layout,
mono_layout,
);
if matches!(lowlevel, LowLevel::NotEq) {
self.code_builder.i32_eqz();
}
@ -1235,6 +1251,7 @@ impl<'a> WasmBackend<'a> {
locations: [StackMemoryLocation; 2],
arguments: &'a [Symbol],
return_layout: WasmLayout,
mono_layout: &Layout<'a>,
) {
match format {
StackMemoryFormat::Decimal => {
@ -1247,6 +1264,7 @@ impl<'a> WasmBackend<'a> {
LowLevel::NumIsFinite,
&first,
&return_layout,
mono_layout,
);
dispatch_low_level(
&mut self.code_builder,
@ -1254,6 +1272,7 @@ impl<'a> WasmBackend<'a> {
LowLevel::NumIsFinite,
&second,
&return_layout,
mono_layout,
);
self.code_builder.i32_and();

View file

@ -1,6 +1,7 @@
use roc_builtins::bitcode::{self, FloatWidth};
use roc_module::low_level::{LowLevel, LowLevel::*};
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout};
use roc_reporting::internal_error;
use crate::layout::{StackMemoryFormat::*, WasmLayout};
@ -20,6 +21,7 @@ pub fn dispatch_low_level<'a>(
lowlevel: LowLevel,
args: &[Symbol],
ret_layout: &WasmLayout,
mono_layout: &Layout<'a>,
) -> LowlevelBuildResult {
use LowlevelBuildResult::*;
@ -46,7 +48,21 @@ pub fn dispatch_low_level<'a>(
return NotImplemented;
}
StrCountGraphemes => return BuiltinCall(bitcode::STR_COUNT_GRAPEHEME_CLUSTERS),
StrToNum => return NotImplemented, // choose builtin based on storage size
StrToNum => {
let number_layout = match mono_layout {
Layout::Struct(fields) => fields[0],
_ => internal_error!("Unexpected mono layout {:?} for StrToNum", mono_layout),
};
// match on the return layout to figure out which zig builtin we need
let intrinsic = match number_layout {
Layout::Builtin(Builtin::Int(int_width)) => &bitcode::STR_TO_INT[int_width],
Layout::Builtin(Builtin::Float(float_width)) => &bitcode::STR_TO_FLOAT[float_width],
Layout::Builtin(Builtin::Decimal) => bitcode::DEC_FROM_STR,
rest => internal_error!("Unexpected builtin {:?} for StrToNum", rest),
};
return BuiltinCall(intrinsic);
}
StrFromInt => {
// This does not get exposed in user space. We switched to NumToStr instead.
// We can probably just leave this as NotImplemented. We may want remove this LowLevel.

View file

@ -114,7 +114,7 @@ wasm_test_result_primitive!(u64, i64_store, Align::Bytes8);
wasm_test_result_primitive!(i64, i64_store, Align::Bytes8);
wasm_test_result_primitive!(usize, i32_store, Align::Bytes4);
wasm_test_result_primitive!(f32, f32_store, Align::Bytes8);
wasm_test_result_primitive!(f32, f32_store, Align::Bytes4);
wasm_test_result_primitive!(f64, f64_store, Align::Bytes8);
wasm_test_result_stack_memory!(u128);

View file

@ -1121,3 +1121,215 @@ fn str_trim_right_small_to_small_shared() {
(RocStr, RocStr)
);
}
#[test]
fn str_to_nat() {
assert_evals_to!(
indoc!(
r#"
when Str.toNat "1" is
Ok n -> n
Err _ -> 0
"#
),
1,
usize
);
}
#[test]
fn str_to_i128() {
assert_evals_to!(
indoc!(
r#"
when Str.toI128 "1" is
Ok n -> n
Err _ -> 0
"#
),
1,
i128
);
}
#[test]
fn str_to_u128() {
assert_evals_to!(
indoc!(
r#"
when Str.toU128 "1" is
Ok n -> n
Err _ -> 0
"#
),
1,
u128
);
}
#[test]
fn str_to_i64() {
assert_evals_to!(
indoc!(
r#"
when Str.toI64 "1" is
Ok n -> n
Err _ -> 0
"#
),
1,
i64
);
}
#[test]
fn str_to_u64() {
assert_evals_to!(
indoc!(
r#"
when Str.toU64 "1" is
Ok n -> n
Err _ -> 0
"#
),
1,
u64
);
}
#[test]
fn str_to_i32() {
assert_evals_to!(
indoc!(
r#"
when Str.toI32 "1" is
Ok n -> n
Err _ -> 0
"#
),
1,
i32
);
}
#[test]
fn str_to_u32() {
assert_evals_to!(
indoc!(
r#"
when Str.toU32 "1" is
Ok n -> n
Err _ -> 0
"#
),
1,
u32
);
}
#[test]
fn str_to_i16() {
assert_evals_to!(
indoc!(
r#"
when Str.toI16 "1" is
Ok n -> n
Err _ -> 0
"#
),
1,
i16
);
}
#[test]
fn str_to_u16() {
assert_evals_to!(
indoc!(
r#"
when Str.toU16 "1" is
Ok n -> n
Err _ -> 0
"#
),
1,
u16
);
}
#[test]
fn str_to_i8() {
assert_evals_to!(
indoc!(
r#"
when Str.toI8 "1" is
Ok n -> n
Err _ -> 0
"#
),
1,
i8
);
}
#[test]
fn str_to_u8() {
assert_evals_to!(
indoc!(
r#"
when Str.toU8 "1" is
Ok n -> n
Err _ -> 0
"#
),
1,
u8
);
}
#[test]
fn str_to_f64() {
assert_evals_to!(
indoc!(
r#"
when Str.toF64 "1.0" is
Ok n -> n
Err _ -> 0
"#
),
1.0,
f64
);
}
#[test]
fn str_to_f32() {
assert_evals_to!(
indoc!(
r#"
when Str.toF32 "1.0" is
Ok n -> n
Err _ -> 0
"#
),
1.0,
f32
);
}
#[test]
fn str_to_dec() {
use roc_std::RocDec;
assert_evals_to!(
indoc!(
r#"
when Str.toDec "1.0" is
Ok n -> n
Err _ -> 0
"#
),
RocDec::from_str("1.0").unwrap(),
RocDec
);
}