1//===--- InterpStack.cpp - Stack 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#include "InterpStack.h"
10#include "Boolean.h"
11#include "FixedPoint.h"
12#include "Floating.h"
13#include "Integral.h"
14#include "MemberPointer.h"
15#include "Pointer.h"
16#include <cassert>
17#include <cstdlib>
18
19using namespace clang;
20using namespace clang::interp;
21
22InterpStack::~InterpStack() {
23 if (Chunk && Chunk->Next)
24 std::free(ptr: Chunk->Next);
25 if (Chunk)
26 std::free(ptr: Chunk);
27 Chunk = nullptr;
28 StackSize = 0;
29#ifndef NDEBUG
30 ItemTypes.clear();
31#endif
32}
33
34// We keep the last chunk around to reuse.
35void InterpStack::clear() {
36 if (!Chunk)
37 return;
38
39 if (Chunk->Next)
40 std::free(ptr: Chunk->Next);
41
42 assert(Chunk);
43 StackSize = 0;
44#ifndef NDEBUG
45 ItemTypes.clear();
46#endif
47}
48
49void InterpStack::clearTo(size_t NewSize) {
50 assert(NewSize <= size());
51 size_t ToShrink = size() - NewSize;
52 if (ToShrink == 0)
53 return;
54
55 shrink(Size: ToShrink);
56 assert(size() == NewSize);
57}
58
59void *InterpStack::grow(size_t Size) {
60 assert(Size < ChunkSize - sizeof(StackChunk) && "Object too large");
61
62 if (!Chunk || sizeof(StackChunk) + Chunk->size() + Size > ChunkSize) {
63 if (Chunk && Chunk->Next) {
64 Chunk = Chunk->Next;
65 } else {
66 StackChunk *Next = new (std::malloc(size: ChunkSize)) StackChunk(Chunk);
67 if (Chunk)
68 Chunk->Next = Next;
69 Chunk = Next;
70 }
71 }
72
73 auto *Object = reinterpret_cast<void *>(Chunk->End);
74 Chunk->End += Size;
75 StackSize += Size;
76 return Object;
77}
78
79void *InterpStack::peekData(size_t Size) const {
80 assert(Chunk && "Stack is empty!");
81
82 StackChunk *Ptr = Chunk;
83 while (Size > Ptr->size()) {
84 Size -= Ptr->size();
85 Ptr = Ptr->Prev;
86 assert(Ptr && "Offset too large");
87 }
88
89 return reinterpret_cast<void *>(Ptr->End - Size);
90}
91
92void InterpStack::shrink(size_t Size) {
93 assert(Chunk && "Chunk is empty!");
94
95 while (Size > Chunk->size()) {
96 Size -= Chunk->size();
97 if (Chunk->Next) {
98 std::free(ptr: Chunk->Next);
99 Chunk->Next = nullptr;
100 }
101 Chunk->End = Chunk->start();
102 Chunk = Chunk->Prev;
103 assert(Chunk && "Offset too large");
104 }
105
106 Chunk->End -= Size;
107 StackSize -= Size;
108
109#ifndef NDEBUG
110 size_t TypesSize = 0;
111 for (PrimType T : ItemTypes)
112 TYPE_SWITCH(T, { TypesSize += aligned_size<T>(); });
113
114 size_t StackSize = size();
115 while (TypesSize > StackSize) {
116 TYPE_SWITCH(ItemTypes.back(), {
117 TypesSize -= aligned_size<T>();
118 ItemTypes.pop_back();
119 });
120 }
121 assert(TypesSize == StackSize);
122#endif
123}
124
125void InterpStack::dump() const {
126#ifndef NDEBUG
127 llvm::errs() << "Items: " << ItemTypes.size() << ". Size: " << size() << '\n';
128 if (ItemTypes.empty())
129 return;
130
131 size_t Index = 0;
132 size_t Offset = 0;
133
134 // The type of the item on the top of the stack is inserted to the back
135 // of the vector, so the iteration has to happen backwards.
136 for (auto TyIt = ItemTypes.rbegin(); TyIt != ItemTypes.rend(); ++TyIt) {
137 Offset += align(primSize(*TyIt));
138
139 llvm::errs() << Index << '/' << Offset << ": ";
140 TYPE_SWITCH(*TyIt, {
141 const T &V = peek<T>(Offset);
142 llvm::errs() << V;
143 });
144 llvm::errs() << '\n';
145
146 ++Index;
147 }
148#endif
149}
150