OcamlGCPrinter.cpp revision b8f832d331b5206b235262e4395b969d68cf439c
1//===-- OcamlGCPrinter.cpp - Ocaml frametable emitter ---------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements printing the assembly code for an Ocaml frametable.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/CodeGen/GCs.h"
15#include "llvm/CodeGen/AsmPrinter.h"
16#include "llvm/CodeGen/GCMetadataPrinter.h"
17#include "llvm/Module.h"
18#include "llvm/MC/MCAsmInfo.h"
19#include "llvm/MC/MCContext.h"
20#include "llvm/MC/MCSymbol.h"
21#include "llvm/MC/MCStreamer.h"
22#include "llvm/Target/Mangler.h"
23#include "llvm/Target/TargetData.h"
24#include "llvm/Target/TargetLoweringObjectFile.h"
25#include "llvm/Target/TargetMachine.h"
26#include "llvm/ADT/SmallString.h"
27#include "llvm/Support/ErrorHandling.h"
28#include "llvm/Support/FormattedStream.h"
29using namespace llvm;
30
31namespace {
32
33  class OcamlGCMetadataPrinter : public GCMetadataPrinter {
34  public:
35    void beginAssembly(AsmPrinter &AP);
36    void finishAssembly(AsmPrinter &AP);
37  };
38
39}
40
41static GCMetadataPrinterRegistry::Add<OcamlGCMetadataPrinter>
42Y("ocaml", "ocaml 3.10-compatible collector");
43
44void llvm::linkOcamlGCPrinter() { }
45
46static void EmitCamlGlobal(const Module &M, AsmPrinter &AP, const char *Id) {
47  const std::string &MId = M.getModuleIdentifier();
48
49  std::string SymName;
50  SymName += "caml";
51  size_t Letter = SymName.size();
52  SymName.append(MId.begin(), std::find(MId.begin(), MId.end(), '.'));
53  SymName += "__";
54  SymName += Id;
55
56  // Capitalize the first letter of the module name.
57  SymName[Letter] = toupper(SymName[Letter]);
58
59  SmallString<128> TmpStr;
60  AP.Mang->getNameWithPrefix(TmpStr, SymName);
61
62  MCSymbol *Sym = AP.OutContext.GetOrCreateSymbol(TmpStr);
63
64  AP.OutStreamer.EmitSymbolAttribute(Sym, MCSA_Global);
65  AP.OutStreamer.EmitLabel(Sym);
66}
67
68void OcamlGCMetadataPrinter::beginAssembly(AsmPrinter &AP) {
69  AP.OutStreamer.SwitchSection(AP.getObjFileLowering().getTextSection());
70  EmitCamlGlobal(getModule(), AP, "code_begin");
71
72  AP.OutStreamer.SwitchSection(AP.getObjFileLowering().getDataSection());
73  EmitCamlGlobal(getModule(), AP, "data_begin");
74}
75
76/// emitAssembly - Print the frametable. The ocaml frametable format is thus:
77///
78///   extern "C" struct align(sizeof(intptr_t)) {
79///     uint16_t NumDescriptors;
80///     struct align(sizeof(intptr_t)) {
81///       void *ReturnAddress;
82///       uint16_t FrameSize;
83///       uint16_t NumLiveOffsets;
84///       uint16_t LiveOffsets[NumLiveOffsets];
85///     } Descriptors[NumDescriptors];
86///   } caml${module}__frametable;
87///
88/// Note that this precludes programs from stack frames larger than 64K
89/// (FrameSize and LiveOffsets would overflow). FrameTablePrinter will abort if
90/// either condition is detected in a function which uses the GC.
91///
92void OcamlGCMetadataPrinter::finishAssembly(AsmPrinter &AP) {
93  unsigned IntPtrSize = AP.TM.getTargetData()->getPointerSize();
94
95  AP.OutStreamer.SwitchSection(AP.getObjFileLowering().getTextSection());
96  EmitCamlGlobal(getModule(), AP, "code_end");
97
98  AP.OutStreamer.SwitchSection(AP.getObjFileLowering().getDataSection());
99  EmitCamlGlobal(getModule(), AP, "data_end");
100
101  // FIXME: Why does ocaml emit this??
102  AP.OutStreamer.EmitIntValue(0, IntPtrSize, 0);
103
104  AP.OutStreamer.SwitchSection(AP.getObjFileLowering().getDataSection());
105  EmitCamlGlobal(getModule(), AP, "frametable");
106
107  for (iterator I = begin(), IE = end(); I != IE; ++I) {
108    GCFunctionInfo &FI = **I;
109
110    uint64_t FrameSize = FI.getFrameSize();
111    if (FrameSize >= 1<<16) {
112      // Very rude!
113      report_fatal_error("Function '" + FI.getFunction().getName() +
114                         "' is too large for the ocaml GC! "
115                         "Frame size " + Twine(FrameSize) + ">= 65536.\n"
116                         "(" + Twine(uintptr_t(&FI)) + ")");
117    }
118
119    AP.OutStreamer.AddComment("live roots for " +
120                              Twine(FI.getFunction().getName()));
121    AP.OutStreamer.AddBlankLine();
122
123    for (GCFunctionInfo::iterator J = FI.begin(), JE = FI.end(); J != JE; ++J) {
124      size_t LiveCount = FI.live_size(J);
125      if (LiveCount >= 1<<16) {
126        // Very rude!
127        report_fatal_error("Function '" + FI.getFunction().getName() +
128                           "' is too large for the ocaml GC! "
129                           "Live root count "+Twine(LiveCount)+" >= 65536.");
130      }
131
132      AP.OutStreamer.EmitSymbolValue(J->Label, IntPtrSize, 0);
133      AP.EmitInt16(FrameSize);
134      AP.EmitInt16(LiveCount);
135
136      for (GCFunctionInfo::live_iterator K = FI.live_begin(J),
137                                         KE = FI.live_end(J); K != KE; ++K) {
138        assert(K->StackOffset < 1<<16 &&
139               "GC root stack offset is outside of fixed stack frame and out "
140               "of range for ocaml GC!");
141
142        AP.EmitInt32(K->StackOffset);
143      }
144
145      AP.EmitAlignment(IntPtrSize == 4 ? 2 : 3);
146    }
147  }
148}
149