| //===- StatepointLowering.h - SDAGBuilder's statepoint code ---*- C++ -*---===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file includes support code use by SelectionDAGBuilder when lowering a | |
| // statepoint sequence in SelectionDAG IR. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_LIB_CODEGEN_SELECTIONDAG_STATEPOINTLOWERING_H | |
| #define LLVM_LIB_CODEGEN_SELECTIONDAG_STATEPOINTLOWERING_H | |
| #include "llvm/ADT/DenseMap.h" | |
| #include "llvm/ADT/SmallBitVector.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/CodeGen/SelectionDAGNodes.h" | |
| #include "llvm/IR/IntrinsicInst.h" | |
| #include <cassert> | |
| namespace llvm { | |
| class SelectionDAGBuilder; | |
| /// This class tracks both per-statepoint and per-selectiondag information. | |
| /// For each statepoint it tracks locations of it's gc valuess (incoming and | |
| /// relocated) and list of gcreloc calls scheduled for visiting (this is | |
| /// used for a debug mode consistency check only). The spill slot tracking | |
| /// works in concert with information in FunctionLoweringInfo. | |
| class StatepointLoweringState { | |
| public: | |
| StatepointLoweringState() = default; | |
| /// Reset all state tracking for a newly encountered safepoint. Also | |
| /// performs some consistency checking. | |
| void startNewStatepoint(SelectionDAGBuilder &Builder); | |
| /// Clear the memory usage of this object. This is called from | |
| /// SelectionDAGBuilder::clear. We require this is never called in the | |
| /// midst of processing a statepoint sequence. | |
| void clear(); | |
| /// Returns the spill location of a value incoming to the current | |
| /// statepoint. Will return SDValue() if this value hasn't been | |
| /// spilled. Otherwise, the value has already been spilled and no | |
| /// further action is required by the caller. | |
| SDValue getLocation(SDValue Val) { | |
| auto I = Locations.find(Val); | |
| if (I == Locations.end()) | |
| return SDValue(); | |
| return I->second; | |
| } | |
| void setLocation(SDValue Val, SDValue Location) { | |
| assert(!Locations.count(Val) && | |
| "Trying to allocate already allocated location"); | |
| Locations[Val] = Location; | |
| } | |
| /// Record the fact that we expect to encounter a given gc_relocate | |
| /// before the next statepoint. If we don't see it, we'll report | |
| /// an assertion. | |
| void scheduleRelocCall(const GCRelocateInst &RelocCall) { | |
| // We are not interested in lowering dead instructions. | |
| if (!RelocCall.use_empty()) | |
| PendingGCRelocateCalls.push_back(&RelocCall); | |
| } | |
| /// Remove this gc_relocate from the list we're expecting to see | |
| /// before the next statepoint. If we weren't expecting to see | |
| /// it, we'll report an assertion. | |
| void relocCallVisited(const GCRelocateInst &RelocCall) { | |
| // We are not interested in lowering dead instructions. | |
| if (RelocCall.use_empty()) | |
| return; | |
| auto I = llvm::find(PendingGCRelocateCalls, &RelocCall); | |
| assert(I != PendingGCRelocateCalls.end() && | |
| "Visited unexpected gcrelocate call"); | |
| PendingGCRelocateCalls.erase(I); | |
| } | |
| // TODO: Should add consistency tracking to ensure we encounter | |
| // expected gc_result calls too. | |
| /// Get a stack slot we can use to store an value of type ValueType. This | |
| /// will hopefully be a recylced slot from another statepoint. | |
| SDValue allocateStackSlot(EVT ValueType, SelectionDAGBuilder &Builder); | |
| void reserveStackSlot(int Offset) { | |
| assert(Offset >= 0 && Offset < (int)AllocatedStackSlots.size() && | |
| "out of bounds"); | |
| assert(!AllocatedStackSlots.test(Offset) && "already reserved!"); | |
| assert(NextSlotToAllocate <= (unsigned)Offset && "consistency!"); | |
| AllocatedStackSlots.set(Offset); | |
| } | |
| bool isStackSlotAllocated(int Offset) { | |
| assert(Offset >= 0 && Offset < (int)AllocatedStackSlots.size() && | |
| "out of bounds"); | |
| return AllocatedStackSlots.test(Offset); | |
| } | |
| private: | |
| /// Maps pre-relocation value (gc pointer directly incoming into statepoint) | |
| /// into it's location (currently only stack slots) | |
| DenseMap<SDValue, SDValue> Locations; | |
| /// A boolean indicator for each slot listed in the FunctionInfo as to | |
| /// whether it has been used in the current statepoint. Since we try to | |
| /// preserve stack slots across safepoints, there can be gaps in which | |
| /// slots have been allocated. | |
| SmallBitVector AllocatedStackSlots; | |
| /// Points just beyond the last slot known to have been allocated | |
| unsigned NextSlotToAllocate = 0; | |
| /// Keep track of pending gcrelocate calls for consistency check | |
| SmallVector<const GCRelocateInst *, 10> PendingGCRelocateCalls; | |
| }; | |
| } // end namespace llvm | |
| #endif // LLVM_LIB_CODEGEN_SELECTIONDAG_STATEPOINTLOWERING_H |