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