Register Machine Model in Scala
Contrast this to stack machine in VM for Expressions
sealed abstract class Value case class Immediate(const : Int) extends Value sealed abstract class Address extends Value case class Register(r : Int) extends Address case class Memory(a : Int) extends Address case class Indirect(r : Int) extends Address case class IndirectOffset(r : Int, offset : Int) extends Address sealed abstract class ArithOp case object Mul extends ArithOp case object Add extends ArithOp case object Sub extends ArithOp sealed abstract class Instruction case class Quad(destination : Address, op : ArithOp, source1 : Value, source2 : Value) extends Instruction /* Possible restrictions to above, on various architectures: - source1,source2 must be registers (use Move if needed) - destination = source1 - indirect addressing only for some registers - register 0 always stores constant 0 */ case class Move(destination : Address, source : Value) extends Instruction case class BranchIfZero(r : Int, pcOffset : Int) extends Instruction case class BranchIndirect(r : Int) extends Instruction case class JumpSubroutine(pcOffset : Int) extends Instruction class Computer(var code : Array[Instruction], var pc : Int, // program counter (current instruction) var reg : Array[Int], // registers var mem : Array[Int] // memory ) { def compute(op : ArithOp, x : Int, y : Int) : Int = op match { case Mul => x * y case Add => x + y case Sub => x - y } def load(v : Value) : Int = v match { case Immediate(const) => const case Register(r) => reg(r) case Memory(a) => mem(a) case Indirect(r) => mem(reg(r)) case IndirectOffset(r,offset) => mem(reg(r) + offset) } def store(a : Address, x : Int) = a match { case Register(r) => { reg(r) = x } case Memory(a) => { mem(a) = x } case Indirect(r) => { mem(reg(r)) = x } case IndirectOffset(r,offset) => { mem(reg(r) + offset) = x } } def step = code(pc) match { case Quad(destination,op,source1,source2) => { store(destination, compute(op, load(source1), load(source2))) pc = pc + 1 } case Move(destination, source) => { store(destination, load(source)) pc = pc + 1 } case BranchIfZero(r, pcOffset) => { if (reg(r)==0) pc = pc + pcOffset else pc = pc + 1 } case BranchIndirect(r) => { pc = reg(r) } case JumpSubroutine(pcOffset) => { reg(15) = pc + 1 pc = pc + pcOffset } } def run = { while (pc < code.length) step } } object Test { /* Memory layout: 0 : ... 100 : x (initially 7) 104 : y (initially 2) 108 : z (initially 6) Execute statement: x = x*y + y*z + x*z Result should be 68. */ val xAddr = 100 val yAddr = 104 val zAddr = 108 def initMem() : Array[Int] = { val memory = new Array[Int](2048) memory(xAddr) = 7 memory(yAddr) = 2 memory(zAddr) = 6 memory } def main(args : Array[String]) = { val registers = new Array[Int](32) // heavy use of memory addressing val codeMemory : Array[Instruction] = List( Quad(Register(1), Mul, Memory(xAddr), Memory(yAddr)), Quad(Register(2), Mul, Memory(yAddr), Memory(zAddr)), Quad(Register(1), Add, Register(1), Register(2)), Quad(Register(2), Mul, Memory(xAddr), Memory(zAddr)), Quad(Memory(xAddr), Add, Register(1), Register(2)) ).toArray var computer = new Computer(codeMemory,0,registers, initMem) computer.run println(computer.mem(xAddr)) // all arithmetic operations use registers, // always fresh register unless value stored previously val codeManyReg : Array[Instruction] = List( Move(Register(1), Memory(xAddr)), Move(Register(2), Memory(yAddr)), Move(Register(3), Memory(zAddr)), Quad(Register(4), Mul, Register(1), Register(2)), Quad(Register(5), Mul, Register(2), Register(3)), Quad(Register(6), Mul, Register(1), Register(3)), Quad(Register(7), Add, Register(4), Register(5)), Quad(Register(8), Add, Register(7), Register(6)), Move(Memory(xAddr), Register(8)) ).toArray computer = new Computer(codeManyReg,0,registers, initMem) computer.run println(computer.mem(xAddr)) // all arithmetic operations use registers, // minimizing memory traffic and registers val codeSmart : Array[Instruction] = List( Move(Register(1), Memory(xAddr)), Move(Register(2), Memory(yAddr)), Move(Register(3), Memory(zAddr)), Quad(Register(4), Mul, Register(1), Register(2)), Quad(Register(5), Mul, Register(2), Register(3)), Quad(Register(4), Add, Register(4), Register(5)), Quad(Register(5), Mul, Register(1), Register(3)), Quad(Register(4), Add, Register(4), Register(5)), Move(Memory(xAddr), Register(4)) ).toArray computer = new Computer(codeSmart,0,registers, initMem) computer.run println(computer.mem(xAddr)) // Question: can we do it with only one memory load for each x,y,z // but with even fewer registers used? val codeFourReg : Array[Instruction] = List( Move(Register(1), Memory(xAddr)), Move(Register(2), Memory(yAddr)), Move(Register(3), Memory(zAddr)), Quad(Register(4), Mul, Register(1), Register(2)), Quad(Register(2), Mul, Register(2), Register(3)), Quad(Register(4), Add, Register(4), Register(2)), Quad(Register(1), Mul, Register(1), Register(3)), Quad(Register(4), Add, Register(4), Register(1)), Move(Memory(xAddr), Register(4)) ).toArray computer = new Computer(codeFourReg,0,registers, initMem) computer.run println(computer.mem(xAddr)) } }