summaryrefslogtreecommitdiff
path: root/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
blob: 71568ec87a31b663f0cf7993f0b402371a77cb08 (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
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This contains code dealing with C++ code generation.
//
//===----------------------------------------------------------------------===//

#include "CIRGenCXXABI.h"
#include "CIRGenFunction.h"
#include "CIRGenModule.h"

#include "clang/AST/GlobalDecl.h"
#include "clang/CIR/MissingFeatures.h"
#include "llvm/Support/SaveAndRestore.h"

using namespace clang;
using namespace clang::CIRGen;

static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl,
                         cir::GlobalOp globalOp) {
  assert((varDecl->hasGlobalStorage() ||
          (varDecl->hasLocalStorage() &&
           cgf.getContext().getLangOpts().OpenCLCPlusPlus)) &&
         "VarDecl must have global or local (in the case of OpenCL) storage!");
  assert(!varDecl->getType()->isReferenceType() &&
         "Should not call emitDeclInit on a reference!");

  CIRGenBuilderTy &builder = cgf.getBuilder();

  // Set up the ctor region.
  mlir::OpBuilder::InsertionGuard guard(builder);
  mlir::Block *block = builder.createBlock(&globalOp.getCtorRegion());
  CIRGenFunction::LexicalScope lexScope{cgf, globalOp.getLoc(),
                                        builder.getInsertionBlock()};
  lexScope.setAsGlobalInit();
  builder.setInsertionPointToStart(block);

  Address declAddr(cgf.cgm.getAddrOfGlobalVar(varDecl),
                   cgf.cgm.getASTContext().getDeclAlign(varDecl));

  QualType type = varDecl->getType();
  LValue lv = cgf.makeAddrLValue(declAddr, type);

  const Expr *init = varDecl->getInit();
  switch (CIRGenFunction::getEvaluationKind(type)) {
  case cir::TEK_Scalar:
    assert(!cir::MissingFeatures::objCGC());
    cgf.emitScalarInit(init, cgf.getLoc(varDecl->getLocation()), lv, false);
    break;
  case cir::TEK_Complex:
    cgf.emitComplexExprIntoLValue(init, lv, /*isInit=*/true);
    break;
  case cir::TEK_Aggregate:
    assert(!cir::MissingFeatures::aggValueSlotGC());
    cgf.emitAggExpr(init,
                    AggValueSlot::forLValue(lv, AggValueSlot::IsDestructed,
                                            AggValueSlot::IsNotAliased,
                                            AggValueSlot::DoesNotOverlap));
    break;
  }

  // Finish the ctor region.
  builder.setInsertionPointToEnd(block);
  cir::YieldOp::create(builder, globalOp.getLoc());
}

static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd,
                            cir::GlobalOp addr) {
  // Honor __attribute__((no_destroy)) and bail instead of attempting
  // to emit a reference to a possibly nonexistent destructor, which
  // in turn can cause a crash. This will result in a global constructor
  // that isn't balanced out by a destructor call as intended by the
  // attribute. This also checks for -fno-c++-static-destructors and
  // bails even if the attribute is not present.
  QualType::DestructionKind dtorKind = vd->needsDestruction(cgf.getContext());

  // FIXME:  __attribute__((cleanup)) ?

  switch (dtorKind) {
  case QualType::DK_none:
    return;

  case QualType::DK_cxx_destructor:
    break;

  case QualType::DK_objc_strong_lifetime:
  case QualType::DK_objc_weak_lifetime:
  case QualType::DK_nontrivial_c_struct:
    // We don't care about releasing objects during process teardown.
    assert(!vd->getTLSKind() && "should have rejected this");
    return;
  }

  // If not constant storage we'll emit this regardless of NeedsDtor value.
  CIRGenBuilderTy &builder = cgf.getBuilder();

  // Prepare the dtor region.
  mlir::OpBuilder::InsertionGuard guard(builder);
  mlir::Block *block = builder.createBlock(&addr.getDtorRegion());
  CIRGenFunction::LexicalScope lexScope{cgf, addr.getLoc(),
                                        builder.getInsertionBlock()};
  lexScope.setAsGlobalInit();
  builder.setInsertionPointToStart(block);

  CIRGenModule &cgm = cgf.cgm;
  QualType type = vd->getType();

  // Special-case non-array C++ destructors, if they have the right signature.
  // Under some ABIs, destructors return this instead of void, and cannot be
  // passed directly to __cxa_atexit if the target does not allow this
  // mismatch.
  const CXXRecordDecl *record = type->getAsCXXRecordDecl();
  bool canRegisterDestructor =
      record && (!cgm.getCXXABI().hasThisReturn(
                     GlobalDecl(record->getDestructor(), Dtor_Complete)) ||
                 cgm.getCXXABI().canCallMismatchedFunctionType());

  // If __cxa_atexit is disabled via a flag, a different helper function is
  // generated elsewhere which uses atexit instead, and it takes the destructor
  // directly.
  cir::FuncOp fnOp;
  if (record && (canRegisterDestructor || cgm.getCodeGenOpts().CXAAtExit)) {
    if (vd->getTLSKind())
      cgm.errorNYI(vd->getSourceRange(), "TLS destructor");
    assert(!record->hasTrivialDestructor());
    assert(!cir::MissingFeatures::openCL());
    CXXDestructorDecl *dtor = record->getDestructor();
    // In LLVM OG codegen this is done in registerGlobalDtor, but CIRGen
    // relies on LoweringPrepare for further decoupling, so build the
    // call right here.
    auto gd = GlobalDecl(dtor, Dtor_Complete);
    fnOp = cgm.getAddrAndTypeOfCXXStructor(gd).second;
    builder.createCallOp(cgf.getLoc(vd->getSourceRange()),
                         mlir::FlatSymbolRefAttr::get(fnOp.getSymNameAttr()),
                         mlir::ValueRange{cgm.getAddrOfGlobalVar(vd)});
    assert(fnOp && "expected cir.func");
    // TODO(cir): This doesn't do anything but check for unhandled conditions.
    // What it is meant to do should really be happening in LoweringPrepare.
    cgm.getCXXABI().registerGlobalDtor(vd, fnOp, nullptr);
  } else {
    // Otherwise, a custom destroyed is needed. Classic codegen creates a helper
    // function here and emits the destroy into the helper function, which is
    // called from __cxa_atexit.
    // In CIR, we just emit the destroy into the dtor region. It will be moved
    // into a separate function during the LoweringPrepare pass.
    // FIXME(cir): We should create a new operation here to explicitly get the
    // address of the global into whose dtor region we are emiiting the destroy.
    // The same applies to code above where it is calling getAddrOfGlobalVar.
    mlir::Value globalVal = builder.createGetGlobal(addr);
    CharUnits alignment = cgf.getContext().getDeclAlign(vd);
    Address globalAddr{globalVal, cgf.convertTypeForMem(type), alignment};
    cgf.emitDestroy(globalAddr, type, cgf.getDestroyer(dtorKind));
  }

  builder.setInsertionPointToEnd(block);
  if (block->empty()) {
    block->erase();
    // Don't confuse lexical cleanup.
    builder.clearInsertionPoint();
  } else {
    cir::YieldOp::create(builder, addr.getLoc());
  }
}

cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) {
  const CIRGenFunctionInfo &fnInfo =
      getTypes().arrangeCXXStructorDeclaration(gd);
  cir::FuncType funcType = getTypes().getFunctionType(fnInfo);
  cir::FuncOp fn = getAddrOfCXXStructor(gd, &fnInfo, /*FnType=*/nullptr,
                                        /*DontDefer=*/true, ForDefinition);
  setFunctionLinkage(gd, fn);
  CIRGenFunction cgf{*this, builder};
  curCGF = &cgf;
  {
    mlir::OpBuilder::InsertionGuard guard(builder);
    cgf.generateCode(gd, fn, funcType);
  }
  curCGF = nullptr;

  setNonAliasAttributes(gd, fn);
  setCIRFunctionAttributesForDefinition(mlir::cast<FunctionDecl>(gd.getDecl()),
                                        fn);
  return fn;
}

// Global variables requiring non-trivial initialization are handled
// differently in CIR than in classic codegen. Classic codegen emits
// a global init function (__cxx_global_var_init) and inserts
// initialization for each global there. In CIR, we attach a ctor
// region to the global variable and insert the initialization code
// into the ctor region. This will be moved into the
// __cxx_global_var_init function during the LoweringPrepare pass.
void CIRGenModule::emitCXXGlobalVarDeclInit(const VarDecl *varDecl,
                                            cir::GlobalOp addr,
                                            bool performInit) {
  QualType ty = varDecl->getType();

  // TODO: handle address space
  // The address space of a static local variable (addr) may be different
  // from the address space of the "this" argument of the constructor. In that
  // case, we need an addrspacecast before calling the constructor.
  //
  // struct StructWithCtor {
  //   __device__ StructWithCtor() {...}
  // };
  // __device__ void foo() {
  //   __shared__ StructWithCtor s;
  //   ...
  // }
  //
  // For example, in the above CUDA code, the static local variable s has a
  // "shared" address space qualifier, but the constructor of StructWithCtor
  // expects "this" in the "generic" address space.
  assert(!cir::MissingFeatures::addressSpace());

  // Create a CIRGenFunction to emit the initializer. While this isn't a true
  // function, the handling works the same way.
  CIRGenFunction cgf{*this, builder, true};
  llvm::SaveAndRestore<CIRGenFunction *> savedCGF(curCGF, &cgf);
  curCGF->curFn = addr;

  CIRGenFunction::SourceLocRAIIObject fnLoc{cgf,
                                            getLoc(varDecl->getLocation())};

  assert(!cir::MissingFeatures::astVarDeclInterface());

  if (!ty->isReferenceType()) {
    assert(!cir::MissingFeatures::openMP());

    bool needsDtor = varDecl->needsDestruction(getASTContext()) ==
                     QualType::DK_cxx_destructor;
    // PerformInit, constant store invariant / destroy handled below.
    if (performInit)
      emitDeclInit(cgf, varDecl, addr);

    if (varDecl->getType().isConstantStorage(getASTContext(), true, !needsDtor))
      errorNYI(varDecl->getSourceRange(), "global with constant storage");
    else
      emitDeclDestroy(cgf, varDecl, addr);
    return;
  }

  errorNYI(varDecl->getSourceRange(), "global with reference type");
}