mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
commit
ef4bb5e124
6 changed files with 272 additions and 24 deletions
|
@ -204,6 +204,8 @@ macro_rules! int_intrinsic {
|
||||||
($name:literal) => {{
|
($name:literal) => {{
|
||||||
let mut output = IntrinsicName::default();
|
let mut output = IntrinsicName::default();
|
||||||
|
|
||||||
|
// These are LLVM types which don't include
|
||||||
|
// u64 for example
|
||||||
output.options[4] = concat!($name, ".i8");
|
output.options[4] = concat!($name, ".i8");
|
||||||
output.options[5] = concat!($name, ".i16");
|
output.options[5] = concat!($name, ".i16");
|
||||||
output.options[6] = concat!($name, ".i32");
|
output.options[6] = concat!($name, ".i32");
|
||||||
|
|
|
@ -5321,24 +5321,23 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let (string, _string_layout) = load_symbol_and_layout(scope, &args[0]);
|
let (string, _string_layout) = load_symbol_and_layout(scope, &args[0]);
|
||||||
|
|
||||||
if let Layout::Struct(struct_layout) = layout {
|
let number_layout = match layout {
|
||||||
// match on the return layout to figure out which zig builtin we need
|
Layout::Struct(fields) => fields[0], // TODO: why is it sometimes a struct?
|
||||||
let intrinsic = match struct_layout[0] {
|
_ => unreachable!(),
|
||||||
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,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let string =
|
// match on the return layout to figure out which zig builtin we need
|
||||||
complex_bitcast(env.builder, string, env.str_list_c_abi().into(), "to_utf8");
|
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,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
call_bitcode_fn(env, &[string], intrinsic)
|
let string =
|
||||||
} else {
|
complex_bitcast(env.builder, string, env.str_list_c_abi().into(), "to_utf8");
|
||||||
unreachable!()
|
|
||||||
}
|
call_bitcode_fn(env, &[string], intrinsic)
|
||||||
}
|
}
|
||||||
StrFromInt => {
|
StrFromInt => {
|
||||||
// Str.fromInt : Int -> Str
|
// Str.fromInt : Int -> Str
|
||||||
|
|
|
@ -599,6 +599,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
arguments,
|
arguments,
|
||||||
*sym,
|
*sym,
|
||||||
wasm_layout,
|
wasm_layout,
|
||||||
|
layout,
|
||||||
storage,
|
storage,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -636,7 +637,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
CallType::LowLevel { op: lowlevel, .. } => {
|
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),
|
x => todo!("call type {:?}", x),
|
||||||
|
@ -1066,14 +1067,20 @@ impl<'a> WasmBackend<'a> {
|
||||||
arguments: &'a [Symbol],
|
arguments: &'a [Symbol],
|
||||||
return_sym: Symbol,
|
return_sym: Symbol,
|
||||||
return_layout: WasmLayout,
|
return_layout: WasmLayout,
|
||||||
|
mono_layout: &Layout<'a>,
|
||||||
storage: &StoredValue,
|
storage: &StoredValue,
|
||||||
) {
|
) {
|
||||||
use LowLevel::*;
|
use LowLevel::*;
|
||||||
|
|
||||||
match lowlevel {
|
match lowlevel {
|
||||||
Eq | NotEq => {
|
Eq | NotEq => self.build_eq_or_neq(
|
||||||
self.build_eq_or_neq(lowlevel, arguments, return_sym, return_layout, storage)
|
lowlevel,
|
||||||
}
|
arguments,
|
||||||
|
return_sym,
|
||||||
|
return_layout,
|
||||||
|
mono_layout,
|
||||||
|
storage,
|
||||||
|
),
|
||||||
PtrCast => {
|
PtrCast => {
|
||||||
// Don't want Zig calling convention when casting pointers.
|
// Don't want Zig calling convention when casting pointers.
|
||||||
self.storage.load_symbols(&mut self.code_builder, arguments);
|
self.storage.load_symbols(&mut self.code_builder, arguments);
|
||||||
|
@ -1099,6 +1106,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
lowlevel,
|
lowlevel,
|
||||||
arguments,
|
arguments,
|
||||||
&return_layout,
|
&return_layout,
|
||||||
|
mono_layout,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle the result
|
// Handle the result
|
||||||
|
@ -1122,6 +1130,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
arguments: &'a [Symbol],
|
arguments: &'a [Symbol],
|
||||||
return_sym: Symbol,
|
return_sym: Symbol,
|
||||||
return_layout: WasmLayout,
|
return_layout: WasmLayout,
|
||||||
|
mono_layout: &Layout<'a>,
|
||||||
storage: &StoredValue,
|
storage: &StoredValue,
|
||||||
) {
|
) {
|
||||||
let arg_layout = self.storage.symbol_layouts[&arguments[0]];
|
let arg_layout = self.storage.symbol_layouts[&arguments[0]];
|
||||||
|
@ -1134,7 +1143,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
match arg_layout {
|
match arg_layout {
|
||||||
Layout::Builtin(
|
Layout::Builtin(
|
||||||
Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal,
|
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) => {
|
Layout::Builtin(Builtin::Str) => {
|
||||||
let (param_types, ret_type) = self.storage.load_symbols_for_call(
|
let (param_types, ret_type) = self.storage.load_symbols_for_call(
|
||||||
|
@ -1189,6 +1198,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
lowlevel: LowLevel,
|
lowlevel: LowLevel,
|
||||||
arguments: &'a [Symbol],
|
arguments: &'a [Symbol],
|
||||||
return_layout: WasmLayout,
|
return_layout: WasmLayout,
|
||||||
|
mono_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
use StoredValue::*;
|
use StoredValue::*;
|
||||||
match self.storage.get(&arguments[0]).to_owned() {
|
match self.storage.get(&arguments[0]).to_owned() {
|
||||||
|
@ -1220,7 +1230,13 @@ impl<'a> WasmBackend<'a> {
|
||||||
..
|
..
|
||||||
} = self.storage.get(&arguments[1]).to_owned()
|
} = 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) {
|
if matches!(lowlevel, LowLevel::NotEq) {
|
||||||
self.code_builder.i32_eqz();
|
self.code_builder.i32_eqz();
|
||||||
}
|
}
|
||||||
|
@ -1235,6 +1251,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
locations: [StackMemoryLocation; 2],
|
locations: [StackMemoryLocation; 2],
|
||||||
arguments: &'a [Symbol],
|
arguments: &'a [Symbol],
|
||||||
return_layout: WasmLayout,
|
return_layout: WasmLayout,
|
||||||
|
mono_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
match format {
|
match format {
|
||||||
StackMemoryFormat::Decimal => {
|
StackMemoryFormat::Decimal => {
|
||||||
|
@ -1247,6 +1264,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
LowLevel::NumIsFinite,
|
LowLevel::NumIsFinite,
|
||||||
&first,
|
&first,
|
||||||
&return_layout,
|
&return_layout,
|
||||||
|
mono_layout,
|
||||||
);
|
);
|
||||||
dispatch_low_level(
|
dispatch_low_level(
|
||||||
&mut self.code_builder,
|
&mut self.code_builder,
|
||||||
|
@ -1254,6 +1272,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
LowLevel::NumIsFinite,
|
LowLevel::NumIsFinite,
|
||||||
&second,
|
&second,
|
||||||
&return_layout,
|
&return_layout,
|
||||||
|
mono_layout,
|
||||||
);
|
);
|
||||||
self.code_builder.i32_and();
|
self.code_builder.i32_and();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use roc_builtins::bitcode::{self, FloatWidth};
|
use roc_builtins::bitcode::{self, FloatWidth};
|
||||||
use roc_module::low_level::{LowLevel, LowLevel::*};
|
use roc_module::low_level::{LowLevel, LowLevel::*};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
use roc_reporting::internal_error;
|
use roc_reporting::internal_error;
|
||||||
|
|
||||||
use crate::layout::{StackMemoryFormat::*, WasmLayout};
|
use crate::layout::{StackMemoryFormat::*, WasmLayout};
|
||||||
|
@ -20,6 +21,7 @@ pub fn dispatch_low_level<'a>(
|
||||||
lowlevel: LowLevel,
|
lowlevel: LowLevel,
|
||||||
args: &[Symbol],
|
args: &[Symbol],
|
||||||
ret_layout: &WasmLayout,
|
ret_layout: &WasmLayout,
|
||||||
|
mono_layout: &Layout<'a>,
|
||||||
) -> LowlevelBuildResult {
|
) -> LowlevelBuildResult {
|
||||||
use LowlevelBuildResult::*;
|
use LowlevelBuildResult::*;
|
||||||
|
|
||||||
|
@ -46,7 +48,21 @@ pub fn dispatch_low_level<'a>(
|
||||||
return NotImplemented;
|
return NotImplemented;
|
||||||
}
|
}
|
||||||
StrCountGraphemes => return BuiltinCall(bitcode::STR_COUNT_GRAPEHEME_CLUSTERS),
|
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 => {
|
StrFromInt => {
|
||||||
// This does not get exposed in user space. We switched to NumToStr instead.
|
// 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.
|
// We can probably just leave this as NotImplemented. We may want remove this LowLevel.
|
||||||
|
|
|
@ -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!(i64, i64_store, Align::Bytes8);
|
||||||
wasm_test_result_primitive!(usize, i32_store, Align::Bytes4);
|
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_primitive!(f64, f64_store, Align::Bytes8);
|
||||||
|
|
||||||
wasm_test_result_stack_memory!(u128);
|
wasm_test_result_stack_memory!(u128);
|
||||||
|
|
|
@ -1121,3 +1121,215 @@ fn str_trim_right_small_to_small_shared() {
|
||||||
(RocStr, RocStr)
|
(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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue