Skip to content

Commit

Permalink
perf: Add support for tail call optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
mrunix00 committed Apr 20, 2024
1 parent dcbcea2 commit 14b2b63
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 0 deletions.
33 changes: 33 additions & 0 deletions src/bytecode/compiler/Optimizer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "Optimizer.h"
#include "bytecode/instructions/Jump.h"
#include "bytecode/instructions/Return.h"
#include "bytecode/instructions/StoreLocal.h"

bool Bytecode::Optimizer::is_tail_recursive(const Bytecode::Segment &segment, size_t id) {
const auto &instructions = segment.instructions;
return instructions.back()->type == Instruction::Return &&
instructions[instructions.size() - 2]->type == Instruction::Call &&
instructions[instructions.size() - 2]->params.ri_params.reg == id;
}

void Bytecode::Optimizer::optimize_tail_calls(Segment &segment) {
auto &instructions = segment.instructions;
const size_t number_of_args = instructions[instructions.size() - 2]
->params.ri_params.intermediate.asLambda();
const size_t old_number_of_instructions = instructions.size() - 1;
instructions.pop_back();// Remove the Return instruction
instructions.pop_back();// Remove the Call instruction
for (size_t i = number_of_args - 1; i != -1; i--)
instructions.push_back(new Bytecode::StoreLocal(i));
instructions.push_back(new Bytecode::Jump(0));
instructions.push_back(new Bytecode::Return());

// Update jump locations
for (auto &instruction: instructions) {
if ((instruction->type == Instruction::Jump ||
instruction->type == Instruction::CondJumpIfNot) &&
instruction->params.r_param.reg == old_number_of_instructions) {
instruction->params.r_param.reg = instructions.size() - 1;
}
}
}
7 changes: 7 additions & 0 deletions src/bytecode/compiler/Optimizer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once

#include "Segment.h"
namespace Bytecode::Optimizer {
bool is_tail_recursive(const Segment &segment, size_t id);
void optimize_tail_calls(Segment &segment);
}// namespace Bytecode::Optimizer
1 change: 1 addition & 0 deletions src/bytecode/instructions/Instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ namespace Bytecode {
GreaterThanRI,
MultiplyRI,
AddRR,
StoreLocal,
} type;
union params {
struct RI_Params {
Expand Down
20 changes: 20 additions & 0 deletions src/bytecode/instructions/StoreLocal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include "Instruction.h"
#include <string>

namespace Bytecode {
class StoreLocal final : public Instruction {
public:
explicit StoreLocal(const size_t reg) {
params.r_param = {reg};
type = InstructionType::StoreLocal;
}
void execute(Bytecode::VM &vm) override {
vm.call_stack.setLocal(params.r_param.reg, vm.program_stack.pop());
}
[[nodiscard]] std::string toString() const override {
return "StoreLocal $r" + std::to_string(params.r_param.reg);
}
};
}// namespace Bytecode
8 changes: 8 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "bytecode/compiler/Optimizer.h"
#include "bytecode/instructions/Return.h"
#include "bytecode/vm/Interpreter.h"
#include "exceptions/SyntaxError.h"
Expand Down Expand Up @@ -34,6 +35,13 @@ void exec_program(const std::string &program, struct options opts) {

}

for (size_t i = 1; i < compiled_bytecode.segments.size(); i++) {
const auto segment = compiled_bytecode.segments[i];
if (Bytecode::Optimizer::is_tail_recursive(*segment, i))
Bytecode::Optimizer::optimize_tail_calls(*segment);
}


if (opts.dumpBytecode) {
for (size_t i = 0; i < compiled_bytecode.segments.size(); i++) {
std::cout << ':' << i << '\n';
Expand Down

0 comments on commit 14b2b63

Please sign in to comment.