Path: blob/main/contrib/llvm-project/llvm/lib/CodeGen/AsmPrinter/OcamlGCPrinter.cpp
35271 views
//===- OcamlGCPrinter.cpp - Ocaml frametable emitter ----------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements printing the assembly code for an Ocaml frametable.9//10//===----------------------------------------------------------------------===//1112#include "llvm/ADT/STLExtras.h"13#include "llvm/ADT/SmallString.h"14#include "llvm/ADT/Twine.h"15#include "llvm/CodeGen/AsmPrinter.h"16#include "llvm/CodeGen/GCMetadata.h"17#include "llvm/CodeGen/GCMetadataPrinter.h"18#include "llvm/IR/BuiltinGCs.h"19#include "llvm/IR/DataLayout.h"20#include "llvm/IR/Function.h"21#include "llvm/IR/Mangler.h"22#include "llvm/IR/Module.h"23#include "llvm/MC/MCContext.h"24#include "llvm/MC/MCDirectives.h"25#include "llvm/MC/MCStreamer.h"26#include "llvm/Support/ErrorHandling.h"27#include "llvm/Target/TargetLoweringObjectFile.h"28#include <cctype>29#include <cstddef>30#include <cstdint>31#include <string>3233using namespace llvm;3435namespace {3637class OcamlGCMetadataPrinter : public GCMetadataPrinter {38public:39void beginAssembly(Module &M, GCModuleInfo &Info, AsmPrinter &AP) override;40void finishAssembly(Module &M, GCModuleInfo &Info, AsmPrinter &AP) override;41};4243} // end anonymous namespace4445static GCMetadataPrinterRegistry::Add<OcamlGCMetadataPrinter>46Y("ocaml", "ocaml 3.10-compatible collector");4748void llvm::linkOcamlGCPrinter() {}4950static void EmitCamlGlobal(const Module &M, AsmPrinter &AP, const char *Id) {51const std::string &MId = M.getModuleIdentifier();5253std::string SymName;54SymName += "caml";55size_t Letter = SymName.size();56SymName.append(MId.begin(), llvm::find(MId, '.'));57SymName += "__";58SymName += Id;5960// Capitalize the first letter of the module name.61SymName[Letter] = toupper(SymName[Letter]);6263SmallString<128> TmpStr;64Mangler::getNameWithPrefix(TmpStr, SymName, M.getDataLayout());6566MCSymbol *Sym = AP.OutContext.getOrCreateSymbol(TmpStr);6768AP.OutStreamer->emitSymbolAttribute(Sym, MCSA_Global);69AP.OutStreamer->emitLabel(Sym);70}7172void OcamlGCMetadataPrinter::beginAssembly(Module &M, GCModuleInfo &Info,73AsmPrinter &AP) {74AP.OutStreamer->switchSection(AP.getObjFileLowering().getTextSection());75EmitCamlGlobal(M, AP, "code_begin");7677AP.OutStreamer->switchSection(AP.getObjFileLowering().getDataSection());78EmitCamlGlobal(M, AP, "data_begin");79}8081/// emitAssembly - Print the frametable. The ocaml frametable format is thus:82///83/// extern "C" struct align(sizeof(intptr_t)) {84/// uint16_t NumDescriptors;85/// struct align(sizeof(intptr_t)) {86/// void *ReturnAddress;87/// uint16_t FrameSize;88/// uint16_t NumLiveOffsets;89/// uint16_t LiveOffsets[NumLiveOffsets];90/// } Descriptors[NumDescriptors];91/// } caml${module}__frametable;92///93/// Note that this precludes programs from stack frames larger than 64K94/// (FrameSize and LiveOffsets would overflow). FrameTablePrinter will abort if95/// either condition is detected in a function which uses the GC.96///97void OcamlGCMetadataPrinter::finishAssembly(Module &M, GCModuleInfo &Info,98AsmPrinter &AP) {99unsigned IntPtrSize = M.getDataLayout().getPointerSize();100101AP.OutStreamer->switchSection(AP.getObjFileLowering().getTextSection());102EmitCamlGlobal(M, AP, "code_end");103104AP.OutStreamer->switchSection(AP.getObjFileLowering().getDataSection());105EmitCamlGlobal(M, AP, "data_end");106107// FIXME: Why does ocaml emit this??108AP.OutStreamer->emitIntValue(0, IntPtrSize);109110AP.OutStreamer->switchSection(AP.getObjFileLowering().getDataSection());111EmitCamlGlobal(M, AP, "frametable");112113int NumDescriptors = 0;114for (std::unique_ptr<GCFunctionInfo> &FI :115llvm::make_range(Info.funcinfo_begin(), Info.funcinfo_end())) {116if (FI->getStrategy().getName() != getStrategy().getName())117// this function is managed by some other GC118continue;119NumDescriptors += FI->size();120}121122if (NumDescriptors >= 1 << 16) {123// Very rude!124report_fatal_error(" Too much descriptor for ocaml GC");125}126AP.emitInt16(NumDescriptors);127AP.emitAlignment(IntPtrSize == 4 ? Align(4) : Align(8));128129for (std::unique_ptr<GCFunctionInfo> &FI :130llvm::make_range(Info.funcinfo_begin(), Info.funcinfo_end())) {131if (FI->getStrategy().getName() != getStrategy().getName())132// this function is managed by some other GC133continue;134135uint64_t FrameSize = FI->getFrameSize();136if (FrameSize >= 1 << 16) {137// Very rude!138report_fatal_error("Function '" + FI->getFunction().getName() +139"' is too large for the ocaml GC! "140"Frame size " +141Twine(FrameSize) +142">= 65536.\n"143"(" +144Twine(reinterpret_cast<uintptr_t>(FI.get())) + ")");145}146147AP.OutStreamer->AddComment("live roots for " +148Twine(FI->getFunction().getName()));149AP.OutStreamer->addBlankLine();150151for (GCFunctionInfo::iterator J = FI->begin(), JE = FI->end(); J != JE;152++J) {153size_t LiveCount = FI->live_size(J);154if (LiveCount >= 1 << 16) {155// Very rude!156report_fatal_error("Function '" + FI->getFunction().getName() +157"' is too large for the ocaml GC! "158"Live root count " +159Twine(LiveCount) + " >= 65536.");160}161162AP.OutStreamer->emitSymbolValue(J->Label, IntPtrSize);163AP.emitInt16(FrameSize);164AP.emitInt16(LiveCount);165166for (GCFunctionInfo::live_iterator K = FI->live_begin(J),167KE = FI->live_end(J);168K != KE; ++K) {169if (K->StackOffset >= 1 << 16) {170// Very rude!171report_fatal_error(172"GC root stack offset is outside of fixed stack frame and out "173"of range for ocaml GC!");174}175AP.emitInt16(K->StackOffset);176}177178AP.emitAlignment(IntPtrSize == 4 ? Align(4) : Align(8));179}180}181}182183184