Coverage Report

Created: 2024-01-17 10:31

/src/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- ByteCodeEmitter.cpp - Instruction emitter for the VM ---*- C++ -*-===//
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
#include "ByteCodeEmitter.h"
10
#include "ByteCodeGenError.h"
11
#include "Context.h"
12
#include "Floating.h"
13
#include "Opcode.h"
14
#include "Program.h"
15
#include "clang/AST/ASTLambda.h"
16
#include "clang/AST/DeclCXX.h"
17
#include "clang/Basic/Builtins.h"
18
#include <type_traits>
19
20
using namespace clang;
21
using namespace clang::interp;
22
23
Expected<Function *>
24
0
ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
25
  // Set up argument indices.
26
0
  unsigned ParamOffset = 0;
27
0
  SmallVector<PrimType, 8> ParamTypes;
28
0
  SmallVector<unsigned, 8> ParamOffsets;
29
0
  llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors;
30
31
  // If the return is not a primitive, a pointer to the storage where the
32
  // value is initialized in is passed as the first argument. See 'RVO'
33
  // elsewhere in the code.
34
0
  QualType Ty = FuncDecl->getReturnType();
35
0
  bool HasRVO = false;
36
0
  if (!Ty->isVoidType() && !Ctx.classify(Ty)) {
37
0
    HasRVO = true;
38
0
    ParamTypes.push_back(PT_Ptr);
39
0
    ParamOffsets.push_back(ParamOffset);
40
0
    ParamOffset += align(primSize(PT_Ptr));
41
0
  }
42
43
  // If the function decl is a member decl, the next parameter is
44
  // the 'this' pointer. This parameter is pop()ed from the
45
  // InterpStack when calling the function.
46
0
  bool HasThisPointer = false;
47
0
  if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl)) {
48
0
    if (MD->isImplicitObjectMemberFunction()) {
49
0
      HasThisPointer = true;
50
0
      ParamTypes.push_back(PT_Ptr);
51
0
      ParamOffsets.push_back(ParamOffset);
52
0
      ParamOffset += align(primSize(PT_Ptr));
53
0
    }
54
55
    // Set up lambda capture to closure record field mapping.
56
0
    if (isLambdaCallOperator(MD)) {
57
0
      const Record *R = P.getOrCreateRecord(MD->getParent());
58
0
      llvm::DenseMap<const ValueDecl *, FieldDecl *> LC;
59
0
      FieldDecl *LTC;
60
61
0
      MD->getParent()->getCaptureFields(LC, LTC);
62
63
0
      for (auto Cap : LC) {
64
        // Static lambdas cannot have any captures. If this one does,
65
        // it has already been diagnosed and we can only ignore it.
66
0
        if (MD->isStatic())
67
0
          return nullptr;
68
69
0
        unsigned Offset = R->getField(Cap.second)->Offset;
70
0
        this->LambdaCaptures[Cap.first] = {
71
0
            Offset, Cap.second->getType()->isReferenceType()};
72
0
      }
73
0
      if (LTC)
74
0
        this->LambdaThisCapture = R->getField(LTC)->Offset;
75
0
    }
76
0
  }
77
78
  // Assign descriptors to all parameters.
79
  // Composite objects are lowered to pointers.
80
0
  for (const ParmVarDecl *PD : FuncDecl->parameters()) {
81
0
    std::optional<PrimType> T = Ctx.classify(PD->getType());
82
0
    PrimType PT = T.value_or(PT_Ptr);
83
0
    Descriptor *Desc = P.createDescriptor(PD, PT);
84
0
    ParamDescriptors.insert({ParamOffset, {PT, Desc}});
85
0
    Params.insert({PD, {ParamOffset, T != std::nullopt}});
86
0
    ParamOffsets.push_back(ParamOffset);
87
0
    ParamOffset += align(primSize(PT));
88
0
    ParamTypes.push_back(PT);
89
0
  }
90
91
  // Create a handle over the emitted code.
92
0
  Function *Func = P.getFunction(FuncDecl);
93
0
  if (!Func) {
94
0
    bool IsUnevaluatedBuiltin = false;
95
0
    if (unsigned BI = FuncDecl->getBuiltinID())
96
0
      IsUnevaluatedBuiltin = Ctx.getASTContext().BuiltinInfo.isUnevaluated(BI);
97
98
0
    Func =
99
0
        P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes),
100
0
                         std::move(ParamDescriptors), std::move(ParamOffsets),
101
0
                         HasThisPointer, HasRVO, IsUnevaluatedBuiltin);
102
0
  }
103
104
0
  assert(Func);
105
  // For not-yet-defined functions, we only create a Function instance and
106
  // compile their body later.
107
0
  if (!FuncDecl->isDefined()) {
108
0
    Func->setDefined(false);
109
0
    return Func;
110
0
  }
111
112
0
  Func->setDefined(true);
113
114
  // Lambda static invokers are a special case that we emit custom code for.
115
0
  bool IsEligibleForCompilation = false;
