Introduction

Before you start

You are reading this book because you want to write a compiler, do you? You want to understand how a debugger works, do you? If you say yes, this book is for you. The book is self contained in a way such that you do not need to know anything about compiler before you can follow the chapters, but you do need to have some programming experience. Code samples provided are in C++, so familarity of C++ is assumed.

Overall structure

This book is divided into two parts. In the first part, we will focus on building a compiler. To keep it short, the compiler will be very limited. It is intentional to keep it very simple. In fact, to make life easy, it does not generate real machine code. It generates code that targets a simple virtual machine. We will also implement that virtual machine in part one to show that the compiler does generate correct code.

A discerning reader should find no difficulty to extend it to support more, but doing so would obscure our goal, which is to build debugging on top of it. Initially, the compiler will not have any support of debugging, this is also intentionally done so that we know what support we will need to add to an existing compiler to make it debuggable.

In the second part, we will start building the debugger. In order to do so, we will make changes to the virtual machine to support debugging. Once we have done with that, we will be able to do low level debugging such as inspecting register and setting breakpoints on address.

To debug at the source level, we will also make changes to compiler to generate the symbols. We will be able to make sense of the call stack, step through source lines, and read argument and local variables with names.

Excited already? Here is an overview on what you will be able to achieve.

Compiler

Your compiler will be able to compile this program:

function fib(a) {
  if (a == 0) {
    return 1;
  } else {
    if (a == 1) {
      return 1;
    } else {
      return fib(a - 1) + fib(a - 2);
    }
  }
}
function main() {
  print(fib(10));
}

Virtual Machine

Your virtual machine will have a separated instruction memory and stack memory. It will have six registers, two special purpose ones and four general purpose ones. It have two special purpose register IP and SP, and also 4 general purpose registers. Here is the set of instructions that the virtual machine has:

load destination_register, address
load_immediate destination_register, value
compare destination_register, operand_register_1, operand_register_2
store source_register, address
branch_on_zero operand_register, address
branch address
plus destination_register, operand_register_1, operand_register_2
minus destination_register, operand_register_1, operand_register_2
call address
return
push stack_space_to_reserve
pop stack_space_to_reserve
print
break

Debugger

The debugger is the key part of this book, your debugger will have these low level debugging capabilities:

  • Set breakpoint at address
  • Read/Write register
  • Read/Write stack memory
  • Step one instruction

As well as these source level debugging capabilities:

  • Show call stack
  • Set breakpoint on line number
  • Step over lines
  • Step into a call
  • Step out of a call
  • Show current statement

About the project

The full source code for this project is available on GitHub. For the impatient readers, you can just go there, clone the repository, and do whatever you want with it, but that is not the purpose of this book. To maximize your learning, the best way is to actually do it yourself, and the book will guide you step by step to get there.

Moving forward

In the next chapter, we will talk about the structure of the compiler.

results matching ""

    No results matching ""