Coverage Report

Created: 2021-09-21 08:58

/Users/buildslave/jenkins/workspace/coverage/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 "Context.h"
11
#include "Opcode.h"
12
#include "Program.h"
13
#include "clang/AST/DeclCXX.h"
14
#include <type_traits>
15
16
using namespace clang;
17
using namespace clang::interp;
18
19
using APSInt = llvm::APSInt;
20
using Error = llvm::Error;
21
22
1
Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) {
23
  // Do not try to compile undefined functions.
24
1
  if (!F->isDefined(F) || (!F->hasBody() && 
F->willHaveBody()0
))
25
0
    return nullptr;
26
27
  // Set up argument indices.
28
1
  unsigned ParamOffset = 0;
29
1
  SmallVector<PrimType, 8> ParamTypes;
30
1
  llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors;
31
32
  // If the return is not a primitive, a pointer to the storage where the value
33
  // is initialized in is passed as the first argument.
34
1
  QualType Ty = F->getReturnType();
35
1
  if (!Ty->isVoidType() && !Ctx.classify(Ty)) {
36
0
    ParamTypes.push_back(PT_Ptr);
37
0
    ParamOffset += align(primSize(PT_Ptr));
38
0
  }
39
40
  // Assign descriptors to all parameters.
41
  // Composite objects are lowered to pointers.
42
2
  for (const ParmVarDecl *PD : F->parameters()) {
43
2
    PrimType Ty;
44
2
    if (llvm::Optional<PrimType> T = Ctx.classify(PD->getType())) {
45
2
      Ty = *T;
46
2
    } else {
47
0
      Ty = PT_Ptr;
48
0
    }
49
50
2
    Descriptor *Desc = P.createDescriptor(PD, Ty);
51
2
    ParamDescriptors.insert({ParamOffset, {Ty, Desc}});
52
2
    Params.insert({PD, ParamOffset});
53
2
    ParamOffset += align(primSize(Ty));
54
2
    ParamTypes.push_back(Ty);
55
2
  }
56
57
  // Create a handle over the emitted code.
58
1
  Function *Func = P.createFunction(F, ParamOffset, std::move(ParamTypes),
59
1
                                    std::move(ParamDescriptors));
60
  // Compile the function body.
61
1
  if (!F->isConstexpr() || !visitFunc(F)) {
62
    // Return a dummy function if compilation failed.
63
0
    if (BailLocation)
64
0
      return llvm::make_error<ByteCodeGenError>(*BailLocation);
65
0
    else
66
0
      return Func;
67
1
  } else {
68
    // Create scopes from descriptors.
69
1
    llvm::SmallVector<Scope, 2> Scopes;
70
1
    for (auto &DS : Descriptors) {
71
0
      Scopes.emplace_back(std::move(DS));
72
0
    }
73
74
    // Set the function's code.
75
1
    Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap),
76
1
                  std::move(Scopes));
77
1
    return Func;
78
1
  }
79
1
}
80
81
0
Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) {
82
0
  NextLocalOffset += sizeof(Block);
83
0
  unsigned Location = NextLocalOffset;
84
0
  NextLocalOffset += align(D->getAllocSize());
85
0
  return {Location, D};
86
0
}
87
88
2
void ByteCodeEmitter::emitLabel(LabelTy Label) {
89
2
  const size_t Target = Code.size();
90
2
  LabelOffsets.insert({Label, Target});
91
2
  auto It = LabelRelocs.find(Label);
92
2
  if (It != LabelRelocs.end()) {
93
2
    for (unsigned Reloc : It->second) {
94
2
      using namespace llvm::support;
95
96
      /// Rewrite the operand of all jumps to this label.
97
2
      void *Location = Code.data() + Reloc - sizeof(int32_t);
98
2
      const int32_t Offset = Target - static_cast<int64_t>(Reloc);
99
2
      endian::write<int32_t, endianness::native, 1>(Location, Offset);
100
2
    }
101
2
    LabelRelocs.erase(It);
102
2
  }
103
2
}
104
105
2
int32_t ByteCodeEmitter::getOffset(LabelTy Label) {
106
  // Compute the PC offset which the jump is relative to.
107
2
  const int64_t Position = Code.size() + sizeof(Opcode) + sizeof(int32_t);
108
109
  // If target is known, compute jump offset.
110
2
  auto It = LabelOffsets.find(Label);
111
2
  if (It != LabelOffsets.end()) {
112
0
    return It->second - Position;
113
0
  }
114
115
  // Otherwise, record relocation and return dummy offset.
116
2
  LabelRelocs[Label].push_back(Position);
117
2
  return 0ull;
118
2
}
119
120
0
bool ByteCodeEmitter::bail(const SourceLocation &Loc) {
121
0
  if (!BailLocation)
122
0
    BailLocation = Loc;
123
0
  return false;
124
0
}
125
126
/// Helper to write bytecode and bail out if 32-bit offsets become invalid.
127
/// Pointers will be automatically marshalled as 32-bit IDs.
128
template <typename T>
129
static std::enable_if_t<!std::is_pointer<T>::value, void>
130
22
emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) {
131
22
  size_t Size = sizeof(Val);
132
22
  if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
133
0
    Success = false;
134
0
    return;
135
0
  }
