brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.2 KiB · 74fa30a Raw
183 lines · cpp
1//===- OcamlGCPrinter.cpp - Ocaml frametable emitter ----------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements printing the assembly code for an Ocaml frametable.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/ADT/STLExtras.h"14#include "llvm/ADT/SmallString.h"15#include "llvm/ADT/Twine.h"16#include "llvm/CodeGen/AsmPrinter.h"17#include "llvm/CodeGen/GCMetadata.h"18#include "llvm/CodeGen/GCMetadataPrinter.h"19#include "llvm/IR/BuiltinGCs.h"20#include "llvm/IR/DataLayout.h"21#include "llvm/IR/Function.h"22#include "llvm/IR/Mangler.h"23#include "llvm/IR/Module.h"24#include "llvm/MC/MCContext.h"25#include "llvm/MC/MCDirectives.h"26#include "llvm/MC/MCStreamer.h"27#include "llvm/Support/ErrorHandling.h"28#include "llvm/Target/TargetLoweringObjectFile.h"29#include <cctype>30#include <cstddef>31#include <cstdint>32#include <string>33 34using namespace llvm;35 36namespace {37 38class OcamlGCMetadataPrinter : public GCMetadataPrinter {39public:40  void beginAssembly(Module &M, GCModuleInfo &Info, AsmPrinter &AP) override;41  void finishAssembly(Module &M, GCModuleInfo &Info, AsmPrinter &AP) override;42};43 44} // end anonymous namespace45 46static GCMetadataPrinterRegistry::Add<OcamlGCMetadataPrinter>47    Y("ocaml", "ocaml 3.10-compatible collector");48 49void llvm::linkOcamlGCPrinter() {}50 51static void EmitCamlGlobal(const Module &M, AsmPrinter &AP, const char *Id) {52  const std::string &MId = M.getModuleIdentifier();53 54  std::string SymName;55  SymName += "caml";56  size_t Letter = SymName.size();57  SymName.append(MId.begin(), llvm::find(MId, '.'));58  SymName += "__";59  SymName += Id;60 61  // Capitalize the first letter of the module name.62  SymName[Letter] = toupper(SymName[Letter]);63 64  SmallString<128> TmpStr;65  Mangler::getNameWithPrefix(TmpStr, SymName, M.getDataLayout());66 67  MCSymbol *Sym = AP.OutContext.getOrCreateSymbol(TmpStr);68 69  AP.OutStreamer->emitSymbolAttribute(Sym, MCSA_Global);70  AP.OutStreamer->emitLabel(Sym);71}72 73void OcamlGCMetadataPrinter::beginAssembly(Module &M, GCModuleInfo &Info,74                                           AsmPrinter &AP) {75  AP.OutStreamer->switchSection(AP.getObjFileLowering().getTextSection());76  EmitCamlGlobal(M, AP, "code_begin");77 78  AP.OutStreamer->switchSection(AP.getObjFileLowering().getDataSection());79  EmitCamlGlobal(M, AP, "data_begin");80}81 82/// emitAssembly - Print the frametable. The ocaml frametable format is thus:83///84///   extern "C" struct align(sizeof(intptr_t)) {85///     uint16_t NumDescriptors;86///     struct align(sizeof(intptr_t)) {87///       void *ReturnAddress;88///       uint16_t FrameSize;89///       uint16_t NumLiveOffsets;90///       uint16_t LiveOffsets[NumLiveOffsets];91///     } Descriptors[NumDescriptors];92///   } caml${module}__frametable;93///94/// Note that this precludes programs from stack frames larger than 64K95/// (FrameSize and LiveOffsets would overflow). FrameTablePrinter will abort if96/// either condition is detected in a function which uses the GC.97///98void OcamlGCMetadataPrinter::finishAssembly(Module &M, GCModuleInfo &Info,99                                            AsmPrinter &AP) {100  unsigned IntPtrSize = M.getDataLayout().getPointerSize();101 102  AP.OutStreamer->switchSection(AP.getObjFileLowering().getTextSection());103  EmitCamlGlobal(M, AP, "code_end");104 105  AP.OutStreamer->switchSection(AP.getObjFileLowering().getDataSection());106  EmitCamlGlobal(M, AP, "data_end");107 108  // FIXME: Why does ocaml emit this??109  AP.OutStreamer->emitIntValue(0, IntPtrSize);110 111  AP.OutStreamer->switchSection(AP.getObjFileLowering().getDataSection());112  EmitCamlGlobal(M, AP, "frametable");113 114  int NumDescriptors = 0;115  for (std::unique_ptr<GCFunctionInfo> &FI :116       llvm::make_range(Info.funcinfo_begin(), Info.funcinfo_end())) {117    if (FI->getStrategy().getName() != getStrategy().getName())118      // this function is managed by some other GC119      continue;120    NumDescriptors += FI->size();121  }122 123  if (NumDescriptors >= 1 << 16) {124    // Very rude!125    report_fatal_error(" Too much descriptor for ocaml GC");126  }127  AP.emitInt16(NumDescriptors);128  AP.emitAlignment(IntPtrSize == 4 ? Align(4) : Align(8));129 130  for (std::unique_ptr<GCFunctionInfo> &FI :131       llvm::make_range(Info.funcinfo_begin(), Info.funcinfo_end())) {132    if (FI->getStrategy().getName() != getStrategy().getName())133      // this function is managed by some other GC134      continue;135 136    uint64_t FrameSize = FI->getFrameSize();137    if (FrameSize >= 1 << 16) {138      // Very rude!139      report_fatal_error("Function '" + FI->getFunction().getName() +140                         "' is too large for the ocaml GC! "141                         "Frame size " +142                         Twine(FrameSize) +143                         ">= 65536.\n"144                         "(" +145                         Twine(reinterpret_cast<uintptr_t>(FI.get())) + ")");146    }147 148    AP.OutStreamer->AddComment("live roots for " +149                               Twine(FI->getFunction().getName()));150    AP.OutStreamer->addBlankLine();151 152    for (GCFunctionInfo::iterator J = FI->begin(), JE = FI->end(); J != JE;153         ++J) {154      size_t LiveCount = FI->live_size(J);155      if (LiveCount >= 1 << 16) {156        // Very rude!157        report_fatal_error("Function '" + FI->getFunction().getName() +158                           "' is too large for the ocaml GC! "159                           "Live root count " +160                           Twine(LiveCount) + " >= 65536.");161      }162 163      AP.OutStreamer->emitSymbolValue(J->Label, IntPtrSize);164      AP.emitInt16(FrameSize);165      AP.emitInt16(LiveCount);166 167      for (GCFunctionInfo::live_iterator K = FI->live_begin(J),168                                         KE = FI->live_end(J);169           K != KE; ++K) {170        if (K->StackOffset >= 1 << 16) {171          // Very rude!172          report_fatal_error(173              "GC root stack offset is outside of fixed stack frame and out "174              "of range for ocaml GC!");175        }176        AP.emitInt16(K->StackOffset);177      }178 179      AP.emitAlignment(IntPtrSize == 4 ? Align(4) : Align(8));180    }181  }182}183