| /******************************************************************************* |
| * Copyright (c) 2015, 2019 Willink Transformations and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * E.D.Willink - Initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.qvtd.compiler.internal.qvts2qvts; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.ocl.pivot.utilities.NameUtil; |
| import org.eclipse.qvtd.compiler.internal.qvtb2qvts.ScheduleManager; |
| import org.eclipse.qvtd.compiler.internal.qvts2qvts.analysis.PartialRegionAnalysis; |
| import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.PartitionedTransformationAnalysis; |
| import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.PartitionsAnalysis; |
| import org.eclipse.qvtd.compiler.internal.qvts2qvts.partitioner.RootPartitionAnalysis; |
| import org.eclipse.qvtd.compiler.internal.utilities.CompilerUtil; |
| import org.eclipse.qvtd.pivot.qvtschedule.Connection; |
| import org.eclipse.qvtd.pivot.qvtschedule.LoadingPartition; |
| import org.eclipse.qvtd.pivot.qvtschedule.Partition; |
| import org.eclipse.qvtd.pivot.qvtschedule.RootPartition; |
| import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleUtil; |
| |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Sets; |
| |
| /** |
| * ScheduleAnalysis analyses a sequence of concurrent partitions to identofy the necessary connections. |
| */ |
| public class ScheduleAnalysis |
| { |
| protected final @NonNull PartitionedTransformationAnalysis partitionedTransformationAnalysis; |
| protected final @NonNull ScheduleManager scheduleManager; |
| protected final @NonNull ConnectionManager connectionManager; |
| |
| /** |
| * The overall RootPartition. |
| */ |
| protected final @NonNull RootPartitionAnalysis rootPartitionAnalysis; |
| |
| /** |
| * All transitively callable partitions within the rootPartition. |
| */ |
| protected final @NonNull List<@NonNull PartialRegionAnalysis<@NonNull PartitionsAnalysis>> allPartitionAnalyses; |
| |
| private final @NonNull LoadingPartition loadingPartition; |
| |
| /** |
| * Cached list of all incoming connections per-region. |
| */ |
| private final @NonNull Map<@NonNull Partition, @NonNull List<@NonNull Connection>> partition2incomingConnections = new HashMap<>(); |
| |
| /** |
| * Cached list of all outgoing connections per-region. |
| */ |
| private final @NonNull Map<@NonNull Partition, @NonNull List<@NonNull Connection>> partition2outgoingConnections = new HashMap<>(); |
| |
| /** |
| * Cached list of all source regions per-connection and whether the source has unserviced content for the connection. |
| */ |
| private final @NonNull Map<@NonNull Connection, @NonNull List<@NonNull Partition>> connection2sourcePartitions = new HashMap<>(); |
| |
| /** |
| * Cached list of all target regions per-connection and whether the connection has unserviced content for the target. |
| */ |
| private final @NonNull Map<@NonNull Connection, @NonNull List<@NonNull Partition>> connection2targetPartitions = new HashMap<>(); |
| |
| /** |
| * The immediate source region of each incoming connection. |
| */ |
| //private final @NonNull Map<@NonNull Partition, @NonNull Set<@NonNull Partition>> target2sources; |
| |
| /** |
| * The immediate source regions for each set of regions forming a cycle. |
| */ |
| //private final @NonNull Map<@NonNull Set<@NonNull Partition>, @NonNull Set<@NonNull Partition>> cycle2sources; |
| |
| /** |
| * The cycle in which each region may participate. |
| */ |
| //private final Map<@NonNull Partition, @NonNull Set<@NonNull Partition>> partition2cycle; |
| |
| /** |
| * Depth in the call tree of each region. For the most part all source elements are at lower depth than target elements, |
| * however the call tree may have cycles which have the same depth throughout the cycle. |
| * |
| * FIXME this depth analysis has evolved from when scheduling made extensive use of MappingCalls. Now that |
| * Connections have been introduced, is the depth still useful and does it repeat some of the intervalIndex analysis? |
| */ |
| //private final @NonNull Map<@NonNull Partition, @NonNull Integer> partition2pass; |
| |
| /** |
| * The parents (invokers) of each region. |
| */ |
| //private final @NonNull Map<@NonNull Partition, @NonNull List<@NonNull Partition>> partition2parents; |
| |
| /** |
| * The regions that have no outgoing passed connections. |
| * @param partitionedTransformationAnalysis |
| */ |
| // private final @NonNull Set<@NonNull Partition> unpassedPartitions = new HashSet<>(); |
| |
| public ScheduleAnalysis(@NonNull PartitionedTransformationAnalysis partitionedTransformationAnalysis, @NonNull ConnectionManager connectionManager, @NonNull RootPartitionAnalysis rootPartitionAnalysis) { |
| this.partitionedTransformationAnalysis = partitionedTransformationAnalysis; |
| this.scheduleManager = connectionManager.getScheduleManager(); |
| this.rootPartitionAnalysis = rootPartitionAnalysis; |
| this.connectionManager = connectionManager; |
| this.allPartitionAnalyses = CompilerUtil.gatherPartitionAnalyses(rootPartitionAnalysis, new ArrayList<>()); |
| Collections.sort(this.allPartitionAnalyses, NameUtil.NAMEABLE_COMPARATOR); |
| // |
| // Initialize the incoming/looping/outgoing connection analyses of each region |
| // |
| LoadingPartition loadingPartition = null; |
| for (@NonNull PartialRegionAnalysis<@NonNull PartitionsAnalysis> partitionAnalysis : this.allPartitionAnalyses) { |
| analyzeConnections(partitionAnalysis); |
| Partition partition = partitionAnalysis.getPartition(); |
| if (partition instanceof LoadingPartition) { |
| assert loadingPartition == null; |
| loadingPartition = (LoadingPartition) partition; |
| } |
| } |
| assert loadingPartition != null; |
| this.loadingPartition = loadingPartition; |
| // |
| // Initialize the source/target of each connection. |
| // Compute the set of all connections that are not passed. |
| // |
| for (@NonNull PartialRegionAnalysis<@NonNull PartitionsAnalysis> partitionAnalysis : this.allPartitionAnalyses) { |
| analyzeSourcesAndTargets(partitionAnalysis.getPartition()); |
| } |
| // |
| // Identify all the source regions for each target region. |
| // |
| // this.target2sources = analyzeSources(); |
| // |
| // Identify all the cycles and their immedite sources. |
| // |
| // this.cycle2sources = analyzeCycleSources(); |
| // |
| // Identify all the region that participate in cycles. |
| // |
| // this.partition2cycle = analyzeCycleElements(); |
| // |
| // Determine the call tree depth of each connection / region. |
| // |
| // this.partition2pass = analyzeDepths(); |
| // |
| // Determine the call tree parents of each region. |
| // |
| // this.partition2parents = analyzeParents(); |
| // System.out.println(toString()); |
| } |
| |
| /** |
| * Initialize the incoming/looping/outgoing connection analyses of each region |
| */ |
| private void analyzeConnections(@NonNull PartialRegionAnalysis<@NonNull PartitionsAnalysis> partitionAnalysis) { |
| Partition partition = partitionAnalysis.getPartition(); |
| List<@NonNull Connection> incomingConnections = new ArrayList<>(); |
| for (@NonNull Connection connection : connectionManager.getIncomingConnections(partitionAnalysis)) { |
| if (!incomingConnections.contains(connection)) { |
| incomingConnections.add(connection); |
| } |
| } |
| List<@NonNull Connection> outgoingConnections = new ArrayList<>(); |
| for (@NonNull Connection connection : connectionManager.getNextConnections(partition)) { |
| if (!outgoingConnections.contains(connection)) { |
| outgoingConnections.add(connection); |
| } |
| } |
| if (outgoingConnections.size() > 1) { // Ensure that connection ordering is deterministic |
| Collections.sort(outgoingConnections, NameUtil.NAMEABLE_COMPARATOR); |
| } |
| partition2incomingConnections.put(partition, incomingConnections); |
| partition2outgoingConnections.put(partition, outgoingConnections); |
| } |
| |
| // |
| // Determine the cycles and their sources. |
| // |
| /* private @NonNull Map<@NonNull Partition, @NonNull Set<@NonNull Partition>> analyzeCycleElements() { |
| Map<@NonNull Partition, @NonNull Set<@NonNull Partition>> partition2cycle = new HashMap<>(); |
| for (@NonNull Set<@NonNull Partition> cycle : cycle2sources.keySet()) { |
| for (@NonNull Partition partition : cycle) { |
| partition2cycle.put(partition, cycle); |
| } |
| } |
| return partition2cycle; |
| } */ |
| |
| // |
| // Determine the cycles and their sources. |
| // |
| /* private @NonNull Map<@NonNull Set<@NonNull Partition>, @NonNull Set<@NonNull Partition>> analyzeCycleSources() { |
| Map<@NonNull Set<@NonNull Partition>, @NonNull Set<@NonNull Partition>> cycle2sources = new HashMap<>(); |
| Map<@NonNull Partition, @NonNull Set<@NonNull Partition>> target2sourcesClosure = CompilerUtil.computeClosure(target2sources); |
| Map<@NonNull Partition, @NonNull Set<@NonNull Partition>> source2targetsClosure = CompilerUtil.computeInverseClosure(target2sourcesClosure); |
| for (@NonNull Partition partition : allPartitions) { |
| Set<@NonNull Partition> sourcePartitions = target2sourcesClosure.get(partition); |
| Set<@NonNull Partition> targetPartitions = source2targetsClosure.get(partition); |
| assert (sourcePartitions != null) && (targetPartitions != null); |
| Set<@NonNull Partition> cyclicPartitions = new HashSet<>(sourcePartitions); |
| cyclicPartitions.retainAll(targetPartitions); |
| if (!cyclicPartitions.isEmpty() && !cycle2sources.containsKey(cyclicPartitions)) { |
| Set<@NonNull Partition> cycleSources = new HashSet<>(); |
| for (@NonNull Partition cyclicPartition : cyclicPartitions) { |
| Set<@NonNull Partition> sources = target2sources.get(cyclicPartition); |
| assert sources != null; |
| cycleSources.addAll(sources); |
| } |
| cycleSources.removeAll(cyclicPartitions); |
| cycle2sources.put(cyclicPartitions, cycleSources); |
| } |
| } |
| return cycle2sources; |
| } */ |
| |
| /* private @NonNull Map<@NonNull Partition, @NonNull Integer> analyzeDepths() { |
| // |
| // Loop to allocate connection/node element to each depth so that each element has as few sources at greater depth. |
| // |
| Map<@NonNull Partition, @NonNull Integer> partition2pass = new HashMap<>(); |
| Set<@NonNull Partition> allPartitions = new HashSet<>(target2sources.keySet()); |
| // Set<@NonNull Partition> pendingPartitions = new HashSet<>(allRegions); |
| Set<@NonNull Partition> pendingPartitions = new HashSet<>(); |
| for (@NonNull Partition partition : allPartitions) { |
| pendingPartitions.add(partition); |
| } |
| // Set<@NonNull Set<@NonNull Partition>> pendingCycles = new HashSet<>(cycle2sources.keySet()); |
| Set<@NonNull Set<@NonNull Partition>> pendingCycles = new HashSet<>(); |
| for (@NonNull Set<@NonNull Partition> partitions : cycle2sources.keySet()) { |
| Set<@NonNull Partition> cycle = new HashSet<>(); |
| for (@NonNull Partition partition : partitions) { |
| cycle.add(partition); |
| } |
| pendingCycles.add(cycle); |
| } |
| partition2pass.put(rootPartition, 0); |
| for (int depth = 1; !pendingPartitions.isEmpty(); depth++) { |
| Set<@NonNull Partition> readyPartitions = new HashSet<>(partition2pass.keySet()); |
| Set<@NonNull Partition> nowReadyPartitions = new HashSet<>(); |
| Set<@NonNull Set<@NonNull Partition>> nowReadyCycles = new HashSet<>(); |
| for (@NonNull Partition targetPartition : pendingPartitions) { |
| Set<@NonNull Partition> targetSourcePartitions = new HashSet<>(target2sources.get(targetPartition)); |
| assert targetSourcePartitions != null; |
| targetSourcePartitions.removeAll(readyPartitions); |
| if (targetSourcePartitions.isEmpty()) { |
| nowReadyPartitions.add(targetPartition); |
| } |
| } |
| for (@NonNull Set<@NonNull Partition> targetCycle : pendingCycles) { |
| Set<@NonNull Partition> targetSourcePartitions = new HashSet<>(cycle2sources.get(targetCycle)); |
| assert targetSourcePartitions != null; |
| targetSourcePartitions.removeAll(readyPartitions); |
| if (targetSourcePartitions.isEmpty()) { |
| nowReadyPartitions.addAll(targetCycle); |
| nowReadyCycles.add(targetCycle); |
| } |
| } |
| if (nowReadyPartitions.size() > 0) { // one or more elements has all sources at lower depths. |
| for (@NonNull Partition nowReadyPartition : nowReadyPartitions) { |
| partition2pass.put(nowReadyPartition, depth); |
| pendingPartitions.remove(nowReadyPartition); |
| } |
| pendingCycles.removeAll(nowReadyCycles); |
| } |
| else { // else choose an elements with fewest greater depth sources. |
| Partition fewestBadSourcePartition = null; |
| int badSources = Integer.MAX_VALUE; |
| for (@NonNull Partition targetPartition : pendingPartitions) { |
| Set<@NonNull Partition> targetSourcePartitions = new HashSet<>(target2sources.get(targetPartition)); |
| assert targetSourcePartitions != null; |
| targetSourcePartitions.removeAll(readyPartitions); |
| int targetSourceElementsSize = targetSourcePartitions.size(); |
| if ((fewestBadSourcePartition == null) || (targetSourceElementsSize < badSources)) { |
| badSources = targetSourceElementsSize; |
| fewestBadSourcePartition = targetPartition; |
| } |
| } |
| assert fewestBadSourcePartition != null; |
| partition2pass.put(fewestBadSourcePartition, depth); |
| pendingPartitions.remove(fewestBadSourcePartition); |
| } |
| } |
| return partition2pass; |
| } */ |
| |
| /* private @NonNull Map<@NonNull Partition, @NonNull List<@NonNull Partition>> analyzeParents() { |
| Map<@NonNull Partition, @NonNull List<@NonNull Partition>> partition2parents = new HashMap<>(); |
| partition2parents.put(rootPartition, Collections.emptyList()); |
| for (@NonNull Partition targetPartition : allPartitions) { |
| Set<@NonNull Partition> parentSet = new HashSet<>(); |
| Set<@NonNull Partition> sourcePartitions = target2sources.get(targetPartition); |
| assert sourcePartitions != null; |
| for (@NonNull Partition sourcePartition : sourcePartitions) { |
| Set<@NonNull Partition> sourceCycle = partition2cycle.get(sourcePartition); |
| if (sourceCycle != null) { |
| Set<@NonNull Partition> cycleSources = cycle2sources.get(sourceCycle); |
| assert cycleSources != null; |
| parentSet.addAll(cycleSources); |
| } |
| else { |
| parentSet.add(sourcePartition); |
| } |
| } |
| if (parentSet.isEmpty()) { |
| parentSet.add(rootPartition); |
| } |
| List<@NonNull Partition> parentList = new ArrayList<>(parentSet); |
| Collections.sort(parentList, NameUtil.NAMEABLE_COMPARATOR); |
| partition2parents.put(targetPartition, parentList); |
| } |
| return partition2parents; |
| } */ |
| |
| /** |
| * Initialize the source/target content of each connection of each region to empty. |
| * Compute the set of all connections that are not passed. |
| * Compute the set of all regions that are blocked by a mandatory dependence. |
| */ |
| private void analyzeSourcesAndTargets(@NonNull Partition partition) { |
| // boolean hasPassedConnection = false; |
| // for (@NonNull Connection connection : getOutgoingConnections(partition)) { |
| // if (connection.isPassed()) { |
| // hasPassedConnection = true; |
| // break; |
| // } |
| // } |
| // (!hasPassedConnection) { |
| // unpassedPartitions.add(partition); |
| // } |
| for (@NonNull Connection connection : getIncomingConnections(partition)) { |
| List<@NonNull Partition> sourcePartitions = connection2sourcePartitions.get(connection); |
| if (sourcePartitions == null) { |
| sourcePartitions = new ArrayList<>(); |
| for (@NonNull Partition sourcePartition : connection.getSourcePartitions()) { |
| if (!sourcePartitions.contains(sourcePartition)) { |
| sourcePartitions.add(sourcePartition); |
| } |
| } |
| connection2sourcePartitions.put(connection, sourcePartitions); |
| } |
| List<@NonNull Partition> targetPartitions = connection2targetPartitions.get(connection); |
| if (targetPartitions == null) { |
| targetPartitions = new ArrayList<>(); |
| for (@NonNull Partition targetPartition : connection.getTargetPartitions()) { |
| if (!targetPartitions.contains(targetPartition)) { |
| targetPartitions.add(targetPartition); |
| } |
| } |
| connection2targetPartitions.put(connection, targetPartitions); |
| } |
| } |
| } |
| |
| protected void buildCallTree(@NonNull Iterable<@NonNull Concurrency> partitionSchedule) { |
| CallTreeBuilder callTreeBuilder = new CallTreeBuilder(this, rootPartitionAnalysis.getPartition(), loadingPartition); |
| callTreeBuilder.buildTree(partitionSchedule); |
| } |
| |
| private void checkPassNumbers(@NonNull Connection connection) { |
| @SuppressWarnings("unused") int firstPass = connection.getFirstPass(); |
| int lastPass = connection.getLastPass(); |
| for (@NonNull Partition sourcePartition : connection.getSourcePartitions()) { |
| // assert sourcePartition.getFirstPass() <= firstPass; |
| assert sourcePartition.getLastPass() <= lastPass; |
| } |
| for (@NonNull Partition targetPartition : connection.getTargetPartitions()) { |
| // boolean isCyclic = false; |
| // for (EObject eObject = targetPartition; eObject instanceof Partition; eObject = eObject.eContainer()) { |
| // if (eObject instanceof CyclicPartition) { |
| // isCyclic = true; |
| // break; |
| // } |
| // } |
| // if (isCyclic) { |
| // assert firstPass <= targetPartition.getFirstPass(); |
| assert lastPass <= targetPartition.getLastPass(); |
| // } |
| // else { |
| // assert firstPass < targetPartition.getFirstPass(); |
| // assert lastPass < targetPartition.getLastPass(); |
| // } |
| } |
| } |
| |
| public @NonNull ConnectionManager getConnectionManager() { |
| return connectionManager; |
| } |
| |
| protected @NonNull Iterable<? extends @NonNull Connection> getConnections() { |
| return connection2targetPartitions.keySet(); |
| } |
| |
| protected @NonNull Iterable<@NonNull Connection> getIncomingConnections(@NonNull Partition partition) { |
| List<@NonNull Connection> incomingConnections = partition2incomingConnections.get(partition); |
| assert incomingConnections != null; |
| return incomingConnections; |
| } |
| |
| public @NonNull Partition getLoadingPartition() { |
| return loadingPartition; |
| } |
| |
| protected @NonNull Iterable<@NonNull Connection> getOutgoingConnections(@NonNull Partition partition) { |
| List<@NonNull Connection> outgoingConnections = partition2outgoingConnections.get(partition); |
| assert outgoingConnections != null; |
| return outgoingConnections; |
| } |
| |
| public @NonNull RootPartition getRootPartition() { |
| return rootPartitionAnalysis.getPartition(); |
| } |
| |
| public @NonNull ScheduleManager getScheduleManager() { |
| return scheduleManager; |
| } |
| |
| protected @NonNull Iterable<@NonNull Partition> getSourcePartitions(@NonNull Connection connection) { |
| List<@NonNull Partition> sourceRegions = connection2sourcePartitions.get(connection); |
| assert sourceRegions != null; |
| return sourceRegions; |
| } |
| |
| protected @NonNull Iterable<@NonNull Partition> getTargetPartitions(@NonNull Connection connection) { |
| List<@NonNull Partition> targetPartitions = connection2targetPartitions.get(connection); |
| assert targetPartitions != null; |
| return targetPartitions; |
| } |
| |
| private void propagatePassNumbers(@NonNull Partition sourcePartition, @NonNull Set<@NonNull Connection> changedConnections) { |
| Iterable<@NonNull Integer> sourcePasses = QVTscheduleUtil.getPasses(sourcePartition); |
| for (@NonNull Connection outgoingConnection : getOutgoingConnections(sourcePartition)) { |
| boolean isChanged = false; |
| for (int sourcePass : sourcePasses) { |
| if (outgoingConnection.addPass(sourcePass)) { |
| isChanged = true; |
| } |
| } |
| if (isChanged) { |
| changedConnections.add(outgoingConnection); |
| } |
| } |
| } |
| |
| private void propagatePassNumbers(@NonNull Connection incomingConnection, @NonNull Set<@NonNull Partition> changedPartitions) { |
| List<@NonNull Integer> connectionPasses = incomingConnection.getPasses(); |
| for (@NonNull Partition targetPartition : getTargetPartitions(incomingConnection)) { |
| boolean isChanged = false; |
| int lastPass = targetPartition.getLastPass(); |
| for (int connectionPass : connectionPasses) { |
| if ((connectionPass > lastPass) && targetPartition.addPass(connectionPass)) { |
| isChanged = true; |
| } |
| } |
| if (isChanged) { |
| changedPartitions.add(targetPartition); |
| } |
| } |
| } |
| |
| public void schedule(@NonNull RootPartition rootPartition, @NonNull List<@NonNull Concurrency> partitionSchedule) { |
| int passNumber = 0; |
| int cycleDepth = 0; |
| int cycleStart = 0; |
| Set<@NonNull Partition> changedPartitions = new HashSet<>(); |
| for (@NonNull Concurrency concurrency : partitionSchedule) { |
| concurrency.setPass(passNumber); |
| if (concurrency.isCycleStart()) { |
| cycleDepth++; |
| if (cycleDepth == 1) { |
| cycleStart = passNumber; |
| } |
| } |
| Set<@NonNull Connection> incomingConnections = new HashSet<>(); |
| Set<@NonNull Connection> outgoingConnections = new HashSet<>(); |
| for (@NonNull PartialRegionAnalysis<@NonNull PartitionsAnalysis> partitionAnalysis : concurrency) { |
| Partition partition = partitionAnalysis.getPartition(); |
| changedPartitions.add(partition); |
| Iterables.addAll(incomingConnections, getIncomingConnections(partition)); |
| Iterables.addAll(outgoingConnections, getOutgoingConnections(partition)); |
| } |
| Set<@NonNull Connection> loopedConnections = new HashSet<>(incomingConnections); |
| loopedConnections.retainAll(outgoingConnections); |
| for (@NonNull Connection loopingConnection : loopedConnections) { |
| loopingConnection.addPass(passNumber); |
| } |
| if (concurrency.isCycleEnd()) { |
| cycleDepth--; |
| if (cycleDepth == 0) { |
| for (int cyclePass = cycleStart; cyclePass < passNumber; cyclePass++) { |
| Concurrency concurrency2 = partitionSchedule.get(cyclePass); |
| assert concurrency2 != null; |
| concurrency2.addPass(passNumber); |
| } |
| } |
| } |
| passNumber++; |
| } |
| assert cycleDepth == 0; |
| scheduleManager.writeDebugGraphs("6-pass", false, true, false); |
| /** |
| * Ensure connection responds to all sources. |
| */ |
| for (@NonNull Connection connection : getConnections()) { |
| for (@NonNull Partition sourcePartition : getSourcePartitions(connection)) { |
| for (int sourcePass : sourcePartition.getPasses()) { |
| connection.addPass(sourcePass); |
| } |
| } |
| } |
| /** |
| * Propagate the additional connection indexes to their outgoing connections. |
| */ |
| Set<@NonNull Connection> changedConnections = Sets.newHashSet(getConnections()); |
| while (!changedPartitions.isEmpty()) { |
| for (@NonNull Partition changedPartition : changedPartitions) { |
| propagatePassNumbers(changedPartition, changedConnections); |
| } |
| changedPartitions.clear(); |
| for (@NonNull Connection changedConnection : changedConnections) { |
| propagatePassNumbers(changedConnection, changedPartitions); |
| } |
| changedConnections.clear(); |
| } |
| scheduleManager.writeDebugGraphs("7-passes", false, true, false); |
| for (@NonNull Connection connection : getConnections()) { |
| checkPassNumbers(connection); |
| } |
| buildCallTree(partitionSchedule); |
| } |
| |
| @Override |
| public @NonNull String toString() { |
| StringBuilder s = new StringBuilder(); |
| List<@NonNull PartialRegionAnalysis<@NonNull PartitionsAnalysis>> list = new ArrayList<>(allPartitionAnalyses); |
| Collections.sort(list, NameUtil.NAMEABLE_COMPARATOR); |
| for (@NonNull PartialRegionAnalysis<@NonNull PartitionsAnalysis> entry : list) { |
| if (s.length() > 0) { |
| s.append("\n"); |
| } |
| s.append(entry.getPartition().getPassRangeText() + " : " + entry.getName()); |
| } |
| return s.toString(); |
| } |
| } |