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