Skip to content

Commit 381d7d0

Browse files
committed
Remove duplicate stack size
1 parent b604069 commit 381d7d0

2 files changed

Lines changed: 52 additions & 48 deletions

File tree

src/vm.cpp

Lines changed: 48 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ namespace {
1111
ptrdiff_t get_jump_offset(uint8_t high_byte, uint8_t low_byte) {
1212
// NOTE: we have to be really careful here about how we do the static_cast!
1313
// The high and low bytes are encoded including the sign information as the
14-
// most significant bit of the high byte. So we need to combine them into a single
15-
// int16_t FIRST, so that the sign information is in the right place (i.e. the most
16-
// significant bit of the int16_t), and then only convert to ptrdiff_t.
14+
// most significant bit of the high byte. So we need to combine them into a
15+
// single int16_t FIRST, so that the sign information is in the right place
16+
// (i.e. the most significant bit of the int16_t), and then only convert to
17+
// ptrdiff_t.
1718
//
1819
// If we did
1920
// static_cast<ptrdiff_t>(high_byte) << 8 | low_byte
2021
//
21-
// the sign bit would not be in the most significant bit of the ptrdiff_t, and so
22-
// we would end up with a completely different number.
22+
// the sign bit would not be in the most significant bit of the ptrdiff_t, and
23+
// so we would end up with a completely different number.
2324
int16_t jump_offset = (static_cast<int16_t>(high_byte) << 8) | low_byte;
2425
return static_cast<ptrdiff_t>(jump_offset);
2526
}
@@ -60,14 +61,19 @@ lox::InterpretResult interpret(std::string_view source) {
6061
}
6162

6263
VM::VM(std::unique_ptr<scanner::Scanner> scanner, GC gc)
63-
: stack_size(0), _gc(std::move(gc)), parser() {
64+
: _gc(std::move(gc)), parser() {
6465
call_frames.reserve(MAX_CALL_FRAMES);
66+
// NOTE: By reserving memory here we avoid having to reallocate when we call
67+
// push_back. That means that we don't need to do what the book does by having
68+
// stack_pop decrement a pointer: we can just make stack_pop call pop_back()
69+
// on the stack. That allows us to keep track of the stack size using
70+
// stack.size() rather than a separate variable, which can get out of sync.
6571
stack.reserve(MAX_STACK_SIZE);
6672
auto top_level_fn = _gc.alloc<ObjFunction>("#toplevel#", size_t(0));
6773
parser = std::make_unique<Parser>(std::move(scanner), top_level_fn, _gc);
6874

6975
// Aggressive GC: run it every time we allocate
70-
_gc.set_alloc_callback([this]() { this->gc(); });
76+
_gc.set_alloc_callback([this]() { this->maybe_gc(); });
7177
}
7278

7379
lox::InterpretResult VM::invoke_toplevel() {
@@ -101,43 +107,39 @@ lox::Value VM::read_constant() {
101107
}
102108

103109
VM& VM::stack_reset() {
104-
stack_size = 0;
110+
stack.clear();
105111
return *this;
106112
}
107113

108114
VM& VM::stack_push(const lox::Value& value) {
109-
if (stack_size >= MAX_STACK_SIZE) {
115+
if (stack.size() >= MAX_STACK_SIZE) {
110116
error("stack overflow");
111117
}
112-
if (stack_size >= stack.size()) {
113-
stack.push_back(value);
114-
} else {
115-
stack[stack_size] = value;
116-
}
117-
stack_size++;
118+
stack.push_back(value);
118119
return *this;
119120
}
120121

121122
lox::Value* VM::stack_top_address() {
122-
if (stack_size == 0) {
123+
if (stack.empty()) {
123124
error("stack_top_address: stack underflow");
124125
}
125-
return &stack[stack_size - 1];
126+
return &stack.back();
126127
}
127128

128129
lox::Value VM::stack_pop() {
129-
if (stack_size == 0) {
130+
if (stack.empty()) {
130131
error("stack_pop: stack underflow");
131132
}
132-
stack_size--;
133-
return stack[stack_size];
133+
Value value = std::move(stack.back());
134+
stack.pop_back();
135+
return value;
134136
}
135137

136138
lox::Value VM::stack_peek() {
137-
if (stack_size == 0) {
139+
if (stack.empty()) {
138140
error("stack_peek: stack underflow");
139141
}
140-
return stack[stack_size - 1];
142+
return stack.back();
141143
}
142144

143145
std::string VM::read_global_name() {
@@ -149,12 +151,14 @@ std::string VM::read_global_name() {
149151

150152
void VM::close_upvalues_after(Value* addr) {
151153
for (auto it = open_upvalues.begin(); it != open_upvalues.end();) {
152-
// `it` is an iterator over a vector of ObjUpvalue*, so `it` itself is
153-
// ObjUpvalue**
154+
// `it` iterates over a vector of ObjUpvalue*, so we have to dereference
155+
// it to get the ObjUpvalue* itself.
154156
auto upvalue_ptr = *it;
155157
if (upvalue_ptr->location >= addr) {
158+
#ifdef LOX_DEBUG
156159
std::cerr << "closing upvalue at location " << upvalue_ptr->location
157160
<< " with value " << *(upvalue_ptr->location) << "\n";
161+
#endif
158162
upvalue_ptr->closed = *(upvalue_ptr->location);
159163
upvalue_ptr->location = &(upvalue_ptr->closed);
160164
it = open_upvalues.erase(it);
@@ -166,21 +170,22 @@ void VM::close_upvalues_after(Value* addr) {
166170

167171
lox::Value
168172
VM::stack_modify_top(const std::function<lox::Value(const lox::Value&)>& op) {
169-
if (stack_size == 0) {
173+
if (stack.empty()) {
170174
error("stack_modify_top: stack underflow");
171175
}
172-
stack[stack_size - 1] = op(stack[stack_size - 1]);
173-
return stack[stack_size - 1];
176+
Value& value = stack.back();
177+
value = op(value);
178+
return value;
174179
}
175180

176181
std::ostream& VM::stack_dump(std::ostream& out) const {
177-
if (stack_size == 0) {
182+
if (stack.empty()) {
178183
out << " <empty stack>\n";
179184
return out;
180185
}
181186
out << " ";
182-
for (size_t i = 0; i < stack_size; i++) {
183-
out << "[" << stack[i] << "]";
187+
for (const auto& v : stack) {
188+
out << "[" << v << "]";
184189
}
185190
out << "\n";
186191
return out;
@@ -205,7 +210,7 @@ void VM::call(ObjClosure* callee, size_t arg_count) {
205210
std::to_string(callee->function->arity) +
206211
" arguments but got " + std::to_string(arg_count));
207212
}
208-
size_t stack_start = stack_size - arg_count - 1;
213+
size_t stack_start = stack.size() - arg_count - 1;
209214
CallFrame new_frame(callee, 0, stack_start);
210215
call_frames.push_back(new_frame);
211216
}
@@ -319,6 +324,7 @@ InterpretResult VM::run() {
319324
break;
320325
}
321326
case OpCode::CLOSE_UPVALUE: {
327+
// This effectively only closes the upvalue that's at the top of the stack.
322328
close_upvalues_after(stack_top_address());
323329
stack_pop();
324330
break;
@@ -428,8 +434,8 @@ InterpretResult VM::run() {
428434
}
429435
case OpCode::SET_LOCAL: {
430436
uint8_t local_index = read_byte();
431-
if (static_cast<size_t>(local_index) > stack_size) {
432-
std::cerr << "stack_size=" << stack_size
437+
if (static_cast<size_t>(local_index) > stack.size()) {
438+
std::cerr << "stack_size=" << stack.size()
433439
<< ", local_index=" << +local_index << "\n";
434440
error("SET_LOCAL: invalid local variable index");
435441
}
@@ -438,8 +444,8 @@ InterpretResult VM::run() {
438444
}
439445
case OpCode::GET_LOCAL: {
440446
uint8_t local_index = read_byte();
441-
if (static_cast<size_t>(local_index) > stack_size) {
442-
std::cerr << "stack_size=" << stack_size
447+
if (static_cast<size_t>(local_index) > stack.size()) {
448+
std::cerr << "stack_size=" << stack.size()
443449
<< ", local_index=" << +local_index << "\n";
444450
error("GET_LOCAL: invalid local variable index");
445451
}
@@ -473,7 +479,7 @@ InterpretResult VM::run() {
473479
uint8_t nargs = read_byte();
474480
// The function pointer should have been pushed to the stack before
475481
// the arguments.
476-
auto maybe_objptr = stack[stack_size - 1 - nargs];
482+
auto maybe_objptr = stack[stack.size() - 1 - nargs];
477483
if (!std::holds_alternative<Obj*>(maybe_objptr)) {
478484
throw std::runtime_error("objptr was not a pointer to Obj");
479485
}
@@ -485,9 +491,9 @@ InterpretResult VM::run() {
485491
auto native_fnptr = dynamic_cast<ObjNativeFunction*>(maybe_closptr);
486492
if (native_fnptr != nullptr) {
487493
Value retval =
488-
native_fnptr->call(nargs, &stack[stack_size - nargs]);
494+
native_fnptr->call(nargs, &stack[stack.size() - nargs]);
489495
// Pop the function and its arguments off the stack.
490-
stack_size -= (nargs + 1);
496+
stack.resize(stack.size() - nargs - 1);
491497
// Push the return value onto the stack.
492498
stack_push(retval);
493499
} else {
@@ -510,7 +516,7 @@ InterpretResult VM::run() {
510516
} else {
511517
// Reset the VM's state to where it was before it entered the current
512518
// call.
513-
stack_size = current_frame().stack_start;
519+
stack.resize(current_frame().stack_start);
514520
stack_push(retval);
515521
call_frames.pop_back();
516522
}
@@ -538,12 +544,12 @@ InterpretResult VM::run() {
538544
}
539545
}
540546

541-
void VM::gc() {
547+
void VM::maybe_gc() {
542548
if (_gc.should_gc()) {
543549

544550
// Mark roots as grey.
545-
for (size_t i = 0; i < stack_size; ++i) {
546-
_gc.mark_as_grey(stack[i]);
551+
for (const auto& v : stack) {
552+
_gc.mark_as_grey(v);
547553
}
548554
for (const auto& [_, global_value] : globals) {
549555
_gc.mark_as_grey(global_value);

src/vm.hpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ class VM {
6161
// NOTE: We use std::vector here instead of std::stack because the latter does
6262
// not provide random access (you can only access the top element).
6363
std::vector<lox::Value> stack;
64-
// This is zero-indexed.
65-
size_t stack_size;
6664
GC _gc;
6765
// maps from the name of a global variable to its value
6866
std::unordered_map<std::string, lox::Value> globals;
@@ -77,30 +75,30 @@ class VM {
7775
void close_upvalues_after(Value* addr);
7876

7977
// Run garbage collection
80-
void gc();
78+
void maybe_gc();
8179

8280
lox::Value get_local_variable(size_t local_index) {
8381
// Because local_index is an index into the current function's locals,
8482
// which may not start at zero (but instead start at
8583
// current_frame().stack_start), we need to offset it by stack_start.
8684
size_t stack_index = current_frame().stack_start + local_index;
87-
if (stack_index >= stack_size) {
85+
if (stack_index >= stack.size()) {
8886
error("get_local_variable: invalid local variable index");
8987
}
9088
return stack[stack_index];
9189
}
9290

9391
lox::Value* get_local_variable_address(size_t local_index) {
9492
size_t stack_index = current_frame().stack_start + local_index;
95-
if (stack_index >= stack_size) {
93+
if (stack_index >= stack.size()) {
9694
error("get_local_variable: invalid local variable index");
9795
}
9896
return &stack[current_frame().stack_start + local_index];
9997
}
10098

10199
void set_local_variable(size_t local_index, const lox::Value& value) {
102100
size_t stack_index = current_frame().stack_start + local_index;
103-
if (stack_index >= stack_size) {
101+
if (stack_index >= stack.size()) {
104102
error("set_local_variable: invalid local variable index");
105103
}
106104
stack[stack_index] = value;

0 commit comments

Comments
 (0)