This patch is not as straight-forward as the first patch (reviews.llvm.org/D18092). It applies on top of the first patch for swifterror.
There is one more patch left for swifterror, the last patch will modify targets: ARM, X86 and AArch64.
At IR level, the swifterror argument is an input argument with type ErrorObject**. But we want to optimize it to behave as an extra return value with type ErrorObject*. It will be passed in a fixed physical register.
The main idea is to track the virtual registers for each swifterror value. We define swifterror values as AllocaInsts with swifterror attribute or a function argument with the attribute.
We introduce data structures in FunctionLoweringInfo to support versioning for swifterror values.
1> typedef SmallVector<const Value*, 1> SwiftErrorValues;
SwiftErrorValues SwiftErrorVals; --> this is the vector to hold all swifterror values in the function.
2> typedef SmallVector<unsigned, 1> SwiftErrorVRegs;
llvm::DenseMap<const MachineBasicBlock*, SwiftErrorVRegs> SwiftErrorMap; --> Track the virtual register for each swifterror value in a given basic block.
3> llvm::DenseMap<const MachineBasicBlock*, SwiftErrorVRegs> SwiftErrorWorklist;
--> Track the virtual register for a swifterror value at the end of a basic block when the basic block is not yet visited.
In SelectionDAGISel.cpp, we set up swifterror values (SwiftErrorVals) before handling the basic blocks.
When iterating over all basic blocks in RPO, before actually visiting the basic block, we call mergeIncomingSwiftErrors to merge incoming swifterror values when there are multiple predecessors or simply propagate them. There, we create a virtual register for each swifterror value in the entry block. For predecessors that are not yet visited, we create virtual registers to hold the swifterror values at the end of the predecessor. The assignments are saved in SwiftErrorWorklist.
At the end of a basic block, we copy swifterror values to the final virtual registers according to SwiftErrorWorklist.
When visiting a load from a swifterror value, we copy from the current virtual register assignment for the value. When visiting a store to a swifterror value, we create a virtual register to hold the swifterror value and update SwiftErrorMap to track the current virtual register assignment.
When handling the ReturnInst, we fake an output argument for the swifterror argument, its value coming from the virtual register assignment. Target-specific calling convention can assign the output argument to a certain physical register.
When lowering a call to a function with a swifterror parameter, we fake an entry in CLI.Ins (the last entry in CLI.Ins) with pointer type. Target-specific calling convention can assign the output argument to a certain physical register. We then get the last element of CLI.InVals and copy it to a newly-created virtual register and update SwiftErrorMap for version tracking.