passes: add Go GC strategy and stack map printer Change-Id: Iaddfbd5760af515470c069860eb0bf4dc8c38060 Reviewed-on: https://go-review.googlesource.com/c/137762 Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/passes/CMakeLists.txt b/passes/CMakeLists.txt index 4c5a3c0..2919b57 100644 --- a/passes/CMakeLists.txt +++ b/passes/CMakeLists.txt
@@ -7,6 +7,7 @@ # gollvm specific backend passes. add_llvm_library(LLVMCppGoPasses + GC.cpp GoAnnotation.cpp GoStatepoints.cpp Util.cpp
diff --git a/passes/GC.cpp b/passes/GC.cpp new file mode 100644 index 0000000..b877778 --- /dev/null +++ b/passes/GC.cpp
@@ -0,0 +1,177 @@ +//===--- GC.cpp -----------------------------------------------------------===// +// +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +//===----------------------------------------------------------------------===// +// +// GC strategy for Go runtime. +// +//===----------------------------------------------------------------------===// + +#include "GoStackMap.h" +#include "GollvmPasses.h" + +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/Twine.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/GCMetadataPrinter.h" +#include "llvm/CodeGen/GCStrategy.h" +#include "llvm/CodeGen/StackMaps.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/Casting.h" +#include <vector> + +using namespace llvm; + +namespace { + +class GoGC : public GCStrategy { +public: + GoGC() { + UseStatepoints = true; + UsesMetadata = true; // using GCMetadataPrinter to emit stack maps + + // TODO: write barrier? + } + + Optional<bool> isGCManagedPointer(const Type *Ty) const override { + return isa<PointerType>(Ty); + } +}; + +GCRegistry::Add<GoGC> X("go", "Go garbage collector."); + +class GoGCPrinter : public GCMetadataPrinter { +public: + bool emitStackMaps(StackMaps &SM, AsmPrinter &AP) override; +}; + +GCMetadataPrinterRegistry::Add<GoGCPrinter> + X2("go", "Go garbage collector."); + +} // namespace + +// Returns the bitmap (in a vector of bytes) encoding live +// locations. +static std::vector<uint8_t> +computeBitVector(uint64_t StackSize, + const StackMaps::LocationVec &CSLocs, + uint32_t &Size) { + // TODO: this function is silly -- BitVector internally has + // a bitmap storage, but it is private. We basically recompute + // it. Can we do better? + + const int PtrSize = 8; // TODO: get from target info + BitVector BV(StackSize / PtrSize); + for (unsigned i = 0, n = CSLocs.size(); i < n; i++) { + const auto &Loc = CSLocs[i]; + switch (Loc.Type) { + case StackMaps::Location::Direct: + case StackMaps::Location::Indirect: { + // TODO: verify that the base register is SP. + + unsigned Idx = Loc.Offset / PtrSize; + + // If a slot is following a "Constant" location, it is an aggregate type + // and that constant encodes the pointer bitmap. + if (i+1 < n && CSLocs[i+1].Type == StackMaps::Location::Constant) { + // Make sure it is a local/arg slot, not a spill of in-register value. + assert(Loc.Type == StackMaps::Location::Direct); + + i++; + for (; i < n && CSLocs[i].Type == StackMaps::Location::Constant; i++) { + if (Idx + 32 >= BV.size()) + BV.resize(Idx + 32); + uint32_t Bits = CSLocs[i].Offset; + for (unsigned j = 0; j < 32; j++) + if ((Bits>>j)&1) + BV.set(Idx + j); + Idx += 32; + } + i--; // compensate the increment in outer loop + break; + } + + // A single pointer slot. + if (Idx >= BV.size()) + // This can happen if Loc is an arg slot (for byVal aggregate type). + BV.resize(Idx + 1); + BV.set(Idx); + break; + } + default: + break; + } + } + + Size = 0; + std::vector<uint8_t> Bytes; + if (BV.none()) + return Bytes; + int last = BV.find_last(); + Size = last + 1; + Bytes.reserve(last/8 + 1); + int i; + for (i = 0; i <= last; i += 8) { + uint8_t b = 0; + for (int j = 0; j < 8 && i+j <= last; j++) + b |= BV[i+j] ? 1<<j : 0; + Bytes.push_back(b); + } + Bytes.resize(last/8 + 1); + return Bytes; +} + +static void +emitCallsiteEntries(StackMaps &SM, MCStreamer &OS) { + auto &CSInfos = SM.getCSInfos(); + if (CSInfos.empty()) + return; + + MCContext &OutContext = OS.getContext(); + auto CSI = CSInfos.begin(); + for (auto const &FR : SM.getFnInfos()) { + for (unsigned i = 0; i < FR.second.RecordCount; i++, CSI++) { + Twine Name = Twine(GO_STACKMAP_SYM_PREFIX) + Twine((*CSI).ID); + MCSymbol *Sym = OutContext.getOrCreateSymbol(Name); + if (Sym->isDefined()) + // We may have emitted one, due to tail duplication. + continue; + OS.EmitLabel(Sym); + + // Stack map entry: + // uint32_t nbits; + // uint8_t *data; + uint32_t Size; + std::vector<uint8_t> V = + computeBitVector(FR.second.StackSize, (*CSI).Locations, Size); + OS.EmitIntValue(Size, 4); + for (uint8_t Byte : V) + OS.EmitIntValue(Byte, 1); + } + OS.EmitValueToAlignment(8); + } +} + +bool +GoGCPrinter::emitStackMaps(StackMaps &SM, AsmPrinter &AP) { + MCContext &OutContext = AP.OutStreamer->getContext(); + MCStreamer &OS = *AP.OutStreamer; + + // Create the section. + MCSection *StackMapSection = + OutContext.getObjectFileInfo()->getStackMapSection(); + OS.SwitchSection(StackMapSection); + + emitCallsiteEntries(SM, OS); + + return true; +} + +void llvm::linkGoGC() {} +void llvm::linkGoGCPrinter() {}
diff --git a/passes/GollvmPasses.h b/passes/GollvmPasses.h index 61c141e..2691c67 100644 --- a/passes/GollvmPasses.h +++ b/passes/GollvmPasses.h
@@ -26,6 +26,9 @@ FunctionPass *createGoAnnotationPass(); ModulePass *createGoStatepointsLegacyPass(); +void linkGoGC(); +void linkGoGCPrinter(); + } // namespace llvm namespace gollvm {