diff --git a/src/main/scala/leon/codegen/CodeGenPhase.scala b/src/main/scala/leon/codegen/CodeGenPhase.scala index e2fb321c6dbf9251d0531a0d46c533e412a41bcd..cd2df91d2cd90f6c75415e0ef37fcf7ef439af6c 100644 --- a/src/main/scala/leon/codegen/CodeGenPhase.scala +++ b/src/main/scala/leon/codegen/CodeGenPhase.scala @@ -15,45 +15,13 @@ object CodeGenPhase extends LeonPhase[Program,CompilationResult] { def run(ctx : LeonContext)(p : Program) : CompilationResult = { import CodeGeneration._ - // This sets up an environment where all classes and all functions have names. - implicit val env = CompilationEnvironment.fromProgram(p) - - for((parent,children) <- p.algebraicDataTypes) { - val acf = compileAbstractClassDef(p, parent) - val ccfs = children.map(c => compileCaseClassDef(p, c)) - } - - val mainClassName = defToJVMName(p, p.mainObject) - val cf = new ClassFile(mainClassName, None) - 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)) { - - 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) + CompilationUnit.compileProgram(p) match { + case Some(unit) => + unit.writeClassFiles() + CompilationResult(successful = true) + case None => + CompilationResult(successful = false) } - cf.writeToFile(mainClassName + ".class") - - CompilationResult(successful = true) } } diff --git a/src/main/scala/leon/codegen/CodeGeneration.scala b/src/main/scala/leon/codegen/CodeGeneration.scala index 9800b10596d1de0c1ee86793e7b79500511126c1..a036dd71b1d6051bcb1578f2af83ff65bf6fe959 100644 --- a/src/main/scala/leon/codegen/CodeGeneration.scala +++ b/src/main/scala/leon/codegen/CodeGeneration.scala @@ -46,7 +46,7 @@ object CodeGeneration { ch.freeze } - private def mkExpr(e : Expr, ch : CodeHandler)(implicit env : CompilationEnvironment) { + private[codegen] def mkExpr(e : Expr, ch : CodeHandler)(implicit env : CompilationEnvironment) { e match { case Variable(id) => val slot = slotFor(id) @@ -151,7 +151,7 @@ object CodeGeneration { } } - private def mkBranch(cond : Expr, then : String, elze : String, ch : CodeHandler)(implicit env : CompilationEnvironment) { + private[codegen] def mkBranch(cond : Expr, then : String, elze : String, ch : CodeHandler)(implicit env : CompilationEnvironment) { cond match { case BooleanLiteral(true) => ch << Goto(then) @@ -189,22 +189,22 @@ object CodeGeneration { 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) - + // WARNING !!! mkBranch delegates to mkExpr, and mkExpr delegates to mkBranch ! // That means, between the two of them, they'd better know what to generate ! case other => @@ -213,7 +213,7 @@ object CodeGeneration { } } - private def slotFor(id : Identifier)(implicit env : CompilationEnvironment) : Int = { + private[codegen] 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/CompilationUnit.scala b/src/main/scala/leon/codegen/CompilationUnit.scala new file mode 100644 index 0000000000000000000000000000000000000000..d16128a2246ffaa6309ce11e6ec059ceac6f4158 --- /dev/null +++ b/src/main/scala/leon/codegen/CompilationUnit.scala @@ -0,0 +1,133 @@ +package leon +package codegen + +import purescala.Common._ +import purescala.Definitions._ +import purescala.Trees._ +import purescala.TypeTrees._ + +import cafebabe._ +import cafebabe.AbstractByteCodes._ +import cafebabe.ByteCodes._ +import cafebabe.ClassFileTypes._ +import cafebabe.Flags._ + +import CodeGeneration._ + +class CompilationUnit(val p: Program, val mainClass: ClassFile, implicit val env: CompilationEnvironment) { + val mainClassName = defToJVMName(p, p.mainObject) + + val loader = new CafebabeClassLoader + + def writeClassFiles() { + mainClass.writeToFile(mainClassName + ".class") + } + + private var _nextExprId = 0 + def nextExprId = { + _nextExprId += 1 + _nextExprId + } + + def groundExprToJava(e: Expr): AnyRef = { + null + } + + def javaToGroundExpr(e: AnyRef): Expr = { + null + } + + def compileExpression(e: Expr, args: Seq[Identifier]): CompiledExpression = { + + val id = nextExprId + + val cName = "Leon$CodeGen$Expr$"+id + + val cf = new ClassFile(cName, None) + cf.setFlags(( + CLASS_ACC_PUBLIC | + CLASS_ACC_FINAL + ).asInstanceOf[U2]) + + cf.addDefaultConstructor + + val m = cf.addMethod( + typeToJVM(e.getType), + "eval", + args.map(a => typeToJVM(a.getType)) : _* + ) + + m.setFlags(( + METHOD_ACC_PUBLIC | + METHOD_ACC_FINAL | + METHOD_ACC_STATIC + ).asInstanceOf[U2]) + + val ch = m.codeHandler + + val newMapping = args.zipWithIndex.toMap + + val exprToCompile = purescala.TreeOps.matchToIfThenElse(e) + + mkExpr(e, ch)(env.withVars(newMapping)) + + e.getType match { + case Int32Type | BooleanType => + ch << IRETURN + + case UnitType | TupleType(_) | SetType(_) | MapType(_, _) => + ch << ARETURN + + case other => + throw CompilationException("Unsupported return type : " + other) + } + + ch.freeze + + loader.register(cf) + + new CompiledExpression(this, cf, args) + } +} + +object CompilationUnit { + def compileProgram(p: Program): Option[CompilationUnit] = { + implicit val env = CompilationEnvironment.fromProgram(p) + + for((parent,children) <- p.algebraicDataTypes) { + val acf = compileAbstractClassDef(p, parent) + val ccfs = children.map(c => compileCaseClassDef(p, c)) + } + + val mainClassName = defToJVMName(p, p.mainObject) + val cf = new ClassFile(mainClassName, None) + 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)) { + + 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]) + + compileFunDef(funDef, m.codeHandler) + } + + Some(new CompilationUnit(p, cf, env)) + } +} diff --git a/src/main/scala/leon/codegen/CompiledExpression.scala b/src/main/scala/leon/codegen/CompiledExpression.scala new file mode 100644 index 0000000000000000000000000000000000000000..c14b03c802a6c7d6ae20d3891fcf6d1207ff0af1 --- /dev/null +++ b/src/main/scala/leon/codegen/CompiledExpression.scala @@ -0,0 +1,27 @@ +package leon +package codegen + +import purescala.Common._ +import purescala.Definitions._ +import purescala.Trees._ +import purescala.TypeTrees._ + +import cafebabe._ +import cafebabe.AbstractByteCodes._ +import cafebabe.ByteCodes._ +import cafebabe.ClassFileTypes._ +import cafebabe.Flags._ + +class CompiledExpression(unit: CompilationUnit, cf: ClassFile, argsDecl: Seq[Identifier]) { + + def eval(args: Seq[Expr]): Expr = { + val cl = unit.loader.loadClass(cf.className) + val obj = cl.newInstance() + val meth = cl.getMethods()(0) + + val res = meth.invoke(obj, args.map(unit.groundExprToJava)) + + unit.javaToGroundExpr(res) + } + +}