Coverage Report

Created: 2022-05-17 06:19

/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
2
Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) {
23
  // Do not try to compile undefined functions.
24
2
  if (!F->isDefined(F) || (!F->hasBody() && 
F->willHaveBody()0
))
25
0
    return nullptr;
26
27
  // Set up argument indices.
28
2
  unsigned ParamOffset = 0;
29
2
  SmallVector<PrimType, 8> ParamTypes;
30
2
  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
2
  QualType Ty = F->getReturnType();
35
2
  if (!Ty->isVoidType() && 
!Ctx.classify(Ty)1
) {
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
2
  Function *Func = P.createFunction(F, ParamOffset, std::move(ParamTypes),
59
2
                                    std::move(ParamDescriptors));
60
  // Compile the function body.
61
2
  if (!F->isConstexpr() || !visitFunc(F)) {
62
    // Return a dummy function if compilation failed.
63
1
    if (BailLocation)
64
0
      return llvm::make_error<ByteCodeGenError>(*BailLocation);
65
1
    else
66
1
      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
2
}
80
81
1
Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) {
82
1
  NextLocalOffset += sizeof(Block);
83
1
  unsigned Location = NextLocalOffset;
84
1
  NextLocalOffset += align(D->getAllocSize());
85
1
  return {Location, D};
86
1
}
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
28
emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) {
131
28
  size_t Size = sizeof(Val);
132
28
  if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
133
0
    Success = false;
134
0
    return;
135
0
  }
136
137
28
  const char *Data = reinterpret_cast<const char *>(&Val);
138
28
  Code.insert(Code.end(), Data, Data + Size);
139
28
}
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
17
emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) {
131
17
  size_t Size = sizeof(Val);
132
17
  if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
133
0
    Success = false;
134
0
    return;
135
0
  }
136
137
17
  const char *Data = reinterpret_cast<const char *>(&Val);
138
17
  Code.insert(Code.end(), Data, Data + Size);
139
17
}
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
3
emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) {
131
3
  size_t Size = sizeof(Val);
132
3
  if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
133
0
    Success = false;
134
0
    return;
135
0
  }
136
137
3
  const char *Data = reinterpret_cast<const char *>(&Val);
138
3
  Code.insert(Code.end(), Data, Data + Size);
139
3
}
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
8
emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) {
131
8
  size_t Size = sizeof(Val);
132
8
  if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
133
0
    Success = false;
134
0
    return;
135
0
  }
136
137
8
  const char *Data = reinterpret_cast<const char *>(&Val);
138
8
  Code.insert(Code.end(), Data, Data + Size);
139
8
}
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
17
bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
157
17
  bool Success = true;
158
159
  /// The opcode is followed by arguments. The source info is
160
  /// attached to the address after the opcode.
161
17
  emit(P, Code, Op, Success);
162
17
  if (SI)
163
13
    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
17
  (void)std::initializer_list<int>{(emit(P, Code, Args, Success), 0)...};
168
169
17
  return Success;
170
17
}
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
3
bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
157
3
  bool Success = true;
158
159
  /// The opcode is followed by arguments. The source info is
160
  /// attached to the address after the opcode.
161
3
  emit(P, Code, Op, Success);
162
3
  if (SI)
163
1
    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
3
  (void)std::initializer_list<int>{(emit(P, Code, Args, Success), 0)...};
168
169
3
  return Success;
170
3
}
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
8
bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
157
8
  bool Success = true;
158
159
  /// The opcode is followed by arguments. The source info is
160
  /// attached to the address after the opcode.
161
8
  emit(P, Code, Op, Success);
162
8
  if (SI)
163
7
    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
8
  (void)std::initializer_list<int>{(emit(P, Code, Args, Success), 0)...};
168
169
8
  return Success;
170
8
}
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