diff --git a/nuitka/Variables.py b/nuitka/Variables.py index 9faaa06d2..1dba1aef5 100644 --- a/nuitka/Variables.py +++ b/nuitka/Variables.py @@ -175,12 +175,22 @@ class Variable(getMetaClassBase("Variable", require_slots=True)): return self.traces def hasEmptyTracesFor(self, owner): + """Do these traces contain any usage.""" for trace in self.traces: if trace.owner is owner and trace.isUsingTrace(): return False return True + def hasNoWritingTraces(self): + """Do these traces contain any writes.""" + + for trace in self.traces: + if trace.isWritingTrace(): + return False + + return True + def updateUsageState(self): writers = set() users = set() @@ -191,7 +201,7 @@ class Variable(getMetaClassBase("Variable", require_slots=True)): if trace.isAssignTrace(): writers.add(owner) - elif trace.isDeletedTrace() and owner is not self.owner: + elif owner is not self.owner and trace.isDeletedTrace(): writers.add(owner) self.writers = writers diff --git a/nuitka/nodes/FunctionNodes.py b/nuitka/nodes/FunctionNodes.py index 8c45b9495..8b313ad53 100644 --- a/nuitka/nodes/FunctionNodes.py +++ b/nuitka/nodes/FunctionNodes.py @@ -416,7 +416,7 @@ class ExpressionFunctionBodyBase( def optimizeVariableReleases(self): for parameter_variable in self.getParameterVariablesWithManualRelease(): - read_only = self.trace_collection.hasReadOnlyTraces(parameter_variable) + read_only = parameter_variable.hasNoWritingTraces() if read_only: self.trace_collection.signalChange( diff --git a/nuitka/optimizations/TraceCollections.py b/nuitka/optimizations/TraceCollections.py index a242feac0..2ee0fbcf9 100644 --- a/nuitka/optimizations/TraceCollections.py +++ b/nuitka/optimizations/TraceCollections.py @@ -88,17 +88,6 @@ class CollectionUpdateMixin(object): def getVariableTrace(self, variable, version): return self.variable_traces[(variable, version)] - def getVariableTraces(self, variable): - result = [] - - for key, variable_trace in iterItems(self.variable_traces): - candidate = key[0] - - if variable is candidate: - result.append(variable_trace) - - return result - def getVariableTracesAll(self): return self.variable_traces @@ -208,11 +197,7 @@ class CollectionStartPointMixin(CollectionUpdateMixin): def getExceptionRaiseCollections(self): return self.exception_collections - def hasReadOnlyTraces(self, variable): - # TODO: Combine these steps into one for performance gains. - traces = self.getVariableTraces(variable) - return areReadOnlyTraces(traces) - + # TODO: Eliminate this function, call it directly where this is used. def updateVariablesFromCollection(self, old_collection, source_ref): Variables.updateVariablesFromCollection(old_collection, self, source_ref) @@ -384,7 +369,7 @@ class TraceCollectionBase(object): self.addVariableTrace( variable, version, - ValueTraceEscaped(owner=self.owner, previous=current), + ValueTraceEscaped(self.owner, current), ) self.markCurrentVariableTrace(variable, version) @@ -401,22 +386,25 @@ class TraceCollectionBase(object): self.addVariableTrace( variable, version, - ValueTraceUnknown(owner=self.owner, previous=current), + ValueTraceUnknown(self.owner, current), ) self.markCurrentVariableTrace(variable, version) def markActiveVariableAsUnknown(self, variable): - current = self.getVariableCurrentTrace(variable) + version = self.variable_actives[variable] + current = self.getVariableTrace(variable, version) if not current.isUnknownOrVeryTrustedTrace(): - version = variable.allocateTargetNumber() + # Unknown traces are div 3 rem 2. + version = version // 3 * 3 + 2 - self.addVariableTrace( - variable, - version, - ValueTraceUnknown(owner=self.owner, previous=current), - ) + if not self.hasVariableTrace(variable, version): + self.addVariableTrace( + variable, + version, + ValueTraceUnknown(self.owner, current), + ) self.markCurrentVariableTrace(variable, version) @@ -487,9 +475,9 @@ class TraceCollectionBase(object): def onVariableSet(self, variable, version, assign_node): variable_trace = ValueTraceAssign( - owner=self.owner, - assign_node=assign_node, - previous=self.getVariableCurrentTrace(variable), + self.owner, + assign_node, + self.getVariableCurrentTrace(variable), ) self.addVariableTrace(variable, version, variable_trace) @@ -521,9 +509,9 @@ class TraceCollectionBase(object): def onVariableSetToUnescapableValue(self, variable, version, assign_node): variable_trace = ValueTraceAssignUnescapable( - owner=self.owner, - assign_node=assign_node, - previous=self.getVariableCurrentTrace(variable), + self.owner, + assign_node, + self.getVariableCurrentTrace(variable), ) self.addVariableTrace(variable, version, variable_trace) @@ -535,9 +523,9 @@ class TraceCollectionBase(object): def onVariableSetToVeryTrustedValue(self, variable, version, assign_node): variable_trace = ValueTraceAssignVeryTrusted( - owner=self.owner, - assign_node=assign_node, - previous=self.getVariableCurrentTrace(variable), + self.owner, + assign_node, + self.getVariableCurrentTrace(variable), ) self.addVariableTrace(variable, version, variable_trace) @@ -551,10 +539,10 @@ class TraceCollectionBase(object): self, variable, version, assign_node, replacement ): variable_trace = ValueTraceAssignUnescapablePropagated( - owner=self.owner, - assign_node=assign_node, - previous=self.getVariableCurrentTrace(variable), - replacement=replacement, + self.owner, + assign_node, + self.getVariableCurrentTrace(variable), + replacement, ) self.addVariableTrace(variable, version, variable_trace) @@ -572,7 +560,9 @@ class TraceCollectionBase(object): # TODO: Annotate value content as escaped. variable_trace = ValueTraceDeleted( - owner=self.owner, del_node=del_node, previous=old_trace + self.owner, + old_trace, + del_node, ) # Assign to not initialized again. @@ -833,6 +823,8 @@ class TraceCollectionBase(object): with TimerReport( message="Running merge for %s took %%.2f seconds" % collections, decider=False, + include_sleep_time=False, + use_perf_counters=False, ): new_actives = {} @@ -967,14 +959,14 @@ class TraceCollectionBase(object): ) def initVariableUnknown(self, variable): - trace = ValueTraceUnknown(owner=self.owner, previous=None) + trace = ValueTraceUnknown(self.owner, None) self.addVariableTrace(variable, 0, trace) return trace def initVariableModule(self, variable): - trace = ValueTraceUnknown(owner=self.owner, previous=None) + trace = ValueTraceUnknown(self.owner, None) self.addVariableTrace(variable, 0, trace) @@ -1002,7 +994,7 @@ class TraceCollectionBase(object): return trace def initVariableUninitialized(self, variable): - trace = ValueTraceUninitialized(owner=self.owner, previous=None) + trace = ValueTraceUninitialized(self.owner, None) self.addVariableTrace(variable, 0, trace) @@ -1152,7 +1144,9 @@ class TraceCollectionFunction(CollectionStartPointMixin, TraceCollectionBase): return TraceCollectionBase.initVariableModule(self, variable) assign_trace = ValueTraceAssignVeryTrusted( - self.owner, assign_node=trusted_node.getParent(), previous=None + self.owner, + trusted_node.getParent(), + None, ) self.addVariableTrace(variable, 0, assign_trace) @@ -1254,36 +1248,6 @@ class TraceCollectionModule(CollectionStartPointMixin, TraceCollectionBase): self.distribution_names[distribution_name] = success -def areReadOnlyTraces(variable_traces): - """Do these traces contain any writes.""" - - # Many cases immediately return, that is how we do it here, - for variable_trace in variable_traces: - if variable_trace.isAssignTrace(): - return False - elif variable_trace.isInitTrace(): - pass - elif variable_trace.isDeletedTrace(): - # A "del" statement can do this, and needs to prevent variable - # from being not released. - - return False - elif variable_trace.isUninitializedTrace(): - pass - elif variable_trace.isUnknownTrace(): - return False - elif variable_trace.isEscapeTrace(): - pass - elif variable_trace.isMergeTrace(): - pass - elif variable_trace.isLoopTrace(): - pass - else: - assert False, variable_trace - - return True - - def _checkActivesDiff(a1, a2): result = True for variable in a1: diff --git a/nuitka/optimizations/ValueTraces.py b/nuitka/optimizations/ValueTraces.py index c494ec6d7..ea3f4cf07 100644 --- a/nuitka/optimizations/ValueTraces.py +++ b/nuitka/optimizations/ValueTraces.py @@ -134,6 +134,10 @@ class ValueTraceBase(object): def isUsingTrace(self): """Is the trace indicating a usage of the variable.""" + @abstractmethod + def isWritingTrace(self): + """Is the trace indicating a usage of the variable.""" + @staticmethod def isAssignTrace(): return False @@ -310,6 +314,10 @@ class ValueTraceUninitialized(ValueTraceUnassignedBase): def isUsingTrace(self): return self.usage_count + @staticmethod + def isWritingTrace(): + return False + @staticmethod def isTraceThatNeedsEscape(): return False @@ -336,6 +344,10 @@ class ValueTraceDeleted(ValueTraceUnassignedBase): def isUsingTrace(): return True + @staticmethod + def isWritingTrace(): + return True + def getDelNode(self): return self.del_node @@ -366,6 +378,10 @@ class ValueTraceInit(ValueTraceBase): def isUsingTrace(): return True + @staticmethod + def isWritingTrace(): + return False + @staticmethod def mustHaveValue(): return True @@ -438,6 +454,10 @@ class ValueTraceUnknown(ValueTraceBase): def isUsingTrace(self): return self.usage_count + @staticmethod + def isWritingTrace(): + return True + @staticmethod def isUnknownOrVeryTrustedTrace(): return True @@ -518,6 +538,10 @@ class ValueTraceEscaped(ValueTraceUnknown): def isEscapeTrace(): return True + @staticmethod + def isWritingTrace(): + return False + def isUsingTrace(self): return self.usage_count @@ -568,6 +592,10 @@ class ValueTraceAssign(ValueTraceBase): def isUsingTrace(): return True + @staticmethod + def isWritingTrace(): + return True + def compareValueTrace(self, other): return other.isAssignTrace() and self.assign_node is other.assign_node @@ -673,6 +701,8 @@ class ValueTraceAssignUnescapablePropagated(ValueTraceAssignUnescapable): class ValueTraceMergeBase(ValueTraceBase): """Merge of two or more traces or start of loops.""" + # Base classes can be abstract, pylint: disable=I0021,abstract-method + __slots__ = () def addNameUsage(self): @@ -722,9 +752,9 @@ class ValueTraceMerge(ValueTraceMergeBase): if trace not in shorted: shorted.append(trace) - traces = tuple(shorted) + assert len(shorted) > 1, traces - assert len(traces) > 1 + traces = tuple(shorted) # assert len(set(traces)) == len(traces), [(v) for v in traces] @@ -775,6 +805,10 @@ class ValueTraceMerge(ValueTraceMergeBase): # Checking definite is enough, the merges, we shall see them as well. return self.usage_count + @staticmethod + def isWritingTrace(): + return False + def compareValueTrace(self, other): if not other.isMergeTrace(): return False @@ -866,6 +900,10 @@ class ValueTraceLoopBase(ValueTraceMergeBase): def isUsingTrace(): return True + @staticmethod + def isWritingTrace(): + return False + def getTypeShape(self): if self.type_shape is None: if len(self.type_shapes) > 1: