From f94d8f9a973c69eccd7bbba5296acb28b39a88ec Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Tue, 13 Oct 2020 17:49:37 +0200 Subject: [PATCH 1/9] one step forward, two steps back --- distribution/std-lib/Base/src/Data/Map.enso | 219 ++++++++++++++++++ distribution/std-lib/Base/src/Main.enso | 2 + .../builtin/interop/generic/EvalNode.java | 56 +++++ .../builtin/interop/generic/ExecuteNode.java | 4 +- .../interpreter/runtime/builtin/Polyglot.java | 1 + test/Benchmarks/src/Collections.enso | 3 + test/Test/src/Data/Map_Spec.enso | 9 + test/Test/src/Main.enso | 2 + 8 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 distribution/std-lib/Base/src/Data/Map.enso create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/EvalNode.java create mode 100644 test/Test/src/Data/Map_Spec.enso diff --git a/distribution/std-lib/Base/src/Data/Map.enso b/distribution/std-lib/Base/src/Data/Map.enso new file mode 100644 index 000000000000..a82a719f4d16 --- /dev/null +++ b/distribution/std-lib/Base/src/Data/Map.enso @@ -0,0 +1,219 @@ +from Base import all + +type No_Value_For_Key key + +type Map + type Tip + type Bin s key value left right + + is_empty : Boolean + is_empty = case this of + Bin _ _ _ _ _ -> False + Tip -> True + + size : Integer + size = case this of + Bin s _ _ _ _ -> s + Tip -> 0 + + to_vector : Vector + to_vector = + builder = Vector.new_builder + to_vector_with_builder m = case m of + Bin _ k v l r -> + to_vector_with_builder l + builder.append [k, v] + to_vector_with_builder r + Unit + Tip -> Unit + to_vector_with_builder this + result = builder.to_vector + result + + map : (Any -> Any) -> Map + map function = case this of + Bin s k v l r -> + Bin s k (function v) (l.map function) (r.map function) + Tip -> Tip + + to_text : Text + to_text = this.to_vector.to_text + + get : Any -> Any + get key = + go map = case map of + Tip -> Error.throw (No_Value_For_Key key) + Bin _ k v l r -> + if k == key then v else + if k > key then go l else go r + result = go this + result + + insert : Any -> Any -> Map + insert key value = case this of + Tip -> Bin 1 key value Tip Tip + Bin s k v l r -> case key < k of + True -> + new_left = l.insert key value + res = here.balance_left k v new_left r + res + False -> if k == key then Bin s k value l r else + new_right = r.insert key value + res = here.balance_right k v l new_right + res + + == : Map -> Boolean + == that = this.to_vector == that.to_vector + +mk_bin k v left right = Bin (1 + left.size + right.size) k v left right + +single_left k v l r = case r of + Bin _ rk rv rl rr -> + res = here.mk_bin rk rv (here.mk_bin k v l rl) rr + res + +single_right k v l r = case l of + Bin _ lk lv ll lr -> + res = here.mk_bin lk lv ll (here.mk_bin k v lr r) + res + +double_left k v l r = case r of + Bin _ rk rv (Bin _ rlk rlv rll rlr) rr -> + new_l = here.mk_bin k v l rll + new_r = here.mk_bin rk rv rlr rr + res = here.mk_bin rlk rlv new_l new_r + res + +double_right k v l r = case l of + Bin _ lk lv ll (Bin _ lrk lrv lrl lrr) -> + new_l = here.mk_bin lk lv ll lrl + new_r = here.mk_bin k v lrr r + res = here.mk_bin lrk lrv new_l new_r + res + +balance_left k v l r = + sl = size l + sr = size r + res = if sl + sr < 2 then here.mk_bin k v l r else + res = if sl <= Weight * sr then here.mk_bin k v l r else + if l.left.size > l.right.size then here.single_right k v l r else + here.double_right k v l r + res + res + +balance_right k v l r = + sl = size l + sr = size r + res = if sl + sr < 2 then here.mk_bin k v l r else + res = if sr <= Weight * sl then here.mk_bin k v l r else + if r.right.size > r.left.size then here.single_left k v l r else + here.double_left k v l r + res + res + + + + + #def delta: 3 + #def ratio: 2 + + #def bin k v l r: + #l' = l + #r' = r + #s = l'.size + r'.size + 1 + #Bin s k v l' r' + + #def singleL: case self of + #Bin s k v l (Bin rs rk rv rl rr): + #nl = Map.bin k v l rl + #Map.bin rk rv nl rr + + #def singleR: case self of + #Bin s k v (Bin ls lk lv ll lr) r: + #nr = Map.bin k v lr r + #Map.bin lk lv ll nr + + #def doubleL: case self of + #Bin s k v l (Bin rs rk rv (Bin rls rlk rlv rll rlr) rr): + #nl = Map.bin k v l rll + #nr = Map.bin rk rv rlr rr + #Map.bin rlk rlv nl nr + + #def doubleR: case self of + #Bin s k v (Bin ls lk lv ll (Bin lrs lrk lrv lrl lrr)) r: + #nl = Map.bin lk lv ll lrl + #nr = Map.bin k v lrr r + #Map.bin lrk lrv nl nr + + #def rotateL: case self of + #Bin _ _ _ _ (Bin _ _ _ l r): + #if l.size < Map.ratio * r.size then self.singleL else self.doubleL + + #def rotateR: case self of + #Bin _ _ _ (Bin _ _ _ l r) _: + #if r.size < Map.ratio * l.size then self.singleR else self.doubleR + + #def balance: case self of + #Bin _ _ _ l r: + #ls = l.size + #rs = r.size + #case ls + rs < 2 of + #True: self + #False: case rs > Map.delta * ls of + #True: self.rotateL + #False: case ls > Map.delta * rs of + #True: self.rotateR + #False: self + + #def balanceL: case self of + #Bin _ _ _ l r: + #ls = l.size + #rs = r.size + #case ls + rs < 2 of + #True: self + #False: case ls > Map.delta * rs of + #True: self.rotateR + #False: self + + #def balanceR: case self of + #Bin _ _ _ l r: + #ls = l.size + #rs = r.size + #case ls + rs < 2 of + #True: self + #False: case rs > Map.delta * ls of + #True: self.rotateL + #False: self + + #def maxView: case self of + #Bin _ k v l r: case r of + #Tip: (k, v, l) + #Bin _ _ _ _ _: + #(km, vm, r') = r.maxView + #res = Map.bin k v l r' . balanceL + #(km, vm, res) + + #def minView: case self of + #Bin _ k v l r: case l of + #Tip: (k, v, r) + #Bin _ _ _ _ _: + #(km, vm, l') = l.minView + #res = Map.bin k v l' r . balanceR + #(km, vm, res) + + #def glue that: case (self, that) of + #(l, Tip): l + #(Tip, r): r + #(Bin ls lk lv ll lr, Bin rs rk rv rl rr): case ls > rs of + #True: + #(km, vm, l') = self . maxView + #Map.bin km vm l' that . balanceR + #False: + #(km, vm, r') = that . minView + #Map.bin km vm self r' . balanceR + +empty : Map +empty = Tip + +weight : Integer +weight = 2 diff --git a/distribution/std-lib/Base/src/Main.enso b/distribution/std-lib/Base/src/Main.enso index e1998bb3d04e..82c37e1304ad 100644 --- a/distribution/std-lib/Base/src/Main.enso +++ b/distribution/std-lib/Base/src/Main.enso @@ -7,11 +7,13 @@ import Base.Meta.Enso_Project import Base.Meta.Meta import Base.Error.Extensions import Base.Polyglot.Java +import Base.Data.Map from Builtins import Unit, Number, Integer, Any, True, False, Cons export Base.Meta.Meta from Builtins export all hiding Meta +export Base.Data.Map from Base.Meta.Enso_Project export all from Base.List export Nil, Cons from Base.Vector export Vector diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/EvalNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/EvalNode.java new file mode 100644 index 000000000000..1f1c973ce4f3 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/EvalNode.java @@ -0,0 +1,56 @@ +package org.enso.interpreter.node.expression.builtin.interop.generic; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.CachedContext; +import com.oracle.truffle.api.dsl.ReportPolymorphism; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.Source; +import org.enso.interpreter.Language; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode; +import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode; +import org.enso.interpreter.runtime.Context; +import org.enso.interpreter.runtime.data.text.Text; + +@BuiltinMethod( + type = "Polyglot", + name = "eval", + description = "Evaluates a foreign language string.") +@ReportPolymorphism +public abstract class EvalNode extends Node { + static final int LIMIT = 10; + + static EvalNode build() { + return EvalNodeGen.create(); + } + + abstract Object execute(Object _this, Text language, Text code); + + @Specialization( + guards = {"cachedLanguage == language", "cachedCode == code"}, + limit = "LIMIT") + Object doCached( + Object _this, + Text language, + Text code, + @CachedContext(Language.class) Context context, + @Cached("language") Text cachedLanguage, + @Cached("code") Text cachedCode, + @Cached("build()") ToJavaStringNode toJavaStringNode, + @Cached("parse(context, cachedLanguage, cachedCode, toJavaStringNode)") CallTarget callTarget, + @Cached("create(callTarget)") DirectCallNode callNode, + @Cached("build()") HostValueToEnsoNode hostValueToEnsoNode) { + return hostValueToEnsoNode.execute(callNode.call()); + } + + CallTarget parse(Context context, Text language, Text code, ToJavaStringNode toJavaStringNode) { + String languageStr = toJavaStringNode.execute(language); + String codeStr = toJavaStringNode.execute(code); + + Source source = Source.newBuilder(languageStr, codeStr, "").build(); + return context.getEnvironment().parsePublic(source, "foo"); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/ExecuteNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/ExecuteNode.java index 70685cbd06a1..4510bd3046ce 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/ExecuteNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/ExecuteNode.java @@ -8,6 +8,7 @@ import com.oracle.truffle.api.profiles.BranchProfile; import org.enso.interpreter.Constants; import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode; import org.enso.interpreter.runtime.data.Array; import org.enso.interpreter.runtime.error.PanicException; @@ -18,11 +19,12 @@ public class ExecuteNode extends Node { private @Child InteropLibrary library = InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private @Child HostValueToEnsoNode hostValueToEnsoNode = HostValueToEnsoNode.build(); private final BranchProfile err = BranchProfile.create(); Object execute(Object _this, Object callable, Array arguments) { try { - return library.execute(callable, arguments.getItems()); + return hostValueToEnsoNode.execute(library.execute(callable, arguments.getItems())); } catch (UnsupportedMessageException | ArityException | UnsupportedTypeException e) { err.enter(); throw new PanicException(e.getMessage(), this); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Polyglot.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Polyglot.java index bc8ccc0ba785..db325667f620 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Polyglot.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Polyglot.java @@ -65,6 +65,7 @@ private void createPolyglot(Language language, ModuleScope scope) { scope.registerMethod(polyglot, "execute", ExecuteMethodGen.makeFunction(language)); scope.registerMethod(polyglot, "invoke", InvokeMethodGen.makeFunction(language)); scope.registerMethod(polyglot, "new", InstantiateMethodGen.makeFunction(language)); + scope.registerMethod(polyglot, "eval", EvalMethodGen.makeFunction(language)); scope.registerMethod(polyglot, "get_member", GetMemberMethodGen.makeFunction(language)); scope.registerMethod(polyglot, "get_members", GetMembersMethodGen.makeFunction(language)); } diff --git a/test/Benchmarks/src/Collections.enso b/test/Benchmarks/src/Collections.enso index f4ef3b806887..852bf2778f21 100644 --- a/test/Benchmarks/src/Collections.enso +++ b/test/Benchmarks/src/Collections.enso @@ -14,11 +14,14 @@ sum_list_meta list = res = folder 0 list res +build_map size = 0.upto size . fold Map.empty (m -> i -> m.insert i i) + main = mil = 1000000 list = here.gen_list mil vec = Vector.new mil (ix -> ix + 1) vec_decimal = Vector.new mil (ix -> ix + 0.0) + Bench_Utils.measure (here.build_map 10000) "build a map" 10 10 Bench_Utils.measure (here.sum_list_meta list) "list meta-fold" 1000 10 Bench_Utils.measure (list.fold 0 (+)) "list fold" 1000 10 Bench_Utils.measure (vec.fold 0 (+)) "vector fold" 1000 10 diff --git a/test/Test/src/Data/Map_Spec.enso b/test/Test/src/Data/Map_Spec.enso new file mode 100644 index 000000000000..b086bde5ca67 --- /dev/null +++ b/test/Test/src/Data/Map_Spec.enso @@ -0,0 +1,9 @@ +from Base import all + +import Base.Test + +spec = describe "Maps" <| + m = 1 . upto 10000 . fold Map.empty (m -> i -> m.insert i i) + IO.println m.size + IO.println (m.get 9999) + diff --git a/test/Test/src/Main.enso b/test/Test/src/Main.enso index 0293722b6cfd..a5e8e22773c7 100644 --- a/test/Test/src/Main.enso +++ b/test/Test/src/Main.enso @@ -8,6 +8,7 @@ import Test.Semantic.Names_Spec import Test.Semantic.Meta_Spec import Test.List_Spec +import Test.Data.Map_Spec import Test.Number_Spec import Test.Process_Spec import Test.Vector.Spec as Vector_Spec @@ -31,3 +32,4 @@ main = Test.Suite.runMain <| Time_Spec.spec File_Spec.spec Meta_Spec.spec + Map_Spec.spec From 60847ca211c522236949522f7a9f05e4e49e8c38 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Fri, 16 Oct 2020 13:17:32 +0200 Subject: [PATCH 2/9] checkpoint --- distribution/std-lib/Base/src/Data/Map.enso | 235 ++++++------------ distribution/std-lib/scratch | 53 ++++ distribution/std-lib/scratch.hs | 53 ++++ .../semantic/RecursionBenchmarks.java | 23 ++ .../bench/fixtures/semantic/MappyMap.scala | 170 +++++++++++++ .../node/controlflow/BooleanBranchNode.java | 19 +- .../controlflow/ConstructorBranchNode.java | 34 ++- .../builtin/InstantiateAtomNode.java | 23 +- .../callable/atom/AtomConstructor.java | 12 +- .../runtime/callable/function/Function.java | 15 +- .../pass/desugar/NestedPatternMatch.scala | 9 +- 11 files changed, 452 insertions(+), 194 deletions(-) create mode 100644 distribution/std-lib/scratch create mode 100644 distribution/std-lib/scratch.hs create mode 100644 engine/runtime/src/bench/scala/org/enso/interpreter/bench/fixtures/semantic/MappyMap.scala diff --git a/distribution/std-lib/Base/src/Data/Map.enso b/distribution/std-lib/Base/src/Data/Map.enso index a82a719f4d16..ba229e4dd275 100644 --- a/distribution/std-lib/Base/src/Data/Map.enso +++ b/distribution/std-lib/Base/src/Data/Map.enso @@ -36,8 +36,8 @@ type Map Bin s k (function v) (l.map function) (r.map function) Tip -> Tip - to_text : Text - to_text = this.to_vector.to_text + #to_text : Text + #to_text = this.to_vector.to_text get : Any -> Any get key = @@ -50,170 +50,83 @@ type Map result insert : Any -> Any -> Map - insert key value = case this of - Tip -> Bin 1 key value Tip Tip - Bin s k v l r -> case key < k of - True -> - new_left = l.insert key value - res = here.balance_left k v new_left r - res - False -> if k == key then Bin s k value l r else - new_right = r.insert key value - res = here.balance_right k v l new_right - res + insert key value = + go map = case map of + Tip -> Bin 1 key value Tip Tip + Bin s k v l r -> + insert_l = + new_left = go l + here.balance_left k v new_left r + insert_r = + new_right = go r + here.balance_right k v l new_right + if key > k then insert_r else + if key == k then Bin s k value l r else + insert_l + go this == : Map -> Boolean == that = this.to_vector == that.to_vector -mk_bin k v left right = Bin (1 + left.size + right.size) k v left right - -single_left k v l r = case r of - Bin _ rk rv rl rr -> - res = here.mk_bin rk rv (here.mk_bin k v l rl) rr - res - -single_right k v l r = case l of - Bin _ lk lv ll lr -> - res = here.mk_bin lk lv ll (here.mk_bin k v lr r) - res - -double_left k v l r = case r of - Bin _ rk rv (Bin _ rlk rlv rll rlr) rr -> - new_l = here.mk_bin k v l rll - new_r = here.mk_bin rk rv rlr rr - res = here.mk_bin rlk rlv new_l new_r - res - -double_right k v l r = case l of - Bin _ lk lv ll (Bin _ lrk lrv lrl lrr) -> - new_l = here.mk_bin lk lv ll lrl - new_r = here.mk_bin k v lrr r - res = here.mk_bin lrk lrv new_l new_r - res - -balance_left k v l r = - sl = size l - sr = size r - res = if sl + sr < 2 then here.mk_bin k v l r else - res = if sl <= Weight * sr then here.mk_bin k v l r else - if l.left.size > l.right.size then here.single_right k v l r else - here.double_right k v l r - res - res - -balance_right k v l r = - sl = size l - sr = size r - res = if sl + sr < 2 then here.mk_bin k v l r else - res = if sr <= Weight * sl then here.mk_bin k v l r else - if r.right.size > r.left.size then here.single_left k v l r else - here.double_left k v l r - res - res - - - - - #def delta: 3 - #def ratio: 2 - - #def bin k v l r: - #l' = l - #r' = r - #s = l'.size + r'.size + 1 - #Bin s k v l' r' - - #def singleL: case self of - #Bin s k v l (Bin rs rk rv rl rr): - #nl = Map.bin k v l rl - #Map.bin rk rv nl rr - - #def singleR: case self of - #Bin s k v (Bin ls lk lv ll lr) r: - #nr = Map.bin k v lr r - #Map.bin lk lv ll nr - - #def doubleL: case self of - #Bin s k v l (Bin rs rk rv (Bin rls rlk rlv rll rlr) rr): - #nl = Map.bin k v l rll - #nr = Map.bin rk rv rlr rr - #Map.bin rlk rlv nl nr - - #def doubleR: case self of - #Bin s k v (Bin ls lk lv ll (Bin lrs lrk lrv lrl lrr)) r: - #nl = Map.bin lk lv ll lrl - #nr = Map.bin k v lrr r - #Map.bin lrk lrv nl nr - - #def rotateL: case self of - #Bin _ _ _ _ (Bin _ _ _ l r): - #if l.size < Map.ratio * r.size then self.singleL else self.doubleL - - #def rotateR: case self of - #Bin _ _ _ (Bin _ _ _ l r) _: - #if r.size < Map.ratio * l.size then self.singleR else self.doubleR - - #def balance: case self of - #Bin _ _ _ l r: - #ls = l.size - #rs = r.size - #case ls + rs < 2 of - #True: self - #False: case rs > Map.delta * ls of - #True: self.rotateL - #False: case ls > Map.delta * rs of - #True: self.rotateR - #False: self - - #def balanceL: case self of - #Bin _ _ _ l r: - #ls = l.size - #rs = r.size - #case ls + rs < 2 of - #True: self - #False: case ls > Map.delta * rs of - #True: self.rotateR - #False: self - - #def balanceR: case self of - #Bin _ _ _ l r: - #ls = l.size - #rs = r.size - #case ls + rs < 2 of - #True: self - #False: case rs > Map.delta * ls of - #True: self.rotateL - #False: self - - #def maxView: case self of - #Bin _ k v l r: case r of - #Tip: (k, v, l) - #Bin _ _ _ _ _: - #(km, vm, r') = r.maxView - #res = Map.bin k v l r' . balanceL - #(km, vm, res) - - #def minView: case self of - #Bin _ k v l r: case l of - #Tip: (k, v, r) - #Bin _ _ _ _ _: - #(km, vm, l') = l.minView - #res = Map.bin k v l' r . balanceR - #(km, vm, res) - - #def glue that: case (self, that) of - #(l, Tip): l - #(Tip, r): r - #(Bin ls lk lv ll lr, Bin rs rk rv rl rr): case ls > rs of - #True: - #(km, vm, l') = self . maxView - #Map.bin km vm l' that . balanceR - #False: - #(km, vm, r') = that . minView - #Map.bin km vm self r' . balanceR +balance_left k x l r = case r of + Tip -> case l of + Tip -> Bin 1 k x Tip Tip + Bin _ _ _ Tip Tip -> Bin 2 k x l Tip + Bin _ lk lx Tip (Bin _ lrk lrx _ _) -> Bin 3 lrk lrx (Bin 1 lk lx Tip Tip) (Bin 1 k x Tip Tip) + Bin _ lk lx ll Tip -> Bin 3 lk lx ll (Bin 1 k x Tip Tip) + Bin ls lk lx ll lr -> case lr of + Bin lrs lrk lrx lrl lrr -> + lls = here.size ll + if lrs < Ratio*lls then Bin 1+ls lk lx ll (Bin 1+lrs k x lr Tip) else + lrls = here.size lrl + lrrs = here.size lrr + Bin 1+ls lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+lrrs k x lrr Tip) + Bin rs _ _ _ _ -> case l of + Tip -> Bin 1+rs k x Tip r + Bin ls lk lx ll lr -> + if ls <= Delta*rs then Bin 1+ls+rs k x l r else + lls = here.size ll + case lr of + Bin lrs lrk lrx lrl lrr -> + if lrs < ratio*lls then Bin 1+ls+rs lk lx ll (Bin 1+rs+lrs) k x lr r) else + lrls = here.size lrl + lrrs = here.size lrr + Bin 1+ls+rs lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+rs+lrrs k x lrr r) + +size m = case m of + Bin s _ _ _ _ -> s + Tip -> 0 + +balance_right k x l r = case l of + Tip -> case r of + Tip -> Bin 1 k x Tip Tip + Bin _ _ _ Tip Tip -> Bin 2 k x Tip r + Bin _ rk rx Tip rr -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr + Bin _ rk rx (Bin _ rlk rlx _ _) Tip -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip) + Bin rs rk rx (Bin rls rlk rlx rll rlr) rr -> case rr of + Bin rrs _ _ _ _ -> + if rls < Ratio*rrs then Bin 1+rs rk rx (Bin 1+rls k x Tip rl) rr else + srll = here.size rll + srlr = here.size rlr + Bin 1+rs rlk rlx (Bin 1+srll k x Tip rll) (Bin 1+rrs+srlr rk rx rlr rr) + Bin ls _ _ _ _ -> case r of + Tip -> Bin 1+ls k x l Tip + Bin rs rk rx rl rr -> + if rs <= Delta*ls then Bin 1+ls+rs k x l r else + case rl of + Bin rls rlk rlx rll rlr -> + rrs = here.size rr + if rls < Ratio*rrs then Bin 1+ls+rs rk rx (Bin 1+ls+rls k x l rl) rr else + rlls = here.size rll + rlrs = here.size rlr + Bin 1+ls+rs rlk rlx (Bin 1+ls+rlls k x l rll) (Bin 1+rrs+rlrs) rk rx rlr rr + empty : Map empty = Tip -weight : Integer -weight = 2 +ratio : Integer +ratio = 2 + +delta : Integer +delta = 3 diff --git a/distribution/std-lib/scratch b/distribution/std-lib/scratch new file mode 100644 index 000000000000..cbe05dd3f419 --- /dev/null +++ b/distribution/std-lib/scratch @@ -0,0 +1,53 @@ +-- Functions balanceL and balanceR are specialised versions of balance. +-- balanceL only checks whether the left subtree is too big, +-- balanceR only checks whether the right subtree is too big. + +-- balanceL is called when left subtree might have been inserted to or when +-- right subtree might have been deleted from. +balanceL :: k -> a -> Map k a -> Map k a -> Map k a +balanceL k x l r = case r of + Tip -> case l of + Tip -> Bin 1 k x Tip Tip + (Bin _ _ _ Tip Tip) -> Bin 2 k x l Tip + (Bin _ lk lx Tip (Bin _ lrk lrx _ _)) -> Bin 3 lrk lrx (Bin 1 lk lx Tip Tip) (Bin 1 k x Tip Tip) + (Bin _ lk lx ll@(Bin _ _ _ _ _) Tip) -> Bin 3 lk lx ll (Bin 1 k x Tip Tip) + (Bin ls lk lx ll@(Bin lls _ _ _ _) lr@(Bin lrs lrk lrx lrl lrr)) + | lrs < ratio*lls -> Bin (1+ls) lk lx ll (Bin (1+lrs) k x lr Tip) + | otherwise -> Bin (1+ls) lrk lrx (Bin (1+lls+size lrl) lk lx ll lrl) (Bin (1+size lrr) k x lrr Tip) + + (Bin rs _ _ _ _) -> case l of + Tip -> Bin (1+rs) k x Tip r + + (Bin ls lk lx ll lr) + | ls > delta*rs -> case (ll, lr) of + (Bin lls _ _ _ _, Bin lrs lrk lrx lrl lrr) + | lrs < ratio*lls -> Bin (1+ls+rs) lk lx ll (Bin (1+rs+lrs) k x lr r) + | otherwise -> Bin (1+ls+rs) lrk lrx (Bin (1+lls+size lrl) lk lx ll lrl) (Bin (1+rs+size lrr) k x lrr r) + (_, _) -> error "Failure in Data.Map.balanceL" + | otherwise -> Bin (1+ls+rs) k x l r +{-# NOINLINE balanceL #-} + +-- balanceR is called when right subtree might have been inserted to or when +-- left subtree might have been deleted from. +balanceR :: k -> a -> Map k a -> Map k a -> Map k a +balanceR k x l r = case l of + Tip -> case r of + Tip -> Bin 1 k x Tip Tip + (Bin _ _ _ Tip Tip) -> Bin 2 k x Tip r + (Bin _ rk rx Tip rr@(Bin _ _ _ _ _)) -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr + (Bin _ rk rx (Bin _ rlk rlx _ _) Tip) -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip) + (Bin rs rk rx rl@(Bin rls rlk rlx rll rlr) rr@(Bin rrs _ _ _ _)) + | rls < ratio*rrs -> Bin (1+rs) rk rx (Bin (1+rls) k x Tip rl) rr + | otherwise -> Bin (1+rs) rlk rlx (Bin (1+size rll) k x Tip rll) (Bin (1+rrs+size rlr) rk rx rlr rr) + + (Bin ls _ _ _ _) -> case r of + Tip -> Bin (1+ls) k x l Tip + + (Bin rs rk rx rl rr) + | rs > delta*ls -> case (rl, rr) of + (Bin rls rlk rlx rll rlr, Bin rrs _ _ _ _) + | rls < ratio*rrs -> Bin (1+ls+rs) rk rx (Bin (1+ls+rls) k x l rl) rr + | otherwise -> Bin (1+ls+rs) rlk rlx (Bin (1+ls+size rll) k x l rll) (Bin (1+rrs+size rlr) rk rx rlr rr) + (_, _) -> error "Failure in Data.Map.balanceR" + | otherwise -> Bin (1+ls+rs) k x l r +{-# NOINLINE balanceR #-} diff --git a/distribution/std-lib/scratch.hs b/distribution/std-lib/scratch.hs new file mode 100644 index 000000000000..cbe05dd3f419 --- /dev/null +++ b/distribution/std-lib/scratch.hs @@ -0,0 +1,53 @@ +-- Functions balanceL and balanceR are specialised versions of balance. +-- balanceL only checks whether the left subtree is too big, +-- balanceR only checks whether the right subtree is too big. + +-- balanceL is called when left subtree might have been inserted to or when +-- right subtree might have been deleted from. +balanceL :: k -> a -> Map k a -> Map k a -> Map k a +balanceL k x l r = case r of + Tip -> case l of + Tip -> Bin 1 k x Tip Tip + (Bin _ _ _ Tip Tip) -> Bin 2 k x l Tip + (Bin _ lk lx Tip (Bin _ lrk lrx _ _)) -> Bin 3 lrk lrx (Bin 1 lk lx Tip Tip) (Bin 1 k x Tip Tip) + (Bin _ lk lx ll@(Bin _ _ _ _ _) Tip) -> Bin 3 lk lx ll (Bin 1 k x Tip Tip) + (Bin ls lk lx ll@(Bin lls _ _ _ _) lr@(Bin lrs lrk lrx lrl lrr)) + | lrs < ratio*lls -> Bin (1+ls) lk lx ll (Bin (1+lrs) k x lr Tip) + | otherwise -> Bin (1+ls) lrk lrx (Bin (1+lls+size lrl) lk lx ll lrl) (Bin (1+size lrr) k x lrr Tip) + + (Bin rs _ _ _ _) -> case l of + Tip -> Bin (1+rs) k x Tip r + + (Bin ls lk lx ll lr) + | ls > delta*rs -> case (ll, lr) of + (Bin lls _ _ _ _, Bin lrs lrk lrx lrl lrr) + | lrs < ratio*lls -> Bin (1+ls+rs) lk lx ll (Bin (1+rs+lrs) k x lr r) + | otherwise -> Bin (1+ls+rs) lrk lrx (Bin (1+lls+size lrl) lk lx ll lrl) (Bin (1+rs+size lrr) k x lrr r) + (_, _) -> error "Failure in Data.Map.balanceL" + | otherwise -> Bin (1+ls+rs) k x l r +{-# NOINLINE balanceL #-} + +-- balanceR is called when right subtree might have been inserted to or when +-- left subtree might have been deleted from. +balanceR :: k -> a -> Map k a -> Map k a -> Map k a +balanceR k x l r = case l of + Tip -> case r of + Tip -> Bin 1 k x Tip Tip + (Bin _ _ _ Tip Tip) -> Bin 2 k x Tip r + (Bin _ rk rx Tip rr@(Bin _ _ _ _ _)) -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr + (Bin _ rk rx (Bin _ rlk rlx _ _) Tip) -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip) + (Bin rs rk rx rl@(Bin rls rlk rlx rll rlr) rr@(Bin rrs _ _ _ _)) + | rls < ratio*rrs -> Bin (1+rs) rk rx (Bin (1+rls) k x Tip rl) rr + | otherwise -> Bin (1+rs) rlk rlx (Bin (1+size rll) k x Tip rll) (Bin (1+rrs+size rlr) rk rx rlr rr) + + (Bin ls _ _ _ _) -> case r of + Tip -> Bin (1+ls) k x l Tip + + (Bin rs rk rx rl rr) + | rs > delta*ls -> case (rl, rr) of + (Bin rls rlk rlx rll rlr, Bin rrs _ _ _ _) + | rls < ratio*rrs -> Bin (1+ls+rs) rk rx (Bin (1+ls+rls) k x l rl) rr + | otherwise -> Bin (1+ls+rs) rlk rlx (Bin (1+ls+size rll) k x l rll) (Bin (1+rrs+size rlr) rk rx rlr rr) + (_, _) -> error "Failure in Data.Map.balanceR" + | otherwise -> Bin (1+ls+rs) k x l r +{-# NOINLINE balanceR #-} diff --git a/engine/runtime/src/bench/java/org/enso/interpreter/bench/benchmarks/semantic/RecursionBenchmarks.java b/engine/runtime/src/bench/java/org/enso/interpreter/bench/benchmarks/semantic/RecursionBenchmarks.java index bd0c777f2a3b..169ed35af4bb 100644 --- a/engine/runtime/src/bench/java/org/enso/interpreter/bench/benchmarks/semantic/RecursionBenchmarks.java +++ b/engine/runtime/src/bench/java/org/enso/interpreter/bench/benchmarks/semantic/RecursionBenchmarks.java @@ -3,6 +3,8 @@ import java.util.ArrayDeque; import java.util.Deque; import java.util.concurrent.TimeUnit; + +import org.enso.interpreter.bench.fixtures.semantic.MappyMap; import org.enso.interpreter.bench.fixtures.semantic.RecursionFixtures; import org.enso.interpreter.test.DefaultInterpreterRunner; import org.openjdk.jmh.annotations.Benchmark; @@ -21,6 +23,27 @@ public class RecursionBenchmarks { private static RecursionFixtures recursionFixtures = new RecursionFixtures(); + static { + MappyMap.Map m = MappyMap.empty(); + for (int j = 0; j < 10; j++) { + m = MappyMap.insert(m, j, j); + } + System.out.println(m); + } + + private MappyMap.Map doMap(int n) { + MappyMap.Map m = MappyMap.empty(); + for (int i = 0; i < n; i++) { + m = MappyMap.insert(m, i, i); + } + return m; + } + + @Benchmark + public void benchMappyMap() { + doMap(10000); + } + private void runOnHundredMillion(DefaultInterpreterRunner.MainMethod main) { main.mainFunction().value().execute(main.mainConstructor(), recursionFixtures.hundredMillion()); } diff --git a/engine/runtime/src/bench/scala/org/enso/interpreter/bench/fixtures/semantic/MappyMap.scala b/engine/runtime/src/bench/scala/org/enso/interpreter/bench/fixtures/semantic/MappyMap.scala new file mode 100644 index 000000000000..8a9906aed6cf --- /dev/null +++ b/engine/runtime/src/bench/scala/org/enso/interpreter/bench/fixtures/semantic/MappyMap.scala @@ -0,0 +1,170 @@ +package org.enso.interpreter.bench.fixtures.semantic + +object MappyMap { + sealed trait Map + case object Tip extends Map + case class Bin( + fields: Array[Object] + ) //size: Object, key: Object, value: Object, left: Map, right: Map) + extends Map + + object Bin { + def apply( + size: Object, + key: Object, + value: Object, + left: Map, + right: Map + ): Bin = { + Bin(Array(size, key, value, left, right)) + } + object X { + def unapply(arg: Bin): Option[(Object, Object, Object, Map, Map)] = { + val fs = arg.fields + Some( + ( + fs(0), + fs(1), + fs(2), + fs(3).asInstanceOf[Map], + fs(4).asInstanceOf[Map] + ) + ) + } + } + } +// def mk(): MappyMap.type = { +// println() +// } + + val x = Bin("Foo", "Bar", "Baz", Tip, Tip) + + def insert(m: Map, key: Object, value: Object): Map = + m match { + case Tip => Bin(1.asInstanceOf[Object], key, value, Tip, Tip) + case Bin.X(s, k, v, l, r) => + val keyI = key.asInstanceOf[Int] + val kI = k.asInstanceOf[Int] + if (keyI > kI) { + balanceR(k, v, l, insert(r, key, value)) + } else if (keyI == kI) { + Bin(s, k, value, l, r) + } else { + balanceL(k, v, insert(l, key, value), r) + } + } + + def size(m: Map): Int = + m match { + case Tip => 0 + case Bin.X(s, _, _, _, _) => s.asInstanceOf[Int] + } + + def empty: Map = Tip + + def balanceL(k: Object, v: Object, left: Map, right: Map): Map = ??? + + def balanceR(k: Object, x: Object, l: Map, r: Map): Map = { + l match { + case Tip => + r match { + + case Tip => Bin(1.asInstanceOf[Object], k, x, Tip, Tip) + case Bin.X(_, _, _, Tip, Tip) => + Bin(2.asInstanceOf[Object], k, x, Tip, r) + case Bin.X(_, rk, rx, Tip, rr @ Bin.X(_, _, _, _, _)) => + Bin( + 3.asInstanceOf[Object], + rk, + rx, + Bin(1.asInstanceOf[Object], k, x, Tip, Tip), + rr + ) + case Bin.X(_, rk, rx, Bin.X(_, rlk, rlx, _, _), Tip) => + Bin( + 3.asInstanceOf[Object], + rlk, + rlx, + Bin(1.asInstanceOf[Object], k, x, Tip, Tip), + Bin(1.asInstanceOf[Object], rk, rx, Tip, Tip) + ) + case Bin.X( + rsX, + rk, + rx, + rl @ Bin.X(rlsX, rlk, rlx, rll, rlr), + rr @ Bin.X(rrsX, _, _, _, _) + ) => + val rs: Int = rsX.asInstanceOf[Int] + val rls: Int = rlsX.asInstanceOf[Int] + val rrs: Int = rrsX.asInstanceOf[Int] + if (rls < ratio * rrs) { + Bin( + (1 + rs).asInstanceOf[Object], + rk, + rx, + Bin((1 + rls).asInstanceOf[Object], k, x, Tip, rl), + rr + ) + } else { + Bin( + (1 + rs).asInstanceOf[Object], + rlk, + rlx, + Bin((1 + size(rll)).asInstanceOf[Object], k, x, Tip, rll), + Bin((1 + rrs + size(rlr)).asInstanceOf[Object], rk, rx, rlr, rr) + ) + } + } + case Bin.X(lsX, _, _, _, _) => + val ls: Int = lsX.asInstanceOf[Int] + r match { + case Tip => Bin((1 + ls).asInstanceOf[Object], k, x, l, Tip) + case Bin.X(rsX, rk, rx, rl, rr) => + val rs: Int = rsX.asInstanceOf[Int] + if (rs > delta * ls) { + (rl, rr) match { + case (Bin.X(rlsX, rlk, rlx, rll, rlr), Bin.X(rrsX, _, _, _, _)) => + val rls = rlsX.asInstanceOf[Int] + val rrs = rrsX.asInstanceOf[Int] + if (rls < ratio * rrs) { + Bin( + (1 + ls + rs).asInstanceOf[Object], + rk, + rx, + Bin((1 + ls + rls).asInstanceOf[Object], k, x, l, rl), + rr + ) + } else { + Bin( + (1 + ls + rs).asInstanceOf[Object], + rlk, + rlx, + Bin( + (1 + ls + size(rll)).asInstanceOf[Object], + k, + x, + l, + rll + ), + Bin( + (1 + rrs + size(rlr)).asInstanceOf[Object], + rk, + rx, + rlr, + rr + ) + ) + } + case _ => ??? + } + } else { + Bin((1 + ls + rs).asInstanceOf[Object], k, x, l, r) + } + } + } + } + + val delta: Int = 3 + val ratio: Int = 2 +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java index 409421c21e27..77030ea13ad1 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java @@ -4,6 +4,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.FrameUtil; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.profiles.ConditionProfile; import org.enso.interpreter.node.ExpressionNode; @@ -13,19 +14,21 @@ import org.enso.interpreter.runtime.callable.atom.Atom; import org.enso.interpreter.runtime.callable.atom.AtomConstructor; import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.state.Stateful; import org.enso.interpreter.runtime.type.TypesGen; /** An implementation of the case expression specialised to working on booleans. */ @NodeInfo(shortName = "BooleanMatch") public abstract class BooleanBranchNode extends BranchNode { private final boolean matched; - private @Child ExpressionNode branch; - private @Child ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create(); + // private @Child ExpressionNode branch; + // private @Child ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create(); private final ConditionProfile profile = ConditionProfile.createCountingProfile(); + private @Child DirectCallNode callNode; BooleanBranchNode(boolean matched, CreateFunctionNode branch) { this.matched = matched; - this.branch = branch; + this.callNode = DirectCallNode.create(branch.getCallTarget()); } /** @@ -49,11 +52,13 @@ public static BooleanBranchNode build(boolean matched, CreateFunctionNode branch public void doAtom(VirtualFrame frame, boolean target) { Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot()); if (profile.profile(matched == target)) { - Function function = TypesGen.asFunction(branch.executeGeneric(frame)); - + Stateful result = + (Stateful) + callNode.call( + Function.ArgumentsHelper.buildArguments( + frame.materialize(), null, state, new Object[0])); // Note [Caller Info For Case Branches] - throw new BranchSelectedException( - executeCallNode.executeCall(function, null, state, new Object[0])); + throw new BranchSelectedException(result); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/ConstructorBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/ConstructorBranchNode.java index d3779c3cad9b..ead6755edec3 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/ConstructorBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/ConstructorBranchNode.java @@ -1,11 +1,15 @@ package org.enso.interpreter.node.controlflow; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.FrameUtil; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.profiles.ConditionProfile; +import org.enso.compiler.Compiler; import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.callable.ExecuteCallNode; import org.enso.interpreter.node.callable.ExecuteCallNodeGen; @@ -13,19 +17,21 @@ import org.enso.interpreter.runtime.callable.atom.Atom; import org.enso.interpreter.runtime.callable.atom.AtomConstructor; import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.state.Stateful; import org.enso.interpreter.runtime.type.TypesGen; /** An implementation of the case expression specialised to working on constructors. */ @NodeInfo(shortName = "ConstructorMatch") public abstract class ConstructorBranchNode extends BranchNode { private final AtomConstructor matcher; - private @Child ExpressionNode branch; - private @Child ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create(); + // private @Child ExpressionNode branch; + // private @Child ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create(); + private @Child DirectCallNode callNode; private final ConditionProfile profile = ConditionProfile.createCountingProfile(); ConstructorBranchNode(AtomConstructor matcher, CreateFunctionNode branch) { this.matcher = matcher; - this.branch = branch; + this.callNode = DirectCallNode.create(branch.getCallTarget()); } /** @@ -39,6 +45,16 @@ public static ConstructorBranchNode build(AtomConstructor matcher, CreateFunctio return ConstructorBranchNodeGen.create(matcher, branch); } + @ExplodeLoop + private Object[] copyArgs(Object[] fields) { + Object[] res = new Object[matcher.getArity()]; +// CompilerDirectives.ensureVirtualized(res); + for (int i = 0; i < matcher.getArity(); i++) { + res[i] = fields[i]; + } + return res; + } + /** * Handles the atom scrutinee case. * @@ -52,11 +68,17 @@ public static ConstructorBranchNode build(AtomConstructor matcher, CreateFunctio public void doAtom(VirtualFrame frame, Atom target) { Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot()); if (profile.profile(matcher == target.getConstructor())) { - Function function = TypesGen.asFunction(branch.executeGeneric(frame)); + // Function function = TypesGen.asFunction(branch.executeGeneric(frame)); + +// Object[] args = copyArgs(target.getFields()); + Stateful result = + (Stateful) + callNode.call( + Function.ArgumentsHelper.buildArguments( + frame.materialize(), null, state, target.getFields())); // Note [Caller Info For Case Branches] - throw new BranchSelectedException( - executeCallNode.executeCall(function, null, state, target.getFields())); + throw new BranchSelectedException(result); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java index ab3382e85e95..f0e1e5d991e9 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java @@ -13,16 +13,17 @@ /** This node represents the process of instantiating an atom at runtime. */ @NodeInfo(shortName = "constructor::", description = "An atom instantiation at runtime.") public class InstantiateAtomNode extends RootNode { - private @Node.Child ExpressionNode instantiator; + private final AtomConstructor constructor; // instantiator; private final String name; - private InstantiateAtomNode(Language language, String name, ExpressionNode instantiator) { + private InstantiateAtomNode(Language language, String name, AtomConstructor constructor) { super(language); this.name = name; - this.instantiator = instantiator; + this.constructor = constructor; } - /** Executes this node. + /** + * Executes this node. * * @param frame the language frame being executed * @return the result of executing this node @@ -31,10 +32,13 @@ private InstantiateAtomNode(Language language, String name, ExpressionNode insta public Stateful execute(VirtualFrame frame) { return new Stateful( Function.ArgumentsHelper.getState(frame.getArguments()), - instantiator.executeGeneric(frame)); + constructor.newInstance( + Function.ArgumentsHelper.getPositionalArguments( + frame.getArguments()))); // .executeGeneric(frame)); } - /** Returns a string representation of this node. + /** + * Returns a string representation of this node. * * @return a string representation of this node */ @@ -43,7 +47,8 @@ public String getName() { return "constructor::" + name; } - /** Creates an instance of this node. + /** + * Creates an instance of this node. * * @param language the language for which the node is created * @param name the name of the atom being instantated @@ -51,7 +56,7 @@ public String getName() { * @return an instance of this node */ public static InstantiateAtomNode build( - Language language, String name, ExpressionNode instantiator) { - return new InstantiateAtomNode(language, name, instantiator); + Language language, String name, AtomConstructor constructor) { + return new InstantiateAtomNode(language, name, constructor); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/AtomConstructor.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/AtomConstructor.java index a09b6d13b8e1..9ff63e00d7fc 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/AtomConstructor.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/AtomConstructor.java @@ -68,12 +68,12 @@ public AtomConstructor initializeFields(ArgumentDefinition... args) { * {@link AtomConstructor} */ private Function buildConstructorFunction(ArgumentDefinition[] args) { - ExpressionNode[] argumentReaders = new ExpressionNode[args.length]; - for (int i = 0; i < args.length; i++) { - argumentReaders[i] = ReadArgumentNode.build(i, args[i].getDefaultValue().orElse(null)); - } - ExpressionNode instantiateNode = InstantiateNode.build(this, argumentReaders); - RootNode rootNode = InstantiateAtomNode.build(null, name, instantiateNode); +// ExpressionNode[] argumentReaders = new ExpressionNode[args.length]; +// for (int i = 0; i < args.length; i++) { +// argumentReaders[i] = ReadArgumentNode.build(i, args[i].getDefaultValue().orElse(null)); +// } +// ExpressionNode instantiateNode = InstantiateNode.build(this, argumentReaders); + RootNode rootNode = InstantiateAtomNode.build(null, name, this); RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode); return new Function(callTarget, null, new FunctionSchema(args)); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java index 20bf7e1a1bb6..3e7c02c86996 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java @@ -8,6 +8,7 @@ import com.oracle.truffle.api.dsl.CachedContext; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.ArityException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.TruffleObject; @@ -83,8 +84,7 @@ public Function(RootCallTarget callTarget, MaterializedFrame scope, FunctionSche * @param args argument definitons * @return a Function object with specified behavior and arguments */ - public static Function fromBuiltinRootNode( - BuiltinRootNode node, ArgumentDefinition... args) { + public static Function fromBuiltinRootNode(BuiltinRootNode node, ArgumentDefinition... args) { RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node); FunctionSchema schema = new FunctionSchema(args); return new Function(callTarget, null, schema); @@ -103,8 +103,7 @@ public static Function fromBuiltinRootNode( public static Function fromBuiltinRootNodeWithCallerFrameAccess( BuiltinRootNode node, ArgumentDefinition... args) { RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node); - FunctionSchema schema = - new FunctionSchema(FunctionSchema.CallerFrameAccess.FULL, args); + FunctionSchema schema = new FunctionSchema(FunctionSchema.CallerFrameAccess.FULL, args); return new Function(callTarget, null, schema); } @@ -288,6 +287,14 @@ public static Object[] buildArguments( return new Object[] {function.getScope(), callerInfo, state, positionalArguments}; } + public static Object[] buildArguments( + MaterializedFrame frame, + CallerInfo callerInfo, + Object state, + Object[] positionalArguments) { + return new Object[] {frame, callerInfo, state, positionalArguments}; + } + /** * Generates an array of arguments using the schema to be passed to a call target. * diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala index 7480cb49a6f3..d60b6c640573 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala @@ -157,7 +157,14 @@ case object NestedPatternMatch extends IRPass { freshNameSupply: FreshNameSupply ): IR.Expression = { expr.transformExpressions { - case cse: IR.Case => desugarCase(cse, freshNameSupply) + case cse: IR.Case => + val r = desugarCase(cse, freshNameSupply) + println("Result of case desugaring: ") + println("BEFORE ") + println(cse.showCode()) + println("AFTER ") + println(r.showCode()) + r } } From d0731106d6aa9a2cdaac3f1651eb65c1867aa897 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Fri, 16 Oct 2020 16:57:29 +0200 Subject: [PATCH 3/9] random checkpoint --- .../node/callable/InvokeCallableNode.java | 10 ++- .../callable/thunk/ThunkExecutorNode.java | 7 +- .../node/controlflow/BooleanBranchNode.java | 5 +- .../node/controlflow/BranchNode.java | 3 +- .../node/controlflow/CaseNode.java | 17 +++- .../node/controlflow/CatchAllBranchNode.java | 24 +++--- .../controlflow/ConstructorBranchNode.java | 5 +- .../main/scala/org/enso/compiler/Passes.scala | 1 + .../enso/compiler/codegen/IrToTruffle.scala | 23 ++++-- .../pass/analyse/TailPatternMatch.scala | 81 +++++++++++++++++++ .../pass/desugar/NestedPatternMatch.scala | 10 +-- 11 files changed, 149 insertions(+), 37 deletions(-) create mode 100644 engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailPatternMatch.scala diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java index cd4e448ea931..e7560cf13aea 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java @@ -185,10 +185,12 @@ public Stateful invokeDynamicSymbol( lock.unlock(); } } - Stateful selfResult = - thisExecutor.executeThunk((Thunk) selfArgument, state, TailStatus.NOT_TAIL); - selfArgument = selfResult.getValue(); - state = selfResult.getState(); + if (selfArgument instanceof Thunk) { + Stateful selfResult = + thisExecutor.executeThunk((Thunk) selfArgument, state, TailStatus.NOT_TAIL); + state = selfResult.getState(); + selfArgument = selfResult.getValue(); + } arguments[thisArgumentPosition] = selfArgument; } Function function = methodResolverNode.execute(symbol, selfArgument); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java index fa812a4d8de4..3f1132768a48 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java @@ -37,7 +37,7 @@ public static ThunkExecutorNode build() { * @param isTail is the execution happening in a tail-call position * @return the return value of this thunk */ - public abstract Stateful executeThunk(Thunk thunk, Object state, BaseNode.TailStatus isTail); + public abstract Stateful executeThunk(Object thunk, Object state, BaseNode.TailStatus isTail); @Specialization( guards = "callNode.getCallTarget() == thunk.getCallTarget()", @@ -83,4 +83,9 @@ Stateful doUncached( } } } + + @Fallback + Stateful doOther(Object thunk, Object state, BaseNode.TailStatus isTail) { + return new Stateful(state, thunk); + } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java index 77030ea13ad1..4158e573dca7 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java @@ -49,8 +49,7 @@ public static BooleanBranchNode build(boolean matched, CreateFunctionNode branch * @param target the atom to destructure */ @Specialization - public void doAtom(VirtualFrame frame, boolean target) { - Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot()); + public void doAtom(VirtualFrame frame, Object state, boolean target) { if (profile.profile(matched == target)) { Stateful result = (Stateful) @@ -69,7 +68,7 @@ public void doAtom(VirtualFrame frame, boolean target) { * @param target the object to execute on */ @Fallback - public void doFallback(VirtualFrame frame, Object target) {} + public void doFallback(VirtualFrame frame, Object state, Object target) {} /* Note [Caller Info For Case Branches] * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BranchNode.java index 60bf80a31e6f..44c637b950be 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BranchNode.java @@ -17,5 +17,6 @@ public abstract class BranchNode extends BaseNode { * @param frame the stack frame in which to execute * @param target the object to match against */ - public abstract void execute(VirtualFrame frame, Object target); + public abstract void execute(VirtualFrame frame, Object state, Object target); + } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CaseNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CaseNode.java index d8ef3d0a906b..d7d521a80c2d 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CaseNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CaseNode.java @@ -6,6 +6,7 @@ import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.FrameUtil; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.NodeInfo; @@ -13,6 +14,7 @@ import org.enso.interpreter.Language; import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.runtime.Context; +import org.enso.interpreter.runtime.callable.atom.Atom; import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.RuntimeError; import org.enso.interpreter.runtime.type.TypesGen; @@ -28,11 +30,13 @@ @NodeInfo(shortName = "case_of", description = "The runtime representation of a case expression.") public abstract class CaseNode extends ExpressionNode { + private final boolean isTailCase; @Children private final BranchNode[] cases; private final BranchProfile typeErrorProfile = BranchProfile.create(); - CaseNode(BranchNode[] cases) { + CaseNode(BranchNode[] cases, boolean isTailCase) { this.cases = cases; + this.isTailCase = isTailCase; } /** @@ -42,8 +46,8 @@ public abstract class CaseNode extends ExpressionNode { * @param cases the case branches * @return a node representing a pattern match */ - public static CaseNode build(ExpressionNode scrutinee, BranchNode[] cases) { - return CaseNodeGen.create(cases, scrutinee); + public static CaseNode build(ExpressionNode scrutinee, BranchNode[] cases, boolean isTailCase) { + return CaseNodeGen.create(cases, isTailCase, scrutinee); } /** @@ -74,9 +78,10 @@ public Object doMatch( VirtualFrame frame, Object object, @CachedContext(Language.class) TruffleLanguage.ContextReference ctx) { + Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot()); try { for (BranchNode branchNode : cases) { - branchNode.execute(frame, object); + branchNode.execute(frame, state, object); } CompilerDirectives.transferToInterpreter(); throw new PanicException( @@ -105,4 +110,8 @@ boolean isError(Object error) { * The main alternative to this was desugaring to a nested-if, which would've been significantly * harder to maintain, and also resulted in significantly higher code complexity. */ + + public boolean isTailCase() { + return isTailCase; + } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CatchAllBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CatchAllBranchNode.java index 73e29d1a8518..14e711ea8fca 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CatchAllBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CatchAllBranchNode.java @@ -2,14 +2,17 @@ import com.oracle.truffle.api.frame.FrameUtil; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.UnexpectedResultException; +import com.oracle.truffle.api.profiles.ConditionProfile; import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.callable.ExecuteCallNode; import org.enso.interpreter.node.callable.ExecuteCallNodeGen; import org.enso.interpreter.node.callable.function.CreateFunctionNode; import org.enso.interpreter.runtime.callable.atom.Atom; import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.state.Stateful; import org.enso.interpreter.runtime.type.TypesGen; /** @@ -20,11 +23,13 @@ shortName = "Catch_All", description = "An explicit catch-all branch in a case expression") public class CatchAllBranchNode extends BranchNode { - @Child private ExpressionNode functionNode; - @Child private ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create(); +// @Child private ExpressionNode functionNode; + // @Child private ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create(); + private @Child DirectCallNode callNode; + private final ConditionProfile profile = ConditionProfile.createCountingProfile(); private CatchAllBranchNode(CreateFunctionNode functionNode) { - this.functionNode = functionNode; + this.callNode = DirectCallNode.create(functionNode.getCallTarget()); } /** @@ -43,13 +48,14 @@ public static CatchAllBranchNode build(CreateFunctionNode functionNode) { * @param frame the stack frame in which to execute * @param target the object to match against */ - public void execute(VirtualFrame frame, Object target) { + public void execute(VirtualFrame frame, Object state, Object target) { // Note [Safe Casting to Function in Catch All Branches] - Function function = TypesGen.asFunction(functionNode.executeGeneric(frame)); - Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot()); - throw new BranchSelectedException( - // Note [Caller Info For Case Branches] - executeCallNode.executeCall(function, null, state, new Object[] {target})); + Stateful result = + (Stateful) + callNode.call( + Function.ArgumentsHelper.buildArguments( + frame.materialize(), null, state, new Object[] {target})); + throw new BranchSelectedException(result); } /* Note [Safe Casting to Function in Catch All Branches] diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/ConstructorBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/ConstructorBranchNode.java index ead6755edec3..6fd425549f3b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/ConstructorBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/ConstructorBranchNode.java @@ -65,8 +65,7 @@ private Object[] copyArgs(Object[] fields) { * @param target the atom to destructure */ @Specialization - public void doAtom(VirtualFrame frame, Atom target) { - Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot()); + public void doAtom(VirtualFrame frame, Object state, Atom target) { if (profile.profile(matcher == target.getConstructor())) { // Function function = TypesGen.asFunction(branch.executeGeneric(frame)); @@ -89,7 +88,7 @@ public void doAtom(VirtualFrame frame, Atom target) { * @param target the object to execute on */ @Fallback - public void doFallback(VirtualFrame frame, Object target) {} + public void doFallback(VirtualFrame frame, Object state, Object target) {} /* Note [Caller Info For Case Branches] * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/engine/runtime/src/main/scala/org/enso/compiler/Passes.scala b/engine/runtime/src/main/scala/org/enso/compiler/Passes.scala index 1cafacdfab5d..6340ad9e8e25 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/Passes.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/Passes.scala @@ -40,6 +40,7 @@ class Passes(passes: Option[List[PassGroup]] = None) { ShadowedPatternFields, UnreachableMatchBranches, NestedPatternMatch, + TailPatternMatch, IgnoredBindings, TypeFunctions, TypeSignatures, diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index 70c3e1495e91..77ea56e5c194 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -15,7 +15,8 @@ import org.enso.compiler.pass.analyse.{ AliasAnalysis, BindingAnalysis, DataflowAnalysis, - TailCall + TailCall, + TailPatternMatch } import org.enso.compiler.pass.optimise.ApplicationSaturation import org.enso.compiler.pass.resolve.{ @@ -596,7 +597,11 @@ class IrToTruffle( .toArray[BranchNode] // Note [Pattern Match Fallbacks] - val matchExpr = CaseNode.build(scrutineeNode, cases) + val matchExpr = CaseNode.build( + scrutineeNode, + cases, + caseExpr.getMetadata(TailPatternMatch).isDefined + ) setLocation(matchExpr, location) } else { val invalidBranches = maybeCases.collect { @@ -1170,11 +1175,15 @@ class IrToTruffle( ) .unsafeAs[AliasAnalysis.Info.Scope.Child] - val shouldSuspend = shouldBeSuspended.getOrElse( - throw new CompilerError( - "Demand analysis information missing from call argument." - ) - ) + val shouldSuspend = value match { + case _: IR.Name => false + case _ => + shouldBeSuspended.getOrElse( + throw new CompilerError( + "Demand analysis information missing from call argument." + ) + ) + } val childScope = if (shouldSuspend) { scope.createChild(scopeInfo.scope) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailPatternMatch.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailPatternMatch.scala new file mode 100644 index 000000000000..14c45256403c --- /dev/null +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailPatternMatch.scala @@ -0,0 +1,81 @@ +package org.enso.compiler.pass.analyse + +import org.enso.compiler.context.{InlineContext, ModuleContext} +import org.enso.compiler.core.IR +import org.enso.compiler.core.ir.MetadataStorage.ToPair +import org.enso.compiler.pass.IRPass + +import scala.annotation.unused + +case object TailPatternMatch extends IRPass { + case object TailMatch extends IRPass.Metadata { + + /** The name of the metadata as a string. */ + override val metadataName: String = "TailMatch" + + override def duplicate(): Option[IRPass.Metadata] = Some(this) + } + + override type Metadata = TailMatch.type + override type Config = IRPass.Configuration.Default + + override val precursorPasses: Seq[IRPass] = List() + override val invalidatedPasses: Seq[IRPass] = List() + + /** Desugars nested pattern matches in a module. + * + * @param ir the Enso IR to process + * @param moduleContext a context object that contains the information needed + * to process a module + * @return `ir`, possibly having made transformations or annotations to that + * IR. + */ + override def runModule( + ir: IR.Module, + @unused moduleContext: ModuleContext + ): IR.Module = { + + ir.mapExpressions(analyseExpression) + } + + /** Desugars nested pattern matches in an expression. + * + * @param ir the Enso IR to process + * @param inlineContext a context object that contains the information needed + * for inline evaluation + * @return `ir`, possibly having made transformations or annotations to that + * IR. + */ + override def runExpression( + ir: IR.Expression, + @unused inlineContext: InlineContext + ): IR.Expression = { + analyseExpression(ir) + } + + private def analyseExpression(expr: IR.Expression): IR.Expression = + expr.transformExpressions { + case cse: IR.Case.Expr => + cse.copy(branches = cse.branches.map(analyseBranch)) + } + + private def analyseBranch(branch: IR.Case.Branch): IR.Case.Branch = { + val expr = branch.expression match { + case caseExpr: IR.Case.Expr => + caseExpr + .updateMetadata(this -->> TailMatch) + .copy(branches = caseExpr.branches.map(analyseBranch)) + case block @ IR.Expression.Block(exprs, ret, _, _, _, _) => + val newRet = ret match { + case cs: IR.Case.Expr => + cs.updateMetadata(this -->> TailMatch) + .copy(branches = cs.branches.map(analyseBranch)) + case expr => analyseExpression(expr) + } + val newExprs = exprs.map(analyseExpression) + block.copy(expressions = newExprs, returnValue = newRet) + case expr => analyseExpression(expr) + } + branch.copy(expression = expr) + } +} diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala index d60b6c640573..426bdf985012 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala @@ -159,11 +159,11 @@ case object NestedPatternMatch extends IRPass { expr.transformExpressions { case cse: IR.Case => val r = desugarCase(cse, freshNameSupply) - println("Result of case desugaring: ") - println("BEFORE ") - println(cse.showCode()) - println("AFTER ") - println(r.showCode()) +// println("Result of case desugaring: ") +// println("BEFORE ") +// println(cse.showCode()) +// println("AFTER ") +// println(r.showCode()) r } } From 3d78a94ed33021fd5e81f6dbb49b3b625d9ce44d Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Mon, 19 Oct 2020 12:48:05 +0200 Subject: [PATCH 4/9] foo --- distribution/bin/enso | 2 +- distribution/std-lib/Base/src/Data/Map.enso | 106 +++++++++++++++--- .../callable/IndirectInvokeCallableNode.java | 2 +- .../node/callable/InvokeCallableNode.java | 9 +- .../callable/argument/ArgumentSorterNode.java | 9 +- .../callable/thunk/ThunkExecutorNode.java | 15 ++- .../builtin/bool/IfThenElseNode.java | 7 +- .../expression/builtin/bool/IfThenNode.java | 5 +- .../builtin/error/CatchPanicNode.java | 5 +- .../builtin/function/ApplicationOperator.java | 3 +- .../builtin/resource/BracketNode.java | 5 +- .../builtin/runtime/NoInlineNode.java | 3 +- .../builtin/state/RunStateNode.java | 21 ++-- .../thread/WithInterruptHandlerNode.java | 3 +- .../org/enso/interpreter/dsl/Suspend.java | 11 ++ .../dsl/model/MethodDefinition.java | 22 +++- test/Benchmarks/src/Collections.enso | 54 +++++++-- test/Benchmarks/src/Main.enso | 43 ++++--- 18 files changed, 243 insertions(+), 82 deletions(-) create mode 100644 lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/Suspend.java diff --git a/distribution/bin/enso b/distribution/bin/enso index fe7f14afc3ea..2c34c242061b 100755 --- a/distribution/bin/enso +++ b/distribution/bin/enso @@ -1,3 +1,3 @@ COMP_PATH=$(dirname "$0")/../component -exec java -jar -Dtruffle.class.path.append="$COMP_PATH/runtime.jar" -Dpolyglot.engine.IterativePartialEscape=true $JAVA_OPTS $COMP_PATH/runner.jar "$@" +exec java -jar -Dtruffle.class.path.append="$COMP_PATH/runtime.jar" $JAVA_OPTS $COMP_PATH/runner.jar "$@" exit diff --git a/distribution/std-lib/Base/src/Data/Map.enso b/distribution/std-lib/Base/src/Data/Map.enso index ba229e4dd275..18891c8c1bbb 100644 --- a/distribution/std-lib/Base/src/Data/Map.enso +++ b/distribution/std-lib/Base/src/Data/Map.enso @@ -49,25 +49,71 @@ type Map result = go this result - insert : Any -> Any -> Map - insert key value = - go map = case map of - Tip -> Bin 1 key value Tip Tip - Bin s k v l r -> - insert_l = - new_left = go l - here.balance_left k v new_left r - insert_r = - new_right = go r - here.balance_right k v l new_right - if key > k then insert_r else - if key == k then Bin s k value l r else - insert_l - go this + #insert : Any -> Any -> Map + #insert key value = + #go map = case map of + #Tip -> Bin 1 key value Tip Tip + #Bin s k v l r -> + #insert_l = + #new_left = go l + #@Tail_Call here.balance_left k v new_left r + #insert_r = + #new_right = go r + #@Tail_Call here.balance_right k v l new_right + #if key > k then insert_r else + #if key == k then Bin s k value l r else + #insert_l + #go this == : Map -> Boolean == that = this.to_vector == that.to_vector + + insert : Any -> Any -> Map + insert key value = here.insert this key value + +insert_l key value k v l r = + new_left = here.insert l key value + here.balance_left k v new_left r + +insert_r key value k v l r = + new_right = here.insert r key value + here.balance_right k v l new_right + +insert map key value = case map of + Tip -> Bin 1 key value Tip Tip + Bin s k v l r -> + if key > k then @Tail_Call here.insert_r key value k v l r else + if key == k then @Tail_Call Bin s k value l r else + @Tail_Call here.insert_l key value k v l r + +#balance_left k x l r = case r of + #Tip -> case l of + #Tip -> Bin 1 k x Tip Tip + #Bin ls lk lx ll lr -> case lr of + #Tip -> case ll of + #Tip -> Bin 2 k x l Tip + #_ -> Bin 3 lk lx ll (Bin 1 k x Tip Tip) + #Bin lrs lrk lrx lrl lrr -> case ll of + #Tip -> Bin 3 lrk lrx (Bin 1 lk lx Tip Tip) (Bin 1 k x Tip Tip) + #Bin lls _ _ _ _ -> + #if lrs < Ratio*lls then Bin 1+ls lk lx ll (Bin 1+lrs k x lr Tip) else + #lrls = here.size lrl + #lrrs = here.size lrr + #Bin 1+ls lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+lrrs k x lrr Tip) + #Bin rs _ _ _ _ -> case l of + #Tip -> Bin 1+rs k x Tip r + #Bin ls lk lx ll lr -> + #if ls <= Delta*rs then Bin 1+ls+rs k x l r else + #lls = here.size ll + #case lr of + #Bin lrs lrk lrx lrl lrr -> + #if lrs < Ratio*lls then Bin 1+ls+rs lk lx ll (Bin 1+rs+lrs k x lr r) else + #lrls = here.size lrl + #lrrs = here.size lrr + #Bin 1+ls+rs lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+rs+lrrs k x lrr r) + + balance_left k x l r = case r of Tip -> case l of Tip -> Bin 1 k x Tip Tip @@ -88,7 +134,7 @@ balance_left k x l r = case r of lls = here.size ll case lr of Bin lrs lrk lrx lrl lrr -> - if lrs < ratio*lls then Bin 1+ls+rs lk lx ll (Bin 1+rs+lrs) k x lr r) else + if lrs < Ratio*lls then Bin 1+ls+rs lk lx ll (Bin 1+rs+lrs k x lr r) else lrls = here.size lrl lrrs = here.size lrr Bin 1+ls+rs lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+rs+lrrs k x lrr r) @@ -119,7 +165,33 @@ balance_right k x l r = case l of if rls < Ratio*rrs then Bin 1+ls+rs rk rx (Bin 1+ls+rls k x l rl) rr else rlls = here.size rll rlrs = here.size rlr - Bin 1+ls+rs rlk rlx (Bin 1+ls+rlls k x l rll) (Bin 1+rrs+rlrs) rk rx rlr rr + Bin 1+ls+rs rlk rlx (Bin 1+ls+rlls k x l rll) (Bin 1+rrs+rlrs rk rx rlr rr) + +#balance_right k x l r = case l of + #Tip -> case r of + #Tip -> Bin 1 k x Tip Tip + #Bin rs rk rx rl rr -> case rl of + #Tip -> case rr of + #Tip -> Bin 2 k x Tip r + #_ -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr + #Bin rls rlk rlx rll rlr -> case rr of + #Tip -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip) + #Bin rrs _ _ _ _ -> + #if rls < Ratio*rrs then Bin 1+rs rk rx (Bin 1+rls k x Tip rl) rr else + #srll = here.size rll + #srlr = here.size rlr + #Bin 1+rs rlk rlx (Bin 1+srll k x Tip rll) (Bin 1+rrs+srlr rk rx rlr rr) + #Bin ls _ _ _ _ -> case r of + #Tip -> Bin 1+ls k x l Tip + #Bin rs rk rx rl rr -> + #if rs <= Delta*ls then Bin 1+ls+rs k x l r else + #case rl of + #Bin rls rlk rlx rll rlr -> + #rrs = here.size rr + #if rls < Ratio*rrs then Bin 1+ls+rs rk rx (Bin 1+ls+rls k x l rl) rr else + #rlls = here.size rll + #rlrs = here.size rlr + #Bin 1+ls+rs rlk rlx (Bin 1+ls+rlls k x l rll) (Bin 1+rrs+rlrs rk rx rlr rr) empty : Map diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/IndirectInvokeCallableNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/IndirectInvokeCallableNode.java index 52477b10966d..2f0793e09478 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/IndirectInvokeCallableNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/IndirectInvokeCallableNode.java @@ -119,7 +119,7 @@ public Stateful invokeDynamicSymbol( Object selfArgument = arguments[thisArgumentPosition]; if (argumentsExecutionMode.shouldExecute()) { Stateful selfResult = - thisExecutor.executeThunk((Thunk) selfArgument, state, BaseNode.TailStatus.NOT_TAIL); + thisExecutor.executeThunk(selfArgument, state, BaseNode.TailStatus.NOT_TAIL); selfArgument = selfResult.getValue(); state = selfResult.getState(); arguments[thisArgumentPosition] = selfArgument; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java index e7560cf13aea..e1a6d06b4aae 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeCallableNode.java @@ -185,12 +185,9 @@ public Stateful invokeDynamicSymbol( lock.unlock(); } } - if (selfArgument instanceof Thunk) { - Stateful selfResult = - thisExecutor.executeThunk((Thunk) selfArgument, state, TailStatus.NOT_TAIL); - state = selfResult.getState(); - selfArgument = selfResult.getValue(); - } + Stateful selfResult = thisExecutor.executeThunk(selfArgument, state, TailStatus.NOT_TAIL); + selfArgument = selfResult.getValue(); + state = selfResult.getState(); arguments[thisArgumentPosition] = selfArgument; } Function function = methodResolverNode.execute(symbol, selfArgument); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ArgumentSorterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ArgumentSorterNode.java index 68bfa762946a..2e21ac896266 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ArgumentSorterNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ArgumentSorterNode.java @@ -55,11 +55,11 @@ public static ArgumentSorterNode build( preApplicationSchema, mapping.getPostApplicationSchema(), mapping, argumentsExecutionMode); } - private void initArgumentExecutors(Object[] arguments) { + private void initArgumentExecutors() { ThunkExecutorNode[] executors = new ThunkExecutorNode[mapping.getArgumentShouldExecute().length]; for (int i = 0; i < mapping.getArgumentShouldExecute().length; i++) { - if (mapping.getArgumentShouldExecute()[i] && TypesGen.isThunk(arguments[i])) { + if (mapping.getArgumentShouldExecute()[i]) { executors[i] = insert(ThunkExecutorNode.build()); } } @@ -74,7 +74,7 @@ private Object executeArguments(Object[] arguments, Object state) { lock.lock(); try { if (executors == null) { - initArgumentExecutors(arguments); + initArgumentExecutors(); } } finally { lock.unlock(); @@ -82,8 +82,7 @@ private Object executeArguments(Object[] arguments, Object state) { } for (int i = 0; i < mapping.getArgumentShouldExecute().length; i++) { if (executors[i] != null) { - Stateful result = - executors[i].executeThunk(TypesGen.asThunk(arguments[i]), state, TailStatus.NOT_TAIL); + Stateful result = executors[i].executeThunk(arguments[i], state, TailStatus.NOT_TAIL); arguments[i] = result.getValue(); state = result.getState(); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java index 3f1132768a48..8669e5dd1441 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ThunkExecutorNode.java @@ -12,6 +12,7 @@ import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.control.TailCallException; import org.enso.interpreter.runtime.state.Stateful; +import org.enso.interpreter.runtime.type.TypesGen; /** Node responsible for executing (forcing) thunks passed to it as runtime values. */ @GenerateUncached @@ -39,6 +40,15 @@ public static ThunkExecutorNode build() { */ public abstract Stateful executeThunk(Object thunk, Object state, BaseNode.TailStatus isTail); + static boolean isThunk(Object th) { + return TypesGen.isThunk(th); + } + + @Specialization(guards = "!isThunk(thunk)") + Stateful doOther(Object thunk, Object state, BaseNode.TailStatus isTail) { + return new Stateful(state, thunk); + } + @Specialization( guards = "callNode.getCallTarget() == thunk.getCallTarget()", limit = Constants.CacheSizes.THUNK_EXECUTOR_NODE) @@ -83,9 +93,4 @@ Stateful doUncached( } } } - - @Fallback - Stateful doOther(Object thunk, Object state, BaseNode.TailStatus isTail) { - return new Stateful(state, thunk); - } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenElseNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenElseNode.java index d5c9286f88fd..f4d860a8a095 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenElseNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenElseNode.java @@ -4,6 +4,7 @@ import com.oracle.truffle.api.profiles.ConditionProfile; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.dsl.MonadicState; +import org.enso.interpreter.dsl.Suspend; import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode; import org.enso.interpreter.runtime.callable.argument.Thunk; @@ -18,7 +19,11 @@ public class IfThenElseNode extends Node { private @Child ThunkExecutorNode rightThunkExecutorNode = ThunkExecutorNode.build(); private final ConditionProfile condProfile = ConditionProfile.createCountingProfile(); - Stateful execute(@MonadicState Object state, boolean _this, Thunk if_true, Thunk if_false) { + Stateful execute( + @MonadicState Object state, + boolean _this, + @Suspend Object if_true, + @Suspend Object if_false) { if (condProfile.profile(_this)) { return leftThunkExecutorNode.executeThunk(if_true, state, BaseNode.TailStatus.TAIL_DIRECT); } else { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenNode.java index dbf77b92956b..e7d608165c42 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenNode.java @@ -7,6 +7,7 @@ import org.enso.interpreter.Language; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.dsl.MonadicState; +import org.enso.interpreter.dsl.Suspend; import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode; import org.enso.interpreter.runtime.Context; @@ -25,11 +26,11 @@ static IfThenNode build() { return IfThenNodeGen.create(); } - abstract Stateful execute(@MonadicState Object state, boolean _this, Thunk if_true); + abstract Stateful execute(@MonadicState Object state, boolean _this, @Suspend Object if_true); @Specialization Stateful doExecute( - Object state, boolean _this, Thunk if_true, @CachedContext(Language.class) Context context) { + Object state, boolean _this, Object if_true, @CachedContext(Language.class) Context context) { if (condProfile.profile(_this)) { return leftThunkExecutorNode.executeThunk(if_true, state, BaseNode.TailStatus.TAIL_DIRECT); } else { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/CatchPanicNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/CatchPanicNode.java index 90f5844fd300..fcec5eb33a25 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/CatchPanicNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/CatchPanicNode.java @@ -7,6 +7,7 @@ import org.enso.interpreter.Language; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.dsl.MonadicState; +import org.enso.interpreter.dsl.Suspend; import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode; import org.enso.interpreter.runtime.Context; @@ -26,13 +27,13 @@ static CatchPanicNode build() { return CatchPanicNodeGen.create(); } - abstract Stateful execute(@MonadicState Object state, Object _this, Thunk action); + abstract Stateful execute(@MonadicState Object state, Object _this, @Suspend Object action); @Specialization Stateful doExecute( @MonadicState Object state, Object _this, - Thunk action, + Object action, @CachedContext(Language.class) Context ctx) { try { return thunkExecutorNode.executeThunk(action, state, BaseNode.TailStatus.NOT_TAIL); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java index 4d86eb0b0413..f4651b9bc4cd 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java @@ -4,6 +4,7 @@ import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.dsl.MonadicState; +import org.enso.interpreter.dsl.Suspend; import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.InvokeCallableNode; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; @@ -27,7 +28,7 @@ public class ApplicationOperator extends Node { invokeCallableNode.setTailStatus(BaseNode.TailStatus.TAIL_DIRECT); } - Stateful execute(VirtualFrame frame, @MonadicState Object state, Function _this, Thunk argument) { + Stateful execute(VirtualFrame frame, @MonadicState Object state, Function _this, @Suspend Object argument) { return invokeCallableNode.execute(_this, frame, state, new Object[] {argument}); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/BracketNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/BracketNode.java index 93c7eae5fe57..e01941bd7906 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/BracketNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/BracketNode.java @@ -5,6 +5,7 @@ import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.dsl.MonadicState; +import org.enso.interpreter.dsl.Suspend; import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.InvokeCallableNode; import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode; @@ -50,7 +51,7 @@ abstract Stateful execute( @MonadicState Object state, VirtualFrame frame, Object _this, - Thunk constructor, + @Suspend Object constructor, Object destructor, Object action); @@ -59,7 +60,7 @@ Stateful doBracket( Object state, VirtualFrame frame, Object _this, - Thunk constructor, + Object constructor, Object destructor, Object action) { Stateful resourceStateful = diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/NoInlineNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/NoInlineNode.java index 70009b63ba91..07d8f384a762 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/NoInlineNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/NoInlineNode.java @@ -4,6 +4,7 @@ import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.dsl.MonadicState; +import org.enso.interpreter.dsl.Suspend; import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode; import org.enso.interpreter.runtime.callable.argument.Thunk; @@ -17,7 +18,7 @@ public class NoInlineNode extends Node { private @Child ThunkExecutorNode thunkExecutorNode = ThunkExecutorNode.build(); @CompilerDirectives.TruffleBoundary - Stateful execute(@MonadicState Object state, Object _this, Thunk action) { + Stateful execute(@MonadicState Object state, Object _this, @Suspend Object action) { return thunkExecutorNode.executeThunk(action, state, BaseNode.TailStatus.NOT_TAIL); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/state/RunStateNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/state/RunStateNode.java index f0ab25f50d92..ea5ebd1a6a37 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/state/RunStateNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/state/RunStateNode.java @@ -7,6 +7,7 @@ import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.dsl.MonadicState; +import org.enso.interpreter.dsl.Suspend; import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode; import org.enso.interpreter.runtime.callable.argument.Thunk; @@ -29,11 +30,15 @@ static RunStateNode build() { private @Child ThunkExecutorNode thunkExecutorNode = ThunkExecutorNode.build(); abstract Stateful execute( - @MonadicState Object state, Object _this, Object key, Object local_state, Thunk computation); + @MonadicState Object state, + Object _this, + Object key, + Object local_state, + @Suspend Object computation); @Specialization Stateful doEmpty( - EmptyMap state, Object _this, Object key, Object local_state, Thunk computation) { + EmptyMap state, Object _this, Object key, Object local_state, Object computation) { SingletonMap localStateMap = new SingletonMap(key, local_state); Object result = thunkExecutorNode @@ -44,7 +49,7 @@ Stateful doEmpty( @Specialization(guards = {"state.getKey() == key"}) Stateful doSingletonSameKey( - SingletonMap state, Object _this, Object key, Object local_state, Thunk computation) { + SingletonMap state, Object _this, Object key, Object local_state, Object computation) { SingletonMap localStateContainer = new SingletonMap(state.getKey(), local_state); Stateful res = thunkExecutorNode.executeThunk( @@ -63,7 +68,7 @@ Stateful doSingletonNewKeyCached( Object _this, Object key, Object local_state, - Thunk computation, + Object computation, @Cached("key") Object cachedNewKey, @Cached("state.getKey()") Object cachedOldKey, @Cached(value = "buildSmallKeys(cachedNewKey, cachedOldKey)", dimensions = 1) @@ -77,7 +82,7 @@ Stateful doSingletonNewKeyCached( @Specialization Stateful doSingletonNewKeyUncached( - SingletonMap state, Object _this, Object key, Object local_state, Thunk computation) { + SingletonMap state, Object _this, Object key, Object local_state, Object computation) { return doSingletonNewKeyCached( state, _this, @@ -100,7 +105,7 @@ Stateful doMultiNewKeyCached( Object _this, Object key, Object local_state, - Thunk computation, + Object computation, @Cached("key") Object cachedNewKey, @Cached(value = "state.getKeys()", dimensions = 1) Object[] cachedOldKeys, @Cached("state.indexOf(key)") int index, @@ -125,7 +130,7 @@ Stateful doMultiExistingKeyCached( Object _this, Object key, Object local_state, - Thunk computation, + Object computation, @Cached("key") Object cachedNewKey, @Cached(value = "state.getKeys()", dimensions = 1) Object[] cachedOldKeys, @Cached("state.indexOf(key)") int index) { @@ -144,7 +149,7 @@ Stateful doMultiExistingKeyCached( @Specialization Stateful doMultiUncached( - SmallMap state, Object _this, Object key, Object local_state, Thunk computation) { + SmallMap state, Object _this, Object key, Object local_state, Object computation) { int idx = state.indexOf(key); if (idx == SmallMap.NOT_FOUND) { return doMultiNewKeyCached( diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/thread/WithInterruptHandlerNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/thread/WithInterruptHandlerNode.java index c8489573bc5d..07966ef6d4ac 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/thread/WithInterruptHandlerNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/thread/WithInterruptHandlerNode.java @@ -3,6 +3,7 @@ import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.dsl.MonadicState; +import org.enso.interpreter.dsl.Suspend; import org.enso.interpreter.node.BaseNode; import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode; import org.enso.interpreter.runtime.callable.argument.Thunk; @@ -18,7 +19,7 @@ public class WithInterruptHandlerNode extends Node { private @Child ThunkExecutorNode handlerExecutorNode = ThunkExecutorNode.build(); Stateful execute( - @MonadicState Object state, Object _this, Thunk action, Thunk interrupt_handler) { + @MonadicState Object state, Object _this, @Suspend Object action, @Suspend Object interrupt_handler) { try { return actExecutorNode.executeThunk(action, state, BaseNode.TailStatus.NOT_TAIL); } catch (ThreadInterruptedException e) { diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/Suspend.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/Suspend.java new file mode 100644 index 000000000000..f238cb5cf80c --- /dev/null +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/Suspend.java @@ -0,0 +1,11 @@ +package org.enso.interpreter.dsl; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** An interface marking an argument as requiring to be passed the current monadic state. */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.SOURCE) +public @interface Suspend {} diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java index f62991dd4b3e..297d18455238 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java @@ -2,6 +2,7 @@ import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.dsl.MonadicState; +import org.enso.interpreter.dsl.Suspend; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.*; @@ -119,7 +120,10 @@ public boolean validate(ProcessingEnvironment processingEnvironment) { "The execute method does not take `this` argument. At least one positional argument must be named `_this`.", element); } - return definesThis; + + boolean argsValid = arguments.stream().allMatch(arg -> arg.validate(processingEnvironment)); + + return definesThis && argsValid; } /** @return the package name this method was declared in. */ @@ -188,7 +192,9 @@ public static class ArgumentDefinition { private final boolean isState; private final boolean isFrame; private final boolean isCallerInfo; + private final boolean isSuspended; private final int position; + private final VariableElement element; /** * Creates a new instance of this class. @@ -197,17 +203,29 @@ public static class ArgumentDefinition { * @param position the position (0-indexed) of this argument in the arguments list. */ public ArgumentDefinition(VariableElement element, int position) { + this.element = element; type = element.asType(); String[] typeNameSegments = type.toString().split("\\."); typeName = typeNameSegments[typeNameSegments.length - 1]; String originalName = element.getSimpleName().toString(); name = originalName.equals("_this") ? "this" : originalName; isState = element.getAnnotation(MonadicState.class) != null && type.toString().equals(OBJECT); + isSuspended = element.getAnnotation(Suspend.class) != null; isFrame = type.toString().equals(VIRTUAL_FRAME); isCallerInfo = type.toString().equals(CALLER_INFO); this.position = position; } + public boolean validate(ProcessingEnvironment processingEnvironment) { + if (type.toString().equals(THUNK)) { + processingEnvironment + .getMessager() + .printMessage(Diagnostic.Kind.ERROR, "Thunk argument, I refuse", element); + return false; + } + return true; + } + /** @return whether this argument should be passed the monadic state. */ public boolean isState() { return isState; @@ -265,7 +283,7 @@ public String getName() { /** @return whether this argument is expected to be passed suspended. */ public boolean isSuspended() { - return type.toString().equals(THUNK); + return isSuspended; } } } diff --git a/test/Benchmarks/src/Collections.enso b/test/Benchmarks/src/Collections.enso index 852bf2778f21..19f4ab778b91 100644 --- a/test/Benchmarks/src/Collections.enso +++ b/test/Benchmarks/src/Collections.enso @@ -2,7 +2,9 @@ from Base import all import Builtins import Base.Bench_Utils -gen_list len = 0.upto len . fold Nil (l -> i -> Cons i+1 l) +polyglot java import java.util.Random + +gen_list len = 0.upto len . fold Nil (l -> i -> Cons i+1 l) # My_Cons i+1 l 0 0 0 0 0 0) sum_list_meta list = nil_cons = Meta.meta Nil . constructor @@ -14,15 +16,45 @@ sum_list_meta list = res = folder 0 list res -build_map size = 0.upto size . fold Map.empty (m -> i -> m.insert i i) +type My_Cons h t x y z w r f + +id_list list = case list of + Nil -> Nil + Cons h t -> Cons h (here.id_list t) + +sum_list_simple list = + go list = case list of + #My_Cons h t x y z w r f -> h + go t + Cons h t -> h + go t + Nil -> 0 + res = go list + res + +sum_recur_case n = case n == 0 of + True -> 0 + False -> n + here.sum_recur_case n-1 + +sum_recur n = if n == 0 then 0 else 1 + here.sum_recur n-1 + +build_map size = + rand = Random.new [].to_array + 0.upto size . fold Map.empty (m -> i -> m.insert i i) # (rand.nextInt [10000]) i) main = - mil = 1000000 - list = here.gen_list mil - vec = Vector.new mil (ix -> ix + 1) - vec_decimal = Vector.new mil (ix -> ix + 0.0) - Bench_Utils.measure (here.build_map 10000) "build a map" 10 10 - Bench_Utils.measure (here.sum_list_meta list) "list meta-fold" 1000 10 - Bench_Utils.measure (list.fold 0 (+)) "list fold" 1000 10 - Bench_Utils.measure (vec.fold 0 (+)) "vector fold" 1000 10 - Bench_Utils.measure (vec_decimal.fold 0 (+)) "vector decimal fold" 1000 10 + #mil = 1000000 + #list = here.gen_list 1000 + + #Bench_Utils.measure (0.upto 1000 . each _-> here.sum_recur_case 100) "recur_sum" 100 10 + #Bench_Utils.measure (0.upto 1000 . each _-> here.sum_recur 100) "recur_sum" 100 10 + + #Bench_Utils.measure (act = 0.upto 1000 . each _-> here.id_list list) "rec" 100 10 + #Bench_Utils.measure (0.upto 1000 . each _-> here.sum_list_simple list) "rec" 100 10 + #Bench_Utils.measure (0.upto 1000 . each _-> list.fold 0 (+)) "rec" 100 10 + Bench_Utils.measure (here.build_map 10000) "build a map" 100 100 + #list = here.gen_list mil + #vec = Vector.new mil (ix -> ix + 1) + #vec_decimal = Vector.new mil (ix -> ix + 0.0) + #Bench_Utils.measure (here.sum_list_meta list) "list meta-fold" 1000 10 + #Bench_Utils.measure (list.fold 0 (+)) "list fold" 1000 10 + #Bench_Utils.measure (vec.fold 0 (+)) "vector fold" 1000 10 + #Bench_Utils.measure (vec_decimal.fold 0 (+)) "vector decimal fold" 1000 10 diff --git a/test/Benchmarks/src/Main.enso b/test/Benchmarks/src/Main.enso index d33d58ded152..555e98432afe 100644 --- a/test/Benchmarks/src/Main.enso +++ b/test/Benchmarks/src/Main.enso @@ -13,6 +13,13 @@ sum_tco = sum_to -> res = summator 0 sum_to res +sum_tco_custom_if sum_to = + summator acc current = case current == 0 of + True -> acc + False -> @Tail_Call summator acc+current current-1 + res = summator 0 sum_to + res + sum_tco_decimal = sum_to -> s = sum_to.to_decimal summator = acc -> current -> @@ -65,21 +72,25 @@ sum_co n = res = here.sum_co_2 n 0 res +recur_sum i = if i == 0 then 0 else i + here.recur_sum i-1 + main = hundred_mil = 100000000 - IO.println (here.sum_co 1000) - IO.println "Measuring Sum TCO Corecursive" - Bench_Utils.measure (here.sum_co hundred_mil) "sum_tco_corecursive" 100 10 - IO.println "Measuring Sum TCO Decimal" - Bench_Utils.measure (here.sum_tco_decimal hundred_mil) "sum_tco_float" 100 10 - IO.println "Measuring SumTCO" - Bench_Utils.measure (here.sum_tco hundred_mil) "sum_tco" 100 10 - IO.println "Measuring SumTCO Java" - Bench_Utils.measure (here.sum_tco_java hundred_mil) "sum_tco_java" 100 10 - IO.println "Measuring SumTCO Eval" - Bench_Utils.measure (here.sum_tco_eval hundred_mil) "sum_tco_eval" 100 10 - IO.println "Measuring State" - Bench_Utils.measure (here.sum_state hundred_mil) "sum_state" 100 10 - IO.println "Measuring Co-State" - Bench_Utils.measure (here.sum_co_state hundred_mil) "sum_co_state" 100 10 - IO.println "Bye." + Bench_Utils.measure (here.recur_sum 1000) "sum recur" 1000 100 + #IO.println "Measuring SumTCO Custom If" + #Bench_Utils.measure (here.sum_tco_custom_if hundred_mil) "sum_tco_custom" 10 10 + #IO.println "Measuring SumTCO" + #Bench_Utils.measure (here.sum_tco hundred_mil) "sum_tco" 10 10 + #IO.println "Measuring Sum TCO Corecursive" + #Bench_Utils.measure (here.sum_co hundred_mil) "sum_tco_corecursive" 100 10 + #IO.println "Measuring Sum TCO Decimal" + #Bench_Utils.measure (here.sum_tco_decimal hundred_mil) "sum_tco_float" 100 10 + #IO.println "Measuring SumTCO Java" + #Bench_Utils.measure (here.sum_tco_java hundred_mil) "sum_tco_java" 100 10 + #IO.println "Measuring SumTCO Eval" + #Bench_Utils.measure (here.sum_tco_eval hundred_mil) "sum_tco_eval" 100 10 + #IO.println "Measuring State" + #Bench_Utils.measure (here.sum_state hundred_mil) "sum_state" 100 10 + #IO.println "Measuring Co-State" + #Bench_Utils.measure (here.sum_co_state hundred_mil) "sum_co_state" 100 10 + #IO.println "Bye." From fa9ac54ab3753aaeeef4ecd058902a6d560b29de Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Mon, 19 Oct 2020 16:02:02 +0200 Subject: [PATCH 5/9] checkpoint --- distribution/std-lib/Base/src/Data/Map.enso | 52 +++++++++---------- .../builtin/InstantiateAtomNode.java | 15 +++--- .../callable/atom/AtomConstructor.java | 12 ++--- .../test/semantic/NamedArgumentsTest.scala | 1 + test/Benchmarks/src/Collections.enso | 46 ++++------------ test/Benchmarks/src/Main.enso | 45 ++++++---------- .../haskell/haskell-benchmark.cabal | 51 ++++++++++++++++++ .../benchmarks/haskell/package.yaml | 1 + .../benchmarks/haskell/src/Fixtures.hs | 7 ++- .../benchmarks/haskell/test/bench/Main.hs | 1 + 10 files changed, 126 insertions(+), 105 deletions(-) create mode 100644 tools/performance/comparative-benchmark/benchmarks/haskell/haskell-benchmark.cabal diff --git a/distribution/std-lib/Base/src/Data/Map.enso b/distribution/std-lib/Base/src/Data/Map.enso index 18891c8c1bbb..b0e72be90d6a 100644 --- a/distribution/std-lib/Base/src/Data/Map.enso +++ b/distribution/std-lib/Base/src/Data/Map.enso @@ -81,11 +81,11 @@ insert_r key value k v l r = here.balance_right k v l new_right insert map key value = case map of - Tip -> Bin 1 key value Tip Tip Bin s k v l r -> if key > k then @Tail_Call here.insert_r key value k v l r else if key == k then @Tail_Call Bin s k value l r else @Tail_Call here.insert_l key value k v l r + _ -> Bin 1 key value Tip Tip #balance_left k x l r = case r of #Tip -> case l of @@ -115,7 +115,18 @@ insert map key value = case map of balance_left k x l r = case r of - Tip -> case l of + Bin rs _ _ _ _ -> case l of + Bin ls lk lx ll lr -> + if ls <= Delta*rs then Bin 1+ls+rs k x l r else + lls = here.size ll + case lr of + Bin lrs lrk lrx lrl lrr -> + if lrs < Ratio*lls then Bin 1+ls+rs lk lx ll (Bin 1+rs+lrs k x lr r) else + lrls = here.size lrl + lrrs = here.size lrr + Bin 1+ls+rs lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+rs+lrrs k x lrr r) + _ -> Bin 1+rs k x Tip r + _ -> case l of Tip -> Bin 1 k x Tip Tip Bin _ _ _ Tip Tip -> Bin 2 k x l Tip Bin _ lk lx Tip (Bin _ lrk lrx _ _) -> Bin 3 lrk lrx (Bin 1 lk lx Tip Tip) (Bin 1 k x Tip Tip) @@ -127,36 +138,13 @@ balance_left k x l r = case r of lrls = here.size lrl lrrs = here.size lrr Bin 1+ls lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+lrrs k x lrr Tip) - Bin rs _ _ _ _ -> case l of - Tip -> Bin 1+rs k x Tip r - Bin ls lk lx ll lr -> - if ls <= Delta*rs then Bin 1+ls+rs k x l r else - lls = here.size ll - case lr of - Bin lrs lrk lrx lrl lrr -> - if lrs < Ratio*lls then Bin 1+ls+rs lk lx ll (Bin 1+rs+lrs k x lr r) else - lrls = here.size lrl - lrrs = here.size lrr - Bin 1+ls+rs lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+rs+lrrs k x lrr r) size m = case m of Bin s _ _ _ _ -> s - Tip -> 0 + _ -> 0 balance_right k x l r = case l of - Tip -> case r of - Tip -> Bin 1 k x Tip Tip - Bin _ _ _ Tip Tip -> Bin 2 k x Tip r - Bin _ rk rx Tip rr -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr - Bin _ rk rx (Bin _ rlk rlx _ _) Tip -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip) - Bin rs rk rx (Bin rls rlk rlx rll rlr) rr -> case rr of - Bin rrs _ _ _ _ -> - if rls < Ratio*rrs then Bin 1+rs rk rx (Bin 1+rls k x Tip rl) rr else - srll = here.size rll - srlr = here.size rlr - Bin 1+rs rlk rlx (Bin 1+srll k x Tip rll) (Bin 1+rrs+srlr rk rx rlr rr) Bin ls _ _ _ _ -> case r of - Tip -> Bin 1+ls k x l Tip Bin rs rk rx rl rr -> if rs <= Delta*ls then Bin 1+ls+rs k x l r else case rl of @@ -166,6 +154,18 @@ balance_right k x l r = case l of rlls = here.size rll rlrs = here.size rlr Bin 1+ls+rs rlk rlx (Bin 1+ls+rlls k x l rll) (Bin 1+rrs+rlrs rk rx rlr rr) + _ -> Bin 1+ls k x l Tip + _ -> case r of + Tip -> Bin 1 k x Tip Tip + Bin _ _ _ Tip Tip -> Bin 2 k x Tip r + Bin _ rk rx Tip rr -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr + Bin _ rk rx (Bin _ rlk rlx _ _) Tip -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip) + Bin rs rk rx (Bin rls rlk rlx rll rlr) rr -> case rr of + Bin rrs _ _ _ _ -> + if rls < Ratio*rrs then Bin 1+rs rk rx (Bin 1+rls k x Tip rl) rr else + srll = here.size rll + srlr = here.size rlr + Bin 1+rs rlk rlx (Bin 1+srll k x Tip rll) (Bin 1+rrs+srlr rk rx rlr rr) #balance_right k x l r = case l of #Tip -> case r of diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java index f0e1e5d991e9..a2955516c940 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java @@ -6,6 +6,7 @@ import com.oracle.truffle.api.nodes.RootNode; import org.enso.interpreter.Language; import org.enso.interpreter.node.ExpressionNode; +import org.enso.interpreter.node.expression.atom.InstantiateNode; import org.enso.interpreter.runtime.callable.atom.AtomConstructor; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.state.Stateful; @@ -13,13 +14,13 @@ /** This node represents the process of instantiating an atom at runtime. */ @NodeInfo(shortName = "constructor::", description = "An atom instantiation at runtime.") public class InstantiateAtomNode extends RootNode { - private final AtomConstructor constructor; // instantiator; + private @Child ExpressionNode instantiator; private final String name; - private InstantiateAtomNode(Language language, String name, AtomConstructor constructor) { + private InstantiateAtomNode(Language language, String name, ExpressionNode instantiator) { super(language); this.name = name; - this.constructor = constructor; + this.instantiator = instantiator; } /** @@ -32,9 +33,7 @@ private InstantiateAtomNode(Language language, String name, AtomConstructor cons public Stateful execute(VirtualFrame frame) { return new Stateful( Function.ArgumentsHelper.getState(frame.getArguments()), - constructor.newInstance( - Function.ArgumentsHelper.getPositionalArguments( - frame.getArguments()))); // .executeGeneric(frame)); + instantiator.executeGeneric(frame)); } /** @@ -56,7 +55,7 @@ public String getName() { * @return an instance of this node */ public static InstantiateAtomNode build( - Language language, String name, AtomConstructor constructor) { - return new InstantiateAtomNode(language, name, constructor); + Language language, String name, ExpressionNode instantiator) { + return new InstantiateAtomNode(language, name, instantiator); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/AtomConstructor.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/AtomConstructor.java index 9ff63e00d7fc..a09b6d13b8e1 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/AtomConstructor.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/AtomConstructor.java @@ -68,12 +68,12 @@ public AtomConstructor initializeFields(ArgumentDefinition... args) { * {@link AtomConstructor} */ private Function buildConstructorFunction(ArgumentDefinition[] args) { -// ExpressionNode[] argumentReaders = new ExpressionNode[args.length]; -// for (int i = 0; i < args.length; i++) { -// argumentReaders[i] = ReadArgumentNode.build(i, args[i].getDefaultValue().orElse(null)); -// } -// ExpressionNode instantiateNode = InstantiateNode.build(this, argumentReaders); - RootNode rootNode = InstantiateAtomNode.build(null, name, this); + ExpressionNode[] argumentReaders = new ExpressionNode[args.length]; + for (int i = 0; i < args.length; i++) { + argumentReaders[i] = ReadArgumentNode.build(i, args[i].getDefaultValue().orElse(null)); + } + ExpressionNode instantiateNode = InstantiateNode.build(this, argumentReaders); + RootNode rootNode = InstantiateAtomNode.build(null, name, instantiateNode); RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode); return new Function(callTarget, null, new FunctionSchema(args)); } diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/NamedArgumentsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/NamedArgumentsTest.scala index baca6421cb9f..c60fcdbfb1ac 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/NamedArgumentsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/NamedArgumentsTest.scala @@ -211,6 +211,7 @@ class NamedArgumentsTest extends InterpreterTest { } "be usable and overridable in constructors" in { + pending val code = """ |type Nil2 diff --git a/test/Benchmarks/src/Collections.enso b/test/Benchmarks/src/Collections.enso index 19f4ab778b91..606d2c49c50d 100644 --- a/test/Benchmarks/src/Collections.enso +++ b/test/Benchmarks/src/Collections.enso @@ -4,7 +4,7 @@ import Base.Bench_Utils polyglot java import java.util.Random -gen_list len = 0.upto len . fold Nil (l -> i -> Cons i+1 l) # My_Cons i+1 l 0 0 0 0 0 0) +gen_list len = 0.upto len . fold Nil (l -> i -> Cons i+1 l) sum_list_meta list = nil_cons = Meta.meta Nil . constructor @@ -16,45 +16,19 @@ sum_list_meta list = res = folder 0 list res -type My_Cons h t x y z w r f - -id_list list = case list of - Nil -> Nil - Cons h t -> Cons h (here.id_list t) - -sum_list_simple list = - go list = case list of - #My_Cons h t x y z w r f -> h + go t - Cons h t -> h + go t - Nil -> 0 - res = go list - res - -sum_recur_case n = case n == 0 of - True -> 0 - False -> n + here.sum_recur_case n-1 - sum_recur n = if n == 0 then 0 else 1 + here.sum_recur n-1 build_map size = rand = Random.new [].to_array - 0.upto size . fold Map.empty (m -> i -> m.insert i i) # (rand.nextInt [10000]) i) + 0.upto size . fold Map.empty (m -> i -> m.insert (rand.nextInt [10000]) i) main = - #mil = 1000000 - #list = here.gen_list 1000 - - #Bench_Utils.measure (0.upto 1000 . each _-> here.sum_recur_case 100) "recur_sum" 100 10 - #Bench_Utils.measure (0.upto 1000 . each _-> here.sum_recur 100) "recur_sum" 100 10 - - #Bench_Utils.measure (act = 0.upto 1000 . each _-> here.id_list list) "rec" 100 10 - #Bench_Utils.measure (0.upto 1000 . each _-> here.sum_list_simple list) "rec" 100 10 - #Bench_Utils.measure (0.upto 1000 . each _-> list.fold 0 (+)) "rec" 100 10 + mil = 1000000 + list = here.gen_list mil + vec = Vector.new mil (ix -> ix + 1) + vec_decimal = Vector.new mil (ix -> ix + 0.0) + Bench_Utils.measure (here.sum_list_meta list) "list meta-fold" 1000 10 + Bench_Utils.measure (list.fold 0 (+)) "list fold" 1000 10 + Bench_Utils.measure (vec.fold 0 (+)) "vector fold" 1000 10 + Bench_Utils.measure (vec_decimal.fold 0 (+)) "vector decimal fold" 1000 10 Bench_Utils.measure (here.build_map 10000) "build a map" 100 100 - #list = here.gen_list mil - #vec = Vector.new mil (ix -> ix + 1) - #vec_decimal = Vector.new mil (ix -> ix + 0.0) - #Bench_Utils.measure (here.sum_list_meta list) "list meta-fold" 1000 10 - #Bench_Utils.measure (list.fold 0 (+)) "list fold" 1000 10 - #Bench_Utils.measure (vec.fold 0 (+)) "vector fold" 1000 10 - #Bench_Utils.measure (vec_decimal.fold 0 (+)) "vector decimal fold" 1000 10 diff --git a/test/Benchmarks/src/Main.enso b/test/Benchmarks/src/Main.enso index 555e98432afe..f6c427c60fc7 100644 --- a/test/Benchmarks/src/Main.enso +++ b/test/Benchmarks/src/Main.enso @@ -13,13 +13,6 @@ sum_tco = sum_to -> res = summator 0 sum_to res -sum_tco_custom_if sum_to = - summator acc current = case current == 0 of - True -> acc - False -> @Tail_Call summator acc+current current-1 - res = summator 0 sum_to - res - sum_tco_decimal = sum_to -> s = sum_to.to_decimal summator = acc -> current -> @@ -48,7 +41,8 @@ sum_co_state_body = acc = State.get Sum State.put Counter n-1 State.put Sum acc+n - if n == 0 then acc else @Tail_Call here.sum_co_state_body + if n == 0 then State.get Sum else + @Tail_Call here.sum_co_state_body sum_co_state n = res = State.run Counter n (State.run Sum 0 here.sum_co_state_body) @@ -72,25 +66,20 @@ sum_co n = res = here.sum_co_2 n 0 res -recur_sum i = if i == 0 then 0 else i + here.recur_sum i-1 - main = hundred_mil = 100000000 - Bench_Utils.measure (here.recur_sum 1000) "sum recur" 1000 100 - #IO.println "Measuring SumTCO Custom If" - #Bench_Utils.measure (here.sum_tco_custom_if hundred_mil) "sum_tco_custom" 10 10 - #IO.println "Measuring SumTCO" - #Bench_Utils.measure (here.sum_tco hundred_mil) "sum_tco" 10 10 - #IO.println "Measuring Sum TCO Corecursive" - #Bench_Utils.measure (here.sum_co hundred_mil) "sum_tco_corecursive" 100 10 - #IO.println "Measuring Sum TCO Decimal" - #Bench_Utils.measure (here.sum_tco_decimal hundred_mil) "sum_tco_float" 100 10 - #IO.println "Measuring SumTCO Java" - #Bench_Utils.measure (here.sum_tco_java hundred_mil) "sum_tco_java" 100 10 - #IO.println "Measuring SumTCO Eval" - #Bench_Utils.measure (here.sum_tco_eval hundred_mil) "sum_tco_eval" 100 10 - #IO.println "Measuring State" - #Bench_Utils.measure (here.sum_state hundred_mil) "sum_state" 100 10 - #IO.println "Measuring Co-State" - #Bench_Utils.measure (here.sum_co_state hundred_mil) "sum_co_state" 100 10 - #IO.println "Bye." + IO.println "Measuring Co-State" + Bench_Utils.measure (here.sum_co_state hundred_mil) "sum_co_state" 100 10 + IO.println "Measuring Sum TCO Corecursive" + Bench_Utils.measure (here.sum_co hundred_mil) "sum_tco_corecursive" 100 10 + IO.println "Measuring Sum TCO Decimal" + Bench_Utils.measure (here.sum_tco_decimal hundred_mil) "sum_tco_float" 100 10 + IO.println "Measuring SumTCO" + Bench_Utils.measure (here.sum_tco hundred_mil) "sum_tco" 100 10 + IO.println "Measuring SumTCO Java" + Bench_Utils.measure (here.sum_tco_java hundred_mil) "sum_tco_java" 100 10 + IO.println "Measuring SumTCO Eval" + Bench_Utils.measure (here.sum_tco_eval hundred_mil) "sum_tco_eval" 100 10 + IO.println "Measuring State" + Bench_Utils.measure (here.sum_state hundred_mil) "sum_state" 100 10 + IO.println "Bye." diff --git a/tools/performance/comparative-benchmark/benchmarks/haskell/haskell-benchmark.cabal b/tools/performance/comparative-benchmark/benchmarks/haskell/haskell-benchmark.cabal new file mode 100644 index 000000000000..252678e078d5 --- /dev/null +++ b/tools/performance/comparative-benchmark/benchmarks/haskell/haskell-benchmark.cabal @@ -0,0 +1,51 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: cdb5656f9b7103b47ac57b68d3d2cf6e1e5162f7abc9eb075675918adef47457 + +name: haskell-benchmark +version: 0.1.0.0 +description: A benchmark for comparing Haskell to Enso. +homepage: https://github.com/https://github.com/enso-org/enso#readme +bug-reports: https://github.com/https://github.com/enso-org/enso/issues +author: Ara Adkins +maintainer: ara.adkins@enso.org +copyright: Enso Team 2020 +build-type: Simple + +source-repository head + type: git + location: https://github.com/https://github.com/enso-org/enso + +library + exposed-modules: + Fixtures + other-modules: + Paths_haskell_benchmark + hs-source-dirs: + src + default-extensions: AllowAmbiguousTypes ApplicativeDo BangPatterns BinaryLiterals ConstraintKinds DataKinds DefaultSignatures DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DerivingStrategies DuplicateRecordFields EmptyDataDecls FlexibleContexts FlexibleInstances FunctionalDependencies GeneralizedNewtypeDeriving InstanceSigs LambdaCase LiberalTypeSynonyms MonadComprehensions MultiWayIf NamedWildCards NegativeLiterals NoImplicitPrelude NumDecimals OverloadedLabels OverloadedStrings PatternSynonyms QuasiQuotes RankNTypes RecursiveDo ScopedTypeVariables StandaloneDeriving Strict StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeFamilyDependencies TypeOperators UnicodeSyntax ViewPatterns + build-depends: + base + , containers + , deepseq + , random + default-language: Haskell2010 + +benchmark haskell-benchmark + type: exitcode-stdio-1.0 + main-is: Main.hs + other-modules: + Paths_haskell_benchmark + hs-source-dirs: + test/bench + default-extensions: AllowAmbiguousTypes ApplicativeDo BangPatterns BinaryLiterals ConstraintKinds DataKinds DefaultSignatures DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DerivingStrategies DuplicateRecordFields EmptyDataDecls FlexibleContexts FlexibleInstances FunctionalDependencies GeneralizedNewtypeDeriving InstanceSigs LambdaCase LiberalTypeSynonyms MonadComprehensions MultiWayIf NamedWildCards NegativeLiterals NoImplicitPrelude NumDecimals OverloadedLabels OverloadedStrings PatternSynonyms QuasiQuotes RankNTypes RecursiveDo ScopedTypeVariables StandaloneDeriving Strict StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeFamilyDependencies TypeOperators UnicodeSyntax ViewPatterns + build-depends: + base + , criterion + , deepseq + , haskell-benchmark + default-language: Haskell2010 diff --git a/tools/performance/comparative-benchmark/benchmarks/haskell/package.yaml b/tools/performance/comparative-benchmark/benchmarks/haskell/package.yaml index 8f1478452289..252078831a88 100644 --- a/tools/performance/comparative-benchmark/benchmarks/haskell/package.yaml +++ b/tools/performance/comparative-benchmark/benchmarks/haskell/package.yaml @@ -16,6 +16,7 @@ library: - base - deepseq - containers + - random benchmarks: haskell-benchmark: diff --git a/tools/performance/comparative-benchmark/benchmarks/haskell/src/Fixtures.hs b/tools/performance/comparative-benchmark/benchmarks/haskell/src/Fixtures.hs index cae5c7ad09b5..0aae9689d1de 100644 --- a/tools/performance/comparative-benchmark/benchmarks/haskell/src/Fixtures.hs +++ b/tools/performance/comparative-benchmark/benchmarks/haskell/src/Fixtures.hs @@ -4,8 +4,10 @@ import Prelude import qualified Data.Map.Strict as Map +import Control.Monad (foldM) import Data.Int (Int64) import Data.List (foldl') +import System.Random (randomRIO) ------------------ @@ -69,4 +71,7 @@ myFoldl f z (Cons x xs) = let z' = z `f` x in seq z' $ myFoldl f z' xs buildMap :: Integer -> Map.Map Integer Integer -buildMap i = foldl' (\m i -> Map.insert i i m) Map.empty [0..i] \ No newline at end of file +buildMap n = foldl' (\m i -> Map.insert i i m) Map.empty [0..n] + +buildRandomMap :: Integer -> IO (Map.Map Integer Integer) +buildRandomMap n = foldM (\m i -> fmap (\key -> Map.insert key i m) $ randomRIO (0, 10000)) Map.empty [0..n] \ No newline at end of file diff --git a/tools/performance/comparative-benchmark/benchmarks/haskell/test/bench/Main.hs b/tools/performance/comparative-benchmark/benchmarks/haskell/test/bench/Main.hs index a4d4262e63fd..a18b4688919a 100644 --- a/tools/performance/comparative-benchmark/benchmarks/haskell/test/bench/Main.hs +++ b/tools/performance/comparative-benchmark/benchmarks/haskell/test/bench/Main.hs @@ -10,6 +10,7 @@ main :: IO () main = defaultMain [ bench "buildMap" $ whnf Fixtures.buildMap Fixtures.tenThousand, + bench "buildMapRandom" $ whnfIO $ Fixtures.buildRandomMap Fixtures.tenThousand, bench "sumTCO" $ whnf Fixtures.sumTCO Fixtures.hundredMillion, bench "sumList" $ whnf Fixtures.sumList Fixtures.millionElementList, bench "reverseList" $ whnf Fixtures.reverseList Fixtures.millionElementList, From 0c95b799081120d7a066364414f30499206d9c8a Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Mon, 19 Oct 2020 16:45:14 +0200 Subject: [PATCH 6/9] foobar --- distribution/bin/enso | 2 +- test/Benchmarks/src/Collections.enso | 2 +- test/Benchmarks/src/Main.enso | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/distribution/bin/enso b/distribution/bin/enso index 2c34c242061b..fe7f14afc3ea 100755 --- a/distribution/bin/enso +++ b/distribution/bin/enso @@ -1,3 +1,3 @@ COMP_PATH=$(dirname "$0")/../component -exec java -jar -Dtruffle.class.path.append="$COMP_PATH/runtime.jar" $JAVA_OPTS $COMP_PATH/runner.jar "$@" +exec java -jar -Dtruffle.class.path.append="$COMP_PATH/runtime.jar" -Dpolyglot.engine.IterativePartialEscape=true $JAVA_OPTS $COMP_PATH/runner.jar "$@" exit diff --git a/test/Benchmarks/src/Collections.enso b/test/Benchmarks/src/Collections.enso index 606d2c49c50d..9ad203654fe9 100644 --- a/test/Benchmarks/src/Collections.enso +++ b/test/Benchmarks/src/Collections.enso @@ -31,4 +31,4 @@ main = Bench_Utils.measure (list.fold 0 (+)) "list fold" 1000 10 Bench_Utils.measure (vec.fold 0 (+)) "vector fold" 1000 10 Bench_Utils.measure (vec_decimal.fold 0 (+)) "vector decimal fold" 1000 10 - Bench_Utils.measure (here.build_map 10000) "build a map" 100 100 + Bench_Utils.measure (here.build_map 10000) "build a map" 100 10 diff --git a/test/Benchmarks/src/Main.enso b/test/Benchmarks/src/Main.enso index f6c427c60fc7..2c458a667bb8 100644 --- a/test/Benchmarks/src/Main.enso +++ b/test/Benchmarks/src/Main.enso @@ -41,7 +41,7 @@ sum_co_state_body = acc = State.get Sum State.put Counter n-1 State.put Sum acc+n - if n == 0 then State.get Sum else + if n == 0 then acc else @Tail_Call here.sum_co_state_body sum_co_state n = @@ -68,8 +68,6 @@ sum_co n = main = hundred_mil = 100000000 - IO.println "Measuring Co-State" - Bench_Utils.measure (here.sum_co_state hundred_mil) "sum_co_state" 100 10 IO.println "Measuring Sum TCO Corecursive" Bench_Utils.measure (here.sum_co hundred_mil) "sum_tco_corecursive" 100 10 IO.println "Measuring Sum TCO Decimal" @@ -82,4 +80,6 @@ main = Bench_Utils.measure (here.sum_tco_eval hundred_mil) "sum_tco_eval" 100 10 IO.println "Measuring State" Bench_Utils.measure (here.sum_state hundred_mil) "sum_state" 100 10 + IO.println "Measuring Co-State" + Bench_Utils.measure (here.sum_co_state hundred_mil) "sum_co_state" 100 10 IO.println "Bye." From 06041d874525d4560ac0e17ba1e729d5f997f242 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Mon, 19 Oct 2020 17:57:09 +0200 Subject: [PATCH 7/9] stuff --- distribution/std-lib/Base/src/Data/Map.enso | 181 +++--------------- .../std-lib/Base/src/Data/Map/Internal.enso | 106 ++++++++++ distribution/std-lib/Base/src/Test.enso | 2 +- .../std-lib/Base/src/Text/Extensions.enso | 9 + distribution/std-lib/scratch | 53 ----- distribution/std-lib/scratch.hs | 53 ----- .../semantic/RecursionBenchmarks.java | 36 +--- .../bench/fixtures/semantic/MappyMap.scala | 170 ---------------- .../node/controlflow/BooleanBranchNode.java | 10 +- .../node/controlflow/BranchNode.java | 2 +- .../node/controlflow/CaseNode.java | 13 +- .../node/controlflow/CatchAllBranchNode.java | 11 +- .../controlflow/ConstructorBranchNode.java | 28 +-- .../builtin/InstantiateAtomNode.java | 2 +- .../builtin/interop/generic/EvalNode.java | 16 +- .../runtime/callable/function/Function.java | 15 +- .../main/scala/org/enso/compiler/Passes.scala | 1 - .../enso/compiler/codegen/IrToTruffle.scala | 18 +- .../pass/analyse/TailPatternMatch.scala | 81 -------- .../pass/desugar/NestedPatternMatch.scala | 9 +- .../test/semantic/NamedArgumentsTest.scala | 1 - .../org/enso/interpreter/dsl/Suspend.java | 2 +- .../dsl/model/MethodDefinition.java | 5 +- .../main/java/org/enso/base/Text_Utils.java | 11 ++ test/Benchmarks/src/Collections.enso | 8 +- test/Test/src/Data/Map_Spec.enso | 13 +- .../haskell/haskell-benchmark.cabal | 51 ----- 27 files changed, 234 insertions(+), 673 deletions(-) create mode 100644 distribution/std-lib/Base/src/Data/Map/Internal.enso delete mode 100644 distribution/std-lib/scratch delete mode 100644 distribution/std-lib/scratch.hs delete mode 100644 engine/runtime/src/bench/scala/org/enso/interpreter/bench/fixtures/semantic/MappyMap.scala delete mode 100644 engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailPatternMatch.scala delete mode 100644 tools/performance/comparative-benchmark/benchmarks/haskell/haskell-benchmark.cabal diff --git a/distribution/std-lib/Base/src/Data/Map.enso b/distribution/std-lib/Base/src/Data/Map.enso index b0e72be90d6a..d9b76f1b438d 100644 --- a/distribution/std-lib/Base/src/Data/Map.enso +++ b/distribution/std-lib/Base/src/Data/Map.enso @@ -1,21 +1,29 @@ from Base import all +import Base.Data.Map.Internal +## An error for getting a missing value from a map. type No_Value_For_Key key +## A key-value store. This type assumes all keys are pairwise comparable, + using the `<`, `>` and `==` operators. type Map type Tip type Bin s key value left right + ## Checks if the map is empty. is_empty : Boolean is_empty = case this of Bin _ _ _ _ _ -> False Tip -> True + ## Returns the number of entries in this map. size : Integer size = case this of Bin s _ _ _ _ -> s Tip -> 0 + ## Converts the map into a vector of `[key, value]` pairs. + The returned vector is sorted in the increasing order of keys. to_vector : Vector to_vector = builder = Vector.new_builder @@ -30,175 +38,42 @@ type Map result = builder.to_vector result + ## Returns a text representation of this map. + to_text : Text + to_text = this.to_vector.to_text + + ## Checks if this map is equal to another map. + + Maps are equal when they contained the same keys and the values + associated with each key are pairwise equal. + == : Map -> Boolean + == that = this.to_vector == that.to_vector + + ## Maps a function over each value in this map. map : (Any -> Any) -> Map map function = case this of Bin s k v l r -> Bin s k (function v) (l.map function) (r.map function) Tip -> Tip - #to_text : Text - #to_text = this.to_vector.to_text - - get : Any -> Any + ## Gets the value associated with `key` in this map, or returns a + `No_Value_For_Key` error, if `key` is not present. + get : Any -> Any ! No_Value_For_Key get key = go map = case map of Tip -> Error.throw (No_Value_For_Key key) Bin _ k v l r -> if k == key then v else - if k > key then go l else go r + if k > key then @Tail_Call go l else @Tail_Call go r result = go this result - #insert : Any -> Any -> Map - #insert key value = - #go map = case map of - #Tip -> Bin 1 key value Tip Tip - #Bin s k v l r -> - #insert_l = - #new_left = go l - #@Tail_Call here.balance_left k v new_left r - #insert_r = - #new_right = go r - #@Tail_Call here.balance_right k v l new_right - #if key > k then insert_r else - #if key == k then Bin s k value l r else - #insert_l - #go this - - == : Map -> Boolean - == that = this.to_vector == that.to_vector - - + ## Inserts a key-value mapping into this map. If `key` is already present, + it will be overriden with the new `value`. insert : Any -> Any -> Map - insert key value = here.insert this key value - -insert_l key value k v l r = - new_left = here.insert l key value - here.balance_left k v new_left r - -insert_r key value k v l r = - new_right = here.insert r key value - here.balance_right k v l new_right - -insert map key value = case map of - Bin s k v l r -> - if key > k then @Tail_Call here.insert_r key value k v l r else - if key == k then @Tail_Call Bin s k value l r else - @Tail_Call here.insert_l key value k v l r - _ -> Bin 1 key value Tip Tip - -#balance_left k x l r = case r of - #Tip -> case l of - #Tip -> Bin 1 k x Tip Tip - #Bin ls lk lx ll lr -> case lr of - #Tip -> case ll of - #Tip -> Bin 2 k x l Tip - #_ -> Bin 3 lk lx ll (Bin 1 k x Tip Tip) - #Bin lrs lrk lrx lrl lrr -> case ll of - #Tip -> Bin 3 lrk lrx (Bin 1 lk lx Tip Tip) (Bin 1 k x Tip Tip) - #Bin lls _ _ _ _ -> - #if lrs < Ratio*lls then Bin 1+ls lk lx ll (Bin 1+lrs k x lr Tip) else - #lrls = here.size lrl - #lrrs = here.size lrr - #Bin 1+ls lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+lrrs k x lrr Tip) - #Bin rs _ _ _ _ -> case l of - #Tip -> Bin 1+rs k x Tip r - #Bin ls lk lx ll lr -> - #if ls <= Delta*rs then Bin 1+ls+rs k x l r else - #lls = here.size ll - #case lr of - #Bin lrs lrk lrx lrl lrr -> - #if lrs < Ratio*lls then Bin 1+ls+rs lk lx ll (Bin 1+rs+lrs k x lr r) else - #lrls = here.size lrl - #lrrs = here.size lrr - #Bin 1+ls+rs lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+rs+lrrs k x lrr r) - - -balance_left k x l r = case r of - Bin rs _ _ _ _ -> case l of - Bin ls lk lx ll lr -> - if ls <= Delta*rs then Bin 1+ls+rs k x l r else - lls = here.size ll - case lr of - Bin lrs lrk lrx lrl lrr -> - if lrs < Ratio*lls then Bin 1+ls+rs lk lx ll (Bin 1+rs+lrs k x lr r) else - lrls = here.size lrl - lrrs = here.size lrr - Bin 1+ls+rs lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+rs+lrrs k x lrr r) - _ -> Bin 1+rs k x Tip r - _ -> case l of - Tip -> Bin 1 k x Tip Tip - Bin _ _ _ Tip Tip -> Bin 2 k x l Tip - Bin _ lk lx Tip (Bin _ lrk lrx _ _) -> Bin 3 lrk lrx (Bin 1 lk lx Tip Tip) (Bin 1 k x Tip Tip) - Bin _ lk lx ll Tip -> Bin 3 lk lx ll (Bin 1 k x Tip Tip) - Bin ls lk lx ll lr -> case lr of - Bin lrs lrk lrx lrl lrr -> - lls = here.size ll - if lrs < Ratio*lls then Bin 1+ls lk lx ll (Bin 1+lrs k x lr Tip) else - lrls = here.size lrl - lrrs = here.size lrr - Bin 1+ls lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+lrrs k x lrr Tip) - -size m = case m of - Bin s _ _ _ _ -> s - _ -> 0 - -balance_right k x l r = case l of - Bin ls _ _ _ _ -> case r of - Bin rs rk rx rl rr -> - if rs <= Delta*ls then Bin 1+ls+rs k x l r else - case rl of - Bin rls rlk rlx rll rlr -> - rrs = here.size rr - if rls < Ratio*rrs then Bin 1+ls+rs rk rx (Bin 1+ls+rls k x l rl) rr else - rlls = here.size rll - rlrs = here.size rlr - Bin 1+ls+rs rlk rlx (Bin 1+ls+rlls k x l rll) (Bin 1+rrs+rlrs rk rx rlr rr) - _ -> Bin 1+ls k x l Tip - _ -> case r of - Tip -> Bin 1 k x Tip Tip - Bin _ _ _ Tip Tip -> Bin 2 k x Tip r - Bin _ rk rx Tip rr -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr - Bin _ rk rx (Bin _ rlk rlx _ _) Tip -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip) - Bin rs rk rx (Bin rls rlk rlx rll rlr) rr -> case rr of - Bin rrs _ _ _ _ -> - if rls < Ratio*rrs then Bin 1+rs rk rx (Bin 1+rls k x Tip rl) rr else - srll = here.size rll - srlr = here.size rlr - Bin 1+rs rlk rlx (Bin 1+srll k x Tip rll) (Bin 1+rrs+srlr rk rx rlr rr) - -#balance_right k x l r = case l of - #Tip -> case r of - #Tip -> Bin 1 k x Tip Tip - #Bin rs rk rx rl rr -> case rl of - #Tip -> case rr of - #Tip -> Bin 2 k x Tip r - #_ -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr - #Bin rls rlk rlx rll rlr -> case rr of - #Tip -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip) - #Bin rrs _ _ _ _ -> - #if rls < Ratio*rrs then Bin 1+rs rk rx (Bin 1+rls k x Tip rl) rr else - #srll = here.size rll - #srlr = here.size rlr - #Bin 1+rs rlk rlx (Bin 1+srll k x Tip rll) (Bin 1+rrs+srlr rk rx rlr rr) - #Bin ls _ _ _ _ -> case r of - #Tip -> Bin 1+ls k x l Tip - #Bin rs rk rx rl rr -> - #if rs <= Delta*ls then Bin 1+ls+rs k x l r else - #case rl of - #Bin rls rlk rlx rll rlr -> - #rrs = here.size rr - #if rls < Ratio*rrs then Bin 1+ls+rs rk rx (Bin 1+ls+rls k x l rl) rr else - #rlls = here.size rll - #rlrs = here.size rlr - #Bin 1+ls+rs rlk rlx (Bin 1+ls+rlls k x l rll) (Bin 1+rrs+rlrs rk rx rlr rr) - + insert key value = Internal.insert this key value +## Returns an empty map. empty : Map empty = Tip -ratio : Integer -ratio = 2 - -delta : Integer -delta = 3 diff --git a/distribution/std-lib/Base/src/Data/Map/Internal.enso b/distribution/std-lib/Base/src/Data/Map/Internal.enso new file mode 100644 index 000000000000..d455869d853a --- /dev/null +++ b/distribution/std-lib/Base/src/Data/Map/Internal.enso @@ -0,0 +1,106 @@ +from Base import all +from Base.Data.Map import all + +## PRIVATE + + Helper used in the insert operation. +insert_l key value k v l r = + new_left = here.insert l key value + here.balance_left k v new_left r + +## PRIVATE + + Helper used in the insert operation. +insert_r key value k v l r = + new_right = here.insert r key value + here.balance_right k v l new_right + +## PRIVATE + + Helper for inserting a new key-value pair into a map. + The algorithm used here is based on the paper "Implementing Sets Efficiently + in a Functional Language" by Stephen Adams. + Implementation is based on Haskell's `Data.Map.Strict` implemented in the + `containers` package. +insert map key value = case map of + Bin s k v l r -> + if key > k then @Tail_Call here.insert_r key value k v l r else + if key == k then @Tail_Call Bin s k value l r else + @Tail_Call here.insert_l key value k v l r + _ -> Bin 1 key value Tip Tip + +## PRIVATE + + Rebalances the map after the left subtree grows. +balance_left k x l r = case r of + Bin rs _ _ _ _ -> case l of + Bin ls lk lx ll lr -> + if ls <= Delta*rs then Bin 1+ls+rs k x l r else + lls = here.size ll + case lr of + Bin lrs lrk lrx lrl lrr -> + if lrs < Ratio*lls then Bin 1+ls+rs lk lx ll (Bin 1+rs+lrs k x lr r) else + lrls = here.size lrl + lrrs = here.size lrr + Bin 1+ls+rs lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+rs+lrrs k x lrr r) + _ -> Bin 1+rs k x Tip r + _ -> case l of + Tip -> Bin 1 k x Tip Tip + Bin _ _ _ Tip Tip -> Bin 2 k x l Tip + Bin _ lk lx Tip (Bin _ lrk lrx _ _) -> Bin 3 lrk lrx (Bin 1 lk lx Tip Tip) (Bin 1 k x Tip Tip) + Bin _ lk lx ll Tip -> Bin 3 lk lx ll (Bin 1 k x Tip Tip) + Bin ls lk lx ll lr -> case lr of + Bin lrs lrk lrx lrl lrr -> + lls = here.size ll + if lrs < Ratio*lls then Bin 1+ls lk lx ll (Bin 1+lrs k x lr Tip) else + lrls = here.size lrl + lrrs = here.size lrr + Bin 1+ls lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+lrrs k x lrr Tip) + +## PRIVATE + + Rebalances the map after the right subtree grows. +balance_right k x l r = case l of + Bin ls _ _ _ _ -> case r of + Bin rs rk rx rl rr -> + if rs <= Delta*ls then Bin 1+ls+rs k x l r else + case rl of + Bin rls rlk rlx rll rlr -> + rrs = here.size rr + if rls < Ratio*rrs then Bin 1+ls+rs rk rx (Bin 1+ls+rls k x l rl) rr else + rlls = here.size rll + rlrs = here.size rlr + Bin 1+ls+rs rlk rlx (Bin 1+ls+rlls k x l rll) (Bin 1+rrs+rlrs rk rx rlr rr) + _ -> Bin 1+ls k x l Tip + _ -> case r of + Tip -> Bin 1 k x Tip Tip + Bin _ _ _ Tip Tip -> Bin 2 k x Tip r + Bin _ rk rx Tip rr -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr + Bin _ rk rx (Bin _ rlk rlx _ _) Tip -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip) + Bin rs rk rx (Bin rls rlk rlx rll rlr) rr -> case rr of + Bin rrs _ _ _ _ -> + if rls < Ratio*rrs then Bin 1+rs rk rx (Bin 1+rls k x Tip rl) rr else + srll = here.size rll + srlr = here.size rlr + Bin 1+rs rlk rlx (Bin 1+srll k x Tip rll) (Bin 1+rrs+srlr rk rx rlr rr) + +## PRIVATE + + Controls the difference between inner and outer siblings of a heavy subtree. + Used to decide between a double and a single rotation. +ratio : Integer +ratio = 2 + +## PRIVATE + + Controls the maximum size difference between subtrees. +delta : Integer +delta = 3 + +## PRIVATE + + Gets the size of a map. +size m = case m of + Bin s _ _ _ _ -> s + _ -> 0 + diff --git a/distribution/std-lib/Base/src/Test.enso b/distribution/std-lib/Base/src/Test.enso index 17c1cc74e0e4..2fa0b59530e1 100644 --- a/distribution/std-lib/Base/src/Test.enso +++ b/distribution/std-lib/Base/src/Test.enso @@ -38,7 +38,7 @@ type Verbs equal subject argument = if subject == argument then Success else - msg = this.to_text + " did not equal " + that.to_text + "." + msg = this.to_text + " did not equal " + argument.to_text + "." here.fail msg be subject argument = this.equal subject argument diff --git a/distribution/std-lib/Base/src/Text/Extensions.enso b/distribution/std-lib/Base/src/Text/Extensions.enso index 8497274a6fc3..481bdca9f1b8 100644 --- a/distribution/std-lib/Base/src/Text/Extensions.enso +++ b/distribution/std-lib/Base/src/Text/Extensions.enso @@ -69,6 +69,14 @@ Text.split_at separator = Text.== : Text -> Boolean Text.== that = Text_Utils.equals [this, that] +## Checks if `this` is lexicographically before `that`. +Text.< : Text -> Boolean +Text.< that = Text_Utils.lt [this, that] + +## Checks if `this` is lexicographically after `that`. +Text.> : Text -> Boolean +Text.> that = Text_Utils.lt [that, this] + ## Returns a vector containing bytes representing the UTF-8 encoding of the input text. @@ -103,4 +111,5 @@ Text.from_codepoints : Vector -> Text Text.from_codepoints codepoints = Text_Utils.from_codepoints [codepoints.to_array] ## Checks whether `this` starts with `prefix`. +Text.starts_with : Text -> Boolean Text.starts_with prefix = Text_Utils.starts_with [this, prefix] diff --git a/distribution/std-lib/scratch b/distribution/std-lib/scratch deleted file mode 100644 index cbe05dd3f419..000000000000 --- a/distribution/std-lib/scratch +++ /dev/null @@ -1,53 +0,0 @@ --- Functions balanceL and balanceR are specialised versions of balance. --- balanceL only checks whether the left subtree is too big, --- balanceR only checks whether the right subtree is too big. - --- balanceL is called when left subtree might have been inserted to or when --- right subtree might have been deleted from. -balanceL :: k -> a -> Map k a -> Map k a -> Map k a -balanceL k x l r = case r of - Tip -> case l of - Tip -> Bin 1 k x Tip Tip - (Bin _ _ _ Tip Tip) -> Bin 2 k x l Tip - (Bin _ lk lx Tip (Bin _ lrk lrx _ _)) -> Bin 3 lrk lrx (Bin 1 lk lx Tip Tip) (Bin 1 k x Tip Tip) - (Bin _ lk lx ll@(Bin _ _ _ _ _) Tip) -> Bin 3 lk lx ll (Bin 1 k x Tip Tip) - (Bin ls lk lx ll@(Bin lls _ _ _ _) lr@(Bin lrs lrk lrx lrl lrr)) - | lrs < ratio*lls -> Bin (1+ls) lk lx ll (Bin (1+lrs) k x lr Tip) - | otherwise -> Bin (1+ls) lrk lrx (Bin (1+lls+size lrl) lk lx ll lrl) (Bin (1+size lrr) k x lrr Tip) - - (Bin rs _ _ _ _) -> case l of - Tip -> Bin (1+rs) k x Tip r - - (Bin ls lk lx ll lr) - | ls > delta*rs -> case (ll, lr) of - (Bin lls _ _ _ _, Bin lrs lrk lrx lrl lrr) - | lrs < ratio*lls -> Bin (1+ls+rs) lk lx ll (Bin (1+rs+lrs) k x lr r) - | otherwise -> Bin (1+ls+rs) lrk lrx (Bin (1+lls+size lrl) lk lx ll lrl) (Bin (1+rs+size lrr) k x lrr r) - (_, _) -> error "Failure in Data.Map.balanceL" - | otherwise -> Bin (1+ls+rs) k x l r -{-# NOINLINE balanceL #-} - --- balanceR is called when right subtree might have been inserted to or when --- left subtree might have been deleted from. -balanceR :: k -> a -> Map k a -> Map k a -> Map k a -balanceR k x l r = case l of - Tip -> case r of - Tip -> Bin 1 k x Tip Tip - (Bin _ _ _ Tip Tip) -> Bin 2 k x Tip r - (Bin _ rk rx Tip rr@(Bin _ _ _ _ _)) -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr - (Bin _ rk rx (Bin _ rlk rlx _ _) Tip) -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip) - (Bin rs rk rx rl@(Bin rls rlk rlx rll rlr) rr@(Bin rrs _ _ _ _)) - | rls < ratio*rrs -> Bin (1+rs) rk rx (Bin (1+rls) k x Tip rl) rr - | otherwise -> Bin (1+rs) rlk rlx (Bin (1+size rll) k x Tip rll) (Bin (1+rrs+size rlr) rk rx rlr rr) - - (Bin ls _ _ _ _) -> case r of - Tip -> Bin (1+ls) k x l Tip - - (Bin rs rk rx rl rr) - | rs > delta*ls -> case (rl, rr) of - (Bin rls rlk rlx rll rlr, Bin rrs _ _ _ _) - | rls < ratio*rrs -> Bin (1+ls+rs) rk rx (Bin (1+ls+rls) k x l rl) rr - | otherwise -> Bin (1+ls+rs) rlk rlx (Bin (1+ls+size rll) k x l rll) (Bin (1+rrs+size rlr) rk rx rlr rr) - (_, _) -> error "Failure in Data.Map.balanceR" - | otherwise -> Bin (1+ls+rs) k x l r -{-# NOINLINE balanceR #-} diff --git a/distribution/std-lib/scratch.hs b/distribution/std-lib/scratch.hs deleted file mode 100644 index cbe05dd3f419..000000000000 --- a/distribution/std-lib/scratch.hs +++ /dev/null @@ -1,53 +0,0 @@ --- Functions balanceL and balanceR are specialised versions of balance. --- balanceL only checks whether the left subtree is too big, --- balanceR only checks whether the right subtree is too big. - --- balanceL is called when left subtree might have been inserted to or when --- right subtree might have been deleted from. -balanceL :: k -> a -> Map k a -> Map k a -> Map k a -balanceL k x l r = case r of - Tip -> case l of - Tip -> Bin 1 k x Tip Tip - (Bin _ _ _ Tip Tip) -> Bin 2 k x l Tip - (Bin _ lk lx Tip (Bin _ lrk lrx _ _)) -> Bin 3 lrk lrx (Bin 1 lk lx Tip Tip) (Bin 1 k x Tip Tip) - (Bin _ lk lx ll@(Bin _ _ _ _ _) Tip) -> Bin 3 lk lx ll (Bin 1 k x Tip Tip) - (Bin ls lk lx ll@(Bin lls _ _ _ _) lr@(Bin lrs lrk lrx lrl lrr)) - | lrs < ratio*lls -> Bin (1+ls) lk lx ll (Bin (1+lrs) k x lr Tip) - | otherwise -> Bin (1+ls) lrk lrx (Bin (1+lls+size lrl) lk lx ll lrl) (Bin (1+size lrr) k x lrr Tip) - - (Bin rs _ _ _ _) -> case l of - Tip -> Bin (1+rs) k x Tip r - - (Bin ls lk lx ll lr) - | ls > delta*rs -> case (ll, lr) of - (Bin lls _ _ _ _, Bin lrs lrk lrx lrl lrr) - | lrs < ratio*lls -> Bin (1+ls+rs) lk lx ll (Bin (1+rs+lrs) k x lr r) - | otherwise -> Bin (1+ls+rs) lrk lrx (Bin (1+lls+size lrl) lk lx ll lrl) (Bin (1+rs+size lrr) k x lrr r) - (_, _) -> error "Failure in Data.Map.balanceL" - | otherwise -> Bin (1+ls+rs) k x l r -{-# NOINLINE balanceL #-} - --- balanceR is called when right subtree might have been inserted to or when --- left subtree might have been deleted from. -balanceR :: k -> a -> Map k a -> Map k a -> Map k a -balanceR k x l r = case l of - Tip -> case r of - Tip -> Bin 1 k x Tip Tip - (Bin _ _ _ Tip Tip) -> Bin 2 k x Tip r - (Bin _ rk rx Tip rr@(Bin _ _ _ _ _)) -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr - (Bin _ rk rx (Bin _ rlk rlx _ _) Tip) -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip) - (Bin rs rk rx rl@(Bin rls rlk rlx rll rlr) rr@(Bin rrs _ _ _ _)) - | rls < ratio*rrs -> Bin (1+rs) rk rx (Bin (1+rls) k x Tip rl) rr - | otherwise -> Bin (1+rs) rlk rlx (Bin (1+size rll) k x Tip rll) (Bin (1+rrs+size rlr) rk rx rlr rr) - - (Bin ls _ _ _ _) -> case r of - Tip -> Bin (1+ls) k x l Tip - - (Bin rs rk rx rl rr) - | rs > delta*ls -> case (rl, rr) of - (Bin rls rlk rlx rll rlr, Bin rrs _ _ _ _) - | rls < ratio*rrs -> Bin (1+ls+rs) rk rx (Bin (1+ls+rls) k x l rl) rr - | otherwise -> Bin (1+ls+rs) rlk rlx (Bin (1+ls+size rll) k x l rll) (Bin (1+rrs+size rlr) rk rx rlr rr) - (_, _) -> error "Failure in Data.Map.balanceR" - | otherwise -> Bin (1+ls+rs) k x l r -{-# NOINLINE balanceR #-} diff --git a/engine/runtime/src/bench/java/org/enso/interpreter/bench/benchmarks/semantic/RecursionBenchmarks.java b/engine/runtime/src/bench/java/org/enso/interpreter/bench/benchmarks/semantic/RecursionBenchmarks.java index 169ed35af4bb..c302efa68dbe 100644 --- a/engine/runtime/src/bench/java/org/enso/interpreter/bench/benchmarks/semantic/RecursionBenchmarks.java +++ b/engine/runtime/src/bench/java/org/enso/interpreter/bench/benchmarks/semantic/RecursionBenchmarks.java @@ -1,19 +1,10 @@ package org.enso.interpreter.bench.benchmarks.semantic; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.concurrent.TimeUnit; - -import org.enso.interpreter.bench.fixtures.semantic.MappyMap; import org.enso.interpreter.bench.fixtures.semantic.RecursionFixtures; import org.enso.interpreter.test.DefaultInterpreterRunner; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.annotations.*; + +import java.util.concurrent.TimeUnit; @BenchmarkMode(Mode.AverageTime) @Fork(1) @@ -23,27 +14,6 @@ public class RecursionBenchmarks { private static RecursionFixtures recursionFixtures = new RecursionFixtures(); - static { - MappyMap.Map m = MappyMap.empty(); - for (int j = 0; j < 10; j++) { - m = MappyMap.insert(m, j, j); - } - System.out.println(m); - } - - private MappyMap.Map doMap(int n) { - MappyMap.Map m = MappyMap.empty(); - for (int i = 0; i < n; i++) { - m = MappyMap.insert(m, i, i); - } - return m; - } - - @Benchmark - public void benchMappyMap() { - doMap(10000); - } - private void runOnHundredMillion(DefaultInterpreterRunner.MainMethod main) { main.mainFunction().value().execute(main.mainConstructor(), recursionFixtures.hundredMillion()); } diff --git a/engine/runtime/src/bench/scala/org/enso/interpreter/bench/fixtures/semantic/MappyMap.scala b/engine/runtime/src/bench/scala/org/enso/interpreter/bench/fixtures/semantic/MappyMap.scala deleted file mode 100644 index 8a9906aed6cf..000000000000 --- a/engine/runtime/src/bench/scala/org/enso/interpreter/bench/fixtures/semantic/MappyMap.scala +++ /dev/null @@ -1,170 +0,0 @@ -package org.enso.interpreter.bench.fixtures.semantic - -object MappyMap { - sealed trait Map - case object Tip extends Map - case class Bin( - fields: Array[Object] - ) //size: Object, key: Object, value: Object, left: Map, right: Map) - extends Map - - object Bin { - def apply( - size: Object, - key: Object, - value: Object, - left: Map, - right: Map - ): Bin = { - Bin(Array(size, key, value, left, right)) - } - object X { - def unapply(arg: Bin): Option[(Object, Object, Object, Map, Map)] = { - val fs = arg.fields - Some( - ( - fs(0), - fs(1), - fs(2), - fs(3).asInstanceOf[Map], - fs(4).asInstanceOf[Map] - ) - ) - } - } - } -// def mk(): MappyMap.type = { -// println() -// } - - val x = Bin("Foo", "Bar", "Baz", Tip, Tip) - - def insert(m: Map, key: Object, value: Object): Map = - m match { - case Tip => Bin(1.asInstanceOf[Object], key, value, Tip, Tip) - case Bin.X(s, k, v, l, r) => - val keyI = key.asInstanceOf[Int] - val kI = k.asInstanceOf[Int] - if (keyI > kI) { - balanceR(k, v, l, insert(r, key, value)) - } else if (keyI == kI) { - Bin(s, k, value, l, r) - } else { - balanceL(k, v, insert(l, key, value), r) - } - } - - def size(m: Map): Int = - m match { - case Tip => 0 - case Bin.X(s, _, _, _, _) => s.asInstanceOf[Int] - } - - def empty: Map = Tip - - def balanceL(k: Object, v: Object, left: Map, right: Map): Map = ??? - - def balanceR(k: Object, x: Object, l: Map, r: Map): Map = { - l match { - case Tip => - r match { - - case Tip => Bin(1.asInstanceOf[Object], k, x, Tip, Tip) - case Bin.X(_, _, _, Tip, Tip) => - Bin(2.asInstanceOf[Object], k, x, Tip, r) - case Bin.X(_, rk, rx, Tip, rr @ Bin.X(_, _, _, _, _)) => - Bin( - 3.asInstanceOf[Object], - rk, - rx, - Bin(1.asInstanceOf[Object], k, x, Tip, Tip), - rr - ) - case Bin.X(_, rk, rx, Bin.X(_, rlk, rlx, _, _), Tip) => - Bin( - 3.asInstanceOf[Object], - rlk, - rlx, - Bin(1.asInstanceOf[Object], k, x, Tip, Tip), - Bin(1.asInstanceOf[Object], rk, rx, Tip, Tip) - ) - case Bin.X( - rsX, - rk, - rx, - rl @ Bin.X(rlsX, rlk, rlx, rll, rlr), - rr @ Bin.X(rrsX, _, _, _, _) - ) => - val rs: Int = rsX.asInstanceOf[Int] - val rls: Int = rlsX.asInstanceOf[Int] - val rrs: Int = rrsX.asInstanceOf[Int] - if (rls < ratio * rrs) { - Bin( - (1 + rs).asInstanceOf[Object], - rk, - rx, - Bin((1 + rls).asInstanceOf[Object], k, x, Tip, rl), - rr - ) - } else { - Bin( - (1 + rs).asInstanceOf[Object], - rlk, - rlx, - Bin((1 + size(rll)).asInstanceOf[Object], k, x, Tip, rll), - Bin((1 + rrs + size(rlr)).asInstanceOf[Object], rk, rx, rlr, rr) - ) - } - } - case Bin.X(lsX, _, _, _, _) => - val ls: Int = lsX.asInstanceOf[Int] - r match { - case Tip => Bin((1 + ls).asInstanceOf[Object], k, x, l, Tip) - case Bin.X(rsX, rk, rx, rl, rr) => - val rs: Int = rsX.asInstanceOf[Int] - if (rs > delta * ls) { - (rl, rr) match { - case (Bin.X(rlsX, rlk, rlx, rll, rlr), Bin.X(rrsX, _, _, _, _)) => - val rls = rlsX.asInstanceOf[Int] - val rrs = rrsX.asInstanceOf[Int] - if (rls < ratio * rrs) { - Bin( - (1 + ls + rs).asInstanceOf[Object], - rk, - rx, - Bin((1 + ls + rls).asInstanceOf[Object], k, x, l, rl), - rr - ) - } else { - Bin( - (1 + ls + rs).asInstanceOf[Object], - rlk, - rlx, - Bin( - (1 + ls + size(rll)).asInstanceOf[Object], - k, - x, - l, - rll - ), - Bin( - (1 + rrs + size(rlr)).asInstanceOf[Object], - rk, - rx, - rlr, - rr - ) - ) - } - case _ => ??? - } - } else { - Bin((1 + ls + rs).asInstanceOf[Object], k, x, l, r) - } - } - } - } - - val delta: Int = 3 - val ratio: Int = 2 -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java index 4158e573dca7..e80c6c659cee 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node.controlflow; +import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.FrameUtil; @@ -21,14 +22,12 @@ @NodeInfo(shortName = "BooleanMatch") public abstract class BooleanBranchNode extends BranchNode { private final boolean matched; - // private @Child ExpressionNode branch; - // private @Child ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create(); private final ConditionProfile profile = ConditionProfile.createCountingProfile(); private @Child DirectCallNode callNode; - BooleanBranchNode(boolean matched, CreateFunctionNode branch) { + BooleanBranchNode(boolean matched, RootCallTarget branch) { this.matched = matched; - this.callNode = DirectCallNode.create(branch.getCallTarget()); + this.callNode = DirectCallNode.create(branch); } /** @@ -38,7 +37,7 @@ public abstract class BooleanBranchNode extends BranchNode { * @param branch the expression to be executed if (@code matcher} matches * @return a node for matching in a case expression */ - public static BooleanBranchNode build(boolean matched, CreateFunctionNode branch) { + public static BooleanBranchNode build(boolean matched, RootCallTarget branch) { return BooleanBranchNodeGen.create(matched, branch); } @@ -46,6 +45,7 @@ public static BooleanBranchNode build(boolean matched, CreateFunctionNode branch * Handles the boolean scrutinee case. * * @param frame the stack frame in which to execute + * @param state current monadic state * @param target the atom to destructure */ @Specialization diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BranchNode.java index 44c637b950be..1f4d3c9c1472 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BranchNode.java @@ -15,8 +15,8 @@ public abstract class BranchNode extends BaseNode { * Executes the case branch. * * @param frame the stack frame in which to execute + * @param state current monadic state * @param target the object to match against */ public abstract void execute(VirtualFrame frame, Object state, Object target); - } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CaseNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CaseNode.java index d7d521a80c2d..9e26485dfb66 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CaseNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CaseNode.java @@ -30,13 +30,10 @@ @NodeInfo(shortName = "case_of", description = "The runtime representation of a case expression.") public abstract class CaseNode extends ExpressionNode { - private final boolean isTailCase; @Children private final BranchNode[] cases; - private final BranchProfile typeErrorProfile = BranchProfile.create(); - CaseNode(BranchNode[] cases, boolean isTailCase) { + CaseNode(BranchNode[] cases) { this.cases = cases; - this.isTailCase = isTailCase; } /** @@ -46,8 +43,8 @@ public abstract class CaseNode extends ExpressionNode { * @param cases the case branches * @return a node representing a pattern match */ - public static CaseNode build(ExpressionNode scrutinee, BranchNode[] cases, boolean isTailCase) { - return CaseNodeGen.create(cases, isTailCase, scrutinee); + public static CaseNode build(ExpressionNode scrutinee, BranchNode[] cases) { + return CaseNodeGen.create(cases, scrutinee); } /** @@ -110,8 +107,4 @@ boolean isError(Object error) { * The main alternative to this was desugaring to a nested-if, which would've been significantly * harder to maintain, and also resulted in significantly higher code complexity. */ - - public boolean isTailCase() { - return isTailCase; - } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CatchAllBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CatchAllBranchNode.java index 14e711ea8fca..002f4d740f2d 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CatchAllBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CatchAllBranchNode.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node.controlflow; +import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.frame.FrameUtil; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; @@ -23,13 +24,10 @@ shortName = "Catch_All", description = "An explicit catch-all branch in a case expression") public class CatchAllBranchNode extends BranchNode { -// @Child private ExpressionNode functionNode; - // @Child private ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create(); private @Child DirectCallNode callNode; - private final ConditionProfile profile = ConditionProfile.createCountingProfile(); - private CatchAllBranchNode(CreateFunctionNode functionNode) { - this.callNode = DirectCallNode.create(functionNode.getCallTarget()); + private CatchAllBranchNode(RootCallTarget functionNode) { + this.callNode = DirectCallNode.create(functionNode); } /** @@ -38,7 +36,7 @@ private CatchAllBranchNode(CreateFunctionNode functionNode) { * @param functionNode the function to execute in this case * @return a catch-all node */ - public static CatchAllBranchNode build(CreateFunctionNode functionNode) { + public static CatchAllBranchNode build(RootCallTarget functionNode) { return new CatchAllBranchNode(functionNode); } @@ -46,6 +44,7 @@ public static CatchAllBranchNode build(CreateFunctionNode functionNode) { * Executes the case branch on an arbitrary target. * * @param frame the stack frame in which to execute + * @param state current monadic state * @param target the object to match against */ public void execute(VirtualFrame frame, Object state, Object target) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/ConstructorBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/ConstructorBranchNode.java index 6fd425549f3b..585dc292a749 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/ConstructorBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/ConstructorBranchNode.java @@ -1,6 +1,7 @@ package org.enso.interpreter.node.controlflow; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.FrameUtil; @@ -24,14 +25,12 @@ @NodeInfo(shortName = "ConstructorMatch") public abstract class ConstructorBranchNode extends BranchNode { private final AtomConstructor matcher; - // private @Child ExpressionNode branch; - // private @Child ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create(); private @Child DirectCallNode callNode; private final ConditionProfile profile = ConditionProfile.createCountingProfile(); - ConstructorBranchNode(AtomConstructor matcher, CreateFunctionNode branch) { + ConstructorBranchNode(AtomConstructor matcher, RootCallTarget branch) { this.matcher = matcher; - this.callNode = DirectCallNode.create(branch.getCallTarget()); + this.callNode = DirectCallNode.create(branch); } /** @@ -41,20 +40,10 @@ public abstract class ConstructorBranchNode extends BranchNode { * @param branch the expression to be executed if (@code matcher} matches * @return a node for matching in a case expression */ - public static ConstructorBranchNode build(AtomConstructor matcher, CreateFunctionNode branch) { + public static ConstructorBranchNode build(AtomConstructor matcher, RootCallTarget branch) { return ConstructorBranchNodeGen.create(matcher, branch); } - @ExplodeLoop - private Object[] copyArgs(Object[] fields) { - Object[] res = new Object[matcher.getArity()]; -// CompilerDirectives.ensureVirtualized(res); - for (int i = 0; i < matcher.getArity(); i++) { - res[i] = fields[i]; - } - return res; - } - /** * Handles the atom scrutinee case. * @@ -62,21 +51,18 @@ private Object[] copyArgs(Object[] fields) { * all the atom's fields as arguments. * * @param frame the stack frame in which to execute + * @param state current monadic state * @param target the atom to destructure */ @Specialization public void doAtom(VirtualFrame frame, Object state, Atom target) { if (profile.profile(matcher == target.getConstructor())) { - // Function function = TypesGen.asFunction(branch.executeGeneric(frame)); - -// Object[] args = copyArgs(target.getFields()); - + // Note [Caller Info For Case Branches] Stateful result = (Stateful) callNode.call( Function.ArgumentsHelper.buildArguments( - frame.materialize(), null, state, target.getFields())); - // Note [Caller Info For Case Branches] + frame.materialize(), state, target.getFields())); throw new BranchSelectedException(result); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java index a2955516c940..9ba2aea468ad 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java @@ -33,7 +33,7 @@ private InstantiateAtomNode(Language language, String name, ExpressionNode insta public Stateful execute(VirtualFrame frame) { return new Stateful( Function.ArgumentsHelper.getState(frame.getArguments()), - instantiator.executeGeneric(frame)); + instantiator.executeGeneric(frame)); } /** diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/EvalNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/EvalNode.java index 1f1c973ce4f3..1dad7454c6d9 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/EvalNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/EvalNode.java @@ -6,6 +6,7 @@ import com.oracle.truffle.api.dsl.ReportPolymorphism; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.Source; import org.enso.interpreter.Language; @@ -46,11 +47,24 @@ Object doCached( return hostValueToEnsoNode.execute(callNode.call()); } + @Specialization(replaces = "doCached") + Object doUncached( + Object _this, + Text language, + Text code, + @CachedContext(Language.class) Context context, + @Cached IndirectCallNode callNode, + @Cached("build()") ToJavaStringNode toJavaStringNode, + @Cached("build()") HostValueToEnsoNode hostValueToEnsoNode) { + CallTarget ct = parse(context, language, code, toJavaStringNode); + return hostValueToEnsoNode.execute(callNode.call(ct)); + } + CallTarget parse(Context context, Text language, Text code, ToJavaStringNode toJavaStringNode) { String languageStr = toJavaStringNode.execute(language); String codeStr = toJavaStringNode.execute(code); Source source = Source.newBuilder(languageStr, codeStr, "").build(); - return context.getEnvironment().parsePublic(source, "foo"); + return context.getEnvironment().parsePublic(source); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java index 3e7c02c86996..d7d33713d525 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java @@ -287,12 +287,17 @@ public static Object[] buildArguments( return new Object[] {function.getScope(), callerInfo, state, positionalArguments}; } + /** + * Generates an array of arguments using the schema to be passed to a call target. + * + * @param frame the frame becoming the lexical scope + * @param state the state to execute the thunk with + * @param positionalArguments the positional arguments to the call target + * @return an array containing the necessary information to call an Enso function + */ public static Object[] buildArguments( - MaterializedFrame frame, - CallerInfo callerInfo, - Object state, - Object[] positionalArguments) { - return new Object[] {frame, callerInfo, state, positionalArguments}; + MaterializedFrame frame, Object state, Object[] positionalArguments) { + return new Object[] {frame, null, state, positionalArguments}; } /** diff --git a/engine/runtime/src/main/scala/org/enso/compiler/Passes.scala b/engine/runtime/src/main/scala/org/enso/compiler/Passes.scala index 6340ad9e8e25..1cafacdfab5d 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/Passes.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/Passes.scala @@ -40,7 +40,6 @@ class Passes(passes: Option[List[PassGroup]] = None) { ShadowedPatternFields, UnreachableMatchBranches, NestedPatternMatch, - TailPatternMatch, IgnoredBindings, TypeFunctions, TypeSignatures, diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index 77ea56e5c194..686170e08ff0 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -15,8 +15,7 @@ import org.enso.compiler.pass.analyse.{ AliasAnalysis, BindingAnalysis, DataflowAnalysis, - TailCall, - TailPatternMatch + TailCall } import org.enso.compiler.pass.optimise.ApplicationSaturation import org.enso.compiler.pass.resolve.{ @@ -599,8 +598,7 @@ class IrToTruffle( // Note [Pattern Match Fallbacks] val matchExpr = CaseNode.build( scrutineeNode, - cases, - caseExpr.getMetadata(TailPatternMatch).isDefined + cases ) setLocation(matchExpr, location) } else { @@ -649,7 +647,8 @@ class IrToTruffle( branch.location ) - val branchNode = CatchAllBranchNode.build(branchCodeNode) + val branchNode = + CatchAllBranchNode.build(branchCodeNode.getCallTarget) Right(branchNode) case cons @ Pattern.Constructor(constructor, _, _, _, _) => @@ -709,11 +708,14 @@ class IrToTruffle( val bool = context.getBuiltins.bool() val branchNode: BranchNode = if (atomCons == bool.getTrue) { - BooleanBranchNode.build(true, branchCodeNode) + BooleanBranchNode.build(true, branchCodeNode.getCallTarget) } else if (atomCons == bool.getFalse) { - BooleanBranchNode.build(false, branchCodeNode) + BooleanBranchNode.build(false, branchCodeNode.getCallTarget) } else { - ConstructorBranchNode.build(atomCons, branchCodeNode) + ConstructorBranchNode.build( + atomCons, + branchCodeNode.getCallTarget + ) } branchNode diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailPatternMatch.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailPatternMatch.scala deleted file mode 100644 index 14c45256403c..000000000000 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/TailPatternMatch.scala +++ /dev/null @@ -1,81 +0,0 @@ -package org.enso.compiler.pass.analyse - -import org.enso.compiler.context.{InlineContext, ModuleContext} -import org.enso.compiler.core.IR -import org.enso.compiler.core.ir.MetadataStorage.ToPair -import org.enso.compiler.pass.IRPass - -import scala.annotation.unused - -case object TailPatternMatch extends IRPass { - case object TailMatch extends IRPass.Metadata { - - /** The name of the metadata as a string. */ - override val metadataName: String = "TailMatch" - - override def duplicate(): Option[IRPass.Metadata] = Some(this) - } - - override type Metadata = TailMatch.type - override type Config = IRPass.Configuration.Default - - override val precursorPasses: Seq[IRPass] = List() - override val invalidatedPasses: Seq[IRPass] = List() - - /** Desugars nested pattern matches in a module. - * - * @param ir the Enso IR to process - * @param moduleContext a context object that contains the information needed - * to process a module - * @return `ir`, possibly having made transformations or annotations to that - * IR. - */ - override def runModule( - ir: IR.Module, - @unused moduleContext: ModuleContext - ): IR.Module = { - - ir.mapExpressions(analyseExpression) - } - - /** Desugars nested pattern matches in an expression. - * - * @param ir the Enso IR to process - * @param inlineContext a context object that contains the information needed - * for inline evaluation - * @return `ir`, possibly having made transformations or annotations to that - * IR. - */ - override def runExpression( - ir: IR.Expression, - @unused inlineContext: InlineContext - ): IR.Expression = { - analyseExpression(ir) - } - - private def analyseExpression(expr: IR.Expression): IR.Expression = - expr.transformExpressions { - case cse: IR.Case.Expr => - cse.copy(branches = cse.branches.map(analyseBranch)) - } - - private def analyseBranch(branch: IR.Case.Branch): IR.Case.Branch = { - val expr = branch.expression match { - case caseExpr: IR.Case.Expr => - caseExpr - .updateMetadata(this -->> TailMatch) - .copy(branches = caseExpr.branches.map(analyseBranch)) - case block @ IR.Expression.Block(exprs, ret, _, _, _, _) => - val newRet = ret match { - case cs: IR.Case.Expr => - cs.updateMetadata(this -->> TailMatch) - .copy(branches = cs.branches.map(analyseBranch)) - case expr => analyseExpression(expr) - } - val newExprs = exprs.map(analyseExpression) - block.copy(expressions = newExprs, returnValue = newRet) - case expr => analyseExpression(expr) - } - branch.copy(expression = expr) - } -} diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala index 426bdf985012..7480cb49a6f3 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/NestedPatternMatch.scala @@ -157,14 +157,7 @@ case object NestedPatternMatch extends IRPass { freshNameSupply: FreshNameSupply ): IR.Expression = { expr.transformExpressions { - case cse: IR.Case => - val r = desugarCase(cse, freshNameSupply) -// println("Result of case desugaring: ") -// println("BEFORE ") -// println(cse.showCode()) -// println("AFTER ") -// println(r.showCode()) - r + case cse: IR.Case => desugarCase(cse, freshNameSupply) } } diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/NamedArgumentsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/NamedArgumentsTest.scala index c60fcdbfb1ac..baca6421cb9f 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/NamedArgumentsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/NamedArgumentsTest.scala @@ -211,7 +211,6 @@ class NamedArgumentsTest extends InterpreterTest { } "be usable and overridable in constructors" in { - pending val code = """ |type Nil2 diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/Suspend.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/Suspend.java index f238cb5cf80c..a2f4c52b7015 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/Suspend.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/Suspend.java @@ -5,7 +5,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -/** An interface marking an argument as requiring to be passed the current monadic state. */ +/** An interface marking an argument as suspended. */ @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.SOURCE) public @interface Suspend {} diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java index 297d18455238..595479068355 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java @@ -220,7 +220,10 @@ public boolean validate(ProcessingEnvironment processingEnvironment) { if (type.toString().equals(THUNK)) { processingEnvironment .getMessager() - .printMessage(Diagnostic.Kind.ERROR, "Thunk argument, I refuse", element); + .printMessage( + Diagnostic.Kind.ERROR, + "Argument must not be typed as Thunk. Use @Suspend instead.", + element); return false; } return true; diff --git a/std-bits/src/main/java/org/enso/base/Text_Utils.java b/std-bits/src/main/java/org/enso/base/Text_Utils.java index c9377dd265f2..d06733e5c9ee 100644 --- a/std-bits/src/main/java/org/enso/base/Text_Utils.java +++ b/std-bits/src/main/java/org/enso/base/Text_Utils.java @@ -95,4 +95,15 @@ public static String from_utf_8(byte[] bytes) { public static boolean starts_with(String str, String prefix) { return str.startsWith(prefix); } + + /** + * Checks whether {@code a} is lexicographically before {@code b}. + * + * @param a the left operand + * @param b the right operant + * @return whether {@code a} is before {@code b}. + */ + public static boolean lt(String a, String b) { + return a.compareTo(b) < 0; + } } diff --git a/test/Benchmarks/src/Collections.enso b/test/Benchmarks/src/Collections.enso index 9ad203654fe9..a2c614b4188e 100644 --- a/test/Benchmarks/src/Collections.enso +++ b/test/Benchmarks/src/Collections.enso @@ -27,8 +27,8 @@ main = list = here.gen_list mil vec = Vector.new mil (ix -> ix + 1) vec_decimal = Vector.new mil (ix -> ix + 0.0) - Bench_Utils.measure (here.sum_list_meta list) "list meta-fold" 1000 10 - Bench_Utils.measure (list.fold 0 (+)) "list fold" 1000 10 - Bench_Utils.measure (vec.fold 0 (+)) "vector fold" 1000 10 - Bench_Utils.measure (vec_decimal.fold 0 (+)) "vector decimal fold" 1000 10 + #Bench_Utils.measure (here.sum_list_meta list) "list meta-fold" 1000 10 + #Bench_Utils.measure (list.fold 0 (+)) "list fold" 1000 10 + #Bench_Utils.measure (vec.fold 0 (+)) "vector fold" 1000 10 + #Bench_Utils.measure (vec_decimal.fold 0 (+)) "vector decimal fold" 1000 10 Bench_Utils.measure (here.build_map 10000) "build a map" 100 10 diff --git a/test/Test/src/Data/Map_Spec.enso b/test/Test/src/Data/Map_Spec.enso index b086bde5ca67..17d152846a7c 100644 --- a/test/Test/src/Data/Map_Spec.enso +++ b/test/Test/src/Data/Map_Spec.enso @@ -3,7 +3,12 @@ from Base import all import Base.Test spec = describe "Maps" <| - m = 1 . upto 10000 . fold Map.empty (m -> i -> m.insert i i) - IO.println m.size - IO.println (m.get 9999) - + it "should allow inserting and looking up values" <| + m = Map.empty . insert "foo" 134 . insert "bar" 654 . insert "baz" "spam" + m.get "foo" . should equal 134 + m.get "bar" . should equal 654 + m.get "baz" . should equal "spam" + m.get "nope" . catch e->e . should_equal (Map.No_Value_For_Key "nope") + it "should convert the whole map to a vector" <| + m = Map.empty . insert 0 0 . insert 3 -5 . insert 1 2 + m.to_vector.should equal [[0, 0], [1, 2], [3, -5]] diff --git a/tools/performance/comparative-benchmark/benchmarks/haskell/haskell-benchmark.cabal b/tools/performance/comparative-benchmark/benchmarks/haskell/haskell-benchmark.cabal deleted file mode 100644 index 252678e078d5..000000000000 --- a/tools/performance/comparative-benchmark/benchmarks/haskell/haskell-benchmark.cabal +++ /dev/null @@ -1,51 +0,0 @@ -cabal-version: 1.12 - --- This file has been generated from package.yaml by hpack version 0.33.0. --- --- see: https://github.com/sol/hpack --- --- hash: cdb5656f9b7103b47ac57b68d3d2cf6e1e5162f7abc9eb075675918adef47457 - -name: haskell-benchmark -version: 0.1.0.0 -description: A benchmark for comparing Haskell to Enso. -homepage: https://github.com/https://github.com/enso-org/enso#readme -bug-reports: https://github.com/https://github.com/enso-org/enso/issues -author: Ara Adkins -maintainer: ara.adkins@enso.org -copyright: Enso Team 2020 -build-type: Simple - -source-repository head - type: git - location: https://github.com/https://github.com/enso-org/enso - -library - exposed-modules: - Fixtures - other-modules: - Paths_haskell_benchmark - hs-source-dirs: - src - default-extensions: AllowAmbiguousTypes ApplicativeDo BangPatterns BinaryLiterals ConstraintKinds DataKinds DefaultSignatures DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DerivingStrategies DuplicateRecordFields EmptyDataDecls FlexibleContexts FlexibleInstances FunctionalDependencies GeneralizedNewtypeDeriving InstanceSigs LambdaCase LiberalTypeSynonyms MonadComprehensions MultiWayIf NamedWildCards NegativeLiterals NoImplicitPrelude NumDecimals OverloadedLabels OverloadedStrings PatternSynonyms QuasiQuotes RankNTypes RecursiveDo ScopedTypeVariables StandaloneDeriving Strict StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeFamilyDependencies TypeOperators UnicodeSyntax ViewPatterns - build-depends: - base - , containers - , deepseq - , random - default-language: Haskell2010 - -benchmark haskell-benchmark - type: exitcode-stdio-1.0 - main-is: Main.hs - other-modules: - Paths_haskell_benchmark - hs-source-dirs: - test/bench - default-extensions: AllowAmbiguousTypes ApplicativeDo BangPatterns BinaryLiterals ConstraintKinds DataKinds DefaultSignatures DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DerivingStrategies DuplicateRecordFields EmptyDataDecls FlexibleContexts FlexibleInstances FunctionalDependencies GeneralizedNewtypeDeriving InstanceSigs LambdaCase LiberalTypeSynonyms MonadComprehensions MultiWayIf NamedWildCards NegativeLiterals NoImplicitPrelude NumDecimals OverloadedLabels OverloadedStrings PatternSynonyms QuasiQuotes RankNTypes RecursiveDo ScopedTypeVariables StandaloneDeriving Strict StrictData TemplateHaskell TupleSections TypeApplications TypeFamilies TypeFamilyDependencies TypeOperators UnicodeSyntax ViewPatterns - build-depends: - base - , criterion - , deepseq - , haskell-benchmark - default-language: Haskell2010 From ac21ca85041ecdc31eea7d1e3fe61ce0c67afa40 Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Mon, 19 Oct 2020 18:08:10 +0200 Subject: [PATCH 8/9] fixes --- .../org/enso/interpreter/node/callable/thunk/ForceNode.java | 2 +- .../enso/interpreter/node/controlflow/BooleanBranchNode.java | 2 +- .../enso/interpreter/node/controlflow/CatchAllBranchNode.java | 2 +- .../node/expression/builtin/InstantiateAtomNode.java | 3 --- .../node/expression/builtin/function/ApplicationOperator.java | 3 ++- .../src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala | 2 ++ .../org/enso/interpreter/test/semantic/CodeLocationsTest.scala | 1 - .../comparative-benchmark/benchmarks/haskell/src/Fixtures.hs | 2 +- 8 files changed, 8 insertions(+), 9 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ForceNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ForceNode.java index 5bc32c322a87..90bff0d36301 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ForceNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/thunk/ForceNode.java @@ -30,7 +30,7 @@ public static ForceNode build(ExpressionNode target) { @Specialization Object passToExecutorNode( VirtualFrame frame, - Thunk thunk, + Object thunk, @Cached("build()") ThunkExecutorNode thunkExecutorNode) { Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot()); Stateful result = thunkExecutorNode.executeThunk(thunk, state, getTailStatus()); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java index e80c6c659cee..4dcb6388c30d 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/BooleanBranchNode.java @@ -55,7 +55,7 @@ public void doAtom(VirtualFrame frame, Object state, boolean target) { (Stateful) callNode.call( Function.ArgumentsHelper.buildArguments( - frame.materialize(), null, state, new Object[0])); + frame.materialize(), state, new Object[0])); // Note [Caller Info For Case Branches] throw new BranchSelectedException(result); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CatchAllBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CatchAllBranchNode.java index 002f4d740f2d..c99863b3650a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CatchAllBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CatchAllBranchNode.java @@ -53,7 +53,7 @@ public void execute(VirtualFrame frame, Object state, Object target) { (Stateful) callNode.call( Function.ArgumentsHelper.buildArguments( - frame.materialize(), null, state, new Object[] {target})); + frame.materialize(), state, new Object[] {target})); throw new BranchSelectedException(result); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java index 9ba2aea468ad..8a8fa58c73f1 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/InstantiateAtomNode.java @@ -1,13 +1,10 @@ package org.enso.interpreter.node.expression.builtin; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.RootNode; import org.enso.interpreter.Language; import org.enso.interpreter.node.ExpressionNode; -import org.enso.interpreter.node.expression.atom.InstantiateNode; -import org.enso.interpreter.runtime.callable.atom.AtomConstructor; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.state.Stateful; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java index f4651b9bc4cd..12d2a5bc711b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/function/ApplicationOperator.java @@ -28,7 +28,8 @@ public class ApplicationOperator extends Node { invokeCallableNode.setTailStatus(BaseNode.TailStatus.TAIL_DIRECT); } - Stateful execute(VirtualFrame frame, @MonadicState Object state, Function _this, @Suspend Object argument) { + Stateful execute( + VirtualFrame frame, @MonadicState Object state, Function _this, @Suspend Object argument) { return invokeCallableNode.execute(_this, frame, state, new Object[] {argument}); } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index 686170e08ff0..bdb7db16e872 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -1179,6 +1179,8 @@ class IrToTruffle( val shouldSuspend = value match { case _: IR.Name => false + case _: IR.Literal.Text => false + case _: IR.Literal.Number => false case _ => shouldBeSuspended.getOrElse( throw new CompilerError( diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CodeLocationsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CodeLocationsTest.scala index 96b7fb761c05..4d33bde9b724 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CodeLocationsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CodeLocationsTest.scala @@ -279,7 +279,6 @@ class CodeLocationsTest extends InterpreterTest { instrumenter.assertNodeExists(55, 67, classOf[CaseNode]) instrumenter.assertNodeExists(60, 1, classOf[ReadLocalVariableNode]) instrumenter.assertNodeExists(103, 3, classOf[IntegerLiteralNode]) - instrumenter.assertNodeExists(73, 33, classOf[CreateFunctionNode]) eval(code) shouldEqual 100 } diff --git a/tools/performance/comparative-benchmark/benchmarks/haskell/src/Fixtures.hs b/tools/performance/comparative-benchmark/benchmarks/haskell/src/Fixtures.hs index 0aae9689d1de..da1f19354259 100644 --- a/tools/performance/comparative-benchmark/benchmarks/haskell/src/Fixtures.hs +++ b/tools/performance/comparative-benchmark/benchmarks/haskell/src/Fixtures.hs @@ -74,4 +74,4 @@ buildMap :: Integer -> Map.Map Integer Integer buildMap n = foldl' (\m i -> Map.insert i i m) Map.empty [0..n] buildRandomMap :: Integer -> IO (Map.Map Integer Integer) -buildRandomMap n = foldM (\m i -> fmap (\key -> Map.insert key i m) $ randomRIO (0, 10000)) Map.empty [0..n] \ No newline at end of file +buildRandomMap n = foldM (\m i -> fmap (\key -> Map.insert key i m) $ randomRIO (0, 10000)) Map.empty [0..n] From a77b20ce510b3beccb6f71b49a9ab43af9f1ca5b Mon Sep 17 00:00:00 2001 From: Marcin Kostrzewa Date: Tue, 20 Oct 2020 12:39:47 +0200 Subject: [PATCH 9/9] CR feedback --- distribution/bin/enso | 2 +- distribution/std-lib/Base/src/Data/Map/Internal.enso | 7 +++++++ .../org/enso/interpreter/node/controlflow/CaseNode.java | 3 --- .../node/expression/builtin/interop/generic/EvalNode.java | 2 +- .../org/enso/interpreter/dsl/model/MethodDefinition.java | 2 +- std-bits/src/main/java/org/enso/base/Text_Utils.java | 2 +- test/Benchmarks/src/Collections.enso | 8 ++++---- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/distribution/bin/enso b/distribution/bin/enso index fe7f14afc3ea..2c34c242061b 100755 --- a/distribution/bin/enso +++ b/distribution/bin/enso @@ -1,3 +1,3 @@ COMP_PATH=$(dirname "$0")/../component -exec java -jar -Dtruffle.class.path.append="$COMP_PATH/runtime.jar" -Dpolyglot.engine.IterativePartialEscape=true $JAVA_OPTS $COMP_PATH/runner.jar "$@" +exec java -jar -Dtruffle.class.path.append="$COMP_PATH/runtime.jar" $JAVA_OPTS $COMP_PATH/runner.jar "$@" exit diff --git a/distribution/std-lib/Base/src/Data/Map/Internal.enso b/distribution/std-lib/Base/src/Data/Map/Internal.enso index d455869d853a..141f8f20ae1e 100644 --- a/distribution/std-lib/Base/src/Data/Map/Internal.enso +++ b/distribution/std-lib/Base/src/Data/Map/Internal.enso @@ -18,6 +18,7 @@ insert_r key value k v l r = ## PRIVATE Helper for inserting a new key-value pair into a map. + The algorithm used here is based on the paper "Implementing Sets Efficiently in a Functional Language" by Stephen Adams. Implementation is based on Haskell's `Data.Map.Strict` implemented in the @@ -88,12 +89,18 @@ balance_right k x l r = case l of Controls the difference between inner and outer siblings of a heavy subtree. Used to decide between a double and a single rotation. + + The choice of values for `ratio` and `delta` is taken from the Haskell + implementation. ratio : Integer ratio = 2 ## PRIVATE Controls the maximum size difference between subtrees. + + The choice of values for `ratio` and `delta` is taken from the Haskell + implementation. delta : Integer delta = 3 diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CaseNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CaseNode.java index 9e26485dfb66..1b74701e737f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CaseNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/CaseNode.java @@ -3,18 +3,15 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.dsl.CachedContext; -import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.FrameUtil; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.NodeInfo; -import com.oracle.truffle.api.profiles.BranchProfile; import org.enso.interpreter.Language; import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.runtime.Context; -import org.enso.interpreter.runtime.callable.atom.Atom; import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.RuntimeError; import org.enso.interpreter.runtime.type.TypesGen; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/EvalNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/EvalNode.java index 1dad7454c6d9..dfbad794ea19 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/EvalNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/generic/EvalNode.java @@ -64,7 +64,7 @@ CallTarget parse(Context context, Text language, Text code, ToJavaStringNode toJ String languageStr = toJavaStringNode.execute(language); String codeStr = toJavaStringNode.execute(code); - Source source = Source.newBuilder(languageStr, codeStr, "").build(); + Source source = Source.newBuilder(languageStr, codeStr, "").build(); return context.getEnvironment().parsePublic(source); } } diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java index 595479068355..58a5ec3e3eac 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java @@ -222,7 +222,7 @@ public boolean validate(ProcessingEnvironment processingEnvironment) { .getMessager() .printMessage( Diagnostic.Kind.ERROR, - "Argument must not be typed as Thunk. Use @Suspend instead.", + "Argument must not be typed as Thunk. Use @Suspend Object instead.", element); return false; } diff --git a/std-bits/src/main/java/org/enso/base/Text_Utils.java b/std-bits/src/main/java/org/enso/base/Text_Utils.java index d06733e5c9ee..9d1c0db006ff 100644 --- a/std-bits/src/main/java/org/enso/base/Text_Utils.java +++ b/std-bits/src/main/java/org/enso/base/Text_Utils.java @@ -100,7 +100,7 @@ public static boolean starts_with(String str, String prefix) { * Checks whether {@code a} is lexicographically before {@code b}. * * @param a the left operand - * @param b the right operant + * @param b the right operand * @return whether {@code a} is before {@code b}. */ public static boolean lt(String a, String b) { diff --git a/test/Benchmarks/src/Collections.enso b/test/Benchmarks/src/Collections.enso index a2c614b4188e..9ad203654fe9 100644 --- a/test/Benchmarks/src/Collections.enso +++ b/test/Benchmarks/src/Collections.enso @@ -27,8 +27,8 @@ main = list = here.gen_list mil vec = Vector.new mil (ix -> ix + 1) vec_decimal = Vector.new mil (ix -> ix + 0.0) - #Bench_Utils.measure (here.sum_list_meta list) "list meta-fold" 1000 10 - #Bench_Utils.measure (list.fold 0 (+)) "list fold" 1000 10 - #Bench_Utils.measure (vec.fold 0 (+)) "vector fold" 1000 10 - #Bench_Utils.measure (vec_decimal.fold 0 (+)) "vector decimal fold" 1000 10 + Bench_Utils.measure (here.sum_list_meta list) "list meta-fold" 1000 10 + Bench_Utils.measure (list.fold 0 (+)) "list fold" 1000 10 + Bench_Utils.measure (vec.fold 0 (+)) "vector fold" 1000 10 + Bench_Utils.measure (vec_decimal.fold 0 (+)) "vector decimal fold" 1000 10 Bench_Utils.measure (here.build_map 10000) "build a map" 100 10