From a083e33535ff0d364634b313945f58b59d4fb412 Mon Sep 17 00:00:00 2001
From: Regis Blanc <regwblanc@gmail.com>
Date: Fri, 20 Feb 2015 18:55:08 +0100
Subject: [PATCH] implement BigInt code generation

---
 .../java/leon/codegen/runtime/BigInt.java     | 82 ++++++++++++++++++
 .../scala/leon/codegen/CodeGeneration.scala   | 85 ++++++++++++-------
 .../scala/leon/codegen/CompilationUnit.scala  | 12 +--
 .../leon/test/codegen/CodeGenTests.scala      | 76 ++++++++++++++++-
 4 files changed, 217 insertions(+), 38 deletions(-)
 create mode 100644 src/main/java/leon/codegen/runtime/BigInt.java

diff --git a/src/main/java/leon/codegen/runtime/BigInt.java b/src/main/java/leon/codegen/runtime/BigInt.java
new file mode 100644
index 000000000..7506b70b7
--- /dev/null
+++ b/src/main/java/leon/codegen/runtime/BigInt.java
@@ -0,0 +1,82 @@
+/* Copyright 2009-2014 EPFL, Lausanne */
+package leon.codegen.runtime;
+
+import java.math.BigInteger;
+
+public final class BigInt {
+
+  private final BigInteger _underlying;
+
+  public final BigInteger underlying() {
+    return _underlying;
+  }
+
+  private BigInt(BigInteger value) {
+    _underlying = value;
+  }
+  public BigInt(String value) {
+    _underlying = new BigInteger(value);
+  }
+
+  public BigInt add(BigInt that) {
+    return new BigInt(_underlying.add(that.underlying()));
+  }
+
+  public BigInt sub(BigInt that) {
+    return new BigInt(_underlying.subtract(that.underlying()));
+  }
+
+  public BigInt mult(BigInt that) {
+    return new BigInt(_underlying.multiply(that.underlying()));
+  }
+
+  public BigInt div(BigInt that) {
+    return new BigInt(_underlying.divide(that.underlying()));
+  }
+
+  public BigInt mod(BigInt that) {
+    return new BigInt(_underlying.mod(that.underlying()));
+  }
+
+  public BigInt neg() {
+    return new BigInt(_underlying.negate());
+  }
+
+
+  public boolean lessThan(BigInt that) {
+    return _underlying.compareTo(that.underlying()) < 0;
+  }
+
+  public boolean lessEquals(BigInt that) {
+    return _underlying.compareTo(that.underlying()) <= 0;
+  }
+
+  public boolean greaterThan(BigInt that) {
+    return _underlying.compareTo(that.underlying()) > 0;
+  }
+
+  public boolean greaterEquals(BigInt that) {
+    return _underlying.compareTo(that.underlying()) >= 0;
+  }
+
+
+  @Override
+  public boolean equals(Object that) {
+    if(that == this) return true;
+    if(!(that instanceof BigInt)) return false;
+
+    BigInt other = (BigInt)that;
+    return this.underlying().equals(other.underlying());
+  }
+
+  @Override
+  public String toString() {
+    return _underlying.toString();
+  }
+
+  @Override
+  public int hashCode() {
+    return _underlying.hashCode();
+  }
+
+}
diff --git a/src/main/scala/leon/codegen/CodeGeneration.scala b/src/main/scala/leon/codegen/CodeGeneration.scala
index 75e15d5ac..bfea7dabe 100644
--- a/src/main/scala/leon/codegen/CodeGeneration.scala
+++ b/src/main/scala/leon/codegen/CodeGeneration.scala
@@ -66,6 +66,7 @@ trait CodeGeneration {
   private[codegen] val TupleClass                = "leon/codegen/runtime/Tuple"
   private[codegen] val SetClass                  = "leon/codegen/runtime/Set"
   private[codegen] val MapClass                  = "leon/codegen/runtime/Map"
+  private[codegen] val BigIntClass               = "leon/codegen/runtime/BigInt"
   private[codegen] val CaseClassClass            = "leon/codegen/runtime/CaseClass"
   private[codegen] val LambdaClass               = "leon/codegen/runtime/Lambda"
   private[codegen] val ErrorClass                = "leon/codegen/runtime/LeonCodeGenRuntimeException"
@@ -84,7 +85,6 @@ trait CodeGeneration {
   /** Return the respective JVM type from a Leon type */
   def typeToJVM(tpe : TypeTree) : String = tpe match {
     case Int32Type => "I"
-    case IntegerType => "I"
 
     case BooleanType => "Z"
 
@@ -104,6 +104,9 @@ trait CodeGeneration {
     case _ : MapType =>
       "L" + MapClass + ";"
 
+    case IntegerType =>
+      "L" + BigIntClass + ";"
+
     case _ : FunctionType =>
       "L" + LambdaClass + ";"
 
@@ -119,7 +122,6 @@ trait CodeGeneration {
   /** Return the respective boxed JVM type from a Leon type */
   def typeToJVMBoxed(tpe : TypeTree) : String = tpe match {
     case Int32Type              => s"L$BoxedIntClass;"
-    case IntegerType            => s"L$BoxedIntClass;"
     case BooleanType | UnitType => s"L$BoxedBoolClass;"
     case CharType               => s"L$BoxedCharClass;"
     case other => typeToJVM(other)
@@ -192,10 +194,10 @@ trait CodeGeneration {
     mkExpr(bodyWithPost, ch)(Locals(newMapping, Map.empty, Map.empty, isStatic))
 
     funDef.returnType match {
-      case IntegerType | Int32Type | BooleanType | UnitType =>
+      case Int32Type | BooleanType | UnitType =>
         ch << IRETURN
 
-      case _ : ClassType | _ : TupleType | _ : SetType | _ : MapType | _ : ArrayType | _ : FunctionType | _ : TypeParameter =>
+      case IntegerType | _ : ClassType | _ : TupleType | _ : SetType | _ : MapType | _ : ArrayType | _ : FunctionType | _ : TypeParameter =>
         ch << ARETURN
 
       case other =>
@@ -220,7 +222,7 @@ trait CodeGeneration {
         mkExpr(d, ch)
         val slot = ch.getFreshVar
         val instr = i.getType match {
-          case IntegerType | Int32Type | CharType | BooleanType | UnitType => IStore(slot)
+          case Int32Type | CharType | BooleanType | UnitType => IStore(slot)
           case _ => AStore(slot)
         }
         ch << instr
@@ -228,8 +230,6 @@ trait CodeGeneration {
 
       case IntLiteral(v) =>
         ch << Ldc(v)
-      case InfiniteIntegerLiteral(v) =>
-        ch << Ldc(v.toInt)
 
       case CharLiteral(v) =>
         ch << Ldc(v)
@@ -240,6 +240,11 @@ trait CodeGeneration {
       case UnitLiteral() =>
         ch << Ldc(1)
 
+      case InfiniteIntegerLiteral(v) =>
+        ch << New(BigIntClass) << DUP
+        ch << Ldc(v.toString)
+        ch << InvokeSpecial(BigIntClass, constructorName, "(Ljava/lang/String;)V")
+
       // Case classes
       case CaseClass(cct, as) =>
         val (ccName, ccApplySig) = leonClassToJVMInfo(cct.classDef).getOrElse {
@@ -572,31 +577,31 @@ trait CodeGeneration {
       case Plus(l, r) =>
         mkExpr(l, ch)
         mkExpr(r, ch)
-        ch << IADD
+        ch << InvokeVirtual(BigIntClass, "add", "(L%s;)L%s;".format(BigIntClass, BigIntClass))
 
       case Minus(l, r) =>
         mkExpr(l, ch)
         mkExpr(r, ch)
-        ch << ISUB
+        ch << InvokeVirtual(BigIntClass, "sub", "(L%s;)L%s;".format(BigIntClass, BigIntClass))
 
       case Times(l, r) =>
         mkExpr(l, ch)
         mkExpr(r, ch)
-        ch << IMUL
+        ch << InvokeVirtual(BigIntClass, "mult", "(L%s;)L%s;".format(BigIntClass, BigIntClass))
 
       case Division(l, r) =>
         mkExpr(l, ch)
         mkExpr(r, ch)
-        ch << IDIV
+        ch << InvokeVirtual(BigIntClass, "div", "(L%s;)L%s;".format(BigIntClass, BigIntClass))
 
       case Modulo(l, r) =>
         mkExpr(l, ch)
         mkExpr(r, ch)
-        ch << IREM
+        ch << InvokeVirtual(BigIntClass, "mod", "(L%s;)L%s;".format(BigIntClass, BigIntClass))
 
       case UMinus(e) =>
         mkExpr(e, ch)
-        ch << INEG
+        ch << InvokeVirtual(BigIntClass, "neg", "()L%s;".format(BigIntClass))
 
       //BV arithmetic
       case BVPlus(l, r) =>
@@ -674,7 +679,6 @@ trait CodeGeneration {
           case Untyped => throw CompilationException("Cannot compile untyped array access.")
           case CharType => CALOAD
           case Int32Type => IALOAD
-          case IntegerType => IALOAD
           case BooleanType => BALOAD
           case _ => AALOAD
         })
@@ -686,7 +690,6 @@ trait CodeGeneration {
         val storeInstr = a.getType match {
           case ArrayType(CharType) => ch << NewArray.primitive("T_CHAR"); CASTORE
           case ArrayType(Int32Type) => ch << NewArray.primitive("T_INT"); IASTORE
-          case ArrayType(IntegerType) => ch << NewArray.primitive("T_INT"); IASTORE
           case ArrayType(BooleanType) => ch << NewArray.primitive("T_BOOLEAN"); BASTORE
           case ArrayType(other) => ch << NewArray(typeToJVM(other)); AASTORE
           case other => throw CompilationException("Cannot compile finite array expression whose type is %s.".format(other))
@@ -711,7 +714,6 @@ trait CodeGeneration {
         val storeInstr = a.getType match {
           case ArrayType(CharType) => ch << NewArray.primitive("T_CHAR"); CASTORE
           case ArrayType(Int32Type) => ch << NewArray.primitive("T_INT"); IASTORE
-          case ArrayType(IntegerType) => ch << NewArray.primitive("T_INT"); IASTORE
           case ArrayType(BooleanType) => ch << NewArray.primitive("T_BOOLEAN"); BASTORE
           case ArrayType(other) => ch << NewArray(typeToJVM(other)); AASTORE
           case other => throw CompilationException("Cannot compile finite array expression whose type is %s.".format(other))
@@ -794,10 +796,6 @@ trait CodeGeneration {
         ch << New(BoxedIntClass) << DUP
         mkExpr(e, ch)
         ch << InvokeSpecial(BoxedIntClass, constructorName, "(I)V")
-      case IntegerType =>
-        ch << New(BoxedIntClass) << DUP
-        mkExpr(e, ch)
-        ch << InvokeSpecial(BoxedIntClass, constructorName, "(I)V")
 
       case BooleanType | UnitType =>
         ch << New(BoxedBoolClass) << DUP
@@ -825,8 +823,6 @@ trait CodeGeneration {
     tpe match {
       case Int32Type =>
         ch << New(BoxedIntClass) << DUP_X1 << SWAP << InvokeSpecial(BoxedIntClass, constructorName, "(I)V")
-      case IntegerType =>
-        ch << New(BoxedIntClass) << DUP_X1 << SWAP << InvokeSpecial(BoxedIntClass, constructorName, "(I)V")
 
       case BooleanType | UnitType =>
         ch << New(BoxedBoolClass) << DUP_X1 << SWAP << InvokeSpecial(BoxedBoolClass, constructorName, "(Z)V")
@@ -845,8 +841,6 @@ trait CodeGeneration {
     tpe match {
       case Int32Type =>
         ch << CheckCast(BoxedIntClass) << InvokeVirtual(BoxedIntClass, "intValue", "()I")
-      case IntegerType =>
-        ch << CheckCast(BoxedIntClass) << InvokeVirtual(BoxedIntClass, "intValue", "()I")
 
       case BooleanType | UnitType =>
         ch << CheckCast(BoxedBoolClass) << InvokeVirtual(BoxedBoolClass, "booleanValue", "()Z")
@@ -860,6 +854,9 @@ trait CodeGeneration {
         }
         ch << CheckCast(cn)
 
+      case IntegerType =>
+        ch << CheckCast(BigIntClass)
+
       case tt : TupleType =>
         ch << CheckCast(TupleClass)
 
@@ -917,7 +914,7 @@ trait CodeGeneration {
         mkExpr(l, ch)
         mkExpr(r, ch)
         l.getType match {
-          case Int32Type | IntegerType | BooleanType | UnitType | CharType =>
+          case Int32Type | BooleanType | UnitType | CharType =>
             ch << If_ICmpEq(thenn) << Goto(elze)
 
           case _ =>
@@ -928,22 +925,46 @@ trait CodeGeneration {
       case LessThan(l,r) =>
         mkExpr(l, ch)
         mkExpr(r, ch)
-        ch << If_ICmpLt(thenn) << Goto(elze) 
+        l.getType match {
+          case Int32Type =>
+            ch << If_ICmpLt(thenn) << Goto(elze) 
+          case IntegerType =>
+            ch << InvokeVirtual(BigIntClass, "lessThan", "(L%s;)Z".format(BigIntClass)) 
+            ch << IfEq(elze) << Goto(thenn)
+        }
 
       case GreaterThan(l,r) =>
         mkExpr(l, ch)
         mkExpr(r, ch)
-        ch << If_ICmpGt(thenn) << Goto(elze) 
+        l.getType match {
+          case Int32Type =>
+            ch << If_ICmpGt(thenn) << Goto(elze) 
+          case IntegerType =>
+            ch << InvokeVirtual(BigIntClass, "greaterThan", "(L%s;)Z".format(BigIntClass)) 
+            ch << IfEq(elze) << Goto(thenn)
+        }
 
       case LessEquals(l,r) =>
         mkExpr(l, ch)
         mkExpr(r, ch)
-        ch << If_ICmpLe(thenn) << Goto(elze) 
+        l.getType match {
+          case Int32Type =>
+            ch << If_ICmpLe(thenn) << Goto(elze) 
+          case IntegerType =>
+            ch << InvokeVirtual(BigIntClass, "lessEquals", "(L%s;)Z".format(BigIntClass)) 
+            ch << IfEq(elze) << Goto(thenn)
+        }
 
       case GreaterEquals(l,r) =>
         mkExpr(l, ch)
         mkExpr(r, ch)
-        ch << If_ICmpGe(thenn) << Goto(elze) 
+        l.getType match {
+          case Int32Type =>
+            ch << If_ICmpGe(thenn) << Goto(elze) 
+          case IntegerType =>
+            ch << InvokeVirtual(BigIntClass, "greaterEquals", "(L%s;)Z".format(BigIntClass)) 
+            ch << IfEq(elze) << Goto(thenn)
+        }
   
       case IfExpr(c, t, e) => 
         val innerThen = ch.getFreshLabel("then")
@@ -977,7 +998,7 @@ trait CodeGeneration {
         case None => locals.varToLocal(id) match {
           case Some(slot) =>
             val instr = id.getType match {
-              case IntegerType | Int32Type | CharType | BooleanType | UnitType => ILoad(slot)
+              case Int32Type | CharType | BooleanType | UnitType => ILoad(slot)
               case _ => ALoad(slot)
             }
             ch << instr
@@ -1068,11 +1089,11 @@ trait CodeGeneration {
       ch << Label(initLabel)  // return lzy 
       //newValue
       lzy.returnType match {
-        case Int32Type | IntegerType | BooleanType | UnitType | CharType => 
+        case Int32Type | BooleanType | UnitType | CharType => 
           // Since the underlying field only has boxed types, we have to unbox them to return them
           mkUnbox(lzy.returnType, ch)(NoLocals(isStatic)) 
           ch << IRETURN      
-        case _ : ClassType | _ : TupleType | _ : SetType | _ : MapType | _ : ArrayType | _: TypeParameter => 
+        case IntegerType | _ : ClassType | _ : TupleType | _ : SetType | _ : MapType | _ : ArrayType | _: TypeParameter => 
           ch << ARETURN
         case other => throw CompilationException("Unsupported return type : " + other.getClass)
       }
diff --git a/src/main/scala/leon/codegen/CompilationUnit.scala b/src/main/scala/leon/codegen/CompilationUnit.scala
index b24a5f0f9..52509ab71 100644
--- a/src/main/scala/leon/codegen/CompilationUnit.scala
+++ b/src/main/scala/leon/codegen/CompilationUnit.scala
@@ -125,8 +125,9 @@ class CompilationUnit(val ctx: LeonContext,
   def exprToJVM(e: Expr)(implicit monitor : LeonCodeGenRuntimeMonitor): AnyRef = e match {
     case IntLiteral(v) =>
       new java.lang.Integer(v)
+
     case InfiniteIntegerLiteral(v) =>
-      new java.lang.Integer(v.toInt)
+      new leon.codegen.runtime.BigInt(v.toString)
 
     case BooleanLiteral(v) =>
       new java.lang.Boolean(v)
@@ -186,8 +187,9 @@ class CompilationUnit(val ctx: LeonContext,
   def jvmToExpr(e: AnyRef, tpe: TypeTree): Expr = (e, tpe) match {
     case (i: Integer, Int32Type) =>
       IntLiteral(i.toInt)
-    case (i: Integer, IntegerType) =>
-      InfiniteIntegerLiteral(i.toInt)
+
+    case (c: runtime.BigInt, IntegerType) =>
+      InfiniteIntegerLiteral(BigInt(c.underlying))
 
     case (b: java.lang.Boolean, BooleanType) =>
       BooleanLiteral(b.booleanValue)
@@ -288,10 +290,10 @@ class CompilationUnit(val ctx: LeonContext,
     mkExpr(e, ch)(Locals(newMapping, Map.empty, Map.empty, true))
 
     e.getType match {
-      case IntegerType | Int32Type | BooleanType =>
+      case Int32Type | BooleanType =>
         ch << IRETURN
 
-      case UnitType | _: TupleType  | _: SetType | _: MapType | _: AbstractClassType | _: CaseClassType | _: ArrayType | _: FunctionType | _: TypeParameter =>
+      case IntegerType | UnitType | _: TupleType  | _: SetType | _: MapType | _: AbstractClassType | _: CaseClassType | _: ArrayType | _: FunctionType | _: TypeParameter =>
         ch << ARETURN
 
       case other =>
diff --git a/src/test/scala/leon/test/codegen/CodeGenTests.scala b/src/test/scala/leon/test/codegen/CodeGenTests.scala
index 1e0ad3a11..5640cf1bb 100644
--- a/src/test/scala/leon/test/codegen/CodeGenTests.scala
+++ b/src/test/scala/leon/test/codegen/CodeGenTests.scala
@@ -170,8 +170,19 @@ class CodeGenTests extends test.LeonTestSuite {
       }""",
       IntLiteral(10)
     ),
+
+    TestCase("methMakeBigInt", """
+      object methSimple {
+        
+        val x: BigInt = 42
+
+        def test = x
+
+      }""",
+      InfiniteIntegerLiteral(42)
+    ),
     
-    TestCase("methSimpleBignt", """
+    TestCase("methSimpleBigInt", """
       object methSimple {
         
         sealed abstract class Ab { 
@@ -183,6 +194,69 @@ class CodeGenTests extends test.LeonTestSuite {
       }""",
       InfiniteIntegerLiteral(10)
     ),
+
+    TestCase("BigIntNoOverflow", """
+      object methSimple {
+        
+        sealed abstract class Ab { 
+          def f2(x : BigInt): BigInt = x + BigInt(2000000000)
+        }
+        case class Con() extends Ab { }
+        
+        def test = Con().f2(2000000000)
+      }""",
+      InfiniteIntegerLiteral(BigInt("4000000000"))
+    ),
+    
+
+    TestCase("BigIntOps", """
+      object methSimple {
+        
+        def f(x: BigInt): BigInt = ((x * 2) - 10)/2
+        
+        def test = f(12)
+      }""",
+      InfiniteIntegerLiteral(BigInt(7))
+    ),
+
+
+    TestCase("BigIntComp0", """
+      object methSimple {
+        
+        def f(x: BigInt): Boolean = x == BigInt(17)
+        
+        def test = f(17)
+      }""",
+      BooleanLiteral(true)
+    ),
+
+    TestCase("BigIntComp1", """
+      object methSimple {
+        
+        def f(x: BigInt): BigInt = if(x <= 0) -x else x
+        
+        def test = f(-17)
+      }""",
+      InfiniteIntegerLiteral(BigInt(17))
+    ),
+    TestCase("BigIntComp2", """
+      object methSimple {
+        
+        def f(x: BigInt): BigInt = if(x < 0) -x else x
+        
+        def test = f(-12)
+      }""",
+      InfiniteIntegerLiteral(BigInt(12))
+    ),
+    TestCase("BigIntComp3", """
+      object methSimple {
+        
+        def f(x: BigInt): BigInt = if(x >= 0) -x else x
+        
+        def test = f(-7)
+      }""",
+      InfiniteIntegerLiteral(BigInt(-7))
+    ),
     
     TestCase("methods", """
       object methods {
-- 
GitLab