Merge pull request #6176 from roc-lang/set-perf

Improve perf of Dict and Set
This commit is contained in:
Brendan Hansknecht 2023-12-06 02:02:01 -08:00 committed by GitHub
commit 81eff6a23f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 1089 additions and 1004 deletions

View file

@ -633,8 +633,8 @@ pub fn exportMulOrPanic(comptime T: type, comptime W: type, comptime name: []con
pub fn exportCountLeadingZeroBits(comptime T: type, comptime name: []const u8) void { pub fn exportCountLeadingZeroBits(comptime T: type, comptime name: []const u8) void {
comptime var f = struct { comptime var f = struct {
fn func(self: T) callconv(.C) usize { fn func(self: T) callconv(.C) u8 {
return @as(usize, @clz(self)); return @as(u8, @clz(self));
} }
}.func; }.func;
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong }); @export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
@ -642,8 +642,8 @@ pub fn exportCountLeadingZeroBits(comptime T: type, comptime name: []const u8) v
pub fn exportCountTrailingZeroBits(comptime T: type, comptime name: []const u8) void { pub fn exportCountTrailingZeroBits(comptime T: type, comptime name: []const u8) void {
comptime var f = struct { comptime var f = struct {
fn func(self: T) callconv(.C) usize { fn func(self: T) callconv(.C) u8 {
return @as(usize, @ctz(self)); return @as(u8, @ctz(self));
} }
}.func; }.func;
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong }); @export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
@ -651,8 +651,8 @@ pub fn exportCountTrailingZeroBits(comptime T: type, comptime name: []const u8)
pub fn exportCountOneBits(comptime T: type, comptime name: []const u8) void { pub fn exportCountOneBits(comptime T: type, comptime name: []const u8) void {
comptime var f = struct { comptime var f = struct {
fn func(self: T) callconv(.C) usize { fn func(self: T) callconv(.C) u8 {
return @as(usize, @popCount(self)); return @as(u8, @popCount(self));
} }
}.func; }.func;
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong }); @export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });

View file

@ -102,7 +102,6 @@ Dict k v := {
metadata : List I8, metadata : List I8,
dataIndices : List Nat, dataIndices : List Nat,
data : List (k, v), data : List (k, v),
size : Nat,
} where k implements Hash & Eq } where k implements Hash & Eq
implements [ implements [
Eq { Eq {
@ -137,6 +136,9 @@ toInspectorDict = \dict ->
fmt <- Inspect.custom fmt <- Inspect.custom
Inspect.apply (Inspect.dict dict walk Inspect.toInspector Inspect.toInspector) fmt Inspect.apply (Inspect.dict dict walk Inspect.toInspector Inspect.toInspector) fmt
emptyMetadata = [emptySlot, emptySlot, emptySlot, emptySlot, emptySlot, emptySlot, emptySlot, emptySlot]
emptyDataIndices = [0, 0, 0, 0, 0, 0, 0, 0]
## Return an empty dictionary. ## Return an empty dictionary.
## ``` ## ```
## emptyDict = Dict.empty {} ## emptyDict = Dict.empty {}
@ -144,12 +146,41 @@ toInspectorDict = \dict ->
empty : {} -> Dict * * empty : {} -> Dict * *
empty = \{} -> empty = \{} ->
@Dict { @Dict {
metadata: List.repeat emptySlot 8, metadata: emptyMetadata,
dataIndices: List.repeat 0 8, dataIndices: emptyDataIndices,
data: [], data: [],
size: 0,
} }
## Return a dictionary with space allocated for a number of entries. This
## may provide a performance optimization if you know how many entries will be
## inserted.
withCapacity : Nat -> Dict * *
withCapacity = \size ->
if size == 0 then
empty {}
else
# Max load is 7/8.
# To avoid potential rehash, multiply size by 8/7.
# Then map to containing power of 2 to make dict indices happy.
cap =
size
|> Num.toU64
|> Num.mul 8
|> Num.divTrunc 7
|> containingPowerOfTwo
|> Num.max 8
|> Num.toNat
@Dict {
metadata: List.repeat emptySlot cap,
dataIndices: List.repeat 0 cap,
data: List.withCapacity cap,
}
containingPowerOfTwo : U64 -> U64
containingPowerOfTwo = \size ->
Num.shiftLeftBy 1 (64 - Num.countLeadingZeroBits (size - 1))
## Returns the max number of elements the dictionary can hold before requiring a rehash. ## Returns the max number of elements the dictionary can hold before requiring a rehash.
## ``` ## ```
## foodDict = ## foodDict =
@ -164,14 +195,6 @@ capacity = \@Dict { dataIndices } ->
Num.subWrap cap (Num.shiftRightZfBy cap 3) Num.subWrap cap (Num.shiftRightZfBy cap 3)
## Return a dictionary with space allocated for a number of entries. This
## may provide a performance optimization if you know how many entries will be
## inserted.
withCapacity : Nat -> Dict * *
withCapacity = \_ ->
# TODO: power of 2 * 8 and actual implementation
empty {}
## Returns a dictionary containing the key and value provided as input. ## Returns a dictionary containing the key and value provided as input.
## ``` ## ```
## expect ## expect
@ -193,8 +216,15 @@ single = \k, v ->
## ``` ## ```
fromList : List (k, v) -> Dict k v where k implements Hash & Eq fromList : List (k, v) -> Dict k v where k implements Hash & Eq
fromList = \data -> fromList = \data ->
# TODO: make this efficient. Should just set data and then set all indicies in the hashmap. # TODO: make more efficient.
List.walk data (empty {}) (\dict, (k, v) -> insert dict k v) # Want to just set the data and then set all indicies in the hashmap.
# That said, we need to also deal with duplicates.
size = List.len data
if size > 0 then
List.walk data (withCapacity size) (\dict, (k, v) -> insert dict k v)
else
empty {}
## Returns the number of values in the dictionary. ## Returns the number of values in the dictionary.
## ``` ## ```
@ -207,8 +237,8 @@ fromList = \data ->
## |> Bool.isEq 3 ## |> Bool.isEq 3
## ``` ## ```
len : Dict * * -> Nat len : Dict * * -> Nat
len = \@Dict { size } -> len = \@Dict { data } ->
size List.len data
## Check if the dictinoary is empty. ## Check if the dictinoary is empty.
## ``` ## ```
@ -217,8 +247,8 @@ len = \@Dict { size } ->
## Dict.isEmpty (Dict.empty {}) ## Dict.isEmpty (Dict.empty {})
## ``` ## ```
isEmpty : Dict * * -> Bool isEmpty : Dict * * -> Bool
isEmpty = \@Dict { size } -> isEmpty = \@Dict { data } ->
size == 0 List.isEmpty data
## Clears all elements from a dictionary keeping around the allocation if it isn't huge. ## Clears all elements from a dictionary keeping around the allocation if it isn't huge.
## ``` ## ```
@ -246,7 +276,6 @@ clear = \@Dict { metadata, dataIndices, data } ->
dataIndices, dataIndices,
# use takeFirst to keep around the capacity. # use takeFirst to keep around the capacity.
data: List.takeFirst data 0, data: List.takeFirst data 0,
size: 0,
} }
## Convert each value in the dictionary to something new, by calling a conversion ## Convert each value in the dictionary to something new, by calling a conversion
@ -424,7 +453,7 @@ contains = \@Dict { metadata, dataIndices, data }, key ->
## |> Bool.isEq (Ok 12) ## |> Bool.isEq (Ok 12)
## ``` ## ```
insert : Dict k v, k, v -> Dict k v where k implements Hash & Eq insert : Dict k v, k, v -> Dict k v where k implements Hash & Eq
insert = \@Dict { metadata, dataIndices, data, size }, key, value -> insert = \@Dict { metadata, dataIndices, data }, key, value ->
hashKey = hashKey =
createLowLevelHasher PseudoRandSeed createLowLevelHasher PseudoRandSeed
|> Hash.hash key |> Hash.hash key
@ -441,7 +470,6 @@ insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
metadata, metadata,
dataIndices, dataIndices,
data: List.set data dataIndex (key, value), data: List.set data dataIndex (key, value),
size,
} }
Err NotFound -> Err NotFound ->
@ -453,7 +481,6 @@ insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
metadata, metadata,
dataIndices, dataIndices,
data, data,
size: Num.addWrap size 1,
} }
) )
@ -470,7 +497,7 @@ insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
## |> Bool.isEq 0 ## |> Bool.isEq 0
## ``` ## ```
remove : Dict k v, k -> Dict k v where k implements Hash & Eq remove : Dict k v, k -> Dict k v where k implements Hash & Eq
remove = \@Dict { metadata, dataIndices, data, size }, key -> remove = \@Dict { metadata, dataIndices, data }, key ->
# TODO: change this from swap remove to tombstone and test is performance is still good. # TODO: change this from swap remove to tombstone and test is performance is still good.
hashKey = hashKey =
createLowLevelHasher PseudoRandSeed createLowLevelHasher PseudoRandSeed
@ -490,13 +517,12 @@ remove = \@Dict { metadata, dataIndices, data, size }, key ->
metadata: List.set metadata index deletedSlot, metadata: List.set metadata index deletedSlot,
dataIndices, dataIndices,
data: List.dropLast data 1, data: List.dropLast data 1,
size: Num.subWrap size 1,
} }
else else
swapAndUpdateDataIndex (@Dict { metadata, dataIndices, data, size }) index last swapAndUpdateDataIndex (@Dict { metadata, dataIndices, data }) index last
Err NotFound -> Err NotFound ->
@Dict { metadata, dataIndices, data, size } @Dict { metadata, dataIndices, data }
## Insert or remove a value for a specified key. This function enables a ## Insert or remove a value for a specified key. This function enables a
## performance optimization for the use case of providing a default when a value ## performance optimization for the use case of providing a default when a value
@ -596,36 +622,49 @@ values = \@Dict { data } ->
## ``` ## ```
insertAll : Dict k v, Dict k v -> Dict k v where k implements Hash & Eq insertAll : Dict k v, Dict k v -> Dict k v where k implements Hash & Eq
insertAll = \xs, ys -> insertAll = \xs, ys ->
walk ys xs insert if len ys > len xs then
insertAll ys xs
else
walk ys xs insert
## Combine two dictionaries by keeping the [intersection](https://en.wikipedia.org/wiki/Intersection_(set_theory)) ## Combine two dictionaries by keeping the [intersection](https://en.wikipedia.org/wiki/Intersection_(set_theory))
## of all the key-value pairs. This means that we keep only those pairs ## of all the key-value pairs. This means that we keep only those pairs
## that are in both dictionaries. Note that where there are pairs with ## that are in both dictionaries. Both the key and value must match to be kept.
## the same key, the value contained in the first input will be retained,
## and the value in the second input will be removed.
## ``` ## ```
## first = ## first =
## Dict.single 1 "Keep Me" ## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me" ## |> Dict.insert 2 "And Me"
## |> Dict.insert 3 "Not this one"
## ##
## second = ## second =
## Dict.single 1 "Keep Me" ## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me" ## |> Dict.insert 2 "And Me"
## |> Dict.insert 3 "But Not Me" ## |> Dict.insert 3 "This has a different value"
## |> Dict.insert 4 "Or Me" ## |> Dict.insert 4 "Or Me"
## ##
## expect Dict.keepShared first second == first ## expected =
## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me"
##
## expect Dict.keepShared first second == expected
## ``` ## ```
keepShared : Dict k v, Dict k v -> Dict k v where k implements Hash & Eq keepShared : Dict k v, Dict k v -> Dict k v where k implements Hash & Eq, v implements Eq
keepShared = \xs, ys -> keepShared = \xs0, ys0 ->
(xs1, ys1) =
if len ys0 < len xs0 then
(ys0, xs0)
else
(xs0, ys0)
walk walk
xs xs1
(empty {}) (withCapacity (len xs1))
(\state, k, v -> (\state, k, v ->
if contains ys k then when get ys1 k is
insert state k v Ok yv if v == yv ->
else insert state k v
state
_ ->
state
) )
## Remove the key-value pairs in the first input that are also in the second ## Remove the key-value pairs in the first input that are also in the second
@ -653,7 +692,7 @@ removeAll = \xs, ys ->
walk ys xs (\state, k, _ -> remove state k) walk ys xs (\state, k, _ -> remove state k)
swapAndUpdateDataIndex : Dict k v, Nat, Nat -> Dict k v where k implements Hash & Eq swapAndUpdateDataIndex : Dict k v, Nat, Nat -> Dict k v where k implements Hash & Eq
swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data, size }, removedIndex, lastIndex -> swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data }, removedIndex, lastIndex ->
(key, _) = listGetUnsafe data lastIndex (key, _) = listGetUnsafe data lastIndex
hashKey = hashKey =
createLowLevelHasher PseudoRandSeed createLowLevelHasher PseudoRandSeed
@ -678,7 +717,6 @@ swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data, size }, removedIn
# Update index of swaped element. # Update index of swaped element.
dataIndices: List.set dataIndices index dataIndex, dataIndices: List.set dataIndices index dataIndex,
data: nextData, data: nextData,
size: Num.subWrap size 1,
} }
Err NotFound -> Err NotFound ->
@ -686,7 +724,7 @@ swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data, size }, removedIn
crash "unreachable state in dict swapAndUpdateDataIndex hit. Definitely a standard library bug." crash "unreachable state in dict swapAndUpdateDataIndex hit. Definitely a standard library bug."
insertNotFoundHelper : Dict k v, k, v, U64, I8 -> Dict k v insertNotFoundHelper : Dict k v, k, v, U64, I8 -> Dict k v
insertNotFoundHelper = \@Dict { metadata, dataIndices, data, size }, key, value, h1Key, h2Key -> insertNotFoundHelper = \@Dict { metadata, dataIndices, data }, key, value, h1Key, h2Key ->
probe = newProbe h1Key (div8 (List.len metadata)) probe = newProbe h1Key (div8 (List.len metadata))
index = nextEmptyOrDeletedHelper metadata probe 0 index = nextEmptyOrDeletedHelper metadata probe 0
dataIndex = List.len data dataIndex = List.len data
@ -696,7 +734,6 @@ insertNotFoundHelper = \@Dict { metadata, dataIndices, data, size }, key, value,
metadata: List.set metadata index h2Key, metadata: List.set metadata index h2Key,
dataIndices: List.set dataIndices index dataIndex, dataIndices: List.set dataIndices index dataIndex,
data: nextData, data: nextData,
size,
} }
nextEmptyOrDeletedHelper : List I8, Probe, Nat -> Nat nextEmptyOrDeletedHelper : List I8, Probe, Nat -> Nat
@ -749,27 +786,26 @@ findIndexHelper = \metadata, dataIndices, data, h2Key, key, probe, offset ->
# If we aren't to the load factor yet, just ignore this. # If we aren't to the load factor yet, just ignore this.
# The container must have an updated size including any elements about to be inserted. # The container must have an updated size including any elements about to be inserted.
maybeRehash : Dict k v -> Dict k v where k implements Hash & Eq maybeRehash : Dict k v -> Dict k v where k implements Hash & Eq
maybeRehash = \@Dict { metadata, dataIndices, data, size } -> maybeRehash = \@Dict { metadata, dataIndices, data } ->
cap = List.len dataIndices cap = List.len dataIndices
maxLoadCap = maxLoadCap =
# This is 7/8 * capacity, which is the max load factor. # This is 7/8 * capacity, which is the max load factor.
Num.subWrap cap (Num.shiftRightZfBy cap 3) Num.subWrap cap (Num.shiftRightZfBy cap 3)
if size > maxLoadCap then if (List.len data + 1) > maxLoadCap then
rehash (@Dict { metadata, dataIndices, data, size }) rehash (@Dict { metadata, dataIndices, data })
else else
@Dict { metadata, dataIndices, data, size } @Dict { metadata, dataIndices, data }
# TODO: switch rehash to iterate data and eventually clear out tombstones as well. # TODO: switch rehash to iterate data and eventually clear out tombstones as well.
rehash : Dict k v -> Dict k v where k implements Hash & Eq rehash : Dict k v -> Dict k v where k implements Hash & Eq
rehash = \@Dict { metadata, dataIndices, data, size } -> rehash = \@Dict { metadata, dataIndices, data } ->
newLen = 2 * List.len dataIndices newLen = 2 * List.len dataIndices
newDict = newDict =
@Dict { @Dict {
metadata: List.repeat emptySlot newLen, metadata: List.repeat emptySlot newLen,
dataIndices: List.repeat 0 newLen, dataIndices: List.repeat 0 newLen,
data, data,
size,
} }
rehashHelper newDict metadata dataIndices data 0 rehashHelper newDict metadata dataIndices data 0
@ -796,7 +832,7 @@ rehashHelper = \dict, oldMetadata, oldDataIndices, oldData, index ->
dict dict
insertForRehash : Dict k v, k, Nat -> Dict k v where k implements Hash & Eq insertForRehash : Dict k v, k, Nat -> Dict k v where k implements Hash & Eq
insertForRehash = \@Dict { metadata, dataIndices, data, size }, key, dataIndex -> insertForRehash = \@Dict { metadata, dataIndices, data }, key, dataIndex ->
hashKey = hashKey =
createLowLevelHasher PseudoRandSeed createLowLevelHasher PseudoRandSeed
|> Hash.hash key |> Hash.hash key
@ -810,7 +846,6 @@ insertForRehash = \@Dict { metadata, dataIndices, data, size }, key, dataIndex -
metadata: List.set metadata index h2Key, metadata: List.set metadata index h2Key,
dataIndices: List.set dataIndices index dataIndex, dataIndices: List.set dataIndices index dataIndex,
data, data,
size,
} }
emptySlot : I8 emptySlot : I8

View file

@ -1112,7 +1112,7 @@ powInt : Int a, Int a -> Int a
## ##
## 8 ## 8
## ``` ## ```
countLeadingZeroBits : Int a -> Nat countLeadingZeroBits : Int a -> U8
## Counts the number of least-significant (trailing in a big-Endian sense) zeroes in an integer. ## Counts the number of least-significant (trailing in a big-Endian sense) zeroes in an integer.
## ##
@ -1125,7 +1125,7 @@ countLeadingZeroBits : Int a -> Nat
## ##
## 8 ## 8
## ``` ## ```
countTrailingZeroBits : Int a -> Nat countTrailingZeroBits : Int a -> U8
## Counts the number of set bits in an integer. ## Counts the number of set bits in an integer.
## ##
@ -1138,7 +1138,7 @@ countTrailingZeroBits : Int a -> Nat
## ##
## 0 ## 0
## ``` ## ```
countOneBits : Int a -> Nat countOneBits : Int a -> U8
addWrap : Int range, Int range -> Int range addWrap : Int range, Int range -> Int range

View file

@ -237,9 +237,10 @@ toList = \@Set dict ->
## ``` ## ```
fromList : List k -> Set k where k implements Hash & Eq fromList : List k -> Set k where k implements Hash & Eq
fromList = \list -> fromList = \list ->
initial = @Set (Dict.withCapacity (List.len list)) list
|> List.map \k -> (k, {})
List.walk list initial insert |> Dict.fromList
|> @Set
## Combine two `Set` collection by keeping the ## Combine two `Set` collection by keeping the
## [union](https://en.wikipedia.org/wiki/Union_(set_theory)) ## [union](https://en.wikipedia.org/wiki/Union_(set_theory))

View file

@ -1765,6 +1765,21 @@ trait Backend<'a> {
let intrinsic = bitcode::NUM_IS_MULTIPLE_OF[int_width].to_string(); let intrinsic = bitcode::NUM_IS_MULTIPLE_OF[int_width].to_string();
self.build_fn_call(sym, intrinsic, args, arg_layouts, ret_layout); self.build_fn_call(sym, intrinsic, args, arg_layouts, ret_layout);
} }
LowLevel::NumCountLeadingZeroBits => {
let int_width = arg_layouts[0].try_int_width().unwrap();
let intrinsic = bitcode::NUM_COUNT_LEADING_ZERO_BITS[int_width].to_string();
self.build_fn_call(sym, intrinsic, args, arg_layouts, ret_layout);
}
LowLevel::NumCountTrailingZeroBits => {
let int_width = arg_layouts[0].try_int_width().unwrap();
let intrinsic = bitcode::NUM_COUNT_TRAILING_ZERO_BITS[int_width].to_string();
self.build_fn_call(sym, intrinsic, args, arg_layouts, ret_layout);
}
LowLevel::NumCountOneBits => {
let int_width = arg_layouts[0].try_int_width().unwrap();
let intrinsic = bitcode::NUM_COUNT_ONE_BITS[int_width].to_string();
self.build_fn_call(sym, intrinsic, args, arg_layouts, ret_layout);
}
LowLevel::ListSublist => { LowLevel::ListSublist => {
// list: RocList, // list: RocList,
// alignment: u32, // alignment: u32,

View file

@ -436,7 +436,7 @@ fn keep_shared() {
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn keep_shared_prefer_first() { fn keep_shared_value_must_match() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
@ -453,14 +453,14 @@ fn keep_shared_prefer_first() {
dict2 = dict2 =
Dict.empty {} Dict.empty {}
|> Dict.insert 0 100 |> Dict.insert 0 100
|> Dict.insert 2 200 |> Dict.insert 2 2
|> Dict.insert 4 300 |> Dict.insert 4 300
Dict.keepShared dict1 dict2 Dict.keepShared dict1 dict2
|> Dict.values |> Dict.values
"# "#
), ),
RocList::from_slice(&[2, 4]), RocList::from_slice(&[2]),
RocList<i64> RocList<i64>
); );
} }

View file

@ -3869,30 +3869,30 @@ fn condition_polymorphic_num_becomes_float() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn num_count_leading_zero_bits() { fn num_count_leading_zero_bits() {
assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u8"#, 2, usize); assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u8"#, 2, u8);
assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u16"#, 10, usize); assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u16"#, 10, u8);
assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u32"#, 26, usize); assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u32"#, 26, u8);
assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u64"#, 58, usize); assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u64"#, 58, u8);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn num_count_trailing_zero_bits() { fn num_count_trailing_zero_bits() {
assert_evals_to!(r#"Num.countTrailingZeroBits 0b0010_1000u8"#, 3, usize); assert_evals_to!(r#"Num.countTrailingZeroBits 0b0010_1000u8"#, 3, u8);
assert_evals_to!(r#"Num.countTrailingZeroBits 0b0010_0000u16"#, 5, usize); assert_evals_to!(r#"Num.countTrailingZeroBits 0b0010_0000u16"#, 5, u8);
assert_evals_to!(r#"Num.countTrailingZeroBits 0u32"#, 32, usize); assert_evals_to!(r#"Num.countTrailingZeroBits 0u32"#, 32, u8);
assert_evals_to!(r#"Num.countTrailingZeroBits 0b0010_1111u64"#, 0, usize); assert_evals_to!(r#"Num.countTrailingZeroBits 0b0010_1111u64"#, 0, u8);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn num_count_one_bits() { fn num_count_one_bits() {
assert_evals_to!(r#"Num.countOneBits 0b0010_1000u8"#, 2, usize); assert_evals_to!(r#"Num.countOneBits 0b0010_1000u8"#, 2, u8);
assert_evals_to!(r#"Num.countOneBits 0b0010_0000u16"#, 1, usize); assert_evals_to!(r#"Num.countOneBits 0b0010_0000u16"#, 1, u8);
assert_evals_to!(r#"Num.countOneBits 0u32"#, 0, usize); assert_evals_to!(r#"Num.countOneBits 0u32"#, 0, u8);
assert_evals_to!(r#"Num.countOneBits 0b0010_1111u64"#, 5, usize); assert_evals_to!(r#"Num.countOneBits 0b0010_1111u64"#, 5, u8);
} }
#[test] #[test]

View file

@ -120,7 +120,7 @@ fn union() {
|> Set.toList |> Set.toList
"# "#
), ),
RocList::from_slice(&[1, 2, 3, 4]), RocList::from_slice(&[1, 3, 4, 2]),
RocList<i64> RocList<i64>
); );
} }

View file

@ -1,93 +1,46 @@
procedure Dict.1 (Dict.595): procedure Dict.1 (Dict.596):
let Dict.605 : List {[], []} = Array []; let Dict.606 : List {[], []} = Array [];
let Dict.612 : U64 = 0i64; let Dict.35 : List U64 = CallByName Dict.35;
let Dict.613 : U64 = 8i64; let Dict.34 : List I8 = CallByName Dict.34;
let Dict.606 : List U64 = CallByName List.11 Dict.612 Dict.613; let Dict.605 : {List {[], []}, List U64, List I8} = Struct {Dict.606, Dict.35, Dict.34};
let Dict.609 : I8 = CallByName Dict.43; ret Dict.605;
let Dict.610 : U64 = 8i64;
let Dict.607 : List I8 = CallByName List.11 Dict.609 Dict.610; procedure Dict.34 ():
let Dict.608 : U64 = 0i64; let Dict.608 : I8 = CallByName Dict.46;
let Dict.604 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.605, Dict.606, Dict.607, Dict.608}; let Dict.609 : I8 = CallByName Dict.46;
let Dict.610 : I8 = CallByName Dict.46;
let Dict.611 : I8 = CallByName Dict.46;
let Dict.612 : I8 = CallByName Dict.46;
let Dict.613 : I8 = CallByName Dict.46;
let Dict.614 : I8 = CallByName Dict.46;
let Dict.615 : I8 = CallByName Dict.46;
let Dict.607 : List I8 = Array [Dict.608, Dict.609, Dict.610, Dict.611, Dict.612, Dict.613, Dict.614, Dict.615];
ret Dict.607;
procedure Dict.35 ():
let Dict.617 : List U64 = Array [0i64, 0i64, 0i64, 0i64, 0i64, 0i64, 0i64, 0i64];
ret Dict.617;
procedure Dict.4 (Dict.603):
let Dict.114 : List {[], []} = StructAtIndex 0 Dict.603;
let #Derived_gen.1 : List U64 = StructAtIndex 1 Dict.603;
dec #Derived_gen.1;
let #Derived_gen.0 : List I8 = StructAtIndex 2 Dict.603;
dec #Derived_gen.0;
let Dict.604 : U64 = CallByName List.6 Dict.114;
dec Dict.114;
ret Dict.604; ret Dict.604;
procedure Dict.4 (Dict.602): procedure Dict.46 ():
let Dict.108 : U64 = StructAtIndex 3 Dict.602; let Dict.616 : I8 = -128i64;
let #Derived_gen.8 : List {[], []} = StructAtIndex 0 Dict.602; ret Dict.616;
dec #Derived_gen.8;
let #Derived_gen.7 : List U64 = StructAtIndex 1 Dict.602;
dec #Derived_gen.7;
let #Derived_gen.6 : List I8 = StructAtIndex 2 Dict.602;
dec #Derived_gen.6;
ret Dict.108;
procedure Dict.43 (): procedure List.6 (#Attr.2):
let Dict.611 : I8 = -128i64; let List.553 : U64 = lowlevel ListLen #Attr.2;
ret Dict.611;
procedure List.11 (List.133, List.134):
let List.554 : List I8 = CallByName List.68 List.134;
let List.553 : List I8 = CallByName List.86 List.133 List.134 List.554;
ret List.553; ret List.553;
procedure List.11 (List.133, List.134):
let List.566 : List U64 = CallByName List.68 List.134;
let List.565 : List U64 = CallByName List.86 List.133 List.134 List.566;
ret List.565;
procedure List.68 (#Attr.2):
let List.564 : List I8 = lowlevel ListWithCapacity #Attr.2;
ret List.564;
procedure List.68 (#Attr.2):
let List.576 : List U64 = lowlevel ListWithCapacity #Attr.2;
ret List.576;
procedure List.71 (#Attr.2, #Attr.3):
let List.561 : List I8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.561;
procedure List.71 (#Attr.2, #Attr.3):
let List.573 : List U64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.573;
procedure List.86 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2):
joinpoint List.555 List.135 List.136 List.137:
let List.563 : U64 = 0i64;
let List.557 : Int1 = CallByName Num.24 List.136 List.563;
if List.557 then
let List.562 : U64 = 1i64;
let List.559 : U64 = CallByName Num.75 List.136 List.562;
let List.560 : List I8 = CallByName List.71 List.137 List.135;
jump List.555 List.135 List.559 List.560;
else
ret List.137;
in
jump List.555 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2;
procedure List.86 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5):
joinpoint List.567 List.135 List.136 List.137:
let List.575 : U64 = 0i64;
let List.569 : Int1 = CallByName Num.24 List.136 List.575;
if List.569 then
let List.574 : U64 = 1i64;
let List.571 : U64 = CallByName Num.75 List.136 List.574;
let List.572 : List U64 = CallByName List.71 List.137 List.135;
jump List.567 List.135 List.571 List.572;
else
ret List.137;
in
jump List.567 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5;
procedure Num.24 (#Attr.2, #Attr.3):
let Num.294 : Int1 = lowlevel NumGt #Attr.2 #Attr.3;
ret Num.294;
procedure Num.75 (#Attr.2, #Attr.3):
let Num.292 : U64 = lowlevel NumSubWrap #Attr.2 #Attr.3;
ret Num.292;
procedure Test.0 (): procedure Test.0 ():
let Test.3 : {} = Struct {}; let Test.3 : {} = Struct {};
let Test.2 : {List {[], []}, List U64, List I8, U64} = CallByName Dict.1 Test.3; let Test.2 : {List {[], []}, List U64, List I8} = CallByName Dict.1 Test.3;
let Test.1 : U64 = CallByName Dict.4 Test.2; let Test.1 : U64 = CallByName Dict.4 Test.2;
ret Test.1; ret Test.1;

File diff suppressed because it is too large Load diff

View file

@ -3,22 +3,22 @@
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
f = \{} -> f = \{} ->
#^{-1} <2846><117>{} -<120>[[f(1)]]-> <116>[Ok <2854>{}]<80>* #^{-1} <2826><117>{} -<120>[[f(1)]]-> <116>[Ok <2834>{}]<80>*
when g {} is when g {} is
# ^ <2836><2854>{} -<2844>[[g(2)]]-> <72>[Ok <2854>{}]<102>* # ^ <2816><2834>{} -<2824>[[g(2)]]-> <72>[Ok <2834>{}]<102>*
_ -> Ok {} _ -> Ok {}
g = \{} -> g = \{} ->
#^{-1} <2836><2854>{} -<2844>[[g(2)]]-> <72>[Ok <2854>{}]<102>* #^{-1} <2816><2834>{} -<2824>[[g(2)]]-> <72>[Ok <2834>{}]<102>*
when h {} is when h {} is
# ^ <2841><2854>{} -<2849>[[h(3)]]-> <94>[Ok <2854>{}]<124>* # ^ <2821><2834>{} -<2829>[[h(3)]]-> <94>[Ok <2834>{}]<124>*
_ -> Ok {} _ -> Ok {}
h = \{} -> h = \{} ->
#^{-1} <2841><2854>{} -<2849>[[h(3)]]-> <94>[Ok <2854>{}]<124>* #^{-1} <2821><2834>{} -<2829>[[h(3)]]-> <94>[Ok <2834>{}]<124>*
when f {} is when f {} is
# ^ <2846><117>{} -<120>[[f(1)]]-> <116>[Ok <2854>{}]<80>* # ^ <2826><117>{} -<120>[[f(1)]]-> <116>[Ok <2834>{}]<80>*
_ -> Ok {} _ -> Ok {}
main = f {} main = f {}
# ^ <2856><133>{} -<136>[[f(1)]]-> <138>[Ok <2854>{}]<2855>w_a # ^ <2836><133>{} -<136>[[f(1)]]-> <138>[Ok <2834>{}]<2835>w_a