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