From 62d28e6faa09084dfeba49b418be51b2e5ee3945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20=C3=96qvist?= <jesper.oqvist@cs.lth.se> Date: Thu, 4 Feb 2016 00:20:19 +0100 Subject: [PATCH] Safe caching of non-circular attributes Added --safeLazy option which enables safe in-cycle caching of non-circular attributes. --- ChangeLog | 10 +++++ src/jastadd/ast/JragCodeGen.jrag | 9 ++++ src/java/org/jastadd/Configuration.java | 14 +++++++ src/java/org/jastadd/JastAddTask.java | 8 ++++ src/template/ast/ASTNode.tt | 3 +- src/template/ast/Attributes.tt | 55 +++++++++++++++++++++++-- src/template/ast/State.tt | 5 ++- src/template/flush/Flush.tt | 9 +++- 8 files changed, 107 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6be4b5de..798df6c9 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 bb240e0d..aa4ce8a5 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 03c03737..28021029 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 d362f242..e02c7720 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 4962dba5..310849e5 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 6957419f..84db75b9 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 6cd3d553..31b6963b 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 c438ebc8..37219f83 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; -- GitLab