diff --git a/ChangeLog b/ChangeLog index 6be4b5de854b714cbeae57430bf2b9c8500f2169..798df6c9829196ab917282b88c9d77c47a8571dc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2016-03-21 Jesper Öqvist <jesper.oqvist@cs.lth.se> + + * Added --safeLazy option which allows non-circular attributes to be + safely cached during circular evaluation, even if the attribute is + effectively circular. The --safeLazy option adds an extra cache field for + each attributes which tracks the last cycle on which the attribute was + evaluated. The next time the attribute is accessed it checks if it can + reuse the last cached value based on the current cycle state and the last + cached cycle state. + 2016-03-16 Jesper Öqvist <jesper.oqvist@cs.lth.se> * Improved circular NTA rewrite implementation: implicit rewrite diff --git a/src/jastadd/ast/JragCodeGen.jrag b/src/jastadd/ast/JragCodeGen.jrag index bb240e0d5a31f2ecad6c0fbd6d27a26b7c953335..aa4ce8a5e03ecbb1174e68577ceeaa902a9a88b9 100644 --- a/src/jastadd/ast/JragCodeGen.jrag +++ b/src/jastadd/ast/JragCodeGen.jrag @@ -226,6 +226,10 @@ aspect JragCodeGen { sb.append(String.format("if (%s_visited == null) %s_visited = %s;\n", signature(), signature(), config().createDefaultSet())); } + if (getNumParameter() != 0 && isLazy() && !simpleCacheCheck()) { + sb.append(String.format("if (%s_computed == null) %s_computed = %s;\n", + signature(), signature(), config().createDefaultMap())); + } } if (getNumParameter() != 0 && (isLazy() || isCircular())) { sb.append(String.format("if (%s_values == null) %s_values = %s;\n", @@ -791,4 +795,9 @@ aspect Compute { eq CollDecl.circularComputeRhs() = String.format("combine_%s_contributions(%s)", signature(), getBottomValue()); + + syn boolean AttrDecl.simpleCacheCheck() = + config().safeLazy() + ? (isCircular() || declaredNTA() || isAttrNTA()) + : true; } diff --git a/src/java/org/jastadd/Configuration.java b/src/java/org/jastadd/Configuration.java index 03c037376fb36ae9a4cc5c7c4d64004f67824641..28021029de29720126990544073e807b38b8daad 100644 --- a/src/java/org/jastadd/Configuration.java +++ b/src/java/org/jastadd/Configuration.java @@ -381,6 +381,10 @@ public class Configuration { Option<Boolean> dotOption = new FlagOption("dot", "generate a Dot graph from the grammar") .nonStandard(); + Option<Boolean> safeLazyOption = new FlagOption("safeLazy", + "safe in-cycle caching of non-circular attributes") + .nonStandard(); + Collection<String> filenames = new LinkedList<String>(); /** @@ -458,6 +462,9 @@ public class Configuration { // New since 2.1.12. allOptions.add(stateClassNameOption); + // New since 2.2.1: + allOptions.add(safeLazyOption); + // Deprecated in 2.1.5. allOptions.add(doxygenOption); allOptions.add(cacheAllOption); @@ -1264,4 +1271,11 @@ public class Configuration { public boolean generateImplicits() { return generateImplicitsOption.value(); } + + /** + * @return {@code true} if safe lazy attribute caching in circular evaluation should be used. + */ + public boolean safeLazy() { + return safeLazyOption.value(); + } } diff --git a/src/java/org/jastadd/JastAddTask.java b/src/java/org/jastadd/JastAddTask.java index d362f242e03c36c0f0cd5068508e50be791e5bd8..e02c772064696ad5bc68f3d39aaed93d65c7bec9 100644 --- a/src/java/org/jastadd/JastAddTask.java +++ b/src/java/org/jastadd/JastAddTask.java @@ -274,6 +274,14 @@ public class JastAddTask extends Task { setOption(config.minListSizeOption, arg); } + public void setDot(boolean enable) { + setOption(config.dotOption, enable); + } + + public void setSafeLazy(boolean enable) { + setOption(config.safeLazyOption, enable); + } + @Override public void execute() throws BuildException { System.err.println("generating node types and weaving aspects"); diff --git a/src/template/ast/ASTNode.tt b/src/template/ast/ASTNode.tt index 4962dba55901a99b97d03898922b74dc2c45ecd5..310849e537443152ed220f86e29ef0a702a7942b 100644 --- a/src/template/ast/ASTNode.tt +++ b/src/template/ast/ASTNode.tt @@ -199,8 +199,9 @@ $if(DebugMode) while (node != null && !node.debugNodeAttachmentIsRoot()) { $if(LegacyRewrite) - if (node.in$$Circle()) + if (node.in$$Circle()) { return; + } $endif $ASTNode parent = ($ASTNode) node.parent; if (parent != null && parent.getIndexOfChild(node) == -1) { diff --git a/src/template/ast/Attributes.tt b/src/template/ast/Attributes.tt index 6957419facd5c9ab46467ec4c095e62c839c4b5e..84db75b902c82c9403cad71b098d72311ecd2a77 100644 --- a/src/template/ast/Attributes.tt +++ b/src/template/ast/Attributes.tt @@ -41,7 +41,11 @@ $endif AttrDecl.cacheDeclarations [[ $if(!#isParameterized) /** @apilevel internal */ + $if(#simpleCacheCheck) protected boolean #(signature)_computed = false; + $else + protected $StateClass.Cycle #(signature)_computed = null; + $endif /** @apilevel internal */ protected #getType #(signature)_value; @@ -61,9 +65,14 @@ $else /** @apilevel internal */ protected $DefaultMapType #(signature)_values = $CreateDefaultMap; $endif + $if(!#simpleCacheCheck) + /** @apilevel internal */ + protected $DefaultMapType #(signature)_computed; + $endif $endif ]] +# Method headers for attribute declarations. AttrDecl.synDecl = AttrDecl.inhDecl [[ #docComment #annotations @@ -291,7 +300,11 @@ AttrDecl.cacheCheck [[ $if(#hasCache) $include(AttrDecl.incHookAttrRead) $if(!#isParameterized) + $if(#simpleCacheCheck) if (#(signature)_computed) { + $else +if (#(signature)_computed == $StateClass.NON_CYCLE || #(signature)_computed == state().cycle()) { + $endif $include(AttrDecl.traceCacheRead) $if(#isAttrNTA) return (#boxedType) getChild(#(signature)ChildPosition()); @@ -300,7 +313,13 @@ if (#(signature)_computed) { $endif } $else + $if(#simpleCacheCheck) if (#(signature)_values.containsKey(_parameters)) { + $else +if (#(signature)_values.containsKey(_parameters) && #(signature)_computed != null + && #(signature)_computed.containsKey(_parameters) + && (#(signature)_computed.get(_parameters) == $StateClass.NON_CYCLE || #(signature)_computed.get(_parameters) == state().cycle())) { + $endif $include(AttrDecl.traceCacheRead) $if(#isAttrNTA) return (#boxedType) getChild(#(signature)ChildPosition())); @@ -318,14 +337,40 @@ $if(#isLazy) $if(LegacyRewrite) if (#cacheStoreCondition) { $endif - $if(#isParameterized) + $if(#simpleCacheCheck) + $if(#isParameterized) +$include(AttrDecl.incHookAttrCompBeforeStore) +#(signature)_values.put(_parameters, #(signature)_value); +$include(AttrDecl.traceCacheStore) + $else +$include(AttrDecl.incHookAttrCompBeforeStore) +#(signature)_computed = true; +$include(AttrDecl.traceCacheStore) + $endif + $else +if (state().inCircle()) { + $if(#isParameterized) $include(AttrDecl.incHookAttrCompBeforeStore) #(signature)_values.put(_parameters, #(signature)_value); + #(signature)_computed.put(_parameters, state().cycle()); $include(AttrDecl.traceCacheStore) - $else + $else + $include(AttrDecl.incHookAttrCompBeforeStore) + #(signature)_computed = state().cycle(); + $include(AttrDecl.traceCacheStore) + $endif +} else { + $if(#isParameterized) + $include(AttrDecl.incHookAttrCompBeforeStore) + #(signature)_values.put(_parameters, #(signature)_value); + #(signature)_computed.put(_parameters, $StateClass.NON_CYCLE); + $include(AttrDecl.traceCacheStore) + $else $include(AttrDecl.incHookAttrCompBeforeStore) - #(signature)_computed = true; + #(signature)_computed = $StateClass.NON_CYCLE; $include(AttrDecl.traceCacheStore) + $endif +} $endif $if(LegacyRewrite) } else { @@ -363,7 +408,9 @@ state().assertSameCircle(#(signature)_circle, "#hostClassName.#signatureJavaStyl state().enterLazyAttribute(); $else $if(#isLazy) + $if(#simpleCacheCheck) state().enterLazyAttribute(); + $endif $endif $endif ]] @@ -373,7 +420,9 @@ $if(ComponentCheck) state().leaveLazyAttribute(); $else $if(#isLazy) + $if(#simpleCacheCheck) state().leaveLazyAttribute(); + $endif $endif $endif ]] diff --git a/src/template/ast/State.tt b/src/template/ast/State.tt index 6cd3d553a0289caf8117225df4cf7e40082d7c3a..31b6963bb3722bc9621ee34690c4f067b2856c2d 100644 --- a/src/template/ast/State.tt +++ b/src/template/ast/State.tt @@ -41,6 +41,9 @@ public class $StateClass { protected static class Cycle { } + /** The cycle ID used outside of circular evaluation. */ + public static final Cycle NON_CYCLE = new Cycle(); + /** * Tracks the state of the current circular evaluation. This class defines a * stack structure where the next element on the stack is pointed to by the @@ -67,7 +70,7 @@ $if(ComponentCheck) $endif /** Cycle ID of the latest cycle in this circular evaluation. */ - Cycle cycle = null; + Cycle cycle = NON_CYCLE; protected CircleState(CircleState next) { this.next = next; diff --git a/src/template/flush/Flush.tt b/src/template/flush/Flush.tt index c438ebc801a8e13634eb5eb3f66987c3ec337efa..37219f83711817b8ae07280309b6b851dfad81f6 100644 --- a/src/template/flush/Flush.tt +++ b/src/template/flush/Flush.tt @@ -128,6 +128,9 @@ $endif AttrDecl.resetAttrCache [[ $if(#isLazy) $if(#isParameterized) + $if(!#simpleCacheCheck) +#(signature)_computed = $CreateDefaultMap; + $endif $if(LazyMaps) #(signature)_values = null; $else @@ -137,9 +140,13 @@ $if(#isLazy) #(signature)_list = null; $endif $else + $if(#simpleCacheCheck) #(signature)_computed = false; - $if(#isCircular) + $if(#isCircular) #(signature)_initialized = false; + $endif + $else +#(signature)_computed = null; $endif $if(!#isPrimitive) #(signature)_value = null;