summaryrefslogtreecommitdiff
path: root/lib/CodeGen/OcamlCollector.cpp
blob: 6b947b10507f8b912be32bcd64804c3ce9654d37 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//===-- OcamlCollector.cpp - Ocaml frametable emitter ---------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements lowering for the llvm.gc* intrinsics compatible with
// Objective Caml 3.10.0, which uses a liveness-accurate static stack map.
//
//===----------------------------------------------------------------------===//
                        
#include "llvm/CodeGen/Collectors.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/Collector.h"
#include "llvm/Module.h"
#include "llvm/Target/TargetAsmInfo.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Target/TargetMachine.h"

using namespace llvm;

namespace {

  class VISIBILITY_HIDDEN OcamlCollector : public Collector {
  public:
    OcamlCollector();
    
    void beginAssembly(std::ostream &OS, AsmPrinter &AP,
                       const TargetAsmInfo &TAI);
    
    void finishAssembly(std::ostream &OS, AsmPrinter &AP,
                        const TargetAsmInfo &TAI);
  };
  
}

static CollectorRegistry::Add<OcamlCollector>
X("ocaml", "ocaml 3.10-compatible collector");

// -----------------------------------------------------------------------------

static void EmitCamlGlobal(const Module &M, std::ostream &OS, AsmPrinter &AP,
                           const TargetAsmInfo &TAI, const char *Id) {
  const std::string &MId = M.getModuleIdentifier();
  
  std::string Mangled;
  Mangled += TAI.getGlobalPrefix();
  Mangled += "caml";
  size_t Letter = Mangled.size();
  Mangled.append(MId.begin(), std::find(MId.begin(), MId.end(), '.'));
  Mangled += "__";
  Mangled += Id;
  
  // Capitalize the first letter of the module name.
  Mangled[Letter] = toupper(Mangled[Letter]);
  
  if (const char *GlobalDirective = TAI.getGlobalDirective())
    OS << GlobalDirective << Mangled << "\n";
  OS << Mangled << ":\n";
}

Collector *llvm::createOcamlCollector() {
  return new OcamlCollector();
}

OcamlCollector::OcamlCollector() {
  NeededSafePoints = 1 << GC::PostCall;
}

void OcamlCollector::beginAssembly(std::ostream &OS, AsmPrinter &AP,
                                   const TargetAsmInfo &TAI) {
  AP.SwitchToTextSection(TAI.getTextSection());
  EmitCamlGlobal(getModule(), OS, AP, TAI, "code_begin");
  
  AP.SwitchToDataSection(TAI.getDataSection());
  EmitCamlGlobal(getModule(), OS, AP, TAI, "data_begin");
}

/// emitAssembly - Print the frametable. The ocaml frametable format is thus:
/// 
///   extern "C" struct align(sizeof(intptr_t)) {
///     uint16_t NumDescriptors;
///     struct align(sizeof(intptr_t)) {
///       void *ReturnAddress;
///       uint16_t FrameSize;
///       uint16_t NumLiveOffsets;
///       uint16_t LiveOffsets[NumLiveOffsets];
///     } Descriptors[NumDescriptors];
///   } caml${module}__frametable;
/// 
/// Note that this precludes programs from stack frames larger than 64K
/// (FrameSize and LiveOffsets would overflow). FrameTablePrinter will abort if
/// either condition is detected in a function which uses the collector.
/// 
void OcamlCollector::finishAssembly(std::ostream &OS, AsmPrinter &AP,
                                    const TargetAsmInfo &TAI) {
  const char *AddressDirective;
  int AddressAlignLog;
  if (AP.TM.getTargetData()->getPointerSize() == sizeof(int32_t)) {
    AddressDirective = TAI.getData32bitsDirective();
    AddressAlignLog = 2;
  } else {
    AddressDirective = TAI.getData64bitsDirective();
    AddressAlignLog = 3;
  }

  AP.SwitchToTextSection(TAI.getTextSection());
  EmitCamlGlobal(getModule(), OS, AP, TAI, "code_end");
  
  AP.SwitchToDataSection(TAI.getDataSection());
  EmitCamlGlobal(getModule(), OS, AP, TAI, "data_end");
  
  OS << AddressDirective << 0; // FIXME: Why does ocaml emit this??
  AP.EOL();
  
  AP.SwitchToDataSection(TAI.getDataSection());
  EmitCamlGlobal(getModule(), OS, AP, TAI, "frametable");
  
  for (iterator FI = begin(), FE = end(); FI != FE; ++FI) {
    CollectorMetadata &MD = **FI;
    
    OS << "\t" << TAI.getCommentString() << " live roots for "
       << MD.getFunction().getNameStart() << "\n";
    
    for (CollectorMetadata::iterator PI = MD.begin(),
                                     PE = MD.end(); PI != PE; ++PI) {
      
      uint64_t FrameSize = MD.getFrameSize();
      if (FrameSize >= 2<<16) {
        cerr << "Function '" << MD.getFunction().getNameStart()
             << "' is too large for the ocaml collector! "
             << "Frame size " << FrameSize << " >= 65536.\n";
        abort(); // Very rude!
      }
      
      size_t LiveCount = MD.live_size(PI);
      if (LiveCount >= 2<<16) {
        cerr << "Function '" << MD.getFunction().getNameStart()
             << "' is too large for the ocaml collector! "
             << "Live root count " << LiveCount << " >= 65536.\n";
        abort(); // Very rude!
      }
      
      OS << AddressDirective
         << TAI.getPrivateGlobalPrefix() << "label" << PI->Num;
      AP.EOL("call return address");
      
      AP.EmitInt16(FrameSize);
      AP.EOL("stack frame size");
      
      AP.EmitInt16(LiveCount);
      AP.EOL("live root count");
      
      for (CollectorMetadata::live_iterator LI = MD.live_begin(PI),
                                            LE = MD.live_end(PI);
                                            LI != LE; ++LI) {
        assert(LI->StackOffset < 2<<16 &&
               "GC root stack offset is outside of fixed stack frame and out "
               "of range for Ocaml collector!");
        
        OS << "\t.word\t" << LI->StackOffset;
        AP.EOL("stack offset");
      }
      
      AP.EmitAlignment(AddressAlignLog);
    }
  }
}