1//===--- InterpFrame.h - Call Frame implementation 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// Defines the class storing information about stack frames in the interpreter.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_CLANG_AST_INTERP_INTERPFRAME_H
14#define LLVM_CLANG_AST_INTERP_INTERPFRAME_H
15
16#include "Frame.h"
17#include "InterpBlock.h"
18#include "Pointer.h"
19
20namespace clang {
21namespace interp {
22class Function;
23class InterpState;
24class Pointer;
25
26/// Frame storing local variables.
27class InterpFrame final : public Frame {
28public:
29 /// Bottom Frame.
30 InterpFrame(InterpState &S);
31
32 /// Creates a new frame for a method call.
33 InterpFrame(InterpState &S, const Function *Func, InterpFrame *Caller,
34 CodePtr RetPC, unsigned ArgSize);
35
36 /// Creates a new frame with the values that make sense.
37 /// I.e., the caller is the current frame of S,
38 /// the This() pointer is the current Pointer on the top of S's stack,
39 /// and the RVO pointer is before that.
40 InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC,
41 unsigned VarArgSize = 0);
42
43 /// Destroys the frame, killing all live pointers to stack slots.
44 ~InterpFrame();
45
46 /// Returns the number of bytes needed to allocate an InterpFrame for the
47 /// given function.
48 static size_t allocSize(const Function *F) {
49 return sizeof(InterpFrame) + F->getFrameSize() +
50 (F->getArgSize() + (sizeof(Block) * F->getNumWrittenParams()));
51 }
52
53 std::string getName() const {
54 if (!Func)
55 return "Bottom frame";
56 return Func->getName();
57 }
58
59 static void free(InterpFrame *F) {
60 if (!F->isBottomFrame()) {
61 F->~InterpFrame();
62 delete[] reinterpret_cast<char *>(F);
63 } else {
64 F->~InterpFrame();
65 }
66 }
67
68 /// Invokes the destructors for a scope.
69 void destroy(unsigned Idx);
70 void initScope(unsigned Idx);
71 void destroyScopes();
72 void enableLocal(unsigned Idx);
73 bool isLocalEnabled(unsigned Idx) const {
74 return localInlineDesc(Offset: Idx)->IsActive;
75 }
76
77 /// Describes the frame with arguments for diagnostic purposes.
78 void describe(llvm::raw_ostream &OS) const override;
79
80 /// Returns the parent frame object.
81 Frame *getCaller() const override { return Caller; }
82
83 /// Returns the location of the call to the frame.
84 SourceRange getCallRange() const override;
85
86 /// Returns the caller.
87 const FunctionDecl *getCallee() const override;
88
89 /// Returns the current function.
90 const Function *getFunction() const { return Func; }
91
92#ifndef NDEBUG
93 /// Returns the offset on the stack at which the frame starts.
94 size_t getFrameOffset() const { return FrameOffset; }
95#endif
96
97 /// Returns the value of a local variable.
98 template <typename T> const T &getLocal(unsigned Offset) const {
99 return localRef<T>(Offset);
100 }
101
102 /// Mutates a local variable.
103 template <typename T> void setLocal(unsigned Offset, const T &Value) {
104 localRef<T>(Offset) = Value;
105 localInlineDesc(Offset)->IsInitialized = true;
106 localInlineDesc(Offset)->LifeState = Lifetime::Started;
107 }
108
109 /// Returns a pointer to a local variables.
110 Pointer getLocalPointer(unsigned Offset) const;
111 Block *getLocalBlock(unsigned Offset) const;
112
113 /// Returns the value of an argument.
114 template <typename T> const T &getParam(unsigned Index) const {
115 Block *ArgBlock = argBlock(Index);
116 if (!ArgBlock->isInitialized())
117 return stackRef<T>(Func->getParamDescriptor(Index).Offset);
118 return ArgBlock->deref<T>();
119 }
120
121 /// Mutates a local copy of a parameter.
122 template <typename T> void setParam(unsigned Index, const T &Value) {
123 argBlock(Index)->deref<T>() = Value;
124 }
125
126 /// Returns a pointer to an argument - lazily creates a block.
127 Pointer getParamPointer(unsigned Offset);
128
129 bool hasThisPointer() const { return FuncFlags & HasThisFlag; }
130
131 /// Returns the 'this' pointer.
132 const Pointer &getThis() const {
133 assert(hasThisPointer());
134 assert(!isBottomFrame());
135 return stackRef<Pointer>(Offset: (FuncFlags & HasRVOFlag) ? sizeof(Pointer) : 0);
136 }
137
138 /// Returns the RVO pointer, if the Function has one.
139 const Pointer &getRVOPtr() const {
140 assert(Func);
141 assert(Func->hasRVO());
142 assert(!isBottomFrame());
143 return stackRef<Pointer>(Offset: 0);
144 }
145
146 /// Checks if the frame is a root frame - return should quit the interpreter.
147 bool isRoot() const { return !Func; }
148
149 /// Returns the return address of the frame.
150 CodePtr getRetPC() const { return RetPC; }
151 /// Returns the return address of the opcode in the caller frame.
152 CodePtr getRetOpPC() const {
153 // All the Call ops we have take a Function* and an unsigned.
154 if (RetPC)
155 return RetPC - align(Size: sizeof(void *)) - align(Size: sizeof(unsigned));
156 return RetPC;
157 }
158
159 /// Map a location to a source.
160 SourceInfo getSource(CodePtr PC) const;
161 const Expr *getExpr(CodePtr PC) const { return getSource(PC).asExpr(); }
162 SourceLocation getLocation(CodePtr PC) const {
163 return getSource(PC).getLoc();
164 }
165 SourceRange getRange(CodePtr PC) const { return getSource(PC).getRange(); }
166
167 unsigned getDepth() const { return Depth; }
168 unsigned getArgSize() const { return ArgSize; }
169
170 bool isStdFunction() const;
171
172 bool isBottomFrame() const { return !Caller; }
173
174 void dump() const { dump(OS&: llvm::errs(), Indent: 0); }
175 void dump(llvm::raw_ostream &OS, unsigned Indent = 0) const;
176
177private:
178 static constexpr uint8_t HasRVOFlag = 1u << 0u;
179 static constexpr uint8_t HasThisFlag = 1u << 1u;
180
181 /// Returns an original argument from the stack.
182 template <typename T> const T &stackRef(unsigned Offset) const {
183 assert(Args);
184 return *reinterpret_cast<const T *>(Args - ArgSize + Offset);
185 }
186
187 /// Returns an offset to a local.
188 template <typename T> T &localRef(unsigned Offset) const {
189 return localBlock(Offset)->deref<T>();
190 }
191
192 /// Pointer to local memory.
193 char *locals() const {
194 return (reinterpret_cast<char *>(const_cast<InterpFrame *>(this))) +
195 align(Size: sizeof(InterpFrame));
196 }
197
198 /// Pointer to argument memory.
199 char *args() const {
200 return (reinterpret_cast<char *>(const_cast<InterpFrame *>(this))) +
201 sizeof(InterpFrame) + Func->getFrameSize();
202 }
203
204 /// Returns a pointer to a local's block.
205 Block *localBlock(unsigned Offset) const {
206 return reinterpret_cast<Block *>(locals() + Offset - sizeof(Block));
207 }
208
209 /// Returns a pointer to an argument block.
210 Block *argBlock(unsigned Index) const {
211 unsigned ByteOffset = Func->getParamDescriptor(Index).BlockOffset;
212 return reinterpret_cast<Block *>(args() + ByteOffset);
213 }
214
215 /// Returns the inline descriptor of the local.
216 InlineDescriptor *localInlineDesc(unsigned Offset) const {
217 return reinterpret_cast<InlineDescriptor *>(locals() + Offset);
218 }
219
220public:
221 /// The frame of the previous function.
222 InterpFrame *Caller;
223
224private:
225 /// Reference to the interpreter state.
226 InterpState &S;
227 /// Reference to the function being executed.
228 const Function *Func;
229 /// Return address.
230 CodePtr RetPC;
231 /// Pointer to the arguments in the callee's frame.
232 char *Args = nullptr;
233#ifndef NDEBUG
234 /// Offset on the stack at entry.
235 size_t FrameOffset = 0;
236#endif
237 /// The size of all the arguments.
238 const unsigned ArgSize;
239 /// Depth of this frame.
240 unsigned Depth;
241
242public:
243 unsigned MSVCConstexprAllowed = 0;
244
245private:
246 /// Relevant flags about the function.
247 uint8_t FuncFlags = 0;
248};
249
250} // namespace interp
251} // namespace clang
252
253#endif
254