Skip to content

Commit b2e16eb

Browse files
committed
Make sure to scan everything in ASTInterpreter.
1 parent 23cfe7c commit b2e16eb

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

src/codegen/ast_interpreter.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ extern "C" Box* executeInnerAndSetupFrame(ASTInterpreter& interpreter, CFGBlock*
6565
*
6666
* All ASTInterpreter instances have to live on the stack because otherwise the GC won't scan the fields.
6767
*/
68-
class ASTInterpreter {
68+
class ASTInterpreter : public gc::StackObjectWithGCHandler {
6969
public:
7070
ASTInterpreter(CLFunction* clfunc, Box** vregs);
7171

@@ -187,6 +187,8 @@ class ASTInterpreter {
187187
void setFrameInfo(const FrameInfo* frame_info);
188188
void setGlobals(Box* globals);
189189

190+
void gc_visit(GCVisitor* visitor);
191+
190192
friend struct pyston::ASTInterpreterJitInterface;
191193
};
192194

@@ -228,6 +230,22 @@ void ASTInterpreter::setGlobals(Box* globals) {
228230
this->globals = globals;
229231
}
230232

233+
void ASTInterpreter::gc_visit(GCVisitor* visitor) {
234+
// Not all fields need to be visited - some should be already conservatively scanned due
235+
// to the object being in the stack.
236+
assert(!isValidGCObject(this));
237+
frame_info.gcVisit(visitor);
238+
239+
if (visitor->shouldVisitRedundants()) {
240+
visitor->visitRedundant(source_info->parent_module);
241+
for (auto& it : getSymVRegMap()) {
242+
if (vregs[it.second]) {
243+
visitor->visitRedundant(vregs[it.second]);
244+
}
245+
}
246+
}
247+
}
248+
231249
ASTInterpreter::ASTInterpreter(CLFunction* clfunc, Box** vregs)
232250
: current_block(0),
233251
current_inst(0),

src/gc/gc.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,45 @@ class GCAllocatedRuntime {
9393

9494
virtual void gc_visit(GCVisitor* visitor) = 0;
9595
};
96+
97+
// Situation: Sometimes, we allocate an object on the stack (e.g. ASTInterpreter) who fields may be pointers
98+
// to objects in the Pyston heap. These pointers need to be scanned by the GC. Since the GC scans the entire
99+
// stack conservatively, these fields will be scanned. However, it is also possible that the stack-allocated
100+
// object points to a non-Pyston heap object which contains pointers to Pyston heap objects. In that case, the
101+
// conservative scanner won't get to those pointers.
102+
//
103+
// As such, objects who contain pointers to pointers to Pyston heap objects need a GC handler function. To do
104+
// that, we allocate a small object in the Pyston heap that refers back to the stack object and scans it.
105+
class StackObjectWithGCHandler {
106+
class StackObjectHandler : public GCAllocatedRuntime {
107+
public:
108+
StackObjectWithGCHandler* stack_object;
109+
110+
StackObjectHandler(StackObjectWithGCHandler* obj) : stack_object(obj) {}
111+
~StackObjectHandler() = default;
112+
113+
virtual void gc_visit(GCVisitor* visitor) {
114+
assert(stack_object);
115+
stack_object->gc_visit(visitor);
116+
}
117+
};
118+
119+
// This is allocated in the Pyston heap and is conservatively scanned.
120+
StackObjectHandler* handler;
121+
122+
public:
123+
// GC visit function that will be called from the handler.
124+
virtual void gc_visit(GCVisitor* visitor) = 0;
125+
126+
StackObjectWithGCHandler() { handler = new StackObjectHandler(this); }
127+
128+
// We delete the handler manually instead of letting the GC take care of it. In theory,
129+
// the StackObjectHandler will become unreachable as long as the stack object is popped
130+
// off the stack. The danger is that if the StackObjectHandler happens to be pointed to
131+
// conservatively, it will be "reached" and its gc_visit function will be called.
132+
virtual ~StackObjectWithGCHandler() { delete handler; }
133+
};
134+
96135
} // namespace gc
97136
}
98137

0 commit comments

Comments
 (0)