progs/fun_llvm.scala
changeset 646 2abd285c66d1
parent 645 30943d5491b6
child 648 36379b038438
equal deleted inserted replaced
645:30943d5491b6 646:2abd285c66d1
       
     1 // A Small Compiler for a Simple Functional Language
       
     2 // (includes an external lexer and parser)
       
     3 //
       
     4 // call with 
       
     5 //
       
     6 //     scala fun.scala fact
       
     7 //
       
     8 //     scala fun.scala defs
       
     9 //
       
    10 // this will generate a .j file and run the jasmin
       
    11 // assembler (installed at jvm/jasmin-2.4/jasmin.jar)
       
    12 // it runs the resulting JVM file twice for timing 
       
    13 // purposes.
       
    14 
       
    15 
       
    16 
       
    17 
       
    18 object Compiler {
       
    19 
       
    20 import java.io._  
       
    21 import scala.util._
       
    22 import scala.sys.process._
       
    23 
       
    24 // Abstract syntax trees for the Fun language
       
    25 abstract class Exp extends Serializable 
       
    26 abstract class BExp extends Serializable 
       
    27 abstract class Decl extends Serializable 
       
    28 
       
    29 case class Def(name: String, args: List[String], body: Exp) extends Decl
       
    30 case class Main(e: Exp) extends Decl
       
    31 
       
    32 case class Call(name: String, args: List[Exp]) extends Exp
       
    33 case class If(a: BExp, e1: Exp, e2: Exp) extends Exp
       
    34 case class Write(e: Exp) extends Exp
       
    35 case class Var(s: String) extends Exp
       
    36 case class Num(i: Int) extends Exp
       
    37 case class Aop(o: String, a1: Exp, a2: Exp) extends Exp
       
    38 case class Sequence(e1: Exp, e2: Exp) extends Exp
       
    39 case class Bop(o: String, a1: Exp, a2: Exp) extends BExp
       
    40 
       
    41 
       
    42 // compiler - built-in functions 
       
    43 // copied from http://www.ceng.metu.edu.tr/courses/ceng444/link/jvm-cpm.html
       
    44 //
       
    45 
       
    46 val library = """
       
    47 .class public XXX.XXX
       
    48 .super java/lang/Object
       
    49 
       
    50 .method public <init>()V
       
    51         aload_0
       
    52         invokenonvirtual java/lang/Object/<init>()V
       
    53         return
       
    54 .end method
       
    55 
       
    56 .method public static write(I)V 
       
    57         .limit locals 1 
       
    58         .limit stack 2 
       
    59         getstatic java/lang/System/out Ljava/io/PrintStream; 
       
    60         iload 0
       
    61         invokevirtual java/io/PrintStream/println(I)V 
       
    62         return 
       
    63 .end method
       
    64 
       
    65 """
       
    66 
       
    67 // for generating new labels
       
    68 var counter = -1
       
    69 
       
    70 def Fresh(x: String) = {
       
    71   counter += 1
       
    72   x ++ "_" ++ counter.toString()
       
    73 }
       
    74 
       
    75 // convenient string interpolations 
       
    76 // for instructions, labels and methods
       
    77 import scala.language.implicitConversions
       
    78 import scala.language.reflectiveCalls
       
    79 
       
    80 implicit def sring_inters(sc: StringContext) = new {
       
    81     def i(args: Any*): String = "   " ++ sc.s(args:_*) ++ "\n"
       
    82     def l(args: Any*): String = sc.s(args:_*) ++ ":\n"
       
    83     def m(args: Any*): String = sc.s(args:_*) ++ "\n"
       
    84 }
       
    85 
       
    86 
       
    87 type Env = Map[String, Int]
       
    88 
       
    89 // compile expressions
       
    90 def compile_exp(a: Exp, env : Env) : String = a match {
       
    91   case Num(i) => i"ldc $i"
       
    92   case Var(s) => i"iload ${env(s)}"
       
    93   case Aop("+", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"iadd"
       
    94   case Aop("-", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"isub"
       
    95   case Aop("*", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"imul"
       
    96   case Aop("/", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"idiv"
       
    97   case Aop("%", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"irem"
       
    98   case If(b, a1, a2) => {
       
    99     val if_else = Fresh("If_else")
       
   100     val if_end = Fresh("If_end")
       
   101     compile_bexp(b, env, if_else) ++
       
   102     compile_exp(a1, env) ++
       
   103     i"goto $if_end" ++
       
   104     l"$if_else" ++
       
   105     compile_exp(a2, env) ++
       
   106     l"$if_end"
       
   107   }
       
   108   case Call(name, args) => {
       
   109     val is = "I" * args.length
       
   110     args.map(a => compile_exp(a, env)).mkString ++
       
   111     i"invokestatic XXX/XXX/$name($is)I"
       
   112   }
       
   113   case Sequence(a1, a2) => {
       
   114     compile_exp(a1, env) ++ i"pop" ++ compile_exp(a2, env)
       
   115   }
       
   116   case Write(a1) => {
       
   117     compile_exp(a1, env) ++
       
   118     i"dup" ++
       
   119     i"invokestatic XXX/XXX/write(I)V"
       
   120   }
       
   121 }
       
   122 
       
   123 // compile boolean expressions
       
   124 def compile_bexp(b: BExp, env : Env, jmp: String) : String = b match {
       
   125   case Bop("==", a1, a2) => 
       
   126     compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"if_icmpne $jmp"
       
   127   case Bop("!=", a1, a2) => 
       
   128     compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"if_icmpeq $jmp"
       
   129   case Bop("<", a1, a2) => 
       
   130     compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"if_icmpge $jmp"
       
   131   case Bop("<=", a1, a2) => 
       
   132     compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"if_icmpgt $jmp"
       
   133 }
       
   134 
       
   135 // compile function for declarations and main
       
   136 def compile_decl(d: Decl) : String = d match {
       
   137   case Def(name, args, a) => { 
       
   138     val env = args.zipWithIndex.toMap
       
   139     val is = "I" * args.length
       
   140     m".method public static $name($is)I" ++
       
   141     m".limit locals ${args.length.toString}" ++
       
   142     m".limit stack ${1 + max_stack_exp(a)}" ++
       
   143     l"${name}_Start" ++   
       
   144     compile_exp(a, env) ++
       
   145     i"ireturn" ++
       
   146     m".end method\n"
       
   147   }
       
   148   case Main(a) => {
       
   149     m".method public static main([Ljava/lang/String;)V" ++
       
   150     m".limit locals 200" ++
       
   151     m".limit stack 200" ++
       
   152     compile_exp(a, Map()) ++
       
   153     i"invokestatic XXX/XXX/write(I)V" ++
       
   154     i"return" ++
       
   155     m".end method\n"
       
   156   }
       
   157 }
       
   158 
       
   159 // main compiler functions
       
   160 
       
   161 def time_needed[T](i: Int, code: => T) = {
       
   162   val start = System.nanoTime()
       
   163   for (j <- 1 to i) code
       
   164   val end = System.nanoTime()
       
   165   (end - start)/(i * 1.0e9)
       
   166 }
       
   167 
       
   168 def deserialise[T](fname: String) : Try[T] = {
       
   169   import scala.util.Using
       
   170   Using(new ObjectInputStream(new FileInputStream(fname))) {
       
   171     in => in.readObject.asInstanceOf[T]
       
   172   }
       
   173 }
       
   174 
       
   175 def compile(class_name: String) : String = {
       
   176   val ast = deserialise[List[Decl]](class_name ++ ".prs").getOrElse(Nil) 
       
   177   val instructions = ast.map(compile_decl).mkString
       
   178   (library + instructions).replaceAllLiterally("XXX", class_name)
       
   179 }
       
   180 
       
   181 def compile_to_file(class_name: String) = {
       
   182   val output = compile(class_name)
       
   183   scala.tools.nsc.io.File(s"${class_name}.j").writeAll(output)
       
   184 }
       
   185 
       
   186 def compile_and_run(class_name: String) : Unit = {
       
   187   compile_to_file(class_name)
       
   188   (s"java -jar jvm/jasmin-2.4/jasmin.jar ${class_name}.j").!!
       
   189   println("Time: " + time_needed(2, (s"java ${class_name}/${class_name}").!))
       
   190 }
       
   191 
       
   192 
       
   193 // some examples of .fun files
       
   194 //compile_to_file("fact")
       
   195 //compile_and_run("fact")
       
   196 //compile_and_run("defs")
       
   197 
       
   198 
       
   199 def main(args: Array[String]) = 
       
   200    compile_and_run(args(0))
       
   201 
       
   202 
       
   203 }