116
0
  if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl))
117
0
    IsEligibleForCompilation = MD->isLambdaStaticInvoker();
118
0
  if (!IsEligibleForCompilation)
119
0
    IsEligibleForCompilation = FuncDecl->isConstexpr();
120
121
  // Compile the function body.
122
0
  if (!IsEligibleForCompilation || !visitFunc(FuncDecl)) {
123
    // Return a dummy function if compilation failed.
124
0
    if (BailLocation)
125
0
      return llvm::make_error<ByteCodeGenError>(*BailLocation);
126
127
0
    Func->setIsFullyCompiled(true);
128
0
    return Func;
129
0
  }
130
131
  // Create scopes from descriptors.
132
0
  llvm::SmallVector<Scope, 2> Scopes;
133
0
  for (auto &DS : Descriptors) {
134
0
    Scopes.emplace_back(std::move(DS));
135
0
  }
136
137
  // Set the function's code.
138
0
  Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap),
139
0
                std::move(Scopes), FuncDecl->hasBody());
140
0
  Func->setIsFullyCompiled(true);
141
0
  return Func;
142
0
}
143
144
0
Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) {
145
0
  NextLocalOffset += sizeof(Block);
146
0
  unsigned Location = NextLocalOffset;
147
0
  NextLocalOffset += align(D->getAllocSize());
148
0
  return {Location, D};
149
0
}
150
151
0
void ByteCodeEmitter::emitLabel(LabelTy Label) {
152
0
  const size_t Target = Code.size();
153
0
  LabelOffsets.insert({Label, Target});
154
155
0
  if (auto It = LabelRelocs.find(Label);
156
0
      It != LabelRelocs.end()) {
157
0
    for (unsigned Reloc : It->second) {
158
0
      using namespace llvm::support;
159
160
      // Rewrite the operand of all jumps to this label.
161
0
      void *Location = Code.data() + Reloc - align(sizeof(int32_t));
162
0
      assert(aligned(Location));
163
0
      const int32_t Offset = Target - static_cast<int64_t>(Reloc);
164
0
      endian::write<int32_t, llvm::endianness::native>(Location, Offset);
165
0
    }
166
0
    LabelRelocs.erase(It);
167
0
  }
168
0
}
169
170
0
int32_t ByteCodeEmitter::getOffset(LabelTy Label) {
171
  // Compute the PC offset which the jump is relative to.
172
0
  const int64_t Position =
173
0
      Code.size() + align(sizeof(Opcode)) + align(sizeof(int32_t));
174
0
  assert(aligned(Position));
175
176
  // If target is known, compute jump offset.
177
0
  if (auto It = LabelOffsets.find(Label);
178
0
      It != LabelOffsets.end())
179
0
    return It->second - Position;
180
181
  // Otherwise, record relocation and return dummy offset.
182
0
  LabelRelocs[Label].push_back(Position);
183
0
  return 0ull;
184
0
}
185
186
0
bool ByteCodeEmitter::bail(const SourceLocation &Loc) {
187
0
  if (!BailLocation)
188
0
    BailLocation = Loc;
189
0
  return false;
190
0
}
191
192
/// Helper to write bytecode and bail out if 32-bit offsets become invalid.
193
/// Pointers will be automatically marshalled as 32-bit IDs.
194
template <typename T>
195
static void emit(Program &P, std::vector<std::byte> &Code, const T &Val,
196
0
                 bool &Success) {
197
0
  size_t Size;
198
199
0
  if constexpr (std::is_pointer_v<T>)
200
0
    Size = sizeof(uint32_t);
201
0
  else
202
0
    Size = sizeof(T);
203
204
0
  if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
205
0
    Success = false;
206
0
    return;
207
0
  }
208
209
  // Access must be aligned!
210
0
  size_t ValPos = align(Code.size());
211
0
  Size = align(Size);
212
0
  assert(aligned(ValPos + Size));
213
0
  Code.resize(ValPos + Size);
214
215
0
  if constexpr (!std::is_pointer_v<T>) {
216
0
    new (Code.data() + ValPos) T(Val);
217
0
  } else {
218
0
    uint32_t ID = P.getOrCreateNativePointer(Val);
219
0
    new (Code.data() + ValPos) uint32_t(ID);
220
0
  }
221
0
}
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<clang::interp::Opcode>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, clang::interp::Opcode const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<llvm::RoundingMode>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, llvm::RoundingMode const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<clang::ComparisonCategoryInfo const*>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, clang::ComparisonCategoryInfo const* const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<clang::interp::Function const*>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, clang::interp::Function const* const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<clang::CallExpr const*>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, clang::CallExpr const* const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<unsigned int>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, unsigned int const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<llvm::fltSemantics const*>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, llvm::fltSemantics const* const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<bool>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, bool const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<signed char>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, signed char const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<short>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, short const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<int>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, int const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<long>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, long const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<unsigned char>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, unsigned char const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<unsigned short>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, unsigned short const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<unsigned long>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, unsigned long const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<clang::RecordDecl const*>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, clang::RecordDecl const* const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<clang::interp::Record::Field const*>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, clang::interp::Record::Field const* const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<clang::LifetimeExtendedTemporaryDecl const*>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, clang::LifetimeExtendedTemporaryDecl const* const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<clang::interp::CastKind>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, clang::interp::CastKind const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<clang::DeclRefExpr const*>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, clang::DeclRefExpr const* const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:void emit<clang::OffsetOfExpr const*>(clang::interp::Program&, std::__1::vector<std::byte, std::__1::allocator<std::byte> >&, clang::OffsetOfExpr const* const&, bool&)
222
223
template <>
224
void emit(Program &P, std::vector<std::byte> &Code, const Floating &Val,
225
0
          bool &Success) {
226
0
  size_t Size = Val.bytesToSerialize();
227
228
0
  if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
229
0
    Success = false;
230
0
    return;
231
0
  }
232
233
  // Access must be aligned!
234
0
  size_t ValPos = align(Code.size());
235
0
  Size = align(Size);
236
0
  assert(aligned(ValPos + Size));
237
0
  Code.resize(ValPos + Size);
238
239
0
  Val.serialize(Code.data() + ValPos);
240
0
}
241
242
template <typename... Tys>
243
0
bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
244
0
  bool Success = true;
245
246
  // The opcode is followed by arguments. The source info is
247
  // attached to the address after the opcode.
248
0
  emit(P, Code, Op, Success);
249
0
  if (SI)
250
0
    SrcMap.emplace_back(Code.size(), SI);
251
252
  // The initializer list forces the expression to be evaluated
253
  // for each argument in the variadic template, in order.
254
0
  (void)std::initializer_list<int>{(emit(P, Code, Args, Success), 0)...};
255
256
0
  return Success;
257
0
}
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<>(clang::interp::Opcode, , clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<llvm::RoundingMode>(clang::interp::Opcode, llvm::RoundingMode const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<clang::ComparisonCategoryInfo const*>(clang::interp::Opcode, clang::ComparisonCategoryInfo const* const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<clang::interp::Function const*>(clang::interp::Opcode, clang::interp::Function const* const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<clang::interp::Function const*, clang::CallExpr const*>(clang::interp::Opcode, clang::interp::Function const* const&, clang::CallExpr const* const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<unsigned int>(clang::interp::Opcode, unsigned int const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<llvm::fltSemantics const*, llvm::RoundingMode>(clang::interp::Opcode, llvm::fltSemantics const* const&, llvm::RoundingMode const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<bool>(clang::interp::Opcode, bool const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<clang::interp::Floating>(clang::interp::Opcode, clang::interp::Floating const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<signed char>(clang::interp::Opcode, signed char const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<short>(clang::interp::Opcode, short const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<int>(clang::interp::Opcode, int const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<long>(clang::interp::Opcode, long const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<unsigned char>(clang::interp::Opcode, unsigned char const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<unsigned short>(clang::interp::Opcode, unsigned short const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<unsigned long>(clang::interp::Opcode, unsigned long const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<clang::RecordDecl const*>(clang::interp::Opcode, clang::RecordDecl const* const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<clang::interp::Record::Field const*>(clang::interp::Opcode, clang::interp::Record::Field const* const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<unsigned int, clang::LifetimeExtendedTemporaryDecl const*>(clang::interp::Opcode, unsigned int const&, clang::LifetimeExtendedTemporaryDecl const* const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<clang::LifetimeExtendedTemporaryDecl const*>(clang::interp::Opcode, clang::LifetimeExtendedTemporaryDecl const* const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<clang::interp::CastKind>(clang::interp::Opcode, clang::interp::CastKind const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<clang::DeclRefExpr const*>(clang::interp::Opcode, clang::DeclRefExpr const* const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<clang::OffsetOfExpr const*>(clang::interp::Opcode, clang::OffsetOfExpr const* const&, clang::interp::SourceInfo const&)
258
259
0
bool ByteCodeEmitter::jumpTrue(const LabelTy &Label) {
260
0
  return emitJt(getOffset(Label), SourceInfo{});
261
0
}
262
263
0
bool ByteCodeEmitter::jumpFalse(const LabelTy &Label) {
264
0
  return emitJf(getOffset(Label), SourceInfo{});
265
0
}
266
267
0
bool ByteCodeEmitter::jump(const LabelTy &Label) {
268
0
  return emitJmp(getOffset(Label), SourceInfo{});
269
0
}
270
271
0
bool ByteCodeEmitter::fallthrough(const LabelTy &Label) {
272
0
  emitLabel(Label);
273
0
  return true;
274
0
}
275
276
//===----------------------------------------------------------------------===//
277
// Opcode emitters
278
//===----------------------------------------------------------------------===//
279
280
#define GET_LINK_IMPL
281
#include "Opcodes.inc"
282
#undef GET_LINK_IMPL