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 {