mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-23 06:25:10 +00:00
add Num.count*Bits functions
This commit is contained in:
parent
fbaa257cef
commit
785da377c8
11 changed files with 207 additions and 8 deletions
|
@ -86,6 +86,10 @@ comptime {
|
|||
num.exportMulWithOverflow(T, WIDEINTS[i], ROC_BUILTINS ++ "." ++ NUM ++ ".mul_with_overflow.");
|
||||
num.exportMulOrPanic(T, WIDEINTS[i], ROC_BUILTINS ++ "." ++ NUM ++ ".mul_or_panic.");
|
||||
num.exportMulSaturatedInt(T, WIDEINTS[i], ROC_BUILTINS ++ "." ++ NUM ++ ".mul_saturated.");
|
||||
|
||||
num.exportCountLeadingZeroBits(T, ROC_BUILTINS ++ "." ++ NUM ++ ".count_leading_zero_bits.");
|
||||
num.exportCountTrailingZeroBits(T, ROC_BUILTINS ++ "." ++ NUM ++ ".count_trailing_zero_bits.");
|
||||
num.exportCountOneBits(T, ROC_BUILTINS ++ "." ++ NUM ++ ".count_one_bits.");
|
||||
}
|
||||
|
||||
inline for (INTEGERS) |FROM| {
|
||||
|
|
|
@ -460,3 +460,30 @@ pub fn exportMulOrPanic(comptime T: type, comptime W: type, comptime name: []con
|
|||
}.func;
|
||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||
}
|
||||
|
||||
pub fn exportCountLeadingZeroBits(comptime T: type, comptime name: []const u8) void {
|
||||
comptime var f = struct {
|
||||
fn func(self: T) callconv(.C) usize {
|
||||
return @as(usize, @clz(T, self));
|
||||
}
|
||||
}.func;
|
||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||
}
|
||||
|
||||
pub fn exportCountTrailingZeroBits(comptime T: type, comptime name: []const u8) void {
|
||||
comptime var f = struct {
|
||||
fn func(self: T) callconv(.C) usize {
|
||||
return @as(usize, @ctz(T, self));
|
||||
}
|
||||
}.func;
|
||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||
}
|
||||
|
||||
pub fn exportCountOneBits(comptime T: type, comptime name: []const u8) void {
|
||||
comptime var f = struct {
|
||||
fn func(self: T) callconv(.C) usize {
|
||||
return @as(usize, @popCount(T, self));
|
||||
}
|
||||
}.func;
|
||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||
}
|
||||
|
|
|
@ -68,6 +68,9 @@ interface Num
|
|||
compare,
|
||||
pow,
|
||||
powInt,
|
||||
countLeadingZeroBits,
|
||||
countTrailingZeroBits,
|
||||
countOneBits,
|
||||
addWrap,
|
||||
addChecked,
|
||||
addSaturated,
|
||||
|
@ -930,6 +933,46 @@ pow : Frac a, Frac a -> Frac a
|
|||
## so large it causes an overflow.
|
||||
powInt : Int a, Int a -> Int a
|
||||
|
||||
## Counts the number of most-significant (leading in a big-Endian sense) zeroes in an integer.
|
||||
##
|
||||
## ```
|
||||
## Num.countLeadingZeroBits 0b0001_1100u8
|
||||
##
|
||||
## 3
|
||||
##
|
||||
## Num.countLeadingZeroBits 0b0000_0000u8
|
||||
##
|
||||
## 8
|
||||
## ```
|
||||
countLeadingZeroBits : Int a -> Nat
|
||||
|
||||
## Counts the number of least-significant (trailing in a big-Endian sense) zeroes in an integer.
|
||||
##
|
||||
## ```
|
||||
## Num.countTrailingZeroBits 0b0001_1100u8
|
||||
##
|
||||
## 2
|
||||
##
|
||||
## Num.countTrailingZeroBits 0b0000_0000u8
|
||||
##
|
||||
## 8
|
||||
## ```
|
||||
countTrailingZeroBits : Int a -> Nat
|
||||
|
||||
|
||||
## Counts the number of set bits in an integer.
|
||||
##
|
||||
## ```
|
||||
## Num.countOneBits 0b0001_1100u8
|
||||
##
|
||||
## 3
|
||||
##
|
||||
## Num.countOneBits 0b0000_0000u8
|
||||
##
|
||||
## 0
|
||||
## ```
|
||||
countOneBits : Int a -> Nat
|
||||
|
||||
addWrap : Int range, Int range -> Int range
|
||||
|
||||
## Add two numbers, clamping on the maximum representable number rather than
|
||||
|
|
|
@ -289,6 +289,12 @@ pub const NUM_MUL_CHECKED_INT: IntrinsicName = int_intrinsic!("roc_builtins.num.
|
|||
pub const NUM_MUL_CHECKED_FLOAT: IntrinsicName =
|
||||
float_intrinsic!("roc_builtins.num.mul_with_overflow");
|
||||
|
||||
pub const NUM_COUNT_LEADING_ZERO_BITS: IntrinsicName =
|
||||
int_intrinsic!("roc_builtins.num.count_leading_zero_bits");
|
||||
pub const NUM_COUNT_TRAILING_ZERO_BITS: IntrinsicName =
|
||||
int_intrinsic!("roc_builtins.num.count_trailing_zero_bits");
|
||||
pub const NUM_COUNT_ONE_BITS: IntrinsicName = int_intrinsic!("roc_builtins.num.count_one_bits");
|
||||
|
||||
pub const NUM_BYTES_TO_U16: &str = "roc_builtins.num.bytes_to_u16";
|
||||
pub const NUM_BYTES_TO_U32: &str = "roc_builtins.num.bytes_to_u32";
|
||||
|
||||
|
|
|
@ -194,6 +194,9 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
NumShiftRightBy; NUM_SHIFT_RIGHT; 2,
|
||||
NumShiftRightZfBy; NUM_SHIFT_RIGHT_ZERO_FILL; 2,
|
||||
NumToStr; NUM_TO_STR; 1,
|
||||
NumCountLeadingZeroBits; NUM_COUNT_LEADING_ZERO_BITS; 1,
|
||||
NumCountTrailingZeroBits; NUM_COUNT_TRAILING_ZERO_BITS; 1,
|
||||
NumCountOneBits; NUM_COUNT_ONE_BITS; 1,
|
||||
|
||||
Eq; BOOL_STRUCTURAL_EQ; 2,
|
||||
NotEq; BOOL_STRUCTURAL_NOT_EQ; 2,
|
||||
|
|
|
@ -848,9 +848,24 @@ pub(crate) fn run_low_level<'a, 'ctx, 'env>(
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumLogUnchecked | NumSin | NumCos
|
||||
| NumCeiling | NumFloor | NumToFrac | NumIsFinite | NumAtan | NumAcos | NumAsin
|
||||
| NumToIntChecked => {
|
||||
NumAbs
|
||||
| NumNeg
|
||||
| NumRound
|
||||
| NumSqrtUnchecked
|
||||
| NumLogUnchecked
|
||||
| NumSin
|
||||
| NumCos
|
||||
| NumCeiling
|
||||
| NumFloor
|
||||
| NumToFrac
|
||||
| NumIsFinite
|
||||
| NumAtan
|
||||
| NumAcos
|
||||
| NumAsin
|
||||
| NumToIntChecked
|
||||
| NumCountLeadingZeroBits
|
||||
| NumCountTrailingZeroBits
|
||||
| NumCountOneBits => {
|
||||
arguments_with_layouts!((arg, arg_layout));
|
||||
|
||||
match layout_interner.get(arg_layout) {
|
||||
|
@ -2045,6 +2060,19 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
|
|||
complex_bitcast_check_size(env, result, return_type.into(), "cast_bitpacked")
|
||||
}
|
||||
}
|
||||
NumCountLeadingZeroBits => call_bitcode_fn(
|
||||
env,
|
||||
&[arg.into()],
|
||||
&bitcode::NUM_COUNT_LEADING_ZERO_BITS[arg_width],
|
||||
),
|
||||
NumCountTrailingZeroBits => call_bitcode_fn(
|
||||
env,
|
||||
&[arg.into()],
|
||||
&bitcode::NUM_COUNT_TRAILING_ZERO_BITS[arg_width],
|
||||
),
|
||||
NumCountOneBits => {
|
||||
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_COUNT_ONE_BITS[arg_width])
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Unrecognized int unary operation: {:?}", op);
|
||||
}
|
||||
|
|
|
@ -1489,6 +1489,40 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
_ => panic_ret_type(),
|
||||
},
|
||||
|
||||
NumCountLeadingZeroBits => match backend
|
||||
.layout_interner
|
||||
.get(backend.storage.symbol_layouts[&self.arguments[0]])
|
||||
{
|
||||
Layout::Builtin(Builtin::Int(width)) => {
|
||||
self.load_args_and_call_zig(
|
||||
backend,
|
||||
&bitcode::NUM_COUNT_LEADING_ZERO_BITS[width],
|
||||
);
|
||||
}
|
||||
_ => panic_ret_type(),
|
||||
},
|
||||
NumCountTrailingZeroBits => match backend
|
||||
.layout_interner
|
||||
.get(backend.storage.symbol_layouts[&self.arguments[0]])
|
||||
{
|
||||
Layout::Builtin(Builtin::Int(width)) => {
|
||||
self.load_args_and_call_zig(
|
||||
backend,
|
||||
&bitcode::NUM_COUNT_TRAILING_ZERO_BITS[width],
|
||||
);
|
||||
}
|
||||
_ => panic_ret_type(),
|
||||
},
|
||||
NumCountOneBits => match backend
|
||||
.layout_interner
|
||||
.get(backend.storage.symbol_layouts[&self.arguments[0]])
|
||||
{
|
||||
Layout::Builtin(Builtin::Int(width)) => {
|
||||
self.load_args_and_call_zig(backend, &bitcode::NUM_COUNT_ONE_BITS[width]);
|
||||
}
|
||||
_ => panic_ret_type(),
|
||||
},
|
||||
NumRound => {
|
||||
self.load_args(backend);
|
||||
let arg_type = CodeGenNumType::for_symbol(backend, self.arguments[0]);
|
||||
|
|
|
@ -101,6 +101,9 @@ pub enum LowLevel {
|
|||
NumToIntChecked,
|
||||
NumToFloatChecked,
|
||||
NumToStr,
|
||||
NumCountLeadingZeroBits,
|
||||
NumCountTrailingZeroBits,
|
||||
NumCountOneBits,
|
||||
Eq,
|
||||
NotEq,
|
||||
And,
|
||||
|
@ -312,6 +315,9 @@ map_symbol_to_lowlevel! {
|
|||
NumShiftRightBy <= NUM_SHIFT_RIGHT,
|
||||
NumShiftRightZfBy <= NUM_SHIFT_RIGHT_ZERO_FILL,
|
||||
NumToStr <= NUM_TO_STR,
|
||||
NumCountLeadingZeroBits <= NUM_COUNT_LEADING_ZERO_BITS,
|
||||
NumCountTrailingZeroBits <= NUM_COUNT_TRAILING_ZERO_BITS,
|
||||
NumCountOneBits <= NUM_COUNT_ONE_BITS,
|
||||
Eq <= BOOL_STRUCTURAL_EQ,
|
||||
NotEq <= BOOL_STRUCTURAL_NOT_EQ,
|
||||
And <= BOOL_AND,
|
||||
|
|
|
@ -1246,6 +1246,9 @@ define_builtins! {
|
|||
145 NUM_MUL_CHECKED_LOWLEVEL: "mulCheckedLowlevel"
|
||||
146 NUM_BYTES_TO_U16_LOWLEVEL: "bytesToU16Lowlevel"
|
||||
147 NUM_BYTES_TO_U32_LOWLEVEL: "bytesToU32Lowlevel"
|
||||
148 NUM_COUNT_LEADING_ZERO_BITS: "countLeadingZeroBits"
|
||||
149 NUM_COUNT_TRAILING_ZERO_BITS: "countTrailingZeroBits"
|
||||
150 NUM_COUNT_ONE_BITS: "countOneBits"
|
||||
}
|
||||
4 BOOL: "Bool" => {
|
||||
0 BOOL_BOOL: "Bool" exposed_type=true // the Bool.Bool type alias
|
||||
|
|
|
@ -1026,11 +1026,29 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
| 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
|
||||
| NumAsin | NumIntCast | NumToIntChecked | NumToFloatCast | NumToFloatChecked => {
|
||||
arena.alloc_slice_copy(&[irrelevant])
|
||||
}
|
||||
NumToStr
|
||||
| NumAbs
|
||||
| NumNeg
|
||||
| NumSin
|
||||
| NumCos
|
||||
| NumSqrtUnchecked
|
||||
| NumLogUnchecked
|
||||
| NumRound
|
||||
| NumCeiling
|
||||
| NumFloor
|
||||
| NumToFrac
|
||||
| Not
|
||||
| NumIsFinite
|
||||
| NumAtan
|
||||
| NumAcos
|
||||
| NumAsin
|
||||
| NumIntCast
|
||||
| NumToIntChecked
|
||||
| NumToFloatCast
|
||||
| NumToFloatChecked
|
||||
| NumCountLeadingZeroBits
|
||||
| NumCountTrailingZeroBits
|
||||
| NumCountOneBits => arena.alloc_slice_copy(&[irrelevant]),
|
||||
NumBytesToU16 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||
|
|
|
@ -3807,3 +3807,30 @@ fn condition_polymorphic_num_becomes_float() {
|
|||
f32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn num_count_leading_zero_bits() {
|
||||
assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u8"#, 2, usize);
|
||||
assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u16"#, 10, usize);
|
||||
assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u32"#, 26, usize);
|
||||
assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u64"#, 58, usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn num_count_trailing_zero_bits() {
|
||||
assert_evals_to!(r#"Num.countTrailingZeroBits 0b0010_1000u8"#, 3, usize);
|
||||
assert_evals_to!(r#"Num.countTrailingZeroBits 0b0010_0000u16"#, 5, usize);
|
||||
assert_evals_to!(r#"Num.countTrailingZeroBits 0u32"#, 32, usize);
|
||||
assert_evals_to!(r#"Num.countTrailingZeroBits 0b0010_1111u64"#, 0, usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn num_count_one_bits() {
|
||||
assert_evals_to!(r#"Num.countOneBits 0b0010_1000u8"#, 2, usize);
|
||||
assert_evals_to!(r#"Num.countOneBits 0b0010_0000u16"#, 1, usize);
|
||||
assert_evals_to!(r#"Num.countOneBits 0u32"#, 0, usize);
|
||||
assert_evals_to!(r#"Num.countOneBits 0b0010_1111u64"#, 5, usize);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue