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 {
comptime var f = struct {
fn func(self: T) callconv(.C) usize {
return @as(usize, @clz(self));
fn func(self: T) callconv(.C) u8 {
return @as(u8, @clz(self));
}
}.func;
@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 {
comptime var f = struct {
fn func(self: T) callconv(.C) usize {
return @as(usize, @ctz(self));
fn func(self: T) callconv(.C) u8 {
return @as(u8, @ctz(self));
}
}.func;
@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 {
comptime var f = struct {
fn func(self: T) callconv(.C) usize {
return @as(usize, @popCount(self));
fn func(self: T) callconv(.C) u8 {
return @as(u8, @popCount(self));
}
}.func;
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });

View File

@ -102,7 +102,6 @@ Dict k v := {
metadata : List I8,
dataIndices : List Nat,
data : List (k, v),
size : Nat,
} where k implements Hash & Eq
implements [
Eq {
@ -137,6 +136,9 @@ toInspectorDict = \dict ->
fmt <- Inspect.custom
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.
## ```
## emptyDict = Dict.empty {}
@ -144,12 +146,41 @@ toInspectorDict = \dict ->
empty : {} -> Dict * *
empty = \{} ->
@Dict {
metadata: List.repeat emptySlot 8,
dataIndices: List.repeat 0 8,
metadata: emptyMetadata,
dataIndices: emptyDataIndices,
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.
## ```
## foodDict =
@ -164,14 +195,6 @@ capacity = \@Dict { dataIndices } ->
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.
## ```
## expect
@ -193,8 +216,15 @@ single = \k, v ->
## ```
fromList : List (k, v) -> Dict k v where k implements Hash & Eq
fromList = \data ->
# TODO: make this efficient. Should just set data and then set all indicies in the hashmap.
List.walk data (empty {}) (\dict, (k, v) -> insert dict k v)
# TODO: make more efficient.
# 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.
## ```
@ -207,8 +237,8 @@ fromList = \data ->
## |> Bool.isEq 3
## ```
len : Dict * * -> Nat
len = \@Dict { size } ->
size
len = \@Dict { data } ->
List.len data
## Check if the dictinoary is empty.
## ```
@ -217,8 +247,8 @@ len = \@Dict { size } ->
## Dict.isEmpty (Dict.empty {})
## ```
isEmpty : Dict * * -> Bool
isEmpty = \@Dict { size } ->
size == 0
isEmpty = \@Dict { data } ->
List.isEmpty data
## 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,
# use takeFirst to keep around the capacity.
data: List.takeFirst data 0,
size: 0,
}
## 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)
## ```
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 =
createLowLevelHasher PseudoRandSeed
|> Hash.hash key
@ -441,7 +470,6 @@ insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
metadata,
dataIndices,
data: List.set data dataIndex (key, value),
size,
}
Err NotFound ->
@ -453,7 +481,6 @@ insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
metadata,
dataIndices,
data,
size: Num.addWrap size 1,
}
)
@ -470,7 +497,7 @@ insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
## |> Bool.isEq 0
## ```
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.
hashKey =
createLowLevelHasher PseudoRandSeed
@ -490,13 +517,12 @@ remove = \@Dict { metadata, dataIndices, data, size }, key ->
metadata: List.set metadata index deletedSlot,
dataIndices,
data: List.dropLast data 1,
size: Num.subWrap size 1,
}
else
swapAndUpdateDataIndex (@Dict { metadata, dataIndices, data, size }) index last
swapAndUpdateDataIndex (@Dict { metadata, dataIndices, data }) index last
Err NotFound ->
@Dict { metadata, dataIndices, data, size }
@Dict { metadata, dataIndices, data }
## 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
@ -596,36 +622,49 @@ values = \@Dict { data } ->
## ```
insertAll : Dict k v, Dict k v -> Dict k v where k implements Hash & Eq
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))
## 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
## the same key, the value contained in the first input will be retained,
## and the value in the second input will be removed.
## that are in both dictionaries. Both the key and value must match to be kept.
## ```
## first =
## Dict.single 1 "Keep Me"
## |> Dict.insert 2 "And Me"
## |> Dict.insert 3 "Not this one"
##
## second =
## Dict.single 1 "Keep 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"
##
## 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 = \xs, ys ->
keepShared : Dict k v, Dict k v -> Dict k v where k implements Hash & Eq, v implements Eq
keepShared = \xs0, ys0 ->
(xs1, ys1) =
if len ys0 < len xs0 then
(ys0, xs0)
else
(xs0, ys0)
walk
xs
(empty {})
xs1
(withCapacity (len xs1))
(\state, k, v ->
if contains ys k then
insert state k v
else
state
when get ys1 k is
Ok yv if v == yv ->
insert state k v
_ ->
state
)
## 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)
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
hashKey =
createLowLevelHasher PseudoRandSeed
@ -678,7 +717,6 @@ swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data, size }, removedIn
# Update index of swaped element.
dataIndices: List.set dataIndices index dataIndex,
data: nextData,
size: Num.subWrap size 1,
}
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."
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))
index = nextEmptyOrDeletedHelper metadata probe 0
dataIndex = List.len data
@ -696,7 +734,6 @@ insertNotFoundHelper = \@Dict { metadata, dataIndices, data, size }, key, value,
metadata: List.set metadata index h2Key,
dataIndices: List.set dataIndices index dataIndex,
data: nextData,
size,
}
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.
# 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 { metadata, dataIndices, data, size } ->
maybeRehash = \@Dict { metadata, dataIndices, data } ->
cap = List.len dataIndices
maxLoadCap =
# This is 7/8 * capacity, which is the max load factor.
Num.subWrap cap (Num.shiftRightZfBy cap 3)
if size > maxLoadCap then
rehash (@Dict { metadata, dataIndices, data, size })
if (List.len data + 1) > maxLoadCap then
rehash (@Dict { metadata, dataIndices, data })
else
@Dict { metadata, dataIndices, data, size }
@Dict { metadata, dataIndices, data }
# 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 { metadata, dataIndices, data, size } ->
rehash = \@Dict { metadata, dataIndices, data } ->
newLen = 2 * List.len dataIndices
newDict =
@Dict {
metadata: List.repeat emptySlot newLen,
dataIndices: List.repeat 0 newLen,
data,
size,
}
rehashHelper newDict metadata dataIndices data 0
@ -796,7 +832,7 @@ rehashHelper = \dict, oldMetadata, oldDataIndices, oldData, index ->
dict
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 =
createLowLevelHasher PseudoRandSeed
|> Hash.hash key
@ -810,7 +846,6 @@ insertForRehash = \@Dict { metadata, dataIndices, data, size }, key, dataIndex -
metadata: List.set metadata index h2Key,
dataIndices: List.set dataIndices index dataIndex,
data,
size,
}
emptySlot : I8

View File

@ -1112,7 +1112,7 @@ powInt : Int a, Int a -> Int a
##
## 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.
##
@ -1125,7 +1125,7 @@ countLeadingZeroBits : Int a -> Nat
##
## 8
## ```
countTrailingZeroBits : Int a -> Nat
countTrailingZeroBits : Int a -> U8
## Counts the number of set bits in an integer.
##
@ -1138,7 +1138,7 @@ countTrailingZeroBits : Int a -> Nat
##
## 0
## ```
countOneBits : Int a -> Nat
countOneBits : Int a -> U8
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 ->
initial = @Set (Dict.withCapacity (List.len list))
List.walk list initial insert
list
|> List.map \k -> (k, {})
|> Dict.fromList
|> @Set
## Combine two `Set` collection by keeping the
## [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();
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 => {
// list: RocList,
// alignment: u32,

View File

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

View File

@ -3869,30 +3869,30 @@ fn condition_polymorphic_num_becomes_float() {
}
#[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() {
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);
assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u8"#, 2, u8);
assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u16"#, 10, u8);
assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u32"#, 26, u8);
assert_evals_to!(r#"Num.countLeadingZeroBits 0b0010_1000u64"#, 58, u8);
}
#[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() {
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);
assert_evals_to!(r#"Num.countTrailingZeroBits 0b0010_1000u8"#, 3, u8);
assert_evals_to!(r#"Num.countTrailingZeroBits 0b0010_0000u16"#, 5, u8);
assert_evals_to!(r#"Num.countTrailingZeroBits 0u32"#, 32, u8);
assert_evals_to!(r#"Num.countTrailingZeroBits 0b0010_1111u64"#, 0, u8);
}
#[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() {
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);
assert_evals_to!(r#"Num.countOneBits 0b0010_1000u8"#, 2, u8);
assert_evals_to!(r#"Num.countOneBits 0b0010_0000u16"#, 1, u8);
assert_evals_to!(r#"Num.countOneBits 0u32"#, 0, u8);
assert_evals_to!(r#"Num.countOneBits 0b0010_1111u64"#, 5, u8);
}
#[test]

View File

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

View File

@ -1,93 +1,46 @@
procedure Dict.1 (Dict.595):
let Dict.605 : List {[], []} = Array [];
let Dict.612 : U64 = 0i64;
let Dict.613 : U64 = 8i64;
let Dict.606 : List U64 = CallByName List.11 Dict.612 Dict.613;
let Dict.609 : I8 = CallByName Dict.43;
let Dict.610 : U64 = 8i64;
let Dict.607 : List I8 = CallByName List.11 Dict.609 Dict.610;
let Dict.608 : U64 = 0i64;
let Dict.604 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.605, Dict.606, Dict.607, Dict.608};
procedure Dict.1 (Dict.596):
let Dict.606 : List {[], []} = Array [];
let Dict.35 : List U64 = CallByName Dict.35;
let Dict.34 : List I8 = CallByName Dict.34;
let Dict.605 : {List {[], []}, List U64, List I8} = Struct {Dict.606, Dict.35, Dict.34};
ret Dict.605;
procedure Dict.34 ():
let Dict.608 : I8 = CallByName Dict.46;
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;
procedure Dict.4 (Dict.602):
let Dict.108 : U64 = StructAtIndex 3 Dict.602;
let #Derived_gen.8 : List {[], []} = StructAtIndex 0 Dict.602;
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.46 ():
let Dict.616 : I8 = -128i64;
ret Dict.616;
procedure Dict.43 ():
let Dict.611 : I8 = -128i64;
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;
procedure List.6 (#Attr.2):
let List.553 : U64 = lowlevel ListLen #Attr.2;
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 ():
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;
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"
f = \{} ->
#^{-1} <2846><117>{} -<120>[[f(1)]]-> <116>[Ok <2854>{}]<80>*
#^{-1} <2826><117>{} -<120>[[f(1)]]-> <116>[Ok <2834>{}]<80>*
when g {} is
# ^ <2836><2854>{} -<2844>[[g(2)]]-> <72>[Ok <2854>{}]<102>*
# ^ <2816><2834>{} -<2824>[[g(2)]]-> <72>[Ok <2834>{}]<102>*
_ -> Ok {}
g = \{} ->
#^{-1} <2836><2854>{} -<2844>[[g(2)]]-> <72>[Ok <2854>{}]<102>*
#^{-1} <2816><2834>{} -<2824>[[g(2)]]-> <72>[Ok <2834>{}]<102>*
when h {} is
# ^ <2841><2854>{} -<2849>[[h(3)]]-> <94>[Ok <2854>{}]<124>*
# ^ <2821><2834>{} -<2829>[[h(3)]]-> <94>[Ok <2834>{}]<124>*
_ -> Ok {}
h = \{} ->
#^{-1} <2841><2854>{} -<2849>[[h(3)]]-> <94>[Ok <2854>{}]<124>*
#^{-1} <2821><2834>{} -<2829>[[h(3)]]-> <94>[Ok <2834>{}]<124>*
when f {} is
# ^ <2846><117>{} -<120>[[f(1)]]-> <116>[Ok <2854>{}]<80>*
# ^ <2826><117>{} -<120>[[f(1)]]-> <116>[Ok <2834>{}]<80>*
_ -> Ok {}
main = f {}
# ^ <2856><133>{} -<136>[[f(1)]]-> <138>[Ok <2854>{}]<2855>w_a
# ^ <2836><133>{} -<136>[[f(1)]]-> <138>[Ok <2834>{}]<2835>w_a