136
137
22
  const char *Data = reinterpret_cast<const char *>(&Val);
138
22
  Code.insert(Code.end(), Data, Data + Size);
139
22
}
ByteCodeEmitter.cpp:std::__1::enable_if<!(std::is_pointer<clang::interp::Opcode>::value), void>::type emit<clang::interp::Opcode>(clang::interp::Program&, std::__1::vector<char, std::__1::allocator<char> >&, clang::interp::Opcode const&, bool&)
Line
Count
Source
130
14
emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) {
131
14
  size_t Size = sizeof(Val);
132
14
  if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
133
0
    Success = false;
134
0
    return;
135
0
  }
136
137
14
  const char *Data = reinterpret_cast<const char *>(&Val);
138
14
  Code.insert(Code.end(), Data, Data + Size);
139
14
}
Unexecuted instantiation: ByteCodeEmitter.cpp:std::__1::enable_if<!(std::is_pointer<bool>::value), void>::type emit<bool>(clang::interp::Program&, std::__1::vector<char, std::__1::allocator<char> >&, bool const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:std::__1::enable_if<!(std::is_pointer<short>::value), void>::type emit<short>(clang::interp::Program&, std::__1::vector<char, std::__1::allocator<char> >&, short const&, bool&)
ByteCodeEmitter.cpp:std::__1::enable_if<!(std::is_pointer<int>::value), void>::type emit<int>(clang::interp::Program&, std::__1::vector<char, std::__1::allocator<char> >&, int const&, bool&)
Line
Count
Source
130
2
emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) {
131
2
  size_t Size = sizeof(Val);
132
2
  if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
133
0
    Success = false;
134
0
    return;
135
0
  }
136
137
2
  const char *Data = reinterpret_cast<const char *>(&Val);
138
2
  Code.insert(Code.end(), Data, Data + Size);
139
2
}
Unexecuted instantiation: ByteCodeEmitter.cpp:std::__1::enable_if<!(std::is_pointer<long long>::value), void>::type emit<long long>(clang::interp::Program&, std::__1::vector<char, std::__1::allocator<char> >&, long long const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:std::__1::enable_if<!(std::is_pointer<signed char>::value), void>::type emit<signed char>(clang::interp::Program&, std::__1::vector<char, std::__1::allocator<char> >&, signed char const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:std::__1::enable_if<!(std::is_pointer<unsigned short>::value), void>::type emit<unsigned short>(clang::interp::Program&, std::__1::vector<char, std::__1::allocator<char> >&, unsigned short const&, bool&)
ByteCodeEmitter.cpp:std::__1::enable_if<!(std::is_pointer<unsigned int>::value), void>::type emit<unsigned int>(clang::interp::Program&, std::__1::vector<char, std::__1::allocator<char> >&, unsigned int const&, bool&)
Line
Count
Source
130
6
emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) {
131
6
  size_t Size = sizeof(Val);
132
6
  if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
133
0
    Success = false;
134
0
    return;
135
0
  }
136
137
6
  const char *Data = reinterpret_cast<const char *>(&Val);
138
6
  Code.insert(Code.end(), Data, Data + Size);
139
6
}
Unexecuted instantiation: ByteCodeEmitter.cpp:std::__1::enable_if<!(std::is_pointer<unsigned long long>::value), void>::type emit<unsigned long long>(clang::interp::Program&, std::__1::vector<char, std::__1::allocator<char> >&, unsigned long long const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:std::__1::enable_if<!(std::is_pointer<unsigned char>::value), void>::type emit<unsigned char>(clang::interp::Program&, std::__1::vector<char, std::__1::allocator<char> >&, unsigned char const&, bool&)
140
141
template <typename T>
142
static std::enable_if_t<std::is_pointer<T>::value, void>
143
0
emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) {
144
0
  size_t Size = sizeof(uint32_t);
145
0
  if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
146
0
    Success = false;
147
0
    return;
148
0
  }
149
150
0
  uint32_t ID = P.getOrCreateNativePointer(Val);
151
0
  const char *Data = reinterpret_cast<const char *>(&ID);
152
0
  Code.insert(Code.end(), Data, Data + Size);
153
0
}
Unexecuted instantiation: ByteCodeEmitter.cpp:std::__1::enable_if<std::is_pointer<clang::RecordDecl const*>::value, void>::type emit<clang::RecordDecl const*>(clang::interp::Program&, std::__1::vector<char, std::__1::allocator<char> >&, clang::RecordDecl const* const&, bool&)
Unexecuted instantiation: ByteCodeEmitter.cpp:std::__1::enable_if<std::is_pointer<clang::interp::Record::Field const*>::value, void>::type emit<clang::interp::Record::Field const*>(clang::interp::Program&, std::__1::vector<char, std::__1::allocator<char> >&, clang::interp::Record::Field const* const&, bool&)
154
155
template <typename... Tys>
156
14
bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
157
14
  bool Success = true;
158
159
  /// The opcode is followed by arguments. The source info is
160
  /// attached to the address after the opcode.
161
14
  emit(P, Code, Op, Success);
162
14
  if (SI)
163
11
    SrcMap.emplace_back(Code.size(), SI);
164
165
  /// The initializer list forces the expression to be evaluated
166
  /// for each argument in the variadic template, in order.
167
14
  (void)std::initializer_list<int>{(emit(P, Code, Args, Success), 0)...};
168
169
14
  return Success;
170
14
}
bool clang::interp::ByteCodeEmitter::emitOp<>(clang::interp::Opcode, clang::interp::SourceInfo const&)
Line
Count
Source
156
6
bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
157
6
  bool Success = true;
158
159
  /// The opcode is followed by arguments. The source info is
160
  /// attached to the address after the opcode.
161
6
  emit(P, Code, Op, Success);
162
6
  if (SI)
163
5
    SrcMap.emplace_back(Code.size(), SI);
164
165
  /// The initializer list forces the expression to be evaluated
166
  /// for each argument in the variadic template, in order.
167
6
  (void)std::initializer_list<int>{(emit(P, Code, Args, Success), 0)...};
168
169
6
  return Success;
170
6
}
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<bool>(clang::interp::Opcode, bool const&, clang::interp::SourceInfo const&)
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<short>(clang::interp::Opcode, short const&, clang::interp::SourceInfo const&)
bool clang::interp::ByteCodeEmitter::emitOp<int>(clang::interp::Opcode, int const&, clang::interp::SourceInfo const&)
Line
Count
Source
156
2
bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
157
2
  bool Success = true;
158
159
  /// The opcode is followed by arguments. The source info is
160
  /// attached to the address after the opcode.
161
2
  emit(P, Code, Op, Success);
162
2
  if (SI)
163
0
    SrcMap.emplace_back(Code.size(), SI);
164
165
  /// The initializer list forces the expression to be evaluated
166
  /// for each argument in the variadic template, in order.
167
2
  (void)std::initializer_list<int>{(emit(P, Code, Args, Success), 0)...};
168
169
2
  return Success;
170
2
}
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<long long>(clang::interp::Opcode, long long 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<unsigned short>(clang::interp::Opcode, unsigned short const&, clang::interp::SourceInfo const&)
bool clang::interp::ByteCodeEmitter::emitOp<unsigned int>(clang::interp::Opcode, unsigned int const&, clang::interp::SourceInfo const&)
Line
Count
Source
156
6
bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
157
6
  bool Success = true;
158
159
  /// The opcode is followed by arguments. The source info is
160
  /// attached to the address after the opcode.
161
6
  emit(P, Code, Op, Success);
162
6
  if (SI)
163
6
    SrcMap.emplace_back(Code.size(), SI);
164
165
  /// The initializer list forces the expression to be evaluated
166
  /// for each argument in the variadic template, in order.
167
6
  (void)std::initializer_list<int>{(emit(P, Code, Args, Success), 0)...};
168
169
6
  return Success;
170
6
}
Unexecuted instantiation: bool clang::interp::ByteCodeEmitter::emitOp<unsigned long long>(clang::interp::Opcode, unsigned long 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<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&)
171
172
0
bool ByteCodeEmitter::jumpTrue(const LabelTy &Label) {
173
0
  return emitJt(getOffset(Label), SourceInfo{});
174
0
}
175
176
1
bool ByteCodeEmitter::jumpFalse(const LabelTy &Label) {
177
1
  return emitJf(getOffset(Label), SourceInfo{});
178
1
}
179
180
1
bool ByteCodeEmitter::jump(const LabelTy &Label) {
181
1
  return emitJmp(getOffset(Label), SourceInfo{});
182
1
}
183
184
0
bool ByteCodeEmitter::fallthrough(const LabelTy &Label) {
185
0
  emitLabel(Label);
186
0
  return true;
187
0
}
188
189
//===----------------------------------------------------------------------===//
190
// Opcode emitters
191
//===----------------------------------------------------------------------===//
192
193
#define GET_LINK_IMPL
194
#include "Opcodes.inc"
195
#undef GET_LINK_IMPL