/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 |