aboutsummaryrefslogtreecommitdiffstats
path: root/include/llvm/IR/Statepoint.h
blob: 38720edabad8305e12c717cc42598cd045d85d7d (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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
//===-- llvm/IR/Statepoint.h - gc.statepoint utilities ------ --*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains utility functions and a wrapper class analogous to
// CallSite for accessing the fields of gc.statepoint, gc.relocate, and
// gc.result intrinsics
//
//===----------------------------------------------------------------------===//

#ifndef __LLVM_IR_STATEPOINT_H
#define __LLVM_IR_STATEPOINT_H

#include "llvm/ADT/iterator_range.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/Compiler.h"

namespace llvm {

class GCRelocateOperands;
class ImmutableStatepoint;

bool isStatepoint(const ImmutableCallSite &CS);
bool isStatepoint(const Value *inst);
bool isStatepoint(const Value &inst);

bool isGCRelocate(const Value *inst);
bool isGCRelocate(const ImmutableCallSite &CS);

bool isGCResult(const Value *inst);
bool isGCResult(const ImmutableCallSite &CS);

/// Analogous to CallSiteBase, this provides most of the actual
/// functionality for Statepoint and ImmutableStatepoint.  It is
/// templatized to allow easily specializing of const and non-const
/// concrete subtypes.  This is structured analogous to CallSite
/// rather than the IntrinsicInst.h helpers since we want to support
/// invokable statepoints in the near future.
/// TODO: This does not currently allow the if(Statepoint S = ...)
///   idiom used with CallSites.  Consider refactoring to support.
template <typename InstructionTy, typename ValueTy, typename CallSiteTy>
class StatepointBase {
  CallSiteTy StatepointCS;
  void *operator new(size_t, unsigned) = delete;
  void *operator new(size_t s) = delete;

 protected:
  explicit StatepointBase(InstructionTy *I) : StatepointCS(I) {
    assert(isStatepoint(I));
  }
  explicit StatepointBase(CallSiteTy CS) : StatepointCS(CS) {
    assert(isStatepoint(CS));
  }

 public:
  typedef typename CallSiteTy::arg_iterator arg_iterator;

  /// Return the underlying CallSite.
  CallSiteTy getCallSite() {
    return StatepointCS;
  }

  /// Return the value actually being called or invoked.
  ValueTy *actualCallee() {
    return StatepointCS.getArgument(0);
  }
  /// Number of arguments to be passed to the actual callee.
  int numCallArgs() {
    return cast<ConstantInt>(StatepointCS.getArgument(1))->getZExtValue();
  }
  /// Number of additional arguments excluding those intended
  /// for garbage collection.
  int numTotalVMSArgs() {
    return cast<ConstantInt>(StatepointCS.getArgument(3 + numCallArgs()))->getZExtValue();
  }

  typename CallSiteTy::arg_iterator call_args_begin() {
    // 3 = callTarget, #callArgs, flag
    int Offset = 3;
    assert(Offset <= (int)StatepointCS.arg_size());
    return StatepointCS.arg_begin() + Offset;
  }
  typename CallSiteTy::arg_iterator call_args_end() {
    int Offset = 3 + numCallArgs();
    assert(Offset <= (int)StatepointCS.arg_size());
    return StatepointCS.arg_begin() + Offset;
  }

  /// range adapter for call arguments
  iterator_range<arg_iterator> call_args() {
    return iterator_range<arg_iterator>(call_args_begin(), call_args_end());
  }

  typename CallSiteTy::arg_iterator vm_state_begin() {
    return call_args_end();
  }
  typename CallSiteTy::arg_iterator vm_state_end() {
    int Offset = 3 + numCallArgs() + 1 + numTotalVMSArgs();
    assert(Offset <= (int)StatepointCS.arg_size());
    return StatepointCS.arg_begin() + Offset;
  }

  /// range adapter for vm state arguments
  iterator_range<arg_iterator> vm_state_args() {
    return iterator_range<arg_iterator>(vm_state_begin(), vm_state_end());
  }

  typename CallSiteTy::arg_iterator first_vm_state_stack_begin() {
    // 6 = numTotalVMSArgs, 1st_objectID, 1st_bci,
    //     1st_#stack, 1st_#local, 1st_#monitor
    return vm_state_begin() + 6;
  }

  typename CallSiteTy::arg_iterator gc_args_begin() {
    return vm_state_end();
  }
  typename CallSiteTy::arg_iterator gc_args_end() {
    return StatepointCS.arg_end();
  }

  /// range adapter for gc arguments
  iterator_range<arg_iterator> gc_args() {
    return iterator_range<arg_iterator>(gc_args_begin(), gc_args_end());
  }

  /// Get list of all gc reloactes linked to this statepoint
  /// May contain several relocations for the same base/derived pair.
  /// For example this could happen due to relocations on unwinding
  /// path of invoke.
  std::vector<GCRelocateOperands> getRelocates(ImmutableStatepoint &IS);

#ifndef NDEBUG
  /// Asserts if this statepoint is malformed.  Common cases for failure
  /// include incorrect length prefixes for variable length sections or
  /// illegal values for parameters.
  void verify() {
    assert(numCallArgs() >= 0 &&
           "number of arguments to actually callee can't be negative");

    // The internal asserts in the iterator accessors do the rest.
    (void)call_args_begin();
    (void)call_args_end();
    (void)vm_state_begin();
    (void)vm_state_end();
    (void)gc_args_begin();
    (void)gc_args_end();
  }
#endif
};

/// A specialization of it's base class for read only access
/// to a gc.statepoint.
class ImmutableStatepoint
    : public StatepointBase<const Instruction, const Value,
                            ImmutableCallSite> {
  typedef StatepointBase<const Instruction, const Value, ImmutableCallSite>
      Base;

public:
  explicit ImmutableStatepoint(const Instruction *I) : Base(I) {}
  explicit ImmutableStatepoint(ImmutableCallSite CS) : Base(CS) {}
};

/// A specialization of it's base class for read-write access
/// to a gc.statepoint.
class Statepoint : public StatepointBase<Instruction, Value, CallSite> {
  typedef StatepointBase<Instruction, Value, CallSite> Base;

public:
  explicit Statepoint(Instruction *I) : Base(I) {}
  explicit Statepoint(CallSite CS) : Base(CS) {}
};

/// Wraps a call to a gc.relocate and provides access to it's operands.
/// TODO: This should likely be refactored to resememble the wrappers in
/// InstrinsicInst.h.
class GCRelocateOperands {
  ImmutableCallSite RelocateCS;

 public:
  GCRelocateOperands(const User* U) : RelocateCS(U) {
    assert(isGCRelocate(U));
  }
  GCRelocateOperands(const Instruction *inst) : RelocateCS(inst) {
    assert(isGCRelocate(inst));
  }
  GCRelocateOperands(CallSite CS) : RelocateCS(CS) {
    assert(isGCRelocate(CS));
  }

  /// Return true if this relocate is tied to the invoke statepoint.
  /// This includes relocates which are on the unwinding path.
  bool isTiedToInvoke() const {
    const Value *Token = RelocateCS.getArgument(0);

    return isa<ExtractValueInst>(Token) ||
      isa<InvokeInst>(Token);
  }

  /// Get enclosed relocate intrinsic
  ImmutableCallSite getUnderlyingCallSite() {
    return RelocateCS;
  }

  /// The statepoint with which this gc.relocate is associated.
  const Instruction *statepoint() {
    const Value *token = RelocateCS.getArgument(0);

    // This takes care both of relocates for call statepoints and relocates
    // on normal path of invoke statepoint.
    if (!isa<ExtractValueInst>(token)) {
      return cast<Instruction>(token);
    }

    // This relocate is on exceptional path of an invoke statepoint
    const BasicBlock *invokeBB =
      cast<Instruction>(token)->getParent()->getUniquePredecessor();

    assert(invokeBB && "safepoints should have unique landingpads");
    assert(invokeBB->getTerminator() && "safepoint block should be well formed");
    assert(isStatepoint(invokeBB->getTerminator()));

    return invokeBB->getTerminator();
  }
  /// The index into the associate statepoint's argument list
  /// which contains the base pointer of the pointer whose
  /// relocation this gc.relocate describes.
  unsigned basePtrIndex() {
    return cast<ConstantInt>(RelocateCS.getArgument(1))->getZExtValue();
  }
  /// The index into the associate statepoint's argument list which
  /// contains the pointer whose relocation this gc.relocate describes.
  unsigned derivedPtrIndex() {
    return cast<ConstantInt>(RelocateCS.getArgument(2))->getZExtValue();
  }
  Value *basePtr() {
    ImmutableCallSite CS(statepoint());
    return *(CS.arg_begin() + basePtrIndex());
  }
  Value *derivedPtr() {
    ImmutableCallSite CS(statepoint());
    return *(CS.arg_begin() + derivedPtrIndex());
  }
};

template <typename InstructionTy, typename ValueTy, typename CallSiteTy>
std::vector<GCRelocateOperands>
  StatepointBase<InstructionTy, ValueTy, CallSiteTy>::
    getRelocates(ImmutableStatepoint &IS) {

  std::vector<GCRelocateOperands> res;

  ImmutableCallSite StatepointCS = IS.getCallSite();

  // Search for relocated pointers.  Note that working backwards from the
  // gc_relocates ensures that we only get pairs which are actually relocated
  // and used after the statepoint.
  for (const User *U : StatepointCS.getInstruction()->users()) {
    if (isGCRelocate(U)) {
      res.push_back(GCRelocateOperands(U));
    }
  }

  if (!StatepointCS.isInvoke()) {
    return res;
  }

  // We need to scan thorough exceptional relocations if it is invoke statepoint
  LandingPadInst *LandingPad =
    cast<InvokeInst>(StatepointCS.getInstruction())->getLandingPadInst();

  // Search for extract value from landingpad instruction to which
  // gc relocates will be attached
  for (const User *LandingPadUser : LandingPad->users()) {
    if (!isa<ExtractValueInst>(LandingPadUser)) {
      continue;
    }

    // gc relocates should be attached to this extract value
    for (const User *U : LandingPadUser->users()) {
      if (isGCRelocate(U)) {
        res.push_back(GCRelocateOperands(U));
      }
    }
  }
  return res;
}

}
#endif