mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
Create new operator Num.mulSaturated
This commit is contained in:
parent
e356f1c33f
commit
74f946b1aa
11 changed files with 191 additions and 68 deletions
|
@ -82,6 +82,7 @@ interface Num
|
|||
subChecked,
|
||||
subSaturated,
|
||||
mulWrap,
|
||||
mulSaturated,
|
||||
mulChecked,
|
||||
intCast,
|
||||
bytesToU16,
|
||||
|
@ -862,7 +863,14 @@ subSaturated : Num a, Num a -> Num a
|
|||
subChecked : Num a, Num a -> Result (Num a) [Overflow]*
|
||||
|
||||
mulWrap : Int range, Int range -> Int range
|
||||
# mulSaturated : Num a, Num a -> Num a
|
||||
|
||||
## Multiply two numbers, clamping on the maximum representable number rather than
|
||||
## overflowing.
|
||||
##
|
||||
## This is the same as [Num.mul] except for the saturating behavior if the
|
||||
## addition is to overflow.
|
||||
mulSaturated : Num a, Num a -> Num a
|
||||
|
||||
## Multiply two numbers and check for overflow.
|
||||
##
|
||||
## This is the same as [Num.mul] except if the operation overflows, instead of
|
||||
|
|
|
@ -199,6 +199,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
Box::new(result_type(num_type(flex(TVAR1)), overflow())),
|
||||
);
|
||||
|
||||
// mulSaturated : Int range, Int range -> Int range
|
||||
add_top_level_function_type!(
|
||||
Symbol::NUM_MUL_SATURATED,
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
Box::new(int_type(flex(TVAR1))),
|
||||
);
|
||||
|
||||
// abs : Num a -> Num a
|
||||
add_top_level_function_type!(
|
||||
Symbol::NUM_ABS,
|
||||
|
|
|
@ -187,6 +187,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
|||
NUM_SUB_SATURATED => num_sub_saturated,
|
||||
NUM_MUL => num_mul,
|
||||
NUM_MUL_WRAP => num_mul_wrap,
|
||||
NUM_MUL_SATURATED => num_mul_saturated,
|
||||
NUM_MUL_CHECKED => num_mul_checked,
|
||||
NUM_GT => num_gt,
|
||||
NUM_GTE => num_gte,
|
||||
|
@ -909,6 +910,11 @@ fn num_mul_wrap(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
num_binop(symbol, var_store, LowLevel::NumMulWrap)
|
||||
}
|
||||
|
||||
/// Num.mulSaturated : Num a, Num a -> Num a
|
||||
fn num_mul_saturated(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_binop(symbol, var_store, LowLevel::NumMulSaturated)
|
||||
}
|
||||
|
||||
/// Num.mulChecked : Num a, Num a -> Result (Num a) [Overflow]*
|
||||
fn num_mul_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_overflow_checked(symbol, var_store, LowLevel::NumMulChecked)
|
||||
|
|
|
@ -5952,7 +5952,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked
|
||||
| NumIsMultipleOf | NumAddWrap | NumAddChecked | NumAddSaturated | NumDivUnchecked
|
||||
| NumDivCeilUnchecked | NumPow | NumPowInt | NumSubWrap | NumSubChecked
|
||||
| NumSubSaturated | NumMulWrap | NumMulChecked => {
|
||||
| NumSubSaturated | NumMulWrap | NumMulSaturated | NumMulChecked => {
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
|
||||
let (lhs_arg, lhs_layout) = load_symbol_and_layout(scope, &args[0]);
|
||||
|
@ -6719,6 +6719,11 @@ fn build_int_binop<'a, 'ctx, 'env>(
|
|||
throw_on_overflow(env, parent, result, "integer multiplication overflowed!")
|
||||
}
|
||||
NumMulWrap => bd.build_int_mul(lhs, rhs, "mul_int").into(),
|
||||
NumMulSaturated => call_bitcode_fn(
|
||||
env,
|
||||
&[lhs.into(), rhs.into()],
|
||||
&bitcode::NUM_MUL_SATURATED_INT[int_width],
|
||||
),
|
||||
NumMulChecked => env.call_intrinsic(
|
||||
&LLVM_MUL_WITH_OVERFLOW[int_width],
|
||||
&[lhs.into(), rhs.into()],
|
||||
|
@ -6988,6 +6993,7 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
|||
}
|
||||
NumSubWrap => unreachable!("wrapping subtraction is not defined on floats"),
|
||||
NumMul => bd.build_float_mul(lhs, rhs, "mul_float").into(),
|
||||
NumMulSaturated => bd.build_float_mul(lhs, rhs, "mul_float").into(),
|
||||
NumMulChecked => {
|
||||
let context = env.context;
|
||||
|
||||
|
|
|
@ -549,6 +549,24 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
_ => panic_ret_type(),
|
||||
},
|
||||
NumMulSaturated => match self.ret_layout {
|
||||
Layout::Builtin(Builtin::Int(width)) => {
|
||||
self.load_args_and_call_zig(backend, &bitcode::NUM_MUL_SATURATED_INT[width])
|
||||
}
|
||||
Layout::Builtin(Builtin::Float(FloatWidth::F32)) => {
|
||||
self.load_args(backend);
|
||||
backend.code_builder.f32_mul()
|
||||
}
|
||||
Layout::Builtin(Builtin::Float(FloatWidth::F64)) => {
|
||||
self.load_args(backend);
|
||||
backend.code_builder.f64_mul()
|
||||
}
|
||||
Layout::Builtin(Builtin::Decimal) => {
|
||||
self.load_args_and_call_zig(backend, bitcode::DEC_MUL_SATURATED)
|
||||
}
|
||||
_ => panic_ret_type(),
|
||||
},
|
||||
|
||||
NumMulChecked => {
|
||||
let arg_layout = backend.storage.symbol_layouts[&self.arguments[0]];
|
||||
match arg_layout {
|
||||
|
|
|
@ -78,6 +78,7 @@ pub enum LowLevel {
|
|||
NumSubSaturated,
|
||||
NumMul,
|
||||
NumMulWrap,
|
||||
NumMulSaturated,
|
||||
NumMulChecked,
|
||||
NumGt,
|
||||
NumGte,
|
||||
|
@ -284,6 +285,7 @@ impl LowLevelWrapperType {
|
|||
Symbol::NUM_SUB_SATURATED => CanBeReplacedBy(NumSubSaturated),
|
||||
Symbol::NUM_MUL => CanBeReplacedBy(NumMul),
|
||||
Symbol::NUM_MUL_WRAP => CanBeReplacedBy(NumMulWrap),
|
||||
Symbol::NUM_MUL_SATURATED => CanBeReplacedBy(NumMulSaturated),
|
||||
Symbol::NUM_MUL_CHECKED => WrapperIsRequired,
|
||||
Symbol::NUM_GT => CanBeReplacedBy(NumGt),
|
||||
Symbol::NUM_GTE => CanBeReplacedBy(NumGte),
|
||||
|
|
|
@ -1075,66 +1075,67 @@ define_builtins! {
|
|||
77 NUM_SUB_SATURATED: "subSaturated"
|
||||
78 NUM_MUL_WRAP: "mulWrap"
|
||||
79 NUM_MUL_CHECKED: "mulChecked"
|
||||
80 NUM_INT: "Int"
|
||||
81 NUM_FRAC: "Frac"
|
||||
82 NUM_NATURAL: "Natural"
|
||||
83 NUM_NAT: "Nat"
|
||||
84 NUM_INT_CAST: "intCast"
|
||||
85 NUM_IS_MULTIPLE_OF: "isMultipleOf"
|
||||
86 NUM_DECIMAL: "Decimal"
|
||||
87 NUM_DEC: "Dec" // the Num.Dectype alias
|
||||
88 NUM_BYTES_TO_U16: "bytesToU16"
|
||||
89 NUM_BYTES_TO_U32: "bytesToU32"
|
||||
90 NUM_CAST_TO_NAT: "#castToNat"
|
||||
91 NUM_DIV_CEIL: "divCeil"
|
||||
92 NUM_DIV_CEIL_CHECKED: "divCeilChecked"
|
||||
93 NUM_TO_STR: "toStr"
|
||||
94 NUM_MIN_I8: "minI8"
|
||||
95 NUM_MAX_I8: "maxI8"
|
||||
96 NUM_MIN_U8: "minU8"
|
||||
97 NUM_MAX_U8: "maxU8"
|
||||
98 NUM_MIN_I16: "minI16"
|
||||
99 NUM_MAX_I16: "maxI16"
|
||||
100 NUM_MIN_U16: "minU16"
|
||||
101 NUM_MAX_U16: "maxU16"
|
||||
102 NUM_MIN_I32: "minI32"
|
||||
103 NUM_MAX_I32: "maxI32"
|
||||
104 NUM_MIN_U32: "minU32"
|
||||
105 NUM_MAX_U32: "maxU32"
|
||||
106 NUM_MIN_I64: "minI64"
|
||||
107 NUM_MAX_I64: "maxI64"
|
||||
108 NUM_MIN_U64: "minU64"
|
||||
109 NUM_MAX_U64: "maxU64"
|
||||
110 NUM_MIN_I128: "minI128"
|
||||
111 NUM_MAX_I128: "maxI128"
|
||||
112 NUM_TO_I8: "toI8"
|
||||
113 NUM_TO_I8_CHECKED: "toI8Checked"
|
||||
114 NUM_TO_I16: "toI16"
|
||||
115 NUM_TO_I16_CHECKED: "toI16Checked"
|
||||
116 NUM_TO_I32: "toI32"
|
||||
117 NUM_TO_I32_CHECKED: "toI32Checked"
|
||||
118 NUM_TO_I64: "toI64"
|
||||
119 NUM_TO_I64_CHECKED: "toI64Checked"
|
||||
120 NUM_TO_I128: "toI128"
|
||||
121 NUM_TO_I128_CHECKED: "toI128Checked"
|
||||
122 NUM_TO_U8: "toU8"
|
||||
123 NUM_TO_U8_CHECKED: "toU8Checked"
|
||||
124 NUM_TO_U16: "toU16"
|
||||
125 NUM_TO_U16_CHECKED: "toU16Checked"
|
||||
126 NUM_TO_U32: "toU32"
|
||||
127 NUM_TO_U32_CHECKED: "toU32Checked"
|
||||
128 NUM_TO_U64: "toU64"
|
||||
129 NUM_TO_U64_CHECKED: "toU64Checked"
|
||||
130 NUM_TO_U128: "toU128"
|
||||
131 NUM_TO_U128_CHECKED: "toU128Checked"
|
||||
132 NUM_TO_NAT: "toNat"
|
||||
133 NUM_TO_NAT_CHECKED: "toNatChecked"
|
||||
134 NUM_TO_F32: "toF32"
|
||||
135 NUM_TO_F32_CHECKED: "toF32Checked"
|
||||
136 NUM_TO_F64: "toF64"
|
||||
137 NUM_TO_F64_CHECKED: "toF64Checked"
|
||||
138 NUM_MAX_F64: "maxF64"
|
||||
139 NUM_MIN_F64: "minF64"
|
||||
80 NUM_MUL_SATURATED: "mulSaturated"
|
||||
81 NUM_INT: "Int"
|
||||
82 NUM_FRAC: "Frac"
|
||||
83 NUM_NATURAL: "Natural"
|
||||
84 NUM_NAT: "Nat"
|
||||
85 NUM_INT_CAST: "intCast"
|
||||
86 NUM_IS_MULTIPLE_OF: "isMultipleOf"
|
||||
87 NUM_DECIMAL: "Decimal"
|
||||
88 NUM_DEC: "Dec" // the Num.Dectype alias
|
||||
89 NUM_BYTES_TO_U16: "bytesToU16"
|
||||
90 NUM_BYTES_TO_U32: "bytesToU32"
|
||||
91 NUM_CAST_TO_NAT: "#castToNat"
|
||||
92 NUM_DIV_CEIL: "divCeil"
|
||||
93 NUM_DIV_CEIL_CHECKED: "divCeilChecked"
|
||||
94 NUM_TO_STR: "toStr"
|
||||
95 NUM_MIN_I8: "minI8"
|
||||
96 NUM_MAX_I8: "maxI8"
|
||||
97 NUM_MIN_U8: "minU8"
|
||||
98 NUM_MAX_U8: "maxU8"
|
||||
99 NUM_MIN_I16: "minI16"
|
||||
100 NUM_MAX_I16: "maxI16"
|
||||
101 NUM_MIN_U16: "minU16"
|
||||
102 NUM_MAX_U16: "maxU16"
|
||||
103 NUM_MIN_I32: "minI32"
|
||||
104 NUM_MAX_I32: "maxI32"
|
||||
105 NUM_MIN_U32: "minU32"
|
||||
106 NUM_MAX_U32: "maxU32"
|
||||
107 NUM_MIN_I64: "minI64"
|
||||
108 NUM_MAX_I64: "maxI64"
|
||||
109 NUM_MIN_U64: "minU64"
|
||||
110 NUM_MAX_U64: "maxU64"
|
||||
111 NUM_MIN_I128: "minI128"
|
||||
112 NUM_MAX_I128: "maxI128"
|
||||
113 NUM_TO_I8: "toI8"
|
||||
114 NUM_TO_I8_CHECKED: "toI8Checked"
|
||||
115 NUM_TO_I16: "toI16"
|
||||
116 NUM_TO_I16_CHECKED: "toI16Checked"
|
||||
117 NUM_TO_I32: "toI32"
|
||||
118 NUM_TO_I32_CHECKED: "toI32Checked"
|
||||
119 NUM_TO_I64: "toI64"
|
||||
120 NUM_TO_I64_CHECKED: "toI64Checked"
|
||||
121 NUM_TO_I128: "toI128"
|
||||
122 NUM_TO_I128_CHECKED: "toI128Checked"
|
||||
123 NUM_TO_U8: "toU8"
|
||||
124 NUM_TO_U8_CHECKED: "toU8Checked"
|
||||
125 NUM_TO_U16: "toU16"
|
||||
126 NUM_TO_U16_CHECKED: "toU16Checked"
|
||||
127 NUM_TO_U32: "toU32"
|
||||
128 NUM_TO_U32_CHECKED: "toU32Checked"
|
||||
129 NUM_TO_U64: "toU64"
|
||||
130 NUM_TO_U64_CHECKED: "toU64Checked"
|
||||
131 NUM_TO_U128: "toU128"
|
||||
132 NUM_TO_U128_CHECKED: "toU128Checked"
|
||||
133 NUM_TO_NAT: "toNat"
|
||||
134 NUM_TO_NAT_CHECKED: "toNatChecked"
|
||||
135 NUM_TO_F32: "toF32"
|
||||
136 NUM_TO_F32_CHECKED: "toF32Checked"
|
||||
137 NUM_TO_F64: "toF64"
|
||||
138 NUM_TO_F64_CHECKED: "toF64Checked"
|
||||
139 NUM_MAX_F64: "maxF64"
|
||||
140 NUM_MIN_F64: "minF64"
|
||||
}
|
||||
2 BOOL: "Bool" => {
|
||||
0 BOOL_BOOL: "Bool" // the Bool.Bool type alias
|
||||
|
|
|
@ -938,12 +938,11 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
Eq | NotEq => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||
|
||||
And | Or | NumAdd | NumAddWrap | NumAddChecked | NumAddSaturated | NumSub | NumSubWrap
|
||||
| NumSubChecked | NumSubSaturated | NumMul | NumMulWrap | NumMulChecked | NumGt
|
||||
| NumGte | NumLt | NumLte | NumCompare | NumDivUnchecked | NumDivCeilUnchecked
|
||||
| NumRemUnchecked | NumIsMultipleOf | NumPow | NumPowInt | NumBitwiseAnd
|
||||
| NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy | NumShiftRightBy | NumShiftRightZfBy => {
|
||||
arena.alloc_slice_copy(&[irrelevant, irrelevant])
|
||||
}
|
||||
| NumSubChecked | NumSubSaturated | NumMul | NumMulWrap | NumMulSaturated
|
||||
| NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare | NumDivUnchecked
|
||||
| NumDivCeilUnchecked | NumRemUnchecked | NumIsMultipleOf | NumPow | NumPowInt
|
||||
| NumBitwiseAnd | NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy | NumShiftRightBy
|
||||
| NumShiftRightZfBy => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
||||
|
||||
NumToStr | NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked
|
||||
| NumRound | NumCeiling | NumFloor | NumToFrac | Not | NumIsFinite | NumAtan | NumAcos
|
||||
|
|
|
@ -165,6 +165,7 @@ enum FirstOrder {
|
|||
NumSubChecked,
|
||||
NumMul,
|
||||
NumMulWrap,
|
||||
NumMulSaturated,
|
||||
NumMulChecked,
|
||||
NumGt,
|
||||
NumGte,
|
||||
|
|
|
@ -3180,6 +3180,76 @@ fn sub_saturated() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn mul_saturated() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : U8
|
||||
x = 20
|
||||
y : U8
|
||||
y = 20
|
||||
Num.mulSaturated x y
|
||||
"#
|
||||
),
|
||||
255,
|
||||
u8
|
||||
);
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : I8
|
||||
x = -20
|
||||
y : I8
|
||||
y = -20
|
||||
Num.mulSaturated x y
|
||||
"#
|
||||
),
|
||||
127,
|
||||
i8
|
||||
);
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : I8
|
||||
x = 20
|
||||
y : I8
|
||||
y = -20
|
||||
Num.mulSaturated x y
|
||||
"#
|
||||
),
|
||||
-128,
|
||||
i8
|
||||
);
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : I8
|
||||
x = -20
|
||||
y : I8
|
||||
y = 20
|
||||
Num.mulSaturated x y
|
||||
"#
|
||||
),
|
||||
-128,
|
||||
i8
|
||||
);
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : I8
|
||||
x = 20
|
||||
y : I8
|
||||
y = 20
|
||||
Num.mulSaturated x y
|
||||
"#
|
||||
),
|
||||
127,
|
||||
i8
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn monomorphized_ints() {
|
||||
|
|
|
@ -348,6 +348,11 @@ fn num_mul_wrap() {
|
|||
expect_success("Num.mulWrap Num.maxI64 2", "-2 : I64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_mul_saturated() {
|
||||
expect_success("Num.mulSaturated Num.maxI64 2", "9223372036854775807 : I64");
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "wasm"))]
|
||||
#[test]
|
||||
fn num_add_checked() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue