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 | |
34 | using namespace llvm; |
35 | |
36 | namespace { |
37 | |
38 | class OcamlGCMetadataPrinter : public GCMetadataPrinter { |
39 | public: |
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 | |
46 | static GCMetadataPrinterRegistry::Add<OcamlGCMetadataPrinter> |
47 | Y("ocaml" , "ocaml 3.10-compatible collector" ); |
48 | |
49 | void llvm::linkOcamlGCPrinter() {} |
50 | |
51 | static 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(first: MId.begin(), last: llvm::find(Range: MId, Val: '.')); |
58 | SymName += "__" ; |
59 | SymName += Id; |
60 | |
61 | // Capitalize the first letter of the module name. |
62 | SymName[Letter] = toupper(c: SymName[Letter]); |
63 | |
64 | SmallString<128> TmpStr; |
65 | Mangler::getNameWithPrefix(OutName&: TmpStr, GVName: SymName, DL: M.getDataLayout()); |
66 | |
67 | MCSymbol *Sym = AP.OutContext.getOrCreateSymbol(Name: TmpStr); |
68 | |
69 | AP.OutStreamer->emitSymbolAttribute(Symbol: Sym, Attribute: MCSA_Global); |
70 | AP.OutStreamer->emitLabel(Symbol: Sym); |
71 | } |
72 | |
73 | void OcamlGCMetadataPrinter::beginAssembly(Module &M, GCModuleInfo &Info, |
74 | AsmPrinter &AP) { |
75 | AP.OutStreamer->switchSection(Section: AP.getObjFileLowering().getTextSection()); |
76 | EmitCamlGlobal(M, AP, Id: "code_begin" ); |
77 | |
78 | AP.OutStreamer->switchSection(Section: AP.getObjFileLowering().getDataSection()); |
79 | EmitCamlGlobal(M, AP, Id: "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 | /// |
98 | void OcamlGCMetadataPrinter::finishAssembly(Module &M, GCModuleInfo &Info, |
99 | AsmPrinter &AP) { |
100 | unsigned IntPtrSize = M.getDataLayout().getPointerSize(); |
101 | |
102 | AP.OutStreamer->switchSection(Section: AP.getObjFileLowering().getTextSection()); |
103 | EmitCamlGlobal(M, AP, Id: "code_end" ); |
104 | |
105 | AP.OutStreamer->switchSection(Section: AP.getObjFileLowering().getDataSection()); |
106 | EmitCamlGlobal(M, AP, Id: "data_end" ); |
107 | |
108 | // FIXME: Why does ocaml emit this?? |
109 | AP.OutStreamer->emitIntValue(Value: 0, Size: IntPtrSize); |
110 | |
111 | AP.OutStreamer->switchSection(Section: AP.getObjFileLowering().getDataSection()); |
112 | EmitCamlGlobal(M, AP, Id: "frametable" ); |
113 | |
114 | int NumDescriptors = 0; |
115 | for (std::unique_ptr<GCFunctionInfo> &FI : |
116 | llvm::make_range(x: Info.funcinfo_begin(), y: Info.funcinfo_end())) { |
117 | if (FI->getStrategy().getName() != getStrategy().getName()) |
118 | // this function is managed by some other GC |
119 | continue; |
120 | NumDescriptors += FI->size(); |
121 | } |
122 | |
123 | if (NumDescriptors >= 1 << 16) { |
124 | // Very rude! |
125 | report_fatal_error(reason: " Too much descriptor for ocaml GC" ); |
126 | } |
127 | AP.emitInt16(Value: NumDescriptors); |
128 | AP.emitAlignment(Alignment: IntPtrSize == 4 ? Align(4) : Align(8)); |
129 | |
130 | for (std::unique_ptr<GCFunctionInfo> &FI : |
131 | llvm::make_range(x: Info.funcinfo_begin(), y: Info.funcinfo_end())) { |
132 | if (FI->getStrategy().getName() != getStrategy().getName()) |
133 | // this function is managed by some other GC |
134 | continue; |
135 | |
136 | uint64_t FrameSize = FI->getFrameSize(); |
137 | if (FrameSize >= 1 << 16) { |
138 | // Very rude! |
139 | report_fatal_error(reason: "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(T: "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(p: J); |
155 | if (LiveCount >= 1 << 16) { |
156 | // Very rude! |
157 | report_fatal_error(reason: "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(Sym: J->Label, Size: IntPtrSize); |
164 | AP.emitInt16(Value: FrameSize); |
165 | AP.emitInt16(Value: LiveCount); |
166 | |
167 | for (GCFunctionInfo::live_iterator K = FI->live_begin(p: J), |
168 | KE = FI->live_end(p: J); |
169 | K != KE; ++K) { |
170 | if (K->StackOffset >= 1 << 16) { |
171 | // Very rude! |
172 | report_fatal_error( |
173 | reason: "GC root stack offset is outside of fixed stack frame and out " |
174 | "of range for ocaml GC!" ); |
175 | } |
176 | AP.emitInt16(Value: K->StackOffset); |
177 | } |
178 | |
179 | AP.emitAlignment(Alignment: IntPtrSize == 4 ? Align(4) : Align(8)); |
180 | } |
181 | } |
182 | } |
183 | |