109467b48Spatrick //===- SafeStackLayout.cpp - SafeStack frame layout -----------------------===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick
909467b48Spatrick #include "SafeStackLayout.h"
1009467b48Spatrick #include "llvm/IR/Value.h"
1109467b48Spatrick #include "llvm/Support/CommandLine.h"
1209467b48Spatrick #include "llvm/Support/Compiler.h"
1309467b48Spatrick #include "llvm/Support/Debug.h"
1409467b48Spatrick #include "llvm/Support/raw_ostream.h"
1509467b48Spatrick #include <algorithm>
1609467b48Spatrick #include <cassert>
1709467b48Spatrick
1809467b48Spatrick using namespace llvm;
1909467b48Spatrick using namespace llvm::safestack;
2009467b48Spatrick
2109467b48Spatrick #define DEBUG_TYPE "safestacklayout"
2209467b48Spatrick
2309467b48Spatrick static cl::opt<bool> ClLayout("safe-stack-layout",
2409467b48Spatrick cl::desc("enable safe stack layout"), cl::Hidden,
2509467b48Spatrick cl::init(true));
2609467b48Spatrick
print(raw_ostream & OS)2709467b48Spatrick LLVM_DUMP_METHOD void StackLayout::print(raw_ostream &OS) {
2809467b48Spatrick OS << "Stack regions:\n";
2909467b48Spatrick for (unsigned i = 0; i < Regions.size(); ++i) {
3009467b48Spatrick OS << " " << i << ": [" << Regions[i].Start << ", " << Regions[i].End
3109467b48Spatrick << "), range " << Regions[i].Range << "\n";
3209467b48Spatrick }
3309467b48Spatrick OS << "Stack objects:\n";
3409467b48Spatrick for (auto &IT : ObjectOffsets) {
3509467b48Spatrick OS << " at " << IT.getSecond() << ": " << *IT.getFirst() << "\n";
3609467b48Spatrick }
3709467b48Spatrick }
3809467b48Spatrick
addObject(const Value * V,unsigned Size,Align Alignment,const StackLifetime::LiveRange & Range)39*d415bd75Srobert void StackLayout::addObject(const Value *V, unsigned Size, Align Alignment,
40097a140dSpatrick const StackLifetime::LiveRange &Range) {
4109467b48Spatrick StackObjects.push_back({V, Size, Alignment, Range});
4209467b48Spatrick ObjectAlignments[V] = Alignment;
4309467b48Spatrick MaxAlignment = std::max(MaxAlignment, Alignment);
4409467b48Spatrick }
4509467b48Spatrick
AdjustStackOffset(unsigned Offset,unsigned Size,Align Alignment)4609467b48Spatrick static unsigned AdjustStackOffset(unsigned Offset, unsigned Size,
47*d415bd75Srobert Align Alignment) {
4809467b48Spatrick return alignTo(Offset + Size, Alignment) - Size;
4909467b48Spatrick }
5009467b48Spatrick
layoutObject(StackObject & Obj)5109467b48Spatrick void StackLayout::layoutObject(StackObject &Obj) {
5209467b48Spatrick if (!ClLayout) {
5309467b48Spatrick // If layout is disabled, just grab the next aligned address.
5409467b48Spatrick // This effectively disables stack coloring as well.
5509467b48Spatrick unsigned LastRegionEnd = Regions.empty() ? 0 : Regions.back().End;
5609467b48Spatrick unsigned Start = AdjustStackOffset(LastRegionEnd, Obj.Size, Obj.Alignment);
5709467b48Spatrick unsigned End = Start + Obj.Size;
5809467b48Spatrick Regions.emplace_back(Start, End, Obj.Range);
5909467b48Spatrick ObjectOffsets[Obj.Handle] = End;
6009467b48Spatrick return;
6109467b48Spatrick }
6209467b48Spatrick
6309467b48Spatrick LLVM_DEBUG(dbgs() << "Layout: size " << Obj.Size << ", align "
64*d415bd75Srobert << Obj.Alignment.value() << ", range " << Obj.Range
65*d415bd75Srobert << "\n");
6609467b48Spatrick assert(Obj.Alignment <= MaxAlignment);
6709467b48Spatrick unsigned Start = AdjustStackOffset(0, Obj.Size, Obj.Alignment);
6809467b48Spatrick unsigned End = Start + Obj.Size;
6909467b48Spatrick LLVM_DEBUG(dbgs() << " First candidate: " << Start << " .. " << End << "\n");
7009467b48Spatrick for (const StackRegion &R : Regions) {
7109467b48Spatrick LLVM_DEBUG(dbgs() << " Examining region: " << R.Start << " .. " << R.End
7209467b48Spatrick << ", range " << R.Range << "\n");
7309467b48Spatrick assert(End >= R.Start);
7409467b48Spatrick if (Start >= R.End) {
7509467b48Spatrick LLVM_DEBUG(dbgs() << " Does not intersect, skip.\n");
7609467b48Spatrick continue;
7709467b48Spatrick }
78097a140dSpatrick if (Obj.Range.overlaps(R.Range)) {
7909467b48Spatrick // Find the next appropriate location.
8009467b48Spatrick Start = AdjustStackOffset(R.End, Obj.Size, Obj.Alignment);
8109467b48Spatrick End = Start + Obj.Size;
8209467b48Spatrick LLVM_DEBUG(dbgs() << " Overlaps. Next candidate: " << Start << " .. "
8309467b48Spatrick << End << "\n");
8409467b48Spatrick continue;
8509467b48Spatrick }
8609467b48Spatrick if (End <= R.End) {
8709467b48Spatrick LLVM_DEBUG(dbgs() << " Reusing region(s).\n");
8809467b48Spatrick break;
8909467b48Spatrick }
9009467b48Spatrick }
9109467b48Spatrick
9209467b48Spatrick unsigned LastRegionEnd = Regions.empty() ? 0 : Regions.back().End;
9309467b48Spatrick if (End > LastRegionEnd) {
9409467b48Spatrick // Insert a new region at the end. Maybe two.
9509467b48Spatrick if (Start > LastRegionEnd) {
9609467b48Spatrick LLVM_DEBUG(dbgs() << " Creating gap region: " << LastRegionEnd << " .. "
9709467b48Spatrick << Start << "\n");
98097a140dSpatrick Regions.emplace_back(LastRegionEnd, Start, StackLifetime::LiveRange(0));
9909467b48Spatrick LastRegionEnd = Start;
10009467b48Spatrick }
10109467b48Spatrick LLVM_DEBUG(dbgs() << " Creating new region: " << LastRegionEnd << " .. "
10209467b48Spatrick << End << ", range " << Obj.Range << "\n");
10309467b48Spatrick Regions.emplace_back(LastRegionEnd, End, Obj.Range);
10409467b48Spatrick LastRegionEnd = End;
10509467b48Spatrick }
10609467b48Spatrick
10709467b48Spatrick // Split starting and ending regions if necessary.
10809467b48Spatrick for (unsigned i = 0; i < Regions.size(); ++i) {
10909467b48Spatrick StackRegion &R = Regions[i];
11009467b48Spatrick if (Start > R.Start && Start < R.End) {
11109467b48Spatrick StackRegion R0 = R;
11209467b48Spatrick R.Start = R0.End = Start;
11309467b48Spatrick Regions.insert(&R, R0);
11409467b48Spatrick continue;
11509467b48Spatrick }
11609467b48Spatrick if (End > R.Start && End < R.End) {
11709467b48Spatrick StackRegion R0 = R;
11809467b48Spatrick R0.End = R.Start = End;
11909467b48Spatrick Regions.insert(&R, R0);
12009467b48Spatrick break;
12109467b48Spatrick }
12209467b48Spatrick }
12309467b48Spatrick
12409467b48Spatrick // Update live ranges for all affected regions.
12509467b48Spatrick for (StackRegion &R : Regions) {
12609467b48Spatrick if (Start < R.End && End > R.Start)
127097a140dSpatrick R.Range.join(Obj.Range);
12809467b48Spatrick if (End <= R.End)
12909467b48Spatrick break;
13009467b48Spatrick }
13109467b48Spatrick
13209467b48Spatrick ObjectOffsets[Obj.Handle] = End;
13309467b48Spatrick }
13409467b48Spatrick
computeLayout()13509467b48Spatrick void StackLayout::computeLayout() {
13609467b48Spatrick // Simple greedy algorithm.
13709467b48Spatrick // If this is replaced with something smarter, it must preserve the property
13809467b48Spatrick // that the first object is always at the offset 0 in the stack frame (for
13909467b48Spatrick // StackProtectorSlot), or handle stack protector in some other way.
14009467b48Spatrick
14109467b48Spatrick // Sort objects by size (largest first) to reduce fragmentation.
14209467b48Spatrick if (StackObjects.size() > 2)
14373471bf0Spatrick llvm::stable_sort(drop_begin(StackObjects),
14409467b48Spatrick [](const StackObject &a, const StackObject &b) {
14509467b48Spatrick return a.Size > b.Size;
14609467b48Spatrick });
14709467b48Spatrick
14809467b48Spatrick for (auto &Obj : StackObjects)
14909467b48Spatrick layoutObject(Obj);
15009467b48Spatrick
15109467b48Spatrick LLVM_DEBUG(print(dbgs()));
15209467b48Spatrick }
153