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-exception
6//
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 namespace
45
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 64K
95/// (FrameSize and LiveOffsets would overflow). FrameTablePrinter will abort if
96/// 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 (GCModuleInfo::FuncInfoVec::iterator I = Info.funcinfo_begin(),
116                                           IE = Info.funcinfo_end();
117       I != IE; ++I) {
118    GCFunctionInfo &FI = **I;
119    if (FI.getStrategy().getName() != getStrategy().getName())
120      // this function is managed by some other GC
121      continue;
122    for (GCFunctionInfo::iterator J = FI.begin(), JE = FI.end(); J != JE; ++J) {
123      NumDescriptors++;
124    }
125  }
126
127  if (NumDescriptors >= 1 << 16) {
128    // Very rude!
129    report_fatal_error(" Too much descriptor for ocaml GC");
130  }
131  AP.emitInt16(NumDescriptors);
132  AP.emitAlignment(IntPtrSize == 4 ? Align(4) : Align(8));
133
134  for (GCModuleInfo::FuncInfoVec::iterator I = Info.funcinfo_begin(),
135                                           IE = Info.funcinfo_end();
136       I != IE; ++I) {
137    GCFunctionInfo &FI = **I;
138    if (FI.getStrategy().getName() != getStrategy().getName())
139      // this function is managed by some other GC
140      continue;
141
142    uint64_t FrameSize = FI.getFrameSize();
143    if (FrameSize >= 1 << 16) {
144      // Very rude!
145      report_fatal_error("Function '" + FI.getFunction().getName() +
146                         "' is too large for the ocaml GC! "
147                         "Frame size " +
148                         Twine(FrameSize) +
149                         ">= 65536.\n"
150                         "(" +
151                         Twine(reinterpret_cast<uintptr_t>(&FI)) + ")");
152    }
153
154    AP.OutStreamer->AddComment("live roots for " +
155                               Twine(FI.getFunction().getName()));
156    AP.OutStreamer->AddBlankLine();
157
158    for (GCFunctionInfo::iterator J = FI.begin(), JE = FI.end(); J != JE; ++J) {
159      size_t LiveCount = FI.live_size(J);
160      if (LiveCount >= 1 << 16) {
161        // Very rude!
162        report_fatal_error("Function '" + FI.getFunction().getName() +
163                           "' is too large for the ocaml GC! "
164                           "Live root count " +
165                           Twine(LiveCount) + " >= 65536.");
166      }
167
168      AP.OutStreamer->emitSymbolValue(J->Label, IntPtrSize);
169      AP.emitInt16(FrameSize);
170      AP.emitInt16(LiveCount);
171
172      for (GCFunctionInfo::live_iterator K = FI.live_begin(J),
173                                         KE = FI.live_end(J);
174           K != KE; ++K) {
175        if (K->StackOffset >= 1 << 16) {
176          // Very rude!
177          report_fatal_error(
178              "GC root stack offset is outside of fixed stack frame and out "
179              "of range for ocaml GC!");
180        }
181        AP.emitInt16(K->StackOffset);
182      }
183
184      AP.emitAlignment(IntPtrSize == 4 ? Align(4) : Align(8));
185    }
186  }
187}
188