forked from OSchip/llvm-project
4b8bcf007b
Summary: A desired property of the node order in Swing Modulo Scheduling is that for nodes outside circuits the following holds: none of them is scheduled after both a successor and a predecessor. We call node orders that meet this property valid. Although invalid node orders do not lead to the generation of incorrect code, they can cause the pipeliner not being able to find a pipelined schedule for arbitrary II. The reason is that after scheduling the successor and the predecessor of a node, no room may be left to schedule the node itself. For data flow graphs with 0-latency edges, the node ordering algorithm of Swing Modulo Scheduling can generate such undesired invalid node orders. This patch fixes that. In the remainder of this commit message, I will give an example demonstrating the issue, explain the fix, and explain how the the fix is tested. Consider, as an example, the following data flow graph with all edge latencies 0 and all edges pointing downward. ``` n0 / \ n1 n3 \ / n2 | n4 ``` Consider the implemented node order algorithm in top-down mode. In that mode, the algorithm orders the nodes based on greatest Height and in case of equal Height on lowest Movability. Finally, in case of equal Height and Movability, given two nodes with an edge between them, the algorithm prefers the source-node. In the graph, for every node, the Height and Movability are equal to 0. As will be explained below, the algorithm can generate the order n0, n1, n2, n3, n4. So, node n3 is scheduled after its predecessor n0 and after its successor n2. The reason that the algorithm can put node n2 in the order before node n3, even though they have an edge between them in which node n3 is the source, is the following: Suppose the algorithm has constructed the partial node order n0, n1. Then, the nodes left to be ordered are nodes n2, n3, and n4. Suppose that the while-loop in the implemented algorithm considers the nodes in the order n4, n3, n2. The algorithm will start with node n4, and look for more preferable nodes. First, node n4 will be compared with node n3. As the nodes have equal Height and Movability and have no edge between them, the algorithm will stick with node n4. Then node n4 is compared with node n2. Again the Height and Movability are equal. But, this time, there is an edge between the two nodes, and the algorithm will prefer the source node n2. As there are no nodes left to compare, the algorithm will add node n2 to the node order, yielding the partial node order n0, n1, n2. In this way node n2 arrives in the node-order before node n3. To solve this, this patch introduces the ZeroLatencyHeight (ZLH) property for nodes. It is defined as the maximum unweighted length of a path from the given node to an arbitrary node in which each edge has latency 0. So, ZLH(n0)=3, ZLH(n1)=ZLH(n3)=2, ZLH(n2)=1, and ZLH(n4)=0 In this patch, the preference for a greater ZeroLatencyHeight is added in the top-down mode of the node ordering algorithm, after the preference for a greater Height, and before the preference for a lower Movability. Therefore, the two allowed node-orders are n0, n1, n3, n2, n4 and n0, n3, n1, n2, n4. Both of them are valid node orders. In the same way, the bottom-up mode of the node ordering algorithm is adapted by introducing the ZeroLatencyDepth property for nodes. The patch is tested by adding extra checks to the following existing lit-tests: test/CodeGen/Hexagon/SUnit-boundary-prob.ll test/CodeGen/Hexagon/frame-offset-overflow.ll test/CodeGen/Hexagon/vect/vect-shuffle.ll Before this patch, the pipeliner failed to pipeline the loops in these tests due to invalid node-orders. After the patch, the pipeliner successfully pipelines all these loops. Reviewers: bcahoon Reviewed By: bcahoon Subscribers: Ayal, mgrang, llvm-commits Differential Revision: https://reviews.llvm.org/D43620 llvm-svn: 326925 |
||
---|---|---|
.. | ||
AsmPrinter | ||
GlobalISel | ||
MIRParser | ||
SelectionDAG | ||
AggressiveAntiDepBreaker.cpp | ||
AggressiveAntiDepBreaker.h | ||
AllocationOrder.cpp | ||
AllocationOrder.h | ||
Analysis.cpp | ||
AntiDepBreaker.h | ||
AtomicExpandPass.cpp | ||
BasicTargetTransformInfo.cpp | ||
BranchFolding.cpp | ||
BranchFolding.h | ||
BranchRelaxation.cpp | ||
BreakFalseDeps.cpp | ||
BuiltinGCs.cpp | ||
CMakeLists.txt | ||
CalcSpillWeights.cpp | ||
CallingConvLower.cpp | ||
CodeGen.cpp | ||
CodeGenPrepare.cpp | ||
CriticalAntiDepBreaker.cpp | ||
CriticalAntiDepBreaker.h | ||
DFAPacketizer.cpp | ||
DeadMachineInstructionElim.cpp | ||
DetectDeadLanes.cpp | ||
DwarfEHPrepare.cpp | ||
EarlyIfConversion.cpp | ||
EdgeBundles.cpp | ||
ExecutionDomainFix.cpp | ||
ExpandISelPseudos.cpp | ||
ExpandMemCmp.cpp | ||
ExpandPostRAPseudos.cpp | ||
ExpandReductions.cpp | ||
FEntryInserter.cpp | ||
FaultMaps.cpp | ||
FuncletLayout.cpp | ||
GCMetadata.cpp | ||
GCMetadataPrinter.cpp | ||
GCRootLowering.cpp | ||
GCStrategy.cpp | ||
GlobalMerge.cpp | ||
IfConversion.cpp | ||
ImplicitNullChecks.cpp | ||
IndirectBrExpandPass.cpp | ||
InlineSpiller.cpp | ||
InterferenceCache.cpp | ||
InterferenceCache.h | ||
InterleavedAccessPass.cpp | ||
IntrinsicLowering.cpp | ||
LLVMBuild.txt | ||
LLVMTargetMachine.cpp | ||
LatencyPriorityQueue.cpp | ||
LazyMachineBlockFrequencyInfo.cpp | ||
LexicalScopes.cpp | ||
LiveDebugValues.cpp | ||
LiveDebugVariables.cpp | ||
LiveDebugVariables.h | ||
LiveInterval.cpp | ||
LiveIntervalUnion.cpp | ||
LiveIntervals.cpp | ||
LivePhysRegs.cpp | ||
LiveRangeCalc.cpp | ||
LiveRangeCalc.h | ||
LiveRangeEdit.cpp | ||
LiveRangeShrink.cpp | ||
LiveRangeUtils.h | ||
LiveRegMatrix.cpp | ||
LiveRegUnits.cpp | ||
LiveStacks.cpp | ||
LiveVariables.cpp | ||
LocalStackSlotAllocation.cpp | ||
LoopTraversal.cpp | ||
LowLevelType.cpp | ||
LowerEmuTLS.cpp | ||
MIRCanonicalizerPass.cpp | ||
MIRPrinter.cpp | ||
MIRPrintingPass.cpp | ||
MachineBasicBlock.cpp | ||
MachineBlockFrequencyInfo.cpp | ||
MachineBlockPlacement.cpp | ||
MachineBranchProbabilityInfo.cpp | ||
MachineCSE.cpp | ||
MachineCombiner.cpp | ||
MachineCopyPropagation.cpp | ||
MachineDominanceFrontier.cpp | ||
MachineDominators.cpp | ||
MachineFrameInfo.cpp | ||
MachineFunction.cpp | ||
MachineFunctionPass.cpp | ||
MachineFunctionPrinterPass.cpp | ||
MachineInstr.cpp | ||
MachineInstrBundle.cpp | ||
MachineLICM.cpp | ||
MachineLoopInfo.cpp | ||
MachineModuleInfo.cpp | ||
MachineModuleInfoImpls.cpp | ||
MachineOperand.cpp | ||
MachineOptimizationRemarkEmitter.cpp | ||
MachineOutliner.cpp | ||
MachinePassRegistry.cpp | ||
MachinePipeliner.cpp | ||
MachinePostDominators.cpp | ||
MachineRegionInfo.cpp | ||
MachineRegisterInfo.cpp | ||
MachineSSAUpdater.cpp | ||
MachineScheduler.cpp | ||
MachineSink.cpp | ||
MachineTraceMetrics.cpp | ||
MachineVerifier.cpp | ||
MacroFusion.cpp | ||
OptimizePHIs.cpp | ||
PHIElimination.cpp | ||
PHIEliminationUtils.cpp | ||
PHIEliminationUtils.h | ||
ParallelCG.cpp | ||
PatchableFunction.cpp | ||
PeepholeOptimizer.cpp | ||
PostRAHazardRecognizer.cpp | ||
PostRASchedulerList.cpp | ||
PreISelIntrinsicLowering.cpp | ||
ProcessImplicitDefs.cpp | ||
PrologEpilogInserter.cpp | ||
PseudoSourceValue.cpp | ||
README.txt | ||
ReachingDefAnalysis.cpp | ||
RegAllocBase.cpp | ||
RegAllocBase.h | ||
RegAllocBasic.cpp | ||
RegAllocFast.cpp | ||
RegAllocGreedy.cpp | ||
RegAllocPBQP.cpp | ||
RegUsageInfoCollector.cpp | ||
RegUsageInfoPropagate.cpp | ||
RegisterClassInfo.cpp | ||
RegisterCoalescer.cpp | ||
RegisterCoalescer.h | ||
RegisterPressure.cpp | ||
RegisterScavenging.cpp | ||
RegisterUsageInfo.cpp | ||
RenameIndependentSubregs.cpp | ||
ResetMachineFunctionPass.cpp | ||
SafeStack.cpp | ||
SafeStackColoring.cpp | ||
SafeStackColoring.h | ||
SafeStackLayout.cpp | ||
SafeStackLayout.h | ||
ScalarizeMaskedMemIntrin.cpp | ||
ScheduleDAG.cpp | ||
ScheduleDAGInstrs.cpp | ||
ScheduleDAGPrinter.cpp | ||
ScoreboardHazardRecognizer.cpp | ||
ShadowStackGCLowering.cpp | ||
ShrinkWrap.cpp | ||
SjLjEHPrepare.cpp | ||
SlotIndexes.cpp | ||
SpillPlacement.cpp | ||
SpillPlacement.h | ||
Spiller.h | ||
SplitKit.cpp | ||
SplitKit.h | ||
StackColoring.cpp | ||
StackMapLivenessAnalysis.cpp | ||
StackMaps.cpp | ||
StackProtector.cpp | ||
StackSlotColoring.cpp | ||
TailDuplication.cpp | ||
TailDuplicator.cpp | ||
TargetFrameLoweringImpl.cpp | ||
TargetInstrInfo.cpp | ||
TargetLoweringBase.cpp | ||
TargetLoweringObjectFileImpl.cpp | ||
TargetOptionsImpl.cpp | ||
TargetPassConfig.cpp | ||
TargetRegisterInfo.cpp | ||
TargetSchedule.cpp | ||
TargetSubtargetInfo.cpp | ||
TwoAddressInstructionPass.cpp | ||
UnreachableBlockElim.cpp | ||
VirtRegMap.cpp | ||
WinEHPrepare.cpp | ||
XRayInstrumentation.cpp |
README.txt
//===---------------------------------------------------------------------===// Common register allocation / spilling problem: mul lr, r4, lr str lr, [sp, #+52] ldr lr, [r1, #+32] sxth r3, r3 ldr r4, [sp, #+52] mla r4, r3, lr, r4 can be: mul lr, r4, lr mov r4, lr str lr, [sp, #+52] ldr lr, [r1, #+32] sxth r3, r3 mla r4, r3, lr, r4 and then "merge" mul and mov: mul r4, r4, lr str r4, [sp, #+52] ldr lr, [r1, #+32] sxth r3, r3 mla r4, r3, lr, r4 It also increase the likelihood the store may become dead. //===---------------------------------------------------------------------===// bb27 ... ... %reg1037 = ADDri %reg1039, 1 %reg1038 = ADDrs %reg1032, %reg1039, %noreg, 10 Successors according to CFG: 0x8b03bf0 (#5) bb76 (0x8b03bf0, LLVM BB @0x8b032d0, ID#5): Predecessors according to CFG: 0x8b0c5f0 (#3) 0x8b0a7c0 (#4) %reg1039 = PHI %reg1070, mbb<bb76.outer,0x8b0c5f0>, %reg1037, mbb<bb27,0x8b0a7c0> Note ADDri is not a two-address instruction. However, its result %reg1037 is an operand of the PHI node in bb76 and its operand %reg1039 is the result of the PHI node. We should treat it as a two-address code and make sure the ADDri is scheduled after any node that reads %reg1039. //===---------------------------------------------------------------------===// Use local info (i.e. register scavenger) to assign it a free register to allow reuse: ldr r3, [sp, #+4] add r3, r3, #3 ldr r2, [sp, #+8] add r2, r2, #2 ldr r1, [sp, #+4] <== add r1, r1, #1 ldr r0, [sp, #+4] add r0, r0, #2 //===---------------------------------------------------------------------===// LLVM aggressively lift CSE out of loop. Sometimes this can be negative side- effects: R1 = X + 4 R2 = X + 7 R3 = X + 15 loop: load [i + R1] ... load [i + R2] ... load [i + R3] Suppose there is high register pressure, R1, R2, R3, can be spilled. We need to implement proper re-materialization to handle this: R1 = X + 4 R2 = X + 7 R3 = X + 15 loop: R1 = X + 4 @ re-materialized load [i + R1] ... R2 = X + 7 @ re-materialized load [i + R2] ... R3 = X + 15 @ re-materialized load [i + R3] Furthermore, with re-association, we can enable sharing: R1 = X + 4 R2 = X + 7 R3 = X + 15 loop: T = i + X load [T + 4] ... load [T + 7] ... load [T + 15] //===---------------------------------------------------------------------===// It's not always a good idea to choose rematerialization over spilling. If all the load / store instructions would be folded then spilling is cheaper because it won't require new live intervals / registers. See 2003-05-31-LongShifts for an example. //===---------------------------------------------------------------------===// With a copying garbage collector, derived pointers must not be retained across collector safe points; the collector could move the objects and invalidate the derived pointer. This is bad enough in the first place, but safe points can crop up unpredictably. Consider: %array = load { i32, [0 x %obj] }** %array_addr %nth_el = getelementptr { i32, [0 x %obj] }* %array, i32 0, i32 %n %old = load %obj** %nth_el %z = div i64 %x, %y store %obj* %new, %obj** %nth_el If the i64 division is lowered to a libcall, then a safe point will (must) appear for the call site. If a collection occurs, %array and %nth_el no longer point into the correct object. The fix for this is to copy address calculations so that dependent pointers are never live across safe point boundaries. But the loads cannot be copied like this if there was an intervening store, so may be hard to get right. Only a concurrent mutator can trigger a collection at the libcall safe point. So single-threaded programs do not have this requirement, even with a copying collector. Still, LLVM optimizations would probably undo a front-end's careful work. //===---------------------------------------------------------------------===// The ocaml frametable structure supports liveness information. It would be good to support it. //===---------------------------------------------------------------------===// The FIXME in ComputeCommonTailLength in BranchFolding.cpp needs to be revisited. The check is there to work around a misuse of directives in inline assembly. //===---------------------------------------------------------------------===// It would be good to detect collector/target compatibility instead of silently doing the wrong thing. //===---------------------------------------------------------------------===// It would be really nice to be able to write patterns in .td files for copies, which would eliminate a bunch of explicit predicates on them (e.g. no side effects). Once this is in place, it would be even better to have tblgen synthesize the various copy insertion/inspection methods in TargetInstrInfo. //===---------------------------------------------------------------------===// Stack coloring improvements: 1. Do proper LiveStacks analysis on all stack objects including those which are not spill slots. 2. Reorder objects to fill in gaps between objects. e.g. 4, 1, <gap>, 4, 1, 1, 1, <gap>, 4 => 4, 1, 1, 1, 1, 4, 4 //===---------------------------------------------------------------------===// The scheduler should be able to sort nearby instructions by their address. For example, in an expanded memset sequence it's not uncommon to see code like this: movl $0, 4(%rdi) movl $0, 8(%rdi) movl $0, 12(%rdi) movl $0, 0(%rdi) Each of the stores is independent, and the scheduler is currently making an arbitrary decision about the order. //===---------------------------------------------------------------------===// Another opportunitiy in this code is that the $0 could be moved to a register: movl $0, 4(%rdi) movl $0, 8(%rdi) movl $0, 12(%rdi) movl $0, 0(%rdi) This would save substantial code size, especially for longer sequences like this. It would be easy to have a rule telling isel to avoid matching MOV32mi if the immediate has more than some fixed number of uses. It's more involved to teach the register allocator how to do late folding to recover from excessive register pressure.