mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-08-10 02:29:32 +00:00
The way that stack coloring updated MMOs when merging stack slots, while correct, is suboptimal, and is incompatible with the use of AA during instruction scheduling. The solution, which involves the use of const_cast (and more importantly, updating the IR from within an MI-level pass), obviously requires some explanation: When the stack coloring pass was originally committed, the code in ScheduleDAGInstrs::buildSchedGraph tracked possible alias sets by using GetUnderlyingObject, and all load/store and store/store memory control dependencies where added between SUs at the object level (where only one object, that returned by GetUnderlyingObject, was used to identify the object associated with each MMO). When stack coloring merged stack slots, it would replace MMOs derived from the remapped alloca with the alloca with which the remapped alloca was being replaced. Because ScheduleDAGInstrs only used single objects, and tracked alias sets at the object level, this was a fine solution. In r169744, (Andy and) I updated the code in ScheduleDAGInstrs to use GetUnderlyingObjects, and track alias sets using, potentially, multiple underlying objects for each MMO. This was done, primarily, to provide the ability to look through PHIs, and provide better scheduling for induction-variable-dependent loads and stores inside loops. At this point, the MMO-updating code in stack coloring became suboptimal, because it would clear the MMOs for (i.e. completely pessimize) all instructions for which r169744 might help in scheduling. Updating the IR directly is the simplest fix for this (and the one with, by far, the least compile-time impact), but others are possible (we could give each MMO a small vector of potential values, or make use of a remapping table, constructed from MFI, inside ScheduleDAGInstrs). Unfortunately, replacing all MMO values derived from the remapped alloca with the base replacement alloca fundamentally breaks our ability to use AA during instruction scheduling (which is critical to performance on some targets). The reason is that the original MMO might have had an offset (either constant or dynamic) from the base remapped alloca, and that offset is not present in the updated MMO. One possible way around this would be to use GetPointerBaseWithConstantOffset, and update not only the MMO's value, but also its offset based on the original offset. Unfortunately, this solution would only handle constant offsets, and for safety (because AA is not completely restricted to deducing relationships with constant offsets), we would need to clear all MMOs without constant offsets over the entire function. This would be an even worse pessimization than the current single-object restriction. Any other solution would involve passing around a vector of remapped allocas, and teaching AA to use it, introducing additional complexity and overhead into AA. Instead, when remapping an alloca, we replace all IR uses of that alloca as well (optionally inserting a bitcast as necessary). This is even more efficient that the old MMO-updating code in the stack coloring pass (because it removes the need to call GetUnderlyingObject on all MMO values), removes the single-object pessimization in the default configuration, and enables the correct use of AA during instruction scheduling (all without any additional overhead). LLVM now no longer miscompiles itself on x86_64 when using -enable-misched -enable-aa-sched-mi -misched-bottomup=0 -misched-topdown=0 -misched=shuffle! Fixed PR18497. Because the alloca replacement is now done at the IR level, unless the MMO directly refers to the remapped alloca, the change cannot be seen at the MI level. As a result, there is no good way to fix test/CodeGen/X86/pr14090.ll. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@199658 91177308-0d34-0410-b5e6-96231b3b80d8 |
||
---|---|---|
.. | ||
AsmPrinter | ||
SelectionDAG | ||
AggressiveAntiDepBreaker.cpp | ||
AggressiveAntiDepBreaker.h | ||
AllocationOrder.cpp | ||
AllocationOrder.h | ||
Analysis.cpp | ||
AntiDepBreaker.h | ||
BasicTargetTransformInfo.cpp | ||
BranchFolding.cpp | ||
BranchFolding.h | ||
CalcSpillWeights.cpp | ||
CallingConvLower.cpp | ||
CMakeLists.txt | ||
CodeGen.cpp | ||
CriticalAntiDepBreaker.cpp | ||
CriticalAntiDepBreaker.h | ||
DeadMachineInstructionElim.cpp | ||
DFAPacketizer.cpp | ||
DwarfEHPrepare.cpp | ||
EarlyIfConversion.cpp | ||
EdgeBundles.cpp | ||
ErlangGC.cpp | ||
ExecutionDepsFix.cpp | ||
ExpandISelPseudos.cpp | ||
ExpandPostRAPseudos.cpp | ||
GCMetadata.cpp | ||
GCMetadataPrinter.cpp | ||
GCStrategy.cpp | ||
IfConversion.cpp | ||
InlineSpiller.cpp | ||
InterferenceCache.cpp | ||
InterferenceCache.h | ||
IntrinsicLowering.cpp | ||
JITCodeEmitter.cpp | ||
LatencyPriorityQueue.cpp | ||
LexicalScopes.cpp | ||
LiveDebugVariables.cpp | ||
LiveDebugVariables.h | ||
LiveInterval.cpp | ||
LiveIntervalAnalysis.cpp | ||
LiveIntervalUnion.cpp | ||
LivePhysRegs.cpp | ||
LiveRangeCalc.cpp | ||
LiveRangeCalc.h | ||
LiveRangeEdit.cpp | ||
LiveRegMatrix.cpp | ||
LiveStackAnalysis.cpp | ||
LiveVariables.cpp | ||
LLVMBuild.txt | ||
LLVMTargetMachine.cpp | ||
LocalStackSlotAllocation.cpp | ||
MachineBasicBlock.cpp | ||
MachineBlockFrequencyInfo.cpp | ||
MachineBlockPlacement.cpp | ||
MachineBranchProbabilityInfo.cpp | ||
MachineCodeEmitter.cpp | ||
MachineCopyPropagation.cpp | ||
MachineCSE.cpp | ||
MachineDominators.cpp | ||
MachineFunction.cpp | ||
MachineFunctionAnalysis.cpp | ||
MachineFunctionPass.cpp | ||
MachineFunctionPrinterPass.cpp | ||
MachineInstr.cpp | ||
MachineInstrBundle.cpp | ||
MachineLICM.cpp | ||
MachineLoopInfo.cpp | ||
MachineModuleInfo.cpp | ||
MachineModuleInfoImpls.cpp | ||
MachinePassRegistry.cpp | ||
MachinePostDominators.cpp | ||
MachineRegisterInfo.cpp | ||
MachineScheduler.cpp | ||
MachineSink.cpp | ||
MachineSSAUpdater.cpp | ||
MachineTraceMetrics.cpp | ||
MachineVerifier.cpp | ||
Makefile | ||
OcamlGC.cpp | ||
OptimizePHIs.cpp | ||
Passes.cpp | ||
PeepholeOptimizer.cpp | ||
PHIElimination.cpp | ||
PHIEliminationUtils.cpp | ||
PHIEliminationUtils.h | ||
PostRASchedulerList.cpp | ||
ProcessImplicitDefs.cpp | ||
PrologEpilogInserter.cpp | ||
PrologEpilogInserter.h | ||
PseudoSourceValue.cpp | ||
README.txt | ||
RegAllocBase.cpp | ||
RegAllocBase.h | ||
RegAllocBasic.cpp | ||
RegAllocFast.cpp | ||
RegAllocGreedy.cpp | ||
RegAllocPBQP.cpp | ||
RegisterClassInfo.cpp | ||
RegisterCoalescer.cpp | ||
RegisterCoalescer.h | ||
RegisterPressure.cpp | ||
RegisterScavenging.cpp | ||
ScheduleDAG.cpp | ||
ScheduleDAGInstrs.cpp | ||
ScheduleDAGPrinter.cpp | ||
ScoreboardHazardRecognizer.cpp | ||
ShadowStackGC.cpp | ||
SjLjEHPrepare.cpp | ||
SlotIndexes.cpp | ||
Spiller.cpp | ||
Spiller.h | ||
SpillPlacement.cpp | ||
SpillPlacement.h | ||
SplitKit.cpp | ||
SplitKit.h | ||
StackColoring.cpp | ||
StackMapLivenessAnalysis.cpp | ||
StackMaps.cpp | ||
StackProtector.cpp | ||
StackSlotColoring.cpp | ||
TailDuplication.cpp | ||
TargetFrameLoweringImpl.cpp | ||
TargetInstrInfo.cpp | ||
TargetLoweringBase.cpp | ||
TargetLoweringObjectFileImpl.cpp | ||
TargetOptionsImpl.cpp | ||
TargetRegisterInfo.cpp | ||
TargetSchedule.cpp | ||
TwoAddressInstructionPass.cpp | ||
UnreachableBlockElim.cpp | ||
VirtRegMap.cpp |
//===---------------------------------------------------------------------===// 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 LiveStackAnalysis 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.