/// calculateFrameObjectOffsets - Calculate actual frame offsets for all of the /// abstract stack objects. void PEI::calculateFrameObjectOffsets(MachineFunction &MF) { const TargetFrameLowering &TFI = *MF.getSubtarget().getFrameLowering(); bool StackGrowsDown = TFI.getStackGrowthDirection() == TargetFrameLowering::StackGrowsDown; // Loop over all of the stack objects, assigning sequential addresses... MachineFrameInfo &MFI = MF.getFrameInfo(); // Start at the beginning of the local area. // The Offset is the distance from the stack top in the direction // of stack growth -- so it's always nonnegative. int LocalAreaOffset = TFI.getOffsetOfLocalArea(); if (StackGrowsDown) LocalAreaOffset = -LocalAreaOffset; assert(LocalAreaOffset >= 0 && "Local area offset should be in direction of stack growth"); int64_t Offset = LocalAreaOffset; // Skew to be applied to alignment. unsigned Skew = TFI.getStackAlignmentSkew(MF); // If there are fixed sized objects that are preallocated in the local area, // non-fixed objects can't be allocated right at the start of local area. // Adjust 'Offset' to point to the end of last fixed sized preallocated // object. for (int i = MFI.getObjectIndexBegin(); i != 0; ++i) { int64_t FixedOff; if (StackGrowsDown) { // The maximum distance from the stack pointer is at lower address of // the object -- which is given by offset. For down growing stack // the offset is negative, so we negate the offset to get the distance. FixedOff = -MFI.getObjectOffset(i); } else { // The maximum distance from the start pointer is at the upper // address of the object. FixedOff = MFI.getObjectOffset(i) + MFI.getObjectSize(i); } if (FixedOff > Offset) Offset = FixedOff; } // First assign frame offsets to stack objects that are used to spill // callee saved registers. if (StackGrowsDown) { for (unsigned i = MinCSFrameIndex; i <= MaxCSFrameIndex; ++i) { // If the stack grows down, we need to add the size to find the lowest // address of the object. Offset += MFI.getObjectSize(i); unsigned Align = MFI.getObjectAlignment(i); // Adjust to alignment boundary Offset = alignTo(Offset, Align, Skew); LLVM_DEBUG(dbgs() << "alloc FI(" << i << ") at SP[" << -Offset << "]\n"); MFI.setObjectOffset(i, -Offset); // Set the computed offset } } else if (MaxCSFrameIndex >= MinCSFrameIndex) { // Be careful about underflow in comparisons agains MinCSFrameIndex. for (unsigned i = MaxCSFrameIndex; i != MinCSFrameIndex - 1; --i) { if (MFI.isDeadObjectIndex(i)) continue; unsigned Align = MFI.getObjectAlignment(i); // Adjust to alignment boundary Offset = alignTo(Offset, Align, Skew); LLVM_DEBUG(dbgs() << "alloc FI(" << i << ") at SP[" << Offset << "]\n"); MFI.setObjectOffset(i, Offset); Offset += MFI.getObjectSize(i); } } // FixedCSEnd is the stack offset to the end of the fixed and callee-save // stack area. int64_t FixedCSEnd = Offset; unsigned MaxAlign = MFI.getMaxAlignment(); // Make sure the special register scavenging spill slot is closest to the // incoming stack pointer if a frame pointer is required and is closer // to the incoming rather than the final stack pointer. const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo(); bool EarlyScavengingSlots = (TFI.hasFP(MF) && TFI.isFPCloseToIncomingSP() && RegInfo->useFPForScavengingIndex(MF) && !RegInfo->needsStackRealignment(MF)); if (RS && EarlyScavengingSlots) { SmallVector<int, 2> SFIs; RS->getScavengingFrameIndices(SFIs); for (SmallVectorImpl<int>::iterator I = SFIs.begin(), IE = SFIs.end(); I != IE; ++I) AdjustStackOffset(MFI, *I, StackGrowsDown, Offset, MaxAlign, Skew); } // FIXME: Once this is working, then enable flag will change to a target // check for whether the frame is large enough to want to use virtual // frame index registers. Functions which don't want/need this optimization // will continue to use the existing code path. if (MFI.getUseLocalStackAllocationBlock()) { unsigned Align = MFI.getLocalFrameMaxAlign(); // Adjust to alignment boundary. Offset = alignTo(Offset, Align, Skew); LLVM_DEBUG(dbgs() << "Local frame base offset: " << Offset << "\n"); // Resolve offsets for objects in the local block. for (unsigned i = 0, e = MFI.getLocalFrameObjectCount(); i != e; ++i) { std::pair<int, int64_t> Entry = MFI.getLocalFrameObjectMap(i); int64_t FIOffset = (StackGrowsDown ? -Offset : Offset) + Entry.second; LLVM_DEBUG(dbgs() << "alloc FI(" << Entry.first << ") at SP[" << FIOffset << "]\n"); MFI.setObjectOffset(Entry.first, FIOffset); } // Allocate the local block Offset += MFI.getLocalFrameSize(); MaxAlign = std::max(Align, MaxAlign); } // Retrieve the Exception Handler registration node. int EHRegNodeFrameIndex = std::numeric_limits<int>::max(); if (const WinEHFuncInfo *FuncInfo = MF.getWinEHFuncInfo()) EHRegNodeFrameIndex = FuncInfo->EHRegNodeFrameIndex; // Make sure that the stack protector comes before the local variables on the // stack. SmallSet<int, 16> ProtectedObjs; if (MFI.getStackProtectorIndex() >= 0) { StackObjSet LargeArrayObjs; StackObjSet SmallArrayObjs; StackObjSet AddrOfObjs; AdjustStackOffset(MFI, MFI.getStackProtectorIndex(), StackGrowsDown, Offset, MaxAlign, Skew); // Assign large stack objects first. for (unsigned i = 0, e = MFI.getObjectIndexEnd(); i != e; ++i) { if (MFI.isObjectPreAllocated(i) && MFI.getUseLocalStackAllocationBlock()) continue; if (i >= MinCSFrameIndex && i <= MaxCSFrameIndex) continue; if (RS && RS->isScavengingFrameIndex((int)i)) continue; if (MFI.isDeadObjectIndex(i)) continue; if (MFI.getStackProtectorIndex() == (int)i || EHRegNodeFrameIndex == (int)i) continue; switch (MFI.getObjectSSPLayout(i)) { case MachineFrameInfo::SSPLK_None: continue; case MachineFrameInfo::SSPLK_SmallArray: SmallArrayObjs.insert(i); continue; case MachineFrameInfo::SSPLK_AddrOf: AddrOfObjs.insert(i); continue; case MachineFrameInfo::SSPLK_LargeArray: LargeArrayObjs.insert(i); continue; } llvm_unreachable("Unexpected SSPLayoutKind."); } AssignProtectedObjSet(LargeArrayObjs, ProtectedObjs, MFI, StackGrowsDown, Offset, MaxAlign, Skew); AssignProtectedObjSet(SmallArrayObjs, ProtectedObjs, MFI, StackGrowsDown, Offset, MaxAlign, Skew); AssignProtectedObjSet(AddrOfObjs, ProtectedObjs, MFI, StackGrowsDown, Offset, MaxAlign, Skew); } SmallVector<int, 8> ObjectsToAllocate; // Then prepare to assign frame offsets to stack objects that are not used to // spill callee saved registers. for (unsigned i = 0, e = MFI.getObjectIndexEnd(); i != e; ++i) { if (MFI.isObjectPreAllocated(i) && MFI.getUseLocalStackAllocationBlock()) continue; if (i >= MinCSFrameIndex && i <= MaxCSFrameIndex) continue; if (RS && RS->isScavengingFrameIndex((int)i)) continue; if (MFI.isDeadObjectIndex(i)) continue; if (MFI.getStackProtectorIndex() == (int)i || EHRegNodeFrameIndex == (int)i) continue; if (ProtectedObjs.count(i)) continue; // Add the objects that we need to allocate to our working set. ObjectsToAllocate.push_back(i); } // Allocate the EH registration node first if one is present. if (EHRegNodeFrameIndex != std::numeric_limits<int>::max()) AdjustStackOffset(MFI, EHRegNodeFrameIndex, StackGrowsDown, Offset, MaxAlign, Skew); // Give the targets a chance to order the objects the way they like it. if (MF.getTarget().getOptLevel() != CodeGenOpt::None && MF.getTarget().Options.StackSymbolOrdering) TFI.orderFrameObjects(MF, ObjectsToAllocate); // Keep track of which bytes in the fixed and callee-save range are used so we // can use the holes when allocating later stack objects. Only do this if // stack protector isn't being used and the target requests it and we're // optimizing. BitVector StackBytesFree; if (!ObjectsToAllocate.empty() && MF.getTarget().getOptLevel() != CodeGenOpt::None && MFI.getStackProtectorIndex() < 0 && TFI.enableStackSlotScavenging(MF)) computeFreeStackSlots(MFI, StackGrowsDown, MinCSFrameIndex, MaxCSFrameIndex, FixedCSEnd, StackBytesFree); // Now walk the objects and actually assign base offsets to them. for (auto &Object : ObjectsToAllocate) if (!scavengeStackSlot(MFI, Object, StackGrowsDown, MaxAlign, StackBytesFree)) AdjustStackOffset(MFI, Object, StackGrowsDown, Offset, MaxAlign, Skew); // Make sure the special register scavenging spill slot is closest to the // stack pointer. if (RS && !EarlyScavengingSlots) { SmallVector<int, 2> SFIs; RS->getScavengingFrameIndices(SFIs); for (SmallVectorImpl<int>::iterator I = SFIs.begin(), IE = SFIs.end(); I != IE; ++I) AdjustStackOffset(MFI, *I, StackGrowsDown, Offset, MaxAlign, Skew); } if (!TFI.targetHandlesStackFrameRounding()) { // If we have reserved argument space for call sites in the function // immediately on entry to the current function, count it as part of the // overall stack size. if (MFI.adjustsStack() && TFI.hasReservedCallFrame(MF)) Offset += MFI.getMaxCallFrameSize(); // Round up the size to a multiple of the alignment. If the function has // any calls or alloca's, align to the target's StackAlignment value to // ensure that the callee's frame or the alloca data is suitably aligned; // otherwise, for leaf functions, align to the TransientStackAlignment // value. unsigned StackAlign; if (MFI.adjustsStack() || MFI.hasVarSizedObjects() || (RegInfo->needsStackRealignment(MF) && MFI.getObjectIndexEnd() != 0)) StackAlign = TFI.getStackAlignment(); else StackAlign = TFI.getTransientStackAlignment(); // If the frame pointer is eliminated, all frame offsets will be relative to // SP not FP. Align to MaxAlign so this works. StackAlign = std::max(StackAlign, MaxAlign); Offset = alignTo(Offset, StackAlign, Skew); } // Update frame info to pretend that this is part of the stack... int64_t StackSize = Offset - LocalAreaOffset; MFI.setStackSize(StackSize); NumBytesStackSpace += StackSize; }
bool GcInfoRecorder::runOnMachineFunction(MachineFunction &MF) { const Function *F = MF.getFunction(); if (!GcInfo::isGcFunction(F)) { return false; } LLILCJitContext *Context = LLILCJit::TheJit->getLLILCJitContext(); GcFuncInfo *GcFuncInfo = Context->GcInfo->getGcInfo(F); ValueMap<const AllocaInst *, AllocaInfo> &AllocaMap = GcFuncInfo->AllocaMap; #if !defined(NDEBUG) bool EmitLogs = Context->Options->LogGcInfo; if (EmitLogs) { dbgs() << "GcInfoRecorder: " << MF.getFunction()->getName() << "\n"; } #endif // !NDEBUG const MachineFrameInfo *FrameInfo = MF.getFrameInfo(); int ObjectIndexBegin = FrameInfo->getObjectIndexBegin(); int ObjectIndexEnd = FrameInfo->getObjectIndexEnd(); // FrameInfo reports the allocation offsets in terms of the // incoming (caller's) StackPointer. Convert these in terms of the // current (callee's) StackPointer. uint64_t StackPointerSize = MF.getDataLayout().getPointerSize(); uint64_t SpOffset = FrameInfo->getStackSize() + StackPointerSize; for (int Idx = ObjectIndexBegin; Idx < ObjectIndexEnd; Idx++) { const AllocaInst *Alloca = FrameInfo->getObjectAllocation(Idx); if (Alloca == nullptr) { continue; } if (GcFuncInfo->hasRecord(Alloca)) { int32_t SlotOffset = SpOffset + FrameInfo->getObjectOffset(Idx); AllocaInfo &AllocaInfo = AllocaMap[Alloca]; assert(SlotOffset >= 0); assert(AllocaInfo.Offset == GcInfo::InvalidPointerOffset && "Two slots for the same alloca!"); AllocaInfo.Offset = SlotOffset; #if !defined(NDEBUG) if (AllocaInfo.isGcAggregate()) { assert(isa<StructType>(Alloca->getAllocatedType()) && "Unhandled Type of GcAggregate"); } else if (AllocaInfo.isGcPointer()) { assert(GcInfo::isGcPointer(Alloca->getAllocatedType())); } else { assert(!GcInfo::isGcAllocation(Alloca)); } if (EmitLogs) { dbgs() << AllocaInfo.getAllocTypeString() << " @ sp+" << SlotOffset << " ["; Alloca->printAsOperand(dbgs(), false); dbgs() << "]\n"; } #endif // !NDEBUG } else { // All GC-aggregate Allocas must be registered before this phase. // This ensures that the allocations are properly initialized, // and marked as frame-escaped if necessary. // // TODO: The following check should be: // assert(!GcInfo::isGcAllocation(Alloca) && // "Gc Allocation Record missing"); #if !defined(NDEBUG) if (GcInfo::isGcAllocation(Alloca)) { // However, some Gc-pointer slots created by WinEHPrepare phase // go unrecorded currently because the AllocaInfos are recorded // by the Reader post-pass. // TODO: Report the Spill slots created by WinEHPrepare // https://github.com/dotnet/llilc/issues/901 assert(Alloca->hasName()); assert(Alloca->getName().find(".wineh.spillslot") != StringRef::npos); // WinEH shouldn't spill GC-aggregates assert(!GcInfo::isGcAggregate(Alloca->getAllocatedType())); // The unreported slots are live across safepoints in the // EH path, so the execution is correct unless we take the // exception path. assert(!(Context->Options->ExecuteHandlers && Context->Options->DoInsertStatepoints) && "Untested: Use at your own risk"); } #endif // !NDEBUG } } // Unlike the other offsets reported to the GC, the PSPSym offset is relative // to Initial-SP (i.e. the value of the stack pointer just after this // method's prolog), NOT Caller-SP. if (WinEHFuncInfo *EHInfo = MF.getWinEHFuncInfo()) { int PSPSymIndex = EHInfo->PSPSymFrameIdx; if (PSPSymIndex != INT_MAX) { GcFuncInfo->HasFunclets = true; const TargetFrameLowering *TFL = MF.getSubtarget().getFrameLowering(); // SPReg is an out parameter that we're not using here. unsigned SPReg; int PSPOffset = TFL->getFrameIndexReferenceFromSP(MF, PSPSymIndex, SPReg); assert(PSPOffset >= 0); GcFuncInfo->PSPSymOffset = static_cast<uint32_t>(PSPOffset); } } return false; // success }