diff --git a/src/main/scala/leon/codegen/CodeGenPhase.scala b/src/main/scala/leon/codegen/CodeGenPhase.scala index 5c6c0ef16dabd3dd2b1ddafdb7d0b54fffd36f88..bfd072b88b39650a60fcc14f2f3794e40a2f93d0 100644 --- a/src/main/scala/leon/codegen/CodeGenPhase.scala +++ b/src/main/scala/leon/codegen/CodeGenPhase.scala @@ -20,24 +20,35 @@ object CodeGenPhase extends LeonPhase[Program,CompilationResult] { val cName = programToClassName(p) val cf = new ClassFile(cName, None) - cf.setFlags( - (cf.getFlags | CLASS_ACC_PUBLIC | CLASS_ACC_FINAL).asInstanceOf[U2] - ) + cf.addDefaultConstructor + + cf.setFlags(( + CLASS_ACC_SUPER | + CLASS_ACC_PUBLIC | + CLASS_ACC_FINAL + ).asInstanceOf[U2]) // This assumes that all functions of a given program get compiled // as methods of a single class file. for(funDef <- p.definedFunctions; - (_,mn) <- env.funDefToMethod(funDef)) { + (_,mn,_) <- env.funDefToMethod(funDef)) { val m = cf.addMethod( typeToJVM(funDef.returnType), mn, funDef.args.map(a => typeToJVM(a.tpe)) : _* ) + m.setFlags(( + METHOD_ACC_PUBLIC | + METHOD_ACC_FINAL | + METHOD_ACC_STATIC + ).asInstanceOf[U2]) CodeGeneration.compileFunDef(funDef, m.codeHandler) } + cf.writeToFile(cName + ".class") + CompilationResult(successful = true) } } diff --git a/src/main/scala/leon/codegen/CodeGeneration.scala b/src/main/scala/leon/codegen/CodeGeneration.scala index 3fd57a438a86c5bf2ac669f7de0ff19ce8041188..7c55ac76c0de58ceba686919ad0c808d13a4d144 100644 --- a/src/main/scala/leon/codegen/CodeGeneration.scala +++ b/src/main/scala/leon/codegen/CodeGeneration.scala @@ -22,8 +22,8 @@ object CodeGeneration { // Assumes the CodeHandler has never received any bytecode. // Generates method body, and freezes the handler at the end. def compileFunDef(funDef : FunDef, ch : CodeHandler)(implicit env : CompilationEnvironment) { - // TODO, change environment to include args. - mkExpr(funDef.getBody, ch) + val newMapping = funDef.args.map(_.id).zipWithIndex.toMap + mkExpr(funDef.getBody, ch)(env.withVars(newMapping)) funDef.returnType match { case Int32Type | BooleanType => @@ -38,9 +38,52 @@ object CodeGeneration { private def mkExpr(e : Expr, ch : CodeHandler)(implicit env : CompilationEnvironment) { e match { - case IntLiteral(v) => ch << Ldc(v) - case BooleanLiteral(true) => ch << Ldc(1) - case BooleanLiteral(false) => ch << Ldc(0) + case Variable(id) => + val slot = slotFor(id) + val instr = id.getType match { + case Int32Type | BooleanType => ILoad(slot) + case _ => ALoad(slot) + } + ch << instr + + case Let(i,d,b) => + mkExpr(d, ch) + val slot = ch.getFreshVar + val instr = i.getType match { + case Int32Type | BooleanType => IStore(slot) + case _ => AStore(slot) + } + ch << instr + mkExpr(b, ch)(env.withVars(Map(i -> slot))) + + case IntLiteral(v) => + ch << Ldc(v) + + case BooleanLiteral(true) => + ch << Ldc(1) + + case BooleanLiteral(false) => + ch << Ldc(0) + + case IfExpr(c, t, e) => + val tl = ch.getFreshLabel("then") + val el = ch.getFreshLabel("else") + val al = ch.getFreshLabel("after") + mkBranch(c, tl, el, ch) + ch << Label(tl) + mkExpr(t, ch) + ch << Goto(al) << Label(el) + mkExpr(e, ch) + ch << Label(al) + + case FunctionInvocation(fd, as) => + val (cn, mn, ms) = env.funDefToMethod(fd).getOrElse { + throw CompilationException("Unknown method : " + fd.id) + } + for(a <- as) { + mkExpr(a, ch) + } + ch << InvokeStatic(cn, mn, ms) case Plus(l, r) => mkExpr(l, ch) @@ -52,10 +95,71 @@ object CodeGeneration { mkExpr(r, ch) ch << ISUB + case Times(l, r) => + mkExpr(l, ch) + mkExpr(r, ch) + ch << IMUL + case UMinus(e) => mkExpr(Minus(IntLiteral(0), e), ch) - case _ => throw CompilationException("Unsupported expr : " + e) + case _ => throw CompilationException("Unsupported expr. : " + e) + } + } + + private def mkBranch(cond : Expr, then : String, elze : String, ch : CodeHandler)(implicit env : CompilationEnvironment) { + cond match { + case BooleanLiteral(true) => + ch << Goto(then) + + case BooleanLiteral(false) => + ch << Goto(elze) + + case And(es) => + val fl = ch.getFreshLabel("andnext") + mkBranch(es.head, fl, elze, ch) + ch << Label(fl) + mkBranch(And(es.tail), then, elze, ch) + + case Or(es) => + val fl = ch.getFreshLabel("ornext") + mkBranch(es.head, then, fl, ch) + ch << Label(fl) + mkBranch(Or(es.tail), then, elze, ch) + + case Not(c) => + mkBranch(c, elze, then, ch) + + case Variable(b) => + ch << ILoad(slotFor(b)) << IfEq(elze) << Goto(then) + + case LessThan(l,r) => + mkExpr(l, ch) + mkExpr(r, ch) + ch << If_ICmpLt(then) << Goto(elze) + + case GreaterThan(l,r) => + mkExpr(l, ch) + mkExpr(r, ch) + ch << If_ICmpGt(then) << Goto(elze) + + case LessEquals(l,r) => + mkExpr(l, ch) + mkExpr(r, ch) + ch << If_ICmpLe(then) << Goto(elze) + + case GreaterEquals(l,r) => + mkExpr(l, ch) + mkExpr(r, ch) + ch << If_ICmpGe(then) << Goto(elze) + + case _ => throw CompilationException("Unsupported cond. expr. : " + cond) + } + } + + private def slotFor(id : Identifier)(implicit env : CompilationEnvironment) : Int = { + env.varToLocal(id).getOrElse { + throw CompilationException("Unknown variable : " + id) } } } diff --git a/src/main/scala/leon/codegen/CompilationEnvironment.scala b/src/main/scala/leon/codegen/CompilationEnvironment.scala index 1eb9514e1a81e8b615928f6b9f5da0a10d0b4557..2bec36229355f8f1c82ba171336df49314ea219c 100644 --- a/src/main/scala/leon/codegen/CompilationEnvironment.scala +++ b/src/main/scala/leon/codegen/CompilationEnvironment.scala @@ -5,23 +5,53 @@ import purescala.Common._ import purescala.Definitions._ abstract class CompilationEnvironment() { + self => // Should contain: // - a mapping of function defs to class + method name // - a mapping of class defs to class names // - a mapping of class fields to fields - def funDefToMethod(funDef : FunDef) : Option[(String,String)] + def funDefToMethod(funDef : FunDef) : Option[(String,String,String)] + + def varToLocal(v : Identifier) : Option[Int] + + /** Augment the environment with new local var. mappings. */ + def withVars(pairs : Map[Identifier,Int]) = { + new CompilationEnvironment { + def funDefToMethod(funDef : FunDef) = self.funDefToMethod(funDef) + def varToLocal(v : Identifier) = pairs.get(v).orElse(self.varToLocal(v)) + } + } } object CompilationEnvironment { + + lazy val empty = new CompilationEnvironment { + def funDefToMethod(funDef : FunDef) = None + def varToLocal(v : Identifier) = None + } + def fromProgram(p : Program) : CompilationEnvironment = { + import CodeGeneration.typeToJVM + + // This should change: it should contain the case classes before + // we go and generate function signatures. + implicit val env = empty + val className = CodeGeneration.programToClassName(p) val fs = p.definedFunctions.filter(_.hasImplementation) - val fPairs : Map[FunDef,String] = fs.map(fd => (fd -> fd.id.uniqueName)).toMap + + val fMap : Map[FunDef,(String,String,String)] = (fs.map { fd => + val sig = "(" + fd.args.map(a => typeToJVM(a.tpe)).mkString("") + ")" + typeToJVM(fd.returnType) + + fd -> (className, fd.id.uniqueName, sig) + }).toMap new CompilationEnvironment { - def funDefToMethod(funDef : FunDef) = fPairs.get(funDef).map(n => (className, n)) + def funDefToMethod(funDef : FunDef) = fMap.get(funDef) + + def varToLocal(v : Identifier) = None } } } diff --git a/src/test/resources/regression/codegen/purescala/Prog001.scala b/src/test/resources/regression/codegen/purescala/Prog001.scala index c0edc779fbcba95cec98102a9a4699fbbc12ef80..a7f3b23356fe439747cf3f3d6fc9f4a2dc3216a3 100644 --- a/src/test/resources/regression/codegen/purescala/Prog001.scala +++ b/src/test/resources/regression/codegen/purescala/Prog001.scala @@ -1,5 +1,18 @@ object Prog001 { def fortyTwo() = 42 - // def plus(x : Int, y : Int) = x + y + def plus(x : Int, y : Int) = x + y + + def double(x : Int) : Int = { + val a = x + a + a + } + + // def implies(a : Boolean, b : Boolean) : Boolean = !a || b + + def abs(x : Int) : Int = { + if(x < 0) -x else x + } + + def factorial(i : Int) : Int = if(i <= 1) 1 else (i * factorial(i - 1)) } diff --git a/unmanaged/32/cafebabe_2.9.2-1.2.jar b/unmanaged/32/cafebabe_2.9.2-1.2.jar index a7f91ed1b7e26933e0b0c657dbd2f240a423dc50..ccfc6ff432dfbae28dbb6508948a4a87dc52524f 100644 Binary files a/unmanaged/32/cafebabe_2.9.2-1.2.jar and b/unmanaged/32/cafebabe_2.9.2-1.2.jar differ