diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml new file mode 100644 index 0000000000..9b8170126d --- /dev/null +++ b/.github/workflows/windows-ci.yml @@ -0,0 +1,51 @@ +name: Windows CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +env: + SBT_OPTS: '-Xmx6g -Xms1g -Xss4m' + +jobs: + build: + strategy: + matrix: + java: [ '8' ] + + # Use the latest supported version. We will be less affected by ambient changes + # due to the lack of pinning than by breakages because of changing version support. + runs-on: windows-latest + + steps: + - name: Set up git to use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf + - uses: actions/checkout@v3 + - name: Set up JDK ${{ matrix.java }} + uses: coursier/setup-action@v1 + with: + jvm: temurin:1.${{ matrix.java }} + apps: sbt + - uses: actions/setup-node@v4 + with: + node-version: '24.x' + cache: 'npm' + - name: npm install + run: npm install + + # Very far from testing everything, but at least it is a good sanity check + - name: Test suite + run: sbt testSuite2_12/test + - name: Linker test suite + run: sbt linker2_12/test + # partest is slow; only execute one test as a smoke test + - name: partest smoke test + run: sbt "partestSuite2_12/testOnly -- --fastOpt run/option-fold.scala" + # Module splitting has some logic for case-insensitive filesystems, which we must test on Windows + - name: Test suite with module splitting + run: sbt 'set testSuite.v2_12/scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.ESModule).withModuleSplitStyle(ModuleSplitStyle.SmallModulesFor(List("org.scalajs.testsuite"))))' testSuite2_12/test + shell: bash # for the special characters in the command diff --git a/Jenkinsfile b/Jenkinsfile index 165dec8254..c1a4c70069 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -263,6 +263,11 @@ def Tasks = [ 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withModuleSplitStyle(ModuleSplitStyle.SmallestModules))' \ 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withModuleKind(ModuleKind.ESModule))' \ $testSuite$v/test && + sbtretry ++$scala 'set Global/enableMinifyEverywhere := $testMinify' \ + 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withModuleSplitStyle(ModuleSplitStyle.SmallestModules))' \ + 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withModuleKind(ModuleKind.ESModule))' \ + 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withOptimizer(false))' \ + $testSuite$v/test && sbtretry ++$scala 'set Global/enableMinifyEverywhere := $testMinify' \ 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withModuleSplitStyle(ModuleSplitStyle.SmallModulesFor(List("org.scalajs.testsuite"))))' \ 'set scalaJSLinkerConfig in $testSuite.v$v ~= (_.withModuleKind(ModuleKind.ESModule))' \ diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 4f0c93e14c..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,23 +0,0 @@ -version: '{build}' -image: Visual Studio 2015 -environment: - global: - NODEJS_VERSION: "16" - JAVA_HOME: C:\Program Files\Java\jdk1.8.0 -install: - - ps: Install-Product node $env:NODEJS_VERSION - - npm install - - cmd: choco install sbt --version 1.3.12 -ia "INSTALLDIR=""C:\sbt""" - - cmd: SET PATH=C:\sbt\bin;%JAVA_HOME%\bin;%PATH% - - cmd: SET "SBT_OPTS=-Xmx4g -Xms4m" -build: off -test_script: - # Very far from testing everything, but at least it is a good sanity check - # For slow things (partest and scripted), we execute only one test - - cmd: sbt ";clean;testSuite2_12/test;linker2_12/test;partestSuite2_12/testOnly -- --fastOpt run/option-fold.scala" - # Module splitting has some logic for case-insensitive filesystems, which we must test on Windows - - cmd: sbt ";setSmallESModulesForAppVeyorCI;testSuite2_12/test" -cache: - - C:\sbt - - C:\Users\appveyor\.ivy2\cache - - C:\Users\appveyor\.sbt diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala b/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala index e46b1dc14f..9729137ff2 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala @@ -2298,50 +2298,17 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) isJSFunctionDef(currentClassSym)) { val flags = js.MemberFlags.empty.withNamespace(namespace) val body = { - def genAsUnaryOp(op: js.UnaryOp.Code): js.Tree = - js.UnaryOp(op, genThis()) - def genAsBinaryOp(op: js.BinaryOp.Code): js.Tree = - js.BinaryOp(op, genThis(), jsParams.head.ref) - def genAsBinaryOpRhsNotNull(op: js.BinaryOp.Code): js.Tree = - js.BinaryOp(op, genThis(), js.UnaryOp(js.UnaryOp.CheckNotNull, jsParams.head.ref)) - - if (currentClassSym.get == HackedStringClass) { - /* Hijack the bodies of String.length and String.charAt and replace - * them with String_length and String_charAt operations, respectively. - */ - methodName.name match { - case `lengthMethodName` => genAsUnaryOp(js.UnaryOp.String_length) - case `charAtMethodName` => genAsBinaryOp(js.BinaryOp.String_charAt) - case _ => genBody() - } - } else if (currentClassSym.get == ClassClass) { - // Similar, for the Class_x operations - methodName.name match { - case `getNameMethodName` => genAsUnaryOp(js.UnaryOp.Class_name) - case `isPrimitiveMethodName` => genAsUnaryOp(js.UnaryOp.Class_isPrimitive) - case `isInterfaceMethodName` => genAsUnaryOp(js.UnaryOp.Class_isInterface) - case `isArrayMethodName` => genAsUnaryOp(js.UnaryOp.Class_isArray) - case `getComponentTypeMethodName` => genAsUnaryOp(js.UnaryOp.Class_componentType) - case `getSuperclassMethodName` => genAsUnaryOp(js.UnaryOp.Class_superClass) - - case `isInstanceMethodName` => genAsBinaryOp(js.BinaryOp.Class_isInstance) - case `isAssignableFromMethodName` => genAsBinaryOpRhsNotNull(js.BinaryOp.Class_isAssignableFrom) - case `castMethodName` => genAsBinaryOp(js.BinaryOp.Class_cast) - - case _ => genBody() - } - } else if (currentClassSym.get == JavaLangReflectArrayModClass) { - methodName.name match { - case `arrayNewInstanceMethodName` => - val List(jlClassParam, lengthParam) = jsParams - js.BinaryOp(js.BinaryOp.Class_newArray, - js.UnaryOp(js.UnaryOp.CheckNotNull, jlClassParam.ref), - lengthParam.ref) - case _ => + val classOwner = currentClassSym.owner + if (classOwner != JavaLangPackageClass && classOwner.owner != JavaLangPackageClass) { + // Fast path; it cannot be any of the special methods of the javalib + genBody() + } else { + JavalibMethodsWithOpBody.get((encodeClassName(currentClassSym), methodName.name)) match { + case None => genBody() + case Some(javalibOpBody) => + javalibOpBody.generate(genThis(), jsParams.map(_.ref)) } - } else { - genBody() } } js.MethodDef(flags, methodName, originalName, jsParams, resultIRType, @@ -4559,50 +4526,78 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) if (opType == jstpe.AnyType) rsrc_in else adaptPrimitive(rsrc_in, if (isShift) jstpe.IntType else opType) + def regular(op: js.BinaryOp.Code): js.Tree = + js.BinaryOp(op, lsrc, rsrc) + (opType: @unchecked) match { case jstpe.IntType => - val op = (code: @switch) match { - case ADD => Int_+ - case SUB => Int_- - case MUL => Int_* - case DIV => Int_/ - case MOD => Int_% - case OR => Int_| - case AND => Int_& - case XOR => Int_^ - case LSL => Int_<< - case LSR => Int_>>> - case ASR => Int_>> - case EQ => Int_== - case NE => Int_!= - case LT => Int_< - case LE => Int_<= - case GT => Int_> - case GE => Int_>= + def comparison(signedOp: js.BinaryOp.Code, unsignedOp: js.BinaryOp.Code): js.Tree = { + (lsrc, rsrc) match { + case (IntFlipSign(flippedLhs), IntFlipSign(flippedRhs)) => + js.BinaryOp(unsignedOp, flippedLhs, flippedRhs) + case (IntFlipSign(flippedLhs), js.IntLiteral(r)) => + js.BinaryOp(unsignedOp, flippedLhs, js.IntLiteral(r ^ Int.MinValue)(rsrc.pos)) + case (js.IntLiteral(l), IntFlipSign(flippedRhs)) => + js.BinaryOp(unsignedOp, js.IntLiteral(l ^ Int.MinValue)(lsrc.pos), flippedRhs) + case _ => + regular(signedOp) + } + } + + (code: @switch) match { + case ADD => regular(Int_+) + case SUB => regular(Int_-) + case MUL => regular(Int_*) + case DIV => regular(Int_/) + case MOD => regular(Int_%) + case OR => regular(Int_|) + case AND => regular(Int_&) + case XOR => regular(Int_^) + case LSL => regular(Int_<<) + case LSR => regular(Int_>>>) + case ASR => regular(Int_>>) + case EQ => regular(Int_==) + case NE => regular(Int_!=) + + case LT => comparison(Int_<, Int_unsigned_<) + case LE => comparison(Int_<=, Int_unsigned_<=) + case GT => comparison(Int_>, Int_unsigned_>) + case GE => comparison(Int_>=, Int_unsigned_>=) } - js.BinaryOp(op, lsrc, rsrc) case jstpe.LongType => - val op = (code: @switch) match { - case ADD => Long_+ - case SUB => Long_- - case MUL => Long_* - case DIV => Long_/ - case MOD => Long_% - case OR => Long_| - case XOR => Long_^ - case AND => Long_& - case LSL => Long_<< - case LSR => Long_>>> - case ASR => Long_>> - case EQ => Long_== - case NE => Long_!= - case LT => Long_< - case LE => Long_<= - case GT => Long_> - case GE => Long_>= + def comparison(signedOp: js.BinaryOp.Code, unsignedOp: js.BinaryOp.Code): js.Tree = { + (lsrc, rsrc) match { + case (LongFlipSign(flippedLhs), LongFlipSign(flippedRhs)) => + js.BinaryOp(unsignedOp, flippedLhs, flippedRhs) + case (LongFlipSign(flippedLhs), js.LongLiteral(r)) => + js.BinaryOp(unsignedOp, flippedLhs, js.LongLiteral(r ^ Long.MinValue)(rsrc.pos)) + case (js.LongLiteral(l), LongFlipSign(flippedRhs)) => + js.BinaryOp(unsignedOp, js.LongLiteral(l ^ Long.MinValue)(lsrc.pos), flippedRhs) + case _ => + regular(signedOp) + } + } + + (code: @switch) match { + case ADD => regular(Long_+) + case SUB => regular(Long_-) + case MUL => regular(Long_*) + case DIV => regular(Long_/) + case MOD => regular(Long_%) + case OR => regular(Long_|) + case XOR => regular(Long_^) + case AND => regular(Long_&) + case LSL => regular(Long_<<) + case LSR => regular(Long_>>>) + case ASR => regular(Long_>>) + case EQ => regular(Long_==) + case NE => regular(Long_!=) + case LT => comparison(Long_<, Long_unsigned_<) + case LE => comparison(Long_<=, Long_unsigned_<=) + case GT => comparison(Long_>, Long_unsigned_>) + case GE => comparison(Long_>=, Long_unsigned_>=) } - js.BinaryOp(op, lsrc, rsrc) case jstpe.FloatType => def withFloats(op: Int): js.Tree = @@ -5287,11 +5282,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) genStatOrExpr(args(1), isStat) } - case IDENTITY_HASH_CODE => - // runtime.identityHashCode(arg) - val arg = genArgs1 - js.UnaryOp(js.UnaryOp.IdentityHashCode, arg) - case DEBUGGER => // js.special.debugger() js.Debugger() @@ -5511,6 +5501,16 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) js.UnaryOp(js.UnaryOp.UnwrapFromThrowable, js.UnaryOp(js.UnaryOp.CheckNotNull, genArgs1)) + case LINKTIME_IF => + // LinkingInfo.linkTimeIf(cond, thenp, elsep) + val cond = genLinkTimeExpr(args(0)) + val thenp = genExpr(args(1)) + val elsep = genExpr(args(2)) + val tpe = + if (isStat) jstpe.VoidType + else toIRType(tree.tpe) + js.LinkTimeIf(cond, thenp, elsep)(tpe) + case LINKTIME_PROPERTY => // LinkingInfo.linkTimePropertyXXX("...") val arg = genArgs1 @@ -5529,6 +5529,83 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) } } + private def genLinkTimeExpr(tree: Tree): js.Tree = { + import scalaPrimitives._ + + implicit val pos = tree.pos + + def invalid(): js.Tree = { + reporter.error(tree.pos, + "Illegal expression in the condition of a linkTimeIf. " + + "Valid expressions are: boolean and int primitives; " + + "references to link-time properties; " + + "primitive operations on booleans; " + + "and comparisons on ints.") + js.BooleanLiteral(false) + } + + tree match { + case Literal(c) => + c.tag match { + case BooleanTag => js.BooleanLiteral(c.booleanValue) + case IntTag => js.IntLiteral(c.intValue) + case _ => invalid() + } + + case Apply(fun @ Select(receiver, _), args) => + fun.symbol.getAnnotation(LinkTimePropertyAnnotation) match { + case Some(annotation) => + val propName = annotation.constantAtIndex(0).get.stringValue + js.LinkTimeProperty(propName)(toIRType(tree.tpe)) + + case None if isPrimitive(fun.symbol) => + val code = getPrimitive(fun.symbol) + + def genLhs: js.Tree = genLinkTimeExpr(receiver) + def genRhs: js.Tree = genLinkTimeExpr(args.head) + + def unaryOp(op: js.UnaryOp.Code): js.Tree = + js.UnaryOp(op, genLhs) + def binaryOp(op: js.BinaryOp.Code): js.Tree = + js.BinaryOp(op, genLhs, genRhs) + + toIRType(receiver.tpe) match { + case jstpe.BooleanType => + (code: @switch) match { + case ZNOT => unaryOp(js.UnaryOp.Boolean_!) + case EQ => binaryOp(js.BinaryOp.Boolean_==) + case NE | XOR => binaryOp(js.BinaryOp.Boolean_!=) + case OR => binaryOp(js.BinaryOp.Boolean_|) + case AND => binaryOp(js.BinaryOp.Boolean_&) + case ZOR => js.LinkTimeIf(genLhs, js.BooleanLiteral(true), genRhs)(jstpe.BooleanType) + case ZAND => js.LinkTimeIf(genLhs, genRhs, js.BooleanLiteral(false))(jstpe.BooleanType) + case _ => invalid() + } + + case jstpe.IntType => + (code: @switch) match { + case EQ => binaryOp(js.BinaryOp.Int_==) + case NE => binaryOp(js.BinaryOp.Int_!=) + case LT => binaryOp(js.BinaryOp.Int_<) + case LE => binaryOp(js.BinaryOp.Int_<=) + case GT => binaryOp(js.BinaryOp.Int_>) + case GE => binaryOp(js.BinaryOp.Int_>=) + case _ => invalid() + } + + case _ => + invalid() + } + + case None => // if !isPrimitive + invalid() + } + + case _ => + invalid() + } + } + /** Gen JS code for a primitive JS call (to a method of a subclass of js.Any) * This is the typed Scala.js to JS bridge feature. Basically it boils * down to calling the method without name mangling. But other aspects @@ -7292,37 +7369,6 @@ private object GenJSCode { private val ObjectArgConstructorName = MethodName.constructor(List(jswkn.ObjectRef)) - private val lengthMethodName = - MethodName("length", Nil, jstpe.IntRef) - private val charAtMethodName = - MethodName("charAt", List(jstpe.IntRef), jstpe.CharRef) - - private val getNameMethodName = - MethodName("getName", Nil, jstpe.ClassRef(jswkn.BoxedStringClass)) - private val isPrimitiveMethodName = - MethodName("isPrimitive", Nil, jstpe.BooleanRef) - private val isInterfaceMethodName = - MethodName("isInterface", Nil, jstpe.BooleanRef) - private val isArrayMethodName = - MethodName("isArray", Nil, jstpe.BooleanRef) - private val getComponentTypeMethodName = - MethodName("getComponentType", Nil, jstpe.ClassRef(jswkn.ClassClass)) - private val getSuperclassMethodName = - MethodName("getSuperclass", Nil, jstpe.ClassRef(jswkn.ClassClass)) - - private val isInstanceMethodName = - MethodName("isInstance", List(jstpe.ClassRef(jswkn.ObjectClass)), jstpe.BooleanRef) - private val isAssignableFromMethodName = - MethodName("isAssignableFrom", List(jstpe.ClassRef(jswkn.ClassClass)), jstpe.BooleanRef) - private val castMethodName = - MethodName("cast", List(jstpe.ClassRef(jswkn.ObjectClass)), jstpe.ClassRef(jswkn.ObjectClass)) - - private val arrayNewInstanceMethodName = { - MethodName("newInstance", - List(jstpe.ClassRef(jswkn.ClassClass), jstpe.IntRef), - jstpe.ClassRef(jswkn.ObjectClass)) - } - private val thisOriginalName = OriginalName("this") private object BlockOrAlone { @@ -7338,4 +7384,145 @@ private object GenJSCode { case _ => Some((tree, Nil)) } } + + private object IntFlipSign { + def unapply(tree: js.Tree): Option[js.Tree] = tree match { + case js.BinaryOp(js.BinaryOp.Int_^, lhs, js.IntLiteral(Int.MinValue)) => + Some(lhs) + case js.BinaryOp(js.BinaryOp.Int_^, js.IntLiteral(Int.MinValue), rhs) => + Some(rhs) + case _ => + None + } + } + + private object LongFlipSign { + def unapply(tree: js.Tree): Option[js.Tree] = tree match { + case js.BinaryOp(js.BinaryOp.Long_^, lhs, js.LongLiteral(Long.MinValue)) => + Some(lhs) + case js.BinaryOp(js.BinaryOp.Long_^, js.LongLiteral(Long.MinValue), rhs) => + Some(rhs) + case _ => + None + } + } + + private abstract class JavalibOpBody { + /** Generates the body of this special method, given references to the receiver and parameters. */ + def generate(receiver: js.Tree, args: List[js.Tree])(implicit pos: ir.Position): js.Tree + } + + private object JavalibOpBody { + private def checkNotNullIf(arg: js.Tree, checkNulls: Boolean)(implicit pos: ir.Position): js.Tree = + if (checkNulls && arg.tpe.isNullable) js.UnaryOp(js.UnaryOp.CheckNotNull, arg) + else arg + + /* These are case classes for convenience (for the apply method). + * They are not intended for pattern matching. + */ + + /** UnaryOp applying to the `this` parameter. */ + final case class ThisUnaryOp(op: js.UnaryOp.Code) extends JavalibOpBody { + def generate(receiver: js.Tree, args: List[js.Tree])(implicit pos: ir.Position): js.Tree = { + assert(args.isEmpty) + js.UnaryOp(op, receiver) + } + } + + /** BinaryOp applying to the `this` parameter and the regular parameter. */ + final case class ThisBinaryOp(op: js.BinaryOp.Code, checkNulls: Boolean = false) extends JavalibOpBody { + def generate(receiver: js.Tree, args: List[js.Tree])(implicit pos: ir.Position): js.Tree = { + val List(rhs) = args: @unchecked + js.BinaryOp(op, receiver, checkNotNullIf(rhs, checkNulls)) + } + } + + /** UnaryOp applying to the only regular parameter (`this` is ignored). */ + final case class ArgUnaryOp(op: js.UnaryOp.Code, checkNulls: Boolean = false) extends JavalibOpBody { + def generate(receiver: js.Tree, args: List[js.Tree])(implicit pos: ir.Position): js.Tree = { + val List(arg) = args: @unchecked + js.UnaryOp(op, checkNotNullIf(arg, checkNulls)) + } + } + + /** BinaryOp applying to the two regular paramters (`this` is ignored). */ + final case class ArgBinaryOp(op: js.BinaryOp.Code, checkNulls: Boolean = false) extends JavalibOpBody { + def generate(receiver: js.Tree, args: List[js.Tree])(implicit pos: ir.Position): js.Tree = { + val List(lhs, rhs) = args: @unchecked + js.BinaryOp(op, checkNotNullIf(lhs, checkNulls), checkNotNullIf(rhs, checkNulls)) + } + } + } + + /** Methods of the javalib whose body must be replaced by a dedicated + * UnaryOp or BinaryOp. + * + * We use IR encoded names to identify them, rather than scalac Symbols. + * There is no fundamental reason for that. It makes it easier to define + * this map in a declarative way, especially when overloaded methods are + * concerned (Array.newInstance). It also allows to define it independently + * of the Global instance, but that is marginal. + */ + private lazy val JavalibMethodsWithOpBody: Map[(ClassName, MethodName), JavalibOpBody] = { + import JavalibOpBody._ + import js.{UnaryOp => unop, BinaryOp => binop} + import jstpe.{BooleanRef => Z, CharRef => C, IntRef => I, LongRef => J, FloatRef => F, DoubleRef => D} + import MethodName.{apply => m} + + val O = jswkn.ObjectRef + val CC = jstpe.ClassRef(jswkn.ClassClass) + val T = jstpe.ClassRef(jswkn.BoxedStringClass) + + val byClass: Map[ClassName, Map[MethodName, JavalibOpBody]] = Map( + jswkn.BoxedIntegerClass.withSuffix("$") -> Map( + m("toUnsignedLong", List(I), J) -> ArgUnaryOp(unop.UnsignedIntToLong), + m("divideUnsigned", List(I, I), I) -> ArgBinaryOp(binop.Int_unsigned_/), + m("remainderUnsigned", List(I, I), I) -> ArgBinaryOp(binop.Int_unsigned_%), + m("numberOfLeadingZeros", List(I), I) -> ArgUnaryOp(unop.Int_clz) + ), + jswkn.BoxedLongClass.withSuffix("$") -> Map( + m("divideUnsigned", List(J, J), J) -> ArgBinaryOp(binop.Long_unsigned_/), + m("remainderUnsigned", List(J, J), J) -> ArgBinaryOp(binop.Long_unsigned_%), + m("numberOfLeadingZeros", List(J), I) -> ArgUnaryOp(unop.Long_clz) + ), + jswkn.BoxedFloatClass.withSuffix("$") -> Map( + m("floatToRawIntBits", List(F), I) -> ArgUnaryOp(unop.Float_toBits), + m("intBitsToFloat", List(I), F) -> ArgUnaryOp(unop.Float_fromBits) + ), + jswkn.BoxedDoubleClass.withSuffix("$") -> Map( + m("doubleToRawLongBits", List(D), J) -> ArgUnaryOp(unop.Double_toBits), + m("longBitsToDouble", List(J), D) -> ArgUnaryOp(unop.Double_fromBits) + ), + jswkn.BoxedStringClass -> Map( + m("length", Nil, I) -> ThisUnaryOp(unop.String_length), + m("charAt", List(I), C) -> ThisBinaryOp(binop.String_charAt) + ), + jswkn.ClassClass -> Map( + // Unary operators + m("getName", Nil, T) -> ThisUnaryOp(unop.Class_name), + m("isPrimitive", Nil, Z) -> ThisUnaryOp(unop.Class_isPrimitive), + m("isInterface", Nil, Z) -> ThisUnaryOp(unop.Class_isInterface), + m("isArray", Nil, Z) -> ThisUnaryOp(unop.Class_isArray), + m("getComponentType", Nil, CC) -> ThisUnaryOp(unop.Class_componentType), + m("getSuperclass", Nil, CC) -> ThisUnaryOp(unop.Class_superClass), + // Binary operators + m("isInstance", List(O), Z) -> ThisBinaryOp(binop.Class_isInstance), + m("isAssignableFrom", List(CC), Z) -> ThisBinaryOp(binop.Class_isAssignableFrom, checkNulls = true), + m("cast", List(O), O) -> ThisBinaryOp(binop.Class_cast) + ), + ClassName("java.lang.System$") -> Map( + m("identityHashCode", List(O), I) -> ArgUnaryOp(unop.IdentityHashCode) + ), + ClassName("java.lang.reflect.Array$") -> Map( + m("newInstance", List(CC, I), O) -> ArgBinaryOp(binop.Class_newArray, checkNulls = true) + ) + ) + + for { + (cls, methods) <- byClass + (methodName, body) <- methods + } yield { + (cls, methodName) -> body + } + } } diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala b/compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala index 2b0c5590d9..e91b74d4ff 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala @@ -131,14 +131,16 @@ trait JSDefinitions { lazy val Runtime_withContextualJSClassValue = getMemberMethod(RuntimePackageModule, newTermName("withContextualJSClassValue")) lazy val Runtime_privateFieldsSymbol = getMemberMethod(RuntimePackageModule, newTermName("privateFieldsSymbol")) lazy val Runtime_linkingInfo = getMemberMethod(RuntimePackageModule, newTermName("linkingInfo")) - lazy val Runtime_identityHashCode = getMemberMethod(RuntimePackageModule, newTermName("identityHashCode")) lazy val Runtime_dynamicImport = getMemberMethod(RuntimePackageModule, newTermName("dynamicImport")) lazy val LinkingInfoModule = getRequiredModule("scala.scalajs.LinkingInfo") + lazy val LinkingInfo_linkTimeIf = getMemberMethod(LinkingInfoModule, newTermName("linkTimeIf")) lazy val LinkingInfo_linkTimePropertyBoolean = getMemberMethod(LinkingInfoModule, newTermName("linkTimePropertyBoolean")) lazy val LinkingInfo_linkTimePropertyInt = getMemberMethod(LinkingInfoModule, newTermName("linkTimePropertyInt")) lazy val LinkingInfo_linkTimePropertyString = getMemberMethod(LinkingInfoModule, newTermName("linkTimePropertyString")) + lazy val LinkTimePropertyAnnotation = getRequiredClass("scala.scalajs.annotation.linkTimeProperty") + lazy val DynamicImportThunkClass = getRequiredClass("scala.scalajs.runtime.DynamicImportThunk") lazy val DynamicImportThunkClass_apply = getMemberMethod(DynamicImportThunkClass, nme.apply) diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/JSPrimitives.scala b/compiler/src/main/scala/org/scalajs/nscplugin/JSPrimitives.scala index 90aa1b1513..a199b87f98 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/JSPrimitives.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/JSPrimitives.scala @@ -58,8 +58,7 @@ abstract class JSPrimitives { final val CREATE_INNER_JS_CLASS = CONSTRUCTOROF + 1 // runtime.createInnerJSClass final val CREATE_LOCAL_JS_CLASS = CREATE_INNER_JS_CLASS + 1 // runtime.createLocalJSClass final val WITH_CONTEXTUAL_JS_CLASS_VALUE = CREATE_LOCAL_JS_CLASS + 1 // runtime.withContextualJSClassValue - final val IDENTITY_HASH_CODE = WITH_CONTEXTUAL_JS_CLASS_VALUE + 1 // runtime.identityHashCode - final val DYNAMIC_IMPORT = IDENTITY_HASH_CODE + 1 // runtime.dynamicImport + final val DYNAMIC_IMPORT = WITH_CONTEXTUAL_JS_CLASS_VALUE + 1 // runtime.dynamicImport final val STRICT_EQ = DYNAMIC_IMPORT + 1 // js.special.strictEquals final val IN = STRICT_EQ + 1 // js.special.in @@ -71,7 +70,8 @@ abstract class JSPrimitives { final val WRAP_AS_THROWABLE = JS_TRY_CATCH + 1 // js.special.wrapAsThrowable final val UNWRAP_FROM_THROWABLE = WRAP_AS_THROWABLE + 1 // js.special.unwrapFromThrowable final val DEBUGGER = UNWRAP_FROM_THROWABLE + 1 // js.special.debugger - final val LINKTIME_PROPERTY = DEBUGGER + 1 // LinkingInfo.linkTimePropertyXXX + final val LINKTIME_IF = DEBUGGER + 1 // LinkingInfo.linkTimeIf + final val LINKTIME_PROPERTY = LINKTIME_IF + 1 // LinkingInfo.linkTimePropertyXXX final val LastJSPrimitiveCode = LINKTIME_PROPERTY @@ -114,7 +114,6 @@ abstract class JSPrimitives { addPrimitive(Runtime_createLocalJSClass, CREATE_LOCAL_JS_CLASS) addPrimitive(Runtime_withContextualJSClassValue, WITH_CONTEXTUAL_JS_CLASS_VALUE) - addPrimitive(Runtime_identityHashCode, IDENTITY_HASH_CODE) addPrimitive(Runtime_dynamicImport, DYNAMIC_IMPORT) addPrimitive(Special_strictEquals, STRICT_EQ) @@ -128,6 +127,7 @@ abstract class JSPrimitives { addPrimitive(Special_unwrapFromThrowable, UNWRAP_FROM_THROWABLE) addPrimitive(Special_debugger, DEBUGGER) + addPrimitive(LinkingInfo_linkTimeIf, LINKTIME_IF) addPrimitive(LinkingInfo_linkTimePropertyBoolean, LINKTIME_PROPERTY) addPrimitive(LinkingInfo_linkTimePropertyInt, LINKTIME_PROPERTY) addPrimitive(LinkingInfo_linkTimePropertyString, LINKTIME_PROPERTY) diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/LinkTimeIfTest.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/LinkTimeIfTest.scala new file mode 100644 index 0000000000..881c0e9a2f --- /dev/null +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/LinkTimeIfTest.scala @@ -0,0 +1,109 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.nscplugin.test + +import util._ + +import org.junit.Test +import org.junit.Assert._ + +// scalastyle:off line.size.limit + +class LinkTimeIfTest extends TestHelpers { + override def preamble: String = "import scala.scalajs.LinkingInfo._" + + private final val IllegalLinkTimeIfArgMessage = { + "Illegal expression in the condition of a linkTimeIf. " + + "Valid expressions are: boolean and int primitives; " + + "references to link-time properties; " + + "primitive operations on booleans; " + + "and comparisons on ints." + } + + @Test + def linkTimeErrorInvalidOp(): Unit = { + """ + object A { + def foo = + linkTimeIf((esVersion + 1) < ESVersion.ES2015) { } { } + } + """ hasErrors + s""" + |newSource1.scala:4: error: $IllegalLinkTimeIfArgMessage + | linkTimeIf((esVersion + 1) < ESVersion.ES2015) { } { } + | ^ + """ + } + + @Test + def linkTimeErrorInvalidEntities(): Unit = { + """ + object A { + def foo(x: String) = { + val bar = 1 + linkTimeIf(bar == 0) { } { } + } + } + """ hasErrors + s""" + |newSource1.scala:5: error: $IllegalLinkTimeIfArgMessage + | linkTimeIf(bar == 0) { } { } + | ^ + """ + + // String comparison is a `BinaryOp.===`, which is not allowed + """ + object A { + def foo(x: String) = + linkTimeIf("foo" == x) { } { } + } + """ hasErrors + s""" + |newSource1.scala:4: error: $IllegalLinkTimeIfArgMessage + | linkTimeIf("foo" == x) { } { } + | ^ + """ + + """ + object A { + def bar = true + def foo(x: String) = + linkTimeIf(bar || !bar) { } { } + } + """ hasErrors + s""" + |newSource1.scala:5: error: $IllegalLinkTimeIfArgMessage + | linkTimeIf(bar || !bar) { } { } + | ^ + |newSource1.scala:5: error: $IllegalLinkTimeIfArgMessage + | linkTimeIf(bar || !bar) { } { } + | ^ + """ + } + + @Test + def linkTimeCondInvalidTree(): Unit = { + """ + object A { + def bar = true + def foo(x: String) = + linkTimeIf(if (bar) true else false) { } { } + } + """ hasErrors + s""" + |newSource1.scala:5: error: $IllegalLinkTimeIfArgMessage + | linkTimeIf(if (bar) true else false) { } { } + | ^ + """ + } +} diff --git a/compiler/src/test/scala/org/scalajs/nscplugin/test/OptimizationTest.scala b/compiler/src/test/scala/org/scalajs/nscplugin/test/OptimizationTest.scala index b10bef4b95..f38e2adf28 100644 --- a/compiler/src/test/scala/org/scalajs/nscplugin/test/OptimizationTest.scala +++ b/compiler/src/test/scala/org/scalajs/nscplugin/test/OptimizationTest.scala @@ -582,6 +582,72 @@ class OptimizationTest extends JSASTTest { case js.LoadModule(`testName`) => } } + + @Test + def unsignedComparisonsInt: Unit = { + import js.BinaryOp._ + + val comparisons = List( + (Int_unsigned_<, "<"), + (Int_unsigned_<=, "<="), + (Int_unsigned_>, ">"), + (Int_unsigned_>=, ">=") + ) + + for ((op, codeOp) <- comparisons) { + s""" + class Test { + private final val SignBit = Int.MinValue + + def unsignedComparisonsInt(x: Int, y: Int): Unit = { + (x ^ 0x80000000) $codeOp (y ^ 0x80000000) + (SignBit ^ x) $codeOp (y ^ SignBit) + (SignBit ^ x) $codeOp 0x80000010 + 0x00000020 $codeOp (y ^ SignBit) + } + } + """.hasExactly(4, "unsigned comparisons") { + case js.BinaryOp(`op`, _, _) => + }.hasNot("any Int_^") { + case js.BinaryOp(Int_^, _, _) => + }.hasNot("any signed comparison") { + case js.BinaryOp(Int_< | Int_<= | Int_> | Int_>=, _, _) => + } + } + } + + @Test + def unsignedComparisonsLong: Unit = { + import js.BinaryOp._ + + val comparisons = List( + (Long_unsigned_<, "<"), + (Long_unsigned_<=, "<="), + (Long_unsigned_>, ">"), + (Long_unsigned_>=, ">=") + ) + + for ((op, codeOp) <- comparisons) { + s""" + class Test { + private final val SignBit = Long.MinValue + + def unsignedComparisonsInt(x: Long, y: Long): Unit = { + (x ^ 0x8000000000000000L) $codeOp (y ^ 0x8000000000000000L) + (SignBit ^ x) $codeOp (y ^ SignBit) + (SignBit ^ x) $codeOp 0x8000000000000010L + 0x0000000000000020L $codeOp (y ^ SignBit) + } + } + """.hasExactly(4, "unsigned comparisons") { + case js.BinaryOp(`op`, _, _) => + }.hasNot("any Long_^") { + case js.BinaryOp(Long_^, _, _) => + }.hasNot("any signed comparison") { + case js.BinaryOp(Long_< | Long_<= | Long_> | Long_>=, _, _) => + } + } + } } object OptimizationTest { diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Hashers.scala b/ir/shared/src/main/scala/org/scalajs/ir/Hashers.scala index ad94d65549..599e9e8c1c 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Hashers.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Hashers.scala @@ -206,6 +206,13 @@ object Hashers { mixTree(elsep) mixType(tree.tpe) + case LinkTimeIf(cond, thenp, elsep) => + mixTag(TagLinkTimeIf) + mixTree(cond) + mixTree(thenp) + mixTree(elsep) + mixType(tree.tpe) + case While(cond, body) => mixTag(TagWhile) mixTree(cond) diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Printers.scala b/ir/shared/src/main/scala/org/scalajs/ir/Printers.scala index c69ad1447c..216bec733e 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Printers.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Printers.scala @@ -93,6 +93,7 @@ object Printers { protected def printBlock(tree: Tree): Unit = { val trees = tree match { case Block(trees) => trees + case Skip() => Nil case _ => tree :: Nil } printBlock(trees) @@ -232,6 +233,14 @@ object Printers { printBlock(elsep) } + case LinkTimeIf(cond, thenp, elsep) => + print("link-time-if (") + print(cond) + print(") ") + printBlock(thenp) + print(" else ") + printBlock(elsep) + case While(cond, body) => print("while (") print(cond) @@ -436,6 +445,16 @@ object Printers { case UnwrapFromThrowable => p("(", ")") case Throw => p("throw ", "") + + case Float_toBits => p("(", ")") + case Float_fromBits => p("(", ")") + case Double_toBits => p("(", ")") + case Double_fromBits => p("(", ")") + + case Int_clz => p("(", ")") + case Long_clz => p("(", ")") + + case UnsignedIntToLong => p("(", ")") } case BinaryOp(BinaryOp.Int_-, IntLiteral(0), rhs) => @@ -562,6 +581,21 @@ object Printers { case Double_<= => "<=[double]" case Double_> => ">[double]" case Double_>= => ">=[double]" + + case Int_unsigned_/ => "unsigned_/[int]" + case Int_unsigned_% => "unsigned_%[int]" + case Long_unsigned_/ => "unsigned_/[long]" + case Long_unsigned_% => "unsigned_%[long]" + + case Int_unsigned_< => "unsigned_<[int]" + case Int_unsigned_<= => "unsigned_<=[int]" + case Int_unsigned_> => "unsigned_>[int]" + case Int_unsigned_>= => "unsigned_>=[int]" + + case Long_unsigned_< => "unsigned_<[long]" + case Long_unsigned_<= => "unsigned_<=[long]" + case Long_unsigned_> => "unsigned_>[long]" + case Long_unsigned_>= => "unsigned_>=[long]" }) print(' ') print(rhs) diff --git a/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala b/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala index 7ad9ee3876..23292cbcdc 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/ScalaJSVersions.scala @@ -17,8 +17,8 @@ import java.util.concurrent.ConcurrentHashMap import scala.util.matching.Regex object ScalaJSVersions extends VersionChecks( - current = "1.19.0", - binaryEmitted = "1.19" + current = "1.20.0-SNAPSHOT", + binaryEmitted = "1.20-SNAPSHOT" ) /** Helper class to allow for testing of logic. */ diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala b/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala index 7cc64e28e1..628630dfa1 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala @@ -297,6 +297,11 @@ object Serializers { writeTree(cond); writeTree(thenp); writeTree(elsep) writeType(tree.tpe) + case LinkTimeIf(cond, thenp, elsep) => + writeTagAndPos(TagLinkTimeIf) + writeTree(cond); writeTree(thenp); writeTree(elsep) + writeType(tree.tpe) + case While(cond, body) => writeTagAndPos(TagWhile) writeTree(cond); writeTree(body) @@ -1196,9 +1201,14 @@ object Serializers { Assign(lhs.asInstanceOf[AssignLhs], rhs) - case TagReturn => Return(readTree(), readLabelName()) - case TagIf => If(readTree(), readTree(), readTree())(readType()) - case TagWhile => While(readTree(), readTree()) + case TagReturn => + Return(readTree(), readLabelName()) + case TagIf => + If(readTree(), readTree(), readTree())(readType()) + case TagLinkTimeIf => + LinkTimeIf(readTree(), readTree(), readTree())(readType()) + case TagWhile => + While(readTree(), readTree()) case TagDoWhile => if (!hacks.useBelow(13)) diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Tags.scala b/ir/shared/src/main/scala/org/scalajs/ir/Tags.scala index bc7d2982b0..dc2862b7ec 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Tags.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Tags.scala @@ -135,6 +135,9 @@ private[ir] object Tags { final val TagNewLambda = TagApplyTypedClosure + 1 final val TagJSAwait = TagNewLambda + 1 + // New in 1.20 + final val TagLinkTimeIf = TagJSAwait + 1 + // Tags for member defs final val TagFieldDef = 1 diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Transformers.scala b/ir/shared/src/main/scala/org/scalajs/ir/Transformers.scala index 27d9086435..e95a154e1c 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Transformers.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Transformers.scala @@ -60,6 +60,9 @@ object Transformers { case If(cond, thenp, elsep) => If(transform(cond), transform(thenp), transform(elsep))(tree.tpe) + case LinkTimeIf(cond, thenp, elsep) => + LinkTimeIf(transform(cond), transform(thenp), transform(elsep))(tree.tpe) + case While(cond, body) => While(transform(cond), transform(body)) diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Traversers.scala b/ir/shared/src/main/scala/org/scalajs/ir/Traversers.scala index d5782da074..15c9da9093 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Traversers.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Traversers.scala @@ -48,6 +48,11 @@ object Traversers { traverse(thenp) traverse(elsep) + case LinkTimeIf(cond, thenp, elsep) => + traverse(cond) + traverse(thenp) + traverse(elsep) + case While(cond, body) => traverse(cond) traverse(body) diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala b/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala index ccc3b56196..d77fc80ba7 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Trees.scala @@ -168,6 +168,38 @@ object Trees { sealed case class If(cond: Tree, thenp: Tree, elsep: Tree)(val tpe: Type)( implicit val pos: Position) extends Tree + /** Link-time `if` expression. + * + * The `cond` must be a well-typed link-time tree of type `boolean`. + * + * A link-time tree is a `Tree` matching the following sub-grammar: + * + * {{{ + * link-time-tree ::= + * BooleanLiteral + * | IntLiteral + * | StringLiteral + * | LinkTimeProperty + * | UnaryOp(link-time-unary-op, link-time-tree) + * | BinaryOp(link-time-binary-op, link-time-tree, link-time-tree) + * | LinkTimeIf(link-time-tree, link-time-tree, link-time-tree) + * + * link-time-unary-op ::= + * Boolean_! + * + * link-time-binary-op ::= + * Boolean_== | Boolean_!= | Boolean_| | Boolean_& + * | Int_== | Int_!= | Int_< | Int_<= | Int_> | Int_>= + * }}} + * + * Note: nested `LinkTimeIf` nodes in the `cond` are used to encode + * short-circuiting boolean `&&` and `||`, just like we do with regular + * `If` nodes. + */ + sealed case class LinkTimeIf(cond: Tree, thenp: Tree, elsep: Tree)( + val tpe: Type)(implicit val pos: Position) + extends Tree + sealed case class While(cond: Tree, body: Tree)( implicit val pos: Position) extends Tree { val tpe = cond match { @@ -477,6 +509,17 @@ object Trees { final val UnwrapFromThrowable = 30 final val Throw = 31 + // Floating point bit manipulation, introduced in 1.20 + final val Float_toBits = 32 // (this is the raw version, without any guarantee for NaN bit patterns) + final val Float_fromBits = 33 + final val Double_toBits = 34 // (this is the raw version, without any guarantee for NaN bit patterns) + final val Double_fromBits = 35 + + // Other nodes introduced in 1.20 + final val Int_clz = 36 + final val Long_clz = 37 + final val UnsignedIntToLong = 38 + def isClassOp(op: Code): Boolean = op >= Class_name && op <= Class_superClass @@ -498,13 +541,14 @@ object Trees { case IntToShort => ShortType case CharToInt | ByteToInt | ShortToInt | LongToInt | DoubleToInt | - String_length | Array_length | IdentityHashCode => + String_length | Array_length | IdentityHashCode | Float_toBits | + Int_clz | Long_clz => IntType - case IntToLong | DoubleToLong => + case IntToLong | DoubleToLong | Double_toBits | UnsignedIntToLong => LongType - case DoubleToFloat | LongToFloat => + case DoubleToFloat | LongToFloat | Float_fromBits => FloatType - case IntToDouble | LongToDouble | FloatToDouble => + case IntToDouble | LongToDouble | FloatToDouble | Double_fromBits => DoubleType case CheckNotNull | Clone => argType.toNonNullable @@ -639,6 +683,23 @@ object Trees { final val Class_cast = 61 final val Class_newArray = 62 + // New in 1.20 + + final val Int_unsigned_/ = 63 + final val Int_unsigned_% = 64 + final val Long_unsigned_/ = 65 + final val Long_unsigned_% = 66 + + final val Int_unsigned_< = 67 + final val Int_unsigned_<= = 68 + final val Int_unsigned_> = 69 + final val Int_unsigned_>= = 70 + + final val Long_unsigned_< = 71 + final val Long_unsigned_<= = 72 + final val Long_unsigned_> = 73 + final val Long_unsigned_>= = 74 + def isClassOp(op: Code): Boolean = op >= Class_isInstance && op <= Class_newArray @@ -648,15 +709,19 @@ object Trees { Int_== | Int_!= | Int_< | Int_<= | Int_> | Int_>= | Long_== | Long_!= | Long_< | Long_<= | Long_> | Long_>= | Double_== | Double_!= | Double_< | Double_<= | Double_> | Double_>= | - Class_isInstance | Class_isAssignableFrom => + Class_isInstance | Class_isAssignableFrom | + Int_unsigned_< | Int_unsigned_<= | Int_unsigned_> | Int_unsigned_>= | + Long_unsigned_< | Long_unsigned_<= | Long_unsigned_> | Long_unsigned_>= => BooleanType case String_+ => StringType case Int_+ | Int_- | Int_* | Int_/ | Int_% | - Int_| | Int_& | Int_^ | Int_<< | Int_>>> | Int_>> => + Int_| | Int_& | Int_^ | Int_<< | Int_>>> | Int_>> | + Int_unsigned_/ | Int_unsigned_% => IntType case Long_+ | Long_- | Long_* | Long_/ | Long_% | - Long_| | Long_& | Long_^ | Long_<< | Long_>>> | Long_>> => + Long_| | Long_& | Long_^ | Long_<< | Long_>>> | Long_>> | + Long_unsigned_/ | Long_unsigned_% => LongType case Float_+ | Float_- | Float_* | Float_/ | Float_% => FloatType diff --git a/ir/shared/src/test/scala/org/scalajs/ir/PrintersTest.scala b/ir/shared/src/test/scala/org/scalajs/ir/PrintersTest.scala index 060bf4fdb8..ea68b14864 100644 --- a/ir/shared/src/test/scala/org/scalajs/ir/PrintersTest.scala +++ b/ir/shared/src/test/scala/org/scalajs/ir/PrintersTest.scala @@ -202,6 +202,61 @@ class PrintersTest { If(ref("x", BooleanType), ref("y", BooleanType), b(false))(BooleanType)) } + @Test def printLinkTimeIf(): Unit = { + assertPrintEquals( + """ + |link-time-if (true) { + | 5 + |} else { + | 6 + |} + """, + LinkTimeIf(b(true), i(5), i(6))(IntType)) + + assertPrintEquals( + """ + |link-time-if (true) { + | 5 + |} else { + |} + """, + LinkTimeIf(b(true), i(5), Skip())(VoidType)) + + assertPrintEquals( + """ + |link-time-if (true) { + | 5 + |} else { + | link-time-if (false) { + | 6 + | } else { + | 7 + | } + |} + """, + LinkTimeIf(b(true), i(5), LinkTimeIf(b(false), i(6), i(7))(IntType))(IntType)) + + assertPrintEquals( + """ + |link-time-if (x) { + | true + |} else { + | y + |} + """, + LinkTimeIf(ref("x", BooleanType), b(true), ref("y", BooleanType))(BooleanType)) + + assertPrintEquals( + """ + |link-time-if (x) { + | y + |} else { + | false + |} + """, + LinkTimeIf(ref("x", BooleanType), ref("y", BooleanType), b(false))(BooleanType)) + } + @Test def printWhile(): Unit = { assertPrintEquals( """ @@ -459,6 +514,16 @@ class PrintersTest { assertPrintEquals("(e)", UnaryOp(WrapAsThrowable, ref("e", AnyType))) assertPrintEquals("(e)", UnaryOp(UnwrapFromThrowable, ref("e", ClassType(ThrowableClass, nullable = true)))) + + assertPrintEquals("(x)", UnaryOp(Float_toBits, ref("x", FloatType))) + assertPrintEquals("(x)", UnaryOp(Float_fromBits, ref("x", IntType))) + assertPrintEquals("(x)", UnaryOp(Double_toBits, ref("x", DoubleType))) + assertPrintEquals("(x)", UnaryOp(Double_fromBits, ref("x", LongType))) + + assertPrintEquals("(x)", UnaryOp(Int_clz, ref("x", IntType))) + assertPrintEquals("(x)", UnaryOp(Long_clz, ref("x", LongType))) + + assertPrintEquals("(x)", UnaryOp(UnsignedIntToLong, ref("x", IntType))) } @Test def printPseudoUnaryOp(): Unit = { @@ -611,6 +676,33 @@ class PrintersTest { BinaryOp(Class_isAssignableFrom, classVarRef, ref("y", ClassType(ClassClass, nullable = false)))) assertPrintEquals("cast(x, y)", BinaryOp(Class_cast, classVarRef, ref("y", AnyType))) assertPrintEquals("newArray(x, y)", BinaryOp(Class_newArray, classVarRef, ref("y", IntType))) + + assertPrintEquals("(x unsigned_/[int] y)", + BinaryOp(Int_unsigned_/, ref("x", IntType), ref("y", IntType))) + assertPrintEquals("(x unsigned_%[int] y)", + BinaryOp(Int_unsigned_%, ref("x", IntType), ref("y", IntType))) + assertPrintEquals("(x unsigned_/[long] y)", + BinaryOp(Long_unsigned_/, ref("x", LongType), ref("y", LongType))) + assertPrintEquals("(x unsigned_%[long] y)", + BinaryOp(Long_unsigned_%, ref("x", LongType), ref("y", LongType))) + + assertPrintEquals("(x unsigned_<[int] y)", + BinaryOp(Int_unsigned_<, ref("x", IntType), ref("y", IntType))) + assertPrintEquals("(x unsigned_<=[int] y)", + BinaryOp(Int_unsigned_<=, ref("x", IntType), ref("y", IntType))) + assertPrintEquals("(x unsigned_>[int] y)", + BinaryOp(Int_unsigned_>, ref("x", IntType), ref("y", IntType))) + assertPrintEquals("(x unsigned_>=[int] y)", + BinaryOp(Int_unsigned_>=, ref("x", IntType), ref("y", IntType))) + + assertPrintEquals("(x unsigned_<[long] y)", + BinaryOp(Long_unsigned_<, ref("x", LongType), ref("y", LongType))) + assertPrintEquals("(x unsigned_<=[long] y)", + BinaryOp(Long_unsigned_<=, ref("x", LongType), ref("y", LongType))) + assertPrintEquals("(x unsigned_>[long] y)", + BinaryOp(Long_unsigned_>, ref("x", LongType), ref("y", LongType))) + assertPrintEquals("(x unsigned_>=[long] y)", + BinaryOp(Long_unsigned_>=, ref("x", LongType), ref("y", LongType))) } @Test def printNewArray(): Unit = { diff --git a/javalib/src/main/scala/java/io/DataOutputStream.scala b/javalib/src/main/scala/java/io/DataOutputStream.scala index 4b2f2ddda1..8112d8d0d7 100644 --- a/javalib/src/main/scala/java/io/DataOutputStream.scala +++ b/javalib/src/main/scala/java/io/DataOutputStream.scala @@ -62,10 +62,10 @@ class DataOutputStream(out: OutputStream) } final def writeFloat(v: Float): Unit = - writeInt(java.lang.Float.floatToIntBits(v)) + writeInt(java.lang.Float.floatToIntBits(v)) // must canonicalize NaNs final def writeDouble(v: Double): Unit = - writeLong(java.lang.Double.doubleToLongBits(v)) + writeLong(java.lang.Double.doubleToLongBits(v)) // must canonicalize NaNs final def writeBytes(s: String): Unit = { for (i <- 0 until s.length()) diff --git a/javalib/src/main/scala/java/lang/Double.scala b/javalib/src/main/scala/java/lang/Double.scala index aa6e3bc8d9..439f23c0d5 100644 --- a/javalib/src/main/scala/java/lang/Double.scala +++ b/javalib/src/main/scala/java/lang/Double.scala @@ -74,6 +74,9 @@ object Double { final val SIZE = 64 final val BYTES = 8 + private final val PosInfinityBits = 0x7ff0000000000000L + private final val CanonicalNaNBits = 0x7ff8000000000000L + @inline def `new`(value: scala.Double): Double = valueOf(value) @inline def `new`(s: String): Double = valueOf(s) @@ -280,7 +283,7 @@ object Double { val mbits = 52 // mantissa size val bias = (1 << (ebits - 1)) - 1 - val bits = doubleToLongBits(d) + val bits = doubleToRawLongBits(d) val s = bits < 0 val m = bits & ((1L << mbits) - 1L) val e = (bits >>> mbits).toInt & ((1 << ebits) - 1) // biased @@ -364,31 +367,79 @@ object Double { @inline def isFinite(d: scala.Double): scala.Boolean = !isNaN(d) && !isInfinite(d) + /** Hash code of a number (excluding Longs). + * + * Because of the common encoding for integer and floating point values, + * the hashCode of Floats and Doubles must align with that of Ints for the + * common values. + * + * For other values, we use the hashCode specified by the JavaDoc for + * *Doubles*, even for values which are valid Float values. Because of the + * previous point, we cannot align completely with the Java specification, + * so there is no point trying to be a bit more aligned here. Always using + * the Double version requires fewer branches. + * + * We use different code paths in JS and Wasm for performance reasons. + * The two implementations compute the same results. + */ @inline def hashCode(value: scala.Double): Int = { if (LinkingInfo.isWebAssembly) hashCodeForWasm(value) else - FloatingPointBits.numberHashCode(value) + hashCodeForJS(value) } - // See FloatingPointBits for the spec of this computation @inline private def hashCodeForWasm(value: scala.Double): Int = { - val bits = doubleToLongBits(value) + val bits = doubleToRawLongBits(value) val valueInt = value.toInt - if (doubleToLongBits(valueInt.toDouble) == bits) + if (doubleToRawLongBits(valueInt.toDouble) == bits) valueInt + else if (isNaNBitPattern(bits)) + Long.hashCode(CanonicalNaNBits) else Long.hashCode(bits) } - // Wasm intrinsic + @inline + private def hashCodeForJS(value: scala.Double): Int = { + val valueInt = (value.asInstanceOf[js.Dynamic] | 0.asInstanceOf[js.Dynamic]).asInstanceOf[Int] + if (valueInt.toDouble == value && 1.0/value != scala.Double.NegativeInfinity) + valueInt + else if (value != value) + Long.hashCode(CanonicalNaNBits) + else + Long.hashCode(doubleToRawLongBits(value)) + } + @inline def longBitsToDouble(bits: scala.Long): scala.Double = - FloatingPointBits.longBitsToDouble(bits) + throw new Error("stub") // body replaced by the compiler back-end + + @inline def doubleToRawLongBits(value: scala.Double): scala.Long = + throw new Error("stub") // body replaced by the compiler back-end + + @inline def doubleToLongBits(value: scala.Double): scala.Long = { + if (LinkingInfo.isWebAssembly) { + val rawBits = doubleToRawLongBits(value) + if (isNaNBitPattern(rawBits)) + CanonicalNaNBits + else + rawBits + } else { + /* On JS, the Long comparison inside isNaNBitPattern is expensive. + * We compare to NaN at the double level instead. + */ + if (value != value) + CanonicalNaNBits + else + doubleToRawLongBits(value) + } + } - // Wasm intrinsic - @inline def doubleToLongBits(value: scala.Double): scala.Long = - FloatingPointBits.doubleToLongBits(value) + @inline private def isNaNBitPattern(bits: scala.Long): scala.Boolean = { + // Both operands are non-negative; it does not matter whether the comparison is signed or not + (bits & ~scala.Long.MinValue) > PosInfinityBits + } @inline def sum(a: scala.Double, b: scala.Double): scala.Double = a + b diff --git a/javalib/src/main/scala/java/lang/Float.scala b/javalib/src/main/scala/java/lang/Float.scala index 8fa4ce3070..567bf9d098 100644 --- a/javalib/src/main/scala/java/lang/Float.scala +++ b/javalib/src/main/scala/java/lang/Float.scala @@ -13,9 +13,9 @@ package java.lang import java.lang.constant.{Constable, ConstantDesc} -import java.math.BigInteger import scala.scalajs.js +import scala.scalajs.LinkingInfo._ /* This is a hijacked class. Its instances are primitive numbers. * Constructors are not emitted. @@ -72,6 +72,9 @@ object Float { final val SIZE = 32 final val BYTES = 4 + private final val PosInfinityBits = 0x7f800000 + private final val CanonicalNaNBits = 0x7fc00000 + @inline def `new`(value: scala.Float): Float = valueOf(value) @inline def `new`(value: scala.Double): Float = valueOf(value.toFloat) @@ -226,9 +229,23 @@ object Float { fractionalPartStr: String, exponentStr: String, zDown: scala.Float, zUp: scala.Float, mid: scala.Double): scala.Float = { + /* Get the best available implementation of big integers for the given platform. + * + * If JS bigint's are supported, use them. Otherwise fall back on + * `java.math.BigInteger`. + * + * We need a `linkTimeIf` here because the JS bigint implementation uses + * the `**` operator, which does not link when `esVersion < ESVersion.ES2016`. + */ + val bigIntImpl = linkTimeIf[BigIntImpl](esVersion >= ESVersion.ES2020) { + BigIntImpl.JSBigInt + } { + BigIntImpl.JBigInteger + } + // 1. Accurately parse the string with the representation f × 10ᵉ - val f: BigInteger = new BigInteger(integralPartStr + fractionalPartStr) + val f: bigIntImpl.Repr = bigIntImpl.fromString(integralPartStr + fractionalPartStr) val e: Int = Integer.parseInt(exponentStr) - fractionalPartStr.length() /* Note: we know that `e` is "reasonable" (in the range [-324, +308]). If @@ -249,7 +266,7 @@ object Float { val kbits = 11 // number of bits of the exponent val bias = (1 << (kbits - 1)) - 1 // the bias of the exponent - val midBits = Double.doubleToLongBits(mid) + val midBits = Double.doubleToRawLongBits(mid) val biasedK = (midBits >> mbits).toInt /* Because `mid` is a double value halfway between two floats, it cannot @@ -261,24 +278,23 @@ object Float { val mExplicitBits = midBits & ((1L << mbits) - 1) val mImplicit1Bit = 1L << mbits // the implicit '1' bit of a normalized floating-point number - val m = BigInteger.valueOf(mExplicitBits | mImplicit1Bit) + val m = bigIntImpl.fromUnsignedLong53(mExplicitBits | mImplicit1Bit) val k = biasedK - bias - mbits // 3. Accurately compare f × 10ᵉ to m × 2ᵏ - @inline def compare(x: BigInteger, y: BigInteger): Int = - x.compareTo(y) + import bigIntImpl.{multiplyBy2Pow, multiplyBy10Pow} val cmp = if (e >= 0) { if (k >= 0) - compare(multiplyBy10Pow(f, e), multiplyBy2Pow(m, k)) + bigIntImpl.compare(multiplyBy10Pow(f, e), multiplyBy2Pow(m, k)) else - compare(multiplyBy2Pow(multiplyBy10Pow(f, e), -k), m) // this branch may be dead code in practice + bigIntImpl.compare(multiplyBy2Pow(multiplyBy10Pow(f, e), -k), m) // this branch may be dead code in practice } else { if (k >= 0) - compare(f, multiplyBy2Pow(multiplyBy10Pow(m, -e), k)) + bigIntImpl.compare(f, multiplyBy2Pow(multiplyBy10Pow(m, -e), k)) else - compare(multiplyBy2Pow(f, -k), multiplyBy10Pow(m, -e)) + bigIntImpl.compare(multiplyBy2Pow(f, -k), multiplyBy10Pow(m, -e)) } // 4. Choose zDown or zUp depending on the result of the comparison @@ -287,17 +303,60 @@ object Float { zDown else if (cmp > 0) zUp - else if ((floatToIntBits(zDown) & 1) == 0) // zDown is even + else if ((floatToRawIntBits(zDown) & 1) == 0) // zDown is even zDown else zUp } - @inline private def multiplyBy10Pow(v: BigInteger, e: Int): BigInteger = - v.multiply(BigInteger.TEN.pow(e)) + /** An implementation of big integer arithmetics that we need in the above method. */ + private sealed abstract class BigIntImpl { + type Repr + + def fromString(str: String): Repr + + /** Creates a big integer from a `Long` that needs at most 53 bits (unsigned). */ + def fromUnsignedLong53(x: scala.Long): Repr - @inline private def multiplyBy2Pow(v: BigInteger, e: Int): BigInteger = - v.shiftLeft(e) + def multiplyBy2Pow(v: Repr, e: Int): Repr + def multiplyBy10Pow(v: Repr, e: Int): Repr + + def compare(x: Repr, y: Repr): Int + } + + private object BigIntImpl { + object JSBigInt extends BigIntImpl { + type Repr = js.BigInt + + @inline def fromString(str: String): Repr = js.BigInt(str) + + // The 53-bit restriction guarantees that the conversion to `Double` is lossless. + @inline def fromUnsignedLong53(x: scala.Long): Repr = js.BigInt(x.toDouble) + + @inline def multiplyBy2Pow(v: Repr, e: Int): Repr = v << js.BigInt(e) + @inline def multiplyBy10Pow(v: Repr, e: Int): Repr = v * (js.BigInt(10) ** js.BigInt(e)) + + @inline def compare(x: Repr, y: Repr): Int = { + if (x < y) -1 + else if (x > y) 1 + else 0 + } + } + + object JBigInteger extends BigIntImpl { + import java.math.BigInteger + + type Repr = BigInteger + + @inline def fromString(str: String): Repr = new BigInteger(str) + @inline def fromUnsignedLong53(x: scala.Long): Repr = BigInteger.valueOf(x) + + @inline def multiplyBy2Pow(v: Repr, e: Int): Repr = v.shiftLeft(e) + @inline def multiplyBy10Pow(v: Repr, e: Int): Repr = v.multiply(BigInteger.TEN.pow(e)) + + @inline def compare(x: Repr, y: Repr): Int = x.compareTo(y) + } + } private def parseFloatHexadecimal(integralPartStr: String, fractionalPartStr: String, binaryExpStr: String): scala.Float = { @@ -371,13 +430,24 @@ object Float { @inline def hashCode(value: scala.Float): Int = Double.hashCode(value.toDouble) - // Wasm intrinsic @inline def intBitsToFloat(bits: scala.Int): scala.Float = - FloatingPointBits.intBitsToFloat(bits) + throw new Error("stub") // body replaced by the compiler back-end - // Wasm intrinsic - @inline def floatToIntBits(value: scala.Float): scala.Int = - FloatingPointBits.floatToIntBits(value) + @inline def floatToRawIntBits(value: scala.Float): scala.Int = + throw new Error("stub") // body replaced by the compiler back-end + + @inline def floatToIntBits(value: scala.Float): scala.Int = { + val rawBits = floatToRawIntBits(value) + if (isNaNBitPattern(rawBits)) + CanonicalNaNBits + else + rawBits + } + + @inline private def isNaNBitPattern(bits: scala.Int): scala.Boolean = { + // Both operands are non-negative; it does not matter whether the comparison is signed or not + (bits & ~Int.MinValue) > PosInfinityBits + } @inline def sum(a: scala.Float, b: scala.Float): scala.Float = a + b diff --git a/javalib/src/main/scala/java/lang/FloatingPointBits.scala b/javalib/src/main/scala/java/lang/FloatingPointBits.scala deleted file mode 100644 index 96e1c8f64c..0000000000 --- a/javalib/src/main/scala/java/lang/FloatingPointBits.scala +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Scala.js (https://www.scala-js.org/) - * - * Copyright EPFL. - * - * Licensed under Apache License 2.0 - * (https://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package java.lang - -import scala.scalajs.js -import scala.scalajs.js.Dynamic.global -import scala.scalajs.js.typedarray -import scala.scalajs.LinkingInfo.ESVersion - -/** Manipulating the bits of floating point numbers. */ -private[lang] object FloatingPointBits { - - import scala.scalajs.LinkingInfo - - private[this] val _areTypedArraysSupported = { - // Here we use the `esVersion` test to dce the 4 subsequent tests - LinkingInfo.esVersion >= ESVersion.ES2015 || { - js.typeOf(global.ArrayBuffer) != "undefined" && - js.typeOf(global.Int32Array) != "undefined" && - js.typeOf(global.Float32Array) != "undefined" && - js.typeOf(global.Float64Array) != "undefined" - } - } - - @inline - private def areTypedArraysSupported: scala.Boolean = { - /* We have a forwarder to the internal `val _areTypedArraysSupported` to - * be able to inline it. This achieves the following: - * * If we emit ES2015+, dce `|| _areTypedArraysSupported` and replace - * `areTypedArraysSupported` by `true` in the calling code, allowing - * polyfills in the calling code to be dce'ed in turn. - * * If we emit ES5, replace `areTypedArraysSupported` by - * `_areTypedArraysSupported` so we do not calculate it multiple times. - */ - LinkingInfo.esVersion >= ESVersion.ES2015 || _areTypedArraysSupported - } - - private val arrayBuffer = - if (areTypedArraysSupported) new typedarray.ArrayBuffer(8) - else null - - private val int32Array = - if (areTypedArraysSupported) new typedarray.Int32Array(arrayBuffer, 0, 2) - else null - - private val float32Array = - if (areTypedArraysSupported) new typedarray.Float32Array(arrayBuffer, 0, 2) - else null - - private val float64Array = - if (areTypedArraysSupported) new typedarray.Float64Array(arrayBuffer, 0, 1) - else null - - private val areTypedArraysBigEndian = { - if (areTypedArraysSupported) { - int32Array(0) = 0x01020304 - (new typedarray.Int8Array(arrayBuffer, 0, 8))(0) == 0x01 - } else { - true // as good a value as any - } - } - - private val highOffset = if (areTypedArraysBigEndian) 0 else 1 - private val lowOffset = if (areTypedArraysBigEndian) 1 else 0 - - private val floatPowsOf2: js.Array[scala.Double] = - if (areTypedArraysSupported) null - else makePowsOf2(len = 1 << 8, java.lang.Float.MIN_NORMAL.toDouble) - - private val doublePowsOf2: js.Array[scala.Double] = - if (areTypedArraysSupported) null - else makePowsOf2(len = 1 << 11, java.lang.Double.MIN_NORMAL) - - private def makePowsOf2(len: Int, minNormal: scala.Double): js.Array[scala.Double] = { - val r = new js.Array[scala.Double](len) - r(0) = 0.0 - var i = 1 - var next = minNormal - while (i != len - 1) { - r(i) = next - i += 1 - next *= 2 - } - r(len - 1) = scala.Double.PositiveInfinity - r - } - - /** Hash code of a number (excluding Longs). - * - * Because of the common encoding for integer and floating point values, - * the hashCode of Floats and Doubles must align with that of Ints for the - * common values. - * - * For other values, we use the hashCode specified by the JavaDoc for - * *Doubles*, even for values which are valid Float values. Because of the - * previous point, we cannot align completely with the Java specification, - * so there is no point trying to be a bit more aligned here. Always using - * the Double version should typically be faster on VMs without fround - * support because we avoid several fround operations. - */ - def numberHashCode(value: scala.Double): Int = { - val iv = rawToInt(value) - if (iv == value && 1.0/value != scala.Double.NegativeInfinity) { - iv - } else { - /* Basically an inlined version of `Long.hashCode(doubleToLongBits(value))`, - * so that we never allocate a RuntimeLong instance (or anything, for - * that matter). - * - * In addition, in the happy path where typed arrays are supported, since - * we xor together the two Ints, it doesn't matter which one comes first - * or second, and hence we can use constants 0 and 1 instead of having an - * indirection through `highOffset` and `lowOffset`. - */ - if (areTypedArraysSupported) { - float64Array(0) = value - int32Array(0) ^ int32Array(1) - } else { - doubleHashCodePolyfill(value) - } - } - } - - @noinline - private def doubleHashCodePolyfill(value: scala.Double): Int = - Long.hashCode(doubleToLongBitsPolyfillInline(value)) - - def intBitsToFloat(bits: Int): scala.Float = { - if (areTypedArraysSupported) { - int32Array(0) = bits - float32Array(0) - } else { - intBitsToFloatPolyfill(bits).toFloat - } - } - - def floatToIntBits(value: scala.Float): Int = { - if (areTypedArraysSupported) { - float32Array(0) = value - int32Array(0) - } else { - floatToIntBitsPolyfill(value) - } - } - - def longBitsToDouble(bits: scala.Long): scala.Double = { - if (areTypedArraysSupported) { - int32Array(highOffset) = (bits >>> 32).toInt - int32Array(lowOffset) = bits.toInt - float64Array(0) - } else { - longBitsToDoublePolyfill(bits) - } - } - - def doubleToLongBits(value: scala.Double): scala.Long = { - if (areTypedArraysSupported) { - float64Array(0) = value - ((int32Array(highOffset).toLong << 32) | - (int32Array(lowOffset).toLong & 0xffffffffL)) - } else { - doubleToLongBitsPolyfill(value) - } - } - - /* --- Polyfills for floating point bit manipulations --- - * - * Originally inspired by - * https://github.com/inexorabletash/polyfill/blob/3447582628b6e3ea81959c4d5987aa332c22d1ca/typedarray.js#L150-L264 - * - * Note that if typed arrays are not supported, it is almost certain that - * fround is not supported natively, so Float operations are extremely slow. - * - * We therefore do all computations in Doubles here. - */ - - private def intBitsToFloatPolyfill(bits: Int): scala.Double = { - val ebits = 8 - val fbits = 23 - val sign = (bits >> 31) | 1 // -1 or 1 - val e = (bits >> fbits) & ((1 << ebits) - 1) - val f = bits & ((1 << fbits) - 1) - decodeIEEE754(ebits, fbits, floatPowsOf2, scala.Float.MinPositiveValue, sign, e, f) - } - - private def floatToIntBitsPolyfill(floatValue: scala.Float): Int = { - // Some constants - val ebits = 8 - val fbits = 23 - - // Force computations to be on Doubles - val value = floatValue.toDouble - - // Determine sign bit and compute the absolute value av - val sign = if (value < 0.0 || (value == 0.0 && 1.0 / value < 0.0)) -1 else 1 - val s = sign & scala.Int.MinValue - val av = sign * value - - // Compute e and f - val powsOf2 = this.floatPowsOf2 // local cache - val e = encodeIEEE754Exponent(ebits, powsOf2, av) - val f = encodeIEEE754MantissaBits(ebits, fbits, powsOf2, scala.Float.MinPositiveValue.toDouble, av, e) - - // Encode - s | (e << fbits) | rawToInt(f) - } - - private def longBitsToDoublePolyfill(bits: scala.Long): scala.Double = { - val ebits = 11 - val fbits = 52 - val hifbits = fbits - 32 - val hi = (bits >>> 32).toInt - val lo = Utils.toUint(bits.toInt) - val sign = (hi >> 31) | 1 // -1 or 1 - val e = (hi >> hifbits) & ((1 << ebits) - 1) - val f = (hi & ((1 << hifbits) - 1)).toDouble * 0x100000000L.toDouble + lo - decodeIEEE754(ebits, fbits, doublePowsOf2, scala.Double.MinPositiveValue, sign, e, f) - } - - @noinline - private def doubleToLongBitsPolyfill(value: scala.Double): scala.Long = - doubleToLongBitsPolyfillInline(value) - - @inline - private def doubleToLongBitsPolyfillInline(value: scala.Double): scala.Long = { - // Some constants - val ebits = 11 - val fbits = 52 - val hifbits = fbits - 32 - - // Determine sign bit and compute the absolute value av - val sign = if (value < 0.0 || (value == 0.0 && 1.0 / value < 0.0)) -1 else 1 - val s = sign & scala.Int.MinValue - val av = sign * value - - // Compute e and f - val powsOf2 = this.doublePowsOf2 // local cache - val e = encodeIEEE754Exponent(ebits, powsOf2, av) - val f = encodeIEEE754MantissaBits(ebits, fbits, powsOf2, scala.Double.MinPositiveValue, av, e) - - // Encode - val hi = s | (e << hifbits) | rawToInt(f / 0x100000000L.toDouble) - val lo = rawToInt(f) - (hi.toLong << 32) | (lo.toLong & 0xffffffffL) - } - - @inline - private def decodeIEEE754(ebits: Int, fbits: Int, - powsOf2: js.Array[scala.Double], minPositiveValue: scala.Double, - sign: scala.Int, e: Int, f: scala.Double): scala.Double = { - - // Some constants - val specialExponent = (1 << ebits) - 1 - val twoPowFbits = (1L << fbits).toDouble - - if (e == specialExponent) { - // Special - if (f == 0.0) - sign * scala.Double.PositiveInfinity - else - scala.Double.NaN - } else if (e > 0) { - // Normalized - sign * powsOf2(e) * (1 + f / twoPowFbits) - } else { - // Subnormal - sign * f * minPositiveValue - } - } - - private def encodeIEEE754Exponent(ebits: Int, - powsOf2: js.Array[scala.Double], av: scala.Double): Int = { - - /* Binary search of `av` inside `powsOf2`. - * There are exactly `ebits` iterations of this loop (11 for Double, 8 for Float). - */ - var eMin = 0 - var eMax = 1 << ebits - while (eMin + 1 < eMax) { - val e = (eMin + eMax) >> 1 - if (av < powsOf2(e)) // false when av is NaN - eMax = e - else - eMin = e - } - eMin - } - - @inline - private def encodeIEEE754MantissaBits(ebits: Int, fbits: Int, - powsOf2: js.Array[scala.Double], minPositiveValue: scala.Double, - av: scala.Double, e: Int): scala.Double = { - - // Some constants - val specialExponent = (1 << ebits) - 1 - val twoPowFbits = (1L << fbits).toDouble - - if (e == specialExponent) { - if (av != av) - (1L << (fbits - 1)).toDouble // NaN - else - 0.0 // Infinity - } else { - if (e == 0) - av / minPositiveValue // Subnormal - else - ((av / powsOf2(e)) - 1.0) * twoPowFbits // Normal - } - } - - @inline private def rawToInt(x: scala.Double): Int = { - import scala.scalajs.js.DynamicImplicits.number2dynamic - (x | 0).asInstanceOf[Int] - } - -} diff --git a/javalib/src/main/scala/java/lang/Integer.scala b/javalib/src/main/scala/java/lang/Integer.scala index a4c2694365..fece6cd99f 100644 --- a/javalib/src/main/scala/java/lang/Integer.scala +++ b/javalib/src/main/scala/java/lang/Integer.scala @@ -61,6 +61,8 @@ object Integer { final val SIZE = 32 final val BYTES = 4 + private final val SignBit = Int.MinValue + @inline def `new`(value: scala.Int): Integer = valueOf(value) @inline def `new`(s: String): Integer = valueOf(s) @@ -72,56 +74,151 @@ object Integer { @inline def valueOf(s: String, radix: Int): Integer = valueOf(parseInt(s, radix)) - @inline def parseInt(s: String): scala.Int = parseInt(s, 10) + private def parseIntFail(s: String): Nothing = + throw new NumberFormatException(s"""For input string: "$s"""") - @noinline def parseInt(s: String, radix: scala.Int): scala.Int = - parseIntImpl(s, radix, signed = true) + @inline def parseInt(s: String): scala.Int = + parseIntImpl(s, 10, divideUnsigned(Int.MinValue, 10)) - @inline def parseUnsignedInt(s: String): scala.Int = parseUnsignedInt(s, 10) + @inline // because radix is almost certainly constant at call site + def parseInt(s: String, radix: scala.Int): scala.Int = { + if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) + parseIntFail(s) + parseIntImpl(s, radix, divideUnsigned(Int.MinValue, radix)) + } - @noinline def parseUnsignedInt(s: String, radix: scala.Int): scala.Int = - parseIntImpl(s, radix, signed = false) + /* Must be called only with a valid radix. + * + * The overflowBarrier must be divideUnsigned(Int.MinValue, radix). It will + * be used to detect overflow during the multiplication (and, a posteriori, + * for the addition of `+ digit` from the previous iteration). + * + * `Int.MinValue` is clearly the correct value for the negative case. + * + * For the positive case, in theory it should be `Int.MaxValue`. + * The only case where that would give a different quotient is when + * `MinValue = n * radix`. In that case, we will fail to detect the + * overflow if `result == (n - 1) * radix` just before the multiplication. + * After the multiplication, it will be `MinValue`, which is out of bounds. + * That's fine, though, because that case will be caught either on the + * next iteration of the loop, or in the final overflow check for the + * addition. + * + * That means we can always use the constant `Int.MinValue` here. + */ + @noinline + private def parseIntImpl(s: String, radix: Int, overflowBarrier: Int): Int = { + def fail(): Nothing = parseIntFail(s) - @inline - private def parseIntImpl(s: String, radix: scala.Int, - signed: scala.Boolean): scala.Int = { + // Early checks: s non-null and non-empty + if (s == null) + fail() + val len = s.length + if (len == 0) + fail() - def fail(): Nothing = - throw new NumberFormatException(s"""For input string: "$s"""") + // Load the module instance of Character once, instead of in every loop iteration + val character = Character - val len = if (s == null) 0 else s.length + /* Process the sign character. + * Set `sign` to `-1` if there is a leading '-', and `0` otherwise. + * Set `i` to 1 if there was a leading '+' or '-', and 0 otherwise. + */ + val firstChar = s.charAt(0) + val negative = firstChar == '-' + val sign = if (negative) -1 else 0 + var i = if (negative || firstChar == '+') 1 else 0 - if (len == 0 || radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) + // We need at least one digit + if (i >= len) fail() - val firstChar = s.charAt(0) - val negative = signed && firstChar == '-' + var result: Int = 0 - val maxAbsValue: scala.Double = { - if (!signed) 0xffffffffL.toDouble - else if (negative) 0x80000000L.toDouble - else 0x7fffffffL.toDouble + while (i != len) { + val digit = character.digitWithValidRadix(s.charAt(i), radix) + if (digit == -1 || (result ^ SignBit) > (overflowBarrier ^ SignBit)) + fail() + result = result * radix + digit + /* The above addition can overflow the range of valid results (but it + * cannot overflow the unsigned int range). If that happens during the + * last iteration, we catch it with the final overflow check. If it + * happens during an earlier iteration, we catch it with the + * `overflowBarrier`-based check. + */ + i += 1 } - var i = if (negative || firstChar == '+') 1 else 0 + /* Final overflow check. So far we computed `result` as an unsigned + * quantity. If negative, the maximum unsigned value allowed in + * `Int.MinValue`. If non-negative, it is `Int.MaxValue`. We can compute + * the right value without branches with `Int.MaxValue - sign`. + */ + if ((result ^ SignBit) > ((Int.MaxValue - sign) ^ SignBit)) + fail() + + /* Compute the final result. Use the standard trick to do this in a + * branchless way. + */ + (result ^ sign) - sign + } + + @inline def parseUnsignedInt(s: String): scala.Int = + parseUnsignedIntImpl(s, 10, divideUnsigned(-1, 10)) + + @inline // because radix is almost certainly constant at call site + def parseUnsignedInt(s: String, radix: scala.Int): scala.Int = { + if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) + parseIntFail(s) + parseUnsignedIntImpl(s, radix, divideUnsigned(-1, radix)) + } + + /* Must be called only with a valid radix. + * + * The overflowBarrier must be divideUnsigned(-1, radix). It will be used to + * detect overflow during the multiplication. + */ + @noinline + private def parseUnsignedIntImpl(s: String, radix: Int, + overflowBarrier: Int): Int = { + + def fail(): Nothing = parseIntFail(s) + + // Early checks: s non-null and non-empty + if (s == null) + fail() + val len = s.length + if (len == 0) + fail() + + // Load the module instance of Character once, instead of in every loop iteration + val character = Character + + // Process a possible leading '+' sign + var i = if (s.charAt(0) == '+') 1 else 0 // We need at least one digit - if (i >= s.length) + if (i >= len) fail() - var result: scala.Double = 0.0 + var result: Int = 0 + while (i != len) { - val digit = Character.digitWithValidRadix(s.charAt(i), radix) + val digit = character.digitWithValidRadix(s.charAt(i), radix) + if (digit == -1 || (result ^ SignBit) > (overflowBarrier ^ SignBit)) + fail() result = result * radix + digit - if (digit == -1 || result > maxAbsValue) + /* Unlike in `parseInt`, the addition overflows outside of the unsigned + * int range (obviously, otherwise it wouldn't be considered an overflow + * for `parseUnsignedInt`). We have to test for it at each iteration, + * as the `overflowBarrier`-based check cannot detect it. + */ + if ((result ^ SignBit) < (digit ^ SignBit)) fail() i += 1 } - if (negative) - asInt(-result) - else - asInt(result) + result } @inline def toString(i: scala.Int): String = "" + i @@ -186,18 +283,23 @@ object Integer { parse(s, base) } - @inline def compare(x: scala.Int, y: scala.Int): scala.Int = - if (x == y) 0 else if (x < y) -1 else 1 + @inline def compare(x: scala.Int, y: scala.Int): scala.Int = { + if (x == y) 0 + else if (x < y) -1 + else 1 + } @inline def compareUnsigned(x: scala.Int, y: scala.Int): scala.Int = { - import Utils.toUint if (x == y) 0 - else if (toUint(x) > toUint(y)) 1 - else -1 + else if ((x ^ Int.MinValue) < (y ^ Int.MinValue)) -1 + else 1 } @inline def toUnsignedLong(x: Int): scala.Long = - x.toLong & 0xffffffffL + throw new Error("stub") // body replaced by the compiler back-end + + @inline private[lang] def toUnsignedDouble(x: Int): scala.Double = + toUnsignedLong(x).toDouble // Wasm intrinsic def bitCount(i: scala.Int): scala.Int = { @@ -221,15 +323,11 @@ object Integer { (((t2 + (t2 >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24 } - // Wasm intrinsic @inline def divideUnsigned(dividend: Int, divisor: Int): Int = - if (divisor == 0) 0 / 0 - else asInt(asUint(dividend) / asUint(divisor)) + throw new Error("stub") // body replaced by the compiler back-end - // Wasm intrinsic @inline def remainderUnsigned(dividend: Int, divisor: Int): Int = - if (divisor == 0) 0 % 0 - else asInt(asUint(dividend) % asUint(divisor)) + throw new Error("stub") // body replaced by the compiler back-end @inline def highestOneBit(i: Int): Int = { /* The natural way of implementing this is: @@ -278,30 +376,8 @@ object Integer { @inline def signum(i: scala.Int): scala.Int = if (i == 0) 0 else if (i < 0) -1 else 1 - // Intrinsic, fallback on actual code for non-literal in JS - @inline def numberOfLeadingZeros(i: scala.Int): scala.Int = { - if (LinkingInfo.esVersion >= ESVersion.ES2015) js.Math.clz32(i) - else clz32Dynamic(i) - } - - private def clz32Dynamic(i: scala.Int) = { - if (js.typeOf(js.Dynamic.global.Math.clz32) == "function") { - js.Math.clz32(i) - } else { - // See Hacker's Delight, Section 5-3 - var x = i - if (x == 0) { - 32 - } else { - var r = 1 - if ((x & 0xffff0000) == 0) { x <<= 16; r += 16 } - if ((x & 0xff000000) == 0) { x <<= 8; r += 8 } - if ((x & 0xf0000000) == 0) { x <<= 4; r += 4 } - if ((x & 0xc0000000) == 0) { x <<= 2; r += 2 } - r + (x >> 31) - } - } - } + @inline def numberOfLeadingZeros(i: scala.Int): scala.Int = + throw new Error("stub") // body replaced by the compiler back-end // Wasm intrinsic @inline def numberOfTrailingZeros(i: scala.Int): scala.Int = @@ -332,16 +408,6 @@ object Integer { @inline private[this] def toStringBase(i: scala.Int, base: scala.Int): String = { import js.JSNumberOps.enableJSNumberOps - asUint(i).toString(base) - } - - @inline private def asInt(n: scala.Double): scala.Int = { - import js.DynamicImplicits.number2dynamic - (n | 0).asInstanceOf[Int] - } - - @inline private def asUint(n: scala.Int): scala.Double = { - import js.DynamicImplicits.number2dynamic - (n.toDouble >>> 0).asInstanceOf[scala.Double] + toUnsignedDouble(i).toString(base) } } diff --git a/javalib/src/main/scala/java/lang/Long.scala b/javalib/src/main/scala/java/lang/Long.scala index 0413372acf..bebf3501a1 100644 --- a/javalib/src/main/scala/java/lang/Long.scala +++ b/javalib/src/main/scala/java/lang/Long.scala @@ -15,6 +15,7 @@ package java.lang import scala.annotation.{switch, tailrec} import java.lang.constant.{Constable, ConstantDesc} +import java.util.ScalaOps._ import scala.scalajs.js @@ -65,33 +66,39 @@ object Long { private final val SignBit = scala.Long.MinValue + /** Quantities in this class are interpreted as unsigned values. */ private final class StringRadixInfo(val chunkLength: Int, - val radixPowLength: scala.Long, val paddingZeros: String, - val overflowBarrier: scala.Long) + val radixPowLength: Int, val radixPowLengthInverse: scala.Double, + val paddingZeros: String, val overflowBarrier: scala.Long) /** Precomputed table for toUnsignedStringInternalLarge and * parseUnsignedLongInternal. */ private lazy val StringRadixInfos: js.Array[StringRadixInfo] = { val r = new js.Array[StringRadixInfo]() - var radix = 0 - while (radix < Character.MIN_RADIX) { + for (radix <- 0 until Character.MIN_RADIX) r.push(null) - radix += 1 - } - while (radix <= Character.MAX_RADIX) { + for (radix <- Character.MIN_RADIX to Character.MAX_RADIX) { /* Find the biggest chunk size we can use. * - * - radixPowLength should be the biggest signed int32 value that is an - * exact power of radix. + * - radixPowLength should be the biggest exact power of radix that is <= 2^30. * - chunkLength is then log_radix(radixPowLength). * - paddingZeros is a string with exactly chunkLength '0's. * - overflowBarrier is divideUnsigned(-1L, radixPowLength) so that we * can test whether someValue * radixPowLength will overflow. + * + * It holds that 2^30 >= radixPowLength > 2^30 / maxRadix = 2^30 / 36 > 2^24. + * + * Therefore, (2^64 - 1) / radixPowLength < 2^(64-24) = 2^40, which + * comfortably fits in a `Double`. `toUnsignedStringLarge` relies on that + * property. + * + * Also, radixPowLength² < 2^63, and radixPowLength³ > 2^64. + * `parseUnsignedLongInternal` relies on that property. */ - val barrier = Int.MaxValue / radix + val barrier = Integer.divideUnsigned(1 << 30, radix) var radixPowLength = radix var chunkLength = 1 var paddingZeros = "0" @@ -100,11 +107,9 @@ object Long { chunkLength += 1 paddingZeros += "0" } - val radixPowLengthLong = radixPowLength.toLong - val overflowBarrier = Long.divideUnsigned(-1L, radixPowLengthLong) - r.push(new StringRadixInfo(chunkLength, radixPowLengthLong, - paddingZeros, overflowBarrier)) - radix += 1 + val overflowBarrier = Long.divideUnsigned(-1L, Integer.toUnsignedLong(radixPowLength)) + r.push(new StringRadixInfo(chunkLength, radixPowLength, + 1.0 / radixPowLength.toDouble, paddingZeros, overflowBarrier)) } r @@ -140,52 +145,84 @@ object Long { // Must be called only with valid radix private def toStringImpl(i: scala.Long, radix: Int): String = { + import js.JSNumberOps.enableJSNumberOps + val lo = i.toInt val hi = (i >>> 32).toInt + if (lo >> 31 == hi) { // It's a signed int32 - import js.JSNumberOps.enableJSNumberOps lo.toString(radix) - } else if (hi < 0) { - "-" + toUnsignedStringInternalLarge(-i, radix) + } else if (((hi ^ (hi >> 10)) & 0xffe00000) == 0) { // see RuntimeLong.isSignedSafeDouble + // (lo, hi) is small enough to be a Double, so toDouble is exact + i.toDouble.toString(radix) } else { - toUnsignedStringInternalLarge(i, radix) + val abs = Math.abs(i) + val s = toUnsignedStringInternalLarge(abs.toInt, (abs >>> 32).toInt, radix) + if (hi < 0) "-" + s else s } } // Must be called only with valid radix private def toUnsignedStringImpl(i: scala.Long, radix: Int): String = { - if ((i >>> 32).toInt == 0) { + import js.JSNumberOps.enableJSNumberOps + + val lo = i.toInt + val hi = (i >>> 32).toInt + + if (hi == 0) { // It's an unsigned int32 - import js.JSNumberOps.enableJSNumberOps - Utils.toUint(i.toInt).toString(radix) + Integer.toUnsignedDouble(lo).toString(radix) + } else if ((hi & 0xffe00000) == 0) { // see RuntimeLong.isUnsignedSafeDouble + // (lo, hi) is small enough to be a Double, so toDouble is exact + i.toDouble.toString(radix) } else { - toUnsignedStringInternalLarge(i, radix) + toUnsignedStringInternalLarge(lo, hi, radix) } } - // Must be called only with valid radix - private def toUnsignedStringInternalLarge(i: scala.Long, radix: Int): String = { + // Must be called only with valid radix and with (lo, hi) >= 2^53 + @inline // inlined twice: once in toStringImpl and once in toUnsignedStringImpl + private def toUnsignedStringInternalLarge(lo: Int, hi: Int, radix: Int): String = { import js.JSNumberOps.enableJSNumberOps import js.JSStringOps.enableJSStringOps + @inline def unsignedSafeDoubleLo(n: scala.Double): Int = { + import js.DynamicImplicits.number2dynamic + (n | 0).asInstanceOf[Int] + } + + val TwoPow32 = (1L << 32).toDouble + + /* See RuntimeLong.toUnsignedString for a proof. Although that proof is + * done in terms of a fixed divisor of 10^9, it generalizes to any + * divisor that statisfies 2^12 < divisor <= 2^30 and + * ULong.MaxValue / divisor < 2^53, which is true for `radixPowLength`. + */ + val radixInfo = StringRadixInfos(radix) val divisor = radixInfo.radixPowLength + val divisorInv = radixInfo.radixPowLengthInverse val paddingZeros = radixInfo.paddingZeros - val divisorXorSignBit = divisor.toLong ^ SignBit - - var res = "" - var value = i - while ((value ^ SignBit) >= divisorXorSignBit) { // unsigned comparison - val div = divideUnsigned(value, divisor) - val rem = value - div * divisor // == remainderUnsigned(value, divisor) - val remStr = rem.toInt.toString(radix) - res = paddingZeros.jsSubstring(remStr.length) + remStr + res - value = div + // initial approximation of the quotient and remainder + val approxNum = + Integer.toUnsignedDouble(hi) * TwoPow32 + Integer.toUnsignedDouble(lo) + var approxQuot = Math.floor(approxNum * divisorInv) + var approxRem = lo - divisor * unsignedSafeDoubleLo(approxQuot) + + // correct the approximations + if (approxRem < 0) { + approxQuot -= 1.0 + approxRem += divisor + } else if (approxRem >= divisor) { + approxQuot += 1.0 + approxRem -= divisor } - value.toInt.toString(radix) + res + // build the result string + val remStr = approxRem.toString(radix) + approxQuot.toString(radix) + paddingZeros.jsSubstring(remStr.length) + remStr } def parseLong(s: String, radix: Int): scala.Long = { @@ -291,7 +328,7 @@ object Long { firstResult } else { // Second chunk. Still cannot overflow. - val multiplier = radixInfo.radixPowLength + val multiplier = Integer.toUnsignedLong(radixInfo.radixPowLength) val secondChunkEnd = firstChunkEnd + chunkLen val secondResult = firstResult * multiplier + parseChunk(firstChunkEnd, secondChunkEnd) @@ -337,58 +374,25 @@ object Long { @inline def hashCode(value: scala.Long): Int = value.toInt ^ (value >>> 32).toInt - // Intrinsic + // RuntimeLong intrinsic @inline def compare(x: scala.Long, y: scala.Long): scala.Int = { if (x == y) 0 else if (x < y) -1 else 1 } - // TODO Intrinsic? - @inline def compareUnsigned(x: scala.Long, y: scala.Long): scala.Int = - compare(x ^ SignBit, y ^ SignBit) - - // Intrinsic, except for JS when using bigint's for longs - def divideUnsigned(dividend: scala.Long, divisor: scala.Long): scala.Long = - divModUnsigned(dividend, divisor, isDivide = true) - - // Intrinsic, except for JS when using bigint's for longs - def remainderUnsigned(dividend: scala.Long, divisor: scala.Long): scala.Long = - divModUnsigned(dividend, divisor, isDivide = false) - - private def divModUnsigned(a: scala.Long, b: scala.Long, - isDivide: scala.Boolean): scala.Long = { - /* This is a much simplified (and slow) version of - * RuntimeLong.unsignedDivModHelper. - */ - - if (b == 0L) - throw new ArithmeticException("/ by zero") - - var shift = numberOfLeadingZeros(b) - numberOfLeadingZeros(a) - var bShift = b << shift - - var rem = a - var quot = 0L + // TODO RuntimeLong intrinsic? + @inline def compareUnsigned(x: scala.Long, y: scala.Long): scala.Int = { + if (x == y) 0 + else if ((x ^ scala.Long.MinValue) < (y ^ scala.Long.MinValue)) -1 + else 1 + } - /* Invariants: - * bShift == b << shift == b * 2^shift - * quot >= 0 - * 0 <= rem < 2 * bShift - * quot * b + rem == a - */ - while (shift >= 0 && rem != 0) { - if ((rem ^ SignBit) >= (bShift ^ SignBit)) { - rem -= bShift - quot |= (1L << shift) - } - shift -= 1 - bShift >>>= 1 - } + @inline def divideUnsigned(dividend: scala.Long, divisor: scala.Long): scala.Long = + throw new Error("stub") // body replaced by the compiler back-end - if (isDivide) quot - else rem - } + @inline def remainderUnsigned(dividend: scala.Long, divisor: scala.Long): scala.Long = + throw new Error("stub") // body replaced by the compiler back-end @inline def highestOneBit(i: scala.Long): scala.Long = { @@ -455,13 +459,9 @@ object Long { else 1 } - // Wasm intrinsic @inline - def numberOfLeadingZeros(l: scala.Long): Int = { - val hi = (l >>> 32).toInt - if (hi != 0) Integer.numberOfLeadingZeros(hi) - else Integer.numberOfLeadingZeros(l.toInt) + 32 - } + def numberOfLeadingZeros(l: scala.Long): Int = + throw new Error("stub") // body replaced by the compiler back-end // Wasm intrinsic @inline diff --git a/javalib/src/main/scala/java/lang/Math.scala b/javalib/src/main/scala/java/lang/Math.scala index 7d77391990..348a7b8b41 100644 --- a/javalib/src/main/scala/java/lang/Math.scala +++ b/javalib/src/main/scala/java/lang/Math.scala @@ -26,8 +26,17 @@ object Math { @inline private def assumingES6: scala.Boolean = LinkingInfo.esVersion >= ESVersion.ES2015 - @inline def abs(a: scala.Int): scala.Int = if (a < 0) -a else a - @inline def abs(a: scala.Long): scala.Long = if (a < 0) -a else a + @inline def abs(a: scala.Int): scala.Int = { + // Hacker's Delight, Section 2-4 + val sign = a >> 31 + (a ^ sign) - sign + } + + // RuntimeLong intrinsic + @inline def abs(a: scala.Long): scala.Long = { + val sign = a >> 63 + (a ^ sign) - sign + } // Wasm intrinsics @inline def abs(a: scala.Float): scala.Float = js.Math.abs(a).toFloat @@ -53,71 +62,38 @@ object Math { // Wasm intrinsic def rint(a: scala.Double): scala.Double = { - /* Is the integer-valued `x` odd? Fused by hand of `(x.toLong & 1L) != 0L`. - * Corner cases: returns false for Infinities and NaN. - */ - @inline def isOdd(x: scala.Double): scala.Boolean = - (x.asInstanceOf[js.Dynamic] & 1.asInstanceOf[js.Dynamic]).asInstanceOf[Int] != 0 - - /* js.Math.round(a) does *almost* what we want. It rounds to nearest, - * breaking ties *up*. We need to break ties to *even*. So we need to - * detect ties, and for them, detect if we rounded to odd instead of even. - * - * The reasons why the apparently simple algorithm below works are subtle, - * and vary a lot depending on the range of `a`: - * - * - a is NaN - * r is NaN, then the == is false - * -> return r - * - * - a is +-Infinity - * r == a, then == is true! but isOdd(r) is false - * -> return r + /* We apply the technique described in Section II of + * Claude-Pierre Jeannerod, Jean-Michel Muller, Paul Zimmermann. + * On various ways to split a floating-point number. + * ARITH 2018 - 25th IEEE Symposium on Computer Arithmetic, + * Jun 2018, Amherst (MA), United States. + * pp.53-60, 10.1109/ARITH.2018.8464793. hal-01774587v2 + * available at + * https://hal.inria.fr/hal-01774587v2/document + * with β = 2, p = 53, and C = 2^(p-1) = 2^52. * - * - 2**53 <= abs(a) < Infinity - * r == a, r - 0.5 rounds back to a so == is true! - * fortunately, isOdd(r) is false because all a >= 2**53 are even - * -> return r + * That is only valid for values x <= 2^52. Fortunately, all values that + * are >= 2^52 are already integers, so we can return them as is. * - * - 2**52 <= abs(a) < 2**53 - * r == a (because all a's are integers in that range) - * - a is odd - * r - 0.5 rounds down (towards even) to r - 1.0 - * so a == r - 0.5 is false - * -> return r - * - a is even - * r - 0.5 rounds back up! (towards even) to r - * so a == r - 0.5 is true! - * but, isOdd(r) is false - * -> return r - * - * - 0.5 < abs(a) < 2**52 - * then -2**52 + 0.5 <= a <= 2**52 - 0.5 (because values in-between are not representable) - * since Math.round rounds *up* on ties, r is an integer in the range (-2**52, 2**52] - * r - 0.5 is therefore lossless - * so a == r - 0.5 accurately detects ties, and isOdd(r) breaks ties - * -> return `r`` or `r - 1.0` - * - * - a == +0.5 - * r == 1.0 - * a == r - 0.5 is true and isOdd(r) is true - * -> return `r - 1.0`, which is +0.0 - * - * - a == -0.5 - * r == -0.0 - * a == r - 0.5 is true and isOdd(r) is false - * -> return `r`, which is -0.0 - * - * - 0.0 <= abs(a) < 0.5 - * r == 0.0 with the same sign as a - * a == r - 0.5 is false - * -> return r + * We cannot use "the 1.5 trick" with C = 2^(p-1) + 2^(p-2) to handle + * negative numbers, because that would further reduce the range of valid + * `x` to maximum 2^51, although we actually need it up to 2^52. Therefore, + * we have a separate branch for negative numbers. This also allows to + * gracefully deal with the fact that we need to return -0.0 for values in + * the range [-0.5,-0.0). */ - val r = js.Math.round(a) - if ((a == r - 0.5) && isOdd(r)) - r - 1.0 - else - r + val C = 4503599627370496.0 // 2^52 + if (a > 0) { + if (a >= C) a + else (C + a) - C + } else if (a < 0) { + // do not "optimize" as `C - (C - a)`, as it would return +0.0 where it should return -0.0 + if (a <= -C) a + else -((C - a) - C) + } else { + // Handling zeroes here avoids the need to distinguish +0.0 from -0.0 + a // 0.0, -0.0 and NaN + } } @inline def round(a: scala.Float): scala.Int = js.Math.round(a).toInt @@ -202,7 +178,7 @@ object Math { } else if (a == -0.0) { // also matches +0.0 but that's fine scala.Double.MinPositiveValue } else { - val abits = Double.doubleToLongBits(a) + val abits = Double.doubleToRawLongBits(a) val rbits = if (a > 0) abits + 1L else abits - 1L Double.longBitsToDouble(rbits) } @@ -214,7 +190,7 @@ object Math { } else if (a == -0.0f) { // also matches +0.0f but that's fine scala.Float.MinPositiveValue } else { - val abits = Float.floatToIntBits(a) + val abits = Float.floatToRawIntBits(a) val rbits = if (a > 0) abits + 1 else abits - 1 Float.intBitsToFloat(rbits) } @@ -226,7 +202,7 @@ object Math { } else if (a == 0.0) { // also matches -0.0 but that's fine -scala.Double.MinPositiveValue } else { - val abits = Double.doubleToLongBits(a) + val abits = Double.doubleToRawLongBits(a) val rbits = if (a > 0) abits - 1L else abits + 1L Double.longBitsToDouble(rbits) } @@ -238,7 +214,7 @@ object Math { } else if (a == 0.0f) { // also matches -0.0f but that's fine -scala.Float.MinPositiveValue } else { - val abits = Float.floatToIntBits(a) + val abits = Float.floatToRawIntBits(a) val rbits = if (a > 0) abits - 1 else abits + 1 Float.intBitsToFloat(rbits) } @@ -462,6 +438,43 @@ object Math { if (a >= Integer.MIN_VALUE && a <= Integer.MAX_VALUE) a.toInt else throw new ArithmeticException("Integer overflow") + // RuntimeLong intrinsic + @inline + def multiplyFull(x: scala.Int, y: scala.Int): scala.Long = + x.toLong * y.toLong + + @inline + def multiplyHigh(x: scala.Long, y: scala.Long): scala.Long = { + /* Hacker's Delight, Section 8-2, Figure 8-2, + * where we have "inlined" all the variables used only once to help our + * optimizer perform simplifications. + */ + + val x0 = x & 0xffffffffL + val x1 = x >> 32 + val y0 = y & 0xffffffffL + val y1 = y >> 32 + + val t = x1 * y0 + ((x0 * y0) >>> 32) + x1 * y1 + (t >> 32) + (((t & 0xffffffffL) + x0 * y1) >> 32) + } + + @inline + def unsignedMultiplyHigh(x: scala.Long, y: scala.Long): scala.Long = { + /* Hacker's Delight, Section 8-2: + * > For an unsigned version, simply change all the int declarations to unsigned. + * In Scala, that means changing all the >> into >>>. + */ + + val x0 = x & 0xffffffffL + val x1 = x >>> 32 + val y0 = y & 0xffffffffL + val y1 = y >>> 32 + + val t = x1 * y0 + ((x0 * y0) >>> 32) + x1 * y1 + (t >>> 32) + (((t & 0xffffffffL) + x0 * y1) >>> 32) + } + def floorDiv(a: scala.Int, b: scala.Int): scala.Int = { val quot = a / b if ((a < 0) == (b < 0) || quot * b == a) quot diff --git a/javalib/src/main/scala/java/lang/System.scala b/javalib/src/main/scala/java/lang/System.scala index 976ea7ff15..d6ee87996f 100644 --- a/javalib/src/main/scala/java/lang/System.scala +++ b/javalib/src/main/scala/java/lang/System.scala @@ -184,7 +184,7 @@ object System { @inline def identityHashCode(x: Any): scala.Int = - scala.scalajs.runtime.identityHashCode(x.asInstanceOf[AnyRef]) + throw new Error("stub") // body replaced by the compiler back-end // System properties -------------------------------------------------------- diff --git a/javalib/src/main/scala/java/lang/Utils.scala b/javalib/src/main/scala/java/lang/Utils.scala index 94d323e026..35e38e5a68 100644 --- a/javalib/src/main/scala/java/lang/Utils.scala +++ b/javalib/src/main/scala/java/lang/Utils.scala @@ -187,9 +187,4 @@ private[java] object Utils { false // scalastyle:on return } - - @inline def toUint(x: scala.Double): scala.Double = { - import js.DynamicImplicits.number2dynamic - (x >>> 0).asInstanceOf[scala.Double] - } } diff --git a/javalib/src/main/scala/java/math/BigDecimal.scala b/javalib/src/main/scala/java/math/BigDecimal.scala index d045ffc57e..617112fa46 100644 --- a/javalib/src/main/scala/java/math/BigDecimal.scala +++ b/javalib/src/main/scala/java/math/BigDecimal.scala @@ -504,7 +504,7 @@ class BigDecimal() extends Number with Comparable[BigDecimal] { if (JDouble.isInfinite(dVal) || JDouble.isNaN(dVal)) throw new NumberFormatException("Infinity or NaN: " + dVal) - val bits = java.lang.Double.doubleToLongBits(dVal) + val bits = java.lang.Double.doubleToRawLongBits(dVal) // Extracting the exponent, note that the bias is 1023 _scale = 1075 - ((bits >> 52) & 2047).toInt // Extracting the 52 bits of the mantissa. diff --git a/javalib/src/main/scala/java/nio/ByteArrayBits.scala b/javalib/src/main/scala/java/nio/ByteArrayBits.scala index 1b4a326e6c..4fb158cd3e 100644 --- a/javalib/src/main/scala/java/nio/ByteArrayBits.scala +++ b/javalib/src/main/scala/java/nio/ByteArrayBits.scala @@ -170,12 +170,12 @@ private[nio] final class ByteArrayBits( @inline private def unmakeFloat(f: Float): (Byte, Byte, Byte, Byte) = - unmakeInt(java.lang.Float.floatToIntBits(f)) + unmakeInt(java.lang.Float.floatToRawIntBits(f)) // NaN bit patterns are unspecified here @inline private def unmakeDouble( d: Double): (Byte, Byte, Byte, Byte, Byte, Byte, Byte, Byte) = - unmakeLong(java.lang.Double.doubleToLongBits(d)) + unmakeLong(java.lang.Double.doubleToRawLongBits(d)) // NaN bit patterns are unspecified here // Loading and storing bytes diff --git a/javalib/src/main/scala/java/util/ArrayDeque.scala b/javalib/src/main/scala/java/util/ArrayDeque.scala index b45e075d03..53a048a46c 100644 --- a/javalib/src/main/scala/java/util/ArrayDeque.scala +++ b/javalib/src/main/scala/java/util/ArrayDeque.scala @@ -17,15 +17,11 @@ import java.lang.Utils._ import java.util.ScalaOps._ -import scala.scalajs.js - class ArrayDeque[E] private (initialCapacity: Int) extends AbstractCollection[E] with Deque[E] with Cloneable with Serializable { self => - private val inner: js.Array[E] = new js.Array[E](Math.max(initialCapacity, 16)) - - fillNulls(0, inner.length) + private var inner: Array[AnyRef] = new Array[AnyRef](Math.max(initialCapacity, 16)) private var status = 0 private var startIndex = 0 // inclusive, 0 <= startIndex < inner.length @@ -56,7 +52,7 @@ class ArrayDeque[E] private (initialCapacity: Int) startIndex -= 1 if (startIndex < 0) startIndex = inner.length - 1 - inner(startIndex) = e + inner(startIndex) = e.asInstanceOf[AnyRef] status += 1 empty = false true @@ -71,7 +67,7 @@ class ArrayDeque[E] private (initialCapacity: Int) endIndex += 1 if (endIndex > inner.length) endIndex = 1 - inner(endIndex - 1) = e + inner(endIndex - 1) = e.asInstanceOf[AnyRef] status += 1 empty = false true @@ -95,8 +91,8 @@ class ArrayDeque[E] private (initialCapacity: Int) def pollFirst(): E = { if (isEmpty()) null.asInstanceOf[E] else { - val res = inner(startIndex) - inner(startIndex) = null.asInstanceOf[E] // free reference for GC + val res = inner(startIndex).asInstanceOf[E] + inner(startIndex) = null // free reference for GC startIndex += 1 if (startIndex == endIndex) empty = true @@ -111,8 +107,8 @@ class ArrayDeque[E] private (initialCapacity: Int) if (isEmpty()) { null.asInstanceOf[E] } else { - val res = inner(endIndex - 1) - inner(endIndex - 1) = null.asInstanceOf[E] // free reference for GC + val res = inner(endIndex - 1).asInstanceOf[E] + inner(endIndex - 1) = null // free reference for GC endIndex -= 1 if (startIndex == endIndex) empty = true @@ -139,12 +135,12 @@ class ArrayDeque[E] private (initialCapacity: Int) def peekFirst(): E = { if (isEmpty()) null.asInstanceOf[E] - else inner(startIndex) + else inner(startIndex).asInstanceOf[E] } def peekLast(): E = { if (isEmpty()) null.asInstanceOf[E] - else inner(endIndex - 1) + else inner(endIndex - 1).asInstanceOf[E] } def removeFirstOccurrence(o: Any): Boolean = { @@ -222,7 +218,7 @@ class ArrayDeque[E] private (initialCapacity: Int) else if (nextIndex >= inner.length) nextIndex = 0 - inner(lastIndex) + inner(lastIndex).asInstanceOf[E] } override def remove(): Unit = { @@ -278,7 +274,7 @@ class ArrayDeque[E] private (initialCapacity: Int) nextIndex = inner.length - 1 } - inner(lastIndex) + inner(lastIndex).asInstanceOf[E] } override def remove(): Unit = { @@ -358,20 +354,14 @@ class ArrayDeque[E] private (initialCapacity: Int) // Nothing to do (constructor ensures capacity is always non-zero). } else if (startIndex == 0 && endIndex == inner.length) { val oldCapacity = inner.length - inner.length *= 2 - // no copying required: We just keep adding to the end. - // However, ensure array is dense. - fillNulls(oldCapacity, inner.length) + // No moving required within the array; we grow only at the end. + inner = Arrays.copyOf(inner, oldCapacity * 2) } else if (startIndex == endIndex) { val oldCapacity = inner.length - inner.length *= 2 // move beginning of array to end - for (i <- 0 until endIndex) { - inner(i + oldCapacity) = inner(i) - inner(i) = null.asInstanceOf[E] // free old reference for GC - } - // ensure rest of array is dense - fillNulls(endIndex + oldCapacity, inner.length) + val newArr = new Array[AnyRef](oldCapacity * 2) + System.arraycopy(inner, 0, newArr, oldCapacity, endIndex) + inner = newArr endIndex += oldCapacity } } @@ -398,9 +388,8 @@ class ArrayDeque[E] private (initialCapacity: Int) true } else if (target < endIndex) { // Shift elements from endIndex towards target - for (i <- target until endIndex - 1) - inner(i) = inner(i + 1) - inner(endIndex - 1) = null.asInstanceOf[E] // free reference for GC + System.arraycopy(inner, target + 1, inner, target, endIndex - (target + 1)) + inner(endIndex - 1) = null // free reference for GC status += 1 /* Note that endIndex >= 2: @@ -429,13 +418,8 @@ class ArrayDeque[E] private (initialCapacity: Int) * ==> contradiction. */ - // for (i <- target until startIndex by -1) - var i = target - while (i != startIndex) { - inner(i) = inner(i - 1) - i -= 1 - } - inner(startIndex) = null.asInstanceOf[E] // free reference for GC + System.arraycopy(inner, startIndex, inner, startIndex + 1, target - startIndex) + inner(startIndex) = null // free reference for GC status += 1 @@ -451,9 +435,4 @@ class ArrayDeque[E] private (initialCapacity: Int) false } } - - private def fillNulls(from: Int, until: Int): Unit = { - for (i <- from until until) - inner(i) = null.asInstanceOf[E] - } } diff --git a/javalib/src/main/scala/java/util/ArrayList.scala b/javalib/src/main/scala/java/util/ArrayList.scala index 68b9705f62..1c67de682b 100644 --- a/javalib/src/main/scala/java/util/ArrayList.scala +++ b/javalib/src/main/scala/java/util/ArrayList.scala @@ -14,80 +14,181 @@ package java.util import java.lang.Cloneable import java.lang.Utils._ +import java.util.ScalaOps._ import scala.scalajs._ +import scala.scalajs.LinkingInfo.isWebAssembly -class ArrayList[E] private (private[ArrayList] val inner: js.Array[E]) +class ArrayList[E] private (innerInit: AnyRef, private var _size: Int) extends AbstractList[E] with RandomAccess with Cloneable with Serializable { self => + /* This class has two different implementations for handling the + * internal data storage, depending on whether we are on Wasm or JS. + * On JS, we utilize `js.Array`. On Wasm, for performance reasons, + * we avoid JS interop and use a scala.Array. + * The `_size` field (unused in JS) keeps track of the effective size + * of the underlying Array for the Wasm implementation. + */ + + private val innerJS: js.Array[E] = + if (isWebAssembly) null + else innerInit.asInstanceOf[js.Array[E]] + + private var innerWasm: Array[AnyRef] = + if (!isWebAssembly) null + else innerInit.asInstanceOf[Array[AnyRef]] + def this(initialCapacity: Int) = { - this(new js.Array[E]) - if (initialCapacity < 0) - throw new IllegalArgumentException + this( + { + if (initialCapacity < 0) + throw new IllegalArgumentException + if (isWebAssembly) new Array[AnyRef](initialCapacity) + else new js.Array[E] + }, + 0 + ) } - def this() = - this(new js.Array[E]) + def this() = this(16) def this(c: Collection[_ <: E]) = { - this() + this(c.size()) addAll(c) } def trimToSize(): Unit = { - // We ignore this as js.Array doesn't support explicit pre-allocation + if (isWebAssembly) + resizeTo(size()) + // We ignore this in JS as js.Array doesn't support explicit pre-allocation } def ensureCapacity(minCapacity: Int): Unit = { - // We ignore this as js.Array doesn't support explicit pre-allocation + if (isWebAssembly) { + if (innerWasm.length < minCapacity) { + if (minCapacity > (1 << 30)) + resizeTo(minCapacity) + else + resizeTo(((1 << 31) >>> (Integer.numberOfLeadingZeros(minCapacity - 1)) - 1)) + } + } + // We ignore this in JS as js.Array doesn't support explicit pre-allocation } def size(): Int = - inner.length - - override def clone(): AnyRef = - new ArrayList(inner.jsSlice(0)) + if (isWebAssembly) _size + else innerJS.length + + override def clone(): AnyRef = { + if (isWebAssembly) + new ArrayList(innerWasm.clone(), size()) + else + new ArrayList(innerJS.jsSlice(0), 0) + } def get(index: Int): E = { checkIndexInBounds(index) - inner(index) + if (isWebAssembly) + innerWasm(index).asInstanceOf[E] + else + innerJS(index) } override def set(index: Int, element: E): E = { val e = get(index) - inner(index) = element + if (isWebAssembly) + innerWasm(index) = element.asInstanceOf[AnyRef] + else + innerJS(index) = element e } override def add(e: E): Boolean = { - inner.push(e) + if (isWebAssembly) { + if (size() >= innerWasm.length) + expand() + innerWasm(size()) = e.asInstanceOf[AnyRef] + _size += 1 + } else { + innerJS.push(e) + } true } override def add(index: Int, element: E): Unit = { checkIndexOnBounds(index) - inner.splice(index, 0, element) + if (isWebAssembly) { + if (size() >= innerWasm.length) + expand() + System.arraycopy(innerWasm, index, innerWasm, index + 1, size() - index) + innerWasm(index) = element.asInstanceOf[AnyRef] + _size += 1 + } else { + innerJS.splice(index, 0, element) + } } override def remove(index: Int): E = { checkIndexInBounds(index) - arrayRemoveAndGet(inner, index) + if (isWebAssembly) { + val removed = innerWasm(index).asInstanceOf[E] + System.arraycopy(innerWasm, index + 1, innerWasm, index, size() - index - 1) + innerWasm(size - 1) = null // free reference for GC + _size -= 1 + removed + } else { + arrayRemoveAndGet(innerJS, index) + } } override def clear(): Unit = - inner.length = 0 + if (isWebAssembly) { + Arrays.fill(innerWasm, null) // free references for GC + _size = 0 + } else { + innerJS.length = 0 + } override def addAll(index: Int, c: Collection[_ <: E]): Boolean = { c match { case other: ArrayList[_] => - inner.splice(index, 0, other.inner.toSeq: _*) + checkIndexOnBounds(index) + if (isWebAssembly) { + ensureCapacity(size() + other.size()) + System.arraycopy(innerWasm, index, innerWasm, index + other.size(), size() - index) + System.arraycopy(other.innerWasm, 0, innerWasm, index, other.size()) + _size += c.size() + } else { + innerJS.splice(index, 0, other.innerJS.toSeq: _*) + } other.size() > 0 case _ => super.addAll(index, c) } } - override protected def removeRange(fromIndex: Int, toIndex: Int): Unit = - inner.splice(fromIndex, toIndex - fromIndex) + override protected def removeRange(fromIndex: Int, toIndex: Int): Unit = { + if (fromIndex < 0 || toIndex > size() || toIndex < fromIndex) + throw new IndexOutOfBoundsException() + if (isWebAssembly) { + if (fromIndex != toIndex) { + System.arraycopy(innerWasm, toIndex, innerWasm, fromIndex, size() - toIndex) + val newSize = size() - toIndex + fromIndex + Arrays.fill(innerWasm, newSize, size(), null) // free references for GC + _size = newSize + } + } else { + innerJS.splice(fromIndex, toIndex - fromIndex) + } + } + // Wasm only + private def expand(): Unit = { + resizeTo(Math.max(innerWasm.length * 2, 16)) + } + + // Wasm only + private def resizeTo(newCapacity: Int): Unit = { + innerWasm = Arrays.copyOf(innerWasm, newCapacity) + } } diff --git a/javalib/src/main/scala/java/util/Formatter.scala b/javalib/src/main/scala/java/util/Formatter.scala index 909fab1929..18b11dc24e 100644 --- a/javalib/src/main/scala/java/util/Formatter.scala +++ b/javalib/src/main/scala/java/util/Formatter.scala @@ -638,7 +638,7 @@ final class Formatter private (private[this] var dest: Appendable, val mbitsMask = ((1L << mbits) - 1L) val bias = (1 << (ebits - 1)) - 1 - val bits = JDouble.doubleToLongBits(arg) + val bits = JDouble.doubleToRawLongBits(arg) val negative = bits < 0 val explicitMBits = bits & mbitsMask val biasedExponent = (bits >>> mbits).toInt & ((1 << ebits) - 1) diff --git a/javalib/src/main/scala/java/util/Random.scala b/javalib/src/main/scala/java/util/Random.scala index 7840b8c3c1..1c6819a7cc 100644 --- a/javalib/src/main/scala/java/util/Random.scala +++ b/javalib/src/main/scala/java/util/Random.scala @@ -14,7 +14,6 @@ package java.util import scala.annotation.tailrec -import scala.scalajs.js import scala.scalajs.LinkingInfo import java.util.random.RandomGenerator @@ -23,15 +22,19 @@ class Random(seed_in: Long) extends AnyRef with RandomGenerator with java.io.Serializable { /* This class has two different implementations of seeding and computing - * bits, depending on whether we are on Wasm or JS. On Wasm, we use the - * implementation specified in the JavaDoc verbatim. On JS, however, that is - * too slow, due to the use of `Long`s. Therefore, we decompose the - * computations using 2x24 bits. See `nextJS()` for details. + * bits, depending on whether we are on Wasm or JS. + * + * On Wasm, we use the implementation specified in the JavaDoc verbatim. + * + * On JS, the naive implementation is too slow, due to the use of `Long`s. + * We use semantically equivalent formulas that better fold away. + * We also separately store the 2x32 bits of the `Long`, in order not to + * allocate a `RuntimeLong` when storing in the field. */ private var seed: Long = _ // the full seed on Wasm (dce'ed on JS) - private var seedHi: Int = _ // 24 msb of the seed in JS (dce'ed on Wasm) - private var seedLo: Int = _ // 24 lsb of the seed in JS (dce'ed on Wasm) + private var seedHi: Int = _ // 32 msb of the seed in JS (dce'ed on Wasm) + private var seedLo: Int = _ // 32 lsb of the seed in JS (dce'ed on Wasm) // see nextGaussian() private var nextNextGaussian: Double = _ @@ -46,8 +49,8 @@ class Random(seed_in: Long) if (LinkingInfo.isWebAssembly) { this.seed = seed } else { - seedHi = (seed >>> 24).toInt - seedLo = seed.toInt & ((1 << 24) - 1) + seedHi = (seed >>> 32).toInt + seedLo = seed.toInt } haveNextNextGaussian = false } @@ -67,49 +70,35 @@ class Random(seed_in: Long) @inline private def nextJS(bits: Int): Int = { - /* This method is originally supposed to work with a Long seed from which - * 48 bits are used. - * Since Longs are too slow, we manually decompose the 48-bit seed in two - * parts of 24 bits each. - * The computation below is the translation in 24-by-24 bits of the - * specified computation, taking care never to produce intermediate values - * requiring more than 52 bits of precision. + /* Spec: seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1) + * + * Instead we compute the new seed << 16 (where 16 = 64 - 48). + * This is done by shifting both constants by 16 (appending 0000 at the end + * of their hex value) and removing the & ... + * + * Then we compute new values of `seedHi`, `seedLo` and the result by + * adding 16 to all the shifts. + * + * By doing this, the `a0` part of the multiplicative constants is `0`. + * That allows the optimizer to constant-fold away 2 of the 6 int + * multiplications it would normally have to do. */ - @inline - def rawToInt(x: Double): Int = - (x.asInstanceOf[js.Dynamic] | 0.asInstanceOf[js.Dynamic]).asInstanceOf[Int] - - @inline - def _24msbOf(x: Double): Int = rawToInt(x / (1 << 24).toDouble) - - @inline - def _24lsbOf(x: Double): Int = rawToInt(x) & ((1 << 24) - 1) - - // seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1) - - val oldSeedHi = seedHi - val oldSeedLo = seedLo - - val mul = 0x5DEECE66DL - val mulHi = (mul >>> 24).toInt - val mulLo = mul.toInt & ((1 << 24) - 1) - - val loProd = oldSeedLo.toDouble * mulLo.toDouble + 0xB - val hiProd = oldSeedLo.toDouble * mulHi.toDouble + oldSeedHi.toDouble * mulLo.toDouble - val newSeedHi = - (_24msbOf(loProd) + _24lsbOf(hiProd)) & ((1 << 24) - 1) - val newSeedLo = - _24lsbOf(loProd) - - seedHi = newSeedHi - seedLo = newSeedLo - - // (seed >>> (48 - bits)).toInt - // === ((seed >>> 16) >>> (32 - bits)).toInt because (bits <= 32) - - val result32 = (newSeedHi << 8) | (newSeedLo >> 16) - result32 >>> (32 - bits) + val oldSeed = (seedHi.toLong << 32) | Integer.toUnsignedLong(seedLo) // free + val newSeedShift16 = 0x5DEECE66D0000L * oldSeed + 0xB0000L + seedHi = (newSeedShift16 >>> (16 + 32)).toInt + seedLo = (newSeedShift16 >>> 16).toInt + + /* Spec: (newSeed >>> (48 - bits)).toInt + * with shift: (newSeedShift16 >>> (16 + 48 - bits)).toInt + * + * Since 1 <= bits <= 32 (by spec of next(bits)), the shift is + * 32 <= 64 - bits <= 63, which should result in a branchless shift inside + * RuntimeLong. The optimizer does not know that, though, so we help it by + * first shifting by 32 (which is free), extracting the `toInt` (also free), + * then shifting by `32 - bits`. + */ + (newSeedShift16 >>> 32).toInt >>> (32 - bits) } override def nextDouble(): Double = { @@ -215,6 +204,6 @@ object Random { (randomInt().toLong << 32) | (randomInt().toLong & 0xffffffffL) private def randomInt(): Int = - (Math.floor(js.Math.random() * 4294967296.0) - 2147483648.0).toInt + (Math.floor(Math.random() * 4294967296.0) - 2147483648.0).toInt } diff --git a/library/src/main/scala/scala/scalajs/LinkingInfo.scala b/library/src/main/scala/scala/scalajs/LinkingInfo.scala index ea9d6c1a2f..0a7218fb44 100644 --- a/library/src/main/scala/scala/scalajs/LinkingInfo.scala +++ b/library/src/main/scala/scala/scalajs/LinkingInfo.scala @@ -12,6 +12,8 @@ package scala.scalajs +import scala.scalajs.annotation.linkTimeProperty + object LinkingInfo { /** Returns true if we are linking for production, false otherwise. @@ -42,7 +44,7 @@ object LinkingInfo { * * @see [[developmentMode]] */ - @inline + @inline @linkTimeProperty("core/productionMode") def productionMode: Boolean = linkTimePropertyBoolean("core/productionMode") @@ -120,7 +122,7 @@ object LinkingInfo { * useES2018Feature() * }}} */ - @inline + @inline @linkTimeProperty("core/esVersion") def esVersion: Int = linkTimePropertyInt("core/esVersion") @@ -218,7 +220,7 @@ object LinkingInfo { * implementationWithoutES2015Semantics() * }}} */ - @inline + @inline @linkTimeProperty("core/useECMAScript2015Semantics") def useECMAScript2015Semantics: Boolean = linkTimePropertyBoolean("core/useECMAScript2015Semantics") @@ -252,15 +254,50 @@ object LinkingInfo { * implementationOptimizedForJavaScript() * }}} */ - @inline + @inline @linkTimeProperty("core/isWebAssembly") def isWebAssembly: Boolean = linkTimePropertyBoolean("core/isWebAssembly") /** Version of the linker. */ - @inline + @inline @linkTimeProperty("core/linkerVersion") def linkerVersion: String = linkTimePropertyString("core/linkerVersion") + /** Link-time conditional branching. + * + * A `linkTimeIf` expression behaves like an `if`, but it is guaranteed to + * be resolved at link-time. This prevents the unused branch to be linked at + * all. It can therefore reference APIs or language features that would + * otherwise fail to link. + * + * The condition `cond` can be constructed using: + * + * - Calls to methods annotated with `@linkTimeProperty` + * - Integer or boolean constants + * - Binary operators that return a boolean value + * + * A typical use case is to leverage the `**` operator on JavaScript + * `bigint`s if it is available, and otherwise fall back on using Scala + * `BigInt`s. Indeed, the `**` operator refuses to link when the target + * `esVersion` is too low. + * + * {{{ + * // Returns true iff 2^x < 10^y, for x and y positive integers + * def compareTwoPowTenPow(x: Int, y: Int): Boolean = { + * import scala.scalajs.LinkingInfo._ + * linkTimeIf(esVersion >= ESVersion.ES2020) { + * // JS bigints are available, and a fortiori their ** operator + * (js.BigInt(2) ** js.BigInt(x)) < (js.BigInt(10) ** js.BigInt(y)) + * } { + * // Fall back on Scala's BigInt's, which use a lot more code size + * BigInt(2).pow(x) < BigInt(10).pow(y) + * } + * } + * }}} + */ + def linkTimeIf[T](cond: Boolean)(thenp: T)(elsep: T): T = + throw new Error("stub") + /** Constants for the value of `esVersion`. */ object ESVersion { /** ECMAScrîpt 5.1. */ diff --git a/library/src/main/scala/scala/scalajs/annotation/linkTimeProperty.scala b/library/src/main/scala/scala/scalajs/annotation/linkTimeProperty.scala new file mode 100644 index 0000000000..6b93167c88 --- /dev/null +++ b/library/src/main/scala/scala/scalajs/annotation/linkTimeProperty.scala @@ -0,0 +1,33 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.scalajs.annotation + +/** Publicly marks the annotated method as being a link-time property. + * + * When an entity is annotated with `@linkTimeProperty`, its body must be a + * link-time property with the same `name`. The annotation makes that body + * "public", and it can therefore be inlined at call site at compile-time. + * + * From a user perspective, we can treat the presence of that annotation as if + * it were the `inline` keyword of Scala 3: it forces the inlining to happen + * at compile-time. + * + * This is necessary for the target method to be used in the condition of a + * `LinkingInfo.linkTimeIf`. + * + * @param name The name used to resolve the link-time value. + * + * @see [[LinkingInfo.linkTimeIf]] + */ +private[scalajs] final class linkTimeProperty(name: String) + extends scala.annotation.StaticAnnotation diff --git a/library/src/main/scala/scala/scalajs/js/JSNumberOps.scala b/library/src/main/scala/scala/scalajs/js/JSNumberOps.scala index 6f234fb201..4adef7f9a3 100644 --- a/library/src/main/scala/scala/scalajs/js/JSNumberOps.scala +++ b/library/src/main/scala/scala/scalajs/js/JSNumberOps.scala @@ -79,15 +79,19 @@ object JSNumberOps { implicit def enableJSNumberOps(x: Double): js.JSNumberOps = x.asInstanceOf[js.JSNumberOps] + @deprecated("Use Integer.toUnsignedLong(x).toDouble instead of toUint.", since = "1.20.0") implicit def enableJSNumberExtOps(x: Int): ExtOps = new ExtOps(x.asInstanceOf[js.Dynamic]) + @deprecated("Use Integer.toUnsignedLong(x).toDouble instead of toUint.", since = "1.20.0") implicit def enableJSNumberExtOps(x: Double): ExtOps = new ExtOps(x.asInstanceOf[js.Dynamic]) + @deprecated("Use Integer.toUnsignedLong(x).toDouble instead of toUint.", since = "1.20.0") final class ExtOps private[JSNumberOps] (private val self: js.Dynamic) extends AnyVal { + @deprecated("Use Integer.toUnsignedLong(x).toDouble instead.", since = "1.20.0") @inline def toUint: Double = (self >>> 0.asInstanceOf[js.Dynamic]).asInstanceOf[Double] } diff --git a/library/src/main/scala/scala/scalajs/runtime/package.scala b/library/src/main/scala/scala/scalajs/runtime/package.scala index d3ba4f766f..342081817d 100644 --- a/library/src/main/scala/scala/scalajs/runtime/package.scala +++ b/library/src/main/scala/scala/scalajs/runtime/package.scala @@ -111,7 +111,9 @@ package object runtime { } /** Identity hash code of an object. */ - def identityHashCode(x: Object): Int = throw new Error("stub") + @deprecated("Unused; use System.identityHashCode(x) instead.", since = "1.20.0") + def identityHashCode(x: Object): Int = + System.identityHashCode(x) def dynamicImport[A](thunk: DynamicImportThunk): js.Promise[A] = throw new Error("stub") diff --git a/linker-private-library/src/main/scala/org/scalajs/linker/runtime/FloatingPointBitsPolyfills.scala b/linker-private-library/src/main/scala/org/scalajs/linker/runtime/FloatingPointBitsPolyfills.scala new file mode 100644 index 0000000000..43a27ee085 --- /dev/null +++ b/linker-private-library/src/main/scala/org/scalajs/linker/runtime/FloatingPointBitsPolyfills.scala @@ -0,0 +1,187 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.linker.runtime + +import scala.scalajs.js + +/** Polyfills for manipulating the bits of floating point numbers without DataView. + * + * These polyfills are only used when targeting ECMAScript 5.1. + * + * Originally inspired by + * https://github.com/inexorabletash/polyfill/blob/3447582628b6e3ea81959c4d5987aa332c22d1ca/typedarray.js#L150-L264 + * + * Note that if typed arrays are not supported, it is almost certain that + * fround is not supported natively, so Float operations are extremely slow. + * + * We therefore do all computations in Doubles here. + */ +object FloatingPointBitsPolyfills { + private val floatPowsOf2: js.Array[Double] = + makePowsOf2(len = 1 << 8, java.lang.Float.MIN_NORMAL.toDouble) + + private val doublePowsOf2: js.Array[Double] = + makePowsOf2(len = 1 << 11, java.lang.Double.MIN_NORMAL) + + private def makePowsOf2(len: Int, minNormal: Double): js.Array[Double] = { + val r = new js.Array[Double](len) + r(0) = 0.0 + var i = 1 + var next = minNormal + while (i != len - 1) { + r(i) = next + i += 1 + next *= 2 + } + r(len - 1) = Double.PositiveInfinity + r + } + + @inline // inline into the static forwarder, which will be the entry point + def floatFromBits(bits: Int): Double = { + val ebits = 8 + val fbits = 23 + val sign = (bits >> 31) | 1 // -1 or 1 + val e = (bits >> fbits) & ((1 << ebits) - 1) + val f = bits & ((1 << fbits) - 1) + decodeIEEE754(ebits, fbits, floatPowsOf2, Float.MinPositiveValue, sign, e, f) + } + + @inline // inline into the static forwarder, which will be the entry point + def floatToBits(floatValue: Float): Int = { + // Some constants + val ebits = 8 + val fbits = 23 + + // Force computations to be on Doubles + val value = floatValue.toDouble + + // Determine sign bit and compute the absolute value av + val sign = if (value < 0.0 || (value == 0.0 && 1.0 / value < 0.0)) -1 else 1 + val s = sign & Int.MinValue + val av = sign * value + + // Compute e and f + val powsOf2 = this.floatPowsOf2 // local cache + val e = encodeIEEE754Exponent(ebits, powsOf2, av) + val f = encodeIEEE754MantissaBits(ebits, fbits, powsOf2, Float.MinPositiveValue.toDouble, av, e) + + // Encode + s | (e << fbits) | rawToInt(f) + } + + @inline // inline into the static forwarder, which will be the entry point + def doubleFromBits(bits: Long): Double = { + val ebits = 11 + val fbits = 52 + val hifbits = fbits - 32 + val hi = (bits >>> 32).toInt + val lo = (bits & 0xffffffffL).toDouble + val sign = (hi >> 31) | 1 // -1 or 1 + val e = (hi >> hifbits) & ((1 << ebits) - 1) + val f = (hi & ((1 << hifbits) - 1)).toDouble * 0x100000000L.toDouble + lo + decodeIEEE754(ebits, fbits, doublePowsOf2, Double.MinPositiveValue, sign, e, f) + } + + @inline // inline into the static forwarder, which will be the entry point + def doubleToBits(value: Double): Long = { + // Some constants + val ebits = 11 + val fbits = 52 + val hifbits = fbits - 32 + + // Determine sign bit and compute the absolute value av + val sign = if (value < 0.0 || (value == 0.0 && 1.0 / value < 0.0)) -1 else 1 + val s = sign & Int.MinValue + val av = sign * value + + // Compute e and f + val powsOf2 = this.doublePowsOf2 // local cache + val e = encodeIEEE754Exponent(ebits, powsOf2, av) + val f = encodeIEEE754MantissaBits(ebits, fbits, powsOf2, Double.MinPositiveValue, av, e) + + // Encode + val hi = s | (e << hifbits) | rawToInt(f / 0x100000000L.toDouble) + val lo = rawToInt(f) + (hi.toLong << 32) | (lo.toLong & 0xffffffffL) + } + + @inline + private def decodeIEEE754(ebits: Int, fbits: Int, + powsOf2: js.Array[Double], minPositiveValue: Double, + sign: Int, e: Int, f: Double): Double = { + + // Some constants + val specialExponent = (1 << ebits) - 1 + val twoPowFbits = (1L << fbits).toDouble + + if (e == specialExponent) { + // Special + if (f == 0.0) + sign * Double.PositiveInfinity + else + Double.NaN + } else if (e > 0) { + // Normalized + sign * powsOf2(e) * (1 + f / twoPowFbits) + } else { + // Subnormal + sign * f * minPositiveValue + } + } + + @inline + private def encodeIEEE754Exponent(ebits: Int, + powsOf2: js.Array[Double], av: Double): Int = { + + /* Binary search of `av` inside `powsOf2`. + * There are exactly `ebits` iterations of this loop (11 for Double, 8 for Float). + */ + var eMin = 0 + var eMax = 1 << ebits + while (eMin + 1 < eMax) { + val e = (eMin + eMax) >> 1 + if (av < powsOf2(e)) // false when av is NaN + eMax = e + else + eMin = e + } + eMin + } + + @inline + private def encodeIEEE754MantissaBits(ebits: Int, fbits: Int, + powsOf2: js.Array[Double], minPositiveValue: Double, + av: Double, e: Int): Double = { + + // Some constants + val specialExponent = (1 << ebits) - 1 + val twoPowFbits = (1L << fbits).toDouble + + if (e == specialExponent) { + if (av != av) + (1L << (fbits - 1)).toDouble // NaN + else + 0.0 // Infinity + } else { + if (e == 0) + av / minPositiveValue // Subnormal + else + ((av / powsOf2(e)) - 1.0) * twoPowFbits // Normal + } + } + + @inline private def rawToInt(x: Double): Int = + (x.asInstanceOf[js.Dynamic] | 0.asInstanceOf[js.Dynamic]).asInstanceOf[Int] + +} diff --git a/linker-private-library/src/main/scala/org/scalajs/linker/runtime/RuntimeLong.scala b/linker-private-library/src/main/scala/org/scalajs/linker/runtime/RuntimeLong.scala index 7a90e2a9e1..5c7b774354 100644 --- a/linker-private-library/src/main/scala/org/scalajs/linker/runtime/RuntimeLong.scala +++ b/linker-private-library/src/main/scala/org/scalajs/linker/runtime/RuntimeLong.scala @@ -40,74 +40,77 @@ import scala.annotation.tailrec /** Emulates a Long on the JavaScript platform. */ @inline final class RuntimeLong(val lo: Int, val hi: Int) { - a => - import RuntimeLong._ - // Universal equality + // java.lang.Object @inline override def equals(that: Any): Boolean = that match { - case b: RuntimeLong => inline_equals(b) - case _ => false + case that: RuntimeLong => RuntimeLong.equals(this, that) + case _ => false } @inline override def hashCode(): Int = lo ^ hi - // String operations - @inline override def toString(): String = - RuntimeLong.toString(lo, hi) - - // Conversions - - @inline def toByte: Byte = lo.toByte - @inline def toShort: Short = lo.toShort - @inline def toChar: Char = lo.toChar - @inline def toInt: Int = lo - @inline def toLong: Long = this.asInstanceOf[Long] - @inline def toFloat: Float = RuntimeLong.toFloat(lo, hi) - @inline def toDouble: Double = RuntimeLong.toDouble(lo, hi) + RuntimeLong.toString(this) // java.lang.Number - @inline def byteValue(): Byte = toByte - @inline def shortValue(): Short = toShort - @inline def intValue(): Int = toInt - @inline def longValue(): Long = toLong - @inline def floatValue(): Float = toFloat - @inline def doubleValue(): Double = toDouble + @inline def byteValue(): Byte = lo.toByte + @inline def shortValue(): Short = lo.toShort + @inline def intValue(): Int = lo + @inline def longValue(): Long = this.asInstanceOf[Long] + @inline def floatValue(): Float = RuntimeLong.toFloat(this) + @inline def doubleValue(): Double = RuntimeLong.toDouble(this) // java.lang.Comparable, including bridges @inline def compareTo(that: Object): Int = - compareTo(that.asInstanceOf[RuntimeLong]) + RuntimeLong.compare(this, that.asInstanceOf[RuntimeLong]) @inline def compareTo(that: java.lang.Long): Int = - compareTo(that.asInstanceOf[RuntimeLong]) + RuntimeLong.compare(this, that.asInstanceOf[RuntimeLong]) + + // A few operator-friendly methods used by the division algorithms + + @inline private def <<(b: Int): RuntimeLong = RuntimeLong.shl(this, b) + @inline private def >>>(b: Int): RuntimeLong = RuntimeLong.shr(this, b) + @inline private def +(b: RuntimeLong): RuntimeLong = RuntimeLong.add(this, b) + @inline private def -(b: RuntimeLong): RuntimeLong = RuntimeLong.sub(this, b) +} + +object RuntimeLong { + private final val TwoPow32 = 4294967296.0 + private final val TwoPow63 = 9223372036854775808.0 + + /** The magical mask that allows to test whether an unsigned long is a safe + * double. + * @see isUnsignedSafeDouble + */ + private final val SafeDoubleHiMask = 0xffe00000 + + /** The hi part of a (lo, hi) return value. */ + private[this] var hiReturn: Int = _ // Comparisons @inline - def compareTo(b: RuntimeLong): Int = + def compare(a: RuntimeLong, b: RuntimeLong): Int = RuntimeLong.compare(a.lo, a.hi, b.lo, b.hi) @inline - private def inline_equals(b: RuntimeLong): Boolean = + def equals(a: RuntimeLong, b: RuntimeLong): Boolean = a.lo == b.lo && a.hi == b.hi @inline - def equals(b: RuntimeLong): Boolean = - inline_equals(b) - - @inline - def notEquals(b: RuntimeLong): Boolean = - !inline_equals(b) + def notEquals(a: RuntimeLong, b: RuntimeLong): Boolean = + !equals(a, b) @inline - def <(b: RuntimeLong): Boolean = { + def lt(a: RuntimeLong, b: RuntimeLong): Boolean = { /* We should use `inlineUnsignedInt_<(a.lo, b.lo)`, but that first extracts * a.lo and b.lo into local variables, which cause the if/else not to be * a valid JavaScript expression anymore. This causes useless explosion of @@ -121,7 +124,7 @@ final class RuntimeLong(val lo: Int, val hi: Int) { } @inline - def <=(b: RuntimeLong): Boolean = { + def le(a: RuntimeLong, b: RuntimeLong): Boolean = { /* Manually inline `inlineUnsignedInt_<=(a.lo, b.lo)`. * See the comment in `<` for the rationale. */ @@ -132,7 +135,7 @@ final class RuntimeLong(val lo: Int, val hi: Int) { } @inline - def >(b: RuntimeLong): Boolean = { + def gt(a: RuntimeLong, b: RuntimeLong): Boolean = { /* Manually inline `inlineUnsignedInt_>a.lo, b.lo)`. * See the comment in `<` for the rationale. */ @@ -143,7 +146,7 @@ final class RuntimeLong(val lo: Int, val hi: Int) { } @inline - def >=(b: RuntimeLong): Boolean = { + def ge(a: RuntimeLong, b: RuntimeLong): Boolean = { /* Manually inline `inlineUnsignedInt_>=(a.lo, b.lo)`. * See the comment in `<` for the rationale. */ @@ -153,29 +156,69 @@ final class RuntimeLong(val lo: Int, val hi: Int) { else ahi > bhi } - // Bitwise operations + @inline + def ltu(a: RuntimeLong, b: RuntimeLong): Boolean = { + /* Manually inline `inlineUnsignedInt_<(a.lo, b.lo)`. + * See the comment in `<` for the rationale. + */ + val ahi = a.hi + val bhi = b.hi + if (ahi == bhi) (a.lo ^ 0x80000000) < (b.lo ^ 0x80000000) + else inlineUnsignedInt_<(ahi, bhi) + } @inline - def unary_~ : RuntimeLong = // scalastyle:ignore - new RuntimeLong(~lo, ~hi) + def leu(a: RuntimeLong, b: RuntimeLong): Boolean = { + /* Manually inline `inlineUnsignedInt_<=(a.lo, b.lo)`. + * See the comment in `<` for the rationale. + */ + val ahi = a.hi + val bhi = b.hi + if (ahi == bhi) (a.lo ^ 0x80000000) <= (b.lo ^ 0x80000000) + else inlineUnsignedInt_<=(ahi, bhi) + } @inline - def |(b: RuntimeLong): RuntimeLong = + def gtu(a: RuntimeLong, b: RuntimeLong): Boolean = { + /* Manually inline `inlineUnsignedInt_>(a.lo, b.lo)`. + * See the comment in `<` for the rationale. + */ + val ahi = a.hi + val bhi = b.hi + if (ahi == bhi) (a.lo ^ 0x80000000) > (b.lo ^ 0x80000000) + else inlineUnsignedInt_>(ahi, bhi) + } + + @inline + def geu(a: RuntimeLong, b: RuntimeLong): Boolean = { + /* Manually inline `inlineUnsignedInt_>=(a.lo, b.lo)`. + * See the comment in `<` for the rationale. + */ + val ahi = a.hi + val bhi = b.hi + if (ahi == bhi) (a.lo ^ 0x80000000) >= (b.lo ^ 0x80000000) + else inlineUnsignedInt_>=(ahi, bhi) + } + + // Bitwise operations + + @inline + def or(a: RuntimeLong, b: RuntimeLong): RuntimeLong = new RuntimeLong(a.lo | b.lo, a.hi | b.hi) @inline - def &(b: RuntimeLong): RuntimeLong = + def and(a: RuntimeLong, b: RuntimeLong): RuntimeLong = new RuntimeLong(a.lo & b.lo, a.hi & b.hi) @inline - def ^(b: RuntimeLong): RuntimeLong = + def xor(a: RuntimeLong, b: RuntimeLong): RuntimeLong = new RuntimeLong(a.lo ^ b.lo, a.hi ^ b.hi) // Shifts /** Shift left */ @inline - def <<(n: Int): RuntimeLong = { + def shl(a: RuntimeLong, n: Int): RuntimeLong = { /* This should *reasonably* be: * val n1 = n & 63 * if (n1 < 32) @@ -241,63 +284,67 @@ final class RuntimeLong(val lo: Int, val hi: Int) { * * Finally we have: */ - val lo = this.lo + val lo = a.lo new RuntimeLong( if ((n & 32) == 0) lo << n else 0, - if ((n & 32) == 0) (lo >>> 1 >>> (31-n)) | (hi << n) else lo << n) + if ((n & 32) == 0) (lo >>> 1 >>> (31-n)) | (a.hi << n) else lo << n) } /** Logical shift right */ @inline - def >>>(n: Int): RuntimeLong = { + def shr(a: RuntimeLong, n: Int): RuntimeLong = { // This derives in a similar way as in << - val hi = this.hi + val hi = a.hi new RuntimeLong( - if ((n & 32) == 0) (lo >>> n) | (hi << 1 << (31-n)) else hi >>> n, + if ((n & 32) == 0) (a.lo >>> n) | (hi << 1 << (31-n)) else hi >>> n, if ((n & 32) == 0) hi >>> n else 0) } /** Arithmetic shift right */ @inline - def >>(n: Int): RuntimeLong = { + def sar(a: RuntimeLong, n: Int): RuntimeLong = { // This derives in a similar way as in << - val hi = this.hi + val hi = a.hi new RuntimeLong( - if ((n & 32) == 0) (lo >>> n) | (hi << 1 << (31-n)) else hi >> n, + if ((n & 32) == 0) (a.lo >>> n) | (hi << 1 << (31-n)) else hi >> n, if ((n & 32) == 0) hi >> n else hi >> 31) } // Arithmetic operations @inline - def unary_- : RuntimeLong = { // scalastyle:ignore - val lo = this.lo - val hi = this.hi - new RuntimeLong(inline_lo_unary_-(lo), inline_hi_unary_-(lo, hi)) - } - - @inline - def +(b: RuntimeLong): RuntimeLong = { + def add(a: RuntimeLong, b: RuntimeLong): RuntimeLong = { + // Hacker's Delight, Section 2-16 val alo = a.lo - val ahi = a.hi - val bhi = b.hi - val lo = alo + b.lo + val blo = b.lo + val lo = alo + blo new RuntimeLong(lo, - if (inlineUnsignedInt_<(lo, alo)) ahi + bhi + 1 else ahi + bhi) + a.hi + b.hi + (((alo & blo) | ((alo | blo) & ~lo)) >>> 31)) } @inline - def -(b: RuntimeLong): RuntimeLong = { + def sub(a: RuntimeLong, b: RuntimeLong): RuntimeLong = { + /* Hacker's Delight, Section 2-16 + * + * We deviate a bit from the original algorithm. Hacker's Delight uses + * `- (... >>> 31)`. Instead, we use `+ (... >> 31)`. These are equivalent, + * since `(x >> 31) == -(x >>> 31)` for all x. The variant with `+` folds + * better when `a.hi` and `b.hi` are both known to be 0. This happens in + * practice when `a` and `b` are 0-extended from `Int` values. + */ val alo = a.lo - val ahi = a.hi - val bhi = b.hi - val lo = alo - b.lo + val blo = b.lo + val lo = alo - blo new RuntimeLong(lo, - if (inlineUnsignedInt_>(lo, alo)) ahi - bhi - 1 else ahi - bhi) + a.hi - b.hi + (((~alo & blo) | (~(alo ^ blo) & lo)) >> 31)) } @inline - def *(b: RuntimeLong): RuntimeLong = { + def abs(a: RuntimeLong): RuntimeLong = + inline_abs(a.lo, a.hi) + + @inline + def mul(a: RuntimeLong, b: RuntimeLong): RuntimeLong = { /* The following algorithm is based on the decomposition in 32-bit and then * 16-bit subproducts of the unsigned interpretation of operands. * @@ -523,90 +570,131 @@ final class RuntimeLong(val lo: Int, val hi: Int) { new RuntimeLong(lo, hi) } + /** Computes `longBitsToDouble(a)`. + * + * `fpBitsDataView` must be a scratch `js.typedarray.DataView` whose + * underlying buffer is at least 8 bytes long. + */ @inline - def /(b: RuntimeLong): RuntimeLong = - RuntimeLong.divide(a, b) - - /** `java.lang.Long.divideUnsigned(a, b)` */ - @inline - def divideUnsigned(b: RuntimeLong): RuntimeLong = - RuntimeLong.divideUnsigned(a, b) + def bitsToDouble(a: RuntimeLong, + fpBitsDataView: scala.scalajs.js.typedarray.DataView): Double = { - @inline - def %(b: RuntimeLong): RuntimeLong = - RuntimeLong.remainder(a, b) + fpBitsDataView.setInt32(0, a.lo, littleEndian = true) + fpBitsDataView.setInt32(4, a.hi, littleEndian = true) + fpBitsDataView.getFloat64(0, littleEndian = true) + } - /** `java.lang.Long.remainderUnsigned(a, b)` */ @inline - def remainderUnsigned(b: RuntimeLong): RuntimeLong = - RuntimeLong.remainderUnsigned(a, b) - -} - -object RuntimeLong { - private final val TwoPow32 = 4294967296.0 - private final val TwoPow63 = 9223372036854775808.0 - - /** The magical mask that allows to test whether an unsigned long is a safe - * double. - * @see isUnsignedSafeDouble - */ - private final val UnsignedSafeDoubleHiMask = 0xffe00000 - - private final val AskQuotient = 0 - private final val AskRemainder = 1 - private final val AskToString = 2 - - /** The hi part of a (lo, hi) return value. */ - private[this] var hiReturn: Int = _ + def toString(a: RuntimeLong): String = + toString(a.lo, a.hi) private def toString(lo: Int, hi: Int): String = { if (isInt32(lo, hi)) { lo.toString() - } else if (hi < 0) { - "-" + toUnsignedString(inline_lo_unary_-(lo), inline_hi_unary_-(lo, hi)) + } else if (isSignedSafeDouble(hi)) { + asSafeDouble(lo, hi).toString() } else { - toUnsignedString(lo, hi) + val abs = inline_abs(lo, hi) + val s = toUnsignedStringLarge(abs.lo, abs.hi) + if (hi < 0) "-" + s else s } } - private def toUnsignedString(lo: Int, hi: Int): String = { - // This is called only if (lo, hi) is not an Int32 + @inline + private def toUnsignedStringLarge(lo: Int, hi: Int): String = { + /* This is called only if (lo, hi) is >= 2^53. + * + * The idea is to divide (lo, hi) once by 10^9 and keep the remainder. + * + * The remainder must then be < 10^9, and is therefore an int32. + * + * The quotient must be <= ULong.MaxValue / 10^9, which is < 2^53, and + * is therefore a valid double. It must also be non-zero, since + * (lo, hi) >= 2^53 > 10^9. + * + * We should do that single division as a Long division. However, that is + * slow. We can cheat with a Double division instead. + * + * We convert the unsigned value num = (lo, hi) to a Double value + * approxNum. This is an approximation. It can lose as many as + * 64 - 53 = 11 low-order bits. Hence |approxNum - num| <= 2^12. + * + * We then compute an approximated quotient + * approxQuot = floor(approxNum / 10^9) + * instead of the theoretical value + * quot = floor(num / 10^9) + * + * Since 10^9 > 2^29 > 2^12, we have |approxNum - num| < 10^9. + * Therefore, |approxQuot - quot| <= 1. + * + * We also have 0 <= approxQuot < 2^53, which means that approxQuot is an + * "unsigned safe double" and that `approxQuot.toLong` is lossless. + * + * At this point, we compute the approximated remainder + * approxRem = num - 10^9 * approxQuot.toLong + * as if with Long arithmetics. + * + * Since the theoretical remainder rem = num - 10^9 * quot is such that + * 0 <= rem < 10^9, and since |approxQuot - quot| <= 1, we have that + * -10^9 <= approxRem < 2 * 10^9 + * + * Interestingly, that range entirely fits within a signed int32. + * That means approxRem = approxRem.toInt, and therefore + * + * approxRem + * = (num - 10^9 * approxQuot.toLong).toInt + * = num.toInt - 10^9 * approxQuot.toLong.toInt (thanks to modular arithmetics) + * = lo - 10^9 * unsignedSafeDoubleLo(approxQuot) + * + * That allows to compute approxRem with Int arithmetics without loss of + * precision. + * + * We can use approxRem to detect and correct the error on approxQuot. + * If approxRem < 0, correct approxQuot by -1 and approxRem by +10^9. + * If approxRem >= 10^9, correct them by +1 and -10^9, respectively. + * + * After the correction, we know that approxQuot and approxRem are equal + * to their theoretical counterparts quot and rem. We have successfully + * computed the correct quotient and remainder without using any Long + * division. + * + * We can finally convert both to strings using the native string + * conversions, and concatenate the results to produce our final result. + */ - if (isUnsignedSafeDouble(hi)) { - // (lo, hi) is small enough to be a Double, use that directly - asUnsignedSafeDouble(lo, hi).toString - } else { - /* At this point, (lo, hi) >= 2^53. - * We divide (lo, hi) once by 10^9 and keep the remainder. - * - * The remainder must then be < 10^9, and is therefore an int32. - * - * The quotient must be <= ULong.MaxValue / 10^9, which is < 2^53, and - * is therefore a valid double. It must also be non-zero, since - * (lo, hi) >= 2^53 > 10^9. - * - * To avoid allocating a tuple with the quotient and remainder, we push - * the final conversion to string inside unsignedDivModHelper. According - * to micro-benchmarks, this optimization makes toString 25% faster in - * this branch. - */ - unsignedDivModHelper(lo, hi, 1000000000, 0, - AskToString).asInstanceOf[String] + // constants + val divisor = 1000000000 // 10^9 + val divisorInv = 1.0 / divisor.toDouble + + // initial approximation of the quotient and remainder + val approxNum = unsignedToDoubleApprox(lo, hi) + var approxQuot = scala.scalajs.js.Math.floor(approxNum * divisorInv) + var approxRem = lo - divisor * unsignedSafeDoubleLo(approxQuot) + + // correct the approximations + if (approxRem < 0) { + approxQuot -= 1.0 + approxRem += divisor + } else if (approxRem >= divisor) { + approxQuot += 1.0 + approxRem -= divisor } - } - private def toDouble(lo: Int, hi: Int): Double = { - if (hi < 0) { - // We do asUint() on the hi part specifically for MinValue - -(asUint(inline_hi_unary_-(lo, hi)) * TwoPow32 + - asUint(inline_lo_unary_-(lo))) - } else { - hi * TwoPow32 + asUint(lo) - } + // build the result string + val remStr = approxRem.toString() + approxQuot.toString() + substring("000000000", remStr.length()) + remStr } - private def toFloat(lo: Int, hi: Int): Float = { + @inline + def toInt(a: RuntimeLong): Int = + a.lo + + @inline + def toDouble(a: RuntimeLong): Double = + signedToDoubleApprox(a.lo, a.hi) + + @inline + def toFloat(a: RuntimeLong): Float = { /* This implementation is based on the property that, *if* the conversion * `x.toDouble` is lossless, then the result of `x.toFloat` is equivalent * to `x.toDouble.toFloat`. @@ -625,46 +713,65 @@ object RuntimeLong { * * The algorithm works as follows: * - * First, we take the absolute value of the input. We will negate the - * result at the end if the input was negative. - * - * Second, if the abs input is an unsigned safe Double, then the conversion - * to double is lossless, so we don't have to do anything special - * (`y == x` in terms of the above explanation). - * - * Otherwise, we know that the input's highest 1 bit is in the 11 - * highest-order bits. That means that rounding to float, which only has 24 - * bits in the significand, can only take into account the - * `11 + 23 + 1 = 35` highest-order bits (the `+ 1` is for the rounding - * bit). The remaining bits can only affect the result by two states: - * either they are all 0's, or there is at least one 1. We use that - * property to "compress" the 16 low-order bits into a single 0 or 1 bit - * representing those two states. The compressed Long value - * `y = (compressedAbsLo, abs.hi)` has at most `32 + 17 = 49` significant + * If the input is a signed safe Double, then the conversion to double is + * lossless, so we don't have to do anything special (`y == x` in terms of + * the above explanation). + * + * Otherwise, let us first assume that `x >= 0`. In that case, we know that + * the input's highest 1 bit is in the 11 highest-order bits. That means + * that rounding to float, which only has 24 bits in the significand, can + * only take into account the `11 + 23 + 1 = 35` highest-order bits (the + * `+ 1` is for the rounding bit). The remaining bits can only affect the + * result by two states: either they are all 0's, or there is at least one + * 1. We use that property to "compress" the 16 low-order bits into a + * single 0 or 1 bit representing those two states. The compressed Long + * value `y = (compressedLo, hi)` has at most `32 + 17 = 49` significant * bits. Therefore its conversion to Double is lossless. * * Now that we always have a lossless compression to Double, we can perform * it, followed by a conversion from Double to Float, which will apply the * appropriate rounding. * - * (A similar strategy is used in `parseFloat` for the hexadecimal format.) + * (A similar strategy is used in `parseFloat` for the hexadecimal format, + * where we only have the non-negative case.) + * + * For the case `x < 0`, logically we should negate it, perform the above + * transformation and convert to Double, then negate the result. It turns + * out we do not need a separate code path. Indeed, if x is a safe double, + * then -x also converts losslessly (-x may not be safe double by our + * definition, because it could be exactly 2^53, but the conversion is + * still exact). Otherwise, we should apply a compression if + * `(-x & 0xffffL) != 0L`. Because of how two's complement negation work, + * that is equivalent to `(x & 0xffffL) != 0L`, and therefore also + * equivalent to `(lo & 0xffff) != 0`. When we do need a compression, we + * can do it on the signed representation just as well as the unsigned + * representation, because it only affects `lo`, and `lo` is interpreted as + * unsigned regardless, when converting to a double. */ - val abs = inline_abs(lo, hi) - val compressedAbsLo = - if (isUnsignedSafeDouble(abs.hi) || (abs.lo & 0xffff) == 0) abs.lo - else (abs.lo & ~0xffff) | 0x8000 - - // We do asUint() on the hi part specifically for MinValue - val absRes = (asUint(abs.hi) * TwoPow32 + asUint(compressedAbsLo)) + val lo = a.lo + val hi = a.hi + val compressedLo = + if (isSignedSafeDouble(hi) || (lo & 0xffff) == 0) lo + else (lo & ~0xffff) | 0x8000 + signedToDoubleApprox(compressedLo, hi).toFloat + } - (if (hi < 0) -absRes else absRes).toFloat + @inline + def clz(a: RuntimeLong): Int = { + val hi = a.hi + if (hi != 0) Integer.numberOfLeadingZeros(hi) + else 32 + Integer.numberOfLeadingZeros(a.lo) } @inline def fromInt(value: Int): RuntimeLong = new RuntimeLong(value, value >> 31) + @inline + def fromUnsignedInt(value: Int): RuntimeLong = + new RuntimeLong(value, 0) + @inline def fromDouble(value: Double): RuntimeLong = { val lo = fromDoubleImpl(value) @@ -728,6 +835,22 @@ object RuntimeLong { } } + /** Computes `doubleToRawLongBits(value)`. + * + * `fpBitsDataView` must be a scratch `js.typedarray.DataView` whose + * underlying buffer is at least 8 bytes long. + */ + @inline + def fromDoubleBits(value: Double, + fpBitsDataView: scala.scalajs.js.typedarray.DataView): RuntimeLong = { + + fpBitsDataView.setFloat64(0, value, littleEndian = true) + new RuntimeLong( + fpBitsDataView.getInt32(0, littleEndian = true), + fpBitsDataView.getInt32(4, littleEndian = true) + ) + } + private def compare(alo: Int, ahi: Int, blo: Int, bhi: Int): Int = { if (ahi == bhi) { if (alo == blo) 0 @@ -739,6 +862,47 @@ object RuntimeLong { } } + /** Intrinsic for Math.multiplyFull. + * + * Compared to the regular expansion of `x.toLong * y.toLong`, this + * intrinsic avoids 2 int multiplications. + */ + @inline + def multiplyFull(a: Int, b: Int): RuntimeLong = { + /* We use Hacker's Delight, Section 8-2, Figure 8-2, to compute the hi + * word of the result. We reuse intermediate products to compute the lo + * word, like we do in `RuntimeLong.*`. + * + * We swap the role of a1b0 and a0b1 compared to Hacker's Delight, to + * optimize for the case where a1b0 collapses to 0, like we do in + * `RuntimeLong.*`. The optimizer normalizes constants in multiplyFull to + * be on the left-hand-side (when it cannot do constant-folding to begin + * with). Therefore, `b` is never constant in practice. + */ + + val a0 = a & 0xffff + val a1 = a >> 16 + val b0 = b & 0xffff + val b1 = b >> 16 + + val a0b0 = a0 * b0 + val a1b0 = a1 * b0 // collapses to 0 when a is constant and 0 <= a <= 0xffff + val a0b1 = a0 * b1 + + /* lo = a * b, but we compute the above 3 subproducts for hi anyway, + * so we reuse them to compute lo too, trading a * for 2 +'s and 1 <<. + */ + val lo = a0b0 + ((a1b0 + a0b1) << 16) + + val t = a0b1 + (a0b0 >>> 16) + val hi = { + a1 * b1 + (t >> 16) + + (((t & 0xffff) + a1b0) >> 16) // collapses to 0 when a1b0 = 0 + } + + new RuntimeLong(lo, hi) + } + @inline def divide(a: RuntimeLong, b: RuntimeLong): RuntimeLong = { val lo = divideImpl(a.lo, a.hi, b.lo, b.hi) @@ -775,7 +939,7 @@ object RuntimeLong { val bAbs = inline_abs(blo, bhi) val absRLo = unsigned_/(aAbs.lo, aAbs.hi, bAbs.lo, bAbs.hi) if ((ahi ^ bhi) >= 0) absRLo // a and b have the same sign bit - else inline_hiReturn_unary_-(absRLo, hiReturn) + else inline_negate_hiReturn(absRLo, hiReturn) } } @@ -807,8 +971,8 @@ object RuntimeLong { // This method is not called if isInt32(alo, ahi) nor if isZero(blo, bhi) if (isUnsignedSafeDouble(ahi)) { if (isUnsignedSafeDouble(bhi)) { - val aDouble = asUnsignedSafeDouble(alo, ahi) - val bDouble = asUnsignedSafeDouble(blo, bhi) + val aDouble = asSafeDouble(alo, ahi) + val bDouble = asSafeDouble(blo, bhi) val rDouble = aDouble / bDouble hiReturn = unsignedSafeDoubleHi(rDouble) unsignedSafeDoubleLo(rDouble) @@ -827,7 +991,7 @@ object RuntimeLong { hiReturn = 0 ahi >>> pow } else { - unsignedDivModHelper(alo, ahi, blo, bhi, AskQuotient).asInstanceOf[Int] + unsignedDivModHelper(alo, ahi, blo, bhi, askQuotient = true) } } } @@ -844,15 +1008,9 @@ object RuntimeLong { if (isInt32(alo, ahi)) { if (isInt32(blo, bhi)) { - if (blo != -1) { - val lo = alo % blo - hiReturn = lo >> 31 - lo - } else { - // Work around https://github.com/ariya/phantomjs/issues/12198 - hiReturn = 0 - 0 - } + val lo = alo % blo + hiReturn = lo >> 31 + lo } else { // Either a == Int.MinValue && b == (Int.MaxValue + 1), or (abs(b) > abs(a)) if (alo == Int.MinValue && (blo == 0x80000000 && bhi == 0)) { @@ -868,7 +1026,7 @@ object RuntimeLong { val aAbs = inline_abs(alo, ahi) val bAbs = inline_abs(blo, bhi) val absRLo = unsigned_%(aAbs.lo, aAbs.hi, bAbs.lo, bAbs.hi) - if (ahi < 0) inline_hiReturn_unary_-(absRLo, hiReturn) + if (ahi < 0) inline_negate_hiReturn(absRLo, hiReturn) else absRLo } } @@ -901,8 +1059,8 @@ object RuntimeLong { // This method is not called if isInt32(alo, ahi) nor if isZero(blo, bhi) if (isUnsignedSafeDouble(ahi)) { if (isUnsignedSafeDouble(bhi)) { - val aDouble = asUnsignedSafeDouble(alo, ahi) - val bDouble = asUnsignedSafeDouble(blo, bhi) + val aDouble = asSafeDouble(alo, ahi) + val bDouble = asSafeDouble(blo, bhi) val rDouble = aDouble % bDouble hiReturn = unsignedSafeDoubleHi(rDouble) unsignedSafeDoubleLo(rDouble) @@ -919,22 +1077,19 @@ object RuntimeLong { hiReturn = ahi & (bhi - 1) alo } else { - unsignedDivModHelper(alo, ahi, blo, bhi, AskRemainder).asInstanceOf[Int] + unsignedDivModHelper(alo, ahi, blo, bhi, askQuotient = false) } } } - /** Helper for `unsigned_/`, `unsigned_%` and `toUnsignedString()`. - * - * The value of `ask` may be one of: + /** Helper for `unsigned_/` and `unsigned_%`. * - * - `AskQuotient`: returns the quotient (with the hi part in `hiReturn`) - * - `AskRemainder`: returns the remainder (with the hi part in `hiReturn`) - * - `AskToString`: returns the conversion of `(alo, ahi)` to string. - * In this case, `blo` must be 10^9 and `bhi` must be 0. + * If `askQuotient` is true, computes the quotient, otherwise computes the + * remainder. Stores the hi word of the result in `hiReturn`, and returns + * the lo word. */ private def unsignedDivModHelper(alo: Int, ahi: Int, blo: Int, bhi: Int, - ask: Int): Any = { + askQuotient: Boolean): Int = { var shift = inlineNumberOfLeadingZeros(blo, bhi) - inlineNumberOfLeadingZeros(alo, ahi) @@ -959,7 +1114,7 @@ object RuntimeLong { * val, which will explose the while condition as a while(true) + if + * break, and we don't want that. */ - while (shift >= 0 && (remHi & UnsignedSafeDoubleHiMask) != 0) { + while (shift >= 0 && (remHi & SafeDoubleHiMask) != 0) { if (inlineUnsigned_>=(remLo, remHi, bShiftLo, bShiftHi)) { val newRem = new RuntimeLong(remLo, remHi) - new RuntimeLong(bShiftLo, bShiftHi) @@ -978,43 +1133,30 @@ object RuntimeLong { // Now rem < 2^53, we can finish with a double division if (inlineUnsigned_>=(remLo, remHi, blo, bhi)) { - val remDouble = asUnsignedSafeDouble(remLo, remHi) - val bDouble = asUnsignedSafeDouble(blo, bhi) + val remDouble = asSafeDouble(remLo, remHi) + val bDouble = asSafeDouble(blo, bhi) - if (ask != AskRemainder) { + if (askQuotient) { val rem_div_bDouble = fromUnsignedSafeDouble(remDouble / bDouble) val newQuot = new RuntimeLong(quotLo, quotHi) + rem_div_bDouble - quotLo = newQuot.lo - quotHi = newQuot.hi - } - - if (ask != AskQuotient) { + hiReturn = newQuot.hi + newQuot.lo + } else { val rem_mod_bDouble = remDouble % bDouble - remLo = unsignedSafeDoubleLo(rem_mod_bDouble) - remHi = unsignedSafeDoubleHi(rem_mod_bDouble) + hiReturn = unsignedSafeDoubleHi(rem_mod_bDouble) + unsignedSafeDoubleLo(rem_mod_bDouble) } - } - - if (ask == AskQuotient) { - hiReturn = quotHi - quotLo - } else if (ask == AskRemainder) { - hiReturn = remHi - remLo } else { - // AskToString (recall that b = 10^9 in this case) - val quot = asUnsignedSafeDouble(quotLo, quotHi) // != 0 - val remStr = remLo.toString // remHi is always 0 - quot.toString + substring("000000000", remStr.length) + remStr + if (askQuotient) { + hiReturn = quotHi + quotLo + } else { + hiReturn = remHi + remLo + } } } - @inline - private def inline_hiReturn_unary_-(lo: Int, hi: Int): Int = { - hiReturn = inline_hi_unary_-(lo, hi) - inline_lo_unary_-(lo) - } - @inline private def substring(s: String, start: Int): String = { import scala.scalajs.js.JSStringOps.enableJSStringOps @@ -1043,11 +1185,27 @@ object RuntimeLong { * stay on the fast side. */ @inline def isUnsignedSafeDouble(hi: Int): Boolean = - (hi & UnsignedSafeDoubleHiMask) == 0 + (hi & SafeDoubleHiMask) == 0 - /** Converts an unsigned safe double into its Double representation. */ - @inline def asUnsignedSafeDouble(lo: Int, hi: Int): Double = - hi * TwoPow32 + asUint(lo) + /** Tests whether a signed long (lo, hi) is a safe Double. + * + * This test is in fact slightly stricter than necessary, as it tests + * whether `-2^53 <= x < 2^53`, although x == 2^53 would be a perfectly safe + * Double. We do it this way because it corresponds to testing whether the + * value can be represented as a signed 54-bit integer. That is true if and + * only if the (64 - 54) = 10 most significant bits are all equal to bit 53, + * or equivalently, whether the 11 most significant bits all equal. + * + * Since there is virtually no gain to treating 2^53 itself as a safe + * Double, compared to all numbers smaller than it, we don't bother, and + * stay on the fast side. + */ + @inline def isSignedSafeDouble(hi: Int): Boolean = + ((hi ^ (hi >> 10)) & SafeDoubleHiMask) == 0 + + /** Converts a safe double (signed or unsigned) into its exact Double representation. */ + @inline def asSafeDouble(lo: Int, hi: Int): Double = + signedToDoubleApprox(lo, hi) /** Converts an unsigned safe double into its RuntimeLong representation. */ @inline def fromUnsignedSafeDouble(x: Double): RuntimeLong = @@ -1061,10 +1219,54 @@ object RuntimeLong { @inline def unsignedSafeDoubleHi(x: Double): Int = rawToInt(x / TwoPow32) + /** Approximates an unsigned (lo, hi) with a Double. */ + @inline def unsignedToDoubleApprox(lo: Int, hi: Int): Double = + uintToDouble(hi) * TwoPow32 + uintToDouble(lo) + + /** Approximates a signed (lo, hi) with a Double. + * + * If `hi` is known to be non-negative, this method is equivalent to + * `unsignedToDoubleApprox`, but it can fold away part of the computation if + * `hi` is in fact constant. + */ + @inline def signedToDoubleApprox(lo: Int, hi: Int): Double = { + /* We note a_u the mathematical value of a when interpreted as an unsigned + * quantity, and a_s when interpreted as a signed quantity. + * + * For x = (lo, hi), the result must be the correctly rounded value of x_s. + * + * If x_s >= 0, then hi_s >= 0. The obvious mathematical value of x_s is + * x_s = hi_s * 2^32 + lo_u + * + * If x_s < 0, then hi_s < 0. The fundamental definition of two's + * completement means that + * x_s = -2^64 + hi_u * 2^32 + lo_u + * Likewise, + * hi_s = -2^32 + hi_u + * + * Now take the computation for the x_s >= 0 case, but substituting values + * for the negative case: + * hi_s * 2^32 + lo_u + * = (-2^32 + hi_u) * 2^32 + lo_u + * = (-2^64 + hi_u * 2^32) + lo_u + * which is the correct mathematical result for x_s in the negative case. + * + * Therefore, we can always compute + * x_s = hi_s * 2^32 + lo_u + * When computed with `Double` values, only the last `+` can be inexact, + * hence the result is correctly round. + */ + hi.toDouble * TwoPow32 + uintToDouble(lo) + } + /** Interprets an `Int` as an unsigned integer and returns its value as a * `Double`. + * + * In user space, this would be `Integer.toUnsignedLong(x).toDouble`. + * However, we cannot use that, since it would circle back here into an + * infinite recursion. */ - @inline def asUint(x: Int): Double = { + @inline def uintToDouble(x: Int): Double = { import scala.scalajs.js.DynamicImplicits.number2dynamic (x.toDouble >>> 0).asInstanceOf[Double] } @@ -1098,6 +1300,10 @@ object RuntimeLong { def inlineUnsignedInt_<(a: Int, b: Int): Boolean = (a ^ 0x80000000) < (b ^ 0x80000000) + @inline + def inlineUnsignedInt_<=(a: Int, b: Int): Boolean = + (a ^ 0x80000000) <= (b ^ 0x80000000) + @inline def inlineUnsignedInt_>(a: Int, b: Int): Boolean = (a ^ 0x80000000) > (b ^ 0x80000000) @@ -1107,19 +1313,102 @@ object RuntimeLong { (a ^ 0x80000000) >= (b ^ 0x80000000) @inline - def inline_lo_unary_-(lo: Int): Int = - -lo - - @inline - def inline_hi_unary_-(lo: Int, hi: Int): Int = - if (lo != 0) ~hi else -hi + def inline_negate_hiReturn(lo: Int, hi: Int): Int = { + val n = sub(new RuntimeLong(0, 0), new RuntimeLong(lo, hi)) + hiReturn = n.hi + n.lo + } @inline def inline_abs(lo: Int, hi: Int): RuntimeLong = { - if (hi < 0) - new RuntimeLong(inline_lo_unary_-(lo), inline_hi_unary_-(lo, hi)) - else - new RuntimeLong(lo, hi) + /* The algorithm here is inspired by Hacker's Delight formula for `abs`. + * However, a naive application of that formula does not give good code for + * our RuntimeLong implementation. + * + * Let a be the input value RTLong(lo, hi). Overall, we want to compute: + * val longSign = a >> 63 + * (a ^ longSign) + (if (longSign) 1L else 0L) + * The last addition is performed as `- longSign` in the original formula. + * For our purpose, it is more convenient to take a step back and express + * it as the if expression. + * + * First, observe that `longSign.lo` and `longSign.hi` are both equal to + * `hi >> 31`. We therefore define + * val sign = hi >> 31 + * and rewrite our second expression by expanding the long xor: + * RTLong(lo ^ sign, hi ^ sign) + (if (sign) 1L else 0L) + * + * Making the lo/hi words of the result of the xor explicit, we get: + * + * val xlo = lo ^ sign + * val xhi = hi ^ sign + * RTLong(xlo, xhi) + (if (sign) 1L else 0L) + * + * At this point, it is convenient to examine what happens when we expand + * the addition, assuming that the 1L branch is taken. We expand + * `RTLong(xlo, xhi) + RTLong(1, 0)` and get: + * + * val rlo = xlo + 1 + * val rhi = xhi + 0 + (((xlo & 1) | ((xlo | 1) & ~rlo)) >>> 31) + * val rhi = xhi + (((xlo & 1) | ((xlo | 1) & ~rlo)) >>> 31) + * + * Since only the most significant bit of the complicated logic expression + * is relevant, we can rewrite the 1's as 0's so that `& 0` and `| 0` fold + * away: + * + * val rhi = xhi + (((xlo & 0) | ((xlo | 0) & ~rlo)) >>> 31) + * = xhi + ((( 0) | ((xlo ) & ~rlo)) >>> 31) + * = xhi + (( 0 | ( xlo & ~rlo)) >>> 31) + * = xhi + (( ( xlo & ~rlo)) >>> 31) + * = xhi + ((xlo & ~rlo) >>> 31) + * + * If the 0L branch was taken, then we should instead have + * + * val rlo = xlo + 0 = xlo + * val rhi = xhi + 0 = xhi + * + * Let us put back together everything we have so far: + * + * val sign = hi >> 31 + * val xlo = lo ^ sign + * val xhi = hi ^ sign + * val rlo = xlo + (if (sign) 1 else 0) + * val rhi = xhi + (if (sign) ((xlo & ~rlo) >>> 31) else 0) + * + * We can remove the branch for rlo as + * + * val rlo = xlo - sign + * + * Now let's just imagine we always took the then branch for rhi. We would + * overall get: + * + * val sign = hi >> 31 + * val xlo = lo ^ sign + * val xhi = hi ^ sign + * val rlo = xlo - sign + * val rhi = xhi + ((xlo & ~rlo) >>> 31) + * + * That's correct when `sign = -1`. But what happens when `sign = 0`? Let + * us work it out: + * + * val sign = 0 + * val xlo = lo ^ sign = lo + * val xhi = hi ^ sign = hi + * val rlo = xlo - sign = xlo = lo + * val rhi = xhi + ((xlo & ~rlo) >>> 31) + * = xhi + (( lo & ~lo) >>> 31) + * = xhi + (( 0 ) >>> 31) + * = xhi + 0 + * + * Bingo! It works as well! So we can take the code of the "let's just + * imagine" step. We inline the rhs of xhi at the only place where it is + * used, and we get the final algorithm. + */ + val sign = hi >> 31 + val xlo = lo ^ sign + val rlo = xlo - sign + val rhi = (hi ^ sign) + ((xlo & ~rlo) >>> 31) + new RuntimeLong(rlo, rhi) } } diff --git a/linker-private-library/src/main/scala/org/scalajs/linker/runtime/WasmRuntime.scala b/linker-private-library/src/main/scala/org/scalajs/linker/runtime/WasmRuntime.scala index 4f62a64213..aa7f1fa4e4 100644 --- a/linker-private-library/src/main/scala/org/scalajs/linker/runtime/WasmRuntime.scala +++ b/linker-private-library/src/main/scala/org/scalajs/linker/runtime/WasmRuntime.scala @@ -40,10 +40,7 @@ object WasmRuntime { val iZero = 0L val iOne = 1L - /* TODO Use doubleToRawLongBits when we add support for it. Either is - * correct in this context, but the raw variant would be more efficient. - */ - @inline def toBits(v: FType): IType = java.lang.Double.doubleToLongBits(v) + @inline def toBits(v: FType): IType = java.lang.Double.doubleToRawLongBits(v) @inline def fromBits(v: IType): FType = java.lang.Double.longBitsToDouble(v) @inline def leadingZeros(v: IType): Int = java.lang.Long.numberOfLeadingZeros(v) @inline def extendFromInt(v: Int): IType = v.toLong @@ -259,10 +256,7 @@ object WasmRuntime { val iZero = 0 val iOne = 1 - /* TODO Use floatToRawIntBits when we add support for it. Either is - * correct in this context, but the raw variant would be more efficient. - */ - @inline def toBits(v: FType): IType = java.lang.Float.floatToIntBits(v) + @inline def toBits(v: FType): IType = java.lang.Float.floatToRawIntBits(v) @inline def fromBits(v: IType): FType = java.lang.Float.intBitsToFloat(v) @inline def leadingZeros(v: IType): Int = java.lang.Integer.numberOfLeadingZeros(v) @inline def extendFromInt(v: Int): IType = v diff --git a/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureAstTransformer.scala b/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureAstTransformer.scala index 2397ff94af..56c0232121 100644 --- a/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureAstTransformer.scala +++ b/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureAstTransformer.scala @@ -376,6 +376,8 @@ private class ClosureAstTransformer(featureSet: FeatureSet, if (value) new Node(Token.TRUE) else new Node(Token.FALSE) case IntLiteral(value) => mkNumberLiteral(value) + case UintLiteral(value) => + mkNumberLiteral(Integer.toUnsignedLong(value).toDouble) case DoubleLiteral(value) => mkNumberLiteral(value) case StringLiteral(value) => diff --git a/linker/jvm/src/main/scala/org/scalajs/linker/backend/emitter/PrivateLibHolder.scala b/linker/jvm/src/main/scala/org/scalajs/linker/backend/emitter/PrivateLibHolder.scala index d668c26e25..c4849c8955 100644 --- a/linker/jvm/src/main/scala/org/scalajs/linker/backend/emitter/PrivateLibHolder.scala +++ b/linker/jvm/src/main/scala/org/scalajs/linker/backend/emitter/PrivateLibHolder.scala @@ -24,6 +24,8 @@ object PrivateLibHolder { private val stableVersion = ir.Version.fromInt(0) // never changes private val sjsirPaths = Seq( + "org/scalajs/linker/runtime/FloatingPointBitsPolyfills.sjsir", + "org/scalajs/linker/runtime/FloatingPointBitsPolyfills$.sjsir", "org/scalajs/linker/runtime/RuntimeLong.sjsir", "org/scalajs/linker/runtime/RuntimeLong$.sjsir", "org/scalajs/linker/runtime/UndefinedBehaviorError.sjsir", diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala index 9a80ac96a2..c3b428dbeb 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala @@ -31,7 +31,7 @@ import org.scalajs.ir.WellKnownNames._ import org.scalajs.linker._ import org.scalajs.linker.checker.CheckingPhase -import org.scalajs.linker.frontend.{IRLoader, LambdaSynthesizer, SyntheticClassKind} +import org.scalajs.linker.frontend.{IRLoader, LambdaSynthesizer, LinkTimeProperties, SyntheticClassKind} import org.scalajs.linker.interface._ import org.scalajs.linker.interface.unstable.ModuleInitializerImpl import org.scalajs.linker.standard._ @@ -47,15 +47,17 @@ import Infos.{NamespacedMethodName, ReachabilityInfo, ReachabilityInfoInClass} final class Analyzer(config: CommonPhaseConfig, initial: Boolean, checkIRFor: Option[CheckingPhase], failOnError: Boolean, irLoader: IRLoader) { + private val linkTimeProperties = LinkTimeProperties.fromCoreSpec(config.coreSpec) + private val infoLoader: InfoLoader = - new InfoLoader(irLoader, checkIRFor) + new InfoLoader(irLoader, checkIRFor, linkTimeProperties) def computeReachability(moduleInitializers: Seq[ModuleInitializer], symbolRequirements: SymbolRequirement, logger: Logger)(implicit ec: ExecutionContext): Future[Analysis] = { infoLoader.update(logger) - val run = new AnalyzerRun(config, initial, infoLoader)( + val run = new AnalyzerRun(config, initial, infoLoader, linkTimeProperties)( adjustExecutionContextForParallelism(ec, config.parallel)) run @@ -99,7 +101,10 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean, } private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean, - infoLoader: InfoLoader)(implicit ec: ExecutionContext) extends Analysis { + infoLoader: InfoLoader, linkTimeProperties: LinkTimeProperties)( + implicit ec: ExecutionContext) + extends Analysis { + import AnalyzerRun._ private val allowAddingSyntheticMethods = initial @@ -1488,6 +1493,7 @@ private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean, lookupOrSynthesizeClass(className, SyntheticClassKind.Lambda(descriptor)) { lambdaClassInfo => lambdaClassInfo.instantiated() lambdaClassInfo.callMethodStatically(MemberNamespace.Constructor, ctorName) + moduleUnit.addStaticDependency(lambdaClassInfo.className) } } } @@ -1538,7 +1544,7 @@ private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean, if (data.referencedLinkTimeProperties.nonEmpty) { for ((name, tpe) <- data.referencedLinkTimeProperties) { - if (!config.coreSpec.linkTimeProperties.validate(name, tpe)) { + if (!linkTimeProperties.get(name).exists(_.tpe == tpe)) { _errors ::= InvalidLinkTimeProperty(name, tpe, from) } } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala index 83003e6be5..c791727110 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/InfoLoader.scala @@ -23,13 +23,16 @@ import org.scalajs.ir.Trees._ import org.scalajs.logging._ import org.scalajs.linker.checker._ -import org.scalajs.linker.frontend.IRLoader +import org.scalajs.linker.frontend.{IRLoader, LinkTimeProperties} import org.scalajs.linker.interface.LinkingException import org.scalajs.linker.CollectionsCompat.MutableMapCompatOps import Platform.emptyThreadSafeMap -private[analyzer] final class InfoLoader(irLoader: IRLoader, checkIRFor: Option[CheckingPhase]) { +private[analyzer] final class InfoLoader(irLoader: IRLoader, + checkIRFor: Option[CheckingPhase], linkTimeProperties: LinkTimeProperties) { + + private val generator = new Infos.InfoGenerator(linkTimeProperties) private var logger: Logger = _ private val cache = emptyThreadSafeMap[ClassName, InfoLoader.ClassInfoCache] @@ -44,7 +47,7 @@ private[analyzer] final class InfoLoader(irLoader: IRLoader, checkIRFor: Option[ implicit ec: ExecutionContext): Option[Future[Infos.ClassInfo]] = { if (irLoader.classExists(className)) { val infoCache = cache.getOrElseUpdate(className, - new InfoLoader.ClassInfoCache(className, irLoader, checkIRFor)) + new InfoLoader.ClassInfoCache(className, irLoader, checkIRFor, generator)) Some(infoCache.loadInfo(logger)) } else { None @@ -60,7 +63,9 @@ private[analyzer] final class InfoLoader(irLoader: IRLoader, checkIRFor: Option[ private[analyzer] object InfoLoader { private type MethodInfos = Array[Map[MethodName, Infos.MethodInfo]] - private class ClassInfoCache(className: ClassName, irLoader: IRLoader, checkIRFor: Option[CheckingPhase]) { + private class ClassInfoCache(className: ClassName, irLoader: IRLoader, + checkIRFor: Option[CheckingPhase], generator: Infos.InfoGenerator) { + private var cacheUsed: Boolean = false private var version: Version = Version.Unversioned private var info: Future[Infos.ClassInfo] = _ @@ -103,12 +108,12 @@ private[analyzer] object InfoLoader { } private def generateInfos(classDef: ClassDef): Infos.ClassInfo = { - val referencedFieldClasses = Infos.genReferencedFieldClasses(classDef.fields) + val referencedFieldClasses = generator.genReferencedFieldClasses(classDef.fields) - prevMethodInfos = genMethodInfos(classDef.methods, prevMethodInfos) - prevJSCtorInfo = genJSCtorInfo(classDef.jsConstructor, prevJSCtorInfo) + prevMethodInfos = genMethodInfos(classDef.methods, prevMethodInfos, generator) + prevJSCtorInfo = genJSCtorInfo(classDef.jsConstructor, prevJSCtorInfo, generator) prevJSMethodPropDefInfos = - genJSMethodPropDefInfos(classDef.jsMethodProps, prevJSMethodPropDefInfos) + genJSMethodPropDefInfos(classDef.jsMethodProps, prevJSMethodPropDefInfos, generator) val exportedMembers = prevJSCtorInfo.toList ::: prevJSMethodPropDefInfos @@ -116,7 +121,7 @@ private[analyzer] object InfoLoader { * and usually quite small when they exist. */ val topLevelExports = classDef.topLevelExportDefs - .map(Infos.generateTopLevelExportInfo(classDef.name.name, _)) + .map(generator.generateTopLevelExportInfo(classDef.name.name, _)) val jsNativeMembers = classDef.jsNativeMembers .map(m => m.name.name -> m.jsNativeLoadSpec).toMap @@ -136,7 +141,7 @@ private[analyzer] object InfoLoader { } private def genMethodInfos(methods: List[MethodDef], - prevMethodInfos: MethodInfos): MethodInfos = { + prevMethodInfos: MethodInfos, generator: Infos.InfoGenerator): MethodInfos = { val builders = Array.fill(MemberNamespace.Count)(Map.newBuilder[MethodName, Infos.MethodInfo]) @@ -144,7 +149,7 @@ private[analyzer] object InfoLoader { val info = prevMethodInfos(method.flags.namespace.ordinal) .get(method.methodName) .filter(_.version.sameVersion(method.version)) - .getOrElse(Infos.generateMethodInfo(method)) + .getOrElse(generator.generateMethodInfo(method)) builders(method.flags.namespace.ordinal) += method.methodName -> info } @@ -153,16 +158,18 @@ private[analyzer] object InfoLoader { } private def genJSCtorInfo(jsCtor: Option[JSConstructorDef], - prevJSCtorInfo: Option[Infos.ReachabilityInfo]): Option[Infos.ReachabilityInfo] = { + prevJSCtorInfo: Option[Infos.ReachabilityInfo], + generator: Infos.InfoGenerator): Option[Infos.ReachabilityInfo] = { jsCtor.map { ctor => prevJSCtorInfo .filter(_.version.sameVersion(ctor.version)) - .getOrElse(Infos.generateJSConstructorInfo(ctor)) + .getOrElse(generator.generateJSConstructorInfo(ctor)) } } private def genJSMethodPropDefInfos(jsMethodProps: List[JSMethodPropDef], - prevJSMethodPropDefInfos: List[Infos.ReachabilityInfo]): List[Infos.ReachabilityInfo] = { + prevJSMethodPropDefInfos: List[Infos.ReachabilityInfo], + generator: Infos.InfoGenerator): List[Infos.ReachabilityInfo] = { /* For JS method and property definitions, we use their index in the list of * `linkedClass.exportedMembers` as their identity. We cannot use their name * because the name itself is a `Tree`. @@ -176,13 +183,13 @@ private[analyzer] object InfoLoader { if (prevJSMethodPropDefInfos.size != jsMethodProps.size) { // Regenerate everything. - jsMethodProps.map(Infos.generateJSMethodPropDefInfo(_)) + jsMethodProps.map(generator.generateJSMethodPropDefInfo(_)) } else { for { (prevInfo, member) <- prevJSMethodPropDefInfos.zip(jsMethodProps) } yield { if (prevInfo.version.sameVersion(member.version)) prevInfo - else Infos.generateJSMethodPropDefInfo(member) + else generator.generateJSMethodPropDefInfo(member) } } } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala index fe957ca837..90fe76ca07 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala @@ -22,8 +22,7 @@ import org.scalajs.ir.Types._ import org.scalajs.ir.Version import org.scalajs.ir.WellKnownNames._ -import org.scalajs.linker.backend.emitter.Transients._ -import org.scalajs.linker.standard.LinkedTopLevelExport +import org.scalajs.linker.frontend.{LinkTimeEvaluator, LinkTimeProperties} import org.scalajs.linker.standard.ModuleSet.ModuleID object Infos { @@ -184,27 +183,6 @@ object Infos { val methodName: MethodName ) extends MemberReachabilityInfo - def genReferencedFieldClasses(fields: List[AnyFieldDef]): Map[FieldName, ClassName] = { - val builder = Map.newBuilder[FieldName, ClassName] - - fields.foreach { - case FieldDef(flags, FieldIdent(name), _, ftpe) => - if (!flags.namespace.isStatic) { - ftpe match { - case ClassType(cls, _) => - builder += name -> cls - case ArrayType(ArrayTypeRef(ClassRef(cls), _), _) => - builder += name -> cls - case _ => - } - } - case _: JSFieldDef => - // Nothing to do. - } - - builder.result() - } - final class ReachabilityInfoBuilder(version: Version) { import ReachabilityInfoBuilder._ private val byClass = mutable.Map.empty[ClassName, ReachabilityInfoInClassBuilder] @@ -415,8 +393,11 @@ object Infos { def addUsedClassSuperClass(): this.type = setFlag(ReachabilityInfo.FlagUsedClassSuperClass) - def addReferencedLinkTimeProperty(linkTimeProperty: LinkTimeProperty): this.type = { + def markNeedsDesugaring(): this.type = setFlag(ReachabilityInfo.FlagNeedsDesugaring) + + def addReferencedLinkTimeProperty(linkTimeProperty: LinkTimeProperty): this.type = { + markNeedsDesugaring() linkTimeProperties.append((linkTimeProperty.name, linkTimeProperty.tpe)) this } @@ -539,46 +520,71 @@ object Infos { } } - /** Generates the [[MethodInfo]] of a - * [[org.scalajs.ir.Trees.MethodDef Trees.MethodDef]]. - */ - def generateMethodInfo(methodDef: MethodDef): MethodInfo = - new GenInfoTraverser(methodDef.version).generateMethodInfo(methodDef) + final class InfoGenerator(linkTimeProperties: LinkTimeProperties) { + def genReferencedFieldClasses(fields: List[AnyFieldDef]): Map[FieldName, ClassName] = { + val builder = Map.newBuilder[FieldName, ClassName] + + fields.foreach { + case FieldDef(flags, FieldIdent(name), _, ftpe) => + if (!flags.namespace.isStatic) { + ftpe match { + case ClassType(cls, _) => + builder += name -> cls + case ArrayType(ArrayTypeRef(ClassRef(cls), _), _) => + builder += name -> cls + case _ => + } + } + case _: JSFieldDef => + // Nothing to do. + } - /** Generates the [[ReachabilityInfo]] of a - * [[org.scalajs.ir.Trees.JSConstructorDef Trees.JSConstructorDef]]. - */ - def generateJSConstructorInfo(ctorDef: JSConstructorDef): ReachabilityInfo = - new GenInfoTraverser(ctorDef.version).generateJSConstructorInfo(ctorDef) + builder.result() + } - /** Generates the [[ReachabilityInfo]] of a - * [[org.scalajs.ir.Trees.JSMethodDef Trees.JSMethodDef]]. - */ - def generateJSMethodInfo(methodDef: JSMethodDef): ReachabilityInfo = - new GenInfoTraverser(methodDef.version).generateJSMethodInfo(methodDef) + /** Generates the [[MethodInfo]] of a + * [[org.scalajs.ir.Trees.MethodDef Trees.MethodDef]]. + */ + def generateMethodInfo(methodDef: MethodDef): MethodInfo = + new GenInfoTraverser(methodDef.version, linkTimeProperties).generateMethodInfo(methodDef) - /** Generates the [[ReachabilityInfo]] of a - * [[org.scalajs.ir.Trees.JSPropertyDef Trees.JSPropertyDef]]. - */ - def generateJSPropertyInfo(propertyDef: JSPropertyDef): ReachabilityInfo = - new GenInfoTraverser(propertyDef.version).generateJSPropertyInfo(propertyDef) + /** Generates the [[ReachabilityInfo]] of a + * [[org.scalajs.ir.Trees.JSConstructorDef Trees.JSConstructorDef]]. + */ + def generateJSConstructorInfo(ctorDef: JSConstructorDef): ReachabilityInfo = + new GenInfoTraverser(ctorDef.version, linkTimeProperties).generateJSConstructorInfo(ctorDef) - def generateJSMethodPropDefInfo(member: JSMethodPropDef): ReachabilityInfo = member match { - case methodDef: JSMethodDef => generateJSMethodInfo(methodDef) - case propertyDef: JSPropertyDef => generateJSPropertyInfo(propertyDef) - } + /** Generates the [[ReachabilityInfo]] of a + * [[org.scalajs.ir.Trees.JSMethodDef Trees.JSMethodDef]]. + */ + def generateJSMethodInfo(methodDef: JSMethodDef): ReachabilityInfo = + new GenInfoTraverser(methodDef.version, linkTimeProperties).generateJSMethodInfo(methodDef) + + /** Generates the [[ReachabilityInfo]] of a + * [[org.scalajs.ir.Trees.JSPropertyDef Trees.JSPropertyDef]]. + */ + def generateJSPropertyInfo(propertyDef: JSPropertyDef): ReachabilityInfo = + new GenInfoTraverser(propertyDef.version, linkTimeProperties).generateJSPropertyInfo(propertyDef) - /** Generates the [[MethodInfo]] for the top-level exports. */ - def generateTopLevelExportInfo(enclosingClass: ClassName, - topLevelExportDef: TopLevelExportDef): TopLevelExportInfo = { - val info = new GenInfoTraverser(Version.Unversioned) - .generateTopLevelExportInfo(enclosingClass, topLevelExportDef) - new TopLevelExportInfo(info, - ModuleID(topLevelExportDef.moduleID), - topLevelExportDef.topLevelExportName) + def generateJSMethodPropDefInfo(member: JSMethodPropDef): ReachabilityInfo = member match { + case methodDef: JSMethodDef => generateJSMethodInfo(methodDef) + case propertyDef: JSPropertyDef => generateJSPropertyInfo(propertyDef) + } + + /** Generates the [[MethodInfo]] for the top-level exports. */ + def generateTopLevelExportInfo(enclosingClass: ClassName, + topLevelExportDef: TopLevelExportDef): TopLevelExportInfo = { + val info = new GenInfoTraverser(Version.Unversioned, linkTimeProperties) + .generateTopLevelExportInfo(enclosingClass, topLevelExportDef) + new TopLevelExportInfo(info, + ModuleID(topLevelExportDef.moduleID), + topLevelExportDef.topLevelExportName) + } } - private final class GenInfoTraverser(version: Version) extends Traverser { + private final class GenInfoTraverser(version: Version, + linkTimeProperties: LinkTimeProperties) extends Traverser { + private val builder = new ReachabilityInfoBuilder(version) /** Whether we are currently in the body of an `async` closure. @@ -684,6 +690,36 @@ object Infos { // Capture values are in the enclosing scope; not the scope of the closure captureValues.foreach(traverse(_)) + // Do not call super.traverse(), as we must follow a single branch + case LinkTimeIf(cond, thenp, elsep) => + builder.markNeedsDesugaring() + traverse(cond) + LinkTimeEvaluator.tryEvalLinkTimeBooleanExpr(linkTimeProperties, cond) match { + case Some(result) => + if (result) + traverse(thenp) + else + traverse(elsep) + case None => + /* Ignore. Recall that we *assume* here that the ClassDef is + * valid on its own, i.e., it would pass the ClassDefChecker + * (irrespective of whether we actually run that checker). + * + * Under that assumption, the only failure mode for evaluating + * the `cond` is that it refers to a `LinkTimeProperty` that + * does not exist or has the wrong type. In that case, the + * analyzer will report a linking error at least for that + * `LinkTimeProperty` inside the `cond` (which we always + * traverse). + * + * If the assumption is broken and the evaluation failure was + * due to an ill-formed or ill-typed `cond`, then Desugar will + * eventually crash (with a message suggesting to enable checking + * the IR). + */ + () + } + // In all other cases, we'll have to call super.traverse() case _ => tree match { @@ -753,12 +789,12 @@ object Infos { import BinaryOp._ op match { - case Int_/ | Int_% => + case Int_/ | Int_% | Int_unsigned_/ | Int_unsigned_% => rhs match { case IntLiteral(r) if r != 0 => case _ => builder.addUsedIntLongDivModByMaybeZero() } - case Long_/ | Long_% => + case Long_/ | Long_% | Long_unsigned_/ | Long_unsigned_% => rhs match { case LongLiteral(r) if r != 0L => case _ => builder.addUsedIntLongDivModByMaybeZero() diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/CoreJSLib.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/CoreJSLib.scala index 6fb37ce343..bc8610d0c0 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/CoreJSLib.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/CoreJSLib.scala @@ -101,6 +101,8 @@ private[emitter] object CoreJSLib { private val StringRef = globalRef("String") private val MathRef = globalRef("Math") private val NumberRef = globalRef("Number") + private val DataViewRef = globalRef("DataView") + private val ArrayBufferRef = globalRef("ArrayBuffer") private val TypeErrorRef = globalRef("TypeError") private def BigIntRef = globalRef("BigInt") private val SymbolRef = globalRef("Symbol") @@ -190,6 +192,25 @@ private[emitter] object CoreJSLib { Return((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0) )) + case Clz32Builtin => + // See Hacker's Delight, Section 5-3 + val x = varRef("x") + val r = varRef("r") + genArrowFunction(paramList(x), { + If(x === 0, { + Return(32) + }, { + Block( + let(r, 1), + If((x & 0xffff0000) === 0, Block(x := x << 16, r := r + 16), Skip()), + If((x & 0xff000000) === 0, Block(x := x << 8, r := r + 8), Skip()), + If((x & 0xf0000000) === 0, Block(x := x << 4, r := r + 4), Skip()), + If((x & 0xc0000000) === 0, Block(x := x << 2, r := r + 2), Skip()), + Return(r + (x >> 31)) + ) + }) + }) + case FroundBuiltin => val v = varRef("v") val Float32ArrayRef = globalRef("Float32Array") @@ -874,18 +895,54 @@ private[emitter] object CoreJSLib { def wrapBigInt64(tree: Tree): Tree = Apply(genIdentBracketSelect(BigIntRef, "asIntN"), 64 :: tree :: Nil) + /* Defines a core function of 1 argument `x` that uses the `fpBitsDataView` + * global var. When linking for ES 2015+, the provided body is always + * used, as `fpBitsDataView` is known to exist. When linking for 5.1, + * a polyfill from `org.scalajs.linker.runtime.FloatingPointBitsPolyfills` + * is used when `fpBitsDataView` is `null`. + * + * The `body` function receives `x` and `fpBitsDataView` as arguments, + * in that order. + */ + def defineFloatingPointBitsFunctionOrPolyfill(name: VarField, + polyfillMethod: MethodName)(body: (VarRef, VarRef) => Tree): List[Tree] = { + + val dataView = varRef("dataView") + val dataViewConst = const(dataView, globalVar(VarField.fpBitsDataView, CoreVar)) + + if (esVersion >= ESVersion.ES2015) { + defineFunction1(name) { x => + Block( + dataViewConst, + body(x, dataView) + ) + } + } else { + val x = varRef("x") + + extractWithGlobals(globalVarDef(name, CoreVar, { + If(globalVar(VarField.fpBitsDataView, CoreVar) !== Null(), { + genArrowFunction(paramList(x), { + Block( + dataViewConst, + body(x, dataView) + ) + }) + }, { + genArrowFunction(paramList(x), { + Return(Apply(globalVar(VarField.s, (FloatingPointBitsPolyfillsClass, polyfillMethod)), List(x))) + }) + }) + })) + } + } + condDefs(shouldDefineIntLongDivModFunctions)( - defineFunction2(VarField.intDiv) { (x, y) => + defineFunction1(VarField.checkIntDivisor) { y => If(y === 0, throwDivByZero, { - Return((x / y) | 0) + Return(y) }) - } ::: - defineFunction2(VarField.intMod) { (x, y) => - If(y === 0, throwDivByZero, { - Return((x % y) | 0) - }) - } ::: - Nil + } ) ::: defineFunction1(VarField.doubleToInt) { x => Return(If(x > 2147483647, 2147483647, If(x < -2147483648, -2147483648, x | 0))) @@ -909,19 +966,34 @@ private[emitter] object CoreJSLib { } ) ::: condDefs(allowBigIntsForLongs && shouldDefineIntLongDivModFunctions)( - defineFunction2(VarField.longDiv) { (x, y) => - If(y === bigInt(0), throwDivByZero, { - Return(wrapBigInt64(x / y)) - }) - } ::: - defineFunction2(VarField.longMod) { (x, y) => + defineFunction1(VarField.checkLongDivisor) { y => If(y === bigInt(0), throwDivByZero, { - Return(wrapBigInt64(x % y)) + Return(y) }) - } ::: - Nil + } ) ::: condDefs(allowBigIntsForLongs)( + defineFunction1(VarField.longClz) { x => + // (Math.clz32 o Number)(bigIntArg), i.e., Math.clz32(Number(bigIntArg)) + def clz32_o_Number(bigIntArg: Tree): Tree = { + genCallPolyfillableBuiltin(PolyfillableBuiltin.Clz32Builtin, + Apply(NumberRef, List(bigIntArg))) + } + + val hi = varRef("hi") + + Block( + const(hi, x >> bigInt(32)), + Return { + If(hi !== bigInt(0L), { + clz32_o_Number(hi) + }, { + int(32) + clz32_o_Number(x) + }) + } + ) + } ::: + defineFunction1(VarField.doubleToLong)(x => Return { If(x < double(-9223372036854775808.0), { // -2^63 bigInt(-9223372036854775808L) @@ -956,7 +1028,51 @@ private[emitter] object CoreJSLib { Return(genCallPolyfillableBuiltin(FroundBuiltin, If(x < bigInt(0L), -absR, absR))) ) } - ) + ) ::: + extractWithGlobals(globalVarDef(VarField.fpBitsDataView, CoreVar, { + val newDataView = New(DataViewRef, List(New(ArrayBufferRef, List(8)))) + if (esVersion >= ESVersion.ES2015) { + newDataView + } else { + If(typeof(DataViewRef) !== str("undefined"), { + newDataView + }, { + Null() + }) + } + })) ::: + defineFloatingPointBitsFunctionOrPolyfill(VarField.floatToBits, floatToBits) { (x, fpBitsDataView) => + Block( + Apply(genIdentBracketSelect(fpBitsDataView, "setFloat32"), List(0, x, bool(true))), + Return(Apply(genIdentBracketSelect(fpBitsDataView, "getInt32"), List(0, bool(true)))) + ) + } ::: + defineFloatingPointBitsFunctionOrPolyfill(VarField.floatFromBits, floatFromBits) { (x, fpBitsDataView) => + Block( + Apply(genIdentBracketSelect(fpBitsDataView, "setInt32"), List(0, x, bool(true))), + Return(Apply(genIdentBracketSelect(fpBitsDataView, "getFloat32"), List(0, bool(true)))) + ) + } ::: + defineFloatingPointBitsFunctionOrPolyfill(VarField.doubleToBits, doubleToBits) { (x, fpBitsDataView) => + if (allowBigIntsForLongs) { + Block( + Apply(genIdentBracketSelect(fpBitsDataView, "setFloat64"), List(0, x, bool(true))), + Return(Apply(genIdentBracketSelect(fpBitsDataView, "getBigInt64"), List(0, bool(true)))) + ) + } else { + Return(genLongApplyStatic(LongImpl.fromDoubleBits, x, fpBitsDataView)) + } + } ::: + defineFloatingPointBitsFunctionOrPolyfill(VarField.doubleFromBits, doubleFromBits) { (x, fpBitsDataView) => + if (allowBigIntsForLongs) { + Block( + Apply(genIdentBracketSelect(fpBitsDataView, "setBigInt64"), List(0, x, bool(true))), + Return(Apply(genIdentBracketSelect(fpBitsDataView, "getFloat64"), List(0, bool(true)))) + ) + } else { + Return(genLongApplyStatic(LongImpl.bitsToDouble, x, fpBitsDataView)) + } + } } private def defineES2015LikeHelpers(): List[Tree] = ( diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala index b625c51c12..77e0c1ba39 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Emitter.scala @@ -1437,8 +1437,18 @@ object Emitter { multiple( instanceTests(LongImpl.RuntimeLongClass), instantiateClass(LongImpl.RuntimeLongClass, LongImpl.AllConstructors.toList), - callMethods(LongImpl.RuntimeLongClass, LongImpl.AllMethods.toList), - callOnModule(LongImpl.RuntimeLongModuleClass, LongImpl.AllModuleMethods.toList) + callMethods(LongImpl.RuntimeLongClass, LongImpl.BoxedLongMethods.toList), + callStaticMethods(LongImpl.RuntimeLongClass, LongImpl.OperatorMethods.toList) + ) + }, + + cond(config.coreSpec.esFeatures.esVersion < ESVersion.ES2015) { + val cls = FloatingPointBitsPolyfillsClass + multiple( + callStaticMethod(cls, floatToBits), + callStaticMethod(cls, floatFromBits), + callStaticMethod(cls, doubleToBits), + callStaticMethod(cls, doubleFromBits) ) } ) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/EmitterNames.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/EmitterNames.scala index 02e46fd548..3bf4e8984a 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/EmitterNames.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/EmitterNames.scala @@ -25,6 +25,9 @@ private[emitter] object EmitterNames { val UndefinedBehaviorErrorClass = ClassName("org.scalajs.linker.runtime.UndefinedBehaviorError") + val FloatingPointBitsPolyfillsClass = + ClassName("org.scalajs.linker.runtime.FloatingPointBitsPolyfills") + // Field names val exceptionFieldName = FieldName(JavaScriptExceptionClass, SimpleFieldName("exception")) @@ -43,4 +46,9 @@ private[emitter] object EmitterNames { val getNameMethodName = MethodName("getName", Nil, ClassRef(BoxedStringClass)) val getSuperclassMethodName = MethodName("getSuperclass", Nil, ClassRef(ClassClass)) + + val floatToBits = MethodName("floatToBits", List(FloatRef), IntRef) + val floatFromBits = MethodName("floatFromBits", List(IntRef), DoubleRef) // yes, Double + val doubleToBits = MethodName("doubleToBits", List(DoubleRef), LongRef) + val doubleFromBits = MethodName("doubleFromBits", List(LongRef), DoubleRef) } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/FunctionEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/FunctionEmitter.scala index e5c444e342..e3f696248c 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/FunctionEmitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/FunctionEmitter.scala @@ -1266,8 +1266,9 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { def test(tree: Tree): Boolean = tree match { // Atomic expressions - case _: Literal => true - case _: JSNewTarget => true + case _: Literal => true + case _: JSNewTarget => true + case Transient(GetFPBitsDataView) => true // Vars (side-effect free, pure if immutable) case VarRef(name) => @@ -1286,12 +1287,14 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { allowSideEffects && test(lhs) // Division and modulo, preserve pureness unless they can divide by 0 - case BinaryOp(BinaryOp.Int_/ | BinaryOp.Int_%, lhs, rhs) if !allowSideEffects => + case BinaryOp(BinaryOp.Int_/ | BinaryOp.Int_% | BinaryOp.Int_unsigned_/ | BinaryOp.Int_unsigned_%, lhs, rhs) + if !allowSideEffects => rhs match { case IntLiteral(r) if r != 0 => test(lhs) case _ => false } - case BinaryOp(BinaryOp.Long_/ | BinaryOp.Long_%, lhs, rhs) if !allowSideEffects => + case BinaryOp(BinaryOp.Long_/ | BinaryOp.Long_% | BinaryOp.Long_unsigned_/ | BinaryOp.Long_unsigned_%, lhs, rhs) + if !allowSideEffects => rhs match { case LongLiteral(r) if r != 0L => test(lhs) case _ => false @@ -2205,6 +2208,13 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { def or0(tree: js.Tree): js.Tree = js.BinaryOp(JSBinaryOp.|, tree, js.IntLiteral(0)) + def shr0(tree: js.Tree): js.Tree = tree match { + case js.IntLiteral(value) => + js.UintLiteral(value) + case _ => + js.BinaryOp(JSBinaryOp.>>>, tree, js.IntLiteral(0)) + } + def bigIntShiftRhs(tree: js.Tree): js.Tree = { tree match { case js.IntLiteral(v) => @@ -2389,7 +2399,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { if (useBigIntForLongs) js.Apply(genGlobalVarRef("BigInt"), List(newLhs)) else - genLongModuleApply(LongImpl.fromInt, newLhs) + genLongApplyStatic(LongImpl.fromInt, newLhs) // Narrowing conversions case IntToChar => @@ -2406,7 +2416,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { if (useBigIntForLongs) js.Apply(genGlobalVarRef("Number"), List(wrapBigInt32(newLhs))) else - genApply(newLhs, LongImpl.toInt) + genLongApplyStatic(LongImpl.toInt, newLhs) case DoubleToInt => genCallHelper(VarField.doubleToInt, newLhs) case DoubleToFloat => @@ -2417,19 +2427,19 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { if (useBigIntForLongs) js.Apply(genGlobalVarRef("Number"), List(newLhs)) else - genApply(newLhs, LongImpl.toDouble) + genLongApplyStatic(LongImpl.toDouble, newLhs) case DoubleToLong => if (useBigIntForLongs) genCallHelper(VarField.doubleToLong, newLhs) else - genLongModuleApply(LongImpl.fromDouble, newLhs) + genLongApplyStatic(LongImpl.fromDouble, newLhs) // Long -> Float (neither widening nor narrowing) case LongToFloat => if (useBigIntForLongs) genCallHelper(VarField.longToFloat, newLhs) else - genApply(newLhs, LongImpl.toFloat) + genLongApplyStatic(LongImpl.toFloat, newLhs) // String.length case String_length => @@ -2515,6 +2525,31 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { genIsInstanceOfClass(newLhs, JavaScriptExceptionClass), genSelect(newLhs, FieldIdent(exceptionFieldName)), newLhs) + + // Floating point bit manipulation + case Float_toBits => + genCallHelper(VarField.floatToBits, newLhs) + case Float_fromBits => + genCallHelper(VarField.floatFromBits, newLhs) + case Double_toBits => + genCallHelper(VarField.doubleToBits, newLhs) + case Double_fromBits => + genCallHelper(VarField.doubleFromBits, newLhs) + + // clz + case Int_clz => + genCallPolyfillableBuiltin(PolyfillableBuiltin.Clz32Builtin, newLhs) + case Long_clz => + if (useBigIntForLongs) + genCallHelper(VarField.longClz, newLhs) + else + genLongApplyStatic(LongImpl.clz, newLhs) + + case UnsignedIntToLong => + if (useBigIntForLongs) + js.Apply(genGlobalVarRef("BigInt"), List(shr0(newLhs))) + else + genLongApplyStatic(LongImpl.fromUnsignedInt, newLhs) } case BinaryOp(op, lhs, rhs) => @@ -2620,20 +2655,17 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { } case Int_* => genCallPolyfillableBuiltin(ImulBuiltin, newLhs, newRhs) - case Int_/ => - rhs match { - case IntLiteral(r) if r != 0 => - or0(js.BinaryOp(JSBinaryOp./, newLhs, newRhs)) - case _ => - genCallHelper(VarField.intDiv, newLhs, newRhs) - } - case Int_% => - rhs match { - case IntLiteral(r) if r != 0 => - or0(js.BinaryOp(JSBinaryOp.%, newLhs, newRhs)) - case _ => - genCallHelper(VarField.intMod, newLhs, newRhs) + case Int_/ | Int_% | Int_unsigned_/ | Int_unsigned_% => + val newRhs1 = rhs match { + case IntLiteral(r) if r != 0 => newRhs + case _ => genCallHelper(VarField.checkIntDivisor, newRhs) } + or0((op: @switch) match { + case Int_/ => js.BinaryOp(JSBinaryOp./, newLhs, newRhs1) + case Int_% => js.BinaryOp(JSBinaryOp.%, newLhs, newRhs1) + case Int_unsigned_/ => js.BinaryOp(JSBinaryOp./, shr0(newLhs), shr0(newRhs1)) + case Int_unsigned_% => js.BinaryOp(JSBinaryOp.%, shr0(newLhs), shr0(newRhs1)) + }) case Int_| => js.BinaryOp(JSBinaryOp.|, newLhs, newRhs) case Int_& => js.BinaryOp(JSBinaryOp.&, newLhs, newRhs) @@ -2655,117 +2687,123 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { if (useBigIntForLongs) wrapBigInt64(js.BinaryOp(JSBinaryOp.+, newLhs, newRhs)) else - genApply(newLhs, LongImpl.+, newRhs) + genLongApplyStatic(LongImpl.add, newLhs, newRhs) case Long_- => - lhs match { - case LongLiteral(0L) => - if (useBigIntForLongs) + if (useBigIntForLongs) { + lhs match { + case LongLiteral(0L) => wrapBigInt64(js.UnaryOp(JSUnaryOp.-, newRhs)) - else - genApply(newRhs, LongImpl.UNARY_-) - case _ => - if (useBigIntForLongs) + case _ => wrapBigInt64(js.BinaryOp(JSBinaryOp.-, newLhs, newRhs)) - else - genApply(newLhs, LongImpl.-, newRhs) + } + } else { + /* RuntimeLong does not have a dedicated method for 0L - b. + * The regular expansion done by the optimizer for the binary + * form is already optimal. + * So we don't special-case it here either. + */ + genLongApplyStatic(LongImpl.sub, newLhs, newRhs) } case Long_* => if (useBigIntForLongs) wrapBigInt64(js.BinaryOp(JSBinaryOp.*, newLhs, newRhs)) else - genApply(newLhs, LongImpl.*, newRhs) - case Long_/ => + genLongApplyStatic(LongImpl.mul, newLhs, newRhs) + case Long_/ | Long_% | Long_unsigned_/ | Long_unsigned_% => if (useBigIntForLongs) { - rhs match { - case LongLiteral(r) if r != 0L => - wrapBigInt64(js.BinaryOp(JSBinaryOp./, newLhs, newRhs)) - case _ => - genCallHelper(VarField.longDiv, newLhs, newRhs) + val newRhs1 = rhs match { + case LongLiteral(r) if r != 0L => newRhs + case _ => genCallHelper(VarField.checkLongDivisor, newRhs) } + wrapBigInt64((op: @switch) match { + case Long_/ => js.BinaryOp(JSBinaryOp./, newLhs, newRhs1) + case Long_% => js.BinaryOp(JSBinaryOp.%, newLhs, newRhs1) + case Long_unsigned_/ => js.BinaryOp(JSBinaryOp./, wrapBigIntU64(newLhs), wrapBigIntU64(newRhs1)) + case Long_unsigned_% => js.BinaryOp(JSBinaryOp.%, wrapBigIntU64(newLhs), wrapBigIntU64(newRhs1)) + }) } else { - genApply(newLhs, LongImpl./, newRhs) - } - case Long_% => - if (useBigIntForLongs) { - rhs match { - case LongLiteral(r) if r != 0L => - wrapBigInt64(js.BinaryOp(JSBinaryOp.%, newLhs, newRhs)) - case _ => - genCallHelper(VarField.longMod, newLhs, newRhs) + // The zero divisor check is performed by the implementation methods + val implMethodName = (op: @switch) match { + case Long_/ => LongImpl.divide + case Long_% => LongImpl.remainder + case Long_unsigned_/ => LongImpl.divideUnsigned + case Long_unsigned_% => LongImpl.remainderUnsigned } - } else { - genApply(newLhs, LongImpl.%, newRhs) + genLongApplyStatic(implMethodName, newLhs, newRhs) } case Long_| => if (useBigIntForLongs) wrapBigInt64(js.BinaryOp(JSBinaryOp.|, newLhs, newRhs)) else - genApply(newLhs, LongImpl.|, newRhs) + genLongApplyStatic(LongImpl.or, newLhs, newRhs) case Long_& => if (useBigIntForLongs) wrapBigInt64(js.BinaryOp(JSBinaryOp.&, newLhs, newRhs)) else - genApply(newLhs, LongImpl.&, newRhs) + genLongApplyStatic(LongImpl.and, newLhs, newRhs) case Long_^ => - lhs match { - case LongLiteral(-1L) => - if (useBigIntForLongs) + if (useBigIntForLongs) { + lhs match { + case LongLiteral(-1L) => wrapBigInt64(js.UnaryOp(JSUnaryOp.~, newRhs)) - else - genApply(newRhs, LongImpl.UNARY_~) - case _ => - if (useBigIntForLongs) + case _ => wrapBigInt64(js.BinaryOp(JSBinaryOp.^, newLhs, newRhs)) - else - genApply(newLhs, LongImpl.^, newRhs) + } + } else { + /* RuntimeLong does not have a dedicated method for -1L ^ b. + * The regular expansion done by the optimizer for the binary + * form is already optimal. + * So we don't special-case it here either. + */ + genLongApplyStatic(LongImpl.xor, newLhs, newRhs) } case Long_<< => if (useBigIntForLongs) wrapBigInt64(js.BinaryOp(JSBinaryOp.<<, newLhs, bigIntShiftRhs(newRhs))) else - genApply(newLhs, LongImpl.<<, newRhs) + genLongApplyStatic(LongImpl.shl, newLhs, newRhs) case Long_>>> => if (useBigIntForLongs) wrapBigInt64(js.BinaryOp(JSBinaryOp.>>, wrapBigIntU64(newLhs), bigIntShiftRhs(newRhs))) else - genApply(newLhs, LongImpl.>>>, newRhs) + genLongApplyStatic(LongImpl.shr, newLhs, newRhs) case Long_>> => if (useBigIntForLongs) wrapBigInt64(js.BinaryOp(JSBinaryOp.>>, newLhs, bigIntShiftRhs(newRhs))) else - genApply(newLhs, LongImpl.>>, newRhs) + genLongApplyStatic(LongImpl.sar, newLhs, newRhs) case Long_== => if (useBigIntForLongs) js.BinaryOp(JSBinaryOp.===, newLhs, newRhs) else - genApply(newLhs, LongImpl.===, newRhs) + genLongApplyStatic(LongImpl.equals_, newLhs, newRhs) case Long_!= => if (useBigIntForLongs) js.BinaryOp(JSBinaryOp.!==, newLhs, newRhs) else - genApply(newLhs, LongImpl.!==, newRhs) + genLongApplyStatic(LongImpl.notEquals, newLhs, newRhs) case Long_< => if (useBigIntForLongs) js.BinaryOp(JSBinaryOp.<, newLhs, newRhs) else - genApply(newLhs, LongImpl.<, newRhs) + genLongApplyStatic(LongImpl.lt, newLhs, newRhs) case Long_<= => if (useBigIntForLongs) js.BinaryOp(JSBinaryOp.<=, newLhs, newRhs) else - genApply(newLhs, LongImpl.<=, newRhs) + genLongApplyStatic(LongImpl.le, newLhs, newRhs) case Long_> => if (useBigIntForLongs) js.BinaryOp(JSBinaryOp.>, newLhs, newRhs) else - genApply(newLhs, LongImpl.>, newRhs) + genLongApplyStatic(LongImpl.gt, newLhs, newRhs) case Long_>= => if (useBigIntForLongs) js.BinaryOp(JSBinaryOp.>=, newLhs, newRhs) else - genApply(newLhs, LongImpl.>=, newRhs) + genLongApplyStatic(LongImpl.ge, newLhs, newRhs) case Float_+ => genFround(js.BinaryOp(JSBinaryOp.+, newLhs, newRhs)) case Float_- => genFround(js.BinaryOp(JSBinaryOp.-, newLhs, newRhs)) @@ -2815,6 +2853,32 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { js.Apply(extractClassData(lhs, newLhs) DOT cpn.cast, newRhs :: Nil) case Class_newArray => js.Apply(extractClassData(lhs, newLhs) DOT cpn.newArray, newRhs :: Nil) + + case Int_unsigned_< => js.BinaryOp(JSBinaryOp.<, shr0(newLhs), shr0(newRhs)) + case Int_unsigned_<= => js.BinaryOp(JSBinaryOp.<=, shr0(newLhs), shr0(newRhs)) + case Int_unsigned_> => js.BinaryOp(JSBinaryOp.>, shr0(newLhs), shr0(newRhs)) + case Int_unsigned_>= => js.BinaryOp(JSBinaryOp.>=, shr0(newLhs), shr0(newRhs)) + + case Long_unsigned_< => + if (useBigIntForLongs) + js.BinaryOp(JSBinaryOp.<, wrapBigIntU64(newLhs), wrapBigIntU64(newRhs)) + else + genLongApplyStatic(LongImpl.ltu, newLhs, newRhs) + case Long_unsigned_<= => + if (useBigIntForLongs) + js.BinaryOp(JSBinaryOp.<=, wrapBigIntU64(newLhs), wrapBigIntU64(newRhs)) + else + genLongApplyStatic(LongImpl.leu, newLhs, newRhs) + case Long_unsigned_> => + if (useBigIntForLongs) + js.BinaryOp(JSBinaryOp.>, wrapBigIntU64(newLhs), wrapBigIntU64(newRhs)) + else + genLongApplyStatic(LongImpl.gtu, newLhs, newRhs) + case Long_unsigned_>= => + if (useBigIntForLongs) + js.BinaryOp(JSBinaryOp.>=, wrapBigIntU64(newLhs), wrapBigIntU64(newRhs)) + else + genLongApplyStatic(LongImpl.geu, newLhs, newRhs) } case NewArray(typeRef, length) => @@ -2879,6 +2943,9 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) { case Transient(ObjectClassName(obj)) => genCallHelper(VarField.objectClassName, transformExprNoChar(obj)) + case Transient(GetFPBitsDataView) => + globalVar(VarField.fpBitsDataView, CoreVar) + case Transient(ArrayToTypedArray(expr, primRef)) => val value = transformExprNoChar(checkNotNull(expr)) val valueUnderlying = genSyntheticPropSelect(value, SyntheticProperty.u) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/LongImpl.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/LongImpl.scala index e4059324e0..86f9b419af 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/LongImpl.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/LongImpl.scala @@ -18,85 +18,109 @@ import org.scalajs.ir.WellKnownNames._ private[linker] object LongImpl { final val RuntimeLongClass = ClassName("org.scalajs.linker.runtime.RuntimeLong") - final val RuntimeLongModuleClass = ClassName("org.scalajs.linker.runtime.RuntimeLong$") final val lo = MethodName("lo", Nil, IntRef) final val hi = MethodName("hi", Nil, IntRef) private final val RTLongRef = ClassRef(RuntimeLongClass) private final val OneRTLongRef = RTLongRef :: Nil + private final val TwoRTLongRefs = RTLongRef :: OneRTLongRef def unaryOp(name: String): MethodName = - MethodName(name, Nil, RTLongRef) + MethodName(name, OneRTLongRef, RTLongRef) def binaryOp(name: String): MethodName = - MethodName(name, OneRTLongRef, RTLongRef) + MethodName(name, TwoRTLongRefs, RTLongRef) def shiftOp(name: String): MethodName = - MethodName(name, List(IntRef), RTLongRef) + MethodName(name, List(RTLongRef, IntRef), RTLongRef) def compareOp(name: String): MethodName = - MethodName(name, OneRTLongRef, BooleanRef) - - final val UNARY_- = unaryOp("unary_$minus") - final val UNARY_~ = unaryOp("unary_$tilde") - - final val + = binaryOp("$plus") - final val - = binaryOp("$minus") - final val * = binaryOp("$times") - final val / = binaryOp("$div") - final val % = binaryOp("$percent") - - final val | = binaryOp("$bar") - final val & = binaryOp("$amp") - final val ^ = binaryOp("$up") - - final val << = shiftOp("$less$less") - final val >>> = shiftOp("$greater$greater$greater") - final val >> = shiftOp("$greater$greater") - - final val === = compareOp("equals") - final val !== = compareOp("notEquals") - final val < = compareOp("$less") - final val <= = compareOp("$less$eq") - final val > = compareOp("$greater") - final val >= = compareOp("$greater$eq") - - final val toInt = MethodName("toInt", Nil, IntRef) - final val toFloat = MethodName("toFloat", Nil, FloatRef) - final val toDouble = MethodName("toDouble", Nil, DoubleRef) - - final val byteValue = MethodName("byteValue", Nil, ByteRef) - final val shortValue = MethodName("shortValue", Nil, ShortRef) - final val intValue = MethodName("intValue", Nil, IntRef) - final val longValue = MethodName("longValue", Nil, LongRef) - final val floatValue = MethodName("floatValue", Nil, FloatRef) - final val doubleValue = MethodName("doubleValue", Nil, DoubleRef) - - final val toString_ = MethodName("toString", Nil, ClassRef(BoxedStringClass)) - final val equals_ = MethodName("equals", List(ClassRef(ObjectClass)), BooleanRef) - final val hashCode_ = MethodName("hashCode", Nil, IntRef) - final val compareTo = MethodName("compareTo", List(ClassRef(BoxedLongClass)), IntRef) - final val compareToO = MethodName("compareTo", List(ClassRef(ObjectClass)), IntRef) - - private val OperatorMethods = Set( - UNARY_-, UNARY_~, this.+, this.-, *, /, %, |, &, ^, <<, >>>, >>, - ===, !==, <, <=, >, >=, toInt, toFloat, toDouble) - - private val BoxedLongMethods = Set( + MethodName(name, TwoRTLongRefs, BooleanRef) + + // Instance methods that we need to reach as part of the jl.Long boxing + + private final val byteValue = MethodName("byteValue", Nil, ByteRef) + private final val shortValue = MethodName("shortValue", Nil, ShortRef) + private final val intValue = MethodName("intValue", Nil, IntRef) + private final val longValue = MethodName("longValue", Nil, LongRef) + private final val floatValue = MethodName("floatValue", Nil, FloatRef) + private final val doubleValue = MethodName("doubleValue", Nil, DoubleRef) + + private final val equalsO = MethodName("equals", List(ClassRef(ObjectClass)), BooleanRef) + private final val hashCode_ = MethodName("hashCode", Nil, IntRef) + private final val compareTo = MethodName("compareTo", List(ClassRef(BoxedLongClass)), IntRef) + private final val compareToO = MethodName("compareTo", List(ClassRef(ObjectClass)), IntRef) + + val BoxedLongMethods = Set( byteValue, shortValue, intValue, longValue, floatValue, doubleValue, - equals_, hashCode_, compareTo, compareToO) + equalsO, hashCode_, compareTo, compareToO) - val AllMethods = OperatorMethods ++ BoxedLongMethods + // Operator methods - // Methods used for intrinsics + final val add = binaryOp("add") + final val sub = binaryOp("sub") + final val mul = binaryOp("mul") + final val divide = binaryOp("divide") + final val remainder = binaryOp("remainder") - final val compareToRTLong = MethodName("compareTo", List(RTLongRef), IntRef) - final val divideUnsigned = binaryOp("divideUnsigned") + final val divideUnsigned = binaryOp("divideUnsigned") final val remainderUnsigned = binaryOp("remainderUnsigned") + final val or = binaryOp("or") + final val and = binaryOp("and") + final val xor = binaryOp("xor") + + final val shl = shiftOp("shl") + final val shr = shiftOp("shr") + final val sar = shiftOp("sar") + + final val equals_ = compareOp("equals") + final val notEquals = compareOp("notEquals") + final val lt = compareOp("lt") + final val le = compareOp("le") + final val gt = compareOp("gt") + final val ge = compareOp("ge") + final val ltu = compareOp("ltu") + final val leu = compareOp("leu") + final val gtu = compareOp("gtu") + final val geu = compareOp("geu") + + final val toInt = MethodName("toInt", OneRTLongRef, IntRef) + final val toFloat = MethodName("toFloat", OneRTLongRef, FloatRef) + final val toDouble = MethodName("toDouble", OneRTLongRef, DoubleRef) + final val bitsToDouble = MethodName("bitsToDouble", List(RTLongRef, ObjectRef), DoubleRef) + final val clz = MethodName("clz", OneRTLongRef, IntRef) + + final val fromInt = MethodName("fromInt", List(IntRef), RTLongRef) + final val fromUnsignedInt = MethodName("fromUnsignedInt", List(IntRef), RTLongRef) + final val fromDouble = MethodName("fromDouble", List(DoubleRef), RTLongRef) + final val fromDoubleBits = MethodName("fromDoubleBits", List(DoubleRef, ObjectRef), RTLongRef) + + val OperatorMethods = Set( + add, sub, mul, + divide, remainder, divideUnsigned, remainderUnsigned, + or, and, xor, shl, shr, sar, + equals_, notEquals, lt, le, gt, ge, ltu, leu, gtu, geu, + toInt, toFloat, toDouble, bitsToDouble, clz, + fromInt, fromUnsignedInt, fromDouble, fromDoubleBits + ) + + // Methods used for intrinsics + + final val toString_ = MethodName("toString", OneRTLongRef, ClassRef(BoxedStringClass)) + + final val compare = MethodName("compare", TwoRTLongRefs, IntRef) + + final val abs = MethodName("abs", OneRTLongRef, RTLongRef) + final val multiplyFull = MethodName("multiplyFull", List(IntRef, IntRef), RTLongRef) + val AllIntrinsicMethods = Set( - compareToRTLong, divideUnsigned, remainderUnsigned) + toString_, + compare, + abs, + multiplyFull + ) // Constructors @@ -105,14 +129,6 @@ private[linker] object LongImpl { val AllConstructors = Set( initFromParts) - // Methods on the companion - - final val fromInt = MethodName("fromInt", List(IntRef), RTLongRef) - final val fromDouble = MethodName("fromDouble", List(DoubleRef), RTLongRef) - - val AllModuleMethods = Set( - fromInt, fromDouble) - // Extract the parts to give to the initFromParts constructor def extractParts(value: Long): (Int, Int) = diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/NameGen.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/NameGen.scala index ffb1d57bbe..d6ab128f25 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/NameGen.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/NameGen.scala @@ -63,7 +63,7 @@ private[backend] final class NameGen { cache.put(ObjectClass, "O") cache.put(BoxedStringClass, "T") cache.put(LongImpl.RuntimeLongClass, "RTLong") - cache.put(LongImpl.RuntimeLongModuleClass, "RTLong$") + cache.put(LongImpl.RuntimeLongClass.withSuffix("$"), "RTLong$") cache } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/PolyfillableBuiltin.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/PolyfillableBuiltin.scala index 908d264a9f..43111d8b3c 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/PolyfillableBuiltin.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/PolyfillableBuiltin.scala @@ -21,6 +21,7 @@ private[emitter] object PolyfillableBuiltin { lazy val All: List[PolyfillableBuiltin] = List( ObjectIsBuiltin, ImulBuiltin, + Clz32Builtin, FroundBuiltin, PrivateSymbolBuiltin, GetOwnPropertyDescriptorsBuiltin @@ -36,6 +37,7 @@ private[emitter] object PolyfillableBuiltin { case object ObjectIsBuiltin extends NamespacedBuiltin("Object", "is", VarField.is, ESVersion.ES2015) case object ImulBuiltin extends NamespacedBuiltin("Math", "imul", VarField.imul, ESVersion.ES2015) + case object Clz32Builtin extends NamespacedBuiltin("Math", "clz32", VarField.clz32, ESVersion.ES2015) case object FroundBuiltin extends NamespacedBuiltin("Math", "fround", VarField.fround, ESVersion.ES2015) case object PrivateSymbolBuiltin extends GlobalVarBuiltin("Symbol", VarField.privateJSFieldSymbol, ESVersion.ES2015) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/SJSGen.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/SJSGen.scala index 73ac6c96c9..09514782bb 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/SJSGen.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/SJSGen.scala @@ -241,13 +241,10 @@ private[emitter] final class SJSGen( globalVar(VarField.bC0, CoreVar) } - def genLongModuleApply(methodName: MethodName, args: Tree*)( + def genLongApplyStatic(methodName: MethodName, args: Tree*)( implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge, pos: Position): Tree = { - import TreeDSL._ - genApply( - genLoadModule(LongImpl.RuntimeLongModuleClass), methodName, - args.toList) + Apply(globalVar(VarField.s, (LongImpl.RuntimeLongClass, methodName)), args.toList) } def usesUnderlyingTypedArray(elemTypeRef: NonArrayTypeRef): Boolean = { diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Transients.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Transients.scala index 00301771cc..b1ac1c10a6 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Transients.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/Transients.scala @@ -156,6 +156,22 @@ object Transients { } } + /** Gets the unique instance of `DataView` used for floating point bit manipulation. + * + * When linking for ES 5.1, the resulting value can be `null`. + */ + final case object GetFPBitsDataView extends Transient.Value { + val tpe: Type = AnyType + + def traverse(traverser: Traverser): Unit = () + + def transform(transformer: Transformer)(implicit pos: Position): Tree = + Transient(this) + + def printIR(out: IRTreePrinter): Unit = + out.print("$fpBitsDataView") + } + /** Copies a primitive `Array` into a new appropriate `TypedArray`. * * This node accepts `null` values for `expr`. Its implementation takes care diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/VarField.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/VarField.scala index 44193542b9..9ce22ed2aa 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/VarField.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/VarField.scala @@ -146,6 +146,9 @@ private[emitter] object VarField { /** Long zero. */ final val L0 = mk("$L0") + /** DataView for floating point bit manipulation. */ + final val fpBitsDataView = mk("$fpBitsDataView") + /** Dispatchers. */ final val dp = mk("$dp") @@ -256,23 +259,27 @@ private[emitter] object VarField { // Arithmetic Call Helpers - final val intDiv = mk("$intDiv") - - final val intMod = mk("$intMod") + final val checkIntDivisor = mk("$checkIntDivisor") - final val longToFloat = mk("$longToFloat") + final val checkLongDivisor = mk("$checkLongDivisor") - final val longDiv = mk("$longDiv") + final val longClz = mk("$longClz") - final val longMod = mk("$longMod") + final val longToFloat = mk("$longToFloat") final val doubleToLong = mk("$doubleToLong") final val doubleToInt = mk("$doubleToInt") + final val floatToBits = mk("$floatToBits") + final val floatFromBits = mk("$floatFromBits") + final val doubleToBits = mk("$doubleToBits") + final val doubleFromBits = mk("$doubleFromBits") + // Polyfills final val imul = mk("$imul") + final val clz32 = mk("$clz32") final val fround = mk("$fround") final val privateJSFieldSymbol = mk("$privateJSFieldSymbol") final val getOwnPropertyDescriptors = mk("$getOwnPropertyDescriptors") diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Printers.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Printers.scala index d4fb5f2284..0d9420dc41 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Printers.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Printers.scala @@ -371,7 +371,7 @@ object Printers { case DotSelect(qualifier, item) => qualifier match { - case _:IntLiteral | _:DoubleLiteral => + case _:IntLiteral | _:UintLiteral | _:DoubleLiteral => print("(") print(qualifier) print(")") @@ -552,6 +552,10 @@ object Printers { } printSeparatorIfStat() + case UintLiteral(value) => + print(Integer.toUnsignedString(value)) + printSeparatorIfStat() + case DoubleLiteral(value) => if (value == 0 && 1 / value < 0) { print("(-0)") diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Trees.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Trees.scala index 0ed4501d8f..1482d5e478 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Trees.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/javascript/Trees.scala @@ -416,6 +416,9 @@ object Trees { sealed case class IntLiteral(value: Int)(implicit val pos: Position) extends Literal + sealed case class UintLiteral(value: Int)(implicit val pos: Position) + extends Literal + sealed case class DoubleLiteral(value: Double)(implicit val pos: Position) extends Literal diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala index a86c55909e..0bce083e98 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/FunctionEmitter.scala @@ -634,7 +634,8 @@ private class FunctionEmitter private ( // Transients (only generated by the optimizer) case t: Transient => genTransient(t) - case _:JSSuperConstructorCall | _:LinkTimeProperty | _:NewLambda => + case _:JSSuperConstructorCall | _:LinkTimeProperty | _:LinkTimeIf | + _:NewLambda => throw new AssertionError(s"Invalid tree: $tree") } @@ -1039,6 +1040,13 @@ private class FunctionEmitter private ( * not need to store the receiver in a local at all. * For the case with the args, it does not hurt either way. We could * move it out, but that would make for a less consistent codegen. + * + * Loading the arguments and storing them in locals inside the block + * only works if their type is defaultable. Currently, for instance + * methods, parameter types are always defaultable, so this is fine. + * We may need to revisit this strategy if that invariant changes. + * If we do, it may be better to use different code paths for the + * no-args case and the with-args case. See #5165 for more context. */ val argsLocals = fb.block(watpe.RefType.any) { labelNotOurObject => // Load receiver and arguments and store them in temporary variables @@ -1637,6 +1645,25 @@ private class FunctionEmitter private ( case Throw => fb += wa.ExternConvertAny fb += wa.Throw(genTagID.exception) + + // Floating point bit manipulation + case Float_toBits => + fb += wa.I32ReinterpretF32 + case Float_fromBits => + fb += wa.F32ReinterpretI32 + case Double_toBits => + fb += wa.I64ReinterpretF64 + case Double_fromBits => + fb += wa.F64ReinterpretI64 + + case Int_clz => + fb += wa.I32Clz + case Long_clz => + fb += wa.I64Clz + fb += wa.I32WrapI64 + + case UnsignedIntToLong => + fb += wa.I64ExtendI32U } tree.tpe @@ -1686,33 +1713,34 @@ private class FunctionEmitter private ( case String_+ => genStringConcat(tree) - case Int_/ => - rhs match { - case IntLiteral(rhsValue) => - genDivModByConstant(tree, isDiv = true, rhsValue, wa.I32Const(_), wa.I32Sub, wa.I32DivS) - case _ => - genDivMod(tree, isDiv = true, wa.I32Const(_), wa.I32Eqz, wa.I32Eq, wa.I32Sub, wa.I32DivS) + case Int_/ | Int_% | Int_unsigned_/ | Int_unsigned_% => + val isSignedDiv = op == Int_/ + val mainOp = (op: @switch) match { + case Int_/ => wa.I32DivS + case Int_% => wa.I32RemS + case Int_unsigned_/ => wa.I32DivU + case Int_unsigned_% => wa.I32RemU } - case Int_% => rhs match { case IntLiteral(rhsValue) => - genDivModByConstant(tree, isDiv = false, rhsValue, wa.I32Const(_), wa.I32Sub, wa.I32RemS) + genDivModByConstant(tree, isSignedDiv, rhsValue, wa.I32Const(_), wa.I32Sub, mainOp) case _ => - genDivMod(tree, isDiv = false, wa.I32Const(_), wa.I32Eqz, wa.I32Eq, wa.I32Sub, wa.I32RemS) + genDivMod(tree, isSignedDiv, wa.I32Const(_), wa.I32Eqz, wa.I32Eq, wa.I32Sub, mainOp) } - case Long_/ => - rhs match { - case LongLiteral(rhsValue) => - genDivModByConstant(tree, isDiv = true, rhsValue, wa.I64Const(_), wa.I64Sub, wa.I64DivS) - case _ => - genDivMod(tree, isDiv = true, wa.I64Const(_), wa.I64Eqz, wa.I64Eq, wa.I64Sub, wa.I64DivS) + + case Long_/ | Long_% | Long_unsigned_/ | Long_unsigned_% => + val isSignedDiv = op == Long_/ + val mainOp = (op: @switch) match { + case Long_/ => wa.I64DivS + case Long_% => wa.I64RemS + case Long_unsigned_/ => wa.I64DivU + case Long_unsigned_% => wa.I64RemU } - case Long_% => rhs match { case LongLiteral(rhsValue) => - genDivModByConstant(tree, isDiv = false, rhsValue, wa.I64Const(_), wa.I64Sub, wa.I64RemS) + genDivModByConstant(tree, isSignedDiv, rhsValue, wa.I64Const(_), wa.I64Sub, mainOp) case _ => - genDivMod(tree, isDiv = false, wa.I64Const(_), wa.I64Eqz, wa.I64Eq, wa.I64Sub, wa.I64RemS) + genDivMod(tree, isSignedDiv, wa.I64Const(_), wa.I64Eqz, wa.I64Eq, wa.I64Sub, mainOp) } case Long_<< => @@ -1919,6 +1947,16 @@ private class FunctionEmitter private ( case Double_>= => wa.F64Ge case Class_newArray => wa.Call(genFunctionID.newArray) + + case Int_unsigned_< => wa.I32LtU + case Int_unsigned_<= => wa.I32LeU + case Int_unsigned_> => wa.I32GtU + case Int_unsigned_>= => wa.I32GeU + + case Long_unsigned_< => wa.I64LtU + case Long_unsigned_<= => wa.I64LeU + case Long_unsigned_> => wa.I64GtU + case Long_unsigned_>= => wa.I64GeU } } @@ -2088,7 +2126,7 @@ private class FunctionEmitter private ( } } - private def genDivModByConstant[T](tree: BinaryOp, isDiv: Boolean, + private def genDivModByConstant[T](tree: BinaryOp, isSignedDiv: Boolean, rhsValue: T, const: T => wa.Instr, sub: wa.Instr, mainOp: wa.Instr)( implicit num: Numeric[T]): Type = { /* When we statically know the value of the rhs, we can avoid the @@ -2098,8 +2136,7 @@ private class FunctionEmitter private ( import BinaryOp._ - val BinaryOp(op, lhs, rhs) = tree - assert(op == Int_/ || op == Int_% || op == Long_/ || op == Long_%) + val BinaryOp(_, lhs, rhs) = tree val tpe = tree.tpe @@ -2108,7 +2145,7 @@ private class FunctionEmitter private ( markPosition(tree) genThrowArithmeticException()(tree.pos) NothingType - } else if (isDiv && rhsValue == num.fromInt(-1)) { + } else if (isSignedDiv && rhsValue == num.fromInt(-1)) { /* MinValue / -1 overflows; it traps in Wasm but we need to wrap. * We rewrite as `0 - lhs` so that we do not need any test. */ @@ -2128,7 +2165,7 @@ private class FunctionEmitter private ( } } - private def genDivMod[T](tree: BinaryOp, isDiv: Boolean, const: T => wa.Instr, + private def genDivMod[T](tree: BinaryOp, isSignedDiv: Boolean, const: T => wa.Instr, eqz: wa.Instr, eqInstr: wa.Instr, sub: wa.Instr, mainOp: wa.Instr)( implicit num: Numeric[T]): Type = { /* Here we perform the same steps as in the static case, but using @@ -2137,8 +2174,7 @@ private class FunctionEmitter private ( import BinaryOp._ - val BinaryOp(op, lhs, rhs) = tree - assert(op == Int_/ || op == Int_% || op == Long_/ || op == Long_%) + val BinaryOp(_, lhs, rhs) = tree val tpe = tree.tpe.asInstanceOf[PrimType] val wasmType = transformPrimType(tpe) @@ -2156,7 +2192,7 @@ private class FunctionEmitter private ( fb.ifThen() { genThrowArithmeticException()(tree.pos) } - if (isDiv) { + if (isSignedDiv) { // Handle the MinValue / -1 corner case fb += wa.LocalGet(rhsLocal) fb += const(num.fromInt(-1)) @@ -2173,7 +2209,7 @@ private class FunctionEmitter private ( fb += mainOp } } else { - // lhs % rhs + // lhs mainOp rhs fb += wa.LocalGet(lhsLocal) fb += wa.LocalGet(rhsLocal) fb += mainOp @@ -3622,6 +3658,11 @@ private class FunctionEmitter private ( * we cannot use the stack for the `try_table` itself: each label has a * dedicated local for its result if it comes from such a crossing `return`. * + * Those locals must have defaultable types, because they are read outside of + * the block where they are first ininitialized. If their natural type is not + * defaultable, we make it defaultable, and cast away nullability when we + * read them back. See #5165. + * * Two more complications: * * - If the `finally` block itself contains another `try..finally`, they may @@ -3849,7 +3890,7 @@ private class FunctionEmitter private ( _crossInfo.getOrElse { val destinationTag = allocateDestinationTag() val resultTypes = transformResultType(expectedType) - val resultLocals = resultTypes.map(addSyntheticLocal(_)) + val resultLocals = resultTypes.map(tpe => addSyntheticLocal(tpe.toDefaultableType)) val crossLabel = fb.genLabel() val info = CrossInfo(destinationTag, resultLocals, crossLabel) _crossInfo = Some(info) @@ -3940,8 +3981,11 @@ private class FunctionEmitter private ( // Add the `br`, `end` and `local.get` at the current position, as usual fb += wa.Br(entry.regularWasmLabel) fb += wa.End - for (local <- resultLocals) + for ((local, origType) <- resultLocals.zip(ty)) { fb += wa.LocalGet(local) + if (!origType.isDefaultable) + fb += wa.RefAsNonNull + } } fb += wa.End @@ -3958,7 +4002,7 @@ private class FunctionEmitter private ( val entry = new TryFinallyEntry(currentUnwindingStackDepth) val resultType = transformResultType(expectedType) - val resultLocals = resultType.map(addSyntheticLocal(_)) + val resultLocals = resultType.map(tpe => addSyntheticLocal(tpe.toDefaultableType)) markPosition(tree) @@ -4073,8 +4117,11 @@ private class FunctionEmitter private ( } // end block $done // reload the result onto the stack - for (resultLocal <- resultLocals) + for ((resultLocal, origType) <- resultLocals.zip(resultType)) { fb += wa.LocalGet(resultLocal) + if (!origType.isDefaultable) + fb += wa.RefAsNonNull + } if (expectedType == NothingType) fb += wa.Unreachable diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/WasmTransients.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/WasmTransients.scala index bf41838a98..3f6a26957d 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/WasmTransients.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/wasmemitter/WasmTransients.scala @@ -47,11 +47,9 @@ object WasmTransients { Transient(WasmUnaryOp(op, transformer.transform(lhs))) def wasmInstr: wa.SimpleInstr = (op: @switch) match { - case I32Clz => wa.I32Clz case I32Ctz => wa.I32Ctz case I32Popcnt => wa.I32Popcnt - case I64Clz => wa.I64Clz case I64Ctz => wa.I64Ctz case I64Popcnt => wa.I64Popcnt @@ -63,10 +61,7 @@ object WasmTransients { case F64Nearest => wa.F64Nearest case F64Sqrt => wa.F64Sqrt - case I32ReinterpretF32 => wa.I32ReinterpretF32 - case I64ReinterpretF64 => wa.I64ReinterpretF64 - case F32ReinterpretI32 => wa.F32ReinterpretI32 - case F64ReinterpretI64 => wa.F64ReinterpretI64 + case F64ConvertI32U => wa.F64ConvertI32U } def printIR(out: IRTreePrinter): Unit = { @@ -80,38 +75,33 @@ object WasmTransients { /** Codes are raw Ints to be able to write switch matches on them. */ type Code = Int - final val I32Clz = 1 - final val I32Ctz = 2 - final val I32Popcnt = 3 + final val I32Ctz = 1 + final val I32Popcnt = 2 - final val I64Clz = 4 - final val I64Ctz = 5 - final val I64Popcnt = 6 + final val I64Ctz = 3 + final val I64Popcnt = 4 - final val F32Abs = 7 + final val F32Abs = 5 - final val F64Abs = 8 - final val F64Ceil = 9 - final val F64Floor = 10 - final val F64Nearest = 11 - final val F64Sqrt = 12 + final val F64Abs = 6 + final val F64Ceil = 7 + final val F64Floor = 8 + final val F64Nearest = 9 + final val F64Sqrt = 10 - final val I32ReinterpretF32 = 13 - final val I64ReinterpretF64 = 14 - final val F32ReinterpretI32 = 15 - final val F64ReinterpretI64 = 16 + final val F64ConvertI32U = 11 def resultTypeOf(op: Code): Type = (op: @switch) match { - case I32Clz | I32Ctz | I32Popcnt | I32ReinterpretF32 => + case I32Ctz | I32Popcnt => IntType - case I64Clz | I64Ctz | I64Popcnt | I64ReinterpretF64 => + case I64Ctz | I64Popcnt => LongType - case F32Abs | F32ReinterpretI32 => + case F32Abs => FloatType - case F64Abs | F64Ceil | F64Floor | F64Nearest | F64Sqrt | F64ReinterpretI64 => + case F64Abs | F64Ceil | F64Floor | F64Nearest | F64Sqrt | F64ConvertI32U => DoubleType } } @@ -147,13 +137,9 @@ object WasmTransients { def wasmInstr: wa.SimpleInstr = (op: @switch) match { case I32GtU => wa.I32GtU - case I32DivU => wa.I32DivU - case I32RemU => wa.I32RemU case I32Rotl => wa.I32Rotl case I32Rotr => wa.I32Rotr - case I64DivU => wa.I64DivU - case I64RemU => wa.I64RemU case I64Rotl => wa.I64Rotl case I64Rotr => wa.I64Rotr @@ -177,13 +163,9 @@ object WasmTransients { final val I32GtU = 1 - final val I32DivU = 2 - final val I32RemU = 3 final val I32Rotl = 4 final val I32Rotr = 5 - final val I64DivU = 6 - final val I64RemU = 7 final val I64Rotl = 8 final val I64Rotr = 9 @@ -197,10 +179,10 @@ object WasmTransients { case I32GtU => BooleanType - case I32DivU | I32RemU | I32Rotl | I32Rotr => + case I32Rotl | I32Rotr => IntType - case I64DivU | I64RemU | I64Rotl | I64Rotr => + case I64Rotl | I64Rotr => LongType case F32Min | F32Max => diff --git a/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/Types.scala b/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/Types.scala index 58f07eba99..62b3be1849 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/Types.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/backend/webassembly/Types.scala @@ -33,7 +33,24 @@ object Types { * typing" point of view. It is also the kind of type we manipulate the most * across the backend, so it also makes sense for it to be the "default". */ - sealed abstract class Type extends StorageType + sealed abstract class Type extends StorageType { + /** Returns true if and only if this type is defaultable. */ + final def isDefaultable: Boolean = this match { + case RefType(nullable, _) => nullable + case _ => true + } + + /** Returns a defaultable supertype of this type. + * + * If this type is already defaultable, return `this`. Otherwise, this + * type must be a non-nullable reference type, and this method returns the + * nullable variant. + */ + final def toDefaultableType: Type = this match { + case RefType(false, heapType) => RefType.nullable(heapType) + case _ => this + } + } /** Convenience superclass for `Type`s that are encoded with a simple opcode. */ sealed abstract class SimpleType(val textName: String, val binaryCode: Byte) extends Type diff --git a/linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala b/linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala index a1c9f6363d..2d1437ee5f 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala @@ -761,6 +761,13 @@ private final class ClassDefChecker(classDef: ClassDef, checkTree(thenp, env) checkTree(elsep, env) + case LinkTimeIf(cond, thenp, elsep) => + if (!featureSet.supports(FeatureSet.LinkTimeNodes)) + reportError(i"Illegal link-time if after desugaring") + checkLinkTimeTree(cond, BooleanType) + checkTree(thenp, env) + checkTree(elsep, env) + case While(cond, body) => checkTree(cond, env) checkTree(body, env) @@ -923,9 +930,16 @@ private final class ClassDefChecker(classDef: ClassDef, } case LinkTimeProperty(name) => - if (!featureSet.supports(FeatureSet.LinkTimeProperty)) + if (!featureSet.supports(FeatureSet.LinkTimeNodes)) reportError(i"Illegal link-time property '$name' after desugaring") + tree.tpe match { + case BooleanType | IntType | StringType => + () // ok + case tpe => + reportError(i"$tpe is not a valid type for LinkTimeProperty") + } + // JavaScript expressions case JSNew(ctor, args) => @@ -1091,6 +1105,60 @@ private final class ClassDefChecker(classDef: ClassDef, } } + private def checkLinkTimeTree(tree: Tree, expectedType: PrimType): Unit = { + implicit val ctx = ErrorContext(tree) + + /* For link-time trees, we need to check the types. Having a well-typed + * condition is required for `LinkTimeIf` to be resolved, and that happens + * before IR checking. Fortunately, only trivial primitive types can appear + * in link-time trees, and it is therefore possible to check them now. + */ + if (tree.tpe != expectedType) + reportError(i"$expectedType expected but ${tree.tpe} found in link-time tree") + + /* Unlike the evaluation algorithm, at this time we allow LinkTimeProperty's + * that are not actually available. We only check that their declared type + * matches the expected type. If it does not exist or does not have the + * type it was declared with, that constitutes a *linking error*, but it + * does not make the ClassDef invalid. + */ + + tree match { + case _:IntLiteral | _:BooleanLiteral | _:StringLiteral | _:LinkTimeProperty => + () // ok + + case UnaryOp(op, lhs) => + import UnaryOp._ + op match { + case Boolean_! => + checkLinkTimeTree(lhs, BooleanType) + case _ => + reportError(i"illegal unary op $op in link-time tree") + } + + case BinaryOp(op, lhs, rhs) => + import BinaryOp._ + op match { + case Boolean_== | Boolean_!= | Boolean_| | Boolean_& => + checkLinkTimeTree(lhs, BooleanType) + checkLinkTimeTree(rhs, BooleanType) + case Int_== | Int_!= | Int_< | Int_<= | Int_> | Int_>= => + checkLinkTimeTree(lhs, IntType) + checkLinkTimeTree(rhs, IntType) + case _ => + reportError(i"illegal binary op $op in link-time tree") + } + + case LinkTimeIf(cond, thenp, elsep) => + checkLinkTimeTree(cond, BooleanType) + checkLinkTimeTree(thenp, expectedType) + checkLinkTimeTree(elsep, expectedType) + + case _ => + reportError(i"illegal tree of class ${tree.getClass().getName()} in link-time tree") + } + } + private def checkArrayType(tpe: ArrayType)( implicit ctx: ErrorContext): Unit = { checkArrayTypeRef(tpe.arrayTypeRef) diff --git a/linker/shared/src/main/scala/org/scalajs/linker/checker/FeatureSet.scala b/linker/shared/src/main/scala/org/scalajs/linker/checker/FeatureSet.scala index 33cbeaa135..94aabffff1 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/checker/FeatureSet.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/checker/FeatureSet.scala @@ -36,8 +36,8 @@ private[checker] object FeatureSet { // Individual features - /** The `LinkTimeProperty` IR node. */ - val LinkTimeProperty = new FeatureSet(1 << 0) + /** Link-time IR nodes: `LinkTimeProperty` and `LinkTimeIf`. */ + val LinkTimeNodes = new FeatureSet(1 << 0) /** The `NewLambda` IR node. */ val NewLambda = new FeatureSet(1 << 1) @@ -84,7 +84,7 @@ private[checker] object FeatureSet { /** Features that must be desugared away. */ private val NeedsDesugaring = - LinkTimeProperty | NewLambda + LinkTimeNodes | NewLambda /** IR that is only the result of desugaring (currently empty). */ private val Desugared = diff --git a/linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala b/linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala index b66dfeea1f..c25ae55672 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala @@ -24,13 +24,13 @@ import org.scalajs.ir.WellKnownNames._ import org.scalajs.logging._ -import org.scalajs.linker.frontend.LinkingUnit +import org.scalajs.linker.frontend.{LinkingUnit, LinkTimeEvaluator, LinkTimeProperties} import org.scalajs.linker.standard.LinkedClass import org.scalajs.linker.checker.ErrorReporter._ /** Checker for the validity of the IR. */ -private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter, - previousPhase: CheckingPhase) { +private final class IRChecker(linkTimeProperties: LinkTimeProperties, + unit: LinkingUnit, reporter: ErrorReporter, previousPhase: CheckingPhase) { import IRChecker._ import reporter.reportError @@ -315,6 +315,26 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter, typecheckExpect(thenp, env, tpe) typecheckExpect(elsep, env, tpe) + case LinkTimeIf(cond, thenp, elsep) if featureSet.supports(FeatureSet.LinkTimeNodes) => + /* The `cond` is entirely checked in ClassDefChecker. + * + * We must only check the branch that is actually selected. + * We *cannot* check the dropped branch, because it may refer to types + * that are dropped by the reachability analysis (which is the whole + * point of LinkTimeIf). It is OK to have ill-typed IR in the dropped + * branch, because it is guaranteed to disappear during desugaring, + * before types are relied upon for any optimization or emission. + */ + LinkTimeEvaluator.tryEvalLinkTimeBooleanExpr(linkTimeProperties, cond) match { + case Some(value) => + if (value) + typecheckExpect(thenp, env, tree.tpe) + else + typecheckExpect(elsep, env, tree.tpe) + case None => + reportError(i"could not evaluate link-time condition: $cond") + } + case While(cond, body) => typecheckExpect(cond, env, BooleanType) typecheck(body, env) @@ -518,13 +538,15 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter, ByteType case ShortToInt => ShortType - case IntToLong | IntToDouble | IntToChar | IntToByte | IntToShort => + case IntToLong | IntToDouble | IntToChar | IntToByte | IntToShort | + Float_fromBits | Int_clz | UnsignedIntToLong => IntType - case LongToInt | LongToDouble | LongToFloat => + case LongToInt | LongToDouble | LongToFloat | Double_fromBits | + Long_clz => LongType - case FloatToDouble => + case FloatToDouble | Float_toBits => FloatType - case DoubleToInt | DoubleToFloat | DoubleToLong => + case DoubleToInt | DoubleToFloat | DoubleToLong | Double_toBits => DoubleType case String_length => StringType @@ -551,11 +573,15 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter, BooleanType case Int_+ | Int_- | Int_* | Int_/ | Int_% | Int_| | Int_& | Int_^ | Int_<< | Int_>>> | Int_>> | - Int_== | Int_!= | Int_< | Int_<= | Int_> | Int_>= => + Int_== | Int_!= | Int_< | Int_<= | Int_> | Int_>= | + Int_unsigned_/ | Int_unsigned_% | + Int_unsigned_< | Int_unsigned_<= | Int_unsigned_> | Int_unsigned_>= => IntType case Long_+ | Long_- | Long_* | Long_/ | Long_% | Long_| | Long_& | Long_^ | Long_<< | Long_>>> | Long_>> | - Long_== | Long_!= | Long_< | Long_<= | Long_> | Long_>= => + Long_== | Long_!= | Long_< | Long_<= | Long_> | Long_>= | + Long_unsigned_/ | Long_unsigned_% | + Long_unsigned_< | Long_unsigned_<= | Long_unsigned_> | Long_unsigned_>= => LongType case Float_+ | Float_- | Float_* | Float_/ | Float_% => FloatType @@ -609,7 +635,7 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter, typecheckAny(expr, env) checkIsAsInstanceTargetType(tpe) - case LinkTimeProperty(name) if featureSet.supports(FeatureSet.LinkTimeProperty) => + case LinkTimeProperty(name) if featureSet.supports(FeatureSet.LinkTimeNodes) => // JavaScript expressions @@ -793,7 +819,7 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter, } case _:RecordSelect | _:RecordValue | _:Transient | - _:JSSuperConstructorCall | _:LinkTimeProperty | + _:JSSuperConstructorCall | _:LinkTimeProperty | _:LinkTimeIf | _:ApplyTypedClosure | _:NewLambda => reportError("invalid tree") } @@ -963,9 +989,10 @@ object IRChecker { * * @return Count of IR checking errors (0 in case of success) */ - def check(unit: LinkingUnit, logger: Logger, previousPhase: CheckingPhase): Int = { + def check(linkTimeProperties: LinkTimeProperties, unit: LinkingUnit, + logger: Logger, previousPhase: CheckingPhase): Int = { val reporter = new LoggerErrorReporter(logger) - new IRChecker(unit, reporter, previousPhase).check() + new IRChecker(linkTimeProperties, unit, reporter, previousPhase).check() reporter.errorCount } } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala index 62d05ff87e..b88ea4fd55 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala @@ -35,6 +35,8 @@ import Analysis._ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { import BaseLinker._ + private val linkTimeProperties = LinkTimeProperties.fromCoreSpec(config.coreSpec) + private val irLoader = new FileIRLoader private val analyzer = { val checkIRFor = if (checkIR) Some(CheckingPhase.Compiler) else None @@ -58,7 +60,8 @@ final class BaseLinker(config: CommonPhaseConfig, checkIR: Boolean) { } yield { if (checkIR) { logger.time("Linker: Check IR") { - val errorCount = IRChecker.check(linkResult, logger, CheckingPhase.BaseLinker) + val errorCount = IRChecker.check(linkTimeProperties, linkResult, + logger, CheckingPhase.BaseLinker) if (errorCount != 0) { throw new LinkingException( s"There were $errorCount IR checking errors.") diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Desugarer.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Desugarer.scala index 44e2f66d09..b97423440d 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Desugarer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Desugarer.scala @@ -28,7 +28,9 @@ import org.scalajs.ir.{Position, Version} final class Desugarer(config: CommonPhaseConfig, checkIR: Boolean) { import Desugarer._ - private val desugarTransformer = new DesugarTransformer(config.coreSpec) + private val linkTimeProperties = LinkTimeProperties.fromCoreSpec(config.coreSpec) + + private val desugarTransformer = new DesugarTransformer(linkTimeProperties) def desugar(unit: LinkingUnit, logger: Logger): LinkingUnit = { val result = logger.time("Desugarer: Desugar") { @@ -41,7 +43,8 @@ final class Desugarer(config: CommonPhaseConfig, checkIR: Boolean) { if (checkIR) { logger.time("Desugarer: Check IR") { - val errorCount = IRChecker.check(result, logger, CheckingPhase.Desugarer) + val errorCount = IRChecker.check(linkTimeProperties, result, logger, + CheckingPhase.Desugarer) if (errorCount != 0) { throw new AssertionError( s"There were $errorCount IR checking errors after desugaring (this is a Scala.js bug)") @@ -118,7 +121,7 @@ final class Desugarer(config: CommonPhaseConfig, checkIR: Boolean) { private[linker] object Desugarer { - private final class DesugarTransformer(coreSpec: CoreSpec) + private final class DesugarTransformer(linkTimeProperties: LinkTimeProperties) extends ClassTransformer { /* Cache the names generated for lambda classes because computing their @@ -135,8 +138,32 @@ private[linker] object Desugarer { override def transform(tree: Tree): Tree = { tree match { - case prop: LinkTimeProperty => - coreSpec.linkTimeProperties.transformLinkTimeProperty(prop) + case LinkTimeProperty(name) => + implicit val pos = tree.pos + val value = linkTimeProperties.get(name).getOrElse { + throw new IllegalArgumentException( + s"link time property not found: '$name' of type ${tree.tpe}") + } + value match { + case LinkTimeProperties.LinkTimeBoolean(value) => BooleanLiteral(value) + case LinkTimeProperties.LinkTimeInt(value) => IntLiteral(value) + case LinkTimeProperties.LinkTimeString(value) => StringLiteral(value) + } + + case LinkTimeIf(cond, thenp, elsep) => + LinkTimeEvaluator.tryEvalLinkTimeBooleanExpr(linkTimeProperties, cond) match { + case Some(result) => + if (result) + transform(thenp) + else + transform(elsep) + case None => + throw new AssertionError( + s"Invalid link-time condition should not have passed the reachability analysis:\n" + + s"${tree.show}\n" + + s"at ${tree.pos}.\n" + + "Consider running the linker with `withCheckIR(true)` before submitting a bug report.") + } case NewLambda(descriptor, fun) => implicit val pos = tree.pos diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeEvaluator.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeEvaluator.scala new file mode 100644 index 0000000000..3ab224306f --- /dev/null +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeEvaluator.scala @@ -0,0 +1,129 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.linker.frontend + +import org.scalajs.ir.Position +import org.scalajs.ir.Trees._ +import org.scalajs.ir.Trees.LinkTimeProperty._ + +import org.scalajs.linker.frontend.LinkTimeProperties._ +import org.scalajs.linker.interface.LinkingException + +private[linker] object LinkTimeEvaluator { + + /** Try and evaluate a link-time expression tree as a boolean value. + * + * This method assumes that the given `tree` is valid according to the + * `ClassDefChecker` and that its `tpe` is `BooleanType`. + * If that is not the case, it may throw or return an arbitrary result. + * + * Returns `None` if any subtree that needed evaluation was a missing + * `LinkTimeProperty` or one with the wrong type (i.e., one that would not + * pass the reachability analysis). + */ + def tryEvalLinkTimeBooleanExpr( + linkTimeProperties: LinkTimeProperties, tree: Tree): Option[Boolean] = { + implicit val pos = tree.pos + + tryEvalLinkTimeExpr(linkTimeProperties, tree).map(booleanValue(_)) + } + + /** Try and evaluate a link-time expression tree. + * + * This method assumes that the given `tree` is valid according to the + * `ClassDefChecker`. + * If that is not the case, it may throw or return an arbitrary result. + * + * Returns `None` if any subtree that needed evaluation was a missing + * `LinkTimeProperty` or one with the wrong type (i.e., one that would not + * pass the reachability analysis). + */ + private def tryEvalLinkTimeExpr( + props: LinkTimeProperties, tree: Tree): Option[LinkTimeValue] = { + implicit val pos = tree.pos + + tree match { + case IntLiteral(value) => Some(LinkTimeInt(value)) + case BooleanLiteral(value) => Some(LinkTimeBoolean(value)) + case StringLiteral(value) => Some(LinkTimeString(value)) + + case LinkTimeProperty(name) => + props.get(name).filter(_.tpe == tree.tpe) + + case UnaryOp(op, lhs) => + import UnaryOp._ + for { + l <- tryEvalLinkTimeExpr(props, lhs) + } yield { + op match { + case Boolean_! => LinkTimeBoolean(!booleanValue(l)) + + case _ => + throw new LinkingException( + s"Illegal unary op $op in link-time tree at $pos") + } + } + + case BinaryOp(op, lhs, rhs) => + import BinaryOp._ + for { + l <- tryEvalLinkTimeExpr(props, lhs) + r <- tryEvalLinkTimeExpr(props, rhs) + } yield { + op match { + case Boolean_== => LinkTimeBoolean(booleanValue(l) == booleanValue(r)) + case Boolean_!= => LinkTimeBoolean(booleanValue(l) != booleanValue(r)) + case Boolean_| => LinkTimeBoolean(booleanValue(l) | booleanValue(r)) + case Boolean_& => LinkTimeBoolean(booleanValue(l) & booleanValue(r)) + + case Int_== => LinkTimeBoolean(intValue(l) == intValue(r)) + case Int_!= => LinkTimeBoolean(intValue(l) != intValue(r)) + case Int_< => LinkTimeBoolean(intValue(l) < intValue(r)) + case Int_<= => LinkTimeBoolean(intValue(l) <= intValue(r)) + case Int_> => LinkTimeBoolean(intValue(l) > intValue(r)) + case Int_>= => LinkTimeBoolean(intValue(l) >= intValue(r)) + + case _ => + throw new LinkingException( + s"Illegal binary op $op in link-time tree at $pos") + } + } + + case LinkTimeIf(cond, thenp, elsep) => + tryEvalLinkTimeExpr(props, cond).flatMap { c => + if (booleanValue(c)) + tryEvalLinkTimeExpr(props, thenp) + else + tryEvalLinkTimeExpr(props, elsep) + } + + case _ => + throw new LinkingException( + s"Illegal tree of class ${tree.getClass().getName()} in link-time tree at $pos") + } + } + + private def intValue(value: LinkTimeValue)(implicit pos: Position): Int = value match { + case LinkTimeInt(value) => + value + case _ => + throw new LinkingException(s"Value of type int expected but got $value at $pos") + } + + private def booleanValue(value: LinkTimeValue)(implicit pos: Position): Boolean = value match { + case LinkTimeBoolean(value) => + value + case _ => + throw new LinkingException(s"Value of type boolean expected but got $value at $pos") + } +} diff --git a/linker/shared/src/main/scala/org/scalajs/linker/standard/LinkTimeProperties.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeProperties.scala similarity index 50% rename from linker/shared/src/main/scala/org/scalajs/linker/standard/LinkTimeProperties.scala rename to linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeProperties.scala index 875196c736..d2c12c67d0 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/standard/LinkTimeProperties.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/LinkTimeProperties.scala @@ -10,15 +10,16 @@ * additional information regarding copyright ownership. */ -package org.scalajs.linker.standard +package org.scalajs.linker.frontend -import org.scalajs.ir.{Types => jstpe, Trees => js} import org.scalajs.ir.Trees.LinkTimeProperty._ +import org.scalajs.ir.Types._ import org.scalajs.ir.ScalaJSVersions -import org.scalajs.ir.Position.NoPosition -import org.scalajs.linker.interface.{Semantics, ESFeatures} -private[linker] final class LinkTimeProperties ( +import org.scalajs.linker.interface.{ESVersion => _, _} +import org.scalajs.linker.standard.CoreSpec + +final class LinkTimeProperties private ( semantics: Semantics, esFeatures: ESFeatures, targetIsWebAssembly: Boolean @@ -38,31 +39,24 @@ private[linker] final class LinkTimeProperties ( LinkTimeString(ScalaJSVersions.current) ) - def validate(name: String, tpe: jstpe.Type): Boolean = { - linkTimeProperties.get(name).exists { - case _: LinkTimeBoolean => tpe == jstpe.BooleanType - case _: LinkTimeInt => tpe == jstpe.IntType - case _: LinkTimeString => tpe == jstpe.StringType - } - } + def get(name: String): Option[LinkTimeValue] = + linkTimeProperties.get(name) +} + +object LinkTimeProperties { + sealed abstract class LinkTimeValue(val tpe: Type) - def transformLinkTimeProperty(prop: js.LinkTimeProperty): js.Literal = { - val value = linkTimeProperties.getOrElse(prop.name, - throw new IllegalArgumentException(s"link time property not found: '${prop.name}' of type ${prop.tpe}")) - value match { - case LinkTimeBoolean(value) => - js.BooleanLiteral(value)(prop.pos) - case LinkTimeInt(value) => - js.IntLiteral(value)(prop.pos) - case LinkTimeString(value) => - js.StringLiteral(value)(prop.pos) - } + final case class LinkTimeInt(value: Int) extends LinkTimeValue(IntType) + + final case class LinkTimeBoolean(value: Boolean) extends LinkTimeValue(BooleanType) + + final case class LinkTimeString(value: String) extends LinkTimeValue(StringType) { + // Being extra careful + require(value != null, "LinkTimeString requires a non-null value.") } -} -private[linker] object LinkTimeProperties { - sealed abstract class LinkTimeValue - final case class LinkTimeInt(value: Int) extends LinkTimeValue - final case class LinkTimeBoolean(value: Boolean) extends LinkTimeValue - final case class LinkTimeString(value: String) extends LinkTimeValue + def fromCoreSpec(coreSpec: CoreSpec): LinkTimeProperties = { + new LinkTimeProperties(coreSpec.semantics, coreSpec.esFeatures, + coreSpec.targetIsWebAssembly) + } } diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala index 0f074adf55..4f778351ba 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/Refiner.scala @@ -30,6 +30,8 @@ import org.scalajs.linker.analyzer._ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { import Refiner._ + private val linkTimeProperties = LinkTimeProperties.fromCoreSpec(config.coreSpec) + private val irLoader = new ClassDefIRLoader private val analyzer = { val checkIRFor = if (checkIR) Some(CheckingPhase.Optimizer) else None @@ -81,7 +83,8 @@ final class Refiner(config: CommonPhaseConfig, checkIR: Boolean) { if (shouldRunIRChecker) { logger.time("Refiner: Check IR") { - val errorCount = IRChecker.check(result, logger, CheckingPhase.Optimizer) + val errorCount = IRChecker.check(linkTimeProperties, result, logger, + CheckingPhase.Optimizer) if (errorCount != 0) { throw new AssertionError( s"There were $errorCount IR checking errors after optimization (this is a Scala.js bug)") diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/IncOptimizer.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/IncOptimizer.scala index 38bdb804c3..f9cd1e2c00 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/IncOptimizer.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/IncOptimizer.scala @@ -75,7 +75,7 @@ final class IncOptimizer private[optimizer] (config: CommonPhaseConfig, collOps: multiple( cond(!targetIsWebAssembly && !esFeatures.allowBigIntsForLongs) { // Required by the intrinsics manipulating Longs - callMethods(LongImpl.RuntimeLongClass, LongImpl.AllIntrinsicMethods.toList) + callStaticMethods(LongImpl.RuntimeLongClass, LongImpl.AllIntrinsicMethods.toList) }, cond(targetIsWebAssembly) { // Required by the intrinsic CharacterCodePointToString diff --git a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala index 51cebcdcca..194e5cbca9 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/frontend/optimizer/OptimizerCore.scala @@ -271,6 +271,8 @@ private[optimizer] abstract class OptimizerCore( (lhs, rhs) match { case (LongType, ClassType(LongImpl.RuntimeLongClass, _)) => true + case (ClassType(LongImpl.RuntimeLongClass, false), LongType) => + true case (ClassType(BoxedLongClass, lhsNullable), ClassType(LongImpl.RuntimeLongClass, rhsNullable)) => rhsNullable || !lhsNullable @@ -644,7 +646,40 @@ private[optimizer] abstract class OptimizerCore( JSUnaryOp(op, transformExpr(lhs)) case JSBinaryOp(op, lhs, rhs) => - JSBinaryOp(op, transformExpr(lhs), transformExpr(rhs)) + val newTree = JSBinaryOp(op, transformExpr(lhs), transformExpr(rhs)) + + // Introduce casts for some idioms that are guaranteed to return certain types + + // Is `arg` guaranteed to evaluate to a JS `number` (and hence, not a `bigint`)? + def isJSNumber(arg: Tree): Boolean = arg.tpe match { + case IntType | DoubleType | ByteType | ShortType | FloatType => true + case _ => false + } + + newTree match { + /* Unless it throws, `x | y` returns either a signed 32-bit integer + * (an `Int`) or a bigint. + * + * The only case in which it returns a bigint is when both arguments + * are (convertible to) bigint's. Custom objects can be converted to + * bigint's if their `valueOf()` method returns a bigint. + * + * Primitive numbers cannot be implicitly converted to bigint's. + * `x | y` throws if one side is a number and the other is (converted + * to) a bigint. Therefore, if at least one of the arguments is known + * to be a primitive number, we know that `x | y` will return a + * signed 32-bit integer (or throw). + */ + case JSBinaryOp(JSBinaryOp.|, x, y) if isJSNumber(x) || isJSNumber(y) => + makeCast(newTree, IntType) + + // >>> always returns a positive number in the unsigned 32-bit range (it rejects bigints) + case JSBinaryOp(JSBinaryOp.>>>, _, _) => + makeCast(newTree, DoubleType) + + case _ => + newTree + } case JSArrayConstr(items) => JSArrayConstr(transformExprsOrSpreads(items)) @@ -689,7 +724,8 @@ private[optimizer] abstract class OptimizerCore( _:JSGlobalRef | _:JSTypeOfGlobalRef | _:Literal => tree - case _:LinkTimeProperty | _:NewLambda | _:RecordSelect | _:Transient => + case _:LinkTimeProperty | _:LinkTimeIf | _:NewLambda | _:RecordSelect | + _:Transient => throw new IllegalArgumentException( s"Invalid tree in transform of class ${tree.getClass.getName}: $tree") } @@ -1504,14 +1540,14 @@ private[optimizer] abstract class OptimizerCore( BinaryOp(op, finishTransformExpr(lhs), finishTransformExpr(rhs)) (op: @switch) match { - case Int_/ | Int_% => + case Int_/ | Int_% | Int_unsigned_/ | Int_unsigned_% => rhs match { case PreTransLit(IntLiteral(r)) if r != 0 => finishNoSideEffects case _ => Block(newLhs, BinaryOp(op, IntLiteral(0), finishTransformExpr(rhs))) } - case Long_/ | Long_% => + case Long_/ | Long_% | Long_unsigned_/ | Long_unsigned_% => rhs match { case PreTransLit(LongLiteral(r)) if r != 0L => finishNoSideEffects @@ -1648,6 +1684,9 @@ private[optimizer] abstract class OptimizerCore( else Block(exprSideEffects, Transient(Cast(Null(), tpe))) + case Transient(GetFPBitsDataView) => + Skip()(stat.pos) + case _ => stat } @@ -1834,8 +1873,9 @@ private[optimizer] abstract class OptimizerCore( case NotFoundPureSoFar => rec(rhs).mapOrKeepGoingIf(BinaryOp(op, lhs, _)) { (op: @switch) match { - case Int_/ | Int_% | Long_/ | Long_% | String_+ | String_charAt | - Class_cast | Class_newArray => + case Int_/ | Int_% | Int_unsigned_/ | Int_unsigned_% | + Long_/ | Long_% | Long_unsigned_/ | Long_unsigned_% | + String_+ | String_charAt | Class_cast | Class_newArray => false case _ => true @@ -1900,6 +1940,8 @@ private[optimizer] abstract class OptimizerCore( case _: Literal => NotFoundPureSoFar + case Transient(GetFPBitsDataView) => + NotFoundPureSoFar case Closure(flags, captureParams, params, restParam, resultType, body, captureValues) => recs(captureValues).mapOrKeepGoing { newCaptureValues => @@ -2213,18 +2255,28 @@ private[optimizer] abstract class OptimizerCore( usePreTransform: Boolean)( cont: PreTransCont)( implicit scope: Scope): TailRec[Tree] = { - val ApplyStatic(flags, className, - methodIdent @ MethodIdent(methodName), args) = tree + val ApplyStatic(flags, className, methodIdent, args) = tree implicit val pos = tree.pos - val target = staticCall(className, MemberNamespace.forStaticCall(flags), - methodName) pretransformExprs(args) { targs => - pretransformSingleDispatch(flags, target, None, targs, isStat, usePreTransform)(cont) { - val newArgs = targs.map(finishTransformExpr) - cont(PreTransTree(ApplyStatic(flags, className, methodIdent, - newArgs)(tree.tpe))) - } + pretransformApplyStatic(flags, className, methodIdent, targs, tree.tpe, + isStat, usePreTransform)( + cont) + } + } + + private def pretransformApplyStatic(flags: ApplyFlags, className: ClassName, + methodIdent: MethodIdent, targs: List[PreTransform], resultType: Type, + isStat: Boolean, usePreTransform: Boolean)( + cont: PreTransCont)( + implicit scope: Scope, pos: Position): TailRec[Tree] = { + + val target = staticCall(className, MemberNamespace.forStaticCall(flags), + methodIdent.name) + pretransformSingleDispatch(flags, target, None, targs, isStat, usePreTransform)(cont) { + val newArgs = targs.map(finishTransformExpr) + cont(PreTransTree( + ApplyStatic(flags, className, methodIdent, newArgs)(resultType))) } } @@ -2728,28 +2780,6 @@ private[optimizer] abstract class OptimizerCore( def wasmBinaryOp(op: WasmBinaryOp.Code, lhs: PreTransform, rhs: PreTransform): Tree = Transient(WasmBinaryOp(op, finishTransformExpr(lhs), finishTransformExpr(rhs))) - def genericWasmDivModUnsigned(wasmOp: WasmBinaryOp.Code, signedOp: BinaryOp.Code, - equalsOp: BinaryOp.Code, zeroLiteral: Literal): TailRec[Tree] = { - targs(1) match { - case PreTransLit(IntLiteral(r)) if r != 0 => - contTree(wasmBinaryOp(wasmOp, targs(0), targs(1))) - case PreTransLit(LongLiteral(r)) if r != 0L => - contTree(wasmBinaryOp(wasmOp, targs(0), targs(1))) - case _ => - withNewTempLocalDefs(targs) { (localDefs, cont1) => - val List(lhsLocalDef, rhsLocalDef) = localDefs - cont1 { - If(BinaryOp(equalsOp, rhsLocalDef.newReplacement, zeroLiteral), { - // trigger the appropriate ArithmeticException - BinaryOp(signedOp, zeroLiteral, zeroLiteral) - }, { - wasmBinaryOp(wasmOp, lhsLocalDef.toPreTransform, rhsLocalDef.toPreTransform) - })(zeroLiteral.tpe).toPreTransform - } - } (cont) - } - } - (intrinsicCode: @switch) match { // Not an intrisic @@ -2846,17 +2876,6 @@ private[optimizer] abstract class OptimizerCore( // java.lang.Integer - case IntegerNLZ => - val tvalue = targs.head - tvalue match { - case PreTransLit(IntLiteral(value)) => - contTree(IntLiteral(Integer.numberOfLeadingZeros(value))) - case _ => - if (isWasm) - contTree(wasmUnaryOp(WasmUnaryOp.I32Clz, tvalue)) - else - default - } case IntegerNTZ => val tvalue = targs.head tvalue match { @@ -2891,23 +2910,8 @@ private[optimizer] abstract class OptimizerCore( contTree(wasmBinaryOp(WasmBinaryOp.I32Rotr, tvalue, tdistance)) } - case IntegerDivideUnsigned => - genericWasmDivModUnsigned(WasmBinaryOp.I32DivU, BinaryOp.Int_/, - BinaryOp.Int_==, IntLiteral(0)) - case IntegerRemainderUnsigned => - genericWasmDivModUnsigned(WasmBinaryOp.I32RemU, BinaryOp.Int_%, - BinaryOp.Int_==, IntLiteral(0)) - // java.lang.Long - case LongNLZ => - val tvalue = targs.head - tvalue match { - case PreTransLit(LongLiteral(value)) => - contTree(IntLiteral(java.lang.Long.numberOfLeadingZeros(value))) - case _ => - contTree(longToInt(wasmUnaryOp(WasmUnaryOp.I64Clz, tvalue))) - } case LongNTZ => val tvalue = targs.head tvalue match { @@ -2945,74 +2949,16 @@ private[optimizer] abstract class OptimizerCore( } case LongToString => - pretransformApply(ApplyFlags.empty, targs.head, - MethodIdent(LongImpl.toString_), Nil, StringClassType, + pretransformApplyStatic(ApplyFlags.empty, LongImpl.RuntimeLongClass, + MethodIdent(LongImpl.toString_), targs, StringClassType, isStat, usePreTransform)( cont) case LongCompare => - pretransformApply(ApplyFlags.empty, targs.head, - MethodIdent(LongImpl.compareToRTLong), targs.tail, IntType, + pretransformApplyStatic(ApplyFlags.empty, LongImpl.RuntimeLongClass, + MethodIdent(LongImpl.compare), targs, IntType, isStat, usePreTransform)( cont) - case LongDivideUnsigned => - if (isWasm) { - genericWasmDivModUnsigned(WasmBinaryOp.I64DivU, BinaryOp.Long_/, - BinaryOp.Long_==, LongLiteral(0L)) - } else { - pretransformApply(ApplyFlags.empty, targs.head, - MethodIdent(LongImpl.divideUnsigned), targs.tail, - ClassType(LongImpl.RuntimeLongClass, nullable = true), isStat, - usePreTransform)( - cont) - } - case LongRemainderUnsigned => - if (isWasm) { - genericWasmDivModUnsigned(WasmBinaryOp.I64RemU, BinaryOp.Long_%, - BinaryOp.Long_==, LongLiteral(0L)) - } else { - pretransformApply(ApplyFlags.empty, targs.head, - MethodIdent(LongImpl.remainderUnsigned), targs.tail, - ClassType(LongImpl.RuntimeLongClass, nullable = true), isStat, - usePreTransform)( - cont) - } - - // java.lang.Float - - case FloatToIntBits => - // The Wasm I32ReinterpretF32 is the *raw* version; we need to normalize NaNs - withNewTempLocalDefs(targs) { (localDefs, cont1) => - val argLocalDef = localDefs.head - def argToDouble = UnaryOp(UnaryOp.FloatToDouble, argLocalDef.newReplacement) - cont1 { - If(BinaryOp(BinaryOp.Double_!=, argToDouble, argToDouble), - IntLiteral(java.lang.Float.floatToIntBits(Float.NaN)), - wasmUnaryOp(WasmUnaryOp.I32ReinterpretF32, argLocalDef.toPreTransform))( - IntType).toPreTransform - } - } (cont) - - case IntBitsToFloat => - contTree(wasmUnaryOp(WasmUnaryOp.F32ReinterpretI32, targs.head)) - - // java.lang.Double - - case DoubleToLongBits => - // The Wasm I64ReinterpretF64 is the *raw* version; we need to normalize NaNs - withNewTempLocalDefs(targs) { (localDefs, cont1) => - val argLocalDef = localDefs.head - cont1 { - If(BinaryOp(BinaryOp.Double_!=, argLocalDef.newReplacement, argLocalDef.newReplacement), - LongLiteral(java.lang.Double.doubleToLongBits(Double.NaN)), - wasmUnaryOp(WasmUnaryOp.I64ReinterpretF64, argLocalDef.toPreTransform))( - LongType).toPreTransform - } - } (cont) - - case LongBitsToDouble => - contTree(wasmUnaryOp(WasmUnaryOp.F64ReinterpretI64, targs.head)) - // java.lang.Character case CharacterCodePointToString => @@ -3051,6 +2997,13 @@ private[optimizer] abstract class OptimizerCore( // java.lang.Math + case MathAbsLong => + pretransformApplyStatic(ApplyFlags.empty, LongImpl.RuntimeLongClass, + MethodIdent(LongImpl.abs), targs, + ClassType(LongImpl.RuntimeLongClass, nullable = true), + isStat, usePreTransform)( + cont) + case MathAbsFloat => contTree(wasmUnaryOp(WasmUnaryOp.F32Abs, targs.head)) case MathAbsDouble => @@ -3073,6 +3026,28 @@ private[optimizer] abstract class OptimizerCore( case MathMaxDouble => contTree(wasmBinaryOp(WasmBinaryOp.F64Max, targs.head, targs.tail.head)) + case MathMultiplyFull => + def expand(targs: List[PreTransform]): TailRec[Tree] = { + pretransformApplyStatic(ApplyFlags.empty, + LongImpl.RuntimeLongClass, + MethodIdent(LongImpl.multiplyFull), + targs, + ClassType(LongImpl.RuntimeLongClass, nullable = true), + isStat, usePreTransform)( + cont) + } + + targs match { + case List(PreTransLit(IntLiteral(x)), PreTransLit(IntLiteral(y))) => + // cannot actually call multiplyHigh to constant-fold because it is JDK9+ + contTree(LongLiteral(x.toLong * y.toLong)) + case List(tlhs, trhs @ PreTransLit(_)) => + // normalize a single constant on the left; the implementation is optimized for that case + expand(trhs :: tlhs :: Nil) + case _ => + expand(targs) + } + // scala.collection.mutable.ArrayBuilder case GenericArrayBuilderResult => @@ -3569,11 +3544,15 @@ private[optimizer] abstract class OptimizerCore( withBinding(rtLongBinding) { (scope1, cont1) => implicit val scope = scope1 val tRef = VarRef(tName)(rtLongClassType) - val newTree = New(LongImpl.RuntimeLongClass, - MethodIdent(LongImpl.initFromParts), - List(Apply(ApplyFlags.empty, tRef, MethodIdent(LongImpl.lo), Nil)(IntType), - Apply(ApplyFlags.empty, tRef, MethodIdent(LongImpl.hi), Nil)(IntType))) - pretransformExpr(newTree)(cont1) + + val lo = Apply(ApplyFlags.empty, tRef, MethodIdent(LongImpl.lo), Nil)(IntType) + val hi = Apply(ApplyFlags.empty, tRef, MethodIdent(LongImpl.hi), Nil)(IntType) + + pretransformExprs(lo, hi) { (tlo, thi) => + inlineClassConstructor(AllocationSite.Anonymous, LongImpl.RuntimeLongClass, + inlinedRTLongStructure, MethodIdent(LongImpl.initFromParts), List(tlo, thi), + () => throw new AssertionError(s"rolled-back RuntimeLong inlining at $pos"))(cont1) + } } (cont) } @@ -3581,33 +3560,11 @@ private[optimizer] abstract class OptimizerCore( implicit scope: Scope): TailRec[Tree] = { implicit val pos = pretrans.pos - // unfortunately nullable for the result types of methods - def rtLongClassType = ClassType(LongImpl.RuntimeLongClass, nullable = true) - - def expandLongModuleOp(methodName: MethodName, - arg: PreTransform): TailRec[Tree] = { - import LongImpl.{RuntimeLongModuleClass => modCls} - val receiver = - makeCast(LoadModule(modCls), ClassType(modCls, nullable = false)).toPreTransform - pretransformApply(ApplyFlags.empty, receiver, MethodIdent(methodName), - arg :: Nil, rtLongClassType, isStat = false, - usePreTransform = true)( - cont) - } - - def expandUnaryOp(methodName: MethodName, arg: PreTransform, - resultType: Type = rtLongClassType): TailRec[Tree] = { - pretransformApply(ApplyFlags.empty, arg, MethodIdent(methodName), Nil, - resultType, isStat = false, usePreTransform = true)( - cont) - } - - def expandBinaryOp(methodName: MethodName, lhs: PreTransform, - rhs: PreTransform, - resultType: Type = rtLongClassType): TailRec[Tree] = { - pretransformApply(ApplyFlags.empty, lhs, MethodIdent(methodName), rhs :: Nil, - resultType, isStat = false, usePreTransform = true)( - cont) + def expand(methodName: MethodName, targs: PreTransform*): TailRec[Tree] = { + val impl = staticCall(LongImpl.RuntimeLongClass, MemberNamespace.PublicStatic, methodName) + pretransformSingleDispatch(ApplyFlags.empty, impl, None, targs.toList, + isStat = false, usePreTransform = true)(cont)( + throw new AssertionError(s"failed to inline RuntimeLong method $methodName at $pos")) } pretrans match { @@ -3616,19 +3573,33 @@ private[optimizer] abstract class OptimizerCore( (op: @switch) match { case IntToLong => - expandLongModuleOp(LongImpl.fromInt, arg) + expand(LongImpl.fromInt, arg) case LongToInt => - expandUnaryOp(LongImpl.toInt, arg, IntType) + expand(LongImpl.toInt, arg) case LongToDouble => - expandUnaryOp(LongImpl.toDouble, arg, DoubleType) + expand(LongImpl.toDouble, arg) case DoubleToLong => - expandLongModuleOp(LongImpl.fromDouble, arg) + expand(LongImpl.fromDouble, arg) case LongToFloat => - expandUnaryOp(LongImpl.toFloat, arg, FloatType) + expand(LongImpl.toFloat, arg) + + case Double_toBits if config.coreSpec.esFeatures.esVersion >= ESVersion.ES2015 => + expand(LongImpl.fromDoubleBits, + arg, PreTransTree(Transient(GetFPBitsDataView))) + + case Double_fromBits if config.coreSpec.esFeatures.esVersion >= ESVersion.ES2015 => + expand(LongImpl.bitsToDouble, + arg, PreTransTree(Transient(GetFPBitsDataView))) + + case Long_clz => + expand(LongImpl.clz, arg) + + case UnsignedIntToLong => + expand(LongImpl.fromUnsignedInt, arg) case _ => cont(pretrans) @@ -3638,34 +3609,34 @@ private[optimizer] abstract class OptimizerCore( import BinaryOp._ (op: @switch) match { - case Long_+ => expandBinaryOp(LongImpl.+, lhs, rhs) - - case Long_- => - lhs match { - case PreTransLit(LongLiteral(0L)) => - expandUnaryOp(LongImpl.UNARY_-, rhs) - case _ => - expandBinaryOp(LongImpl.-, lhs, rhs) - } - - case Long_* => expandBinaryOp(LongImpl.*, lhs, rhs) - case Long_/ => expandBinaryOp(LongImpl./, lhs, rhs) - case Long_% => expandBinaryOp(LongImpl.%, lhs, rhs) - - case Long_& => expandBinaryOp(LongImpl.&, lhs, rhs) - case Long_| => expandBinaryOp(LongImpl.|, lhs, rhs) - case Long_^ => expandBinaryOp(LongImpl.^, lhs, rhs) - - case Long_<< => expandBinaryOp(LongImpl.<<, lhs, rhs) - case Long_>>> => expandBinaryOp(LongImpl.>>>, lhs, rhs) - case Long_>> => expandBinaryOp(LongImpl.>>, lhs, rhs) - - case Long_== => expandBinaryOp(LongImpl.===, lhs, rhs) - case Long_!= => expandBinaryOp(LongImpl.!==, lhs, rhs) - case Long_< => expandBinaryOp(LongImpl.<, lhs, rhs) - case Long_<= => expandBinaryOp(LongImpl.<=, lhs, rhs) - case Long_> => expandBinaryOp(LongImpl.>, lhs, rhs) - case Long_>= => expandBinaryOp(LongImpl.>=, lhs, rhs) + case Long_+ => expand(LongImpl.add, lhs, rhs) + case Long_- => expand(LongImpl.sub, lhs, rhs) + case Long_* => expand(LongImpl.mul, lhs, rhs) + case Long_/ => expand(LongImpl.divide, lhs, rhs) + case Long_% => expand(LongImpl.remainder, lhs, rhs) + + case Long_& => expand(LongImpl.and, lhs, rhs) + case Long_| => expand(LongImpl.or, lhs, rhs) + case Long_^ => expand(LongImpl.xor, lhs, rhs) + + case Long_<< => expand(LongImpl.shl, lhs, rhs) + case Long_>>> => expand(LongImpl.shr, lhs, rhs) + case Long_>> => expand(LongImpl.sar, lhs, rhs) + + case Long_== => expand(LongImpl.equals_, lhs, rhs) + case Long_!= => expand(LongImpl.notEquals, lhs, rhs) + case Long_< => expand(LongImpl.lt, lhs, rhs) + case Long_<= => expand(LongImpl.le, lhs, rhs) + case Long_> => expand(LongImpl.gt, lhs, rhs) + case Long_>= => expand(LongImpl.ge, lhs, rhs) + + case Long_unsigned_/ => expand(LongImpl.divideUnsigned, lhs, rhs) + case Long_unsigned_% => expand(LongImpl.remainderUnsigned, lhs, rhs) + + case Long_unsigned_< => expand(LongImpl.ltu, lhs, rhs) + case Long_unsigned_<= => expand(LongImpl.leu, lhs, rhs) + case Long_unsigned_> => expand(LongImpl.gtu, lhs, rhs) + case Long_unsigned_>= => expand(LongImpl.geu, lhs, rhs) case _ => cont(pretrans) @@ -3702,6 +3673,11 @@ private[optimizer] abstract class OptimizerCore( case BinaryOp.Int_> => BinaryOp.Int_<= case BinaryOp.Int_>= => BinaryOp.Int_< + case BinaryOp.Int_unsigned_< => BinaryOp.Int_unsigned_>= + case BinaryOp.Int_unsigned_<= => BinaryOp.Int_unsigned_> + case BinaryOp.Int_unsigned_> => BinaryOp.Int_unsigned_<= + case BinaryOp.Int_unsigned_>= => BinaryOp.Int_unsigned_< + case BinaryOp.Long_== => BinaryOp.Long_!= case BinaryOp.Long_!= => BinaryOp.Long_== case BinaryOp.Long_< => BinaryOp.Long_>= @@ -3709,6 +3685,11 @@ private[optimizer] abstract class OptimizerCore( case BinaryOp.Long_> => BinaryOp.Long_<= case BinaryOp.Long_>= => BinaryOp.Long_< + case BinaryOp.Long_unsigned_< => BinaryOp.Long_unsigned_>= + case BinaryOp.Long_unsigned_<= => BinaryOp.Long_unsigned_> + case BinaryOp.Long_unsigned_> => BinaryOp.Long_unsigned_<= + case BinaryOp.Long_unsigned_>= => BinaryOp.Long_unsigned_< + case BinaryOp.Double_== => BinaryOp.Double_!= case BinaryOp.Double_!= => BinaryOp.Double_== @@ -3842,6 +3823,23 @@ private[optimizer] abstract class OptimizerCore( PreTransLit(DoubleLiteral(v.toDouble)) case PreTransUnaryOp(IntToLong, x) => foldUnaryOp(IntToDouble, x) + + /* (double) (x) --> (x) + * + * On Wasm, there is a dedicated transient. On JS, that is (x >>> 0). + * + * The latter only kicks in when using bigints for longs. When using + * RuntimeLong, we have eagerly expanded the `UnsignedIntToLong` + * operation, but further inlining and folding will yield the same + * result. + */ + case PreTransUnaryOp(UnsignedIntToLong, x) => + val newX = finishTransformExpr(x) + val resultTree = + if (isWasm) Transient(WasmUnaryOp(WasmUnaryOp.F64ConvertI32U, newX)) + else makeCast(JSBinaryOp(JSBinaryOp.>>>, newX, IntLiteral(0)), DoubleType) + resultTree.toPreTransform + case _ => default } @@ -3939,6 +3937,70 @@ private[optimizer] abstract class OptimizerCore( foldCast(default, ClassType(ClassClass, nullable = false)) } + /* Floating point bit manipulation + * + * The {Float,Double}_fromBits opcodes are the "raw" variants, for which + * the specific bit patterns of NaNs are not specified. We use the + * canonicalizing variants when folding. This is allowed by the spec, + * and ensures that the result of the optimizer is deterministic. + */ + + case Float_toBits => + arg match { + case PreTransLit(FloatLiteral(v)) => + PreTransLit(IntLiteral(java.lang.Float.floatToIntBits(v))) + case _ => + default + } + case Float_fromBits => + arg match { + case PreTransLit(IntLiteral(v)) => + PreTransLit(FloatLiteral(java.lang.Float.intBitsToFloat(v))) + case _ => + default + } + case Double_toBits => + arg match { + case PreTransLit(DoubleLiteral(v)) => + PreTransLit(LongLiteral(java.lang.Double.doubleToLongBits(v))) + case _ => + default + } + case Double_fromBits => + arg match { + case PreTransLit(LongLiteral(v)) => + PreTransLit(DoubleLiteral(java.lang.Double.longBitsToDouble(v))) + case _ => + default + } + + // clz + + case Int_clz => + arg match { + case PreTransLit(IntLiteral(v)) => + PreTransLit(IntLiteral(Integer.numberOfLeadingZeros(v))) + case _ => + default + } + case Long_clz => + arg match { + case PreTransLit(LongLiteral(v)) => + PreTransLit(IntLiteral(java.lang.Long.numberOfLeadingZeros(v))) + case _ => + default + } + + // Unsigned int to long + + case UnsignedIntToLong => + arg match { + case PreTransLit(IntLiteral(v)) => + PreTransLit(LongLiteral(Integer.toUnsignedLong(v))) + case _ => + default + } + case _ => default } @@ -4178,6 +4240,10 @@ private[optimizer] abstract class OptimizerCore( PreTransLit(IntLiteral(y)), z)) => foldBinaryOp(innerOp, PreTransLit(IntLiteral(x + y)), z) + // 1 + (-1 ^ x) == 1 + ~x == -x == 0 - x (this appears when optimizing a Range with step == -1) + case (PreTransLit(IntLiteral(1)), PreTransBinaryOp(Int_^, PreTransLit(IntLiteral(-1)), x)) => + foldBinaryOp(Int_-, PreTransLit(IntLiteral(0)), x) + case _ => default } @@ -4217,12 +4283,8 @@ private[optimizer] abstract class OptimizerCore( case 1 => rhs // Exact power of 2 - case _ if (x & (x - 1)) == 0 => - /* Note that this would match 0, but 0 is handled above. - * It will also match Int.MinValue, but that is not a problem - * as the optimization also works (if you need convincing, - * simply interpret the multiplication as unsigned). - */ + case _ if isUnsignedPowerOf2(x) => + // Interpret the multiplication as unsigned and turn it into a shift. foldBinaryOp(Int_<<, rhs, PreTransLit(IntLiteral(Integer.numberOfTrailingZeros(x)))) @@ -4247,6 +4309,33 @@ private[optimizer] abstract class OptimizerCore( case _ => default } + case Int_unsigned_/ => + (lhs, rhs) match { + case (_, PreTransLit(IntLiteral(0))) => + default + case (PreTransLit(IntLiteral(l)), PreTransLit(IntLiteral(r))) => + intLit(java.lang.Integer.divideUnsigned(l, r)) + + case (_, PreTransLit(IntLiteral(r))) if isUnsignedPowerOf2(r) => + foldBinaryOp(BinaryOp.Int_>>>, lhs, + PreTransLit(IntLiteral(java.lang.Integer.numberOfTrailingZeros(r)))) + + case _ => default + } + + case Int_unsigned_% => + (lhs, rhs) match { + case (_, PreTransLit(IntLiteral(0))) => + default + case (PreTransLit(IntLiteral(l)), PreTransLit(IntLiteral(r))) => + intLit(java.lang.Integer.remainderUnsigned(l, r)) + + case (_, PreTransLit(IntLiteral(r))) if isUnsignedPowerOf2(r) => + foldBinaryOp(BinaryOp.Int_&, PreTransLit(IntLiteral(r - 1)), lhs) + + case _ => default + } + case Int_% => (lhs, rhs) match { case (_, PreTransLit(IntLiteral(0))) => @@ -4275,6 +4364,20 @@ private[optimizer] abstract class OptimizerCore( PreTransBinaryOp(Int_|, PreTransLit(IntLiteral(y)), z)) => foldBinaryOp(Int_|, PreTransLit(IntLiteral(x | y)), z) + case (PreTransLit(IntLiteral(x)), _) => + val rhs2 = simplifyOnlyInterestedInMask(rhs, ~x) + if (rhs2 eq rhs) + default + else + foldBinaryOp(Int_|, lhs, rhs2) + + // x | (~x & z) --> x | z (appears in the inlining of 0L - b) + case (PreTransLocalDef(x), + PreTransBinaryOp(Int_&, + PreTransBinaryOp(Int_^, PreTransLit(IntLiteral(-1)), PreTransLocalDef(y)), + z)) if x eq y => + foldBinaryOp(Int_|, lhs, z) + case _ => default } @@ -4293,6 +4396,13 @@ private[optimizer] abstract class OptimizerCore( PreTransBinaryOp(Int_&, PreTransLit(IntLiteral(y)), z)) => foldBinaryOp(Int_&, PreTransLit(IntLiteral(x & y)), z) + case (PreTransLit(IntLiteral(x)), _) => + val rhs2 = simplifyOnlyInterestedInMask(rhs, x) + if (rhs2 eq rhs) + default + else + foldBinaryOp(Int_&, lhs, rhs2) + case _ => default } @@ -4329,10 +4439,15 @@ private[optimizer] abstract class OptimizerCore( case (_, PreTransLit(IntLiteral(y))) => val dist = y & 31 - if (dist == 0) + if (dist == 0) { lhs - else - PreTransBinaryOp(Int_<<, lhs, PreTransLit(IntLiteral(dist))) + } else { + val lhs2 = simplifyOnlyInterestedInMask(lhs, (-1) >>> dist) + if (lhs2 eq lhs) + PreTransBinaryOp(Int_<<, lhs, PreTransLit(IntLiteral(dist))) + else + foldBinaryOp(Int_<<, lhs2, PreTransLit(IntLiteral(dist))) + } case _ => default } @@ -4351,7 +4466,7 @@ private[optimizer] abstract class OptimizerCore( if (dist >= 32) PreTransTree(Block(finishTransformStat(x), IntLiteral(0))) else - PreTransBinaryOp(Int_>>>, x, PreTransLit(IntLiteral(dist))) + foldBinaryOp(Int_>>>, x, PreTransLit(IntLiteral(dist))) case (PreTransBinaryOp(op @ (Int_| | Int_& | Int_^), PreTransLit(IntLiteral(x)), y), @@ -4363,10 +4478,15 @@ private[optimizer] abstract class OptimizerCore( case (_, PreTransLit(IntLiteral(y))) => val dist = y & 31 - if (dist == 0) + if (dist == 0) { lhs - else - PreTransBinaryOp(Int_>>>, lhs, PreTransLit(IntLiteral(dist))) + } else { + val lhs2 = simplifyOnlyInterestedInMask(lhs, (-1) << dist) + if (lhs2 eq lhs) + PreTransBinaryOp(Int_>>>, lhs, PreTransLit(IntLiteral(dist))) + else + foldBinaryOp(Int_>>>, lhs2, PreTransLit(IntLiteral(dist))) + } case _ => default } @@ -4382,18 +4502,31 @@ private[optimizer] abstract class OptimizerCore( case (PreTransBinaryOp(Int_>>, x, PreTransLit(IntLiteral(y))), PreTransLit(IntLiteral(z))) => val dist = Math.min((y & 31) + (z & 31), 31) - PreTransBinaryOp(Int_>>, x, PreTransLit(IntLiteral(dist))) + foldBinaryOp(Int_>>, x, PreTransLit(IntLiteral(dist))) case (PreTransBinaryOp(Int_>>>, x, PreTransLit(IntLiteral(y))), PreTransLit(IntLiteral(_))) if (y & 31) != 0 => foldBinaryOp(Int_>>>, lhs, rhs) + case (PreTransBinaryOp(op @ (Int_| | Int_& | Int_^), + PreTransLit(IntLiteral(x)), y), + z @ PreTransLit(IntLiteral(zValue))) => + foldBinaryOp( + op, + PreTransLit(IntLiteral(x >> zValue)), + foldBinaryOp(Int_>>, y, z)) + case (_, PreTransLit(IntLiteral(y))) => val dist = y & 31 - if (dist == 0) + if (dist == 0) { lhs - else - PreTransBinaryOp(Int_>>, lhs, PreTransLit(IntLiteral(dist))) + } else { + val lhs2 = simplifyOnlyInterestedInMask(lhs, (-1) << dist) + if (lhs2 eq lhs) + PreTransBinaryOp(Int_>>, lhs, PreTransLit(IntLiteral(dist))) + else + foldBinaryOp(Int_>>, lhs2, PreTransLit(IntLiteral(dist))) + } case _ => default } @@ -4415,57 +4548,81 @@ private[optimizer] abstract class OptimizerCore( PreTransLit(IntLiteral(z))) => foldBinaryOp(op, y, PreTransLit(IntLiteral(x ^ z))) + case (PreTransLocalDef(l), PreTransLocalDef(r)) if l eq r => + booleanLit(op == Int_==) + case (PreTransLit(_), _) => foldBinaryOp(op, rhs, lhs) case _ => default } - case Int_< | Int_<= | Int_> | Int_>= => - def flippedOp = (op: @switch) match { - case Int_< => Int_> - case Int_<= => Int_>= - case Int_> => Int_< - case Int_>= => Int_<= + case Int_< | Int_<= | Int_> | Int_>= | + Int_unsigned_< | Int_unsigned_<= | Int_unsigned_> | Int_unsigned_>= => + val (isSigned, otherSignOp, flippedOp) = (op: @switch) match { + case Int_< => (true, Int_unsigned_<, Int_>) + case Int_<= => (true, Int_unsigned_<=, Int_>=) + case Int_> => (true, Int_unsigned_>, Int_<) + case Int_>= => (true, Int_unsigned_>=, Int_<=) + case Int_unsigned_< => (false, Int_<, Int_unsigned_>) + case Int_unsigned_<= => (false, Int_<=, Int_unsigned_>=) + case Int_unsigned_> => (false, Int_>, Int_unsigned_<) + case Int_unsigned_>= => (false, Int_>=, Int_unsigned_<=) } + val opMinValue = if (isSigned) Int.MinValue else 0 + val opMaxValue = if (isSigned) Int.MaxValue else -1 + val signedOp = if (isSigned) op else otherSignOp // for normalized tests + (lhs, rhs) match { case (PreTransLit(IntLiteral(l)), PreTransLit(IntLiteral(r))) => booleanLit((op: @switch) match { - case Int_< => l < r - case Int_<= => l <= r - case Int_> => l > r - case Int_>= => l >= r + case Int_< => l < r + case Int_<= => l <= r + case Int_> => l > r + case Int_>= => l >= r + case Int_unsigned_< => Integer.compareUnsigned(l, r) < 0 + case Int_unsigned_<= => Integer.compareUnsigned(l, r) <= 0 + case Int_unsigned_> => Integer.compareUnsigned(l, r) > 0 + case Int_unsigned_>= => Integer.compareUnsigned(l, r) >= 0 }) + case (IntFlipSign(x), PreTransLit(IntLiteral(r))) => + foldBinaryOp(otherSignOp, x, PreTransLit(IntLiteral(r ^ Int.MinValue)(rhs.pos))) + case (IntFlipSign(x), IntFlipSign(y)) => + foldBinaryOp(otherSignOp, x, y) + case (_, PreTransLit(IntLiteral(y))) => y match { - case Int.MinValue => - if (op == Int_< || op == Int_>=) { + case `opMinValue` => + if (signedOp == Int_< || signedOp == Int_>=) { Block(finishTransformStat(lhs), - BooleanLiteral(op == Int_>=)).toPreTransform + BooleanLiteral(signedOp == Int_>=)).toPreTransform } else { - foldBinaryOp(if (op == Int_<=) Int_== else Int_!=, lhs, rhs) + foldBinaryOp(if (signedOp == Int_<=) Int_== else Int_!=, lhs, rhs) } - case Int.MaxValue => - if (op == Int_> || op == Int_<=) { + case `opMaxValue` => + if (signedOp == Int_> || signedOp == Int_<=) { Block(finishTransformStat(lhs), - BooleanLiteral(op == Int_<=)).toPreTransform + BooleanLiteral(signedOp == Int_<=)).toPreTransform } else { - foldBinaryOp(if (op == Int_>=) Int_== else Int_!=, lhs, rhs) + foldBinaryOp(if (signedOp == Int_>=) Int_== else Int_!=, lhs, rhs) } - case _ if y == Int.MinValue + 1 && (op == Int_< || op == Int_>=) => - foldBinaryOp(if (op == Int_<) Int_== else Int_!=, lhs, - PreTransLit(IntLiteral(Int.MinValue))) + case _ if y == opMinValue + 1 && (signedOp == Int_< || signedOp == Int_>=) => + foldBinaryOp(if (signedOp == Int_<) Int_== else Int_!=, lhs, + PreTransLit(IntLiteral(opMinValue))) - case _ if y == Int.MaxValue - 1 && (op == Int_> || op == Int_<=) => - foldBinaryOp(if (op == Int_>) Int_== else Int_!=, lhs, - PreTransLit(IntLiteral(Int.MaxValue))) + case _ if y == opMaxValue - 1 && (signedOp == Int_> || signedOp == Int_<=) => + foldBinaryOp(if (signedOp == Int_>) Int_== else Int_!=, lhs, + PreTransLit(IntLiteral(opMaxValue))) case _ => default } + case (PreTransLocalDef(l), PreTransLocalDef(r)) if l eq r => + booleanLit(signedOp == Int_<= || signedOp == Int_>=) + case (PreTransLit(IntLiteral(_)), _) => foldBinaryOp(flippedOp, rhs, lhs) @@ -4525,12 +4682,8 @@ private[optimizer] abstract class OptimizerCore( case 1L => rhs // Exact power of 2 - case _ if (x & (x - 1L)) == 0L => - /* Note that this would match 0L, but 0L is handled above. - * It will also match Long.MinValue, but that is not a problem - * as the optimization also works (if you need convincing, - * simply interpret the multiplication as unsigned). - */ + case _ if isUnsignedPowerOf2(x) => + // Interpret the multiplication as unsigned and turn it into a shift. foldBinaryOp(Long_<<, rhs, PreTransLit( IntLiteral(java.lang.Long.numberOfTrailingZeros(x)))) @@ -4547,10 +4700,10 @@ private[optimizer] abstract class OptimizerCore( case (PreTransLit(LongLiteral(l)), PreTransLit(LongLiteral(r))) => longLit(l / r) - case (_, PreTransLit(LongLiteral(1))) => + case (_, PreTransLit(LongLiteral(1L))) => lhs - case (_, PreTransLit(LongLiteral(-1))) => - foldBinaryOp(Long_-, PreTransLit(LongLiteral(0)), lhs) + case (_, PreTransLit(LongLiteral(-1L))) => + foldBinaryOp(Long_-, PreTransLit(LongLiteral(0L)), lhs) case (LongFromInt(x), LongFromInt(PreTransLit(y: IntLiteral))) if y.value != -1 => @@ -4575,6 +4728,33 @@ private[optimizer] abstract class OptimizerCore( case _ => default } + case Long_unsigned_/ => + (lhs, rhs) match { + case (_, PreTransLit(LongLiteral(0L))) => + default + case (PreTransLit(LongLiteral(l)), PreTransLit(LongLiteral(r))) => + longLit(java.lang.Long.divideUnsigned(l, r)) + + case (_, PreTransLit(LongLiteral(r))) if isUnsignedPowerOf2(r) => + foldBinaryOp(BinaryOp.Long_>>>, lhs, + PreTransLit(IntLiteral(java.lang.Long.numberOfTrailingZeros(r)))) + + case _ => default + } + + case Long_unsigned_% => + (lhs, rhs) match { + case (_, PreTransLit(LongLiteral(0L))) => + default + case (PreTransLit(LongLiteral(l)), PreTransLit(LongLiteral(r))) => + longLit(java.lang.Long.remainderUnsigned(l, r)) + + case (_, PreTransLit(LongLiteral(r))) if isUnsignedPowerOf2(r) => + foldBinaryOp(BinaryOp.Long_&, PreTransLit(LongLiteral(r - 1L)), lhs) + + case _ => default + } + case Long_| => (lhs, rhs) match { case (PreTransLit(LongLiteral(l)), PreTransLit(LongLiteral(r))) => @@ -4608,6 +4788,9 @@ private[optimizer] abstract class OptimizerCore( case (PreTransLit(LongLiteral(0)), _) => PreTransBlock(finishTransformStat(rhs), lhs) + case (PreTransLit(LongLiteral(0xffffffffL)), LongFromInt(intRhs)) => + foldUnaryOp(UnaryOp.UnsignedIntToLong, intRhs) + case (PreTransLit(LongLiteral(x)), PreTransBinaryOp(Long_&, PreTransLit(LongLiteral(y)), z)) => foldBinaryOp(Long_&, PreTransLit(LongLiteral(x & y)), z) @@ -4686,54 +4869,68 @@ private[optimizer] abstract class OptimizerCore( PreTransLit(LongLiteral(z))) => foldBinaryOp(op, y, PreTransLit(LongLiteral(x ^ z))) + case (PreTransLocalDef(l), PreTransLocalDef(r)) if l eq r => + booleanLit(positive) + case (PreTransLit(LongLiteral(_)), _) => foldBinaryOp(op, rhs, lhs) case _ => default } - case Long_< | Long_<= | Long_> | Long_>= => - def flippedOp = (op: @switch) match { - case Long_< => Long_> - case Long_<= => Long_>= - case Long_> => Long_< - case Long_>= => Long_<= + case Long_< | Long_<= | Long_> | Long_>= | + Long_unsigned_< | Long_unsigned_<= | Long_unsigned_> | Long_unsigned_>= => + val (isSigned, otherSignOp, flippedOp, intOp) = (op: @switch) match { + case Long_< => (true, Long_unsigned_<, Long_>, Int_<) + case Long_<= => (true, Long_unsigned_<=, Long_>=, Int_<=) + case Long_> => (true, Long_unsigned_>, Long_<, Int_>) + case Long_>= => (true, Long_unsigned_>=, Long_<=, Int_>=) + case Long_unsigned_< => (false, Long_<, Long_unsigned_>, Int_unsigned_<) + case Long_unsigned_<= => (false, Long_<=, Long_unsigned_>=, Int_unsigned_<=) + case Long_unsigned_> => (false, Long_>, Long_unsigned_<, Int_unsigned_>) + case Long_unsigned_>= => (false, Long_>=, Long_unsigned_<=, Int_unsigned_>=) } - def intOp = (op: @switch) match { - case Long_< => Int_< - case Long_<= => Int_<= - case Long_> => Int_> - case Long_>= => Int_>= - } + val opMinValue = if (isSigned) Long.MinValue else 0L + val opMaxValue = if (isSigned) Long.MaxValue else -1L + val signedOp = if (isSigned) op else otherSignOp // for normalized tests (lhs, rhs) match { case (PreTransLit(LongLiteral(l)), PreTransLit(LongLiteral(r))) => booleanLit((op: @switch) match { - case Long_< => l < r - case Long_<= => l <= r - case Long_> => l > r - case Long_>= => l >= r + case Long_< => l < r + case Long_<= => l <= r + case Long_> => l > r + case Long_>= => l >= r + case Long_unsigned_< => java.lang.Long.compareUnsigned(l, r) < 0 + case Long_unsigned_<= => java.lang.Long.compareUnsigned(l, r) <= 0 + case Long_unsigned_> => java.lang.Long.compareUnsigned(l, r) > 0 + case Long_unsigned_>= => java.lang.Long.compareUnsigned(l, r) >= 0 }) - case (_, PreTransLit(LongLiteral(Long.MinValue))) => - if (op == Long_< || op == Long_>=) { + case (LongFlipSign(x), PreTransLit(LongLiteral(r))) => + foldBinaryOp(otherSignOp, x, PreTransLit(LongLiteral(r ^ Long.MinValue)(rhs.pos))) + case (LongFlipSign(x), LongFlipSign(y)) => + foldBinaryOp(otherSignOp, x, y) + + case (_, PreTransLit(LongLiteral(`opMinValue`))) => + if (signedOp == Long_< || signedOp == Long_>=) { Block(finishTransformStat(lhs), - BooleanLiteral(op == Long_>=)).toPreTransform + BooleanLiteral(signedOp == Long_>=)).toPreTransform } else { - foldBinaryOp(if (op == Long_<=) Long_== else Long_!=, lhs, rhs) + foldBinaryOp(if (signedOp == Long_<=) Long_== else Long_!=, lhs, rhs) } - case (_, PreTransLit(LongLiteral(Long.MaxValue))) => - if (op == Long_> || op == Long_<=) { + case (_, PreTransLit(LongLiteral(`opMaxValue`))) => + if (signedOp == Long_> || signedOp == Long_<=) { Block(finishTransformStat(lhs), - BooleanLiteral(op == Long_<=)).toPreTransform + BooleanLiteral(signedOp == Long_<=)).toPreTransform } else { - foldBinaryOp(if (op == Long_>=) Long_== else Long_!=, lhs, rhs) + foldBinaryOp(if (signedOp == Long_>=) Long_== else Long_!=, lhs, rhs) } case (LongFromInt(x), LongFromInt(y)) => foldBinaryOp(intOp, x, y) - case (LongFromInt(x), PreTransLit(LongLiteral(y))) => + case (LongFromInt(x), PreTransLit(LongLiteral(y))) if isSigned => assert(y > Int.MaxValue || y < Int.MinValue) val result = if (y > Int.MaxValue) op == Long_< || op == Long_<= @@ -4747,7 +4944,8 @@ private[optimizer] abstract class OptimizerCore( */ case (PreTransBinaryOp(Long_+, PreTransLit(LongLiteral(x)), y @ LongFromInt(_)), PreTransLit(LongLiteral(z))) - if canAddLongs(x, Int.MinValue) && + if isSigned && + canAddLongs(x, Int.MinValue) && canAddLongs(x, Int.MaxValue) && canSubtractLongs(z, x) => foldBinaryOp(op, y, PreTransLit(LongLiteral(z-x))) @@ -4759,7 +4957,8 @@ private[optimizer] abstract class OptimizerCore( */ case (PreTransBinaryOp(Long_-, PreTransLit(LongLiteral(x)), y @ LongFromInt(_)), PreTransLit(LongLiteral(z))) - if canSubtractLongs(x, Int.MinValue) && + if isSigned && + canSubtractLongs(x, Int.MinValue) && canSubtractLongs(x, Int.MaxValue) && canSubtractLongs(z, x) => if (z-x != Long.MinValue) { @@ -4787,7 +4986,8 @@ private[optimizer] abstract class OptimizerCore( * This requires to evaluate x and y once. */ case (PreTransBinaryOp(Long_+, LongFromInt(x), LongFromInt(y)), - PreTransLit(LongLiteral(Int.MaxValue))) => + PreTransLit(LongLiteral(Int.MaxValue))) + if isSigned => trampoline { /* HACK: We use an empty scope here for `withNewLocalDefs`. * It's OKish to do that because we're only defining Ints, and @@ -4809,6 +5009,9 @@ private[optimizer] abstract class OptimizerCore( } (finishTransform(isStat = false))(emptyScope) }.toPreTransform + case (PreTransLocalDef(l), PreTransLocalDef(r)) if l eq r => + booleanLit(signedOp == Long_<= || signedOp == Long_>=) + case (PreTransLit(LongLiteral(_)), _) => foldBinaryOp(flippedOp, rhs, lhs) @@ -4874,6 +5077,20 @@ private[optimizer] abstract class OptimizerCore( case (PreTransLit(DoubleLiteral(l)), PreTransLit(DoubleLiteral(r))) => doubleLit(l + r) + /* ±0.0 + cast(a >>> b, DoubleType) --> cast(a >>> b, DoubleType) + * + * In general, `+0.0 + y -> y` is not a valid rewrite, because it does + * not give the same result when y is -0.0. (Paradoxically, with -0.0 + * on the left it *is* a valid rewrite, though not a very useful one.) + * + * However, if y is the result of a JS `>>>` operator, we know it + * cannot be -0.0, hence the rewrite is valid. That particular shape + * appears in the inlining of `Integer.toUnsignedLong(x).toDouble`. + */ + case (PreTransLit(DoubleLiteral(0.0)), // also matches -0.0 + PreTransTree(Transient(Cast(JSBinaryOp(JSBinaryOp.>>>, _, _), DoubleType)), _)) => + rhs + case _ => default } @@ -5008,6 +5225,84 @@ private[optimizer] abstract class OptimizerCore( } } + /** Simplifies the given `value` expression with the knowledge that only some + * of its resulting bits will be relevant. + * + * The relevant bits are those that are 1 in `mask`. These bits must be + * preserved by the simplifications. Bits that are 0 in `mask` can be + * arbitrarily altered. + * + * For an example of why this is useful, consider Long addition where `a` + * is a constant. The formula for the `hi` result contains the following + * subexpression: + * {{{ + * ((alo & blo) | ((alo | blo) & ~lo)) >>> 31 + * }}} + * + * Since we are going to shift by >>> 31, only the most significant bit + * (msb) of the left-hand-side is relevant. We can alter the other ones. + * Since `a` is constant, `alo` is constant. If it were equal to 0, the + * leftmost `&` and the innermost `|` would fold away. It is unfortunately + * often not 0. The end result only depends on its msb, however, and that's + * where this simplification helps. + * + * If the msb of `alo` is 0, we can replace `alo` in that subexpression by 0 + * without altering the final result. That allows parts of the expression to + * fold away. + * + * Likewise, if its msb is 1, we can replace `alo` by -1. That also allows + * to fold the leftmost `&` and the innermost `|` (in different ways). + * + * The simplification performed in this method is capable of performing that + * rewrite. It pushes the relevant masking information down combinations of + * `&`, `|` and `^`, and rewrites constants in the way that allows the most + * folding without altering the end result. + * + * When we cannot improve a fold, we transform constants so that they are + * closer to 0. This is a code size improvement. Constants close to 0 use + * fewer bytes in the final encoding (textual in JS, signed LEB in Wasm). + */ + private def simplifyOnlyInterestedInMask(value: PreTransform, mask: Int): PreTransform = { + import BinaryOp._ + + implicit val pos = value.pos + + def chooseSmallestAbs(a: Int, b: Int): Int = + if (Integer.compareUnsigned(Math.abs(a), Math.abs(b)) <= 0) a + else b + + value match { + case PreTransBinaryOp(op @ (Int_& | Int_| | Int_^), lhs, rhs) => + def simplifyArg(arg: PreTransform): PreTransform = { + simplifyOnlyInterestedInMask(arg, mask) match { + case arg2 @ PreTransLit(IntLiteral(v)) => + val improvedV = (v & mask) match { + case 0 => 0 // foldBinaryOp below will fold this away + case `mask` => -1 // same, except for Int_^, in which case it becomes the ~z representation + case masked => chooseSmallestAbs(masked, masked | ~mask) + } + if (improvedV == v) + arg2 + else + PreTransLit(IntLiteral(improvedV)(arg2.pos)) + case arg2 => + arg2 + } + } + + val lhs2 = simplifyArg(lhs) + val rhs2 = simplifyArg(rhs) + + if ((lhs2 eq lhs) && (rhs2 eq rhs)) + value + else + foldBinaryOp(op, lhs2, rhs2) + + case _ => + value + } + } + private def fold3WayIntComparison(canBeEqual: Boolean, canBeLessThan: Boolean, canBeGreaterThan: Boolean, lhs: PreTransform, rhs: PreTransform)( implicit pos: Position): PreTransform = { @@ -5666,6 +5961,12 @@ private[optimizer] object OptimizerCore { private val ClassTagApplyMethodName = MethodName("apply", List(ClassRef(ClassClass)), ClassRef(ClassName("scala.reflect.ClassTag"))) + def isUnsignedPowerOf2(x: Int): Boolean = + (x & (x - 1)) == 0 && x != 0 + + def isUnsignedPowerOf2(x: Long): Boolean = + (x & (x - 1L)) == 0L && x != 0L + final class InlineableClassStructure(val className: ClassName, private val allFields: List[FieldDef]) { private[OptimizerCore] val refinedType: RefinedType = RefinedType(ClassType(className, nullable = false), isExact = true) @@ -6379,6 +6680,24 @@ private[optimizer] object OptimizerCore { } } + private object IntFlipSign { + def unapply(tree: PreTransform): Option[PreTransform] = tree match { + case PreTransBinaryOp(BinaryOp.Int_^, PreTransLit(IntLiteral(Int.MinValue)), x) => + Some(x) + case _ => + None + } + } + + private object LongFlipSign { + def unapply(tree: PreTransform): Option[PreTransform] = tree match { + case PreTransBinaryOp(BinaryOp.Long_^, PreTransLit(LongLiteral(Long.MinValue)), x) => + Some(x) + case _ => + None + } + } + private object AndThen { def apply(lhs: Tree, rhs: Tree)(implicit pos: Position): Tree = If(lhs, rhs, BooleanLiteral(false))(BooleanType) @@ -6473,37 +6792,26 @@ private[optimizer] object OptimizerCore { final val ArrayUpdate = ArrayApply + 1 final val ArrayLength = ArrayUpdate + 1 - final val IntegerNLZ = ArrayLength + 1 - final val IntegerNTZ = IntegerNLZ + 1 + final val IntegerNTZ = ArrayLength + 1 final val IntegerBitCount = IntegerNTZ + 1 final val IntegerRotateLeft = IntegerBitCount + 1 final val IntegerRotateRight = IntegerRotateLeft + 1 - final val IntegerDivideUnsigned = IntegerRotateRight + 1 - final val IntegerRemainderUnsigned = IntegerDivideUnsigned + 1 - final val LongNLZ = IntegerRemainderUnsigned + 1 - final val LongNTZ = LongNLZ + 1 + final val LongNTZ = IntegerRotateRight + 1 final val LongBitCount = LongNTZ + 1 final val LongRotateLeft = LongBitCount + 1 final val LongRotateRight = LongRotateLeft + 1 final val LongToString = LongRotateRight + 1 final val LongCompare = LongToString + 1 - final val LongDivideUnsigned = LongCompare + 1 - final val LongRemainderUnsigned = LongDivideUnsigned + 1 - final val FloatToIntBits = LongRemainderUnsigned + 1 - final val IntBitsToFloat = FloatToIntBits + 1 - - final val DoubleToLongBits = IntBitsToFloat + 1 - final val LongBitsToDouble = DoubleToLongBits + 1 - - final val CharacterCodePointToString = LongBitsToDouble + 1 + final val CharacterCodePointToString = LongCompare + 1 final val StringCodePointAt = CharacterCodePointToString + 1 final val StringSubstringStart = StringCodePointAt + 1 final val StringSubstringStartEnd = StringSubstringStart + 1 - final val MathAbsFloat = StringSubstringStartEnd + 1 + final val MathAbsLong = StringSubstringStartEnd + 1 + final val MathAbsFloat = MathAbsLong + 1 final val MathAbsDouble = MathAbsFloat + 1 final val MathCeil = MathAbsDouble + 1 final val MathFloor = MathCeil + 1 @@ -6513,8 +6821,9 @@ private[optimizer] object OptimizerCore { final val MathMinDouble = MathMinFloat + 1 final val MathMaxFloat = MathMinDouble + 1 final val MathMaxDouble = MathMaxFloat + 1 + final val MathMultiplyFull = MathMaxDouble + 1 - final val ArrayBuilderZeroOf = MathMaxDouble + 1 + final val ArrayBuilderZeroOf = MathMultiplyFull + 1 final val GenericArrayBuilderResult = ArrayBuilderZeroOf + 1 final val ClassGetName = GenericArrayBuilderResult + 1 @@ -6568,9 +6877,6 @@ private[optimizer] object OptimizerCore { m("array_update", List(O, I, O), V) -> ArrayUpdate, m("array_length", List(O), I) -> ArrayLength ), - ClassName("java.lang.Integer$") -> List( - m("numberOfLeadingZeros", List(I), I) -> IntegerNLZ - ), ClassName("java.lang.Class") -> List( m("getName", Nil, StringClassRef) -> ClassGetName ), @@ -6605,38 +6911,26 @@ private[optimizer] object OptimizerCore { private val runtimeLongIntrinsics: List[(ClassName, List[(MethodName, Int)])] = List( ClassName("java.lang.Long$") -> List( m("toString", List(J), ClassRef(BoxedStringClass)) -> LongToString, - m("compare", List(J, J), I) -> LongCompare, - m("divideUnsigned", List(J, J), J) -> LongDivideUnsigned, - m("remainderUnsigned", List(J, J), J) -> LongRemainderUnsigned + m("compare", List(J, J), I) -> LongCompare + ), + ClassName("java.lang.Math$") -> List( + m("abs", List(J), J) -> MathAbsLong, + m("multiplyFull", List(I, I), J) -> MathMultiplyFull ) ) private val wasmIntrinsics: List[(ClassName, List[(MethodName, Int)])] = List( ClassName("java.lang.Integer$") -> List( - // note: numberOfLeadingZeros in already in the commonIntrinsics m("numberOfTrailingZeros", List(I), I) -> IntegerNTZ, m("bitCount", List(I), I) -> IntegerBitCount, m("rotateLeft", List(I, I), I) -> IntegerRotateLeft, - m("rotateRight", List(I, I), I) -> IntegerRotateRight, - m("divideUnsigned", List(I, I), I) -> IntegerDivideUnsigned, - m("remainderUnsigned", List(I, I), I) -> IntegerRemainderUnsigned + m("rotateRight", List(I, I), I) -> IntegerRotateRight ), ClassName("java.lang.Long$") -> List( - m("numberOfLeadingZeros", List(J), I) -> LongNLZ, m("numberOfTrailingZeros", List(J), I) -> LongNTZ, m("bitCount", List(J), I) -> LongBitCount, m("rotateLeft", List(J, I), J) -> LongRotateLeft, - m("rotateRight", List(J, I), J) -> LongRotateRight, - m("divideUnsigned", List(J, J), J) -> LongDivideUnsigned, - m("remainderUnsigned", List(J, J), J) -> LongRemainderUnsigned - ), - ClassName("java.lang.Float$") -> List( - m("floatToIntBits", List(F), I) -> FloatToIntBits, - m("intBitsToFloat", List(I), F) -> IntBitsToFloat - ), - ClassName("java.lang.Double$") -> List( - m("doubleToLongBits", List(D), J) -> DoubleToLongBits, - m("longBitsToDouble", List(J), D) -> LongBitsToDouble + m("rotateRight", List(J, I), J) -> LongRotateRight ), ClassName("java.lang.Character$") -> List( m("toString", List(I), StringClassRef) -> CharacterCodePointToString diff --git a/linker/shared/src/main/scala/org/scalajs/linker/standard/CoreSpec.scala b/linker/shared/src/main/scala/org/scalajs/linker/standard/CoreSpec.scala index e5e285268f..3c4c979adc 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/standard/CoreSpec.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/standard/CoreSpec.scala @@ -96,9 +96,6 @@ final class CoreSpec private ( targetIsWebAssembly ) } - - private[linker] lazy val linkTimeProperties = new LinkTimeProperties( - semantics, esFeatures, targetIsWebAssembly) } private[linker] object CoreSpec { diff --git a/linker/shared/src/main/scala/org/scalajs/linker/standard/SymbolRequirement.scala b/linker/shared/src/main/scala/org/scalajs/linker/standard/SymbolRequirement.scala index 6838c8341c..5483265491 100644 --- a/linker/shared/src/main/scala/org/scalajs/linker/standard/SymbolRequirement.scala +++ b/linker/shared/src/main/scala/org/scalajs/linker/standard/SymbolRequirement.scala @@ -79,6 +79,11 @@ object SymbolRequirement { CallStaticMethod(origin, className, methodName) } + def callStaticMethods(className: ClassName, + methodNames: List[MethodName]): SymbolRequirement = { + multipleInternal(methodNames.map(callStaticMethod(className, _))) + } + @deprecated("broken (not actually optional), do not use", "1.13.2") def optional(requirement: SymbolRequirement): SymbolRequirement = requirement diff --git a/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala index 4eb535144d..c543be0f2b 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala @@ -874,6 +874,114 @@ class AnalyzerTest { ) Future.sequence(results) } + + @Test + def linkTimeIfReachable(): AsyncResult = await { + val mainMethodName = m("main", Nil, IntRef) + val fooMethodName = m("foo", Nil, IntRef) + val barMethodName = m("bar", Nil, IntRef) + + val thisType = ClassType("A", nullable = false) + + val productionMode = true + + /* linkTimeIf(productionMode) { + * this.foo() + * } { + * this.bar() + * } + */ + val mainBody = LinkTimeIf( + BinaryOp(BinaryOp.Boolean_==, + LinkTimeProperty("core/productionMode")(BooleanType), + BooleanLiteral(productionMode)), + Apply(EAF, This()(thisType), fooMethodName, Nil)(IntType), + Apply(EAF, This()(thisType), barMethodName, Nil)(IntType) + )(IntType) + + val classDefs = Seq( + classDef("A", superClass = Some(ObjectClass), + methods = List( + trivialCtor("A"), + MethodDef(EMF, mainMethodName, NON, Nil, IntType, Some(mainBody))(EOH, UNV), + MethodDef(EMF, fooMethodName, NON, Nil, IntType, Some(int(1)))(EOH, UNV), + MethodDef(EMF, barMethodName, NON, Nil, IntType, Some(int(2)))(EOH, UNV) + ) + ) + ) + + val requirements = { + reqsFactory.instantiateClass("A", NoArgConstructorName) ++ + reqsFactory.callMethod("A", mainMethodName) + } + + val analysisFuture = computeAnalysis(classDefs, requirements, + config = StandardConfig().withSemantics(_.withProductionMode(productionMode))) + + for (analysis <- analysisFuture) yield { + assertNoError(analysis) + + val AfooMethodInfo = analysis.classInfos("A") + .methodInfos(MemberNamespace.Public)(fooMethodName) + assertTrue(AfooMethodInfo.isReachable) + + val AbarMethodInfo = analysis.classInfos("A") + .methodInfos(MemberNamespace.Public)(barMethodName) + assertFalse(AbarMethodInfo.isReachable) + } + } + + @Test + def linkTimeIfError(): AsyncResult = await { + val mainMethodName = m("main", Nil, IntRef) + val fooMethodName = m("foo", Nil, IntRef) + + val thisType = ClassType("A", nullable = false) + + val productionMode = true + + /* linkTimeIf(unknownProperty) { + * this.foo() + * } { + * this.bar() + * } + */ + val mainBody = LinkTimeIf( + BinaryOp(BinaryOp.Boolean_==, + LinkTimeProperty("core/unknownProperty")(BooleanType), + BooleanLiteral(productionMode)), + Apply(EAF, This()(thisType), fooMethodName, Nil)(IntType), + Apply(EAF, This()(thisType), fooMethodName, Nil)(IntType) + )(IntType) + + val classDefs = Seq( + classDef("A", superClass = Some(ObjectClass), + methods = List( + trivialCtor("A"), + MethodDef(EMF, mainMethodName, NON, Nil, IntType, Some(mainBody))(EOH, UNV) + ) + ) + ) + + val requirements = { + reqsFactory.instantiateClass("A", NoArgConstructorName) ++ + reqsFactory.callMethod("A", mainMethodName) + } + + val analysisFuture = computeAnalysis(classDefs, requirements, + config = StandardConfig().withSemantics(_.withProductionMode(productionMode))) + + for (analysis <- analysisFuture) yield { + assertContainsError(s"InvalidLinkTimeProperty(core/unknownProperty)", analysis) { + case InvalidLinkTimeProperty("core/unknownProperty", BooleanType, _) => true + } + + // Branches are not taken, so there is no error for linking `foo` + assertNotContainsError(s"any MissingMethod", analysis) { + case MissingMethod(_, _) => true + } + } + } } object AnalyzerTest { @@ -962,10 +1070,21 @@ object AnalyzerTest { private def assertContainsError(msg: String, analysis: Analysis)( pf: PartialFunction[Error, Boolean]): Unit = { - val fullMessage = s"Expected $msg, got ${analysis.errors}" - assertTrue(fullMessage, analysis.errors.exists { + assertTrue(s"Expected $msg, got ${analysis.errors}", + containsError(analysis)(pf)) + } + + private def assertNotContainsError(msg: String, analysis: Analysis)( + pf: PartialFunction[Error, Boolean]): Unit = { + assertFalse(s"Did not expect $msg, got ${analysis.errors}", + containsError(analysis)(pf)) + } + + private def containsError(analysis: Analysis)( + pf: PartialFunction[Error, Boolean]): Boolean = { + analysis.errors.exists { e => pf.applyOrElse(e, (_: Error) => false) - }) + } } object ClsInfo { diff --git a/linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala index 73dce25631..1c6ae731b1 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala @@ -446,6 +446,7 @@ object IRCheckerTest { new ClassTransformer { override def transform(tree: Tree): Tree = tree match { case tree: LinkTimeProperty => zeroOf(tree.tpe) + case tree: LinkTimeIf => zeroOf(tree.tpe) case tree: NewLambda => UnaryOp(UnaryOp.Throw, Null()) case _ => super.transform(tree) } diff --git a/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala index 137d8e7400..bb17bcb8f0 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/LibrarySizeTest.scala @@ -70,9 +70,9 @@ class LibrarySizeTest { ) testLinkedSizes( - expectedFastLinkSize = 147046, - expectedFullLinkSizeWithoutClosure = 85355, - expectedFullLinkSizeWithClosure = 21492, + expectedFastLinkSize = 147395, + expectedFullLinkSizeWithoutClosure = 87201, + expectedFullLinkSizeWithClosure = 20680, classDefs, moduleInitializers = MainTestModuleInitializers ) diff --git a/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala index 6441fd0c48..309cc5d7a1 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala @@ -834,6 +834,84 @@ class ClassDefCheckerTest { "Assignment to RecordSelect of illegal tree: org.scalajs.ir.Trees$IntLiteral", previousPhase = CheckingPhase.Optimizer) } + + @Test + def linkTimePropertyTest(): Unit = { + // Test that some illegal types are rejected + for (tpe <- List(FloatType, NullType, NothingType, ClassType(BoxedStringClass, nullable = false))) { + assertError( + mainTestClassDef(LinkTimeProperty("foo")(tpe)), + s"${tpe.show()} is not a valid type for LinkTimeProperty") + } + + // Some error also gets reported if used in link-time-tree position + assertError( + mainTestClassDef { + LinkTimeIf(LinkTimeProperty("foo")(NothingType), int(5), int(6))(IntType) + }, + s"boolean expected but nothing found in link-time tree") + + // LinkTimeProperty is rejected after desugaring + assertError( + mainTestClassDef(LinkTimeProperty("foo")(IntType)), + "Illegal link-time property 'foo' after desugaring", + previousPhase = CheckingPhase.Optimizer) + } + + @Test + def linkTimeIfTest(): Unit = { + def makeTestClassDef(cond: Tree): ClassDef = { + classDef( + "Foo", + superClass = Some(ObjectClass), + methods = List( + trivialCtor("Foo"), + MethodDef(EMF, MethodName("foo", Nil, VoidRef), NON, Nil, VoidType, Some { + LinkTimeIf( + cond, + consoleLog(StringLiteral("foo")), + consoleLog(StringLiteral("bar")) + )(VoidType) + })(EOH, UNV) + ) + ) + } + + assertError( + makeTestClassDef( + UnaryOp(UnaryOp.Boolean_!, int(0)) + ), + "boolean expected but int found in link-time tree" + ) + + assertError( + makeTestClassDef( + BinaryOp(BinaryOp.Int_==, int(0), LinkTimeProperty("core/productionMode")(BooleanType)) + ), + "int expected but boolean found in link-time tree" + ) + + assertError( + makeTestClassDef( + BinaryOp(BinaryOp.Boolean_==, int(0), LinkTimeProperty("core/productionMode")(BooleanType)) + ), + "boolean expected but int found in link-time tree" + ) + + assertError( + makeTestClassDef( + BinaryOp(BinaryOp.===, int(0), int(1)) + ), + "illegal binary op 1 in link-time tree" + ) + + assertError( + makeTestClassDef( + If(BooleanLiteral(true), BooleanLiteral(true), BooleanLiteral(false))(BooleanType) + ), + "illegal tree of class org.scalajs.ir.Trees$If in link-time tree" + ) + } } private object ClassDefCheckerTest { diff --git a/linker/shared/src/test/scala/org/scalajs/linker/frontend/modulesplitter/LinkTimeEvaluatorTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/frontend/modulesplitter/LinkTimeEvaluatorTest.scala new file mode 100644 index 0000000000..79e8d36ff6 --- /dev/null +++ b/linker/shared/src/test/scala/org/scalajs/linker/frontend/modulesplitter/LinkTimeEvaluatorTest.scala @@ -0,0 +1,102 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.linker.frontend + +import org.junit.Test +import org.junit.Assert._ + +import org.scalajs.ir.Trees._ +import org.scalajs.ir.Types._ + +import org.scalajs.linker.interface.{ESFeatures, ESVersion, Semantics, StandardConfig} +import org.scalajs.linker.standard.CoreSpec +import org.scalajs.linker.testutils.TestIRBuilder._ + +class LinkTimeEvaluatorTest { + /** Convenience builder for `LinkTimeProperties` with mostly-default configs. */ + private def make( + semantics: Semantics => Semantics = identity, + esFeatures: ESFeatures => ESFeatures = identity, + isWebAssembly: Boolean = false + ): LinkTimeProperties = { + val config = StandardConfig() + .withSemantics(semantics) + .withESFeatures(esFeatures) + .withExperimentalUseWebAssembly(isWebAssembly) + LinkTimeProperties.fromCoreSpec(CoreSpec.fromStandardConfig(config)) + } + + @Test + def testTryEvalLinkTimeBooleanExpr(): Unit = { + val defaults = make() + + def test(expected: Option[Boolean], tree: Tree, config: LinkTimeProperties = defaults): Unit = + assertEquals(expected, LinkTimeEvaluator.tryEvalLinkTimeBooleanExpr(config, tree)) + + def testTrue(tree: Tree, config: LinkTimeProperties = defaults): Unit = + test(Some(true), tree, config) + + def testFalse(tree: Tree, config: LinkTimeProperties = defaults): Unit = + test(Some(false), tree, config) + + def testFail(tree: Tree, config: LinkTimeProperties = defaults): Unit = + test(None, tree, config) + + // Boolean literal + testTrue(bool(true)) + testFalse(bool(false)) + + // Boolean link-time property + testFalse(LinkTimeProperty("core/isWebAssembly")(BooleanType)) + testTrue(LinkTimeProperty("core/isWebAssembly")(BooleanType), make(isWebAssembly = true)) + testFail(LinkTimeProperty("core/missing")(BooleanType)) + testFail(LinkTimeProperty("core/esVersion")(BooleanType)) + + // Int comparison + for (l <- List(3, 5, 7); r <- List(3, 5, 7)) { + test(Some(l == r), BinaryOp(BinaryOp.Int_==, int(l), int(r))) + test(Some(l != r), BinaryOp(BinaryOp.Int_!=, int(l), int(r))) + test(Some(l < r), BinaryOp(BinaryOp.Int_<, int(l), int(r))) + test(Some(l <= r), BinaryOp(BinaryOp.Int_<=, int(l), int(r))) + test(Some(l > r), BinaryOp(BinaryOp.Int_>, int(l), int(r))) + test(Some(l >= r), BinaryOp(BinaryOp.Int_>=, int(l), int(r))) + } + + // Boolean operator + testTrue(UnaryOp(UnaryOp.Boolean_!, bool(false))) + testFalse(UnaryOp(UnaryOp.Boolean_!, bool(true))) + + // Comparison with link-time property + val esVersionProp = LinkTimeProperty("core/esVersion")(IntType) + testTrue(BinaryOp(BinaryOp.Int_>=, esVersionProp, int(ESVersion.ES2015.edition))) + testFalse(BinaryOp(BinaryOp.Int_>=, esVersionProp, int(ESVersion.ES2019.edition))) + testTrue(BinaryOp(BinaryOp.Int_>=, esVersionProp, int(ESVersion.ES2019.edition)), + make(esFeatures = _.withESVersion(ESVersion.ES2021))) + + // LinkTimeIf + testTrue(LinkTimeIf(bool(true), bool(true), bool(false))(BooleanType)) + testFalse(LinkTimeIf(bool(true), bool(false), bool(true))(BooleanType)) + testFalse(LinkTimeIf(bool(false), bool(true), bool(false))(BooleanType)) + + // Complex expression: esVersion >= ES2016 && esVersion <= ES2019 + val complexExpr = LinkTimeIf( + BinaryOp(BinaryOp.Int_>=, esVersionProp, int(ESVersion.ES2016.edition)), + BinaryOp(BinaryOp.Int_<=, esVersionProp, int(ESVersion.ES2019.edition)), + bool(false))( + BooleanType) + testTrue(complexExpr, make(esFeatures = _.withESVersion(ESVersion.ES2017))) + testTrue(complexExpr, make(esFeatures = _.withESVersion(ESVersion.ES2019))) + testFalse(complexExpr, make(esFeatures = _.withESVersion(ESVersion.ES2015))) + testFalse(complexExpr, make(esFeatures = _.withESVersion(ESVersion.ES2021))) + } +} diff --git a/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRBuilder.scala b/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRBuilder.scala index 7d022a5123..a4284ec897 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRBuilder.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/testutils/TestIRBuilder.scala @@ -196,6 +196,7 @@ object TestIRBuilder { implicit def methodName2MethodIdent(name: MethodName): MethodIdent = MethodIdent(name) + def bool(x: Boolean): BooleanLiteral = BooleanLiteral(x) def int(x: Int): IntLiteral = IntLiteral(x) def str(x: String): StringLiteral = StringLiteral(x) } diff --git a/project/BinaryIncompatibilities.scala b/project/BinaryIncompatibilities.scala index 5435860a02..2e94162e72 100644 --- a/project/BinaryIncompatibilities.scala +++ b/project/BinaryIncompatibilities.scala @@ -5,56 +5,15 @@ import com.typesafe.tools.mima.core.ProblemFilters._ object BinaryIncompatibilities { val IR = Seq( - // !!! Breaking, OK in minor release - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Names.*Class"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Names.ClassInitializerName"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Names.DefaultModuleID"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Names.HijackedClasses"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Names.NoArgConstructorName"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Names.ObjectArgConstructorName"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Names.StaticInitializerName"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types.BoxedClassToPrimType"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types.PrimTypeToBoxedClass"), - - // !!! Breaking, OK in minor release - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.InvalidIRException.tree"), - ProblemFilters.exclude[Problem]("org.scalajs.ir.Trees#Closure.*"), - - // !!! Breaking, PrimRef is not a case class anymore - ProblemFilters.exclude[MissingTypesProblem]("org.scalajs.ir.Types$PrimRef"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types#PrimRef.canEqual"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types#PrimRef.productArity"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types#PrimRef.productElement"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types#PrimRef.productElementName"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types#PrimRef.productElementNames"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types#PrimRef.productIterator"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types#PrimRef.productPrefix"), - ProblemFilters.exclude[IncompatibleResultTypeProblem]("org.scalajs.ir.Types#PrimRef.unapply"), - - // !!! Breaking I guess ... we used to leak public things out of a `case class` with a private[ir] constructor - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types#PrimRef.this"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types#PrimRef.apply"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types#PrimRef.copy"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types#PrimRef.copy$default$1"), - ProblemFilters.exclude[MissingTypesProblem]("org.scalajs.ir.Types$PrimRef$"), - - // constructor of a sealed abstract class, not an issue - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Types#PrimTypeWithRef.this"), - - // private, not an issue - ProblemFilters.exclude[MissingClassProblem]("org.scalajs.ir.Serializers$Deserializer$BodyHack5Transformer$"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.ir.Serializers#Hacks.use*"), ) val Linker = Seq( - // !!! Breaking, OK in minor release - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.linker.standard.LinkedClass.this"), - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.linker.standard.LinkedTopLevelExport.this"), + // private[linker], not an issue + ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.linker.standard.CoreSpec.linkTimeProperties"), + ProblemFilters.exclude[MissingClassProblem]("org.scalajs.linker.standard.LinkTimeProperties*"), ) val LinkerInterface = Seq( - // private, not an issue - ProblemFilters.exclude[DirectMissingMethodProblem]("org.scalajs.linker.interface.Semantics.this"), ) val SbtPlugin = Seq( @@ -64,10 +23,6 @@ object BinaryIncompatibilities { ) val Library = Seq( - // Changes covered by a deserialization hack (and the code cannot be used on the JVM, such as in macros) - ProblemFilters.exclude[AbstractClassProblem]("scala.scalajs.runtime.AnonFunction*"), - ProblemFilters.exclude[DirectMissingMethodProblem]("scala.scalajs.runtime.AnonFunction*.this"), - ProblemFilters.exclude[DirectMissingMethodProblem]("scala.scalajs.runtime.AnonFunction*.apply"), ) val TestInterface = Seq( diff --git a/project/Build.scala b/project/Build.scala index eb8b6b9f2f..0883e88c59 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -205,15 +205,6 @@ object MyScalaJSPlugin extends AutoPlugin { } } }, - - /* The AppVeyor CI build definition is very sensitive to weird characthers - * in its command lines, so we cannot directly spell out the correct - * incantation. Instead, we define this alias. - */ - addCommandAlias( - "setSmallESModulesForAppVeyorCI", - "set testSuite.v2_12 / scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.ESModule).withModuleSplitStyle(ModuleSplitStyle.SmallModulesFor(List(\"org.scalajs.testsuite\"))))" - ), ) override def projectSettings: Seq[Setting[_]] = Def.settings( @@ -396,7 +387,7 @@ object Build { "1.3.0", "1.3.1", "1.4.0", "1.5.0", "1.5.1", "1.6.0", "1.7.0", "1.7.1", "1.8.0", "1.9.0", "1.10.0", "1.10.1", "1.11.0", "1.12.0", "1.13.0", "1.13.1", "1.13.2", "1.14.0", "1.15.0", "1.16.0", "1.17.0", "1.18.0", - "1.18.1", "1.18.2") + "1.18.1", "1.18.2", "1.19.0") val previousVersion = previousVersions.last val previousBinaryCrossVersion = CrossVersion.binaryWith("sjs1_", "") @@ -759,9 +750,21 @@ object Build { val libFileMappings = (PathFinder(prevProducts) ** "*.sjsir") .pair(Path.rebase(prevProducts, outputDir)) + /* Note: we cannot use `linkerImpl` here to load `IRFile`s. That would + * create the circular dependency + * linkerPrivateLibrary/products + * -> linkerImpl + * -> linker/fullClasspath + * -> linkerPrivateLibrary/products + */ val dependencyFiles = { val cp = Attributed.data((internalDependencyClasspath in Compile).value) - (PathFinder(cp) ** "*.sjsir").get + cp.flatMap { entry => + if (entry.getName().endsWith(".jar")) + Seq(entry) + else + (PathFinder(entry) ** "*.sjsir").get + } } FileFunction.cached(s.cacheDirectory / "cleaned-sjsir", @@ -2051,14 +2054,14 @@ object Build { if (!useMinifySizes) { Some(ExpectedSizes( fastLink = 624000 to 625000, - fullLink = 96000 to 97000, + fullLink = 94000 to 95000, fastLinkGz = 75000 to 79000, - fullLinkGz = 25000 to 26000, + fullLinkGz = 24000 to 25000, )) } else { Some(ExpectedSizes( - fastLink = 424000 to 425000, - fullLink = 281000 to 282000, + fastLink = 425000 to 426000, + fullLink = 282000 to 283000, fastLinkGz = 60000 to 61000, fullLinkGz = 43000 to 44000, )) @@ -2068,14 +2071,14 @@ object Build { if (!useMinifySizes) { Some(ExpectedSizes( fastLink = 442000 to 443000, - fullLink = 93000 to 94000, + fullLink = 90000 to 91000, fastLinkGz = 57000 to 58000, - fullLinkGz = 25000 to 26000, + fullLinkGz = 24000 to 25000, )) } else { Some(ExpectedSizes( - fastLink = 299000 to 300000, - fullLink = 257000 to 258000, + fastLink = 301000 to 302000, + fullLink = 258000 to 259000, fastLinkGz = 47000 to 48000, fullLinkGz = 42000 to 43000, )) @@ -2135,7 +2138,6 @@ object Build { List(sharedTestDir / "scala", sharedTestDir / "require-scala2") ::: collectionsEraDependentDirectory(scalaV, sharedTestDir) :: includeIf(sharedTestDir / "require-jdk11", javaV >= 11) ::: - includeIf(sharedTestDir / "require-jdk15", javaV >= 15) ::: includeIf(sharedTestDir / "require-jdk17", javaV >= 17) ::: includeIf(sharedTestDir / "require-jdk21", javaV >= 21) ::: includeIf(testDir / "require-scala2", isJSTest) diff --git a/project/JavalibIRCleaner.scala b/project/JavalibIRCleaner.scala index be6103d72c..f2f6f6a046 100644 --- a/project/JavalibIRCleaner.scala +++ b/project/JavalibIRCleaner.scala @@ -10,10 +10,14 @@ import org.scalajs.ir.WellKnownNames._ import java.io._ import java.net.URI -import java.nio.file.Files +import java.nio._ +import java.nio.file._ +import java.nio.file.attribute._ +import java.util.stream.{Stream, StreamSupport} import scala.collection.immutable.IndexedSeq import scala.collection.mutable +import scala.collection.JavaConverters._ import sbt.{Logger, MessageOnlyException} @@ -41,6 +45,8 @@ import sbt.{Logger, MessageOnlyException} final class JavalibIRCleaner(baseDirectoryURI: URI) { import JavalibIRCleaner._ + type JSTypes = Map[ClassName, Option[JSNativeLoadSpec]] + def cleanIR(dependencyFiles: Seq[File], libFileMappings: Seq[(File, File)], logger: Logger): Set[File] = { @@ -53,9 +59,16 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) { } val jsTypes = { - val dependencyIR = dependencyFiles.iterator.map(readIR(_)) - val libIR = libIRMappings.iterator.map(_._1) - getJSTypes(dependencyIR ++ libIR) + val dependencyIR: Stream[ClassDef] = { + dependencyFiles.asJava.stream().flatMap { file => + if (file.getName().endsWith(".jar")) + readIRJar(file) + else + Stream.of[ClassDef](readIR(file)) + } + } + val libIR: Stream[ClassDef] = libIRMappings.asJava.stream().map(_._1) + getJSTypes(Stream.concat(dependencyIR, libIR)) } val resultBuilder = Set.newBuilder[File] @@ -107,14 +120,32 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) { def errorCount: Int = _errorCount } - private def readIR(file: File): ClassDef = { - import java.nio.ByteBuffer + private def readIR(file: File): ClassDef = + readIR(file.toPath()) - val bytes = Files.readAllBytes(file.toPath()) + private def readIR(path: Path): ClassDef = { + val bytes = Files.readAllBytes(path) val buffer = ByteBuffer.wrap(bytes) Serializers.deserialize(buffer) } + private def readIRJar(jar: File): Stream[ClassDef] = { + def isIRFile(path: Path) = { + val fn = path.getFileName() // null if path is FS root + fn != null && fn.toString().endsWith(".sjsir") + } + + // Open zip/jar file as filesystem. + // The type ascription is necessary on JDK 13+. + val fs = FileSystems.newFileSystem(jar.toPath(), null: ClassLoader) + StreamSupport + .stream(fs.getRootDirectories().spliterator(), /* parallel= */ false) + .flatMap(Files.walk(_)) + .filter(isIRFile(_)) + .map[ClassDef](readIR(_)) + .onClose(() => fs.close()) // only close fs once all IR is read. + } + private def writeIRFile(file: File, tree: ClassDef): Unit = { Files.createDirectories(file.toPath().getParent()) val outputStream = @@ -126,17 +157,19 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) { } } - private def getJSTypes(trees: Iterator[ClassDef]): Map[ClassName, ClassDef] = - trees.filter(_.kind.isJSType).map(t => t.className -> t).toMap + private def getJSTypes(trees: Stream[ClassDef]): JSTypes = { + trees.filter(_.kind.isJSType).reduce[JSTypes]( + Map.empty, (m, v) => m.updated(v.className, v.jsNativeLoadSpec), _ ++ _) + } - private def cleanTree(tree: ClassDef, jsTypes: Map[ClassName, ClassDef], + private def cleanTree(tree: ClassDef, jsTypes: JSTypes, errorManager: ErrorManager): ClassDef = { new ClassDefCleaner(tree.className, jsTypes, errorManager) .cleanClassDef(tree) } private final class ClassDefCleaner(enclosingClassName: ClassName, - jsTypes: Map[ClassName, ClassDef], errorManager: ErrorManager) + jsTypes: JSTypes, errorManager: ErrorManager) extends Transformers.ClassTransformer { def cleanClassDef(tree: ClassDef): ClassDef = { @@ -465,16 +498,13 @@ final class JavalibIRCleaner(baseDirectoryURI: URI) { private def genLoadFromLoadSpecOf(className: ClassName)( implicit pos: Position): Tree = { jsTypes.get(className) match { - case Some(classDef) => - classDef.jsNativeLoadSpec match { - case Some(loadSpec) => - genLoadFromLoadSpec(loadSpec) - case None => - reportError( - s"${className.nameString} does not have a load spec " + - "(this shouldn't have happened at all; bug in the compiler?)") - JSGlobalRef("Object") - } + case Some(Some(loadSpec)) => + genLoadFromLoadSpec(loadSpec) + case Some(None) => + reportError( + s"${className.nameString} does not have a load spec " + + "(this shouldn't have happened at all; bug in the compiler?)") + JSGlobalRef("Object") case None => reportError(s"${className.nameString} is not a JS type") JSGlobalRef("Object") diff --git a/project/MiniLib.scala b/project/MiniLib.scala index 0d447e4e30..69f5a08ca9 100644 --- a/project/MiniLib.scala +++ b/project/MiniLib.scala @@ -22,8 +22,6 @@ object MiniLib { "Double", "String", - "FloatingPointBits", - "Throwable", "StackTrace", "Error", diff --git a/project/NodeJSEnvForcePolyfills.scala b/project/NodeJSEnvForcePolyfills.scala index 6859a3aa7b..899bd79c7d 100644 --- a/project/NodeJSEnvForcePolyfills.scala +++ b/project/NodeJSEnvForcePolyfills.scala @@ -66,6 +66,7 @@ final class NodeJSEnvForcePolyfills(esVersion: ESVersion, config: NodeJSEnv.Conf |delete global.Set; |delete global.Symbol; | + |delete global.DataView; |delete global.Int8Array; |delete global.Int16Array; |delete global.Int32Array; diff --git a/scalalib/overrides-2.12/scala/collection/immutable/Range.scala b/scalalib/overrides-2.12/scala/collection/immutable/Range.scala index e5f4287a76..c4d5f9cdf1 100644 --- a/scalalib/overrides-2.12/scala/collection/immutable/Range.scala +++ b/scalalib/overrides-2.12/scala/collection/immutable/Range.scala @@ -33,7 +33,7 @@ import scala.collection.parallel.immutable.ParRange * `init`) are also permitted on overfull ranges. * * @param start the start of this range. - * @param end the end of the range. For exclusive ranges, e.g. + * @param end the end of the range. For exclusive ranges, e.g. * `Range(0,3)` or `(0 until 3)`, this is one * step past the last one in the range. For inclusive * ranges, e.g. `Range.inclusive(0,3)` or `(0 to 3)`, @@ -67,41 +67,92 @@ extends scala.collection.AbstractSeq[Int] { override def par = new ParRange(this) - private def gap = end.toLong - start.toLong - private def isExact = gap % step == 0 - private def hasStub = isInclusive || !isExact - private def longLength = gap / step + ( if (hasStub) 1 else 0 ) - - // Check cannot be evaluated eagerly because we have a pattern where - // ranges are constructed like: "x to y by z" The "x to y" piece - // should not trigger an exception. So the calculation is delayed, - // which means it will not fail fast for those cases where failing was - // correct. override final val isEmpty = ( - (start > end && step > 0) - || (start < end && step < 0) - || (start == end && !isInclusive) + if (isInclusive) + (if (step >= 0) start > end else start < end) + else + (if (step >= 0) start >= end else start <= end) ) + if (step == 0) throw new IllegalArgumentException("step cannot be 0.") + + /** Number of elements in this range, if it is non-empty. + * + * If the range is empty, `numRangeElements` does not have a meaningful value. + * + * Otherwise, `numRangeElements` is interpreted in the range [1, 2^32], + * respecting modular arithmetics wrt. the unsigned interpretation. + * In other words, it is 0 if the mathematical value should be 2^32, and the + * standard unsigned int encoding of the mathematical value otherwise. + * + * This interpretation allows to represent all values with the correct + * modular arithmetics, which streamlines the usage sites. + */ private val numRangeElements: Int = { - if (step == 0) throw new IllegalArgumentException("step cannot be 0.") - else if (isEmpty) 0 - else { - val len = longLength - if (len > scala.Int.MaxValue) -1 - else len.toInt - } + val stepSign = step >> 31 // if (step >= 0) 0 else -1 + val gap = ((end - start) ^ stepSign) - stepSign // if (step >= 0) (end - start) else -(end - start) + val absStep = (step ^ stepSign) - stepSign // if (step >= 0) step else -step + + /* If `absStep` is a constant 1, `div` collapses to being an alias of + * `gap`. Then `absStep * div` also collapses to `gap` and therefore + * `absStep * div != gap` constant-folds to `false`. + * + * Since most ranges are exclusive, that makes `numRangeElements` an alias + * of `gap`. Moreover, for exclusive ranges with step 1 and start 0 (which + * are the common case), it makes it an alias of `end` and the entire + * computation goes away. + */ + val div = Integer.divideUnsigned(gap, absStep) + if (isInclusive || (absStep * div != gap)) div + 1 else div } - // This field has a sensible value only for non-empty ranges - private val lastElement = step match { - case 1 => if (isInclusive) end else end-1 - case -1 => if (isInclusive) end else end+1 - case _ => - val remainder = (gap % step).toInt - if (remainder != 0) end - remainder - else if (isInclusive) end - else end - step + /** Computes the element of this range after `n` steps from `start`. + * + * `n` is interpreted as an unsigned integer. + * + * If the mathematical result is not within this Range, the result won't + * make sense, but won't error out. + */ + @inline + private[this] def locationAfterN(n: Int): Int = { + /* If `step >= 0`, we interpret `step * n` as an unsigned multiplication, + * and the addition as a mixed `(signed, unsigned) -> signed` operation. + * With those interpretation, they do not overflow, assuming the + * mathematical result is within this Range. + * + * If `step < 0`, we should compute `start - (-step * n)`, with the + * multiplication also interpreted as unsigned, and the subtraction as + * mixed. Again, using those interpreatations, they do not overflow. + * But then modular arithmetics allow us to cancel out the two `-` signs, + * so we end up with the same formula. + */ + start + (step * n) + } + + /** Last element of this non-empty range. + * + * For empty ranges, this value is nonsensical. + */ + private[this] val lastElement: Int = { + /* Since we can assume the range is non-empty, `(numRangeElements - 1)` + * is a valid unsigned value in the full int range. The general formula is + * therefore `locationAfterN(numRangeElements - 1)`. + * + * We special-case 1 and -1 so that, in the happy path where `step` is a + * constant 1 or -1, and we only use `foreach`, we can entirely + * dead-code-eliminate `numRangeElements` and its computation. + * + * When `step` is not constant, it is probably 1 or -1 anyway, so the + * single branch should be predictably true. + * + * `step == 1 || step == -1` + * equiv `(step + 1 == 2) || (step + 1 == 0)` + * equiv `((step + 1) & ~2) == 0` + */ + if (((step + 1) & ~2) == 0) + (if (isInclusive) end else end - step) + else + locationAfterN(numRangeElements - 1) } /** The last element of this range. This method will return the correct value @@ -134,18 +185,31 @@ extends scala.collection.AbstractSeq[Int] def isInclusive = false override def size = length - override def length = if (numRangeElements < 0) fail() else numRangeElements + override def length: Int = + if (isEmpty) 0 + else if (numRangeElements > 0) numRangeElements + else fail() + + // Check cannot be evaluated eagerly because we have a pattern where + // ranges are constructed like: "x to y by z" The "x to y" piece + // should not trigger an exception. So the calculation is delayed, + // which means it will not fail fast for those cases where failing was + // correct. private def fail() = Range.fail(start, end, step, isInclusive) private def validateMaxLength() { - if (numRangeElements < 0) + if (numRangeElements <= 0 && !isEmpty) fail() } final def apply(idx: Int): Int = { - validateMaxLength() - if (idx < 0 || idx >= numRangeElements) throw new IndexOutOfBoundsException(idx.toString) - else start + (step * idx) + /* If length is not valid, numRangeElements <= 0, so the condition is always true. + * We push validateMaxLength() inside the then branch, out of the happy path. + */ + if (idx < 0 || idx >= numRangeElements || isEmpty) { + validateMaxLength() + throw new IndexOutOfBoundsException(idx.toString) + } else locationAfterN(idx) } @inline final override def foreach[@specialized(Unit) U](f: Int => U) { @@ -161,6 +225,14 @@ extends scala.collection.AbstractSeq[Int] } } + /** Is the non-negative value `n` greater or equal to the number of elements + * in this non-empty range? + * + * This method returns nonsensical results if `n < 0` or if `this.isEmpty`. + */ + @inline private[this] def greaterEqualNumRangeElements(n: Int): Boolean = + (n ^ Int.MinValue) > ((numRangeElements - 1) ^ Int.MinValue) // unsigned comparison + /** Creates a new range containing the first `n` elements of this range. * * $doesNotUseBuilders @@ -168,15 +240,11 @@ extends scala.collection.AbstractSeq[Int] * @param n the number of elements to take. * @return a new range consisting of `n` first elements. */ - final override def take(n: Int): Range = ( + final override def take(n: Int): Range = { if (n <= 0 || isEmpty) newEmptyRange(start) - else if (n >= numRangeElements && numRangeElements >= 0) this - else { - // May have more than Int.MaxValue elements in range (numRangeElements < 0) - // but the logic is the same either way: take the first n - new Range.Inclusive(start, locationAfterN(n - 1), step) - } - ) + else if (greaterEqualNumRangeElements(n)) this + else new Range.Inclusive(start, locationAfterN(n - 1), step) + } /** Creates a new range containing all the elements of this range except the first `n` elements. * @@ -185,16 +253,12 @@ extends scala.collection.AbstractSeq[Int] * @param n the number of elements to drop. * @return a new range consisting of all the elements of this range except `n` first elements. */ - final override def drop(n: Int): Range = ( + final override def drop(n: Int): Range = { if (n <= 0 || isEmpty) this - else if (n >= numRangeElements && numRangeElements >= 0) newEmptyRange(end) - else { - // May have more than Int.MaxValue elements (numRangeElements < 0) - // but the logic is the same either way: go forwards n steps, keep the rest - copy(locationAfterN(n), end, step) - } - ) - + else if (greaterEqualNumRangeElements(n)) newEmptyRange(end) + else copy(locationAfterN(n), end, step) + } + /** Creates a new range containing the elements starting at `from` up to but not including `until`. * * $doesNotUseBuilders @@ -203,15 +267,17 @@ extends scala.collection.AbstractSeq[Int] * @param until the element at which to end (not included in the range) * @return a new range consisting of a contiguous interval of values in the old range */ - override def slice(from: Int, until: Int): Range = - if (from <= 0) take(until) - else if (until >= numRangeElements && numRangeElements >= 0) drop(from) + override def slice(from: Int, until: Int): Range = { + if (isEmpty) this + else if (from <= 0) take(until) + else if (greaterEqualNumRangeElements(until) && until >= 0) drop(from) else { val fromValue = locationAfterN(from) if (from >= until) newEmptyRange(fromValue) else new Range.Inclusive(fromValue, locationAfterN(until-1), step) } - + } + /** Creates a new range containing all the elements of this range except the last one. * * $doesNotUseBuilders @@ -249,9 +315,6 @@ extends scala.collection.AbstractSeq[Int] else current.toLong + step } } - // Methods like apply throw exceptions on invalid n, but methods like take/drop - // are forgiving: therefore the checks are with the methods. - private def locationAfterN(n: Int) = start + (step * n) // When one drops everything. Can't ever have unchecked operations // like "end + 1" or "end - 1" because ranges involving Int.{ MinValue, MaxValue } @@ -299,15 +362,9 @@ extends scala.collection.AbstractSeq[Int] * $doesNotUseBuilders */ final override def takeRight(n: Int): Range = { - if (n <= 0) newEmptyRange(start) - else if (numRangeElements >= 0) drop(numRangeElements - n) - else { - // Need to handle over-full range separately - val y = last - val x = y - step.toLong*(n-1) - if ((step > 0 && x < start) || (step < 0 && x > start)) this - else new Range.Inclusive(x.toInt, y, step) - } + if (n <= 0 || isEmpty) newEmptyRange(start) + else if (greaterEqualNumRangeElements(n)) this + else copy(locationAfterN(numRangeElements - n), end, step) } /** Creates a new range consisting of the initial `length - n` elements of the range. @@ -315,14 +372,9 @@ extends scala.collection.AbstractSeq[Int] * $doesNotUseBuilders */ final override def dropRight(n: Int): Range = { - if (n <= 0) this - else if (numRangeElements >= 0) take(numRangeElements - n) - else { - // Need to handle over-full range separately - val y = last - step.toInt*n - if ((step > 0 && y < start) || (step < 0 && y > start)) newEmptyRange(start) - else new Range.Inclusive(start, y.toInt, step) - } + if (n <= 0 || isEmpty) this + else if (greaterEqualNumRangeElements(n)) newEmptyRange(end) + else new Range.Inclusive(start, locationAfterN(numRangeElements - 1 - n), step) } /** Returns the reverse of this range. @@ -340,14 +392,14 @@ extends scala.collection.AbstractSeq[Int] else new Range.Inclusive(start, end, step) final def contains(x: Int) = { - if (x==end && !isInclusive) false + if (isEmpty) false else if (step > 0) { - if (x < start || x > end) false - else (step == 1) || (((x - start) % step) == 0) + if (x < start || x > lastElement) false + else (step == 1) || (Integer.remainderUnsigned(x - start, step) == 0) } else { - if (x < end || x > start) false - else (step == -1) || (((x - start) % step) == 0) + if (x > start || x < lastElement) false + else (step == -1) || (Integer.remainderUnsigned(start - x, -step) == 0) } } @@ -398,8 +450,13 @@ extends scala.collection.AbstractSeq[Int] override def toString = { val preposition = if (isInclusive) "to" else "until" + val stepped = if (step == 1) "" else s" by $step" - val prefix = if (isEmpty) "empty " else if (!isExact) "inexact " else "" + def isInexact = + if (isInclusive) lastElement != end + else (lastElement + step) != end + + val prefix = if (isEmpty) "empty " else if (isInexact) "inexact " else "" s"${prefix}Range $start $preposition $end$stepped" } } @@ -432,16 +489,19 @@ object Range { ) if (isEmpty) 0 else { - // Counts with Longs so we can recognize too-large ranges. - val gap: Long = end.toLong - start.toLong - val jumps: Long = gap / step - // Whether the size of this range is one larger than the - // number of full-sized jumps. - val hasStub = isInclusive || (gap % step != 0) - val result: Long = jumps + ( if (hasStub) 1 else 0 ) - - if (result > scala.Int.MaxValue) -1 - else result.toInt + val stepSign = step >> 31 // if (step >= 0) 0 else -1 + val gap = ((end - start) ^ stepSign) - stepSign // if (step >= 0) (end - start) else -(end - start) + val absStep = (step ^ stepSign) - stepSign // if (step >= 0) step else -step + + val div = Integer.divideUnsigned(gap, absStep) + if (isInclusive) { + if (div == -1) // max unsigned int + -1 // corner case: there are 2^32 elements, which would overflow to 0 + else + div + 1 + } else { + if (absStep * div != gap) div + 1 else div + } } } def count(start: Int, end: Int, step: Int): Int = diff --git a/scalalib/overrides-2.13/scala/collection/immutable/Range.scala b/scalalib/overrides-2.13/scala/collection/immutable/Range.scala index 310c45c079..6e3130a886 100644 --- a/scalalib/overrides-2.13/scala/collection/immutable/Range.scala +++ b/scalalib/overrides-2.13/scala/collection/immutable/Range.scala @@ -81,40 +81,99 @@ sealed abstract class Range( r.asInstanceOf[S with EfficientSplit] } - private[this] def gap = end.toLong - start.toLong - private[this] def isExact = gap % step == 0 - private[this] def hasStub = isInclusive || !isExact - private[this] def longLength = gap / step + ( if (hasStub) 1 else 0 ) - def isInclusive: Boolean final override val isEmpty: Boolean = ( - (start > end && step > 0) - || (start < end && step < 0) - || (start == end && !isInclusive) - ) - + if (isInclusive) + (if (step >= 0) start > end else start < end) + else + (if (step >= 0) start >= end else start <= end) + ) + + if (step == 0) throw new IllegalArgumentException("step cannot be 0.") + + /** Number of elements in this range, if it is non-empty. + * + * If the range is empty, `numRangeElements` does not have a meaningful value. + * + * Otherwise, `numRangeElements` is interpreted in the range [1, 2^32], + * respecting modular arithmetics wrt. the unsigned interpretation. + * In other words, it is 0 if the mathematical value should be 2^32, and the + * standard unsigned int encoding of the mathematical value otherwise. + * + * This interpretation allows to represent all values with the correct + * modular arithmetics, which streamlines the usage sites. + */ private[this] val numRangeElements: Int = { - if (step == 0) throw new IllegalArgumentException("step cannot be 0.") - else if (isEmpty) 0 - else { - val len = longLength - if (len > scala.Int.MaxValue) -1 - else len.toInt - } + val stepSign = step >> 31 // if (step >= 0) 0 else -1 + val gap = ((end - start) ^ stepSign) - stepSign // if (step >= 0) (end - start) else -(end - start) + val absStep = (step ^ stepSign) - stepSign // if (step >= 0) step else -step + + /* If `absStep` is a constant 1, `div` collapses to being an alias of + * `gap`. Then `absStep * div` also collapses to `gap` and therefore + * `absStep * div != gap` constant-folds to `false`. + * + * Since most ranges are exclusive, that makes `numRangeElements` an alias + * of `gap`. Moreover, for exclusive ranges with step 1 and start 0 (which + * are the common case), it makes it an alias of `end` and the entire + * computation goes away. + */ + val div = Integer.divideUnsigned(gap, absStep) + if (isInclusive || (absStep * div != gap)) div + 1 else div } - final def length = if (numRangeElements < 0) fail() else numRangeElements + final def length: Int = + if (isEmpty) 0 + else if (numRangeElements > 0) numRangeElements + else fail() + + /** Computes the element of this range after `n` steps from `start`. + * + * `n` is interpreted as an unsigned integer. + * + * If the mathematical result is not within this Range, the result won't + * make sense, but won't error out. + */ + @inline + private[this] def locationAfterN(n: Int): Int = { + /* If `step >= 0`, we interpret `step * n` as an unsigned multiplication, + * and the addition as a mixed `(signed, unsigned) -> signed` operation. + * With those interpretation, they do not overflow, assuming the + * mathematical result is within this Range. + * + * If `step < 0`, we should compute `start - (-step * n)`, with the + * multiplication also interpreted as unsigned, and the subtraction as + * mixed. Again, using those interpreatations, they do not overflow. + * But then modular arithmetics allow us to cancel out the two `-` signs, + * so we end up with the same formula. + */ + start + (step * n) + } - // This field has a sensible value only for non-empty ranges - private[this] val lastElement = step match { - case 1 => if (isInclusive) end else end-1 - case -1 => if (isInclusive) end else end+1 - case _ => - val remainder = (gap % step).toInt - if (remainder != 0) end - remainder - else if (isInclusive) end - else end - step + /** Last element of this non-empty range. + * + * For empty ranges, this value is nonsensical. + */ + private[this] val lastElement: Int = { + /* Since we can assume the range is non-empty, `(numRangeElements - 1)` + * is a valid unsigned value in the full int range. The general formula is + * therefore `locationAfterN(numRangeElements - 1)`. + * + * We special-case 1 and -1 so that, in the happy path where `step` is a + * constant 1 or -1, and we only use `foreach`, we can entirely + * dead-code-eliminate `numRangeElements` and its computation. + * + * When `step` is not constant, it is probably 1 or -1 anyway, so the + * single branch should be predictably true. + * + * `step == 1 || step == -1` + * equiv `(step + 1 == 2) || (step + 1 == 0)` + * equiv `((step + 1) & ~2) == 0` + */ + if (((step + 1) & ~2) == 0) + (if (isInclusive) end else end - step) + else + locationAfterN(numRangeElements - 1) } /** The last element of this range. This method will return the correct value @@ -168,16 +227,21 @@ sealed abstract class Range( // which means it will not fail fast for those cases where failing was // correct. private[this] def validateMaxLength(): Unit = { - if (numRangeElements < 0) + if (numRangeElements <= 0 && !isEmpty) fail() } private[this] def fail() = Range.fail(start, end, step, isInclusive) @throws[IndexOutOfBoundsException] final def apply(idx: Int): Int = { - validateMaxLength() - if (idx < 0 || idx >= numRangeElements) throw new IndexOutOfBoundsException(s"$idx is out of bounds (min 0, max ${numRangeElements-1})") - else start + (step * idx) + /* If length is not valid, numRangeElements <= 0, so the condition is always true. + * We push validateMaxLength() inside the then branch, out of the happy path. + */ + if (idx < 0 || idx >= numRangeElements || isEmpty) { + validateMaxLength() + val max = if (isEmpty) -1 else numRangeElements - 1 + throw new IndexOutOfBoundsException(s"$idx is out of bounds (min 0, max $max)") + } else locationAfterN(idx) } /*@`inline`*/ final override def foreach[@specialized(Unit) U](f: Int => U): Unit = { @@ -225,48 +289,44 @@ sealed abstract class Range( case _ => super.sameElements(that) } + /** Is the non-negative value `n` greater or equal to the number of elements + * in this non-empty range? + * + * This method returns nonsensical results if `n < 0` or if `this.isEmpty`. + */ + @inline private[this] def greaterEqualNumRangeElements(n: Int): Boolean = + (n ^ Int.MinValue) > ((numRangeElements - 1) ^ Int.MinValue) // unsigned comparison + /** Creates a new range containing the first `n` elements of this range. * * @param n the number of elements to take. * @return a new range consisting of `n` first elements. */ - final override def take(n: Int): Range = + final override def take(n: Int): Range = { if (n <= 0 || isEmpty) newEmptyRange(start) - else if (n >= numRangeElements && numRangeElements >= 0) this - else { - // May have more than Int.MaxValue elements in range (numRangeElements < 0) - // but the logic is the same either way: take the first n - new Range.Inclusive(start, locationAfterN(n - 1), step) - } + else if (greaterEqualNumRangeElements(n)) this + else new Range.Inclusive(start, locationAfterN(n - 1), step) + } /** Creates a new range containing all the elements of this range except the first `n` elements. * * @param n the number of elements to drop. * @return a new range consisting of all the elements of this range except `n` first elements. */ - final override def drop(n: Int): Range = + final override def drop(n: Int): Range = { if (n <= 0 || isEmpty) this - else if (n >= numRangeElements && numRangeElements >= 0) newEmptyRange(end) - else { - // May have more than Int.MaxValue elements (numRangeElements < 0) - // but the logic is the same either way: go forwards n steps, keep the rest - copy(locationAfterN(n), end, step) - } + else if (greaterEqualNumRangeElements(n)) newEmptyRange(end) + else copy(locationAfterN(n), end, step) + } /** Creates a new range consisting of the last `n` elements of the range. * * $doesNotUseBuilders */ final override def takeRight(n: Int): Range = { - if (n <= 0) newEmptyRange(start) - else if (numRangeElements >= 0) drop(numRangeElements - n) - else { - // Need to handle over-full range separately - val y = last - val x = y - step.toLong*(n-1) - if ((step > 0 && x < start) || (step < 0 && x > start)) this - else Range.inclusive(x.toInt, y, step) - } + if (n <= 0 || isEmpty) newEmptyRange(start) + else if (greaterEqualNumRangeElements(n)) this + else copy(locationAfterN(numRangeElements - n), end, step) } /** Creates a new range consisting of the initial `length - n` elements of the range. @@ -274,14 +334,9 @@ sealed abstract class Range( * $doesNotUseBuilders */ final override def dropRight(n: Int): Range = { - if (n <= 0) this - else if (numRangeElements >= 0) take(numRangeElements - n) - else { - // Need to handle over-full range separately - val y = last - step.toInt*n - if ((step > 0 && y < start) || (step < 0 && y > start)) newEmptyRange(start) - else Range.inclusive(start, y.toInt, step) - } + if (n <= 0 || isEmpty) this + else if (greaterEqualNumRangeElements(n)) newEmptyRange(end) + else new Range.Inclusive(start, locationAfterN(numRangeElements - 1 - n), step) } // Advance from the start while we meet the given test @@ -334,22 +389,20 @@ sealed abstract class Range( * @param until the element at which to end (not included in the range) * @return a new range consisting of a contiguous interval of values in the old range */ - final override def slice(from: Int, until: Int): Range = - if (from <= 0) take(until) - else if (until >= numRangeElements && numRangeElements >= 0) drop(from) + final override def slice(from: Int, until: Int): Range = { + if (isEmpty) this + else if (from <= 0) take(until) + else if (greaterEqualNumRangeElements(until) && until >= 0) drop(from) else { val fromValue = locationAfterN(from) if (from >= until) newEmptyRange(fromValue) - else Range.inclusive(fromValue, locationAfterN(until-1), step) + else new Range.Inclusive(fromValue, locationAfterN(until-1), step) } + } // Overridden only to refine the return type final override def splitAt(n: Int): (Range, Range) = (take(n), drop(n)) - // Methods like apply throw exceptions on invalid n, but methods like take/drop - // are forgiving: therefore the checks are with the methods. - private[this] def locationAfterN(n: Int) = start + (step * n) - // When one drops everything. Can't ever have unchecked operations // like "end + 1" or "end - 1" because ranges involving Int.{ MinValue, MaxValue } // will overflow. This creates an exclusive range where start == end @@ -369,13 +422,13 @@ sealed abstract class Range( else new Range.Inclusive(start, end, step) final def contains(x: Int) = { - if (x == end && !isInclusive) false + if (isEmpty) false else if (step > 0) { - if (x < start || x > end) false + if (x < start || x > lastElement) false else (step == 1) || (Integer.remainderUnsigned(x - start, step) == 0) } else { - if (x < end || x > start) false + if (x > start || x < lastElement) false else (step == -1) || (Integer.remainderUnsigned(start - x, -step) == 0) } } @@ -478,7 +531,12 @@ sealed abstract class Range( final override def toString: String = { val preposition = if (isInclusive) "to" else "until" val stepped = if (step == 1) "" else s" by $step" - val prefix = if (isEmpty) "empty " else if (!isExact) "inexact " else "" + + def isInexact = + if (isInclusive) lastElement != end + else (lastElement + step) != end + + val prefix = if (isEmpty) "empty " else if (isInexact) "inexact " else "" s"${prefix}Range $start $preposition $end$stepped" } @@ -549,16 +607,19 @@ object Range { if (isEmpty) 0 else { - // Counts with Longs so we can recognize too-large ranges. - val gap: Long = end.toLong - start.toLong - val jumps: Long = gap / step - // Whether the size of this range is one larger than the - // number of full-sized jumps. - val hasStub = isInclusive || (gap % step != 0) - val result: Long = jumps + ( if (hasStub) 1 else 0 ) - - if (result > scala.Int.MaxValue) -1 - else result.toInt + val stepSign = step >> 31 // if (step >= 0) 0 else -1 + val gap = ((end - start) ^ stepSign) - stepSign // if (step >= 0) (end - start) else -(end - start) + val absStep = (step ^ stepSign) - stepSign // if (step >= 0) step else -step + + val div = Integer.divideUnsigned(gap, absStep) + if (isInclusive) { + if (div == -1) // max unsigned int + -1 // corner case: there are 2^32 elements, which would overflow to 0 + else + div + 1 + } else { + if (absStep * div != gap) div + 1 else div + } } } def count(start: Int, end: Int, step: Int): Int = diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/BooleanJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/BooleanJSTest.scala deleted file mode 100644 index d71141dceb..0000000000 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/BooleanJSTest.scala +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Scala.js (https://www.scala-js.org/) - * - * Copyright EPFL. - * - * Licensed under Apache License 2.0 - * (https://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package org.scalajs.testsuite.compiler - -import org.junit.Test -import org.junit.Assert._ - -import scala.scalajs.js - -class BooleanJSTest { - @Test def primitiveOperationsOnBooleansReturnBoolean(): Unit = { - // FIXME: these tests are completely useless: - // they're constant-folded by scalac. We're not at all testing those - // operations are the IR level, nor, a fortiori, at the JS level - assertEquals(js.typeOf(true & false), "boolean") - assertEquals(js.typeOf(true | false), "boolean") - assertEquals(js.typeOf(true ^ false), "boolean") - } -} diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsJSTest.scala deleted file mode 100644 index 9e9c3d65df..0000000000 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsJSTest.scala +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Scala.js (https://www.scala-js.org/) - * - * Copyright EPFL. - * - * Licensed under Apache License 2.0 - * (https://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package org.scalajs.testsuite.compiler - -import org.junit.Test -import org.junit.Assert._ - -class DefaultMethodsJSTest { - - import DefaultMethodsJSTest._ - - @Test def inheritSimpleDefaultMethod(): Unit = { - class InheritSimpleDefaultMethod extends SimpleInterfaceWithDefault { - def value: Int = 5 - } - - val o = new InheritSimpleDefaultMethod - assertEquals(9, o.foo(4)) - } -} - -object DefaultMethodsJSTest { - trait SimpleInterfaceWithDefault { - def value: Int - - def foo(x: Int): Int = value + x - } -} diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/FloatJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/FloatJSTest.scala deleted file mode 100644 index 054f2bac59..0000000000 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/FloatJSTest.scala +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Scala.js (https://www.scala-js.org/) - * - * Copyright EPFL. - * - * Licensed under Apache License 2.0 - * (https://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package org.scalajs.testsuite.compiler - -import org.junit.Test -import org.junit.Assert._ - -import org.scalajs.testsuite.utils.Requires - -class FloatJSTest { - - @noinline def froundNotInlined(x: Double): Float = - x.toFloat - - @Test def froundForSpecialValues(): Unit = { - assertTrue(froundNotInlined(Double.NaN).isNaN) - assertEquals(Double.PositiveInfinity, 1 / froundNotInlined(0.0).toDouble, 0.0) - assertEquals(Double.NegativeInfinity, 1 / froundNotInlined(-0.0).toDouble, 0.0) - assertEquals(Float.PositiveInfinity, froundNotInlined(Double.PositiveInfinity), 0.0) - assertEquals(Float.NegativeInfinity, froundNotInlined(Double.NegativeInfinity), 0.0) - } - - @Test def froundOverflows(): Unit = { - assertEquals(Double.PositiveInfinity, froundNotInlined(1e200), 0.0) - assertEquals(Double.NegativeInfinity, froundNotInlined(-1e200), 0.0) - } - - @Test def froundUnderflows(): Unit = { - assertEquals(Double.PositiveInfinity, 1 / froundNotInlined(1e-300).toDouble, 0.0) - assertEquals(Double.NegativeInfinity, 1 / froundNotInlined(-1e-300).toDouble, 0.0) - } - - @Test def froundNormalCases(): Unit = { - def test(input: Double, expected: Double): Unit = - assertEquals(expected, input.toFloat.toDouble, 0.0) - - // From MDN documentation - test(0.0, 0.0) - test(1.0, 1.0) - test(1.5, 1.5) - test(1.337, 1.3370000123977661) - test(-4.3, -4.300000190734863) - - // Some bounds - test(Float.MinPositiveValue.toDouble, Float.MinPositiveValue.toDouble) - test(Float.MaxValue.toDouble, Float.MaxValue.toDouble) - test(Float.MinValue.toDouble, Float.MinValue.toDouble) - - // Randomly generated Doubles - test(2.705609035558863E20, 2.7056090763400262E20) - test(-1.447710531503027E15, -1.447710532042752E15) - test(-5.1970024617732836E13, -5.1970022834176E13) - test(1.627661085098256E31, 1.6276610930768024E31) - test(-3.7731947682593834E-32, -3.7731946313230934E-32) - test(34.48229849163326, 34.4822998046875) - test(26.62034396181652, 26.620344161987305) - test(8.198435190113375E-24, 8.198434961596576E-24) - test(-6.079928908440255E-23, -6.079928963558556E-23) - test(3.3756949828462674E-13, 3.37569490589662E-13) - test(-1.2599049874324274E33, -1.2599049641449257E33) - test(6.08574575776438E-10, 6.085745796191588E-10) - test(1.973497969450596E-21, 1.973498047135062E-21) - } - - @Test def intWidenedToFloatWhenComparingToFloat_Issue1878(): Unit = { - val intMax: Int = Int.MaxValue - val float: Float = (Int.MaxValue - 1).toFloat - - assertTrue(intMax == float) - assertFalse(intMax != float) - assertFalse(intMax < float) - assertTrue(intMax <= float) - assertFalse(intMax > float) - assertTrue(intMax >= float) - - assertTrue(float == intMax) - assertFalse(float != intMax) - assertFalse(float < intMax) - assertTrue(float <= intMax) - assertFalse(float > intMax) - assertTrue(float >= intMax) - } -} diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/IntJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/IntJSTest.scala deleted file mode 100644 index 5aea1b01c6..0000000000 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/IntJSTest.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Scala.js (https://www.scala-js.org/) - * - * Copyright EPFL. - * - * Licensed under Apache License 2.0 - * (https://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package org.scalajs.testsuite.compiler - -import org.junit.Test -import org.junit.Assert._ -import org.junit.Assume._ - -import org.scalajs.testsuite.utils.Platform._ - -/* General note on the way these tests are written: - * We leverage the constant folding applied by the Scala compiler to write - * sound tests. We always perform the same operation, on the same operands, - * once in a way constant folding understands, and once in a way it doesn't. - * Since constant folding is performed on the JVM, we know it has the right - * semantics. - */ -class IntJSTest { - - // final val without type ascription to make sure these are constant-folded - final val MinVal = Int.MinValue - final val MaxVal = Int.MaxValue - final val AlmostMinVal = Int.MinValue + 43 - final val AlmostMaxVal = Int.MaxValue - 36 - - @Test def remainder(): Unit = { - def test(a: Int, b: Int, expected: Int): Unit = - assertEquals(expected, a % b) - - test(654, 56, 654 % 56) - test(0, 25, 0 % 25) - test(-36, 13, -36 % 13) - test(-55, -6, -55 % -6) - - test(MinVal, 1, MinVal % 1) - test(MinVal, -1, MinVal % -1) - test(MaxVal, 1, MaxVal % 1) - test(MaxVal, -1, MaxVal % -1) - - test(MaxVal, MinVal, MaxVal % MinVal) - test(MaxVal, MaxVal, MaxVal % MaxVal) - test(MinVal, MaxVal, MinVal % MaxVal) - test(MinVal, MinVal, MinVal % MinVal) - - test(AlmostMaxVal, 2, AlmostMaxVal % 2) - test(AlmostMaxVal, 5, AlmostMaxVal % 5) - test(AlmostMaxVal, -7, AlmostMaxVal % -7) - test(AlmostMaxVal, -14, AlmostMaxVal % -14) - test(AlmostMinVal, 100, AlmostMinVal % 100) - test(AlmostMaxVal, -123, AlmostMaxVal % -123) - } -} diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/LongJSTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/LongJSTest.scala deleted file mode 100644 index 01c4eca7ed..0000000000 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/LongJSTest.scala +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Scala.js (https://www.scala-js.org/) - * - * Copyright EPFL. - * - * Licensed under Apache License 2.0 - * (https://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package org.scalajs.testsuite.compiler - -import scala.scalajs.js - -import org.junit.Test -import org.junit.Assert._ -import org.junit.Assume._ - -import org.scalajs.testsuite.utils.AssertThrows.assertThrows -import org.scalajs.testsuite.utils.Platform._ - -/** Tests the re-patching of native longs */ -class LongJSTest { - @Test def longToJSAny(): Unit = { - val x = 5: js.Any - assertEquals(x, 5L: js.Any) - } - - @Test def asInstanceOfIntWithLongAnyThrows(): Unit = { - assumeTrue("Assumed compliant asInstanceOf", hasCompliantAsInstanceOfs) - - val dyn: Any = 5L - assertThrows(classOf[Exception], dyn.asInstanceOf[Int]) - } - -} diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/OptimizerTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/OptimizerTest.scala index 833ae38e64..dddbd185bd 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/OptimizerTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/compiler/OptimizerTest.scala @@ -20,7 +20,6 @@ import org.junit.Assert._ import org.junit.Assume._ import org.scalajs.testsuite.utils.AssertThrows.assertThrows -import org.scalajs.testsuite.utils.Platform._ class OptimizerTest { import OptimizerTest._ @@ -321,22 +320,16 @@ class OptimizerTest { } @Test def foldingDoubleWithDecimalAndString(): Unit = { - assumeFalse("GCC wrongly optimizes this code", usesClosureCompiler) - assertEquals("1.2323919403474454e+21hello", 1.2323919403474454E21 + "hello") assertEquals("hello1.2323919403474454e+21", "hello" + 1.2323919403474454E21) } @Test def foldingDoubleThatJVMWouldPrintInScientificNotationAndString(): Unit = { - assumeFalse("GCC wrongly optimizes this code", usesClosureCompiler) - assertEquals("123456789012345hello", 123456789012345d + "hello") assertEquals("hello123456789012345", "hello" + 123456789012345d) } @Test def foldingDoublesToString(): Unit = { - assumeFalse("GCC wrongly optimizes this code", usesClosureCompiler) - @noinline def toStringNoInline(v: Double): String = v.toString @inline def test(v: Double): Unit = assertEquals(toStringNoInline(v), v.toString) diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/PrimitivesTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/PrimitivesTest.scala index 0aeed0fdbf..253d3d5cb7 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/PrimitivesTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/jsinterop/PrimitivesTest.scala @@ -12,32 +12,50 @@ package org.scalajs.testsuite.jsinterop +import scala.scalajs.js + import org.junit.Assert._ import org.junit.Test class PrimitivesTest { + @noinline + def assertJSEquals(expected: js.Any, actual: js.Any): Unit = { + assertTrue(s"expected: $expected; but got: $actual", + js.special.strictEquals(actual, expected)) + } + + @Test def primitivesToJSAny(): Unit = { + assertJSEquals(false, false) + assertJSEquals(42, 42.toByte) + assertJSEquals(42, 42.toShort) + assertJSEquals(42, 42) + assertJSEquals(42.0, 42L) // converted to Double! + assertJSEquals(42.0f, 42.0f) + assertJSEquals(42.0, 42.0) + } + @Test def javaBoxedTypesToJSAny(): Unit = { - assertEquals(false, new java.lang.Boolean(false)) - assertNull(null: java.lang.Boolean) + assertJSEquals(false, new java.lang.Boolean(false)) + assertJSEquals(null, null: java.lang.Boolean) - assertEquals(42, new java.lang.Byte(42.toByte)) - assertNull(null: java.lang.Byte) + assertJSEquals(42, new java.lang.Byte(42.toByte)) + assertJSEquals(null, null: java.lang.Byte) - assertEquals(42, new java.lang.Short(42.toShort)) - assertNull(null: java.lang.Short) + assertJSEquals(42, new java.lang.Short(42.toShort)) + assertJSEquals(null, null: java.lang.Short) - assertEquals(42, new java.lang.Integer(42)) - assertNull(null: java.lang.Integer) + assertJSEquals(42, new java.lang.Integer(42)) + assertJSEquals(null, null: java.lang.Integer) - assertEquals(42L, new java.lang.Long(42L)) - assertNull(null: java.lang.Long) + assertJSEquals(42.0, new java.lang.Long(42L)) // converted to Double! + assertJSEquals(null, null: java.lang.Long) - assertEquals(42.0f, new java.lang.Float(42.0f), 0.0f) - assertNull(null: java.lang.Float) + assertJSEquals(42.0f, new java.lang.Float(42.0f)) + assertJSEquals(null, null: java.lang.Float) - assertEquals(42.0, new java.lang.Double(42.0), 0.0) - assertNull(null: java.lang.Double) + assertJSEquals(42.0, new java.lang.Double(42.0)) + assertJSEquals(null, null: java.lang.Double) } } diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkTimeIfTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkTimeIfTest.scala new file mode 100644 index 0000000000..1cca641fbf --- /dev/null +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkTimeIfTest.scala @@ -0,0 +1,95 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.testsuite.library + +import scala.scalajs.js +import scala.scalajs.LinkingInfo._ + +import org.junit.Test +import org.junit.Assert._ +import org.junit.Assume._ + +import org.scalajs.testsuite.utils.Platform + +class LinkTimeIfTest { + @Test def linkTimeIfConst(): Unit = { + // boolean const + assertEquals(1, linkTimeIf(true) { 1 } { 2 }) + assertEquals(2, linkTimeIf(false) { 1 } { 2 }) + } + + @Test def linkTimeIfProp(): Unit = { + locally { + val cond = Platform.isInProductionMode + assertEquals(cond, linkTimeIf(productionMode) { true } { false }) + } + + locally { + val cond = !Platform.isInProductionMode + assertEquals(cond, linkTimeIf(!productionMode) { true } { false }) + } + } + + @Test def linkTimIfIntProp(): Unit = { + locally { + val cond = Platform.assumedESVersion >= ESVersion.ES2015 + assertEquals(cond, linkTimeIf(esVersion >= ESVersion.ES2015) { true } { false }) + } + + locally { + val cond = !(Platform.assumedESVersion < ESVersion.ES2015) + assertEquals(cond, linkTimeIf(!(esVersion < ESVersion.ES2015)) { true } { false }) + } + } + + @Test def linkTimeIfNested(): Unit = { + locally { + val cond = { + Platform.isInProductionMode && + Platform.assumedESVersion >= ESVersion.ES2015 + } + assertEquals(if (cond) 53 else 78, + linkTimeIf(productionMode && esVersion >= ESVersion.ES2015) { 53 } { 78 }) + } + + locally { + val cond = { + Platform.assumedESVersion >= ESVersion.ES2015 && + Platform.assumedESVersion < ESVersion.ES2019 && + Platform.isInProductionMode + } + val result = linkTimeIf(esVersion >= ESVersion.ES2015 && + esVersion < ESVersion.ES2019 && productionMode) { + 53 + } { + 78 + } + assertEquals(if (cond) 53 else 78, result) + } + } + + @Test def exponentOp(): Unit = { + def pow(x: Double, y: Double): Double = { + linkTimeIf(esVersion >= ESVersion.ES2016) { + assertTrue("Took the wrong branch of linkTimeIf when linking for ES 2016+", + esVersion >= ESVersion.ES2016) + (x.asInstanceOf[js.Dynamic] ** y.asInstanceOf[js.Dynamic]).asInstanceOf[Double] + } { + assertFalse("Took the wrong branch of linkTimeIf when linking for ES 2015-", + esVersion >= ESVersion.ES2016) + Math.pow(x, y) + } + } + assertEquals(pow(2.0, 8.0), 256.0, 0) + } +} diff --git a/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/lang/MathTestOnJDK11.scala b/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/lang/MathTestOnJDK11.scala new file mode 100644 index 0000000000..a94c198e9e --- /dev/null +++ b/test-suite/shared/src/test/require-jdk11/org/scalajs/testsuite/javalib/lang/MathTestOnJDK11.scala @@ -0,0 +1,242 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.testsuite.javalib.lang + +import java.math.BigInteger +import java.util.SplittableRandom + +import org.junit.Test +import org.junit.Assert._ + +class MathTestOnJDK11 { + + @noinline + private def hideFromOptimizer(x: Int): Int = x + + @Test def testMultiplyFull(): Unit = { + @inline def test(expected: Long, x: Int, y: Int): Unit = { + assertEquals(expected, Math.multiplyFull(x, y)) + assertEquals(expected, Math.multiplyFull(x, hideFromOptimizer(y))) + assertEquals(expected, Math.multiplyFull(hideFromOptimizer(x), y)) + assertEquals(expected, Math.multiplyFull(hideFromOptimizer(x), hideFromOptimizer(y))) + } + + test(2641928036408725662L, 1942041231, 1360387202) + test(54843908448922272L, 1565939409, 35023008) + test(510471553407128558L, 1283300489, 397780222) + test(-1211162085735907941L, -1990140693, 608581137) + test(-1197265696701533712L, -584098468, 2049766884) + test(203152587796496856L, -1809591416, -112264341) + test(-1869763755321108598L, 1235591906, -1513253483) + test(-737954189546644064L, 675415792, -1092592442) + test(-2570904460570261986L, 1639253754, -1568338309) + test(1106623967126000400L, 2088029790, 529984760) + test(1407516248272451352L, -869881054, -1618055988) + test(-2120367337662071940L, -1558894530, 1360173698) + test(-1464086284066637244L, -1417313902, 1033000722) + test(36729253163312334L, -1673852034, -21942951) + test(-3197007331876781046L, 1876799847, -1703435418) + test(461794994386945009L, -246001091, -1877207099) + test(-1206231192496917804L, 867896526, -1389832954) + test(-1739671893103255929L, -1083992841, 1604873969) + test(-409626127116780624L, 240101424, -1706054551) + test(-3083566560548370936L, -1568530113, 1965895672) + test(-1205028798380605000L, -1201743532, 1002733750) + test(-1328689065035027168L, 929349664, -1429697687) + test(-124212693522020684L, 80893862, -1535502082) + test(-82341860111074830L, -243230690, 338534007) + test(-846837059701860202L, 1959770926, -432110227) + test(335728245390354432L, 506816728, 662425344) + test(745294755971022170L, 1521993302, 489683335) + test(-2370525755201631608L, 2023520366, -1171485988) + test(-1039854583047715776L, 593162592, -1753068378) + test(-152985384388127808L, -635946432, 240563319) + test(-678107568956539050L, 649113254, -1044667575) + test(-3064094283703186444L, -1890896836, 1620444979) + test(1240687269228318870L, -1080325230, -1148438669) + test(-46551523496333580L, 27167878, -1713476610) + test(-2500430606368427103L, 2023288183, -1235825241) + test(92963399778762084L, 896198732, 103730787) + test(2469065794894324667L, 2105111101, 1172890967) + test(172558569988357136L, -142945148, -1207166332) + test(335684786634110970L, -1647598405, -203741874) + test(2406859843746696240L, 2049365815, 1174441296) + test(3100973294006114952L, 1991928152, 1556769651) + test(-335912134649077352L, 866240524, -387781598) + test(84303320581066207L, 75666091, 1114149277) + test(-2623126349572207976L, 1426933667, -1838295928) + test(59139945163750590L, 149344270, 395997417) + test(-105764175098643999L, 68726447, -1538915217) + test(8595303129864000L, 726092025, 11837760) + test(-2958527843471399088L, 1536412078, -1925608296) + test(1532625839159904477L, 867021537, 1767690621) + test(384402376484481316L, 1207235521, 318415396) + test(-219376614576542698L, 1816299166, -120782203) + test(-672138807810988440L, 531516745, -1264567512) + test(-193351903065245331L, 170858169, -1131651499) + test(71263251057597648L, 51058196, 1395725988) + test(-774312974742971385L, 1958551603, -395349795) + test(-1846593638370672048L, 1190143097, -1551572784) + test(240083094242536384L, 1404614968, 170924488) + test(-130950827889833280L, -115480554, 1133964320) + test(128954457719585228L, 735993884, 175211317) + test(364779990580792000L, -668489125, -545678272) + test(107252402494512045L, 759517757, 141211185) + test(3038084150893069044L, -1924640913, -1578519988) + test(760804294233336624L, -728394552, -1044494762) + test(1171051779605774913L, 848233701, 1380576813) + test(-1805862307837393080L, -1385644986, 1303264780) + test(172227703288618734L, -104999826, -1640266559) + test(150448013961014407L, 163398103, 920745169) + test(-671469201380991232L, 650262784, -1032612073) + test(-1325861126942924945L, -1773644581, 747534845) + test(987406376890116568L, -1626507773, -607071416) + test(2918138947401192144L, 1695881208, 1720721318) + test(-2590993826910153940L, -1397240042, 1854365570) + test(954644624447419276L, -1516139806, -629654746) + test(407510452326678620L, -384747652, -1059162935) + test(149866317537821404L, 1530355444, 97929091) + test(922044716091910632L, 968149268, 952378674) + test(-3508732521573808284L, 1825364562, -1922209182) + test(1701723136959404304L, 894776752, 1901841027) + test(-2435876799625512705L, -1276062909, 1908900245) + test(-516933170985379201L, 657063047, -786732983) + test(123334479976750576L, 313765817, 393078128) + test(-1072624004420456775L, -894199299, 1199535725) + test(301682711612188737L, 330918981, 911651277) + test(1790992996470651507L, -1115945231, -1604911197) + test(-2750453268538140155L, 1878389719, -1464261245) + test(758285757353272504L, 1259684942, 601964612) + test(-218581674312137400L, -161533394, 1353167100) + test(-1824007072461951836L, -1244277844, 1465916219) + test(-92753167730460334L, -65368843, 1418920138) + test(-2326636630979491248L, 1124395877, -2069232624) + test(-7380586257943446L, 29715454, -248375349) + test(31319707234597638L, 491995506, 63658523) + test(-1196559502630778250L, -1752963990, 682592175) + test(166065559841839548L, -911521074, -182185102) + test(-1222260378510810100L, 1071539812, -1140657925) + test(57800571165871464L, -257569032, -224408077) + test(332444627169725608L, 1247224172, 266547614) + test(217903869180130650L, 1069161915, 203808110) + test(920425054266935850L, -901689546, -1020778225) + test(-507632632656614388L, 864632142, -587108214) + } + + @Test def testMultiplyHigh(): Unit = { + def test(expected: Long, x: Long, y: Long): Unit = + assertEquals(expected, Math.multiplyHigh(x, y)) + + test(-2514789262281153376L, 8217931296694472096L, -5644933286224084859L) + test(-298247406641127011L, -8034902747807161194L, 684724352445702293L) + test(242644198957550459L, 717019025263929004L, 6242505821226454837L) + test(-1089698470915011537L, -7558081430876177893L, 2659588811568490384L) + test(138675986327040026L, 2362930226177876193L, 1082605148727562445L) + test(-1260260349245855816L, -3350308785473442797L, 6938972380570262589L) + test(-1799534229489533301L, -4097805274432763180L, 8100811327075225922L) + test(437623091041087696L, -2968271773754119013L, -2719670493975918294L) + test(-107841114219899514L, 2013609532543228156L, -987936043452088475L) + test(2757621741022067854L, -7005993850636185311L, -7260803191272031988L) + test(-187671345159116030L, 1781219534362173574L, -1943570237881252419L) + test(-515018730942796014L, 6085558843030314089L, -1561141543105626636L) + test(-119091959391883575L, 7423442237814967910L, -295935339127164155L) + test(18351865713513547L, -1886460125362775846L, -179453657960126825L) + test(3928100041033091765L, 8449838094261471293L, 8575389888485029447L) + test(-7404756889594137L, -89549316594063561L, 1525345591296625693L) + test(714591873345926311L, -2929853068304815970L, -4499165349746322236L) + test(1305977852854305585L, -5568549492657237090L, -4326268312655360053L) + test(-2435010516398991446L, 6443930667478151719L, -6970592660082469124L) + test(2031324595328562735L, 5390460907312723801L, 6951413911530987604L) + test(34713245667458599L, -535353692461820541L, -1196118319182197181L) + test(255381044848343425L, -3176530727082196631L, -1483048388428836603L) + test(6566871520624982L, -33326351213089011L, -3634883324950494373L) + test(156130078476475485L, 687410849583778615L, 4189767446364284457L) + test(1647679448547038188L, 4460502251200507739L, 6814102850116870938L) + test(-2241611115434343963L, 5633894511267143863L, -7339581257068946568L) + test(-93572860194426351L, -1075368508503119813L, 1605137764964203383L) + test(1663347345126188661L, -6330756750592024018L, -4846710115399342760L) + test(-1686630202076061136L, 5124142056960069542L, -6071813649745693328L) + test(728105493712673843L, -8079843401135830331L, -1662306437683128283L) + test(-2030727779883712688L, 4452689522888653156L, -8412963770845872378L) + test(734253555387491804L, 5835084770836409518L, 2321232330529258387L) + test(2018627311798804222L, -7211950082779933827L, -5163250018863045382L) + test(-1244560006523295051L, -7326211205612788508L, 3133690700470219958L) + test(-492070935033321215L, 1614944457187625808L, -5620692751550184667L) + test(319340972880203566L, 2310036532484690677L, 2550090059672932009L) + test(1766280783448332865L, 5949345770128658249L, 5476590340096838859L) + test(2757208297958468913L, -5707089944199929572L, -8911987777945981523L) + test(408328069441815717L, 1242541635079749093L, 6062028975489127199L) + test(-77985829287979398L, -7943526433115400350L, 181101510313367840L) + test(-230121117022373017L, -780391911062895469L, 5439555807140802418L) + test(2588662639521587653L, 7451684432618227097L, 6408268846625040081L) + test(861249002493118404L, 1744344496585548181L, 9107856827493957233L) + test(-2703044944335540474L, 8052570526613861366L, -6192106997771248181L) + test(-2975059248415970510L, 6503508572335523474L, -8438546047759521035L) + test(-370291189062632935L, -8722964233277178137L, 783067156383574516L) + test(-90473002639507852L, 852694261922564555L, -1957245873225555126L) + test(-218977334338454381L, -1819563432425194345L, 2219993418476586419L) + test(-1087231185918604076L, -2941838679159182506L, 6817462690146034563L) + test(-1170480051005916145L, -2771463765488827700L, 7790665067735548924L) + test(-371145713487913188L, 3224241917397787909L, -2123423169279885562L) + test(-502492608136209963L, 1568228348895174267L, -5910716094215359887L) + test(1445926343733049503L, -7706328512722939071L, -3461133686196008644L) + test(-1374053009197983052L, -8787832166727089323L, 2884306814637966447L) + test(-1910150305525172307L, 8663815092401732543L, -4067036686787486282L) + test(2074971709256543740L, 8092193156887080609L, 4730049238662438083L) + test(953725989108917020L, 8492699833366153401L, 2071560232049848145L) + test(334989155711573307L, 1093268576921704206L, 5652279186765632978L) + test(129011196343964709L, 1000276763122669782L, 2379178052852915387L) + test(239042793587178901L, 3208737625070847213L, 1374235525371105170L) + test(127809344420152430L, -7696730067895344868L, -306320508313194466L) + test(-2506455997163955037L, -5731747797284935902L, 8066641092198683254L) + test(3016086034985660469L, -6992699346126002928L, -7956436339922591224L) + test(-1527917483534567268L, -8938885845855254814L, 3153089016969294968L) + test(-1268939936756528050L, 5537112727075101653L, -4227439716695399205L) + test(-37535014067603004L, -8605247800544091240L, 80462389271855887L) + test(-2710920384572235679L, -7926242046619125682L, 6309125338878172023L) + test(-3331830886924716794L, 6823617049086893513L, -9007163096323738999L) + test(1854911433578401793L, -4644835313936852982L, -7366693150982113934L) + test(-3840461794042836575L, 8006480391435326631L, -8848334396141248546L) + test(-1212641710132993432L, -7017377545321262459L, 3187699555205380404L) + test(946047090630044138L, -5829622550331878687L, -2993588077419595837L) + test(3518955178043574292L, -7909090733489625033L, -8207424565425867851L) + test(1231895337081111773L, 2841977238766797132L, 7996002817598962425L) + test(-1649686524869089287L, -3558405071306300052L, 8551962049372852642L) + test(1156466789444347220L, -8077807627762096372L, -2640945152160624636L) + test(-284428196958678125L, 7604654143237097972L, -689942508603024688L) + test(24530734973246035L, -4976536915346383672L, -90929133590073966L) + test(915668791878818L, -4915702564252847L, -3436153355352311231L) + test(-59487608720960501L, 2234272329433906652L, -491145452224512365L) + test(-935777346233643464L, 2234022931260640741L, -7726888105936443458L) + test(-539196324963981948L, 1233384294780865907L, -8064328899098291942L) + test(-302740552339519239L, 1652272762436229815L, -3379936785683182277L) + test(-1602328337662720444L, -5891195966699023422L, 5017273391344774367L) + test(1971437877011804292L, 6123334000940359947L, 5939021122948580484L) + test(3518273874050862283L, -7935043146462869940L, -8178997459486413381L) + test(989386049294028022L, 3631504400505165814L, 5025727419987895939L) + test(1075600553777136761L, 8162668046881939535L, 2430740540606242760L) + test(555876997051543592L, -1422006546765159905L, -7211022146415941068L) + test(1442987791832810570L, 3172003226122803882L, 8391676993961733131L) + test(122174343239443206L, 592078109511582332L, 3806455273225175653L) + test(-555975358284841098L, -2610695041141095892L, 3928430928909536969L) + test(1217820260754824228L, -2566343358431797989L, -8753629401971345682L) + test(-843540703271762806L, 2010390971620435041L, -7740076278033066915L) + test(28227414827282063L, 1691814723551530731L, 307778322255183098L) + test(-3487482743675782331L, 8885183126228404590L, -7240447464066348779L) + test(-641218088086423374L, -5793475349478143447L, 2041673650588512538L) + test(491218135799199820L, -3483174304311045377L, -2601470510458659970L) + test(-61083956648009538L, -331097881159246733L, 3403223576515274855L) + test(-1760654512150512675L, -6642702867806073297L, 4889326503714183951L) + } + +} diff --git a/test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK15.scala b/test-suite/shared/src/test/require-jdk17/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK17.scala similarity index 98% rename from test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK15.scala rename to test-suite/shared/src/test/require-jdk17/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK17.scala index 5bda94aa7a..181f15cccf 100644 --- a/test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK15.scala +++ b/test-suite/shared/src/test/require-jdk17/org/scalajs/testsuite/javalib/io/InputStreamTestOnJDK17.scala @@ -21,7 +21,7 @@ import org.junit.Assume._ import org.scalajs.testsuite.utils.AssertThrows.assertThrows import org.scalajs.testsuite.utils.Platform -class InputStreamTestOnJDK15 { +class InputStreamTestOnJDK17 { /** InputStream that only ever skips max bytes at once */ def lowSkipStream(max: Int, seq: Seq[Int]): InputStream = new SeqInputStreamForTest(seq) { require(max > 0) diff --git a/test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/lang/ConstableTest.scala b/test-suite/shared/src/test/require-jdk17/org/scalajs/testsuite/javalib/lang/ConstableTest.scala similarity index 100% rename from test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/lang/ConstableTest.scala rename to test-suite/shared/src/test/require-jdk17/org/scalajs/testsuite/javalib/lang/ConstableTest.scala diff --git a/test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/lang/ConstantDescTest.scala b/test-suite/shared/src/test/require-jdk17/org/scalajs/testsuite/javalib/lang/ConstantDescTest.scala similarity index 100% rename from test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/lang/ConstantDescTest.scala rename to test-suite/shared/src/test/require-jdk17/org/scalajs/testsuite/javalib/lang/ConstantDescTest.scala diff --git a/test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/lang/StringTestOnJDK15.scala b/test-suite/shared/src/test/require-jdk17/org/scalajs/testsuite/javalib/lang/StringTestOnJDK17.scala similarity index 99% rename from test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/lang/StringTestOnJDK15.scala rename to test-suite/shared/src/test/require-jdk17/org/scalajs/testsuite/javalib/lang/StringTestOnJDK17.scala index 25ed53f386..21ffe9cf1d 100644 --- a/test-suite/shared/src/test/require-jdk15/org/scalajs/testsuite/javalib/lang/StringTestOnJDK15.scala +++ b/test-suite/shared/src/test/require-jdk17/org/scalajs/testsuite/javalib/lang/StringTestOnJDK17.scala @@ -17,7 +17,7 @@ import org.junit.Assert._ import org.scalajs.testsuite.utils.AssertThrows.assertThrows -class StringTestOnJDK15 { +class StringTestOnJDK17 { // indent and transform are available since JDK 12 but we're not testing them separately diff --git a/test-suite/shared/src/test/require-jdk21/org/scalajs/testsuite/javalib/lang/MathTestOnJDK21.scala b/test-suite/shared/src/test/require-jdk21/org/scalajs/testsuite/javalib/lang/MathTestOnJDK21.scala new file mode 100644 index 0000000000..b57216847d --- /dev/null +++ b/test-suite/shared/src/test/require-jdk21/org/scalajs/testsuite/javalib/lang/MathTestOnJDK21.scala @@ -0,0 +1,129 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.testsuite.javalib.lang + +import java.math.BigInteger +import java.util.SplittableRandom + +import org.junit.Test +import org.junit.Assert._ + +class MathTestOnJDK21 { + + @Test def testUnsignedMultiplyHigh(): Unit = { + def test(expected: Long, x: Long, y: Long): Unit = + assertEquals(expected, Math.unsignedMultiplyHigh(x, y)) + + test(-4655528149793241951L, -3491544249150011246L, -1435735621922138183L) + test(4723475310515791226L, -5748086171833985033L, 6861570794713764439L) + test(1844940925490449716L, 2113050876768271470L, -2340575756699630680L) + test(702124944283937448L, 2364425117167296916L, 5477830133394302018L) + test(8604154842195983318L, -765591305295706482L, 8976713476968422536L) + test(721433542721114290L, 2866411935332571322L, 4642772995997153672L) + test(198977852337409286L, 901957528446724377L, 4069474895038101365L) + test(763163907759431674L, 5160783458443768226L, 2727858939653224648L) + test(3387911794594526182L, 4369265381816556396L, -4143208729009548760L) + test(7271333002323704409L, -1757891743212949508L, 8037246439257825352L) + test(2439931237179080842L, -6474169961931828112L, 3759324157819640760L) + test(6061120068222317095L, -2837073725411217492L, 7162734907512678039L) + test(34351029958289581L, 2814331433271609755L, 225156373132738607L) + test(5537345867541993285L, -1010313517008906026L, 5858194527486398591L) + test(-5335982525234939944L, -2717969804981747482L, -3070411578621565733L) + test(8785453866123381013L, -8027031729027070236L, -2893241858030230601L) + test(846995399721837047L, 8016550578873635720L, 1949006273527852101L) + test(4706098605093773886L, -8117794498444021304L, 8404745896106734176L) + test(8161884790484487335L, -1014727160850354519L, 8636992531719324619L) + test(-8513922914337796266L, -7911312134726022659L, -1055125873522512866L) + test(5454317624646219097L, -5009725974720812208L, 7487851886286018345L) + test(2492015203153257923L, 5501297108904060131L, 8356132339400095318L) + test(251150029847529372L, 2825249782885673228L, 1639819725946463245L) + test(701745563060384928L, -862030064654873400L, 736146223360275347L) + test(-7730043035170177278L, -81403377518056314L, -7682541838496528017L) + test(7236963176581899295L, -5477193291802744595L, -8153526534093331950L) + test(626928141670112235L, 1514687386182852255L, 7635095589684134756L) + test(413586791425617421L, 2206621030247415833L, 3457471667819472989L) + test(5275688490004831530L, 5303665000787184389L, -97305454699880484L) + test(-4547690235532216689L, -4468474298913285824L, -104539126294685679L) + test(366277061497431976L, 2447428253923437996L, 2760701647814215009L) + test(6869979725009155017L, -6471585201942397935L, -7864107205517734491L) + test(3791302630224081431L, -3133537597544878918L, 4567115935815546723L) + test(1740958585965607218L, -1557363676960182057L, 1901491749479209603L) + test(1523197267704952893L, 6322993002648583029L, 4443786377646975729L) + test(-5259964406327079828L, -1435127053681489364L, -4147506711722433774L) + test(461442857371067152L, -3905080403415491898L, 585360691015982209L) + test(-5941004277830287560L, -823789104926622437L, -5356420579401640852L) + test(-8706994437197957328L, -7948824177902058651L, -1332242280026816373L) + test(-5824918531825640540L, -1108632841430213984L, -5017854248578702419L) + test(-8207168723011034838L, -6116459797997975723L, -3127808865551126328L) + test(73145162458483087L, 984457260399802488L, 1370592860022769137L) + test(1646786118016093153L, 1899627239649590099L, -2455268783649962915L) + test(3040972539165017704L, -1806601545829941920L, 3371127505138255794L) + test(-7035139036718491109L, -5137998055321179172L, -2629554588180521105L) + test(3814121656202007379L, 6827719277638332778L, -8141966856464601543L) + test(846533671710128614L, 928822269112330348L, -1634281118080467412L) + test(2465515036079121285L, -6936668916098982255L, 3951383831786888133L) + test(-8705991089541820518L, -4627352440152123018L, -5444349891042507930L) + test(-1725081446201736077L, -102123348177150214L, -1631993003537285149L) + test(3162229868737586796L, -3928062681119615377L, 4017778440996329527L) + test(-5644865433298065278L, -1848848427461824206L, -4218857359905179258L) + test(8602545182711575119L, -7248610265558893317L, -4275726644671484627L) + test(574312046131959731L, 3094727123103690882L, 3423302576293014745L) + test(1498569573726223576L, -7522287153787738568L, 2530444268837256898L) + test(9201094795447111684L, -7474596509362715910L, -2977553571653290816L) + test(3390173448745695247L, 4278016163532080543L, -3828364997071413384L) + test(1203823557488246474L, 7917135674481131658L, 2804881208044173681L) + test(78424886362034171L, 980323435081771015L, 1475720926338487149L) + test(2441060259411459766L, 5798689165809470750L, 7765481574589679868L) + test(1218713916031010010L, 1847981405952407601L, -6281414035388066866L) + test(3425204597140630L, 23488433971625858L, 2689999370748729443L) + test(1973267136565988242L, -1929721405614247783L, 2203808433804858703L) + test(68657367104675341L, 1589652507076814732L, 796718071475649415L) + test(3001541031524236691L, 8704517430100436852L, 6360910835079676298L) + test(681950840803061472L, 4077993211593106210L, 3084794892591474962L) + test(-8032203953933386693L, -1297027813828962384L, -7244555458827535130L) + test(1705455955228875706L, 7677829613973803154L, 4097526399626386343L) + test(-7072562212974300041L, -4950443113989053629L, -2900512372222814349L) + test(1553202529265923923L, -5818129186601545240L, 2268778469225152008L) + test(468774576517241981L, 4408923425379013709L, 1961332463044936276L) + test(3713249757582606154L, 3805244009484784339L, -445962050491898861L) + test(-6492270872661604844L, -3676904101986751446L, -3516243262736404968L) + test(-1570352970576497130L, -498180996573724276L, -1101931219921881504L) + test(5938952698782807216L, -2212548245883761340L, 6748368792775950262L) + test(6965609214550264138L, -44143931286210318L, 6982318232414802091L) + test(5427827011873507182L, -6210411243958046988L, 8182658739140523648L) + test(-4010444622072654726L, -2893048189377704445L, -1325236533881556505L) + test(-751397756032611136L, -297794630894262553L, -461046011882216476L) + test(547299415238293930L, 4622152711521934473L, 2184240304182297561L) + test(378891801518625290L, -4298572905176069751L, 494008731657528393L) + test(1743296278846509964L, 2577934180605703203L, -5972360304541326595L) + test(1039517173945592548L, 5277562622670643081L, 3633440025065309218L) + test(2800417145934889950L, 4637163034857372585L, -7306618526608114613L) + test(1678445276921448048L, 4821090766475680470L, 6422167091396679498L) + test(6227359204013573541L, -4999273910373625291L, 8542461897755272268L) + test(-5499015746244333303L, -1511897164914852634L, -4343077705835106861L) + test(2697793722365988629L, 6211672456183761935L, 8011612123978580051L) + test(1911468969140043410L, -5555150943474614346L, 2735145185110353700L) + test(2743615826392469850L, 6136958276954783995L, 8246883342207528968L) + test(1464762398133852646L, -6115223270905044522L, 2191140697019557187L) + test(457204414584490485L, -5068587596916665104L, 630425637481565869L) + test(-6229688730810143122L, -4425525343688714952L, -2373612516159392266L) + test(388393998755070731L, 797622384306174158L, 8982451891732844522L) + test(5110014275194731110L, 8574583813914626477L, -7453426194589668982L) + test(6332629791985833635L, -6221980493997883829L, -8891025443683788065L) + test(1100817927132096284L, -5992086725542985450L, 1630434784827422367L) + test(-6893218526017086868L, -566288691658266700L, -6527308893032530287L) + test(6838523840226613906L, 7591397219780968602L, -1829447485155925067L) + test(2870893831454164280L, 3649310365003545712L, -3934784696536939433L) + } + +} diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/BooleanTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/BooleanTest.scala index 607892890a..196de6b7b5 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/BooleanTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/BooleanTest.scala @@ -16,20 +16,81 @@ import org.junit.Test import org.junit.Assert._ class BooleanTest { - @Test def bitwiseAndOrXorOperators(): Unit = { - assertFalse(false & false) - assertFalse(false & true) - assertFalse(true & false) - assertTrue(true & true) - - assertFalse(false | false) - assertTrue(true | false) - assertTrue(false | true) - assertTrue(true | true) - - assertFalse(false ^ false) - assertTrue(true ^ false) - assertTrue(false ^ true) - assertFalse(true ^ true) + @noinline def hideFromOptimizer(x: Boolean): Boolean = x + + // See #220, #409 + @noinline def anyIsBoolean(x: Any): Boolean = x.isInstanceOf[Boolean] + + @Test def nonShortCircuitingAnd(): Unit = { + @inline def test(expected: Boolean, x: Boolean, y: Boolean): Unit = { + assertEquals(expected, x & y) + assertEquals(expected, hideFromOptimizer(x) & y) + assertEquals(expected, x & hideFromOptimizer(y)) + assertEquals(expected, hideFromOptimizer(x) & hideFromOptimizer(y)) + + assertTrue(anyIsBoolean(hideFromOptimizer(x) & hideFromOptimizer(y))) + } + + test(false, false, false) + test(false, false, true) + test(false, true, false) + test(true, true, true) + } + + @Test def nonShortCircuitingOr(): Unit = { + @inline def test(expected: Boolean, x: Boolean, y: Boolean): Unit = { + assertEquals(expected, x | y) + assertEquals(expected, hideFromOptimizer(x) | y) + assertEquals(expected, x | hideFromOptimizer(y)) + assertEquals(expected, hideFromOptimizer(x) | hideFromOptimizer(y)) + + assertTrue(anyIsBoolean(hideFromOptimizer(x) | hideFromOptimizer(y))) + } + + test(false, false, false) + test(true, false, true) + test(true, true, false) + test(true, true, true) + } + + @Test def xorAkaNotEquals(): Unit = { + @inline def test(expected: Boolean, x: Boolean, y: Boolean): Unit = { + assertEquals(expected, x ^ y) + assertEquals(expected, hideFromOptimizer(x) ^ y) + assertEquals(expected, x ^ hideFromOptimizer(y)) + assertEquals(expected, hideFromOptimizer(x) ^ hideFromOptimizer(y)) + + assertTrue(anyIsBoolean(hideFromOptimizer(x) ^ hideFromOptimizer(y))) + + // != also basically computes xor + + assertEquals(expected, x != y) + assertEquals(expected, hideFromOptimizer(x) != y) + assertEquals(expected, x != hideFromOptimizer(y)) + assertEquals(expected, hideFromOptimizer(x) != hideFromOptimizer(y)) + + assertTrue(anyIsBoolean(hideFromOptimizer(x) != hideFromOptimizer(y))) + } + + test(false, false, false) + test(true, false, true) + test(true, true, false) + test(false, true, true) + } + + @Test def eqEq(): Unit = { + @inline def test(expected: Boolean, x: Boolean, y: Boolean): Unit = { + assertEquals(expected, x == y) + assertEquals(expected, hideFromOptimizer(x) == y) + assertEquals(expected, x == hideFromOptimizer(y)) + assertEquals(expected, hideFromOptimizer(x) == hideFromOptimizer(y)) + + assertTrue(anyIsBoolean(hideFromOptimizer(x) == hideFromOptimizer(y))) + } + + test(true, false, false) + test(false, false, true) + test(false, true, false) + test(true, true, true) } } diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsTest.scala index bd41867292..bb71c8d061 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/DefaultMethodsTest.scala @@ -14,13 +14,11 @@ package org.scalajs.testsuite.compiler import org.junit.Test import org.junit.Assert._ -import org.junit.Assume._ import java.{util => ju} -import org.scalajs.testsuite.utils.Platform._ - class DefaultMethodsTest { + import DefaultMethodsTest._ @Test def canOverrideDefaultMethod(): Unit = { var counter = 0 @@ -58,4 +56,21 @@ class DefaultMethodsTest { assertTrue(reversed.compare(5, 7) > 0) } + + @Test def inheritSimpleDefaultMethod(): Unit = { + class InheritSimpleDefaultMethod extends SimpleInterfaceWithDefault { + def value: Int = 5 + } + + val o = new InheritSimpleDefaultMethod + assertEquals(9, o.foo(4)) + } +} + +object DefaultMethodsTest { + trait SimpleInterfaceWithDefault { + def value: Int + + def foo(x: Int): Int = value + x + } } diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/DoubleTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/DoubleTest.scala index 900212675b..3da6554be4 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/DoubleTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/DoubleTest.scala @@ -16,13 +16,9 @@ import org.junit.Test import org.junit.Assert._ import org.junit.Assume._ -class DoubleTest { - final def assertExactEquals(expected: Double, actual: Double): Unit = - assertTrue(s"expected: $expected; actual: $actual", expected.equals(actual)) - - final def assertExactEquals(msg: String, expected: Float, actual: Float): Unit = - assertTrue(s"$msg; expected: $expected; actual: $actual", expected.equals(actual)) +import org.scalajs.testsuite.utils.AssertExtensions.assertExactEquals +class DoubleTest { @Test def `toInt`(): Unit = { @inline diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/FloatTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/FloatTest.scala index e7b96a7324..8245361b97 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/FloatTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/FloatTest.scala @@ -15,9 +15,11 @@ package org.scalajs.testsuite.compiler import org.junit.Test import org.junit.Assert._ +import org.scalajs.testsuite.utils.AssertExtensions.assertExactEquals + class FloatTest { - final def assertExactEquals(expected: Float, actual: Float): Unit = - assertTrue(s"expected: $expected; actual: $actual", expected.equals(actual)) + @noinline def froundNotInlined(x: Double): Float = + x.toFloat @Test def `toInt`(): Unit = { @@ -286,4 +288,76 @@ class FloatTest { assertExactEquals(0.0f, negate(negate(hide(0.0f)))) assertExactEquals(-0.0f, negate(negate(hide(-0.0f)))) } + + @Test def froundForSpecialValues(): Unit = { + assertTrue(froundNotInlined(Double.NaN).isNaN) + assertEquals(Double.PositiveInfinity, 1 / froundNotInlined(0.0).toDouble, 0.0) + assertEquals(Double.NegativeInfinity, 1 / froundNotInlined(-0.0).toDouble, 0.0) + assertEquals(Float.PositiveInfinity, froundNotInlined(Double.PositiveInfinity), 0.0) + assertEquals(Float.NegativeInfinity, froundNotInlined(Double.NegativeInfinity), 0.0) + } + + @Test def froundOverflows(): Unit = { + assertEquals(Double.PositiveInfinity, froundNotInlined(1e200), 0.0) + assertEquals(Double.NegativeInfinity, froundNotInlined(-1e200), 0.0) + } + + @Test def froundUnderflows(): Unit = { + assertEquals(Double.PositiveInfinity, 1 / froundNotInlined(1e-300).toDouble, 0.0) + assertEquals(Double.NegativeInfinity, 1 / froundNotInlined(-1e-300).toDouble, 0.0) + } + + @Test def froundNormalCases(): Unit = { + @inline + def test(input: Double, expected: Double): Unit = { + assertExactEquals(expected, input.toFloat.toDouble) + assertExactEquals(expected, froundNotInlined(input).toDouble) + } + + // From MDN documentation + test(0.0, 0.0) + test(1.0, 1.0) + test(1.5, 1.5) + test(1.337, 1.3370000123977661) + test(-4.3, -4.300000190734863) + + // Some bounds + test(Float.MinPositiveValue.toDouble, Float.MinPositiveValue.toDouble) + test(Float.MaxValue.toDouble, Float.MaxValue.toDouble) + test(Float.MinValue.toDouble, Float.MinValue.toDouble) + + // Randomly generated Doubles + test(2.705609035558863E20, 2.7056090763400262E20) + test(-1.447710531503027E15, -1.447710532042752E15) + test(-5.1970024617732836E13, -5.1970022834176E13) + test(1.627661085098256E31, 1.6276610930768024E31) + test(-3.7731947682593834E-32, -3.7731946313230934E-32) + test(34.48229849163326, 34.4822998046875) + test(26.62034396181652, 26.620344161987305) + test(8.198435190113375E-24, 8.198434961596576E-24) + test(-6.079928908440255E-23, -6.079928963558556E-23) + test(3.3756949828462674E-13, 3.37569490589662E-13) + test(-1.2599049874324274E33, -1.2599049641449257E33) + test(6.08574575776438E-10, 6.085745796191588E-10) + test(1.973497969450596E-21, 1.973498047135062E-21) + } + + @Test def intWidenedToFloatWhenComparingToFloat_Issue1878(): Unit = { + val intMax: Int = Int.MaxValue + val float: Float = (Int.MaxValue - 1).toFloat + + assertTrue(intMax == float) + assertFalse(intMax != float) + assertFalse(intMax < float) + assertTrue(intMax <= float) + assertFalse(intMax > float) + assertTrue(intMax >= float) + + assertTrue(float == intMax) + assertFalse(float != intMax) + assertFalse(float < intMax) + assertTrue(float <= intMax) + assertFalse(float > intMax) + assertTrue(float >= intMax) + } } diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/IntTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/IntTest.scala index 612c1edf0b..ea66d88a36 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/IntTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/IntTest.scala @@ -309,6 +309,33 @@ class IntTest { assertThrows(classOf[ArithmeticException], 5 / 0) } + @Test def remainder(): Unit = { + def test(a: Int, b: Int, expected: Int): Unit = + assertEquals(expected, a % b) + + test(654, 56, 654 % 56) + test(0, 25, 0 % 25) + test(-36, 13, -36 % 13) + test(-55, -6, -55 % -6) + + test(MinVal, 1, MinVal % 1) + test(MinVal, -1, MinVal % -1) + test(MaxVal, 1, MaxVal % 1) + test(MaxVal, -1, MaxVal % -1) + + test(MaxVal, MinVal, MaxVal % MinVal) + test(MaxVal, MaxVal, MaxVal % MaxVal) + test(MinVal, MaxVal, MinVal % MaxVal) + test(MinVal, MinVal, MinVal % MinVal) + + test(AlmostMaxVal, 2, AlmostMaxVal % 2) + test(AlmostMaxVal, 5, AlmostMaxVal % 5) + test(AlmostMaxVal, -7, AlmostMaxVal % -7) + test(AlmostMaxVal, -14, AlmostMaxVal % -14) + test(AlmostMinVal, 100, AlmostMinVal % 100) + test(AlmostMaxVal, -123, AlmostMaxVal % -123) + } + @Test def moduloByZero(): Unit = { @noinline def modNoInline(x: Int, y: Int): Int = x % y diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/LongTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/LongTest.scala index 108dc817de..604a27307c 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/LongTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/LongTest.scala @@ -16,24 +16,13 @@ import org.junit.Test import org.junit.Assert._ import org.junit.Assume._ +import org.scalajs.testsuite.utils.AssertExtensions.assertExactEquals import org.scalajs.testsuite.utils.AssertThrows.assertThrows import org.scalajs.testsuite.utils.Platform._ class LongTest { import LongTest._ - /* Short builders, used for historical reasons. - * In practice, they are always called with constants, which ensures that the - * optimizer can constant-fold them away at the IR level, without even - * looking inside RuntimeLong. - */ - - @inline def lg(lo: Int, hi: Int): Long = - (hi.toLong << 32) | (lo.toLong & 0xffffffffL) - - @inline def lg(i: Int): Long = - i.toLong - // Helpers @noinline def hideFromOptimizer(x: Long): Long = x @@ -44,28 +33,28 @@ class LongTest { // Common values - def MaxVal: Long = lg(0xffffffff, 0x7fffffff) - def MinVal: Long = lg(0, 0x80000000) - def IntMaxVal: Long = lg(Int.MaxValue) - def IntMinVal: Long = lg(Int.MinValue) - def IntMaxValPlus1: Long = lg(0x80000000, 0) - def IntMinValMinus1: Long = lg(2147483647, -1) - def MaxSafeDouble: Long = lg(-1, 2097151) - def TwoPow53: Long = lg(0, 2097152) - def MinSafeDouble: Long = lg(1, -2097152) - def NegTwoPow53: Long = lg(0, -2097152) + def MaxVal: Long = 0x7fffffffffffffffL + def MinVal: Long = 0x8000000000000000L + def IntMaxVal: Long = 0x000000007fffffffL + def IntMinVal: Long = 0xffffffff80000000L + def IntMaxValPlus1: Long = 0x0000000080000000L + def IntMinValMinus1: Long = 0xffffffff7fffffffL + def MaxSafeDouble: Long = 0x001fffffffffffffL + def TwoPow53: Long = 0x0020000000000000L + def MinSafeDouble: Long = 0xffe0000000000001L + def NegTwoPow53: Long = 0xffe0000000000000L // Tests @Test def sanityOfEqualityTests(): Unit = { - assertEquals(1958505087099L, lg(123, 456)) - assertEquals(528280977864L, lg(456, 123)) - - assertNotEquals(17179869307L, lg(123, 456)) - assertNotEquals(lg(123, 4), lg(123, 456)) - assertNotEquals(1958505086977L, lg(123, 456)) - assertNotEquals(lg(1, 456), lg(123, 456)) - assertNotEquals(123L, lg(123, 456)) + assertEquals(1958505087099L, 1958505087099L) + assertEquals(528280977864L, 528280977864L) + + assertNotEquals(17179869307L, 1958505087099L) + assertNotEquals(17179869307L, 1958505087099L) + assertNotEquals(1958505086977L, 1958505087099L) + assertNotEquals(1958505086977L, 1958505087099L) + assertNotEquals(123L, 1958505087099L) } @Test def equalsAny(): Unit = { @@ -80,18 +69,18 @@ class LongTest { inlineCallEquals(hideFromOptimizer(lhs), hideAnyFromOptimizer(rhs))) } - test(false, lg(0, 0), 0) - test(false, lg(0, 0), null) + test(false, 0L, 0) + test(false, 0L, null) - test(true, lg(0, 0), lg(0, 0)) - test(true, lg(123, 456), lg(123, 456)) - test(true, lg(-123, 456), lg(-123, 456)) - test(true, lg(-123, -456), lg(-123, -456)) + test(true, 0L, 0L) + test(true, 1958505087099L, 1958505087099L) + test(true, 1962800054149L, 1962800054149L) + test(true, -1954210119803L, -1954210119803L) - test(false, lg(123, 456), lg(-123, 456)) - test(false, lg(123, 456), lg(123, -456)) - test(false, lg(-123, -456), lg(123, -456)) - test(false, lg(-123, -456), lg(-123, 456)) + test(false, 1958505087099L, 1962800054149L) + test(false, 1958505087099L, -1958505086853L) + test(false, -1954210119803L, -1958505086853L) + test(false, -1954210119803L, 1962800054149L) } @Test def literals(): Unit = { @@ -236,6 +225,13 @@ class LongTest { assertFalse(dyn.isInstanceOf[Int]) } + @Test def asInstanceOfNegative(): Unit = { + assumeTrue("Assumed compliant asInstanceOf", hasCompliantAsInstanceOfs) + + val dyn: Any = 5L + assertThrows(classOf[Exception], dyn.asInstanceOf[Int]) + } + @Test def compareOtherNumericTypes(): Unit = { assertTrue(5L == 5) assertTrue(5 == 5L) @@ -249,113 +245,113 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x).hashCode()) } - test(0, lg(0)) - test(0, lg(-1)) - test(55, lg(55)) - test(11, lg(-12)) - test(10006548, lg(10006548)) - test(1098747, lg(-1098748)) - - test(957662195, lg(579906195, 461662560)) - test(-1075860794, lg(-1403218312, 327367870)) - test(1425294575, lg(-1152051636, -274640221)) - test(-1863811248, lg(1026519507, -1379463549)) - test(-881942797, lg(363765329, -557842270)) - test(548587254, lg(21652572, 569942698)) - test(-1328999812, lg(55820229, -1281708615)) - test(-1756412154, lg(-1843678104, 89453422)) - test(-529144798, lg(-1928579430, 1836700344)) - test(-1163319584, lg(-181377900, 1335444084)) - test(2070477069, lg(1189983760, 1032146717)) - test(-1718642695, lg(-1982789145, 274636318)) - test(260982265, lg(-2087901827, -1945935740)) - test(-385578983, lg(-1911332808, 1729620001)) - test(-1362397169, lg(-1920965295, 592125278)) - test(1419211160, lg(2017870028, 751907156)) - test(-1851816270, lg(1506336851, -933796127)) - test(112959880, lg(-1747722429, -1855422773)) - test(1715333902, lg(-2139132623, -431847873)) - test(-453690224, lg(739274932, -924496860)) - test(-1503679197, lg(-1482800071, 29485338)) - test(1950154296, lg(237609240, 2048220960)) - test(2037562473, lg(-431092385, -1623412426)) - test(220707473, lg(2144172772, 1927987317)) - test(1902658020, lg(971459211, 1217334127)) - test(840583449, lg(-530209544, -763367967)) - test(2065572837, lg(-1322671605, -902331922)) - test(407536450, lg(1361976000, 1231329666)) - test(-1678479110, lg(-96547475, 1640676759)) - test(-1558558486, lg(1799144078, -936998300)) - test(-110470482, lg(221720683, -195204411)) - test(992932874, lg(2080474705, 1194291803)) - test(2035378556, lg(-1962255291, -228903623)) - test(542449527, lg(-1961045404, -1421226733)) - test(-1824846728, lg(1762001719, -96661681)) - test(-985103709, lg(568630982, -458482587)) - test(37361715, lg(-1237704639, -1275053966)) - test(-1555729529, lg(936273516, -1802824213)) - test(1534845437, lg(-870754516, -1755138351)) - test(-715250396, lg(964079858, -332884522)) - test(2003953821, lg(1769001167, 503396434)) - test(1631287431, lg(811930233, 1365142270)) - test(-1393125048, lg(-280291442, 1136496326)) - test(926193137, lg(439731659, 755060794)) - test(1141998463, lg(-561661919, -1701561506)) - test(480895538, lg(1556104387, 1080665841)) - test(-849143869, lg(1931061917, -1099252386)) - test(-1840233445, lg(2086961898, -298531087)) - test(47538111, lg(-1148008529, -1186490352)) - test(540301593, lg(807317094, 271251327)) - test(1903332829, lg(1077071399, 826295290)) - test(-1325859168, lg(781949710, -1637653074)) - test(-1476869146, lg(1778433204, -839352494)) - test(84316181, lg(-2038023199, -2088719372)) - test(524038724, lg(-1764916235, -1980649039)) - test(-794988445, lg(-1796682086, 1148567289)) - test(-1285356617, lg(-1606200144, 320886535)) - test(1441713710, lg(755146140, 2028753842)) - test(365800340, lg(-1851453861, -2073516593)) - test(2130603708, lg(-543327214, -1587342674)) - test(-1414171289, lg(506958308, -1249713021)) - test(-262714124, lg(-2097389477, 1923820719)) - test(158195454, lg(-374932306, -523558320)) - test(50128093, lg(-902905695, -925752196)) - test(-825145129, lg(-397013030, 646399757)) - test(-1344834498, lg(1764398539, -956440075)) - test(-103814738, lg(-1750710329, 1852419689)) - test(-1354282241, lg(-1664538473, 864969320)) - test(1408148925, lg(-500471847, -1312439708)) - test(1910019874, lg(14748928, 1899600418)) - test(1877620608, lg(-1985642880, -431011584)) - test(-378358620, lg(494530531, -200582329)) - test(492633155, lg(-2067225228, -1718331081)) - test(-1581166836, lg(-1799546135, 897340901)) - test(174532880, lg(25821759, 200092463)) - test(-629188646, lg(403690141, -1032813241)) - test(2139225425, lg(-1843541251, -308529236)) - test(200043623, lg(1643311840, 1780391559)) - test(1992690082, lg(1531597671, 764172997)) - test(754072038, lg(638938496, 182932582)) - test(-139359279, lg(309356043, -440275494)) - test(-1669264515, lg(-541225182, 1128039519)) - test(25583899, lg(-387355169, -378598204)) - test(1822592670, lg(1787244135, 103129337)) - test(1468680630, lg(-1654639624, -890602930)) - test(2103231504, lg(-1867306675, -303043235)) - test(1159389820, lg(1255224728, 265017316)) - test(776506096, lg(119985367, 695098919)) - test(-1303579924, lg(-332671386, 1583817866)) - test(1108767081, lg(1610629865, 571880320)) - test(-1101969936, lg(727577343, -1794328817)) - test(-1022615009, lg(730759795, -394092436)) - test(-1221218252, lg(-148400203, 1074931585)) - test(410005178, lg(181091802, 314250080)) - test(1180107886, lg(-1934827635, -889463837)) - test(425308062, lg(-1067099255, -650316777)) - test(1727927187, lg(1821917070, 174468125)) - test(-759140792, lg(474121453, -830281051)) - test(1698140938, lg(-402668999, -2100801229)) - test(512144461, lg(-615008378, -976157749)) + test(0, 0L) + test(0, -1L) + test(55, 55L) + test(11, -12L) + test(10006548, 10006548L) + test(1098747, -1098748L) + + test(957662195, 1982825597567543955L) + test(-1075860794, 1406034298302928504L) + test(1425294575, -1179570764218296756L) + test(-1863811248, -5924750827952573997L) + test(-881942797, -2395914305612636591L) + test(548587254, 2447885248525657180L) + test(-1328999812, -5504896584370634811L) + test(-1756412154, 384199524456576104L) + test(-529144798, 7888567912398337690L) + test(-1163319584, 5735688670530266260L) + test(2070477069, 4433036395378750992L) + test(-1718642695, 1179554006416034279L) + test(260982265, -8357730361210493571L) + test(-385578983, 7428661341186121784L) + test(-1362397169, 2543158706518910289L) + test(1419211160, 3229416646666240204L) + test(-1851816270, -4010623825090125741L) + test(112959880, -7968980127741386941L) + test(1715333902, -1854772489226326735L) + test(-453690224, -3970683778215415628L) + test(-1503679197, 126638565233673273L) + test(1950154296, 8797042038419333400L) + test(2037562473, -6972503273726145185L) + test(220707473, 8280642475761957604L) + test(1902658020, 5228410264741169803L) + test(840583449, -3278640449314249480L) + test(2065572837, -3875486092154527221L) + test(407536450, 5288520647426579136L) + test(-1678479110, 7046653027410693485L) + test(-1558558486, -4024377053108452722L) + test(-110470482, -838396561058221973L) + test(992932874, 5129444237846349393L) + test(2035378556, -983133572388201403L) + test(542449527, -6104122336102002076L) + test(-1824846728, -415158756909382857L) + test(-985103709, -1969167716381843770L) + test(37361715, -5476315081547833279L) + test(-1555729529, -7743071034335664532L) + test(1534845437, -7538261814076156116L) + test(-715250396, -1429728134370512654L) + test(2003953821, 2162071222722023631L) + test(1631287431, 5863241404849132153L) + test(-1393125048, 4881214556208830350L) + test(926193137, 3242961417161524683L) + test(1141998463, -7308151016669202399L) + test(480895538, 4641424446555440323L) + test(-849143869, -4721253045988906339L) + test(-1840233445, -1282181253417368854L) + test(47538111, -5095937255712569425L) + test(540301593, 1165015579268918886L) + test(1903332829, 3548911248465907239L) + test(-1325859168, -7033666394241918194L) + test(-1476869146, -3604991509767603020L) + test(84316181, -8970981391004714015L) + test(524038724, -8506822844828777483L) + test(-794988445, 4933058946008665754L) + test(-1285356617, 1378197176240526512L) + test(1441713710, 8713431403779497372L) + test(365800340, -8905685952204829093L) + test(2130603708, -6817584868623549422L) + test(-1414171289, -5367476554073402908L) + test(-262714124, 8262747073669783643L) + test(158195454, -2248665858028667730L) + test(50128093, -3976075402628120415L) + test(-825145129, 2776265820355301338L) + test(-1344834498, -4107878840944388661L) + test(-103814738, 7956081985265747911L) + test(-1354282241, 3715014944073787543L) + test(1408148925, -5636885620037294119L) + test(1910019874, 8158721670792678656L) + test(1877620608, -1851180655167832448L) + test(-378358620, -861494542715981853L) + test(492633155, -7380175794367584908L) + test(-1581166836, 3854049825653594857L) + test(174532880, 859390584786911807L) + test(-629188646, -4435899092567076195L) + test(2139225425, -1325122976028439811L) + test(200043623, 7646723521622766304L) + test(1992690082, 3282098032132903783L) + test(754072038, 785689457701776768L) + test(-139359279, -1890968847650888181L) + test(-1669264515, 4844892846454312738L) + test(25583899, -1626066900596724257L) + test(1822592670, 442937131460406887L) + test(1468680630, -3825110455431449608L) + test(2103231504, -1301560781171381939L) + test(1159389820, 1138240706348922264L) + test(776506096, 2985427124709938391L) + test(-1303579924, 6802445941252806246L) + test(1108767081, 2456207273236644585L) + test(-1101969936, -7706583586557791489L) + test(-1022615009, -1692614123490213261L) + test(-1221218252, 4616796007159011253L) + test(410005178, 1349693816546475482L) + test(1180107886, -3820218088529535091L) + test(425308062, -2793089286027256951L) + test(1727927187, 749334892891357070L) + test(-759140792, -3566029960059386643L) + test(1698140938, -9022872570059308487L) + test(512144461, -4192565604012017786L) } @Test def toStringTest(): Unit = { @@ -364,120 +360,120 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x).toString()) } - test("0", lg(0)) - test("1", lg(1)) - test("-1", lg(-1)) + test("0", 0L) + test("1", 1L) + test("-1", -1L) test("2147483647", IntMaxVal) test("2147483648", IntMaxValPlus1) test("-2147483648", IntMinVal) test("-2147483649", IntMinValMinus1) - test("999999999", lg(999999999)) - test("1000000000", lg(1000000000)) + test("999999999", 999999999L) + test("1000000000", 1000000000L) test("9007199254740991", MaxSafeDouble) test("9007199254740992", TwoPow53) test("-9007199254740991", MinSafeDouble) test("-9007199254740992", NegTwoPow53) - test("-86922", lg(-86922, -1)) - test("0", lg(0, 0)) - test("-21874015", lg(-21874015, -1)) - test("-2098921896914", lg(1317110830, -489)) - test("80985205273168", lg(-698060208, 18855)) - test("-12451732102972849", lg(858389071, -2899145)) - test("3350", lg(3350, 0)) - test("-92511590195450", lg(2005360390, -21540)) - test("-2", lg(-2, -1)) - test("446248293253325286", lg(1492984294, 103900277)) - test("499596119314678396", lg(116015740, 116321286)) - test("-3205893", lg(-3205893, -1)) - test("-88762100292970", lg(1988813462, -20667)) - test("-1278004", lg(-1278004, -1)) - test("-1", lg(-1, -1)) - test("-305393", lg(-305393, -1)) - test("-2", lg(-2, -1)) - test("80295210784300943", lg(-1678336113, 18695185)) - test("5", lg(5, 0)) - test("21", lg(21, 0)) - test("64", lg(64, 0)) - test("39146094", lg(39146094, 0)) - test("-1725731", lg(-1725731, -1)) - test("-768047304243556260", lg(-874655652, -178824949)) - test("-2726923242838", lg(380990122, -635)) - test("-1781092907033", lg(1318520807, -415)) - test("-213275", lg(-213275, -1)) - test("7662405832810", lg(184176746, 1784)) - test("-154157877107", lg(460945549, -36)) - test("-929963900939521435", lg(1586508389, -216524094)) - test("-6872", lg(-6872, -1)) - test("31842553544728", lg(-333987816, 7413)) - test("567569520305426", lg(-1817926382, 132147)) - test("19649016", lg(19649016, 0)) - test("-1349346", lg(-1349346, -1)) - test("9479824673588660", lg(-1372338764, 2207193)) - test("3521781", lg(3521781, 0)) - test("1740", lg(1740, 0)) - test("0", lg(0, 0)) - test("92834698468", lg(-1654582044, 21)) - test("-80139798970631138", lg(100400158, -18659001)) - test("30058", lg(30058, 0)) - test("-611022189550002", lg(1332815438, -142265)) - test("514941281681226", lg(472694602, 119894)) - test("2454759250363", lg(-1962042949, 571)) - test("14860137468144958", lg(1595551038, 3459895)) - test("-79255", lg(-79255, -1)) - test("2290122305310796", lg(-1501556660, 533210)) - test("-755641947927852310", lg(-463451414, -175936602)) - test("-2621852156570472370", lg(-771329970, -610447526)) - test("-37956135735", lg(698569929, -9)) - test("853219", lg(853219, 0)) - test("901", lg(901, 0)) - test("4385596303898", lg(434694682, 1021)) - test("-972597865", lg(-972597865, -1)) - test("-8057379", lg(-8057379, -1)) - test("-14968", lg(-14968, -1)) - test("-98204964", lg(-98204964, -1)) - test("335479", lg(335479, 0)) - test("-429441918886", lg(54810714, -100)) - test("9798741", lg(9798741, 0)) - test("135908509698671494", lg(-896875642, 31643665)) - test("-141095409221912371", lg(233027789, -32851335)) - test("-9040837797787104", lg(-359183840, -2104985)) - test("-889", lg(-889, -1)) - test("3222082994", lg(-1072884302, 0)) - test("-1454853", lg(-1454853, -1)) - test("547641844425", lg(-2113969463, 127)) - test("2528132853", lg(-1766834443, 0)) - test("242", lg(242, 0)) - test("-1655763891", lg(-1655763891, -1)) - test("82", lg(82, 0)) - test("-120254181", lg(-120254181, -1)) - test("-210088", lg(-210088, -1)) - test("-2", lg(-2, -1)) - test("250255458324299", lg(598888267, 58267)) - test("-100656997", lg(-100656997, -1)) - test("-24097181761", lg(1672622015, -6)) - test("206088", lg(206088, 0)) - test("-593", lg(-593, -1)) - test("-99542049", lg(-99542049, -1)) - test("421501", lg(421501, 0)) - test("-2", lg(-2, -1)) - test("-101", lg(-101, -1)) - test("3", lg(3, 0)) - test("14967492854", lg(2082590966, 3)) - test("-1528445803513883", lg(-86853659, -355870)) - test("26760588095306", lg(-1353126070, 6230)) - test("12452686330472", lg(1576139368, 2899)) - test("-130630407827875", lg(1022479965, -30415)) - test("-10281777615", lg(-1691843023, -3)) - test("-90497242609445", lg(2013284571, -21071)) - test("-13935178716929", lg(1990158591, -3245)) - test("-11308540", lg(-11308540, -1)) - test("545166", lg(545166, 0)) - test("-1043705339124703", lg(1778574369, -243007)) - test("510", lg(510, 0)) - test("-2485453027", lg(1809514269, -1)) - test("-15103", lg(-15103, -1)) - test("-168776672025670194", lg(-779514418, -39296382)) + test("-86922", -86922L) + test("0", 0L) + test("-21874015", -21874015L) + test("-2098921896914", -2098921896914L) + test("80985205273168", 80985205273168L) + test("-12451732102972849", -12451732102972849L) + test("3350", 3350L) + test("-92511590195450", -92511590195450L) + test("-2", -2L) + test("446248293253325286", 446248293253325286L) + test("499596119314678396", 499596119314678396L) + test("-3205893", -3205893L) + test("-88762100292970", -88762100292970L) + test("-1278004", -1278004L) + test("-1", -1L) + test("-305393", -305393L) + test("-2", -2L) + test("80295210784300943", 80295210784300943L) + test("5", 5L) + test("21", 21L) + test("64", 64L) + test("39146094", 39146094L) + test("-1725731", -1725731L) + test("-768047304243556260", -768047304243556260L) + test("-2726923242838", -2726923242838L) + test("-1781092907033", -1781092907033L) + test("-213275", -213275L) + test("7662405832810", 7662405832810L) + test("-154157877107", -154157877107L) + test("-929963900939521435", -929963900939521435L) + test("-6872", -6872L) + test("31842553544728", 31842553544728L) + test("567569520305426", 567569520305426L) + test("19649016", 19649016L) + test("-1349346", -1349346L) + test("9479824673588660", 9479824673588660L) + test("3521781", 3521781L) + test("1740", 1740L) + test("0", 0L) + test("92834698468", 92834698468L) + test("-80139798970631138", -80139798970631138L) + test("30058", 30058L) + test("-611022189550002", -611022189550002L) + test("514941281681226", 514941281681226L) + test("2454759250363", 2454759250363L) + test("14860137468144958", 14860137468144958L) + test("-79255", -79255L) + test("2290122305310796", 2290122305310796L) + test("-755641947927852310", -755641947927852310L) + test("-2621852156570472370", -2621852156570472370L) + test("-37956135735", -37956135735L) + test("853219", 853219L) + test("901", 901L) + test("4385596303898", 4385596303898L) + test("-972597865", -972597865L) + test("-8057379", -8057379L) + test("-14968", -14968L) + test("-98204964", -98204964L) + test("335479", 335479L) + test("-429441918886", -429441918886L) + test("9798741", 9798741L) + test("135908509698671494", 135908509698671494L) + test("-141095409221912371", -141095409221912371L) + test("-9040837797787104", -9040837797787104L) + test("-889", -889L) + test("3222082994", 3222082994L) + test("-1454853", -1454853L) + test("547641844425", 547641844425L) + test("2528132853", 2528132853L) + test("242", 242L) + test("-1655763891", -1655763891L) + test("82", 82L) + test("-120254181", -120254181L) + test("-210088", -210088L) + test("-2", -2L) + test("250255458324299", 250255458324299L) + test("-100656997", -100656997L) + test("-24097181761", -24097181761L) + test("206088", 206088L) + test("-593", -593L) + test("-99542049", -99542049L) + test("421501", 421501L) + test("-2", -2L) + test("-101", -101L) + test("3", 3L) + test("14967492854", 14967492854L) + test("-1528445803513883", -1528445803513883L) + test("26760588095306", 26760588095306L) + test("12452686330472", 12452686330472L) + test("-130630407827875", -130630407827875L) + test("-10281777615", -10281777615L) + test("-90497242609445", -90497242609445L) + test("-13935178716929", -13935178716929L) + test("-11308540", -11308540L) + test("545166", 545166L) + test("-1043705339124703", -1043705339124703L) + test("510", 510L) + test("-2485453027", -2485453027L) + test("-15103", -15103L) + test("-168776672025670194", -168776672025670194L) } @Test def toByte(): Unit = { @@ -486,30 +482,30 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x).toByte) } - test(0, lg(0)) - test(-1, lg(-1)) - test(0x98.toByte, lg(0xfedcba98, 0x76543210)) - - test(102, lg(-1755353242, -1245269156)) - test(77, lg(-359135667, 1391746928)) - test(-47, lg(-957203503, 1516742479)) - test(-22, lg(-1928741654, 1162703256)) - test(-113, lg(-1698228849, 1497186951)) - test(-84, lg(-68041812, -2115448390)) - test(33, lg(1534301729, 1468418695)) - test(113, lg(1101829489, -514588123)) - test(12, lg(-1437577204, 1896338488)) - test(86, lg(-857671082, -1304076936)) - test(-36, lg(-292818212, -1485650549)) - test(88, lg(1044510040, 147719255)) - test(107, lg(-1166136469, 78076997)) - test(61, lg(500131901, 248541787)) - test(99, lg(1863435363, -1465266670)) - test(-76, lg(136483252, 1662447178)) - test(0, lg(1787939584, 1303926235)) - test(-69, lg(2105657787, 845433223)) - test(26, lg(-1298285542, -1826340261)) - test(64, lg(-766959552, -326327606)) + test(0, 0L) + test(-1, -1L) + test(0x98.toByte, 0x76543210fedcba98L) + + test(102, -5348390297197908122L) + test(77, 5977507544004298317L) + test(-47, 6514359347096730577L) + test(-22, 4993772461838941418L) + test(-113, 6430368993139692943L) + test(-84, -9085781647198927956L) + test(33, 6306810273394300449L) + test(113, -2210139158093195919L) + test(12, 8144711790963478540L) + test(86, -5600967788150588842L) + test(-36, -6380820517237296420L) + test(88, 634449370258994520L) + test(107, 335338151813720939L) + test(61, 1067478847354529853L) + test(99, -6293272425705388957L) + test(-76, 7140156260973973940L) + test(0, 5600320537509350144L) + test(-69, 3631108045842532795L) + test(26, -7844071689366422502L) + test(64, -1401566392023965632L) } @Test def toShort(): Unit = { @@ -518,30 +514,30 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x).toShort) } - test(0, lg(0)) - test(-1, lg(-1)) - test(0xba98.toShort, lg(0xfedcba98, 0x76543210)) - - test(-670, lg(1925512546, -812328457)) - test(-15861, lg(2028716555, -1639243756)) - test(9963, lg(-1970657557, -1904990267)) - test(18394, lg(-1012119590, -1704668195)) - test(-7956, lg(848486636, -810351120)) - test(21453, lg(2103989197, 955793808)) - test(22979, lg(-237938237, -703399620)) - test(8452, lg(666247428, -1109641927)) - test(-26563, lg(1824561213, -872828437)) - test(-5754, lg(-10950266, -1779965318)) - test(11796, lg(1251814932, -491043391)) - test(18020, lg(-117750172, -366379322)) - test(3768, lg(-2095575368, 965048164)) - test(-4579, lg(-177410531, 1454361289)) - test(-29102, lg(-359035310, -790126871)) - test(30020, lg(1486058820, 1675509542)) - test(-13051, lg(268881157, -342358099)) - test(-2720, lg(-1089211040, 747294820)) - test(4726, lg(1163661942, 1708185440)) - test(-16878, lg(-1363821038, -1952481751)) + test(0, 0L) + test(-1, -1L) + test(0xba98.toShort, 0x76543210fedcba98L) + + test(-670, -3488924154499629726L) + test(-15861, -7040498320163487221L) + test(9963, -8181870893638998293L) + test(18394, -7321494144773503014L) + test(-7956, -3480431557828484884L) + test(21453, 4105103149183292365L) + test(22979, -3021078359861798461L) + test(8452, -4765875786069171964L) + test(-26563, -3748769590109235139L) + test(-5754, -7644892824540223098L) + test(11796, -2109015304010125804L) + test(18020, -1573587201743436188L) + test(3768, 4144850305644236472L) + test(-4579, 6246434176940961309L) + test(-29102, -3393569066699878830L) + test(30020, 7196258688511997252L) + test(-13051, -1470416838456849147L) + test(-2720, 3209606815575962976L) + test(4726, 7336600601467032182L) + test(-16878, -8385845263650669038L) } @Test def toInt(): Unit = { @@ -550,30 +546,30 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x).toInt) } - test(0, lg(0)) - test(-1, lg(-1)) - test(0xfedcba98, lg(0xfedcba98, 0x76543210)) - - test(-1869423218, lg(-1869423218, -5516698)) - test(450655357, lg(450655357, -521592408)) - test(-596464514, lg(-596464514, 629510497)) - test(1668957409, lg(1668957409, 1231040344)) - test(-313016061, lg(-313016061, 283507721)) - test(-406779255, lg(-406779255, 1389322213)) - test(-1125423893, lg(-1125423893, -436921025)) - test(1491309031, lg(1491309031, 948401259)) - test(360542935, lg(360542935, -1033853853)) - test(178673916, lg(178673916, -2045867551)) - test(-1167644863, lg(-1167644863, 738699232)) - test(-1852739075, lg(-1852739075, 950841298)) - test(-1965326912, lg(-1965326912, 1694989583)) - test(-141857741, lg(-141857741, -1197558189)) - test(-938893686, lg(-938893686, 1763555645)) - test(-1178638558, lg(-1178638558, 299067184)) - test(-1296424902, lg(-1296424902, -1694453755)) - test(204387309, lg(204387309, -240738711)) - test(-942136876, lg(-942136876, -527367452)) - test(-1703892744, lg(-1703892744, 240186844)) + test(0, 0L) + test(-1, -1L) + test(0xfedcba98, 0x76543210fedcba98L) + + test(-1869423218, -23694035066364530L) + test(450655357, -2240222333751233411L) + test(-596464514, 2703727000802208894L) + test(1668957409, 5287278019205547233L) + test(-313016061, 1217656393840443651L) + test(-406779255, 5967093472329534089L) + test(-1125423893, -1876561510140254997L) + test(1491309031, 4073352392381534695L) + test(360542935, -4440368487118048553L) + test(178673916, -8786934223313938180L) + test(-1167644863, 3172689046147639105L) + test(-1852739075, 4083832281038418429L) + test(-1965326912, 7279924828375317952L) + test(-141857741, -5143473252658877389L) + test(-938893686, 7574413823307259530L) + test(-1178638558, 1284483777703143202L) + test(-1296424902, -7277623459310854086L) + test(204387309, -1033964890421808147L) + test(-942136876, -2265025955962019372L) + test(-1703892744, 1031594642500528376L) } @Test def toLong(): Unit = { @@ -582,66 +578,64 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x).toLong) } - test(0L, lg(0)) - test(-1L, lg(-1)) - test(0x76543210fedcba98L, lg(0xfedcba98, 0x76543210)) - - test(6907420169189163269L, lg(-85753595, 1608259083)) - test(-6558938415102325809L, lg(539593679, -1527121853)) - test(-7633462319206780754L, lg(-379998034, -1777303946)) - test(-4051533910437546682L, lg(-655641274, -943321249)) - test(-3890339056676572253L, lg(1727460259, -905790147)) - test(-3091543614186826784L, lg(1824805856, -719806090)) - test(2806266116723834799L, lg(948567983, 653384746)) - test(-1741184441450532748L, lg(-957910924, -405401095)) - test(3395924718030703835L, lg(-433042213, 790675337)) - test(-7712245542997911283L, lg(889526541, -1795647094)) - test(-2751064647855401745L, lg(1316066543, -640532153)) - test(5225909624054208018L, lg(1913378322, 1216751901)) - test(1334025594846136121L, lg(-434813127, 310602037)) - test(-1574909139329823322L, lg(1689963942, -366687109)) - test(-9142211941778525044L, lg(754250892, -2128587091)) - test(-5517402195275269807L, lg(-1817691823, -1284620305)) - test(7612683537409046411L, lg(-222627957, 1772466007)) - test(-2955859733488660001L, lg(-1282993697, -688214725)) - test(462084382441397543L, lg(799857959, 107587404)) - test(8801656334077465992L, lg(2076251528, 2049295309)) + test(0L, 0L) + test(-1L, -1L) + test(0x76543210fedcba98L, 0x76543210fedcba98L) + + test(6907420169189163269L, 6907420169189163269L) + test(-6558938415102325809L, -6558938415102325809L) + test(-7633462319206780754L, -7633462319206780754L) + test(-4051533910437546682L, -4051533910437546682L) + test(-3890339056676572253L, -3890339056676572253L) + test(-3091543614186826784L, -3091543614186826784L) + test(2806266116723834799L, 2806266116723834799L) + test(-1741184441450532748L, -1741184441450532748L) + test(3395924718030703835L, 3395924718030703835L) + test(-7712245542997911283L, -7712245542997911283L) + test(-2751064647855401745L, -2751064647855401745L) + test(5225909624054208018L, 5225909624054208018L) + test(1334025594846136121L, 1334025594846136121L) + test(-1574909139329823322L, -1574909139329823322L) + test(-9142211941778525044L, -9142211941778525044L) + test(-5517402195275269807L, -5517402195275269807L) + test(7612683537409046411L, 7612683537409046411L) + test(-2955859733488660001L, -2955859733488660001L) + test(462084382441397543L, 462084382441397543L) + test(8801656334077465992L, 8801656334077465992L) } @Test def toFloat(): Unit = { - @inline def test(expected: Float, x: Long, epsilon: Float = 0.0f): Unit = { - assertEquals(expected, x.toFloat, epsilon) - assertEquals(expected, hideFromOptimizer(x).toFloat, epsilon) + @inline def test(expected: Float, x: Long): Unit = { + assertExactEquals(expected, x.toFloat) + assertExactEquals(expected, hideFromOptimizer(x).toFloat) } - test(0, lg(0)) - test(-1, lg(-1)) - - // Closure seems to incorrectly rewrite the constant on the right :-( - val epsilon = if (usesClosureCompiler) 1E4f else 0.0f - test(9.223372E18f, MaxVal, epsilon) - test(-9.223372E18f, MinVal, epsilon) - - test(4.7971489E18f, lg(-1026388143, 1116923232)) - test(-2.24047663E18f, lg(-1288678667, -521651607)) - test(4.59211416E18f, lg(1192262605, 1069184891)) - test(3.38942079E18f, lg(-180353617, 789161022)) - test(-6.8076878E18f, lg(-1158443188, -1585038363)) - test(7.4159717E18f, lg(906981906, 1726665521)) - test(-1.85275997E18f, lg(2042933575, -431379283)) - test(5.7344188E18f, lg(599900903, 1335148382)) - test(3.20410168E18f, lg(1458166084, 746013039)) - test(-7.2310311E18f, lg(1956524672, -1683605603)) - test(7.7151362E18f, lg(478583639, 1796320118)) - test(1.41365268E18f, lg(-1645816617, 329141676)) - test(-3.03197918E18f, lg(184187116, -705937657)) - test(-4.04287594E18f, lg(659513335, -941305424)) - test(-7.8204678E18f, lg(770505156, -1820844549)) - test(-5.9733025E18f, lg(929928858, -1390767911)) - test(1.1261721E18f, lg(-1475096259, 262207373)) - test(4.00884963E18f, lg(787691795, 933383012)) - test(-1.43511611E18f, lg(1189057493, -334139018)) - test(3.81415059E18f, lg(-618946450, 888051141)) + test(0, 0L) + test(-1, -1L) + + test(9.223372E18f, MaxVal) + test(-9.223372E18f, MinVal) + + test(4.7971489E18f, 4797148756851199825L) + test(-2.24047663E18f, -2240476588964556043L) + test(4.59211416E18f, 4592114141414587341L) + test(3.38942079E18f, 3389420784882550191L) + test(-6.8076878E18f, -6807687928853852340L) + test(7.4159717E18f, 7415971944732783122L) + test(-1.85275997E18f, -1852759910613995193L) + test(5.7344188E18f, 5734418636597215975L) + test(3.20410168E18f, 3204101606352738628L) + test(-7.2310311E18f, -7231031002290834816L) + test(7.7151362E18f, 7715136160435444567L) + test(1.41365268E18f, 1413652736819778775L) + test(-3.03197918E18f, -3031979149645678356L) + test(-4.04287594E18f, -4042876010967900169L) + test(-7.8204678E18f, -7820467788284364348L) + test(-5.9733025E18f, -5973302693141309798L) + test(1.1261721E18f, 1126172094624944445L) + test(4.00884963E18f, 4008849511969667347L) + test(-1.43511611E18f, -1435116153438497835L) + test(3.81415059E18f, 3814150611446505582L) // #4466 Long values that are close to Float midpoints @@ -663,39 +657,37 @@ class LongTest { } @Test def toDouble(): Unit = { - @inline def test(expected: Double, x: Long, epsilon: Double = 0.0): Unit = { - assertEquals(expected, x.toDouble, epsilon) - assertEquals(expected, hideFromOptimizer(x).toDouble, epsilon) + @inline def test(expected: Double, x: Long): Unit = { + assertExactEquals(expected, x.toDouble) + assertExactEquals(expected, hideFromOptimizer(x).toDouble) } - test(0, lg(0)) - test(-1, lg(-1)) - - // Closure seems to incorrectly rewrite the constant on the right :-( - val epsilon = if (usesClosureCompiler) 1E4 else 0.0 - test(9.223372036854776E18, MaxVal, epsilon) - test(-9.223372036854776E18, MinVal, epsilon) - - test(3.4240179834317537E18, lg(-151011088, 797216310)) - test(8.5596043411285968E16, lg(-508205099, 19929381)) - test(-3.1630346897289943E18, lg(1249322201, -736451403)) - test(-4.4847682439933604E18, lg(483575860, -1044191477)) - test(-6.4014772289576371E17, lg(-1526343930, -149046007)) - test(-1.76968119148756736E18, lg(531728928, -412036011)) - test(-8.5606671350959739E18, lg(-734111585, -1993185640)) - test(-9.0403963253949932E18, lg(-1407864332, -2104881296)) - test(-6.4988752582247977E18, lg(-1712351423, -1513137310)) - test(-7.7788492399114394E17, lg(1969244733, -181115448)) - test(7.6357174849871442E18, lg(-907683842, 1777829016)) - test(1.25338659134517658E18, lg(-815927209, 291826806)) - test(-3.1910241505692349E18, lg(463523496, -742968207)) - test(7.4216510087652332E18, lg(1482622807, 1727987781)) - test(-8.189046896086654E18, lg(1170040143, -1906661060)) - test(6.8316272807487539E18, lg(-85609173, 1590612176)) - test(-8.0611115909320561E18, lg(-1212811257, -1876873801)) - test(1.7127521901359959E18, lg(-648802816, 398781194)) - test(-6.4442523492577423E18, lg(-1484519186, -1500419423)) - test(-1.71264450938175027E18, lg(-2016996893, -398756124)) + test(0, 0L) + test(-1, -1L) + + test(9.223372036854776E18, MaxVal) + test(-9.223372036854776E18, MinVal) + + test(3.4240179834317537E18, 3424017983431753968L) + test(8.5596043411285968E16, 85596043411285973L) + test(-3.1630346897289943E18, -3163034689728994087L) + test(-4.4847682439933604E18, -4484768243993360332L) + test(-6.4014772289576371E17, -640147722895763706L) + test(-1.76968119148756736E18, -1769681191487567328L) + test(-8.5606671350959739E18, -8560667135095973729L) + test(-9.0403963253949932E18, -9040396325394992652L) + test(-6.4988752582247977E18, -6498875258224797887L) + test(-7.7788492399114394E17, -777884923991143875L) + test(7.6357174849871442E18, 7635717484987144190L) + test(1.25338659134517658E18, 1253386591345176663L) + test(-3.1910241505692349E18, -3191024150569234776L) + test(7.4216510087652332E18, 7421651008765232983L) + test(-8.189046896086654E18, -8189046896086653617L) + test(6.8316272807487539E18, 6831627280748754219L) + test(-8.0611115909320561E18, -8061111590932056057L) + test(1.7127521901359959E18, 1712752190135995904L) + test(-6.4442523492577423E18, -6444252349257742098L) + test(-1.71264450938175027E18, -1712644509381750301L) } @Test def fromDouble(): Unit = { @@ -709,24 +701,21 @@ class LongTest { val twoPow63NextDown = 9.2233720368547748E18 // Specials - test(lg(0), 0.0) - test(lg(0), -0.0) - test(lg(0), Double.NaN) + test(0L, 0.0) + test(0L, -0.0) + test(0L, Double.NaN) test(MaxVal, Double.PositiveInfinity) test(MinVal, Double.NegativeInfinity) // Corner cases - test(lg(0), Double.MinPositiveValue) - test(lg(0), -Double.MinPositiveValue) + test(0L, Double.MinPositiveValue) + test(0L, -Double.MinPositiveValue) test(MaxVal, twoPow63) test(MaxVal, twoPow63NextUp) - if (!usesClosureCompiler) { - // GCC incorrectly rewrites the Double constants on the rhs - test(lg(-1024, 2147483647), twoPow63NextDown) - test(MinVal, -twoPow63) - } + test(9223372036854774784L, twoPow63NextDown) + test(MinVal, -twoPow63) test(MinVal, -twoPow63NextUp) - test(lg(1024, -2147483648), -twoPow63NextDown) + test(-9223372036854774784L, -twoPow63NextDown) // Absolute value too big test(MaxVal, 1.5623101234432471E19) @@ -737,49 +726,49 @@ class LongTest { test(MinVal, -1.500625248806836E19) // Normal cases - test(lg(-235867169, -1408375), -6.048920506403873E15) - test(lg(-69250108, 1979931), 8.503743119053764E15) - test(lg(-305079043, 917242), 3.939528382405885E15) - test(lg(687182505, -933310), -4.008535239847255E15) - test(lg(-268193171, -177333), -7.61635408727443E14) - test(lg(-1529111384, 564485), 2.424447379938472E15) - test(lg(1128309745, -1082296), -4.648424796281871E15) - test(lg(-418524847, 1986827), 8.533360864252241E15) - test(lg(615477490, -646039), -2.774715761463054E15) - test(lg(-1546293262, 815087), 3.500774757068786E15) - test(lg(455797153, -1037726), -4.456998776411743E15) - test(lg(587409995, 1185272), 5.090705064274507E15) - test(lg(-1405692887, -769407), -3.304575013039063E15) - test(lg(667130924, 412), 1.770193656876E12) - test(lg(632602096, -506779), -2.176598598697488E15) - test(lg(1820137888, 955044), 4.101884566378912E15) - test(lg(682339811, 951155), 4.085180300766691E15) - test(lg(1394139649, -1084392), -4.657426781904383E15) - test(lg(-677499131, 663585), 2.850079490584325E15) - test(lg(805667746, 1417318), 6.087335263699874E15) - test(lg(990918920, -1563103), -6.713475274360568E15) - test(lg(-1427573595, 969167), 4.162543436756133E15) - test(lg(-699306484, -1852353), -7.955791959986676E15) - test(lg(-1807820942, 1218020), 5.231358553020274E15) - test(lg(1243383338, 349241), 1.499979916805674E15) - test(lg(-479557118, 1183372), 5.08254785441229E15) - test(lg(1413560577, 654135), 2.809489845729537E15) - test(lg(-2047369879, 1135596), 4.877349929065833E15) - test(lg(-741161617, -1594192), -6.846998949739153E15) - test(lg(-2115502919, 1443312), 6.198980017388729E15) - test(lg(1015092168, 1152178), 4.948567844262856E15) - test(lg(-1340352375, -863152), -3.707206656862071E15) - test(lg(1990353383, -2017544), -8.665283507887641E15) - test(lg(-1683508387, -666397), -2.862150709693603E15) - test(lg(2095665836, 369587), 1.587366173692588E15) - test(lg(229204175, 77510), 3.32903144317135E14) - test(lg(-1988104885, 1374301), 5.902580156722507E15) - test(lg(-1032158224, -233238), -1.001746319375376E15) - test(lg(1321723055, -121058), -5.19938829196113E14) - test(lg(-1959869514, -1892991), -8.130332101524554E15) - test(lg(-1173650161, -412038), -1.769686613392113E15) - test(lg(-1692936735, -1697943), -7.292607053441567E15) - test(lg(-1368921565, 621023), 2.667276401109539E15) + test(-6048920506403873L, -6.048920506403873E15) + test(8503743119053764L, 8.503743119053764E15) + test(3939528382405885L, 3.939528382405885E15) + test(-4008535239847255L, -4.008535239847255E15) + test(-761635408727443L, -7.61635408727443E14) + test(2424447379938472L, 2.424447379938472E15) + test(-4648424796281871L, -4.648424796281871E15) + test(8533360864252241L, 8.533360864252241E15) + test(-2774715761463054L, -2.774715761463054E15) + test(3500774757068786L, 3.500774757068786E15) + test(-4456998776411743L, -4.456998776411743E15) + test(5090705064274507L, 5.090705064274507E15) + test(-3304575013039063L, -3.304575013039063E15) + test(1770193656876L, 1.770193656876E12) + test(-2176598598697488L, -2.176598598697488E15) + test(4101884566378912L, 4.101884566378912E15) + test(4085180300766691L, 4.085180300766691E15) + test(-4657426781904383L, -4.657426781904383E15) + test(2850079490584325L, 2.850079490584325E15) + test(6087335263699874L, 6.087335263699874E15) + test(-6713475274360568L, -6.713475274360568E15) + test(4162543436756133L, 4.162543436756133E15) + test(-7955791959986676L, -7.955791959986676E15) + test(5231358553020274L, 5.231358553020274E15) + test(1499979916805674L, 1.499979916805674E15) + test(5082547854412290L, 5.08254785441229E15) + test(2809489845729537L, 2.809489845729537E15) + test(4877349929065833L, 4.877349929065833E15) + test(-6846998949739153L, -6.846998949739153E15) + test(6198980017388729L, 6.198980017388729E15) + test(4948567844262856L, 4.948567844262856E15) + test(-3707206656862071L, -3.707206656862071E15) + test(-8665283507887641L, -8.665283507887641E15) + test(-2862150709693603L, -2.862150709693603E15) + test(1587366173692588L, 1.587366173692588E15) + test(332903144317135L, 3.32903144317135E14) + test(5902580156722507L, 5.902580156722507E15) + test(-1001746319375376L, -1.001746319375376E15) + test(-519938829196113L, -5.19938829196113E14) + test(-8130332101524554L, -8.130332101524554E15) + test(-1769686613392113L, -1.769686613392113E15) + test(-7292607053441567L, -7.292607053441567E15) + test(2667276401109539L, 2.667276401109539E15) } @Test def comparisons(): Unit = { @@ -801,81 +790,81 @@ class LongTest { testInner(hideFromOptimizer(x), hideFromOptimizer(y), expected) } - test(lg(0), lg(0), 0) - test(lg(0), lg(1), -1) - test(lg(0), lg(-1), 1) + test(0L, 0L, 0) + test(0L, 1L, -1) + test(0L, -1L, 1) test(MaxVal, MinVal, 1) test(MinVal, MaxVal, -1) // Positive and negative numbers requiring lo to be compared via unsigned - test(lg(0x87654321, 0x654789ab), lg(0x12345678, 0x654789ab), 1) - test(lg(0x87654321, 0x89abcdef), lg(0x12345678, 0x89abcdef), 1) + test(0x654789ab87654321L, 0x654789ab12345678L, 1) + test(0x89abcdef87654321L, 0x89abcdef12345678L, 1) // Whitebox corner cases - test(lg(-1, 0), lg(0, 0), 1) - test(lg(0, 0), lg(-1, 0), -1) - - test(lg(173547161, -1884162399), lg(173547161, -1884162399), 0) - test(lg(-1131022787, -472928681), lg(-1131022787, -472928681), 0) - test(lg(-1426164191, 1230100202), lg(-1426164191, 1230100202), 0) - test(lg(-865774626, 1656835920), lg(-865774626, 1656835920), 0) - test(lg(323675568, -725625271), lg(323675568, -725625271), 0) - test(lg(-480943595, -1454872354), lg(-480943595, -1454872354), 0) - test(lg(-626788852, 1037229194), lg(-626788852, 1037229194), 0) - test(lg(-717389653, 232764759), lg(-717389653, 232764759), 0) - test(lg(-861190423, -1233377930), lg(-861190423, -1233377930), 0) - test(lg(-424759090, 2081288998), lg(-424759090, 2081288998), 0) - - test(lg(-1092215366, 753517982), lg(349136582, -103427916), 1) - test(lg(363609757, -1151024787), lg(472951646, -1802702403), 1) - test(lg(604332601, 1869576376), lg(1642523661, 1083165388), 1) - test(lg(309732766, 1349689861), lg(1287300335, 1464464808), -1) - test(lg(-1309668929, -965374553), lg(-1952664258, 53355972), -1) - test(lg(1881957750, 388099413), lg(1843907319, -1819358211), 1) - test(lg(-969542710, 864289013), lg(-1025874755, 1102102911), -1) - test(lg(-1425636748, -220185411), lg(1184140796, 40447497), -1) - test(lg(242386079, 452246653), lg(435337552, -956883630), 1) - test(lg(-1007383056, 344856628), lg(-195994328, 635205577), -1) - test(lg(-1652098619, 2042392045), lg(819672742, -2139008380), 1) - test(lg(1423590080, 1919857862), lg(918443721, 1202178673), 1) - test(lg(-1726296442, 302493002), lg(314727886, 1583734481), -1) - test(lg(-2124336701, 769721099), lg(461146322, -591528218), 1) - test(lg(1544826993, -689540243), lg(-1107003972, -1622786326), 1) - test(lg(2050227802, 951848379), lg(-774454951, 1675192386), -1) - test(lg(251298779, -327163776), lg(767615943, 1531730165), -1) - test(lg(1890888425, 761833495), lg(1870917399, 2027251288), -1) - test(lg(594868313, 126374530), lg(-1567484882, -1199917303), 1) - test(lg(-914360997, -703435655), lg(2049249771, -1581791194), 1) - test(lg(-732484281, -738997306), lg(1445589646, 1910084021), -1) - test(lg(340771740, 1351224018), lg(459324247, 1301544548), 1) - test(lg(-940710332, 1344186742), lg(-1143672211, 1112189558), 1) - test(lg(-804347876, 364046111), lg(-4317439, -1733157379), 1) - test(lg(914214836, -1226397169), lg(-299522125, 1393423940), -1) - test(lg(1244546642, 1821771770), lg(44151604, -1398558064), 1) - test(lg(-2094640323, -1469168677), lg(-263524564, 88152070), -1) - test(lg(-124567753, -93039352), lg(-200449699, -30383890), -1) - test(lg(161119306, -1098626173), lg(-137189625, 1289988889), -1) - test(lg(-2052616761, 846341515), lg(-150583666, 1044666783), -1) - test(lg(-10359669, -1628837253), lg(165345114, 1529503183), -1) - test(lg(1717988228, 1622548180), lg(834798590, -1907713185), 1) - test(lg(-1416372109, -353311343), lg(-722195813, -2060788759), 1) - test(lg(980620531, -300588346), lg(-889348218, 1805452697), -1) - test(lg(-465681479, 556544868), lg(-684386776, 724207906), -1) - test(lg(1720493596, 1118244444), lg(2048914469, -789300492), 1) - test(lg(-1259678249, -1557339417), lg(-1908141376, -468055129), -1) - test(lg(1374750478, 1591281700), lg(1107931774, 1073828802), 1) - test(lg(1307860622, -1769647645), lg(-1521056504, 1476896409), -1) - test(lg(1870719065, -606069057), lg(1219817813, -1063559023), 1) - test(lg(-526519712, 1166848880), lg(-748095992, 59925642), 1) - test(lg(-1011429486, -2053277854), lg(537284118, 1714076830), -1) - test(lg(-669104363, -107157886), lg(1647426475, -1784147450), 1) - test(lg(-389860398, 693324889), lg(1047633230, -1757663140), 1) - test(lg(-200206281, 96771163), lg(613429570, -1206384633), 1) - test(lg(-1436571081, -2050819200), lg(-665572561, 644211697), -1) - test(lg(620796821, -567816428), lg(-109412350, -624638338), 1) - test(lg(858464866, -2104597302), lg(-987329519, 1189618105), -1) - test(lg(-1342634556, -1517778924), lg(-693373055, 142499537), -1) - test(lg(1839280888, -168388422), lg(-1645740821, -1967920957), 1) + test(4294967295L, 0L, 1) + test(0L, 4294967295L, -1) + + test(-8092415883884355943L, -8092415883884355943L, 0) + test(-2031213215071472067L, -2031213215071472067L, 0) + test(5283240141261796897L, 5283240141261796897L, 0) + test(7116056094667264990L, 7116056094667264990L, 0) + test(-3116536807772461648L, -3116536807772461648L, 0) + test(-6248629176470511083L, -6248629176470511083L, 0) + test(4454865470354617868L, 4454865470354617868L, 0) + test(999717031143899307L, 999717031143899307L, 0) + test(-5297317869524400407L, -5297317869524400407L, 0) + test(8939068183804817614L, 8939068183804817614L, 0) + + test(3236335092840668602L, -444219516364298554L, 1) + test(-4943613816686756195L, -7742547864832660642L, 1) + test(8029769392898531897L, 4652159919261674509L, 1) + test(5796873813047518622L, 6289828457790219503L, -1) + test(-4146252130540320321L, 229162157128594750L, -1) + test(1666874288313754998L, -7814084014110160137L, 1) + test(3712093048452543434L, 4733495962840491197L, -1) + test(-945689136431988108L, 173720678004198908L, -1) + test(1942384584602846367L, -4109783896492426928L, 1) + test(1481147942356422128L, 2728187183550782760L, -1) + test(8772007041528428997L, -9186971037150267738L, 1) + test(8245726731682071232L, 5163318085402121929L, 1) + test(1299197553427533446L, 6802087801757261262L, -1) + test(3305926949416808899L, -2540594350510012206L, 1) + test(-2961552791416065935L, -6969814195378031172L, 1) + test(4088157660605840986L, 7194896515898720601L, -1) + test(-1405157718104570917L, 6578730965739299783L, -1) + test(3272049947913267945L, 8706977984604794647L, -1) + test(542774473992239193L, -5153605571562040274L, 1) + test(-3021233129684732581L, -6793741445281541653L, 1) + test(-3173969257539621561L, 8203748404252766862L, -1) + test(5803462967220487068L, 5590091268406426455L, 1) + test(5773238099961046596L, 4776817781713990253L, 1) + test(1563566144471605276L, -7443854257335427327L, 1) + test(-5267335731847770188L, 5984710255758911411L, -1) + test(7824450174170580562L, -6006761146392923340L, 1) + test(-6310031417822260419L, 378610261756145452L, -1) + test(-399600969910632649L, -130497809780743843L, -1) + test(-4718563483403518902L, 5540460094616151815L, -1) + test(3635009130414443975L, 4486809672346912398L, -1) + test(-6995802727856870261L, 6569166150278248282L, -1) + test(6968791371002309508L, -8193565738888199170L, 1) + test(-1517460660612243341L, -8851020320296654181L, 1) + test(-1291017114648111885L, 7754360291495616390L, -1) + test(2390342010645922745L, 3110449275385222696L, -1) + test(4802823317634197020L, -3390019797807795163L, 1) + test(-6688721861751417385L, -2010281469393235264L, -1) + test(6834502861598033678L, 4612059587200791166L, 1) + test(-7600578759410557298L, 6343221779008750856L, -1) + test(-2603046777061840807L, -4567951219930893995L, 1) + test(5011577782742676064L, 257378676128675336L, 1) + test(-8818761229247524974L, 7361903928218635798L, -1) + test(-460239612252633323L, -7662854947344368725L, 1) + test(2977807727662937042L, -7549105702637036210L, 1) + test(415628984375646263L, -5181382544518532798L, 1) + test(-8808201391150486985L, 2766868173945056047L, -1) + test(-2438752987770741867L, -2682801229352239102L, 1) + test(-9039176582481370526L, 5109370859012131857L, -1) + test(-6518810838185736764L, 612030854711736193L, -1) + test(-723222763675766024L, -8452156148778795797L, 1) } @Test def bitwiseNot(): Unit = { @@ -884,56 +873,56 @@ class LongTest { assertEquals(expected, ~hideFromOptimizer(x)) } - test(lg(1664374422, 327449892), lg(-1664374423, -327449893)) - test(lg(-2033180390, -1179462631), lg(2033180389, 1179462630)) - test(lg(-1134559214, 581653069), lg(1134559213, -581653070)) - test(lg(-304074638, -795726117), lg(304074637, 795726116)) - test(lg(-1711832787, 1153070599), lg(1711832786, -1153070600)) - test(lg(-1526506637, 966114536), lg(1526506636, -966114537)) - test(lg(4362923, 1155261397), lg(-4362924, -1155261398)) - test(lg(-1976846289, -68873334), lg(1976846288, 68873333)) - test(lg(-980717878, -1171857118), lg(980717877, 1171857117)) - test(lg(1087568370, 543704246), lg(-1087568371, -543704247)) - test(lg(466027718, 693030605), lg(-466027719, -693030606)) - test(lg(457333958, 1344424074), lg(-457333959, -1344424075)) - test(lg(-1195369388, -1211454825), lg(1195369387, 1211454824)) - test(lg(1637646574, 618600148), lg(-1637646575, -618600149)) - test(lg(1882417448, 81477816), lg(-1882417449, -81477817)) - test(lg(-755550612, -520392566), lg(755550611, 520392565)) - test(lg(-754282895, -1550447287), lg(754282894, 1550447286)) - test(lg(949172349, -708028075), lg(-949172350, 708028074)) - test(lg(1587810906, -1344614950), lg(-1587810907, 1344614949)) - test(lg(-1761617639, -353615615), lg(1761617638, 353615614)) - test(lg(-153730678, 249152220), lg(153730677, -249152221)) - test(lg(-189227914, 2071190797), lg(189227913, -2071190798)) - test(lg(-853867870, 445686068), lg(853867869, -445686069)) - test(lg(-779434875, 417640992), lg(779434874, -417640993)) - test(lg(1997707715, -1100729422), lg(-1997707716, 1100729421)) - test(lg(1171311729, -1236578928), lg(-1171311730, 1236578927)) - test(lg(-833922040, 1773972621), lg(833922039, -1773972622)) - test(lg(1414648869, 1222586075), lg(-1414648870, -1222586076)) - test(lg(1123832582, -1270176018), lg(-1123832583, 1270176017)) - test(lg(1163066309, 237396271), lg(-1163066310, -237396272)) - test(lg(-1826566063, 509270117), lg(1826566062, -509270118)) - test(lg(-450318543, 1650640099), lg(450318542, -1650640100)) - test(lg(1461907704, -27364749), lg(-1461907705, 27364748)) - test(lg(1012261256, 1691289854), lg(-1012261257, -1691289855)) - test(lg(-1929178874, 1804481536), lg(1929178873, -1804481537)) - test(lg(-888719200, -1846455123), lg(888719199, 1846455122)) - test(lg(984231682, -867292444), lg(-984231683, 867292443)) - test(lg(2105026705, -16146223), lg(-2105026706, 16146222)) - test(lg(1742028653, -1648876191), lg(-1742028654, 1648876190)) - test(lg(1922039594, -60702355), lg(-1922039595, 60702354)) - test(lg(264728648, 275960741), lg(-264728649, -275960742)) - test(lg(1237639032, -1761272007), lg(-1237639033, 1761272006)) - test(lg(1118919822, 901486922), lg(-1118919823, -901486923)) - test(lg(18001220, -1121574637), lg(-18001221, 1121574636)) - test(lg(2122002356, -1370943785), lg(-2122002357, 1370943784)) - test(lg(2006182035, -1422441078), lg(-2006182036, 1422441077)) - test(lg(1314896174, 460075839), lg(-1314896175, -460075840)) - test(lg(1829402918, -1031934892), lg(-1829402919, 1031934891)) - test(lg(-2138673173, -107590306), lg(2138673172, 107590305)) - test(lg(1382443514, -56307753), lg(-1382443515, 56307752)) + test(1406386578883106454L, -1406386578883106455L) + test(-5065753424737328870L, 5065753424737328869L) + test(2498180912133439506L, -2498180912133439507L) + test(-3417617645097176974L, 3417617645097176973L) + test(4952400515267264813L, -4952400515267264814L) + test(4149430339078675315L, -4149430339078675316L) + test(4961809918450635435L, -4961809918450635436L) + test(-295808714778363857L, 295808714778363856L) + test(-5033087994080563510L, 5033087994080563509L) + test(2335191956353907186L, -2335191956353907187L) + test(2976543784068121798L, -2976543784068121799L) + test(5774257430242417862L, -5774257430242417863L) + test(-5203158850856805292L, 5203158850856805291L) + test(2656867406598406382L, -2656867406598406383L) + test(349944556951922984L, -349944556951922985L) + test(-2235069048512104852L, 2235069048512104851L) + test(-6659120388296241551L, 6659120388296241550L) + test(-3040957425825662851L, 3040957425825662850L) + test(-5775077234374864294L, 5775077234374864293L) + test(-1518767499246577383L, 1518767499246577382L) + test(1070100640767033738L, -1070100640767033739L) + test(8895696740996914294L, -8895696740996914295L) + test(1914207089783931554L, -1914207089783931555L) + test(1793754405624530053L, -1793754405624530054L) + test(-4727596867237275197L, 4727596867237275196L) + test(-5311066053511426959L, 5311066053511426958L) + test(7619154394655448072L, -7619154394655448073L) + test(5250967210084652069L, -5250967210084652070L) + test(-5455364456349674746L, 5455364456349674745L) + test(1019609221300419525L, -1019609221300419526L) + test(2187298499813494865L, -2187298499813494866L) + test(7089445246515851057L, -7089445246515851058L) + test(-117530700556341000L, 117530700556340999L) + test(7264034611998876040L, -7264034611998876041L) + test(7750189185721635078L, -7750189185721635079L) + test(-7930464363410409312L, 7930464363410409311L) + test(-3724992682063679742L, 3724992682063679741L) + test(-69347497633896303L, 69347497633896302L) + test(-7081869313756020883L, 7081869313756020882L) + test(-260714627593142486L, 260714627593142485L) + test(1185242357839654984L, -1185242357839654985L) + test(-7564605668187644040L, 7564605668187644039L) + test(3871856848880622734L, -3871856848880622735L) + test(-4817126385920070332L, 4817126385920070331L) + test(-5888158719107453004L, 5888158719107453003L) + test(-6109337908490803053L, 6109337908490803052L) + test(1976010683499657518L, -1976010683499657519L) + test(-4432126610911889114L, 4432126610911889113L) + test(-462096843480338453L, 462096843480338452L) + test(-241839956263802374L, 241839956263802373L) } @Test def bitwiseOr(): Unit = { @@ -944,56 +933,56 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x) | hideFromOptimizer(y)) } - test(lg(1467334397, -608514), lg(1198889513, -170491266), lg(356560637, 1244673694)) - test(lg(-1645778056, 796647391), lg(-1930990792, 627822941), lg(-1849669008, 185716690)) - test(lg(2121785322, -3735189), lg(711185578, -154795743), lg(1446469570, -104529814)) - test(lg(401988479, 1357601567), lg(356565628, 275405582), lg(380967239, 1356925723)) - test(lg(-167780425, -167778583), lg(1968397619, -447093015), lg(-1242708043, 1353146913)) - test(lg(-34603479, -565777), lg(-2121965024, -76133937), lg(2104409609, -1365814226)) - test(lg(-537280529, -10535202), lg(1496398822, -548061626), lg(-556169301, -245689186)) - test(lg(2132402169, -1093993487), lg(856203065, -1102382704), lg(1276763344, 377524977)) - test(lg(500957183, -5777537), lg(474066920, -215674305), lg(366737695, 530830706)) - test(lg(-1077937506, 1876426559), lg(-1543310820, 664058893), lg(1002387606, 1826081595)) - test(lg(-2121745, -302649859), lg(1606847457, -857707283), lg(-82108753, 628476252)) - test(lg(2113649662, -9748643), lg(703699686, -1218298019), lg(1575693246, -565500071)) - test(lg(1845274268, 1608495102), lg(1281663616, 1255777790), lg(1708663964, 1604300502)) - test(lg(-174066179, 1861146349), lg(-1315547660, 1726760037), lg(-442781559, 235328140)) - test(lg(2139059199, -40115785), lg(2014986997, -1130692301), lg(124088654, 1637408903)) - test(lg(-4195861, -679630869), lg(1653153899, 1412277603), lg(-1615398494, -682581111)) - test(lg(601802239, 1937620978), lg(551077237, 1349033186), lg(597575118, 1662855120)) - test(lg(-1383162189, -1107312899), lg(613289137, -1123701660), lg(-1383294317, 369006329)) - test(lg(-141299717, -576585865), lg(-418175046, -593383309), lg(1468132939, 360734532)) - test(lg(1998808831, -86066691), lg(1428236018, -1294026291), lg(572735565, 1213340152)) - test(lg(-1680360554, -738459673), lg(-1949058688, -1013245209), lg(416580246, 300148007)) - test(lg(-1073808964, -183288105), lg(-1746245220, 1427323605), lg(-1185613404, -469621610)) - test(lg(1475346349, 1845485055), lg(1445648649, 701317455), lg(1407661733, 1287118327)) - test(lg(-33566733, -268503975), lg(-1861500445, 764080137), lg(-33812527, -411163560)) - test(lg(-286605413, 1602191341), lg(-1408712806, 393166157), lg(1323973395, 1580353248)) - test(lg(-553947394, -2013546505), lg(-2072304578, -2142600249), lg(-625840402, -2018265417)) - test(lg(-553746946, -140321), lg(450125308, 1742298015), lg(-999674466, -89794491)) - test(lg(-16643, -68193313), lg(1239068904, -68194107), lg(-1092247939, -639552609)) - test(lg(-52733444, -1159005505), lg(-2075047684, -1706497393), lg(-119858776, -1461536706)) - test(lg(-121509406, 1048526839), lg(-1065293728, 1045575815), lg(943802850, 4130803)) - test(lg(1844952571, -1327497834), lg(1688647147, -1327540094), lg(1767049400, -1609892586)) - test(lg(-5046291, -1345721876), lg(-207425559, 231270892), lg(515004644, -1349918716)) - test(lg(-1075861506, -67698709), lg(781813534, 1274454635), lg(-1814682890, -1182466103)) - test(lg(2144796219, -17303617), lg(1792206347, -54265949), lg(931436592, -625499620)) - test(lg(-874545153, -1611301156), lg(-1957992337, 421859924), lg(1138122674, -1896513908)) - test(lg(-1218644010, -67141891), lg(-1220262128, 1790926509), lg(-2107837994, -245286664)) - test(lg(-2555905, 2146160604), lg(-485426246, 2122993116), lg(-1077361187, 795578180)) - test(lg(999978447, 2129346287), lg(713580935, 2059541733), lg(957494730, 1688940106)) - test(lg(-836113, 1983903423), lg(-181332639, 608154803), lg(787627150, 1378378253)) - test(lg(-273220891, -1242040457), lg(-944448827, -1528432780), lg(-374967708, 364320051)) - test(lg(-52433921, -1615929419), lg(1822361801, -1626992863), lg(-1865553026, -1867721804)) - test(lg(-1646593, -1583649), lg(-333036705, -39743141), lg(-136127263, -404241201)) - test(lg(-105959457, -50406273), lg(1342309595, 143297662), lg(-1448137844, -50933699)) - test(lg(-480707585, -87100434), lg(-514802766, 718197230), lg(1113082335, -259890518)) - test(lg(-73693249, -555903498), lg(-476348284, -1025699402), lg(1518405435, 1545110880)) - test(lg(-1646871041, -403194029), lg(-2058311589, 1135057747), lg(-1664731675, -1535754941)) - test(lg(-203423937, -34342961), lg(333362997, -34482226), lg(-205173969, 1754490115)) - test(lg(2083487743, -159909991), lg(2083354303, -2043490039), lg(1344953817, -195725679)) - test(lg(-134268937, -680984614), lg(-942983837, -683124136), lg(909452980, -1021249590)) - test(lg(-17107060, -35914117), lg(-402624124, -505696678), lg(-688199800, 2110291577)) + test(-2613546261823747L, -732254410524747223L, 5345832810278072061L) + test(3421574493437913976L, 2696479001637514040L, 797647112316668528L) + test(-16042512477593622L, -664842653033835350L, -448952131140493374L) + test(5830854331665341311L, 1182857968182411900L, 5827951603767122247L) + test(-720603522827034697L, -1920249875726639821L, 5811721741070616501L) + test(-2429989451465175L, -326992767357722080L, -5866127430977143287L) + test(-45248344289067025L, -2353906758366184474L, -1055227015112063061L) + test(-4698666246569598983L, -4734697660499845319L, 1621457430914915536L) + test(-24814331965472769L, -926314086088462360L, 2279900522349328671L) + test(8059190707467844254L, 2852111230804619804L, 7842960731354904726L) + test(-1299871242251165713L, -3683824728419169311L, 2699284952865513135L) + test(-41870100751729666L, -5232550147682886938L, -2428804309254984770L) + test(6908433860711458460L, 5393524540374819456L, 6890418190755046556L) + test(7993562706145703421L, 7416377889934169588L, 1010726668980695177L) + test(-172295982489308161L, -4856286452619001099L, 7032617688688324942L) + test(-2918992351416288789L, 6065686119411425387L, -2931663545932777054L) + test(8322018733155337727L, 5794053415639762293L, 7141908358983730638L) + test(-4755872684732145997L, -4826261879547622223L, 1584870117983689363L) + test(-2476417429357203461L, -2548561902270470214L, 1549343018945998411L) + test(-369653621121128705L, -5557800598580943118L, 5211256272336404557L) + test(-3171660142335247466L, -4351855033137776256L, 1289125874441159318L) + test(-787216413499655748L, 6130308206832544156L, -2017009453335512668L) + test(7926297957957107629L, 3012135534784600329L, 5528131121954895525L) + test(-1153215787209601037L, 3281699202371666403L, -1765934039245778991L) + test(6881359415537745819L, 1688635789095255962L, 6787565517611350803L) + test(-8648116384209080578L, -9202397995633793986L, -8668383956993675538L) + test(-602670364721666L, 7483112994760842748L, -385664398910673506L) + test(-292888044845940995L, -292891458105855768L, -2746857536523755907L) + test(-4977890735616730628L, -7329350491424339716L, -6277252349998458456L) + test(4503388486656715234L, 4490713934143219808L, 17741664735021538L) + test(-5701559780695884293L, -5701741286170118677L, -6914436005175818056L) + test(-5779831442641846291L, 993300921744289769L, -5797856736963307292L) + test(-290763737917315074L, 5473740978342430494L, -5078653238533283082L) + test(-74318466972713413L, -233070474449197557L, -2686500410628990928L) + test(-6920485765606572033L, 1811874579410020463L, -8145465210131030094L) + test(-288372222960273450L, 7691970788769154832L, -1053498197837811242L) + test(9217689610436018175L, 9118186006662675386L, 3416982267728807389L) + test(9145472665524008399L, 8845664388695744903L, 7253942521130268106L) + test(8520800324501585391L, 2612004993903957345L, 5920089518940241038L) + test(-5334523139102147867L, -6564568800883844411L, 1564742708242051684L) + test(-6940364003006747649L, -6987881135588046647L, -8021804063776707714L) + test(-6801716370022401L, -170695486873386145L, -1736202733831922463L) + test(-216493289859239969L, 615458773225571547L, -218758568622478452L) + test(-374093511683146753L, 3084633618707954610L, -1116221274237416993L) + test(-2387587339420727361L, -4405345383298137980L, 6636200699812185915L) + test(-1731705165849379329L, 4875035904673097819L, -6596017243635173915L) + test(-147501890251260097L, -148100032629917899L, 7535477669170072367L) + test(-686808179565166593L, -8776722885123410241L, -840635388947440167L) + test(-2924806642048485385L, -2933995819876272797L, -4386233589193955660L) + test(-154249953701857396L, -2171950689813499516L, 9063633311846033288L) } @Test def bitwiseAnd(): Unit = { @@ -1004,56 +993,56 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x) & hideFromOptimizer(y)) } - test(lg(-2012982272, 17896961), lg(-1973652216, 353474049), lg(-576365513, -1546420349)) - test(lg(440467456, -805024688), lg(2054268182, -735220496), lg(-1706223071, -653894309)) - test(lg(-1073741824, -2144861952), lg(-761230816, -1888512251), lg(-988806710, -256349768)) - test(lg(-1977056222, -1878455803), lg(-834874333, -101893315), lg(-1964333382, -1877225849)) - test(lg(-1069166300, 304091682), lg(-767041747, 1403541430), lg(-320482908, 442929698)) - test(lg(193986570, 67633664), lg(1538292767, 67928849), lg(261587146, 2097883842)) - test(lg(167772308, 35669040), lg(448790964, 1852174074), lg(-284620129, 35804464)) - test(lg(540801, 554500096), lg(123267521, 1965916169), lg(-401979731, 588194498)) - test(lg(-1878826824, 268436097), lg(-1725202754, 324931273), lg(-1240211271, 948007557)) - test(lg(306780164, 8388625), lg(1044995460, -1447811559), lg(1381579300, 378161591)) - test(lg(29904144, 12096051), lg(1640550232, -1980050765), lg(-1613988461, 381206391)) - test(lg(-963297278, 537741320), lg(-810205145, 832395272), lg(-153237294, -1368559681)) - test(lg(-2138566639, -1881372656), lg(-2087037677, -539042218), lg(-1930915595, -1879201391)) - test(lg(348136448, 1461360), lg(936077102, 1888906741), lg(-590306112, 153013360)) - test(lg(-2147459072, 50628864), lg(-1520343420, -480326676), lg(-1031638712, 463833361)) - test(lg(-805279656, -972355264), lg(-603625122, -837874740), lg(-266310439, -433325742)) - test(lg(1763723264, 1095287337), lg(2101242821, 1363798717), lg(-337523686, -1007893653)) - test(lg(1296302405, 1947206722), lg(-849542331, 2084521938), lg(1866786159, -179258269)) - test(lg(1275593362, 814484868), lg(1283984114, 1922846117), lg(-42342754, 948944324)) - test(lg(1081520, 35397649), lg(18451376, 39592223), lg(-300891980, 43819665)) - test(lg(539714600, -1617688304), lg(1772840110, -1611388521), lg(876572201, -1080057992)) - test(lg(268660738, 1111507460), lg(-1792575438, 1131693597), lg(2026108738, -691967420)) - test(lg(-1977139054, 2393104), lg(-1977130853, 1105495064), lg(-289941322, 37545108)) - test(lg(-2145341308, -1333516032), lg(-1590955612, -1330697458), lg(-924798828, -1177272879)) - test(lg(-1503395487, -299827136), lg(-285931035, -293654078), lg(-1486596765, -31342500)) - test(lg(1233401994, 34091008), lg(1237743775, -1293389691), lg(1803860874, 1175174664)) - test(lg(-932558672, 270533826), lg(-839976008, 900736195), lg(-362132238, -668577850)) - test(lg(117477888, 473995424), lg(1202887172, 484547048), lg(793351913, -1622877017)) - test(lg(302600257, -2030040226), lg(1393155525, -2025583778), lg(-1164217783, -416769026)) - test(lg(145293649, 536871648), lg(-658787467, -1534848013), lg(770509273, 861439716)) - test(lg(1546608834, 302001248), lg(1550840002, 1588870758), lg(2084528882, 302148833)) - test(lg(201606209, -695465177), lg(481609689, -152204489), lg(1279544421, -561242137)) - test(lg(608207492, -2112820352), lg(-1529763097, -1978531900), lg(641783708, -2039026814)) - test(lg(270672860, -1476361723), lg(887514076, -129985897), lg(423346174, -1364800691)) - test(lg(606102544, -503185240), lg(1736270961, -223672071), lg(748709016, -498985816)) - test(lg(144970344, 74547586), lg(413438572, 628333003), lg(-1964689415, -2039117914)) - test(lg(0, 33646849), lg(-1441786846, -952014445), lg(1364118108, 582220621)) - test(lg(886489100, -1836576552), lg(-167845571, -610782244), lg(920048140, -1832380167)) - test(lg(181408260, 8425760), lg(1070668735, 1223734716), lg(1255200260, 310500128)) - test(lg(18633796, 1494253868), lg(565998918, 2102701486), lg(1230790357, -651115716)) - test(lg(1242169472, 1074954242), lg(1259021457, -988117846), lg(-95497780, 2025257730)) - test(lg(202639938, 134272082), lg(236334914, 210367602), lg(-1388488109, 672191707)) - test(lg(955253125, 1994661641), lg(2029259749, 2012495659), lg(-1125022313, -17866867)) - test(lg(134242336, 1377566768), lg(2078335024, -748696528), lg(-1944488853, 1455161657)) - test(lg(883214088, 536873986), lg(1962270604, 747650594), lg(1051641707, -1606005365)) - test(lg(203000132, 19923458), lg(504991188, 623990339), lg(-1919047324, 331123498)) - test(lg(274893395, 1881151488), lg(409659995, 1887189252), lg(384277491, 1973591160)) - test(lg(115235, 335685459), lg(872793907, 353626075), lg(34859627, 1988247415)) - test(lg(538493100, 441057288), lg(-1407266644, 441386073), lg(1635378940, -548742904)) - test(lg(839516176, 671232089), lg(844761371, 1022505085), lg(1930384912, 688275291)) + test(76866864474772480L, 1518159482761016584L, -6641824821105304521L) + test(-3457554706992136192L, -3157747983614630634L, -2808454669606774239L) + test(-9212111935053496320L, -8111098352606606816L, -1101013866591026742L) + test(-8067906238548507614L, -437628452145933277L, -8062623626330200390L) + test(1306063832401432868L, 6028164543958998829L, 1902368571311640996L) + test(290484375182639114L, 291752186448215071L, 9010342492458418378L) + test(153197360447488148L, 7955027074777874868L, 153779005941156511L) + test(2381559777949401217L, 8443545652655876545L, 2526276136490124973L) + test(1152924260097024184L, 1395569193552412350L, 4071661456730611897L) + test(36028870340188164L, -6218303295630779004L, 1624191667329907236L) + test(51952143485652240L, -8504253278454231208L, 1637268985052167571L) + test(2309581386439540738L, 3575110474069786663L, -5877919068377462574L) + test(-8080434026952257519L, -2315168695265372909L, -8071108514578657035L) + test(6276493755819008L, 8112792678725019438L, 657187380755735744L) + test(217449317261139968L, -2062987362041764220L, 1992149119552090440L) + test(-4176234055483758504L, -3598644602753160866L, -1861119886376276775L) + test(4704223293901654016L, 5857470889943002053L, -4328870273523528678L) + test(8363189190837666117L, 8952953554949964613L, -769908401025784465L) + test(3498185872422470290L, 8258561189039573746L, 4075684841557452446L) + test(152031744811368624L, 170047302979390384L, 188204032090751156L) + test(-6947918360261991384L, -6920860997071969106L, -4638813752546857431L) + test(4773888190228688898L, 4860586990709995570L, -2971977436771387582L) + test(10278305733755026L, 4748065148087263387L, 161255014989813942L) + test(-5727407743982063484L, -5715302060276321884L, -5056348510402596716L) + test(-1287747740781772447L, -1261234657337996827L, -134615009666509469L) + test(146419765681076362L, -5555066422590801761L, 5047336750771649418L) + test(1161933938494163120L, 3868632503303470008L, -2871519996647158542L) + test(2035794844651131392L, 2081113725736229380L, -6970203712651684119L) + test(-8718956379931848639L, -8699816080424968763L, -1790009333525024183L) + test(2305846170454917457L, -6592122016529403019L, 3699855408466037209L) + test(1297085485057794242L, 6824147944731570370L, 1297719358344094450L) + test(-2987000190520245183L, -653713302077782055L, -2410516622272607131L) + test(-9074494313555000700L, -8497729801827538201L, -8757553481155291236L) + test(-6340925317080538148L, -558285175668710436L, -5861774332979855362L) + test(-2161164149023808496L, -960664228237319055L, -2143127760139164520L) + test(320179444010717800L, 2698669699295908460L, -8757944750987462663L) + test(144512116068450304L, -4088870903741410270L, 2500618527615928924L) + test(-7888036226553954292L, -2623289758830370499L, -7870012890183970292L) + test(36188363825353220L, 5255900585270516671L, 1333587896419014148L) + test(6417771495000134724L, 9031034116186600774L, -2796520704900833579L) + test(4616893315328639104L, -4243933831904942959L, 8698415720520667596L) + test(576694201158470210L, 903521970964279106L, 2887041401113893459L) + test(8567006515635945861L, 8643603040776227813L, -76737606277036649L) + test(5916604216750661664L, -3215627100310413264L, 6249871729558647915L) + test(2305856212826375944L, 3211134852027244428L, -6897740518823901333L) + test(85570600736229700L, 2680018099529944532L, 1422164597223041380L) + test(8079484120056629843L, 8105416119112362587L, 8476509488258980851L) + test(1441758068147864099L, 1518812428010637107L, 8539457623816399467L) + test(1894326628160946348L, 1895738751332569260L, -2356832824956688644L) + test(2882919871120277520L, 4391625900913461531L, 2956119867420268048L) } @Test def bitwiseXor(): Unit = { @@ -1064,56 +1053,56 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x) ^ hideFromOptimizer(y)) } - test(lg(1342248740, -313223199), lg(690404572, -1279287229), lg(2032643064, 1592473506)) - test(lg(-1691405730, 274213753), lg(1880634009, 1433776255), lg(-348716857, 1160616710)) - test(lg(882329013, -513228751), lg(-958227509, 287282926), lg(-227156354, -260614433)) - test(lg(1416185065, -1664302164), lg(-266860160, 1815641996), lg(-1536078487, -252396512)) - test(lg(-1268929640, 1388542260), lg(1278830943, 22194981), lg(-127614265, 1402065425)) - test(lg(2107251545, -1588280474), lg(-865349911, -84319450), lg(-1309551184, 1538105408)) - test(lg(-1128180942, 150893828), lg(-1973252863, -1969367363), lg(916708915, -2107399239)) - test(lg(-721878765, 35051090), lg(2098389933, -3394272), lg(-1444158786, -35986574)) - test(lg(-1863503396, 535478572), lg(533612062, -1712875225), lg(-1893500990, -2045945845)) - test(lg(1732708730, -1611595623), lg(799833325, 2072025633), lg(1223390615, -462316872)) - test(lg(-757432261, -1755342186), lg(570370215, 1665373667), lg(-215635812, -199487627)) - test(lg(755676969, 926086823), lg(-1440978805, 1756956707), lg(-2028544094, 1603010180)) - test(lg(1331057947, 1347408402), lg(-1788434031, -203193594), lg(-634323830, -1548988140)) - test(lg(596183682, -256181831), lg(-1101798994, 1399594232), lg(-1646597332, -1546197695)) - test(lg(1360009516, 182700672), lg(-1432962218, -1631098948), lg(-75062662, -1809535684)) - test(lg(594798246, -124892913), lg(699430210, 902448324), lg(180589540, -851178037)) - test(lg(-1331407219, 1819608371), lg(-1873118605, -20501824), lg(553528574, -1833816077)) - test(lg(1679931669, 470452622), lg(-693963147, 616673404), lg(-1300017312, 952842738)) - test(lg(1861159718, -1488989292), lg(1250421224, 1104113895), lg(610853582, -420437133)) - test(lg(1056597675, -102857583), lg(-611286212, -1550148499), lg(-445979241, 1514412284)) - test(lg(255992058, 1610836280), lg(1704771515, 1382796179), lg(1792974657, 845718187)) - test(lg(315376042, 566682776), lg(1042258124, 728098489), lg(752081254, 178455073)) - test(lg(-185728083, -2076881789), lg(-1887944331, 1039677246), lg(2073445080, -1177715779)) - test(lg(22829354, 1511361245), lg(1986213921, -1875380784), lg(2000642315, -903708915)) - test(lg(-1209040105, 1698106233), lg(365179043, -418125319), lg(-1574194252, -2111511936)) - test(lg(-2034371369, -364230501), lg(-376038790, 1936322298), lg(1865150125, -1725716895)) - test(lg(-324294323, -1435696355), lg(182372182, -1389399582), lg(-428511717, 121795327)) - test(lg(-1632322296, 110394084), lg(408417754, -547668779), lg(-2031925038, -640727503)) - test(lg(1545363539, -418308022), lg(1515701412, 860890032), lg(105620727, -733936646)) - test(lg(-2124553361, 1571601224), lg(144626057, 2121098703), lg(-1983696154, 599907975)) - test(lg(-508527758, 679546956), lg(1716685092, -647833300), lg(-2015169962, -236730016)) - test(lg(-703803607, -1904715404), lg(-2016515438, -1674300757), lg(1371710907, 306998239)) - test(lg(-1295788899, 1052686696), lg(-547404938, -860356684), lg(1838979051, -234273060)) - test(lg(-1416482745, -1744821078), lg(1034397763, 1158948099), lg(-1774872572, -585891415)) - test(lg(-420256974, -1759976200), lg(1755131065, -847055172), lg(-1905373301, 1520046660)) - test(lg(-1978435977, -1613559541), lg(755114159, 1707687361), lg(-1492035880, -98945846)) - test(lg(1517584033, -1108617107), lg(1110955283, -394871226), lg(407088050, 1436378667)) - test(lg(1706214170, -555203143), lg(729918767, -1047522396), lg(1311993397, 527980061)) - test(lg(-278231087, -1148948163), lg(-1533968339, 1826223468), lg(1274742780, -681737135)) - test(lg(-204001370, 1220298027), lg(230297309, -219465279), lg(-26402437, -1168671510)) - test(lg(-1169385448, -2039889677), lg(-1364422220, 1487677662), lg(350226860, -557455315)) - test(lg(791138554, 668046473), lg(-1049451753, 1883174397), lg(-296389651, 1475305844)) - test(lg(2103687665, 1121138741), lg(-895088167, 1303802204), lg(-1211781080, 258296169)) - test(lg(-387978954, 908804328), lg(1409034242, -1162000487), lg(-1155284684, -1936324751)) - test(lg(1265820840, 1142688859), lg(861082066, -475962819), lg(2015491450, -1480757658)) - test(lg(1490973918, -277478122), lg(-288714491, 1935424926), lg(-1240144421, -1674954616)) - test(lg(1839163014, 362842460), lg(-699164585, -731232280), lg(-1144193327, -1043673420)) - test(lg(634920094, -2001579101), lg(683993930, 248552821), lg(220002260, -2040344874)) - test(lg(-831642917, -817908795), lg(640417317, 298956382), lg(-398074626, -554826341)) - test(lg(857398449, 1711937081), lg(-1493347776, 1187436882), lg(-1779986703, 550293355)) + test(-1345283394711251164L, -5494496810055058212L, 6839621630049102840L) + test(1177739103851983454L, 6158022126886990489L, 4984810816587366599L) + test(-2204300700029598283L, 1233870775205927883L, -1119330462532772226L) + test(-7148123363625843479L, 7798122998092269952L, -1084034761905582743L) + test(5963743598839966616L, 95326718809172319L, 6021825151394693831L) + test(-6821612690598126759L, -362149276737089815L, 6606112428146152880L) + test(648084059595035442L, -8458368415573046015L, -9051210810203578829L) + test(150543288812241171L, -14578285135338579L, -154561155574275394L) + test(2299862956880245212L, -7356743072970029538L, -8787270491260618814L) + test(-6921750493429036678L, 8899282331008531693L, -1985635844405627497L) + test(-7539137278621614021L, 7152725435954964647L, -856792829842315108L) + test(3977512618797217577L, 7546071599906842763L, 6884876300521496482L) + test(5787075022276678939L, -872709838480168559L, -6652853399531225974L) + test(-1100292585378215294L, 6011211457303404974L, -6640868530527212756L) + test(784693412557232428L, -7005516635337999530L, -7771896579505085830L) + test(-536410976242375002L, 3875986038609442114L, -3655781831807888412L) + test(7815158447936394893L, -88054661166499213L, -7876180077040489218L) + test(2020578627487381781L, 2648592106093999733L, 4092428400936046432L) + test(-6395160311373034714L, 4742133071334599144L, -1805763735648148786L) + test(-441769954074007893L, -6657837103464807620L, 6504351236289652119L) + test(6918489142066290938L, 5939064367543533499L, 3632331956590387009L) + test(2433883990441869738L, 3127159199564273868L, 766458703092373862L) + test(-8920139357303733331L, 4465379772372369781L, -5058250752714718504L) + test(6491247119739672874L, -8054699132840626143L, -3881400233028001525L) + test(7293310738954683159L, -1795834570369388381L, -9068874707512872012L) + test(-1564358087740099369L, 8316440948344494714L, -7411897624314515795L) + test(-6166268887740733107L, -5967425765583698090L, 523106950137081371L) + test(474138983114521864L, -2352219494436833830L, -2751903668769699630L) + test(-1796619272599084973L, 3697494534408094884L, -3152233891800308489L) + test(6749975861603984239L, 9110049561117643145L, 2576585135545856742L) + test(2918631955902790514L, -2782422835043071708L, -1016747674421759402L) + test(-8180690364776263895L, -7191066992704591214L, 1318547397806302651L) + test(4521254935253472413L, -3695203816927444106L, -1006195129194866709L) + test(-7493949464502980537L, 4977644184000768067L, -2516384463912069116L) + test(-7559040216863644878L, -3638074259892523847L, 6528550695483625355L) + test(-6930185456427239817L, 7334461368042660015L, -424969169842121000L) + test(-4761474216833548639L, -1695959000690469613L, 6169199399844162482L) + test(-2384579340115197158L, -4499074431917642449L, 2267657096247078453L) + test(-4934694780867541039L, 7843570073008701485L, -2928038698018994180L) + test(5241140121429290918L, -942596195682218275L, -5019405910948372101L) + test(-8761259447037421544L, 6389526908210287028L, -2394252346556151380L) + test(2869237754534285562L, 8088172451025036055L, 6336390355576255469L) + test(4815254228977302001L, 5599787830032599513L, 1109373601620275240L) + test(3903284871130245430L, -4990754088192038910L, -8316451476840660684L) + test(4907811280174376104L, -2044244740855885358L, -6359805712396061318L) + test(-1191759457854524194L, 8312586765039472901L, -7193875294949415461L) + test(1558396501139351174L, -3140618724783712169L, -4482543203453698351L) + test(-8596716778517160802L, 1067526238207535946L, -8763214506171238444L) + test(-3512891522172443941L, 1284007884260900389L, -2382960985657451266L) + test(7352713776562101425L, 5100002577055830592L, 2363491965446098673L) } @Test def shiftLeft(): Unit = { @@ -1124,56 +1113,56 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x) << hideFromOptimizer(y)) } - test(lg(1065353216, -691528727), lg(-1875389825, 1268606893), -73329513) - test(lg(671088640, -1046568266), lg(869553861, -291578632), -339545061) - test(lg(0, 0), lg(543726956, -1753066291), -809014658) - test(lg(-754974720, -1479892363), lg(-895322669, 847749031), 1030973528) - test(lg(0, 1696595968), lg(1598039634, 819660072), 82069876) - test(lg(0, -763223040), lg(-151740279, -595601314), 503039850) - test(lg(0, -1360527360), lg(-1702267427, 1115684531), 1171866675) - test(lg(508125184, -784066052), lg(-807341493, 286689824), -1938771891) - test(lg(-551288832, 439734876), lg(-382832750, -2134078182), 1537970769) - test(lg(-1409069728, 1129787), lg(-580904341, 939559401), 1856717061) - test(lg(1711276032, 1295846454), lg(-198125160, 663832884), 1561097110) - test(lg(-1004724328, -940313723), lg(-1199332365, -1728151952), 858801923) - test(lg(-1029298112, -1523092059), lg(773140802, -181814355), 1110910853) - test(lg(536870912, 200145086), lg(1601160689, 869229832), -338843811) - test(lg(0, -1735502848), lg(-1919381932, -201750119), -813015128) - test(lg(-1727917056, 2104066035), lg(-52019067, -102802849), -2122946486) - test(lg(0, 771751936), lg(-456947922, 1170727731), 2126487160) - test(lg(0, -710836224), lg(1756719200, -1702547414), -32425558) - test(lg(0, -1073741824), lg(97072750, 409070577), 1222452733) - test(lg(0, -1182793728), lg(1177105779, 212324545), -834196361) - test(lg(0, 1543503872), lg(1395605166, -1743726419), -1762017159) - test(lg(0, -67108864), lg(703808254, 1939941481), 1042647417) - test(lg(0, 1207959552), lg(-702184622, -618243162), -753853766) - test(lg(-58458112, -1619174179), lg(-1368457662, 1747275710), 1382741393) - test(lg(0, -299542812), lg(-74885703, 1342895995), 1929734882) - test(lg(0, -1585446912), lg(-61401466, -496528012), -129147274) - test(lg(1888485376, 630678170), lg(-660169692, 1479330149), 289081298) - test(lg(0, -536870912), lg(-421237721, 1011668330), 370873533) - test(lg(0, 102137856), lg(-821818323, -2029348763), -916638609) - test(lg(0, -1073741824), lg(-1246065172, -1572087360), 1493241980) - test(lg(1156516188, -1812425640), lg(578258094, -906212820), 2074806145) - test(lg(0, 1370357760), lg(61151968, -1770168701), -2062208020) - test(lg(-402653184, 1642287002), lg(1013576541, 460756940), -902835237) - test(lg(-1744830464, 1690731362), lg(-1731171245, 771836652), 868975579) - test(lg(-417260032, 563566725), lg(1123258511, 1049676716), 575477257) - test(lg(411626816, -1915897795), lg(-779579692, 1222433667), 1238257604) - test(lg(0, -2147483648), lg(-1102469156, -543766743), 553354173) - test(lg(0, -1909156352), lg(843520587, -517185932), 1899246569) - test(lg(0, -487976960), lg(-510775647, -896837143), 1487779500) - test(lg(-1148788736, -847308273), lg(-1594115986, -186853391), -119255604) - test(lg(0, 1940424228), lg(-588635767, 1047291343), 2089738146) - test(lg(1726279680, 2137615428), lg(-1002017201, -986188138), 800913356) - test(lg(0, 1650633728), lg(1813551275, -400674286), -1609938966) - test(lg(-1207959552, 897838789), lg(-1333929801, 254558182), -1518372133) - test(lg(0, -1104224256), lg(834127324, 878312672), -923142549) - test(lg(-504160320, 305586753), lg(126340223, -2008491127), -252023418) - test(lg(0, 0), lg(510931784, -1313923431), 1174528765) - test(lg(-1449390900, -1602240664), lg(711394099, -400560166), -967606846) - test(lg(0, 1162928128), lg(1319282800, -1994311032), 1237159401) - test(lg(-1749421258, 1809275319), lg(-874710629, -1242845989), 484063041) + test(-2970093265644158976L, 5448625119334748799L, -73329513) + test(-4494976474830340096L, -1252320687782865211L, -339545061) + test(0L, -7529362387021292180L, -809014658) + test(-6356089297145167872L, 3641054366760334803L, 1030973528) + test(7286824197085462528L, 3520413204675044946L, 82069876) + test(-3278017996353699840L, -2558088160941399927L, 503039850) + test(-5843420516513218560L, 4791828575890798045L, 1171866675) + test(-3367538050735710208L, 1231323421663621707L, -1938771891) + test(1888646915074293760L, -9165795994885001326L, 1537970769) + test(4852401102343520L, 4035376903658412651L, 1856717061) + test(5565618142278844416L, 2851140530886203800L, 1561097110) + test(-4038616684974760040L, -7422356113262926861L, 858801923) + test(-6541630578936633280L, -780886707895193278L, 1110910853) + test(859616599361978368L, 3733313702748734961L, -338843811) + test(-7453927974274859008L, -866510160693522860L, -813015128) + test(9036894811516441600L, -441534870147678075L, -2122946486) + test(3314649325744685056L, 5028237321003304750L, 2126487160) + test(-3053018334892130304L, -7312385461262653344L, -32425558) + test(-4611686018427387904L, 1756944750067922542L, 1222452733) + test(-5080060379673919488L, 911926978090186099L, -834196361) + test(6629298651489370112L, -7489247941380587858L, -1762017159) + test(-288230376151711744L, 8331985217752613630L, 1042647417) + test(5188146770730811392L, -2655334158172847278L, -753853766) + test(-6954300141096140800L, 7504492034471689794L, 1382741393) + test(-1286526581291876352L, 5767694384674461113L, 1929734882) + test(-6809442636584189952L, -2132571568854329722L, -129147274) + test(2708742116339613696L, 6353674613576604708L, 289081298) + test(-2305843009213693952L, 4345082395622665255L, 370873533) + test(438678751203557376L, -8715986565789905875L, -916638609) + test(-4611686018427387904L, -6752063794606076436L, 1493241980) + test(-7784308849075353252L, -3892154424537676626L, 2074806145) + test(5885641763019816960L, -7602816679136650528L, -2062208020) + test(7053568968128200704L, 1978935989718610781L, -902835237) + test(7261635908661673984L, 3315013180757929043L, 868975579) + test(2420500656866532864L, 4508327167715938447L, 575477257) + test(-8228718371591885504L, 5250312624809742036L, 1238257604) + test(-9223372036854775808L, -2335460374644938788L, 553354173) + test(-8199764094790664192L, -2221296663047759285L, 1899246569) + test(-2095845084401500160L, -3851886195238883679L, 1487779500) + test(-3639161319019061248L, -802529200790849426L, -119255604) + test(8334058599626047488L, 4498082071275250057L, 2089738146) + test(9180988356411322368L, -4235645797120184753L, 800913356) + test(7089417879434559488L, -1720882952904599381L, -1609938966) + test(3856188238922252288L, 1093319069580253367L, -1518372133) + test(-4742607066969931776L, 3772324202736502236L, -923142549) + test(1312485114016636864L, -8626403704644842369L, -252023418) + test(0L, -5643258165082180792L, 1174528765) + test(-6881571249355748148L, -1720392812338937037L, -967606846) + test(4994738277358501888L, -8565500659172726672L, 1237159401) + test(7770778327110513462L, -5337982873299519077L, 484063041) } @Test def shiftLogicalRight(): Unit = { @@ -1184,56 +1173,56 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x) >>> hideFromOptimizer(y)) } - test(lg(1982185809, 4856), lg(88517143, 1273092247), 2099569298) - test(lg(40, 0), lg(-1987462914, 1361836721), -2053535175) - test(lg(258, 0), lg(1513792977, 1085974656), -303705162) - test(lg(-1589724844, 2), lg(-2071249600, 1411897130), 1015183069) - test(lg(827423626, 419765), lg(-1560865755, 214919778), 1191603401) - test(lg(376475826, 25773988), lg(944265510, -995896821), 485744647) - test(lg(291969293, 528), lg(1131824263, -2080089658), -386336938) - test(lg(185, 0), lg(-827478170, -1185129975), 2048537528) - test(lg(45022, 0), lg(-916869993, -1344352401), -791372688) - test(lg(587, 0), lg(588931659, -1830830904), -1259543946) - test(lg(-684574597, 28915), lg(473794659, 947514265), -1409717873) - test(lg(3, 0), lg(471518489, -940479957), -847604034) - test(lg(11, 0), lg(-818287716, 1547586919), -216455813) - test(lg(266, 0), lg(-2088976011, -2057680935), 787633143) - test(lg(-800511856, 59336150), lg(306848777, -497453644), 1584315654) - test(lg(25694, 0), lg(-1689341833, -927188015), 1300572337) - test(lg(237982231, 3229829), lg(396954515, 413418119), 1180537031) - test(lg(1319611409, 10188), lg(1478732342, 1335401807), -1668840943) - test(lg(-530293557, 9), lg(-1326271298, -1643756084), -2118687716) - test(lg(26, 0), lg(1205635051, 875594107), 350453433) - test(lg(1698203097, 57089), lg(-2049358216, -553556680), -1203541232) - test(lg(-308392901, 40188), lg(1278981121, -1661145698), 254766480) - test(lg(-1667461656, 7259908), lg(1313272948, 929268302), 1175504903) - test(lg(99018, 0), lg(1982277801, -1050318135), 629735727) - test(lg(16237, 0), lg(-610510955, 1064153335), 577897264) - test(lg(689994, 0), lg(1859860682, 1413109554), 243415787) - test(lg(4088, 0), lg(1757351444, -7991214), -1844808396) - test(lg(48441534, 0), lg(-1277568919, -1194709070), -2102413146) - test(lg(42961906, 0), lg(-1768551066, 1342559), 365466523) - test(lg(1946, 0), lg(1051996382, -213518283), -717261067) - test(lg(-605712863, 10), lg(451444747, -1380034334), -675522340) - test(lg(8, 0), lg(605006440, -1956088854), 192236860) - test(lg(-152492078, 258), lg(-384174131, -2122615661), -1278414057) - test(lg(-1650335224, 9146646), lg(-1579022332, -1953425763), 2134440904) - test(lg(175996054, 0), lg(-433112808, -1479030417), -1873327132) - test(lg(771890457, 0), lg(-1786180708, 385945228), 1526047775) - test(lg(868056695, -1200391723), lg(868056695, -1200391723), 93595840) - test(lg(88233, 0), lg(1335240662, -1403745666), 1625850351) - test(lg(21, 0), lg(-681452715, -1446696044), -742234373) - test(lg(200097858, 0), lg(301750839, 1600782865), 1678034787) - test(lg(1, 0), lg(-2077889650, 445749598), 363036476) - test(lg(-1160719403, 3135), lg(-1633078438, 1644025478), -1297864237) - test(lg(27660, 0), lg(1159483779, 906375175), -1204888593) - test(lg(1096217739, 131290637), lg(179807326, 1050325098), -1598422013) - test(lg(61, 0), lg(952383136, -193355640), 415626042) - test(lg(12362394, 0), lg(972435428, -1130194211), -1259042456) - test(lg(-924965860, 8483), lg(605823642, 555993310), 1780437072) - test(lg(88, 0), lg(665774635, 184915839), 1729784373) - test(lg(27109, 0), lg(-263808048, -741669613), -204793551) - test(lg(-5828381, 10), lg(-954198224, 369053217), 768150041) + test(20858343375185L, 5467889565744671255L, 2099569298) + test(40L, 5849044181494380798L, -2053535175) + test(258L, 4664225633318643153L, -303705162) + test(11295177044L, 6064052000889978176L, 1015183069) + test(1802877774429066L, 923073420507681829L, 1191603401) + test(110698435923972274L, -4277344275441100506L, 485744647) + test(2268034701581L, -8933917052726000505L, -386336938) + test(185L, -5090094480666808474L, 2048537528) + test(45022L, -5773949593215980393L, -791372688) + test(587L, -7863358856597183925L, -1259543946) + test(124192589756539L, 4069542781142272099L, -1409717873) + test(3L, -4039330657386967783L, -847604034) + test(11L, 6646835208299080604L, -216455813) + test(266L, -8837672319221710475L, 787633143) + test(254846827215005840L, -2136547131949177847L, 1584315654) + test(25694L, -3982242199062531977L, 1300572337) + test(13872010164654615L, 1775617301075790739L, 1180537031) + test(43758446423057L, 5735507089563036214L, -1668840943) + test(42419379403L, -7059878620412332866L, -2118687716) + test(26L, 3760648055340959723L, 350453433) + test(245197086164441L, -2377507834836728200L, -1203541232) + test(172610132266043L, -7134566445522111487L, 254766480) + test(31181070059474408L, 3991176967612724340L, 1175504903) + test(99018L, -4511082038238435159L, 629735727) + test(16237L, 4570503775438788501L, 577897264) + test(689994L, 6069259321955006666L, 243415787) + test(4088L, -34322001027985900L, -1844808396) + test(48441534L, -5131236380867176343L, -2102413146) + test(42961906L, 5766249524366694L, 365466523) + test(1946L, -917054041531076386L, -717261067) + test(46638927393L, -5927202331435696117L, -675522340) + test(8L, -8401337655395112344L, 192236860) + test(1112244037586L, -9116564842061629491L, -1278414057) + test(39284548082721288L, -8389899764532901884L, 2134440904) + test(175996054L, -6352387266942387944L, -1873327132) + test(771890457L, 1657622134816050076L, 1526047775) + test(-5155643191806034313L, -5155643191806034313L, 93595840) + test(88233L, -6029041726036498474L, 1625850351) + test(21L, -6213512192619062443L, -742234373) + test(200097858L, 6875310053473933879L, 1678034787) + test(1L, 1914479947832224654L, 363036476) + test(13467856720853L, 7061035664462656346L, -1297864237) + test(27660L, 3892851735690760579L, -1204888593) + test(563888993282225291L, 4511111946257802334L, -1598422013) + test(61L, -830456149344766304L, 415626042) + test(12362394L, -4854147173401088028L, -1259042456) + test(36437577573404L, 2387973083850613402L, 1780437072) + test(88L, 794207481683175979L, 1729784373) + test(27109L, -3185446728240817200L, -204793551) + test(47238811875L, 1585071500839360304L, 768150041) } @Test def shiftArithmeticRight(): Unit = { @@ -1244,56 +1233,56 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x) >> hideFromOptimizer(y)) } - test(lg(144041519, 2813487), lg(-1780076655, 720252680), -1316031160) - test(lg(1519, 0), lg(234061537, 796729805), 1452874739) - test(lg(-935479627, 124), lg(1523206972, 1046748891), 1356453463) - test(lg(-15335, -1), lg(1866043067, -2009962307), 393061105) - test(lg(5, 0), lg(89507691, 183545611), -1980770119) - test(lg(-1283367734, 14309038), lg(-1062312593, 1831556953), 1545082311) - test(lg(523169438, 0), lg(-1568293714, 523169438), -2119005984) - test(lg(-1704853904, -731301), lg(-2013675422, -748851607), 511130378) - test(lg(345569760, -46), lg(-521585277, -770402055), -1176556648) - test(lg(1777038301, 61), lg(-145701849, 257587932), -1512809002) - test(lg(-51, -1), lg(-973180026, -1694110170), 2083093369) - test(lg(-5, -1), lg(1761120319, -539393529), -207994821) - test(lg(-587262921, -3246345), lg(-30904807, -1662128199), -638486135) - test(lg(-10706, -1), lg(1812122560, -701571284), 611632432) - test(lg(7484398, 100362842), lg(119750375, 1605805472), 244039684) - test(lg(1, 0), lg(269986751, 1459449758), -439796226) - test(lg(7, 0), lg(-1969890020, 2011804532), -652735044) - test(lg(-2130588861, 98), lg(-1582649974, 826310885), 613066583) - test(lg(-669931160, -697), lg(756433442, -1459944907), -775565931) - test(lg(933146972, -1), lg(1678061064, -1680910162), -531660641) - test(lg(1601141595, 1298147), lg(1870355258, 332325727), -434372344) - test(lg(-1047936567, -129548), lg(1886551280, -2122502046), -763866098) - test(lg(-72307, -1), lg(-1169141408, -592336405), -1841005139) - test(lg(72262, 0), lg(686282122, 295988927), 69079212) - test(lg(-1582088844, -23862710), lg(1825529126, -1527213400), 1371712838) - test(lg(70395261, 0), lg(633149491, 1126324183), 1948323684) - test(lg(-329, -1), lg(-363762029, -1377253181), -1243200330) - test(lg(1924403917, -21), lg(-1694234908, -689608667), 728732313) - test(lg(-62655, -1), lg(1319661865, -2053067582), -777879057) - test(lg(-1472236443, 19900875), lg(-1472236443, 19900875), 373478400) - test(lg(-1, -1), lg(-1719111010, -1766452468), 942391743) - test(lg(5131, 0), lg(-624682758, 1345231635), -813574478) - test(lg(9, 0), lg(1316519660, 314590421), -641829383) - test(lg(-14492, -1), lg(-1380652891, -474856510), -920501329) - test(lg(40, 0), lg(-2084688189, 1352268039), -177471111) - test(lg(-868447412, 13901269), lg(507881044, 1779362534), -508943033) - test(lg(-37529, -1), lg(1742323077, -1229747072), 401183471) - test(lg(376386, 0), lg(346182810, 770838817), 797274667) - test(lg(-1822, -1), lg(828281422, -477411393), 1298272370) - test(lg(1021967080, -2560), lg(-341778503, -671026265), 532386578) - test(lg(-1683940185, 34921), lg(-1907127360, 1144311248), -2131012273) - test(lg(-121723, -1), lg(756366897, -1994294687), -1642432978) - test(lg(-644688038, 9473), lg(-1363894143, 1241756453), 1681307793) - test(lg(-278047, -1), lg(1708006412, -1138876437), 2010442220) - test(lg(872834, 0), lg(-664430929, 446891142), -1707024855) - test(lg(-1, -1), lg(-1904131429, -938887), -829231944) - test(lg(-2101780246, 11998), lg(-1043053889, 1572668786), 309495249) - test(lg(-11427, -1), lg(563683687, -1497656119), -176819791) - test(lg(201, 0), lg(-627312011, 421917318), 2056663541) - test(lg(-104838948, -3), lg(-904956287, -543423347), -617227620) + test(12083834796762671L, 3093461707971243921L, -1316031160) + test(1519L, 3421928456457518817L, 1452874739) + test(535935432373L, 4495752255492475708L, 1356453463) + test(-15335L, -8632722372891668805L, 393061105) + test(5L, 788322396658845547L, -1980770119) + test(61456853258820810L, 7866477217129063791L, 1545082311) + test(523169438L, 2246995629203373230L, -2119005984) + test(-3140911288418704L, -3216293159340752798L, 511130378) + test(-197222925856L, -3308851627222811261L, -1176556648) + test(263770043357L, 1106331747933537319L, -1512809002) + test(-51L, -7276147772649213050L, 2083093369) + test(-5L, -2316677564967907265L, -207994821) + test(-13942941898828745L, -7138786252200317415L, -638486135) + test(-10706L, -3013225718780605504L, 611632432) + test(431055124131099630L, 6896881986097594087L, 244039684) + test(1L, 6268288981035101119L, -439796226) + test(7L, 8640634673209662748L, -652735044) + test(423071173443L, 3548978230116134282L, 613066583) + test(-2989967169176L, -6270415628770328030L, -775565931) + test(-3361820324L, -7219454171626000888L, -531660641) + test(5575500511542107L, 1427328130954779450L, -434372344) + test(-556401176231479L, -9116076871376536336L, -763866098) + test(-72307L, -2544065484579384992L, -1841005139) + test(72262L, 1271262762129413514L, 69079212) + test(-102489556331053708L, -6559331605187437274L, 1371712838) + test(70395261L, 4837525531312068659L, 1948323684) + test(-329L, -5915257366775763309L, -1243200330) + test(-88269909299L, -2961846669202422044L, 728732313) + test(-62655L, -8817858119848136407L, -777879057) + test(85473610109514853L, 85473610109514853L, 373478400) + test(-1L, -7586855577422630242L, 942391743) + test(5131L, 5777725881539893498L, -813574478) + test(9L, 1351155571146391276L, -641829383) + test(-14492L, -2039493177828382555L, -920501329) + test(40L, 5807947005141331651L, -177471111) + test(59705499154418508L, 7642303891765569108L, -508943033) + test(-37529L, -5281723454849434235L, 401183471) + test(376386L, 3310727509848511642L, 797274667) + test(-1822L, -2050466318844521906L, 1298272370) + test(-10994094310680L, -2882035858978840647L, 532386578) + test(149987163970727L, 4914779388992785344L, -2131012273) + test(-121723L, -8565430458495189455L, -1642432978) + test(40689875474266L, 5333303358163034241L, 1681307793) + test(-278047L, -4891437049391997940L, 2010442220) + test(872834L, 1919382843392628399L, -1707024855) + test(-1L, -4032486568803685L, -829231944) + test(51533210804458L, 6754561006561936063L, 309495249) + test(-11427L, -6432384051195600537L, -176819791) + test(201L, 1812121086093687413L, 2056663541) + test(-8694773540L, -2333985499857848703L, -617227620) } @Test def negate(): Unit = { @@ -1302,63 +1291,63 @@ class LongTest { assertEquals(expected, -hideFromOptimizer(x)) } - test(lg(0), lg(0)) - test(lg(1), lg(-1)) - test(lg(-1), lg(1)) - test(lg(1, -2147483648), MaxVal) + test(0L, 0L) + test(1L, -1L) + test(-1L, 1L) + test(-9223372036854775807L, MaxVal) test(MinVal, MinVal) - test(lg(0, -1), lg(0, 1)) - - test(lg(792771844, -1518464955), lg(-792771844, 1518464954)) - test(lg(1313283210, -1172119606), lg(-1313283210, 1172119605)) - test(lg(-1034897743, -341494686), lg(1034897743, 341494685)) - test(lg(-924881290, 1614058538), lg(924881290, -1614058539)) - test(lg(-1636891236, -1405401040), lg(1636891236, 1405401039)) - test(lg(2044349674, -477271433), lg(-2044349674, 477271432)) - test(lg(1426086684, -1493816436), lg(-1426086684, 1493816435)) - test(lg(-2125201680, 1667846199), lg(2125201680, -1667846200)) - test(lg(161054645, -1272528725), lg(-161054645, 1272528724)) - test(lg(-1013390126, -1323844683), lg(1013390126, 1323844682)) - test(lg(-1028806094, -691441881), lg(1028806094, 691441880)) - test(lg(1060422114, -11477649), lg(-1060422114, 11477648)) - test(lg(1366334123, -2046238761), lg(-1366334123, 2046238760)) - test(lg(1307711795, 940346049), lg(-1307711795, -940346050)) - test(lg(421687960, -250174762), lg(-421687960, 250174761)) - test(lg(379452754, -843386803), lg(-379452754, 843386802)) - test(lg(-1251296999, 1144268297), lg(1251296999, -1144268298)) - test(lg(-690359429, -1676679602), lg(690359429, 1676679601)) - test(lg(1952563749, -882544420), lg(-1952563749, 882544419)) - test(lg(-1420900897, -1865273591), lg(1420900897, 1865273590)) - test(lg(115947827, -832851217), lg(-115947827, 832851216)) - test(lg(-1834973959, -1423776005), lg(1834973959, 1423776004)) - test(lg(1376766876, 1519617584), lg(-1376766876, -1519617585)) - test(lg(-1845217535, 724725865), lg(1845217535, -724725866)) - test(lg(-1133294381, 699400553), lg(1133294381, -699400554)) - test(lg(113507585, 615978889), lg(-113507585, -615978890)) - test(lg(-1839784424, 1163726652), lg(1839784424, -1163726653)) - test(lg(1065777168, 1301742163), lg(-1065777168, -1301742164)) - test(lg(334075220, -1058529734), lg(-334075220, 1058529733)) - test(lg(1443112398, 1148167880), lg(-1443112398, -1148167881)) - test(lg(1647739462, 12310882), lg(-1647739462, -12310883)) - test(lg(1461318149, 518941731), lg(-1461318149, -518941732)) - test(lg(56833825, -162898592), lg(-56833825, 162898591)) - test(lg(-680096727, -1760413869), lg(680096727, 1760413868)) - test(lg(461541717, -1103626950), lg(-461541717, 1103626949)) - test(lg(1287248387, 1483137214), lg(-1287248387, -1483137215)) - test(lg(-1681467124, -1197977023), lg(1681467124, 1197977022)) - test(lg(-310946355, 885055747), lg(310946355, -885055748)) - test(lg(-717629012, -1299204708), lg(717629012, 1299204707)) - test(lg(800584851, 350245993), lg(-800584851, -350245994)) - test(lg(1911014238, -441020786), lg(-1911014238, 441020785)) - test(lg(-1647080824, -1197295589), lg(1647080824, 1197295588)) - test(lg(-925751968, -479541400), lg(925751968, 479541399)) - test(lg(-656919119, 1574890072), lg(656919119, -1574890073)) - test(lg(-1833364814, 432106462), lg(1833364814, -432106463)) - test(lg(-315730911, -1990201785), lg(315730911, 1990201784)) - test(lg(1218524771, -572482048), lg(-1218524771, 572482047)) - test(lg(276668811, 2002398729), lg(-276668811, -2002398730)) - test(lg(1489416833, 834462753), lg(-1489416833, -834462754)) - test(lg(2066446588, 688546120), lg(-2066446588, -688546121)) + test(-4294967296L, 4294967296L) + + test(-6521757321054339836L, 6521757321054339836L) + test(-5034215373457122166L, 5034215373457122166L) + test(-1466708504867719503L, 1466708504867719503L) + test(6932328637909659254L, -6932328637909659254L) + test(-6036151501906311780L, 6036151501906311780L) + test(-2049865194005705494L, 2049865194005705494L) + test(-6415892737421190372L, 6415892737421190372L) + test(7163344881632673520L, -7163344881632673520L) + test(-5465469256934522955L, 5465469256934522955L) + test(-5685869615186909998L, 5685869615186909998L) + test(-2969720262713562574L, 2969720262713562574L) + test(-49296126029544990L, 49296126029544990L) + test(-8788528556936226133L, 8788528556936226133L) + test(4038755528685525299L, -4038755528685525299L) + test(-1074492420652895592L, 1074492420652895592L) + test(-3622318736383541934L, 3622318736383541934L) + test(4914594916508285209L, -4914594916508285209L) + test(-7201284052855688325L, 7201284052855688325L) + test(-3790499419214724571L, 3790499419214724571L) + test(-8011289068563413537L, 8011289068563413537L) + test(-3577068739332851405L, 3577068739332851405L) + test(-6115071375844539143L, 6115071375844539143L) + test(6526707827083299740L, -6526707827083299740L) + test(3112673891190060801L, -3112673891190060801L) + test(3003902505100987603L, -3003902505100987603L) + test(2645609183394921729L, -2645609183394921729L) + test(4998167914278755864L, -4998167914278755864L) + test(5590940018975078416L, -5590940018975078416L) + test(-4546350589039504044L, 4546350589039504044L) + test(4931343496360764878L, -4931343496360764878L) + test(52874837222654534L, -52874837222654534L) + test(2228837764635947525L, -2228837764635947525L) + test(-699644125147613407L, 699644125147613407L) + test(-7560919991164957655L, 7560919991164957655L) + test(-4740041656772685483L, 4740041656772685483L) + test(6370025830897801731L, -6370025830897801731L) + test(-5145272132530939636L, 5145272132530939636L) + test(3801285492485871053L, -3801285492485871053L) + test(-5580041728091891284L, 5580041728091891284L) + test(1504295086290629779L, -1504295086290629779L) + test(-1894169850815200418L, 1894169850815200418L) + test(-5142345395752170872L, 5142345395752170872L) + test(-2059614626708839072L, 2059614626708839072L) + test(6764101357673133489L, -6764101357673133489L) + test(1855883125141869234L, -1855883125141869234L) + test(-8547851575036586975L, 8547851575036586975L) + test(-2458791672488577437L, 2458791672488577437L) + test(8600237054883635595L, -8600237054883635595L) + test(3583990235354542721L, -3583990235354542721L) + test(2957283069254138108L, -2957283069254138108L) } @Test def plus(): Unit = { @@ -1369,56 +1358,56 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x) + hideFromOptimizer(y)) } - test(lg(802149732, -566689627), lg(-202981355, -566689628), lg(1005131087, 0)) - test(lg(902769101, 1674149440), lg(1153016325, 1674149440), lg(-250247224, -1)) - test(lg(1128646485, -1965159800), lg(1701699755, -1965159800), lg(-573053270, -1)) - test(lg(66936416, -973893589), lg(-1183294843, -973893590), lg(1250231259, 0)) - test(lg(-155818001, 449544496), lg(-2145882999, 449544496), lg(1990064998, 0)) - test(lg(-1244599644, -917980205), lg(-528276750, -917980205), lg(-716322894, -1)) - test(lg(580594010, 1794016499), lg(-1061043923, 1794016498), lg(1641637933, 0)) - test(lg(-1874551871, 1883156001), lg(-315483661, 1883156001), lg(-1559068210, -1)) - test(lg(-611587809, 95409025), lg(-1899047326, 95409025), lg(1287459517, 0)) - test(lg(-1393747885, 1167571449), lg(-705065818, 1167571449), lg(-688682067, -1)) - test(lg(1135734754, -607437553), lg(-192210545, -607437554), lg(1327945299, 0)) - test(lg(545472170, -2007097641), lg(11453726, -2007097641), lg(534018444, 0)) - test(lg(-1984029353, -1191350400), lg(1809973610, -1191350400), lg(500964333, 0)) - test(lg(1031291620, 108684756), lg(972641234, 108684756), lg(58650386, 0)) - test(lg(-1375760766, 127758048), lg(-1511325903, 127758048), lg(135565137, 0)) - test(lg(640679472, 429508922), lg(-942832491, 429508921), lg(1583511963, 0)) - test(lg(-820503583, -594798242), lg(1500842230, -594798242), lg(1973621483, 0)) - test(lg(1875301895, 910473912), lg(-1088230684, 910473912), lg(-1331434717, -1)) - test(lg(-1755864971, 378724963), lg(798219431, 378724963), lg(1740882894, 0)) - test(lg(468052904, -683558197), lg(-1763683665, -683558197), lg(-2063230727, -1)) - test(lg(-1488850347, -1636478025), lg(627629519, -1636478024), lg(-2116479866, -1)) - test(lg(915882407, -338305025), lg(-526665240, -338305026), lg(1442547647, 0)) - test(lg(-950882103, -466473801), lg(-1265295286, -466473801), lg(314413183, 0)) - test(lg(-673278223, -1417005301), lg(-1412852606, -1417005301), lg(739574383, 0)) - test(lg(-1565299836, -2035157269), lg(708993121, -2035157269), lg(2020674339, 0)) - test(lg(638729196, 1182702858), lg(847269791, 1182702858), lg(-208540595, -1)) - test(lg(-1453651445, -1902383955), lg(97084677, -1902383954), lg(-1550736122, -1)) - test(lg(1116569659, -606967004), lg(-267181534, -606967005), lg(1383751193, 0)) - test(lg(529048030, 1063184820), lg(-904322265, 1063184819), lg(1433370295, 0)) - test(lg(-499260224, 101142421), lg(1841727454, 101142421), lg(1953979618, 0)) - test(lg(1452864874, 1045175929), lg(-1716387490, 1045175929), lg(-1125714932, -1)) - test(lg(982736721, 1506316757), lg(-1020814821, 1506316756), lg(2003551542, 0)) - test(lg(-1478064805, 1107506955), lg(467820886, 1107506956), lg(-1945885691, -1)) - test(lg(1436947166, -57552832), lg(-103701719, -57552833), lg(1540648885, 0)) - test(lg(3887456, -414981457), lg(1280780483, -414981457), lg(-1276893027, -1)) - test(lg(939083871, 606376864), lg(-1505747919, 606376864), lg(-1850135506, -1)) - test(lg(-1161495325, -606274238), lg(-1797917239, -606274238), lg(636421914, 0)) - test(lg(2146013782, 52949338), lg(-551974000, 52949338), lg(-1596979514, -1)) - test(lg(-159062053, -623553409), lg(484182807, -623553408), lg(-643244860, -1)) - test(lg(1680160313, 371486519), lg(1170065239, 371486519), lg(510095074, 0)) - test(lg(-2071737549, -251530660), lg(553737773, -251530660), lg(1669491974, 0)) - test(lg(793877651, -324566030), lg(1363264202, -324566030), lg(-569386551, -1)) - test(lg(1897556965, 1255689015), lg(1461362302, 1255689015), lg(436194663, 0)) - test(lg(-540868058, 718534179), lg(-1463314706, 718534179), lg(922446648, 0)) - test(lg(2547531, -716998232), lg(-1684072850, -716998233), lg(1686620381, 0)) - test(lg(-1709813271, -2086072551), lg(-183257712, -2086072551), lg(-1526555559, -1)) - test(lg(-2134341942, -1223154956), lg(-485818523, -1223154956), lg(-1648523419, -1)) - test(lg(1634619686, -1934382665), lg(392330048, -1934382665), lg(1242289638, 0)) - test(lg(-1409927090, -75135322), lg(1907808353, -75135322), lg(977231853, 0)) - test(lg(-1393001322, 1362535802), lg(88305723, 1362535803), lg(-1481307045, -1)) + test(-2433913414145288860L, -2433913415150419947L, 1005131087L) + test(7190417094319483341L, 7190417094569730565L, -250247224L) + test(-8440297071285254315L, -8440297070712201045L, -573053270L) + test(-4182841114472128928L, -4182841115722360187L, 1250231259L) + test(1930778912555952111L, 1930778910565887113L, 1990064998L) + test(-3942694955800008028L, -3942694955083685134L, -716322894L) + test(7705242192270010714L, 7705242190628372781L, 1641637933L) + test(8088093439981558721L, 8088093441540626931L, -1559068210L) + test(409778645801625887L, 409778644514166370L, 1287459517L) + test(5014681192099551315L, 5014681192788233382L, -688682067L) + test(-2608924423361531934L, -2608924424689477233L, 1327945299L) + test(-8620418727428276566L, -8620418727962295010L, 534018444L) + test(-5116811003765580457L, -5116811004266544790L, 500964333L) + test(466797473625031396L, 466797473566381010L, 58650386L) + test(548716640880004738L, 548716640744439601L, 135565137L) + test(1844726773970894384L, 1844726772387382421L, 1583511963L) + test(-2554638993633829919L, -2554638995607451402L, 1973621483L) + test(3910455677776483847L, 3910455679107918564L, -1331434717L) + test(1626611332802912373L, 1626611331062029479L, 1740882894L) + test(-2935860100559672408L, -2935860098496441681L, -2063230727L) + test(-7028619595191553451L, -7028619593075073585L, -2116479866L) + test(-1453009017531579993L, -1453009018974127640L, 1442547647L) + test(-2003489716391726903L, -2003489716706140086L, 314413183L) + test(-6085991422431947023L, -6085991423171521406L, 739574383L) + test(-8740933909842007164L, -8740933911862681503L, 2020674339L) + test(5079670096634461164L, 5079670096843001759L, -208540595L) + test(-8170676868318819829L, -8170676866768083707L, -1550736122L) + test(-2606903430814531525L, -2606903432198282718L, 1383751193L) + test(4566344032032694750L, 4566344030599324455L, 1433370295L) + test(434403394228970688L, 434403392274991070L, 1953979618L) + test(4488996435074282858L, 4488996436199997790L, -1125714932L) + test(6469581209714515793L, 6469581207710964251L, 2003551542L) + test(4756706154634446171L, 4756706156580331862L, -1945885691L) + test(-247187529795235106L, -247187531335883991L, 1540648885L) + test(-1782331786257542816L, -1782331784980649789L, -1276893027L) + test(2604368800870123615L, 2604368802720259121L, -1850135506L) + test(-2603928021483848477L, -2603928022120270391L, 636421914L) + test(227415677200863830L, 227415678797843344L, -1596979514L) + test(-2678141494828406821L, -2678141494185161961L, -643244860L) + test(1595522451690042937L, 1595522451179947863L, 510095074L) + test(-1080315956418065613L, -1080315958087557587L, 1669491974L) + test(-1394000483448677229L, -1394000482879290678L, -569386551L) + test(5393143255269010405L, 5393143254832815742L, 436194663L) + test(3086080803617309222L, 3086080802694862574L, 922446648L) + test(-3079483957727273141L, -3079483959413893522L, 1686620381L) + test(-8959613381043138071L, -8959613379516582512L, -1526555559L) + test(-5253410531799693622L, -5253410530151170203L, -1648523419L) + test(-8308110282489704154L, -8308110283731993792L, 1242289638L) + test(-322703747879389106L, -322703748856620959L, 977231853L) + test(5852046712121097366L, 5852046713602404411L, -1481307045L) } @Test def minus(): Unit = { @@ -1430,58 +1419,58 @@ class LongTest { } // Whitebox corner case - test(lg(-1), lg(0), lg(1)) - - test(lg(1318078695, 462416044), lg(406229717, 462416044), lg(-911848978, -1)) - test(lg(459412414, 466142261), lg(873646396, 466142261), lg(414233982, 0)) - test(lg(1749422706, -573388520), lg(-2077914189, -573388520), lg(467630401, 0)) - test(lg(855866353, -1980988131), lg(-789253983, -1980988132), lg(-1645120336, -1)) - test(lg(1858485462, 1825277273), lg(-482388232, 1825277273), lg(1954093602, 0)) - test(lg(1211608504, -1077757379), lg(-1616159373, -1077757379), lg(1467199419, 0)) - test(lg(-1391411781, -1825579414), lg(-105778670, -1825579414), lg(1285633111, 0)) - test(lg(1573921037, -2018677385), lg(1306759468, -2018677385), lg(-267161569, -1)) - test(lg(2075838974, -289291128), lg(618139116, -289291128), lg(-1457699858, -1)) - test(lg(600013127, -1980710784), lg(1736445522, -1980710784), lg(1136432395, 0)) - test(lg(-558434179, 21136449), lg(-1970971750, 21136449), lg(-1412537571, -1)) - test(lg(-343650116, 229693364), lg(-1491842755, 229693364), lg(-1148192639, -1)) - test(lg(1686071974, -2064363005), lg(2125082313, -2064363005), lg(439010339, 0)) - test(lg(-1587252411, -1887690341), lg(922634658, -1887690341), lg(-1785080227, -1)) - test(lg(-992416688, 1754335328), lg(478015362, 1754335329), lg(1470432050, 0)) - test(lg(1718268050, -845578935), lg(-1788952896, -845578935), lg(787746350, 0)) - test(lg(1316319511, -1479013672), lg(-1177368338, -1479013672), lg(1801279447, 0)) - test(lg(1568876561, -2147323821), lg(1761081661, -2147323821), lg(192205100, 0)) - test(lg(-1122491731, 1604940224), lg(261772552, 1604940225), lg(1384264283, 0)) - test(lg(1556996455, 1018615990), lg(-1441241840, 1018615990), lg(1296729001, 0)) - test(lg(-52258673, -155632234), lg(907527568, -155632233), lg(959786241, 0)) - test(lg(1911811399, 1534910973), lg(1509034771, 1534910973), lg(-402776628, -1)) - test(lg(1234505303, -718856464), lg(-344668006, -718856465), lg(-1579173309, -1)) - test(lg(1263823751, 1792314521), lg(-2096618226, 1792314521), lg(934525319, 0)) - test(lg(-1901870284, -977488448), lg(1861956484, -977488448), lg(-531140528, -1)) - test(lg(170060904, -1532994269), lg(-691455907, -1532994270), lg(-861516811, -1)) - test(lg(-417244722, -946809431), lg(-693769914, -946809431), lg(-276525192, -1)) - test(lg(1392505816, -834216711), lg(-1698674051, -834216711), lg(1203787429, 0)) - test(lg(339105023, -930632047), lg(1453492556, -930632047), lg(1114387533, 0)) - test(lg(1588670098, -422836102), lg(-516102112, -422836103), lg(-2104772210, -1)) - test(lg(-1793332542, 1839759286), lg(1194707556, 1839759286), lg(-1306927198, -1)) - test(lg(-1933743595, -1652840750), lg(1188016800, -1652840750), lg(-1173206901, -1)) - test(lg(1172675504, 1790839027), lg(-1268512415, 1790839027), lg(1853779377, 0)) - test(lg(-2038245078, 275932678), lg(-777434907, 275932678), lg(1260810171, 0)) - test(lg(-640120196, 658575618), lg(607917442, 658575619), lg(1248037638, 0)) - test(lg(-939204613, -2089057829), lg(-1490388970, -2089057829), lg(-551184357, -1)) - test(lg(-2089897031, 992436418), lg(-1342917439, 992436418), lg(746979592, 0)) - test(lg(-767046771, -1192540532), lg(-1045496394, -1192540532), lg(-278449623, -1)) - test(lg(735191894, -683257085), lg(1555450000, -683257085), lg(820258106, 0)) - test(lg(2026420598, 481753248), lg(1022728181, 481753248), lg(-1003692417, -1)) - test(lg(-2132649422, 1411964223), lg(2028304312, 1411964223), lg(-134013562, -1)) - test(lg(1346424260, -217374406), lg(704117341, -217374406), lg(-642306919, -1)) - test(lg(-692878557, 278237510), lg(313351245, 278237511), lg(1006229802, 0)) - test(lg(-1545280043, 2054685372), lg(2076724262, 2054685372), lg(-672962991, -1)) - test(lg(1156651977, 261806288), lg(1990098163, 261806288), lg(833446186, 0)) - test(lg(-244547539, 1626774417), lg(1425435353, 1626774418), lg(1669982892, 0)) - test(lg(-125857115, -1714068645), lg(2084724465, -1714068645), lg(-2084385716, -1)) - test(lg(-2124426763, -543675020), lg(-1799809279, -543675020), lg(324617484, 0)) - test(lg(-2145169231, -602489858), lg(1972622018, -602489858), lg(-177176047, -1)) - test(lg(408960051, 967789979), lg(883147297, 967789979), lg(474187246, 0)) + test(-1L, 0L, 1L) + + test(1986061787443775719L, 1986061786531926741L, -911848978L) + test(2002065766737908670L, 2002065767152142652L, 414233982L) + test(-2462684939552419214L, -2462684939084788813L, 467630401L) + test(-8508279235553297423L, -8508279237198417759L, -1645120336L) + test(7839506195525549270L, 7839506197479642872L, 1954093602L) + test(-4628932694616068680L, -4628932693148869261L, 1467199419L) + test(-7840803876477289029L, -7840803875191655918L, 1285633111L) + test(-8670153348175879923L, -8670153348443041492L, -267161569L) + test(-1242495931707110914L, -1242495933164810772L, -1457699858L) + test(-8507088039514506937L, -8507088038378074542L, 1136432395L) + test(90780360945105021L, 90780359532567450L, -1412537571L) + test(986525490439540924L, 986525489291348285L, -1148192639L) + test(-8866371591861212506L, -8866371591422202167L, 439010339L) + test(-8107568276862373051L, -8107568278647453278L, -1785080227L) + test(7534812863279983696L, 7534812864750415746L, 1470432050L) + test(-3631733870293241710L, -3631733869505495360L, 787746350L) + test(-6352315350260551401L, -6352315348459271954L, 1801279447L) + test(-9222685583547881455L, -9222685583355676355L, 192205100L) + test(6893165777287389869L, 6893165778671654152L, 1384264283L) + test(4374922365789659495L, 4374922367086388496L, 1296729001L) + test(-668435350990710641L, -668435350030924400L, 959786241L) + test(6592392433218350407L, 6592392432815573779L, -402776628L) + test(-3087465002163696041L, -3087465003742869350L, -1579173309L) + test(7697932253104728967L, 7697932254039254286L, 934525319L) + test(-4198280913984699596L, -4198280914515840124L, -531140528L) + test(-6584160250140365720L, -6584160251001882531L, -861516811L) + test(-4066515537811646002L, -4066515538088171194L, -276525192L) + test(-3582933490129177640L, -3582933488925390211L, 1203787429L) + test(-3997034206135429889L, -3997034205021042356L, 1114387533L) + test(-1816067228069450094L, -1816067230174222304L, -2104772210L) + test(7901705968383945410L, 7901705967077018212L, -1306927198L) + test(-7098896964384888299L, -7098896965558095200L, -1173206901L) + test(7691595054538136496L, 7691595056391915873L, 1853779377L) + test(1185121830164420906L, 1185121831425231077L, 1260810171L) + test(2828560744907836028L, 2828560746155873666L, 1248037638L) + test(-8972435051651997701L, -8972435052203182058L, -551184357L) + test(4262481960874455993L, 4262481961621435585L, 746979592L) + test(-5121922580566520947L, -5121922580844970570L, -278449623L) + test(-2934566834100100266L, -2934566833279842160L, 820258106L) + test(2069114446928198006L, 2069114445924505589L, -1003692417L) + test(6064340163069368882L, 6064340162935355320L, -134013562L) + test(-933615963411001916L, -933615964053308835L, -642306919L) + test(1195021009572561699L, 1195021010578791501L, 1006229802L) + test(8824806479059281365L, 8824806478386318374L, -672962991L) + test(1124449446003809225L, 1124449446837255411L, 833446186L) + test(6986942923034886189L, 6986942924704869081L, 1669982892L) + test(-7361868769204923739L, -7361868771289309455L, -2084385716L) + test(-2335066428381605387L, -2335066428056987903L, 324617484L) + test(-2587674234131885903L, -2587674234309061950L, -177176047L) + test(4156626309610486835L, 4156626310084674081L, 474187246L) } @Test def times(): Unit = { @@ -1492,56 +1481,56 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x) * hideFromOptimizer(y)) } - test(lg(-1056314208, 1039912134), lg(-1436299491, 1172705251), lg(1721031968, 0)) - test(lg(15417694, -1235494072), lg(-1754547158, 1592794750), lg(-850659149, -1)) - test(lg(-1312839754, -486483117), lg(-582562130, 1508550574), lg(-2054981347, -1)) - test(lg(-377676239, 1969822597), lg(-517256163, 1107889737), lg(324089381, 0)) - test(lg(-1426078720, -1379092277), lg(1862517504, -2146745095), lg(2043533548, 0)) - test(lg(-1611894400, 514550890), lg(-1341087062, 93674761), lg(1272468928, 0)) - test(lg(88803236, -172420721), lg(-1911825604, 1026411170), lg(244738503, 0)) - test(lg(1486387579, 668666773), lg(2102189793, 425022510), lg(750432219, 0)) - test(lg(913918418, 2124658288), lg(-1628887094, 2043879870), lg(-1367964491, -1)) - test(lg(-1067082241, 864193319), lg(454909009, -1096315634), lg(-461844145, -1)) - test(lg(949541055, 403324299), lg(-1346593793, -331776468), lg(1495188289, 0)) - test(lg(-232871624, -1943313306), lg(39946028, -363039140), lg(-1134101206, -1)) - test(lg(-528828160, -1884969955), lg(769959254, -432157368), lg(-488368768, -1)) - test(lg(913322937, -2105457977), lg(1975078475, 1181124823), lg(-1852476533, -1)) - test(lg(1594278208, 943829214), lg(-2118478876, -1521449422), lg(-235907376, -1)) - test(lg(-50678328, 2146883835), lg(-192590815, -1552754278), lg(990887112, 0)) - test(lg(1779498513, -1732099612), lg(-74714605, 386143916), lg(1634792395, 0)) - test(lg(982209626, 857499597), lg(1839773441, -590412588), lg(799604314, 0)) - test(lg(1806268816, -990479821), lg(1395571130, -1228992407), lg(1440046952, 0)) - test(lg(1683728223, -957382628), lg(-1094818235, 1759139279), lg(-156634285, -1)) - test(lg(-1590791694, 595489480), lg(853844787, 525523561), lg(600761926, 0)) - test(lg(1353714367, 146465211), lg(-903115469, 793487771), lg(1986597957, 0)) - test(lg(1421874569, -1462441210), lg(-830036223, 830164681), lg(-1711884663, -1)) - test(lg(-962035602, -2086325336), lg(1514898873, 1802395563), lg(1763957470, 0)) - test(lg(213232144, -1084932179), lg(-1931885288, 136587512), lg(-241565738, -1)) - test(lg(-915935202, 1495104097), lg(571274323, 1264898114), lg(1823828906, 0)) - test(lg(1116543789, -1473151538), lg(-15708939, -2105030313), lg(48280153, 0)) - test(lg(-1230228445, -570579388), lg(1792017337, -1626094957), lg(301685947, 0)) - test(lg(1335719116, 1447187791), lg(-1942632452, -691115342), lg(-889918259, -1)) - test(lg(1398640985, -1330552693), lg(-683458011, -1409200935), lg(-996910555, -1)) - test(lg(-402621042, 1775759707), lg(562125786, -1303526635), lg(-1761056509, -1)) - test(lg(129149596, -78429064), lg(2115902292, -1194658096), lg(-1549721205, -1)) - test(lg(1706925885, 1413499189), lg(1852083423, 330104035), lg(1414822755, 0)) - test(lg(-722178384, 1850552711), lg(-1623207532, 1442771787), lg(-948878276, -1)) - test(lg(545021767, -1389368834), lg(-898643831, 773279296), lg(1294488911, 0)) - test(lg(1541594150, 820379725), lg(421823854, 802578424), lg(1394107269, 0)) - test(lg(-279324848, 1175391379), lg(1589092022, 237831212), lg(-763790472, -1)) - test(lg(2089067814, 975727054), lg(-1247207721, -370556328), lg(1449901386, 0)) - test(lg(-1977714127, -377823390), lg(109386811, 368962517), lg(1406834819, 0)) - test(lg(1759713497, -312922364), lg(2135299059, -798752868), lg(-1861488893, -1)) - test(lg(1030024362, -795941843), lg(-695671854, 1917612060), lg(2083344781, 0)) - test(lg(-704748314, 388197332), lg(250669253, -442179349), lg(-552836178, -1)) - test(lg(758103782, -158300478), lg(1237744278, 206295616), lg(-1547545223, -1)) - test(lg(-629736326, 810097466), lg(492775518, 1691641907), lg(1172634963, 0)) - test(lg(610754048, 1997636055), lg(-1549380722, 49835026), lg(-1645815552, -1)) - test(lg(1696857284, 1549588995), lg(1850430325, -1942955614), lg(-295254732, -1)) - test(lg(-66011146, -376837532), lg(-1276671498, -1984743584), lg(-1583554303, -1)) - test(lg(2033040344, -167450557), lg(-2127158934, -2058421178), lg(1620104636, 0)) - test(lg(-1886196376, -31345953), lg(69958717, -772556465), lg(21655944, 0)) - test(lg(-38147573, -1269583268), lg(406538265, -107036516), lg(2077087683, 0)) + test(4466388609482222752L, 5036730703751139101L, 1721031968L) + test(-5306406633626451618L, 6841001363030916138L, -850659149L) + test(-2089429074589014090L, 6479175383404433070L, -2054981347L) + test(8460323636954078769L, 4758350191766752285L, 324089381L) + test(-5923156225012284416L, -9220199974010895616L, 2043533548L) + test(2209979247360766336L, 402330037909496490L, 1272468928L) + test(-740541357758937180L, 4408402409782238012L, 244738503L) + test(2871901923443243387L, 1825457782616022753L, 750432219L) + test(9125337863049267666L, 8778397201268811722L, -1367964491L) + test(3711682045754580479L, -4708639793668596655L, -461844145L) + test(1732264674836666559L, -1424969076694017025L, 1495188289L) + test(-8346467091089544904L, -1559241233428019412L, -1134101206L) + test(-8095884306901452544L, -1856101761515477674L, -488368768L) + test(-9042873153403997255L, 5072892489253867083L, -1852476533L) + test(4053715608733663552L, -6534575507831614492L, -235907376L) + test(9220795863880349128L, -6669028838631715807L, 990887112L) + test(-7439311185174790639L, 1658475494989623827L, 1634792395L) + test(3682932726430389338L, -2535802754766948607L, 799604314L) + test(-4254078436736665200L, -5278482193701750342L, 1440046952L) + test(-4111927075334805665L, 7555445675614168645L, -156634285L) + test(2557607844416221682L, 2257106508626305843L, 600761926L) + test(629063292600453823L, 3408004029612789043L, 1986597957L) + test(-6281137167850793591L, 3565530158654203649L, -1711884663L) + test(-8960699083603279762L, 7741229999055406521L, 1763957470L) + test(-4659748226969785840L, 586638899445089560L, -241565738L) + test(6421423204109643806L, 5432696032973354067L, 1823828906L) + test(-6327137676645557459L, -9041036347144385291L, 48280153L) + test(-2450619808166955997L, -6984024658713508935L, 301685947L) + test(6215624234851202252L, -2968317789301520388L, -889918259L) + test(-5714680300641087143L, -6052471925706112475L, -996910555L) + test(7626829871011888526L, -5598604266227803174L, -1761056509L) + test(-336850264806741348L, -5131017450105726124L, -1549721205L) + test(6070932791384448829L, 1417786036454722783L, 1414822755L) + test(7948063376841928368L, 6196657643428237716L, -948878276L) + test(-5967293703566631097L, 3321209290390227081L, 1294488911L) + test(3523504090718067750L, 3447048083977045358L, 1394107269L) + test(5048267536820983632L, 1021477279097134774L, -763790472L) + test(4190715788841493798L, -1591527307038089513L, 1449901386L) + test(-1622739101396600271L, 1584681944074230843L, 1406834819L) + test(-1343991317807294247L, -3430617443510905869L, -1861488893L) + test(-3418544184172942166L, 8236081087714485202L, 2083344781L) + test(1667294848924673254L, -1899145842670901051L, -552836178L) + test(-679895375193063706L, 886032925265918614L, -1547545223L) + test(3479342126707702906L, 7265546667600848990L, 1172634963L) + test(8579781526146211328L, 214039809610896270L, -1645815552L) + test(6655434057463364804L, -8344930817859169419L, -295254732L) + test(-1618504871616397322L, -8524408781207533066L, -1583554303L) + test(-719194663978943528L, -8840851638735986326L, 1620104636L) + test(-134629840588182168L, -3318104751418409923L, 21655944L) + test(-5452818611351983605L, -459718335291242471L, 2077087683L) test(8433193943336928478L, -304510477859059605L, -504694402761190L) test(-12731773183499098L, -253162060478L, 50291L) @@ -1705,362 +1694,362 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x) / hideFromOptimizer(y)) } - test(IntMaxValPlus1, IntMinVal, lg(-1)) - test(lg(-1), IntMinVal, IntMaxValPlus1) - test(IntMinVal, IntMaxValPlus1, lg(-1)) - test(lg(-1), IntMaxValPlus1, IntMinVal) + test(IntMaxValPlus1, IntMinVal, -1L) + test(-1L, IntMinVal, IntMaxValPlus1) + test(IntMinVal, IntMaxValPlus1, -1L) + test(-1L, IntMaxValPlus1, IntMinVal) - test(lg(1, -2147483648), MaxVal, lg(-1)) - test(MinVal, MinVal, lg(1)) - test(MinVal, MinVal, lg(-1)) + test(-9223372036854775807L, MaxVal, -1L) + test(MinVal, MinVal, 1L) + test(MinVal, MinVal, -1L) // int32, int32 - test(lg(1, 0), lg(-10426835, -1), lg(-6243356, -1)) - test(lg(-291, -1), lg(49659080, 0), lg(-170373, -1)) - test(lg(3, 0), lg(97420, 0), lg(27521, 0)) - test(lg(26998, 0), lg(-9881291, -1), lg(-366, -1)) - test(lg(0, 0), lg(-40, -1), lg(81, 0)) - test(lg(0, 0), lg(-6007, -1), lg(-326806, -1)) - test(lg(-1, -1), lg(202, 0), lg(-112, -1)) - test(lg(0, 0), lg(0, 0), lg(47, 0)) - test(lg(323816, 0), lg(22667160, 0), lg(70, 0)) - test(lg(0, 0), lg(254, 0), lg(-307349204, -1)) - test(lg(0, 0), lg(-17, -1), lg(-44648, -1)) - test(lg(-40, -1), lg(39646, 0), lg(-976, -1)) - test(lg(0, 0), lg(9, 0), lg(315779722, 0)) - test(lg(0, 0), lg(-2674, -1), lg(-3051991, -1)) - test(lg(0, 0), lg(-37697, -1), lg(2015928, 0)) - test(lg(0, 0), lg(-13, -1), lg(-31, -1)) - test(lg(0, 0), lg(6, 0), lg(-334, -1)) - test(lg(8, 0), lg(-15989, -1), lg(-1918, -1)) - test(lg(8746, 0), lg(-113261535, -1), lg(-12950, -1)) - test(lg(55322, 0), lg(-6362112, -1), lg(-115, -1)) - test(lg(0, 0), lg(455, 0), lg(13919, 0)) - test(lg(36190, 0), lg(293468259, 0), lg(8109, 0)) - test(lg(1, 0), lg(-48287007, -1), lg(-27531186, -1)) - test(lg(349634, 0), lg(1048904, 0), lg(3, 0)) - test(lg(0, 0), lg(-34, -1), lg(3949717, 0)) - test(lg(-1, -1), lg(1449, 0), lg(-983, -1)) - test(lg(-18537151, -1), lg(18537151, 0), lg(-1, -1)) - test(lg(0, 0), lg(14037, 0), lg(23645, 0)) - test(lg(-4, -1), lg(1785, 0), lg(-398, -1)) - test(lg(0, 0), lg(346, 0), lg(2198158, 0)) - test(lg(-802, -1), lg(-3517419, -1), lg(4381, 0)) - test(lg(-6, -1), lg(6, 0), lg(-1, -1)) - test(lg(39, 0), lg(-822, -1), lg(-21, -1)) - test(lg(0, 0), lg(3629, 0), lg(282734, 0)) - test(lg(-92367, -1), lg(-278856469, -1), lg(3019, 0)) - test(lg(0, 0), lg(-13, -1), lg(37, 0)) - test(lg(0, 0), lg(-4, -1), lg(47150459, 0)) - test(lg(0, 0), lg(-26, -1), lg(-210691, -1)) - test(lg(0, 0), lg(-21294, -1), lg(156839456, 0)) - test(lg(0, 0), lg(-5, -1), lg(-25644, -1)) - test(lg(0, 0), lg(-1009, -1), lg(28100, 0)) - test(lg(-857, -1), lg(16282815, 0), lg(-18989, -1)) - test(lg(-7, -1), lg(-2201086, -1), lg(276963, 0)) - test(lg(-300, -1), lg(11412578, 0), lg(-37989, -1)) - test(lg(0, 0), lg(8406900, 0), lg(239727371, 0)) - test(lg(0, 0), lg(-1, -1), lg(-479069, -1)) - test(lg(0, 0), lg(4, 0), lg(-21776, -1)) - test(lg(-16812960, -1), lg(-16812960, -1), lg(1, 0)) - test(lg(0, 0), lg(10873, 0), lg(57145, 0)) - test(lg(0, 0), lg(-1, -1), lg(-7, -1)) + test(1L, -10426835L, -6243356L) + test(-291L, 49659080L, -170373L) + test(3L, 97420L, 27521L) + test(26998L, -9881291L, -366L) + test(0L, -40L, 81L) + test(0L, -6007L, -326806L) + test(-1L, 202L, -112L) + test(0L, 0L, 47L) + test(323816L, 22667160L, 70L) + test(0L, 254L, -307349204L) + test(0L, -17L, -44648L) + test(-40L, 39646L, -976L) + test(0L, 9L, 315779722L) + test(0L, -2674L, -3051991L) + test(0L, -37697L, 2015928L) + test(0L, -13L, -31L) + test(0L, 6L, -334L) + test(8L, -15989L, -1918L) + test(8746L, -113261535L, -12950L) + test(55322L, -6362112L, -115L) + test(0L, 455L, 13919L) + test(36190L, 293468259L, 8109L) + test(1L, -48287007L, -27531186L) + test(349634L, 1048904L, 3L) + test(0L, -34L, 3949717L) + test(-1L, 1449L, -983L) + test(-18537151L, 18537151L, -1L) + test(0L, 14037L, 23645L) + test(-4L, 1785L, -398L) + test(0L, 346L, 2198158L) + test(-802L, -3517419L, 4381L) + test(-6L, 6L, -1L) + test(39L, -822L, -21L) + test(0L, 3629L, 282734L) + test(-92367L, -278856469L, 3019L) + test(0L, -13L, 37L) + test(0L, -4L, 47150459L) + test(0L, -26L, -210691L) + test(0L, -21294L, 156839456L) + test(0L, -5L, -25644L) + test(0L, -1009L, 28100L) + test(-857L, 16282815L, -18989L) + test(-7L, -2201086L, 276963L) + test(-300L, 11412578L, -37989L) + test(0L, 8406900L, 239727371L) + test(0L, -1L, -479069L) + test(0L, 4L, -21776L) + test(-16812960L, -16812960L, 1L) + test(0L, 10873L, 57145L) + test(0L, -1L, -7L) // int32, int53 - test(lg(0, 0), lg(-6975858, -1), lg(42227636, 14)) - test(lg(0, 0), lg(-1, -1), lg(370644892, 82735)) - test(lg(0, 0), lg(43, 0), lg(-1602218381, 49)) - test(lg(0, 0), lg(4063968, 0), lg(973173538, 23810)) - test(lg(0, 0), lg(-388987094, -1), lg(-241988155, 1723)) - test(lg(0, 0), lg(5939808, 0), lg(-1882484681, 12)) - test(lg(0, 0), lg(7, 0), lg(-385609304, 1342)) - test(lg(0, 0), lg(-1175803932, -1), lg(297649103, 2408)) - test(lg(0, 0), lg(464610492, 0), lg(829919518, 2777)) - test(lg(0, 0), lg(214483, 0), lg(1502817270, 8078)) + test(0L, -6975858L, 60171769780L) + test(0L, -1L, 355344489879452L) + test(0L, 43L, 213146146419L) + test(0L, 4063968L, 102264144491298L) + test(0L, -388987094L, 7404281630149L) + test(0L, 5939808L, 53952090167L) + test(0L, 7L, 5767755469224L) + test(0L, -1175803932L, 10342578897871L) + test(0L, 464610492L, 11927954100510L) + test(0L, 214483L, 34696248634358L) // int32, big - test(lg(0, 0), lg(211494165, 0), lg(1365318534, 14804989)) - test(lg(0, 0), lg(5353, 0), lg(-1032992082, -394605386)) - test(lg(0, 0), lg(2926, 0), lg(26982087, -226814570)) - test(lg(0, 0), lg(-6, -1), lg(-1339229562, -580578613)) - test(lg(0, 0), lg(-8, -1), lg(-108570365, 4920615)) - test(lg(0, 0), lg(-585878041, -1), lg(551925027, -1296114209)) - test(lg(0, 0), lg(-4, -1), lg(474545806, 64068407)) - test(lg(0, 0), lg(34, 0), lg(-137127086, -18652281)) - test(lg(0, 0), lg(785315, 0), lg(-881374655, 29722835)) - test(lg(0, 0), lg(713146, 0), lg(1442548271, 2727525)) + test(0L, 211494165L, 63586944937958278L) + test(0L, 5353L, -1694817224433481042L) + test(0L, 2926L, -974161160379320633L) + test(0L, -6L, -2493566152636302714L) + test(0L, -8L, 21133884687603971L) + test(0L, -585878041L, -5566768138983983837L) + test(0L, -4L, 275171713246363278L) + test(0L, 34L, -80110932732961966L) + test(0L, 785315L, 127658607682996801L) + test(0L, 713146L, 11714632116570671L) // int53, int32 - test(lg(-578207, -1), lg(397755625, 53271), lg(-395701427, -1)) - test(lg(-560062154, 0), lg(-1680186460, 2), lg(3, 0)) - test(lg(-926675094, 18), lg(1514942014, 56), lg(3, 0)) - test(lg(-162400270, -1), lg(713597492, 1154), lg(-30524, -1)) - test(lg(-9, -1), lg(2028377478, 1), lg(-691707459, -1)) - test(lg(135006, 0), lg(1387175556, 73), lg(2332622, 0)) - test(lg(-200274428, -13), lg(1756997282, 1397), lg(-116, -1)) - test(lg(1125157, 0), lg(-1655346723, 0), lg(2346, 0)) - test(lg(997096, 0), lg(198249458, 5686), lg(24492497, 0)) - test(lg(1369365326, -302), lg(873090497, 11162), lg(-37, -1)) - test(lg(-2166511, -1), lg(360057887, 3519), lg(-6976354, -1)) - test(lg(1680790298, -2), lg(1115898639, 48), lg(-30, -1)) - test(lg(92036331, 1), lg(154624251, 955), lg(935, 0)) - test(lg(23215066, 0), lg(806830498, 1063), lg(196698, 0)) - test(lg(-13221428, -1), lg(-220365267, 21359), lg(-6938757, -1)) - test(lg(-973041595, -2009), lg(759822848, 648657), lg(-323, -1)) - test(lg(171873494, 1659), lg(-1180673754, 486098), lg(293, 0)) - test(lg(1583541189, 785), lg(1387172319, 769661), lg(980, 0)) - test(lg(-917576, -1), lg(-305851327, 2), lg(-13709, -1)) - test(lg(456092, 0), lg(577374631, 17), lg(161353, 0)) - test(lg(404991630, 376), lg(809983260, 752), lg(2, 0)) - test(lg(495082175, 39), lg(495082175, 39), lg(1, 0)) - test(lg(90893135, 0), lg(1455620681, 30929), lg(1461502, 0)) - test(lg(799104733, 0), lg(1388707384, 34362), lg(184688, 0)) - test(lg(1094556328, -70011), lg(2105854641, 140021), lg(-2, -1)) - test(lg(-1819673734, 1), lg(1310105355, 427420), lg(271150, 0)) - test(lg(-119338773, -6), lg(-236557650, 35455), lg(-7052, -1)) - test(lg(32825, 0), lg(-1127581476, 0), lg(96492, 0)) - test(lg(-57018115, -1), lg(2004387480, 7243), lg(-545624, -1)) - test(lg(-5950946, -1), lg(381447319, 2213), lg(-1597249, -1)) - test(lg(-811421531, -4249), lg(-1860702702, 12744), lg(-3, -1)) - test(lg(4741011, 0), lg(-548164065, 6487), lg(5877480, 0)) - test(lg(-1064193809, 45), lg(-476290317, 131491), lg(2874, 0)) - test(lg(228327608, 0), lg(499912484, 1), lg(21, 0)) - test(lg(99111506, 0), lg(-1509435894, 8467), lg(366943, 0)) - test(lg(-1209485521, -1), lg(-1580093356, 5), lg(-20, -1)) - test(lg(-319956618, -1), lg(1299112295, 55074), lg(-739295, -1)) - test(lg(-62197, -1), lg(-1405948570, 43), lg(-3015755, -1)) - test(lg(9087, 0), lg(1405130313, 57), lg(27093454, 0)) - test(lg(345582531, 0), lg(-1804200888, 1989226), lg(24722497, 0)) - test(lg(-1424974, -1), lg(-1642507127, 886), lg(-2672324, -1)) - test(lg(1991351, 0), lg(-1276796892, 35), lg(77004, 0)) - test(lg(1193137, 0), lg(-1200759296, 816), lg(2939970, 0)) - test(lg(573585390, 0), lg(399171813, 123795), lg(926969, 0)) - test(lg(1683063904, -942), lg(1649267984, 229752), lg(-244, -1)) - test(lg(-6019138, -1), lg(-387146187, 7364), lg(-5255245, -1)) - test(lg(-123416174, 28), lg(149703916, 19121), lg(660, 0)) - test(lg(-40732946, -1), lg(-1582312743, 7920), lg(-835168, -1)) - test(lg(715821610, 298), lg(1431643220, 596), lg(2, 0)) - test(lg(-570078780, -1), lg(-1717918737, 8458), lg(-63727, -1)) + test(-578207L, 228797600580841L, -395701427L) + test(3734905142L, 11204715428L, 3L) + test(80677703530L, 242033110590L, 3L) + test(-162400270L, 4957105857076L, -30524L) + test(-9L, 6323344774L, -691707459L) + test(135006L, 314919788164L, 2332622L) + test(-51739881980L, 6001826309794L, -116L) + test(1125157L, 2639620573L, 2346L) + test(997096L, 24421382294514L, 24492497L) + test(-1295710758066L, 47941298048449L, -37L) + test(-2166511L, 15114349972511L, -6976354L) + test(-6909144294L, 207274328847L, -30L) + test(4387003627L, 4101848391931L, 935L) + test(23215066L, 4566357066146L, 196698L) + test(-13221428L, 91740281077293L, -6938757L) + test(-8625267371963L, 2785961361144320L, -323L) + test(7125522617558L, 2087778126944550L, 293L) + test(3373132868549L, 3305670211178975L, 980L) + test(-917576L, 12579050561L, -13709L) + test(456092L, 73591818663L, 161353L) + test(1615312694926L, 3230625389852L, 2L) + test(167998806719L, 167998806719L, 1L) + test(90893135L, 132840499118665L, 1461502L) + test(799104733L, 147585054932536L, 184688L) + test(-300693860803928L, 601387721607857L, -2L) + test(6770260858L, 1835756231761675L, 271150L) + test(-21594175253L, 152282123889326L, -7052L) + test(32825L, 3167385820L, 96492L) + test(-57018115L, 31110452512408L, -545624L) + test(-5950946L, 9505144073367L, -1597249L) + test(-18245832494939L, 54737497484818L, -3L) + test(4741011L, 27865199652383L, 5877480L) + test(196504301807L, 564753363395315L, 2874L) + test(228327608L, 4794879780L, 21L) + test(99111506L, 36368273626634L, 366943L) + test(-1209485521L, 24189710420L, -20L) + test(-319956618L, 236542327972199L, -739295L) + test(-62197L, 187572612454L, -3015755L) + test(9087L, 246218266185L, 27093454L) + test(345582531L, 8543663105119304L, 24722497L) + test(-1424974L, 3807993484425L, -2672324L) + test(1991351L, 153342025764L, 77004L) + test(1193137L, 3507787521536L, 2939970L) + test(573585390L, 531695875580133L, 926969L) + test(-4044176128928L, 986778975458576L, -244L) + test(-6019138L, 31632046988853L, -5255245L) + test(124430635410L, 82124219370732L, 660L) + test(-40732946L, 34018853638873L, -835168L) + test(1280616075818L, 2561232151636L, 2L) + test(-570078780L, 36329410438127L, -63727L) // int53, int53 - test(lg(1, 0), lg(-1232398900, 28871), lg(13989713, 22345)) - test(lg(0, 0), lg(-916994839, 12266), lg(1713571419, 15301)) - test(lg(32, 0), lg(1133414946, 229), lg(256531666, 7)) - test(lg(368, 0), lg(134792921, 3907), lg(-1656790262, 10)) - test(lg(1, 0), lg(1532393452, 52260), lg(-701373106, 31864)) - test(lg(0, 0), lg(193990135, 1460), lg(867607428, 6918)) - test(lg(0, 0), lg(867672590, 1), lg(-1315044816, 987593)) - test(lg(0, 0), lg(-978844610, 2), lg(720710523, 209)) - test(lg(0, 0), lg(-297570329, 1), lg(-2127979750, 195738)) - test(lg(0, 0), lg(-1035330427, 5), lg(-2091513925, 70)) - test(lg(0, 0), lg(1037142987, 15), lg(-485498951, 30819)) - test(lg(0, 0), lg(744551901, 15), lg(-604684037, 1587)) - test(lg(67766, 0), lg(1341710951, 232724), lg(1864827988, 3)) - test(lg(694, 0), lg(-409318148, 157818), lg(517165426, 227)) - test(lg(1, 0), lg(1908192460, 110512), lg(-61974596, 95795)) - test(lg(0, 0), lg(946490654, 498), lg(-1889366637, 1163)) - test(lg(12, 0), lg(1765257877, 34422), lg(728455544, 2851)) - test(lg(0, 0), lg(-1725136864, 84), lg(1122821677, 14720)) - test(lg(1, 0), lg(1854803780, 2), lg(-302860117, 1)) - test(lg(131, 0), lg(380756581, 107), lg(-806772264, 0)) - test(lg(0, 0), lg(1868292481, 1134), lg(691774521, 33775)) - test(lg(0, 0), lg(-1515810361, 98), lg(2038289788, 198)) - test(lg(315, 0), lg(-1943767475, 31777), lg(-1513506636, 100)) - test(lg(0, 0), lg(1508904915, 18), lg(1834666309, 976)) - test(lg(1, 0), lg(1430753947, 3772), lg(-1853122145, 3615)) - test(lg(2340149, 0), lg(-1654852151, 1195820), lg(-2100231332, 0)) - test(lg(0, 0), lg(1011710080, 18), lg(-616681449, 57)) - test(lg(14, 0), lg(-495370429, 356832), lg(-34555439, 25233)) - test(lg(131, 0), lg(744211838, 511), lg(-475809581, 3)) - test(lg(0, 0), lg(1135128265, 67), lg(163864249, 972)) - test(lg(1, 0), lg(954856869, 5120), lg(1474096435, 3606)) - test(lg(0, 0), lg(1544045220, 1), lg(85376495, 2353)) - test(lg(8, 0), lg(1367437144, 53), lg(2010850631, 6)) - test(lg(0, 0), lg(-1398730804, 13), lg(-2055007528, 52)) - test(lg(0, 0), lg(1598156017, 13), lg(-1006929331, 160)) - test(lg(0, 0), lg(738323529, 41), lg(-1508093984, 10361)) - test(lg(0, 0), lg(-1788797806, 31), lg(588557582, 575930)) - test(lg(76, 0), lg(-913009845, 1002), lg(204577043, 13)) - test(lg(0, 0), lg(1908599465, 6), lg(1058868127, 3383)) - test(lg(0, 0), lg(-634312634, 75), lg(-850292534, 332928)) - test(lg(0, 0), lg(-1679695022, 148), lg(-1395453213, 912)) - test(lg(0, 0), lg(456310936, 71), lg(487720864, 1590813)) - test(lg(0, 0), lg(-1724925398, 0), lg(-273170277, 38)) - test(lg(0, 0), lg(-6742076, 15), lg(192793866, 175)) - test(lg(50, 0), lg(337939061, 2094205), lg(880147944, 41142)) - test(lg(0, 0), lg(-998413092, 0), lg(-1758700885, 29)) - test(lg(0, 0), lg(1986052307, 3), lg(-2092246422, 47)) - test(lg(0, 0), lg(-109615093, 1), lg(-2066395387, 20016)) - test(lg(127, 0), lg(-1147373454, 901), lg(313439710, 7)) - test(lg(0, 0), lg(-792716629, 66379), lg(2017337246, 250513)) + test(1L, 124003063371212L, 95971058218833L) + test(0L, 52685446825193L, 65719008167515L) + test(32L, 984680925730L, 30321302738L) + test(368L, 16780572018393L, 45587849994L) + test(1L, 224456523282412L, 136858431513934L) + test(0L, 6270846242295L, 29713451361156L) + test(0L, 5162639886L, 4241682616681008L) + test(0L, 11906057278L, 898368875387L) + test(0L, 8292364263L, 840690475571994L) + test(0L, 24734473349L, 302851164091L) + test(0L, 65461652427L, 132370406563769L) + test(0L, 65169061341L, 6819803382011L) + test(67766L, 999543310705255L, 14749729876L) + test(694L, 677827034369276L, 975474741618L) + test(1L, 474647334008012L, 411440625113020L) + test(0L, 2139840204062L, 4997452565907L) + test(12L, 147843129520789L, 12245680216440L) + test(0L, 363347083296L, 63223041418797L) + test(1L, 10444738372L, 8287074475L) + test(131L, 459942257253L, 3488195032L) + test(0L, 4872361206145L, 145063212196921L) + test(0L, 423685951943L, 852441814396L) + test(315L, 136483526964813L, 432278190260L) + test(0L, 78818316243L, 4193722747205L) + test(1L, 16202047394459L, 15528748620191L) + test(2340149L, 5136010432017865L, 2194735964L) + test(0L, 78321121408L, 248491421719L) + test(14L, 1532585569763139L, 108379170191825L) + test(131L, 2195472500094L, 16704059603L) + test(0L, 288897937097L, 4174872075961L) + test(1L, 21991187412389L, 15489126165811L) + test(0L, 5839012516L, 10106143423983L) + test(8L, 229000703832L, 27780654407L) + test(0L, 58730811340L, 225578259160L) + test(0L, 57432730865L, 690482805325L) + test(0L, 176831982665L, 44502943027168L) + test(0L, 135650155666L, 2473601103342862L) + test(76L, 4306939188043L, 56039151891L) + test(0L, 27678403241L, 14530933230495L) + test(0L, 325783201862L, 1429918316597450L) + test(0L, 638270432082L, 3919909688035L) + test(0L, 305398988952L, 6832490296772512L) + test(0L, 2570041898L, 167230554267L) + test(0L, 68712734660L, 751812070666L) + test(50L, 8994542324058741L, 176704424639976L) + test(0L, 3296554204L, 127090317995L) + test(0L, 14870954195L, 204066183786L) + test(0L, 8480319499L, 85970293968645L) + test(127L, 3872913127538L, 30378210782L) + test(0L, 285099136391851L, 1075947159560094L) // int53, big - test(lg(0, 0), lg(291278707, 13808), lg(941639833, -14430466)) - test(lg(0, 0), lg(-857819626, 204588), lg(-1909684886, -709519130)) - test(lg(0, 0), lg(-978105991, 7435), lg(-306472275, 158306339)) - test(lg(0, 0), lg(75049741, 248171), lg(-1574105194, 64879257)) - test(lg(0, 0), lg(136051120, 621), lg(-1671784392, 102642869)) - test(lg(0, 0), lg(-448460356, 2858), lg(71740423, -16715717)) - test(lg(0, 0), lg(-1266403435, 2), lg(-1022999838, 25812014)) - test(lg(0, 0), lg(552733494, 22), lg(241731505, -33191170)) - test(lg(0, 0), lg(1366167794, 115591), lg(191854687, -2136953)) - test(lg(0, 0), lg(1329114439, 80951), lg(-51187101, 1471052997)) + test(0L, 59305199701875L, -61978378594400103L) + test(0L, 878702206301718L, -3047361456851090070L) + test(0L, 31936398707065L, 679920552742984365L) + test(0L, 1065886403865357L, 278654289724641174L) + test(0L, 2667310741936L, 440847768145795128L) + test(0L, 12278863038908L, -71793457772450809L) + test(0L, 11618498453L, 110861759245861602L) + test(0L, 95042014006L, -142554989424244815L) + test(0L, 496460930879730L, -9178143056234401L) + test(0L, 347683226692935L, 6318124517041566307L) // big, int32 - test(lg(422668131, 6), lg(-1495113094, 168518701), lg(27633219, 0)) - test(lg(932715295, 204683), lg(-1211847018, -609137255), lg(-2976, -1)) - test(lg(189814434, 0), lg(-457166837, -15040808), lg(-340331202, -1)) - test(lg(-1116045071, -1131771), lg(-104570473, -117704108), lg(104, 0)) - test(lg(-784306379, 14408), lg(453828098, -10187034), lg(-707, -1)) - test(lg(-284027201, 2002401), lg(1911518920, 168201762), lg(84, 0)) - test(lg(-862273257, -2), lg(610589058, 36481453), lg(-30381877, -1)) - test(lg(-761280647, -71), lg(410700182, 503953004), lg(-7181145, -1)) - test(lg(-1212582262, -2538), lg(194917334, -8806907), lg(3471, 0)) - test(lg(-1201233065, 4), lg(852311155, 9671380), lg(2048884, 0)) - test(lg(1324107666, 0), lg(-1028681544, 4163983), lg(13506586, 0)) - test(lg(-354367044, 6361111), lg(-708734088, 12722223), lg(2, 0)) - test(lg(-292170842, -76359), lg(1693696214, 18402294), lg(-241, -1)) - test(lg(2104544550, -41349584), lg(-1932788158, 206747917), lg(-5, -1)) - test(lg(-1928473941, -17816), lg(1427262980, -60732866), lg(3409, 0)) - test(lg(-1929237164, -681), lg(-677896940, 2512898), lg(-3693, -1)) - test(lg(1550060300, -35), lg(-926729663, -9677195), lg(279372, 0)) - test(lg(-1706875941, 0), lg(-405257725, -2271799), lg(-3770075, -1)) - test(lg(1540708852, 10909), lg(-1893733008, -6491069), lg(-595, -1)) - test(lg(-1563665409, -358), lg(-1343018634, -2584815), lg(7233, 0)) - test(lg(278715917, -374389), lg(-1224507547, 122799570), lg(-328, -1)) - test(lg(1421525100, 0), lg(-2082712791, -15998594), lg(-48337828, -1)) - test(lg(1574832373, -2193811), lg(-2147318181, -32907160), lg(15, 0)) - test(lg(-1260116915, -61610), lg(1074158039, 118905936), lg(-1930, -1)) - test(lg(130856059, -15612), lg(1270835097, -2201288), lg(141, 0)) - test(lg(-110248455, 2347), lg(320077861, -446108079), lg(-189997, -1)) - test(lg(-1659387265, 122), lg(1075676628, 54005547), lg(440453, 0)) - test(lg(-144903831, 18), lg(-1800001035, 54578889), lg(2877683, 0)) - test(lg(-1312994937, -23952), lg(-654120591, 33364168), lg(-1393, -1)) - test(lg(-178073210, -1), lg(302695822, -2432394), lg(58667176, 0)) - test(lg(1316938460, 142), lg(523451067, -54366538), lg(-382038, -1)) - test(lg(-1457978633, 17556853), lg(-78968601, 52670560), lg(3, 0)) - test(lg(-1760960552, 505129611), lg(-773046192, -1010259224), lg(-2, -1)) - test(lg(1210355204, 2314), lg(1515488136, -21874592), lg(-9452, -1)) - test(lg(-1625685934, 862807773), lg(-1043595428, -1725615548), lg(-2, -1)) - test(lg(184379181, 4), lg(-1217231978, 1516494005), lg(375097846, 0)) - test(lg(1243945230, 0), lg(-1873413508, -236381131), lg(-816152673, -1)) - test(lg(-1540093941, -876), lg(265593875, 26513736), lg(-30289, -1)) - test(lg(-1304692919, 543912), lg(106204837, -839801203), lg(-1544, -1)) - test(lg(-806250591, 23), lg(815576040, -55524975), lg(-2331779, -1)) - test(lg(-2106907248, -3), lg(-2053929476, -1795047022), lg(720742474, 0)) - test(lg(893100234, -124), lg(1552099699, 65024502), lg(-525272, -1)) - test(lg(-1109915706, 1255), lg(-194253417, -12405472), lg(-9879, -1)) - test(lg(-1177955013, 0), lg(412309016, 112344162), lg(154800321, 0)) - test(lg(-1975688052, -51023804), lg(343591192, -102047607), lg(2, 0)) - test(lg(-728332094, -309956), lg(1756765281, 8058834), lg(-26, -1)) - test(lg(10173004, 1227), lg(1762668787, -960735493), lg(-782994, -1)) - test(lg(1157067129, 5766), lg(1523935530, -109345767), lg(-18963, -1)) - test(lg(1226263794, 42306948), lg(-1256703941, 1438436241), lg(34, 0)) - test(lg(1502167534, -439314), lg(-444491016, -6150392), lg(14, 0)) + test(26192471907L, 723782312359256698L, 27633219L) + test(879107723762463L, -2616224585917092202L, -2976L) + test(189814434L, -64599774627614709L, -340331202L) + test(-4860916252638991L, -505535290274455145L, 104L) + test(61885399461685L, -43752977419411966L, -707L) + test(8600250819417791L, 722421068831094472L, 84L) + test(-5157240553L, 156686648156150146L, -30381877L) + test(-301408991367L, 2164461671311657366L, -7181145L) + test(-10897544612214L, -37825377348996138L, 3471L) + test(20273603415L, 41538261659499635L, 2048884L) + test(1324107666L, 17884174072385720L, 13506586L) + test(27320767651826108L, 54641535303652216L, 2L) + test(-327955404958810L, 79037252595073238L, -241L) + test(-177595108878660314L, 887975544393301570L, -5L) + test(-76516770852181L, -260845671835087356L, 3409L) + test(-2922506998444L, 10792818345254164L, -3693L) + test(-148773795060L, -41563232673777087L, 279372L) + test(2588091355L, -9757298518375933L, -3770075L) + test(46855338940916L, -27878926669845136L, -595L) + test(-1534866990081L, -11101692939261578L, 7233L) + test(-1607988232266227L, 527420140183322469L, -328L) + test(1421525100L, -68713435799727319L, -48337828L) + test(-9422344923772683L, -141335173856590245L, 15L) + test(-264609900256179L, 510697107494427095L, -1930L) + test(-67052898569093L, -9454458698242151L, 141L) + test(10084472962553L, -1916019609466306523L, -189997L) + test(526621590143L, 231952059243267540L, 440453L) + test(81459474793L, 234414545801980405L, 2877683L) + test(-102870074701433L, 143298014059096433L, -1393L) + test(-178073210L, -10447052378290802L, 58667176L) + test(611202294492L, -233502502183290181L, -382038L) + test(75406112292668151L, 226218336878004455L, 3L) + test(2169515162020208600L, -4339030324040417200L, -2L) + test(9939764678148L, -93950655737855096L, -9452L) + test(3705731170438873170L, -7411462340877746340L, -2L) + test(17364248365L, 6513292159132795798L, 375097846L) + test(1243945230L, -1015249224614937988L, -816152673L) + test(-3759636477941L, 113875629280371731L, -30289L) + test(2336087242176329L, -3606918701920252251L, -1544L) + test(102272964513L, -238477950920641560L, -2331779L) + test(-10696841840L, -7709668252031154692L, 720742474L) + test(-531682844470L, 279278111080786291L, -525272L) + test(5393369008070L, -53281092430729833L, -9879L) + test(3117012283L, 482514502098834968L, 154800321L) + test(-219145567178234740L, -438291134356469480L, 2L) + test(-1331247316563774L, 34612430230658145L, -26L) + test(5269935045196L, -4126327520778768141L, -782994L) + test(24765938495865L, -469636491697100502L, -18963L) + test(181706959279836402L, 6178036615514437691L, 34L) + test(-1886837760507410L, -26415728647103752L, 14L) // big, int53 - test(lg(88399, 0), lg(-1883357942, 360257606), lg(1478768728, 4075)) - test(lg(-45459, -1), lg(-1991900757, -48856999), lg(-1087694619, 1074)) - test(lg(4395497, 0), lg(518426119, 218946975), lg(-808940852, 49)) - test(lg(3198134, 0), lg(-946567777, 600381050), lg(-1165957306, 187)) - test(lg(470, 0), lg(257885254, 845979705), lg(792779187, 1798424)) - test(lg(92, 0), lg(1278680372, 6485140), lg(1376461023, 70263)) - test(lg(167728, 0), lg(1445602310, 420550818), lg(1397186900, 2507)) - test(lg(25700177, 0), lg(1822058703, 522114268), lg(1355449555, 20)) - test(lg(-35822646, -1), lg(532749659, -130990067), lg(-1474774415, 3)) - test(lg(-348, -1), lg(1329707986, -2121642), lg(-63366094, 6086)) - test(lg(-2179, -1), lg(1028585430, -118524228), lg(1655878874, 54392)) - test(lg(1187, 0), lg(203502475, 42252914), lg(36519512, 35581)) - test(lg(3223, 0), lg(341088508, 35053507), lg(917391400, 10874)) - test(lg(23608500, 0), lg(1454135412, 69933847), lg(-162213744, 2)) - test(lg(7286803, 0), lg(1674604578, 10565585), lg(1932570831, 1)) - test(lg(-137450, -1), lg(-1910257093, -16610962), lg(-640594227, 120)) - test(lg(114592, 0), lg(1080864951, 17606069), lg(-1542196664, 153)) - test(lg(61, 0), lg(-1419644278, 13937517), lg(-919779905, 227700)) - test(lg(-247360, -1), lg(-1958380469, -855713410), lg(1631833189, 3459)) - test(lg(-61725, -1), lg(1951473618, -4122677), lg(-899615165, 66)) - test(lg(2226, 0), lg(1521276132, 182952467), lg(346742782, 82171)) - test(lg(-997, -1), lg(-1003647481, -7808320), lg(-228453385, 7826)) - test(lg(36, 0), lg(-875689390, 4467236), lg(-590010750, 120938)) - test(lg(56005, 0), lg(1189085620, 611543209), lg(1619962756, 10919)) - test(lg(-90057, -1), lg(-1072173311, -18503031), lg(1971480267, 205)) - test(lg(-9, -1), lg(767303802, -3407362), lg(-339044225, 352939)) - test(lg(62240, 0), lg(427996893, 482974074), lg(-736462105, 7759)) - test(lg(-1774, -1), lg(842450255, -4396651), lg(859272322, 2477)) - test(lg(-153400, -1), lg(1640433988, -2618618), lg(302672196, 17)) - test(lg(2145, 0), lg(-361322518, 63967358), lg(-1922353888, 29810)) - test(lg(106042, 0), lg(-1774479550, 43276853), lg(472456506, 408)) - test(lg(-381407, -1), lg(-1756338345, -38928780), lg(283612141, 102)) - test(lg(1217514, 0), lg(-495049835, 37161263), lg(-2052025512, 30)) - test(lg(-17, -1), lg(1606509747, -10876159), lg(1068727249, 635715)) - test(lg(4880327, 0), lg(-1857686692, 1918485655), lg(454913535, 393)) - test(lg(-1023070, -1), lg(-502107392, -511268482), lg(-1118977400, 499)) - test(lg(439, 0), lg(-909192131, 45216813), lg(1442986382, 102923)) - test(lg(2171202, 0), lg(259184089, 14858724), lg(-671961291, 6)) - test(lg(-5332527, -1), lg(1737846340, -614952982), lg(1379175047, 115)) - test(lg(-435180, -1), lg(-406629212, -528407898), lg(973577032, 1214)) - test(lg(27837, 0), lg(-597461306, 538945619), lg(-1867966522, 19360)) - test(lg(-396, -1), lg(-1906945200, -371170760), lg(151858506, 936902)) - test(lg(-115583279, -1), lg(-1366510, -207691415), lg(-872314548, 1)) - test(lg(-6783543, -1), lg(-1280665444, -104856505), lg(1964875665, 15)) - test(lg(-1464006069, -1), lg(897601097, -1352132581), lg(-328204224, 0)) - test(lg(11599107, 0), lg(-496529216, 32992512), lg(-668292521, 2)) - test(lg(842, 0), lg(1819966537, 311969505), lg(-879441284, 370147)) - test(lg(43514, 0), lg(433235702, 408255734), lg(573404298, 9382)) - test(lg(-230, -1), lg(1693350453, -4127304), lg(-1671879801, 17931)) - test(lg(249094, 0), lg(-492682302, 64433722), lg(-1408841594, 258)) + test(88399L, 1547294638316862730L, 17503470499928L) + test(-45459L, -209839210582638165L, 4616002148581L) + test(4395497L, 940370097701555719L, 213939423948L) + test(3198134L, 2578616978236540319L, 806287894342L) + test(470L, 3633455166312612934L, 7724173057120691L) + test(92L, 27853465488661812L, 301778663579871L) + test(167728L, 1806252011061650438L, 10768880197972L) + test(25700177L, 2242463707657038031L, 87254795475L) + test(-35822646L, -562598053333099173L, 15705094769L) + test(-348L, -9112381674112046L, 26143402564658L) + test(-2179L, -509057682015062058L, 233613517042906L) + test(1187L, 181474883994203019L, 152819267878488L) + test(3223L, 150553666516195580L, 46704391768104L) + test(23608500L, 300363587202603124L, 12722688144L) + test(7286803L, 45378843712712738L, 6227538127L) + test(-137450L, -71343536160388549L, 519050448589L) + test(114592L, 75617491646984375L, 659882766920L) + test(61L, 59861182577767050L, 977967428486591L) + test(-247360L, -3675261108362052533L, 14857923710053L) + test(-61725L, -17706760935497774L, 286863193667L) + test(2226L, 785774864008795364L, 352922104422398L) + test(-997L, -33536475745382905L, 33616480572407L) + test(36L, 19186635942791762L, 519428459800194L) + test(56005L, 2626558083934978484L, 46898367867780L) + test(-90057L, -79469909799080191L, 882439775947L) + test(-9L, -14634507588329350L, 1515865418406015L) + test(62240L, 2074357853073880797L, 33328209754855L) + test(-1774L, -18883471414475441L, 10639493264514L) + test(-153400L, -11246877030282940L, 73317116228L) + test(2145L, 274737714555168746L, 128035347707168L) + test(106042L, 185872670829287234L, 1752819113274L) + test(-381407L, -167197834434549929L, 438370276333L) + test(1217514L, 159606413062972309L, 131091960664L) + test(-17L, -46712745604586317L, 2730376203303889L) + test(4880327L, 8239833148507419484L, 1688377060863L) + test(-1023070L, -2195881405872704768L, 2146364670600L) + test(439L, 194204736450122813L, 442052361992590L) + test(2171202L, 63817733899474393L, 29392809781L) + test(-5332527L, -2641202944529830332L, 495300414087L) + test(-435180L, -2269494636969765724L, 5215063874376L) + test(27837L, 2314753811624982214L, 83152993851334L) + test(-396L, -1594166273043442864L, 4023963601415498L) + test(-115583279L, -892027830791363054L, 7717620044L) + test(-6783543L, -450355256733558628L, 66389385105L) + test(-1464006069L, -5807365214353469879L, 3966763072L) + test(11599107L, 141701763851325632L, 12216609367L) + test(842L, 1339898823144275017L, 1589772675238524L) + test(43514L, 1753445026367710966L, 40295956575370L) + test(-230L, -17726634007299531L, 77015681672071L) + test(249094L, 276740732551840706L, 1110987688070L) // big, big - test(lg(-10, -1), lg(1450795502, -706709103), lg(742056886, 64843937)) - test(lg(0, 0), lg(-392893244, 72026637), lg(1419676270, 875736789)) - test(lg(-2, -1), lg(-1861146463, 8382761), lg(-724412724, -3000735)) - test(lg(0, 0), lg(1373482238, 23344691), lg(1835527248, -294342355)) - test(lg(-37, -1), lg(1956796392, 107480459), lg(-560958184, -2839471)) - test(lg(3, 0), lg(422228275, 30436377), lg(-2023395425, 8226201)) - test(lg(-3, -1), lg(1747624836, -215352612), lg(-1349940168, 58723974)) - test(lg(2, 0), lg(-583006891, 16111063), lg(1853686630, 5479773)) - test(lg(0, 0), lg(1498104050, 7322401), lg(-407388940, 2141575618)) - test(lg(5, 0), lg(1943726712, 869895175), lg(-627430826, 169278540)) - test(lg(0, 0), lg(1872895982, 98966340), lg(1347573135, 529034148)) - test(lg(-2, -1), lg(16010610, 187913494), lg(-848952152, -81951424)) - test(lg(0, 0), lg(830929771, -4393252), lg(1829525088, 52659897)) - test(lg(22, 0), lg(-2093526384, 133319293), lg(-464927151, 6049576)) - test(lg(0, 0), lg(1056318793, 13467735), lg(1970348162, -672507521)) - test(lg(0, 0), lg(-28853693, -169722715), lg(-83877421, 770900857)) - test(lg(-27, -1), lg(1743854071, -302158995), lg(80117835, 11113120)) - test(lg(-6, -1), lg(635796581, -146765250), lg(441664676, 23716738)) - test(lg(0, 0), lg(-1048312948, -37662905), lg(1319664078, 208772026)) - test(lg(0, 0), lg(-784292680, -14102823), lg(2037268040, 744987722)) - test(lg(176, 0), lg(-1116104092, -2073525743), lg(1766685765, -11731135)) - test(lg(0, 0), lg(-1991687284, 19448294), lg(-1731357606, -202272807)) - test(lg(6, 0), lg(-2042068328, -52956481), lg(370482897, -7759903)) - test(lg(1, 0), lg(334395247, 1906338595), lg(342095090, 1248830168)) - test(lg(0, 0), lg(-309616588, 44123460), lg(2040055580, -476494291)) - test(lg(0, 0), lg(137178123, 36336421), lg(-360221107, -515689970)) - test(lg(0, 0), lg(-422856762, -16760844), lg(-334268074, -43984484)) - test(lg(0, 0), lg(-24820293, 25823996), lg(390711705, 288223876)) - test(lg(0, 0), lg(1170265006, 2998984), lg(-134995170, -2123267074)) - test(lg(0, 0), lg(-1501380980, -6088910), lg(-1175861016, -56027408)) - test(lg(-56, -1), lg(307880183, 196786483), lg(-1107761890, -3480429)) - test(lg(0, 0), lg(-588606997, -37732967), lg(-1124435958, -77404915)) - test(lg(108, 0), lg(90560661, 990295925), lg(731139348, 9165999)) - test(lg(0, 0), lg(46312609, -28251908), lg(1279863155, -519028300)) - test(lg(0, 0), lg(1123427761, 55212863), lg(-1081219733, 233090714)) - test(lg(0, 0), lg(1447869812, -3646400), lg(-1237950546, -27122943)) - test(lg(-13, -1), lg(-1399920635, 110072031), lg(-398678056, -8069387)) - test(lg(0, 0), lg(513704441, 14319377), lg(-796719013, 260081997)) - test(lg(8, 0), lg(166886349, -190148673), lg(68245235, -21656365)) - test(lg(0, 0), lg(-1594024534, -144937584), lg(177399758, 200473672)) - test(lg(-1, -1), lg(447753993, -23591908), lg(1399162166, 12505918)) - test(lg(0, 0), lg(1500283330, 5361180), lg(348398676, 156400271)) - test(lg(-1, -1), lg(-216115001, 670826068), lg(1759253954, -470062110)) - test(lg(0, 0), lg(-1251659767, 18831569), lg(-669341445, -34474821)) - test(lg(31, 0), lg(817032953, 218701872), lg(-176557210, 6899121)) - test(lg(-19, -1), lg(1365998269, 613319842), lg(319204438, -30758748)) - test(lg(0, 0), lg(-428500325, 6610536), lg(-46648893, -105360271)) - test(lg(0, 0), lg(784528299, -6958267), lg(1370662827, -774132635)) - test(lg(-2, -1), lg(-769114167, 137614183), lg(-929091402, -67103082)) - test(lg(8, 0), lg(1810734914, 124115952), lg(1149563530, 15197570)) + test(-10L, -3035292483719699986L, 278502589500941238L) + test(0L, 309352054257937604L, 3761260870078728814L) + test(-2L, 36003686779005089L, -12888055118407988L) + test(0L, 100264685753707774L, -1264190786717094832L) + test(-37L, 461625058320865256L, -12195431348931304L) + test(3L, 130723244245954867L, 35331266536894367L) + test(-3L, -924932423900552316L, 252217550766181432L) + test(2L, 69196492400756053L, 23535447678190438L) + test(0L, 31449474321301746L, 9197997245108567284L) + test(5L, 3736171329516923512L, 727045796882164310L) + test(0L, 425057195577712622L, 2272184365474796943L) + test(-2L, 807082311223102834L, -351978682494614360L) + test(0L, -18868872832156821L, 226172537255253600L) + test(22L, 572602005562282640L, 25982734904706641L) + test(0L, 57843482432513353L, -2888397807038685054L) + test(0L, -728953506047215037L, 3310993973484462547L) + test(-27L, -1297762999973373449L, 47730487036641355L) + test(-6L, -630351948303467419L, 101862614519465124L) + test(0L, -161760942000700532L, 896669025309325774L) + test(0L, -60571160055601992L, 3199697903948807752L) + test(176L, -8905725250420237724L, -50384839403275195L) + test(0L, 83529788996273036L, -868755088371510182L) + test(6L, -227446351753346408L, -33328529234649391L) + test(1L, 8187661920961984367L, 5363684730160280818L) + test(0L, 189508821671714868L, -2046527394535651556L) + test(0L, 156063739985865739L, -2214871552090474931L) + test(0L, -71987272961247290L, -188911916350736042L) + test(0L, 110913222542181819L, 1237912121737071001L) + test(0L, 12880539371492270L, -9119362639343639778L) + test(0L, -26151666524701044L, -240635881920542488L) + test(-56L, 845191509087740151L, -14948325543844578L) + test(0L, -162061855539686933L, -332451575304128502L) + test(108L, 4253288611327629461L, 39367666671308052L) + test(0L, -121341020863288159L, -2229209572918613645L) + test(0L, 237137442026956209L, 1001116996845036907L) + test(0L, -15661167300264588L, -116492150099255378L) + test(-13L, 472755776244344837L, -34657749367478312L) + test(0L, 61501256427799033L, 1117043674891618395L) + test(8L, -816682331745911859L, -93013379356993805L) + test(0L, -622502180540310102L, 861027865126430670L) + test(-1L, -101326472862486775L, 53712510215619894L) + test(0L, 23026094268252610L, 671734049378935892L) + test(-1L, 2881176027443124423L, -2018901387779500606L) + test(0L, 80880976030674953L, -148068225104828165L) + test(31L, 939317388631011065L, 29631503184556902L) + test(-19L, 2634188664743885501L, -132107816406700970L) + test(0L, 28392039795497627L, -452518913994378813L) + test(0L, -29885528417307733L, -3324874348720642133L) + test(-2L, 591048418976612297L, -288205539284930378L) + test(8L, 533073956562640706L, 65273067278234250L) } @Test def divisionByZero(): Unit = { @@ -2091,487 +2080,487 @@ class LongTest { assertEquals(expected, hideFromOptimizer(x) % hideFromOptimizer(y)) } - test(lg(0), IntMinVal, lg(-1)) - test(lg(0), IntMinVal, IntMaxValPlus1) - test(lg(0), IntMaxValPlus1, lg(-1)) - test(lg(0), IntMaxValPlus1, IntMinVal) + test(0L, IntMinVal, -1L) + test(0L, IntMinVal, IntMaxValPlus1) + test(0L, IntMaxValPlus1, -1L) + test(0L, IntMaxValPlus1, IntMinVal) - test(lg(0), MaxVal, lg(-1)) - test(lg(0), MinVal, lg(1)) - test(lg(0), MinVal, lg(-1)) + test(0L, MaxVal, -1L) + test(0L, MinVal, 1L) + test(0L, MinVal, -1L) - test(lg(-1, 2147483647), MaxVal, MinVal) - test(lg(0), MaxVal, MaxVal) - test(lg(0), MinVal, MinVal) - test(lg(-1), MinVal, MaxVal) + test(9223372036854775807L, MaxVal, MinVal) + test(0L, MaxVal, MaxVal) + test(0L, MinVal, MinVal) + test(-1L, MinVal, MaxVal) // int32, int32 - test(lg(880, 0), lg(880, 0), lg(-219594, -1)) - test(lg(-27, -1), lg(-49125, -1), lg(98, 0)) - test(lg(-1194, -1), lg(-1922504, -1), lg(4195, 0)) - test(lg(3, 0), lg(3, 0), lg(7963, 0)) - test(lg(-626, -1), lg(-626, -1), lg(-484628621, -1)) - test(lg(11315, 0), lg(11315, 0), lg(-3914076, -1)) - test(lg(26241, 0), lg(15712341, 0), lg(-1045740, -1)) - test(lg(-507, -1), lg(-855439, -1), lg(5213, 0)) - test(lg(-259, -1), lg(-101026259, -1), lg(-500, -1)) - test(lg(27720977, 0), lg(27720977, 0), lg(-42317657, -1)) - test(lg(1, 0), lg(25954, 0), lg(-3, -1)) - test(lg(6724180, 0), lg(338447650, 0), lg(-8505730, -1)) - test(lg(10488, 0), lg(23967, 0), lg(-13479, -1)) - test(lg(1, 0), lg(885202, 0), lg(-3, -1)) - test(lg(0, 0), lg(692795590, 0), lg(-10, -1)) - test(lg(-1, -1), lg(-1, -1), lg(156, 0)) - test(lg(388, 0), lg(388, 0), lg(189523294, 0)) - test(lg(352, 0), lg(352, 0), lg(-3257, -1)) - test(lg(-9, -1), lg(-9, -1), lg(14653, 0)) - test(lg(-1, -1), lg(-258745, -1), lg(8, 0)) - test(lg(-21023, -1), lg(-206976653, -1), lg(34321, 0)) - test(lg(-1, -1), lg(-1, -1), lg(-971, -1)) - test(lg(59, 0), lg(59, 0), lg(388, 0)) - test(lg(0, 0), lg(-7, -1), lg(1, 0)) - test(lg(12, 0), lg(77, 0), lg(13, 0)) - test(lg(224246, 0), lg(224246, 0), lg(719055, 0)) - test(lg(-61296, -1), lg(-61296, -1), lg(-135723660, -1)) - test(lg(549465, 0), lg(6897809, 0), lg(793543, 0)) - test(lg(45, 0), lg(45, 0), lg(984210147, 0)) - test(lg(0, 0), lg(-64, -1), lg(1, 0)) - test(lg(2, 0), lg(379611734, 0), lg(4, 0)) - test(lg(0, 0), lg(0, 0), lg(-263, -1)) - test(lg(29, 0), lg(29, 0), lg(-117, -1)) - test(lg(24, 0), lg(245094, 0), lg(-70, -1)) - test(lg(0, 0), lg(0, 0), lg(5, 0)) - test(lg(2, 0), lg(2, 0), lg(47787927, 0)) - test(lg(-124, -1), lg(-124, -1), lg(-22714040, -1)) - test(lg(412, 0), lg(412, 0), lg(-17176, -1)) - test(lg(-11860, -1), lg(-11860, -1), lg(9506787, 0)) - test(lg(-31, -1), lg(-31, -1), lg(-1544676, -1)) - test(lg(-3, -1), lg(-1990315281, -1), lg(-7, -1)) - test(lg(99, 0), lg(99, 0), lg(-277, -1)) - test(lg(-86, -1), lg(-29227, -1), lg(-161, -1)) - test(lg(106, 0), lg(106, 0), lg(-47032956, -1)) - test(lg(18, 0), lg(18, 0), lg(510836179, 0)) - test(lg(2, 0), lg(3543112, 0), lg(10, 0)) - test(lg(534271, 0), lg(3547603, 0), lg(-1506666, -1)) - test(lg(-16361, -1), lg(-16361, -1), lg(10637613, 0)) - test(lg(8, 0), lg(606879016, 0), lg(-16, -1)) - test(lg(-1, -1), lg(-1, -1), lg(46424570, 0)) + test(880L, 880L, -219594L) + test(-27L, -49125L, 98L) + test(-1194L, -1922504L, 4195L) + test(3L, 3L, 7963L) + test(-626L, -626L, -484628621L) + test(11315L, 11315L, -3914076L) + test(26241L, 15712341L, -1045740L) + test(-507L, -855439L, 5213L) + test(-259L, -101026259L, -500L) + test(27720977L, 27720977L, -42317657L) + test(1L, 25954L, -3L) + test(6724180L, 338447650L, -8505730L) + test(10488L, 23967L, -13479L) + test(1L, 885202L, -3L) + test(0L, 692795590L, -10L) + test(-1L, -1L, 156L) + test(388L, 388L, 189523294L) + test(352L, 352L, -3257L) + test(-9L, -9L, 14653L) + test(-1L, -258745L, 8L) + test(-21023L, -206976653L, 34321L) + test(-1L, -1L, -971L) + test(59L, 59L, 388L) + test(0L, -7L, 1L) + test(12L, 77L, 13L) + test(224246L, 224246L, 719055L) + test(-61296L, -61296L, -135723660L) + test(549465L, 6897809L, 793543L) + test(45L, 45L, 984210147L) + test(0L, -64L, 1L) + test(2L, 379611734L, 4L) + test(0L, 0L, -263L) + test(29L, 29L, -117L) + test(24L, 245094L, -70L) + test(0L, 0L, 5L) + test(2L, 2L, 47787927L) + test(-124L, -124L, -22714040L) + test(412L, 412L, -17176L) + test(-11860L, -11860L, 9506787L) + test(-31L, -31L, -1544676L) + test(-3L, -1990315281L, -7L) + test(99L, 99L, -277L) + test(-86L, -29227L, -161L) + test(106L, 106L, -47032956L) + test(18L, 18L, 510836179L) + test(2L, 3543112L, 10L) + test(534271L, 3547603L, -1506666L) + test(-16361L, -16361L, 10637613L) + test(8L, 606879016L, -16L) + test(-1L, -1L, 46424570L) // int32, int53 - test(lg(-3, -1), lg(-3, -1), lg(206801065, 1)) - test(lg(-57756, -1), lg(-57756, -1), lg(-1211050362, 13)) - test(lg(0, 0), lg(0, 0), lg(-475702596, 10040)) - test(lg(423524, 0), lg(423524, 0), lg(-2084961556, 16)) - test(lg(38317, 0), lg(38317, 0), lg(-1699004544, 24)) - test(lg(60291, 0), lg(60291, 0), lg(-458289291, 56)) - test(lg(1, 0), lg(1, 0), lg(-1247681936, 1229953)) - test(lg(296788, 0), lg(296788, 0), lg(183245860, 52)) - test(lg(-2005515, -1), lg(-2005515, -1), lg(331735459, 17)) - test(lg(-179812, -1), lg(-179812, -1), lg(-853047550, 5154)) - test(lg(-3678, -1), lg(-3678, -1), lg(1751271067, 243605)) - test(lg(-93867, -1), lg(-93867, -1), lg(-1925367590, 42)) - test(lg(7600917, 0), lg(7600917, 0), lg(-1807424604, 95574)) - test(lg(300012, 0), lg(300012, 0), lg(1951216728, 101)) - test(lg(-6347, -1), lg(-6347, -1), lg(-438713154, 23)) - test(lg(-41, -1), lg(-41, -1), lg(-1211982116, 459)) - test(lg(3425, 0), lg(3425, 0), lg(-1580976156, 2)) - test(lg(-25, -1), lg(-25, -1), lg(200240265, 25993)) - test(lg(-8303, -1), lg(-8303, -1), lg(1353761386, 1921)) - test(lg(274032571, 0), lg(274032571, 0), lg(1455543028, 255)) - test(lg(-3, -1), lg(-3, -1), lg(1143775281, 729)) - test(lg(-1124428, -1), lg(-1124428, -1), lg(-521284400, 339)) - test(lg(-2, -1), lg(-2, -1), lg(-303859962, 2524)) - test(lg(1, 0), lg(1, 0), lg(-402000545, 1)) - test(lg(107013504, 0), lg(107013504, 0), lg(157604607, 3)) - test(lg(4976822, 0), lg(4976822, 0), lg(-2046021074, 2230)) - test(lg(-1, -1), lg(-1, -1), lg(-306200858, 41)) - test(lg(80396, 0), lg(80396, 0), lg(-409002766, 13)) - test(lg(937638, 0), lg(937638, 0), lg(-697219650, 26)) - test(lg(756, 0), lg(756, 0), lg(-948806692, 1700920)) - test(lg(5, 0), lg(5, 0), lg(646021801, 21350)) - test(lg(262831839, 0), lg(262831839, 0), lg(1086270794, 10633)) - test(lg(-2146273993, -1), lg(-2146273993, -1), lg(-1539129401, 0)) - test(lg(59799, 0), lg(59799, 0), lg(1910837623, 102082)) - test(lg(-5347, -1), lg(-5347, -1), lg(1965292799, 18)) - test(lg(926, 0), lg(926, 0), lg(1939309159, 104206)) - test(lg(1, 0), lg(1, 0), lg(1651864405, 1233)) - test(lg(334, 0), lg(334, 0), lg(581635234, 20)) - test(lg(-61747, -1), lg(-61747, -1), lg(-842193425, 1497)) - test(lg(-1, -1), lg(-1, -1), lg(758739794, 79508)) - test(lg(59605313, 0), lg(59605313, 0), lg(-1162319751, 0)) - test(lg(12267518, 0), lg(12267518, 0), lg(1340161110, 568352)) - test(lg(19230695, 0), lg(19230695, 0), lg(1844291137, 21)) - test(lg(3950296, 0), lg(3950296, 0), lg(-848670202, 243)) - test(lg(503276, 0), lg(503276, 0), lg(-1756374670, 1)) - test(lg(30880536, 0), lg(30880536, 0), lg(-1380766565, 51064)) - test(lg(5659804, 0), lg(5659804, 0), lg(-725339057, 1)) - test(lg(11882277, 0), lg(11882277, 0), lg(243727355, 7)) - test(lg(371783010, 0), lg(371783010, 0), lg(630143580, 14001)) - test(lg(840, 0), lg(840, 0), lg(-1719362098, 109)) + test(-3L, -3L, 4501768361L) + test(-57756L, -57756L, 58918491782L) + test(0L, 0L, 43125290916540L) + test(423524L, 423524L, 70929482476L) + test(38317L, 38317L, 105675177856L) + test(60291L, 60291L, 244354846581L) + test(1L, 1L, 5282610957902448L) + test(296788L, 296788L, 223521545252L) + test(-2005515L, -2005515L, 73346179491L) + test(-179812L, -179812L, 22139703363330L) + test(-3678L, -3678L, 1046277259413147L) + test(-93867L, -93867L, 182758226138L) + test(7600917L, 7600917L, 410489691890596L) + test(300012L, 300012L, 435742913624L) + test(-6347L, -6347L, 102640501950L) + test(-41L, -41L, 1974472974044L) + test(3425L, 3425L, 11303925732L) + test(-25L, -25L, 111639285165193L) + test(-8303L, -8303L, 8251985937002L) + test(274032571L, 274032571L, 1096672203508L) + test(-3L, -3L, 3132174934065L) + test(-1124428L, -1124428L, 1459767596240L) + test(-2L, -2L, 10844488562438L) + test(1L, 1L, 8187934047L) + test(107013504L, 107013504L, 13042506495L) + test(4976822L, 4976822L, 9580026016302L) + test(-1L, -1L, 180082425574L) + test(80396L, 80396L, 59720539378L) + test(937638L, 937638L, 115266897342L) + test(756L, 756L, 7305399119272924L) + test(5L, 5L, 91698197791401L) + test(262831839L, 262831839L, 45669473529162L) + test(-2146273993L, -2146273993L, 2755837895L) + test(59799L, 59799L, 438440762347895L) + test(-5347L, -5347L, 79274704127L) + test(926L, 926L, 447563301356135L) + test(1L, 1L, 5297346540373L) + test(334L, 334L, 86480981154L) + test(-61747L, -61747L, 6433018815983L) + test(-1L, -1L, 341485018510162L) + test(59605313L, 59605313L, 3132647545L) + test(12267518L, 12267518L, 2441054592777302L) + test(19230695L, 19230695L, 92038604353L) + test(3950296L, 3950296L, 1047123350022L) + test(503276L, 503276L, 6833559922L) + test(30880536L, 30880536L, 219321124203675L) + test(5659804L, 5659804L, 7864595535L) + test(11882277L, 11882277L, 30308498427L) + test(371783010L, 371783010L, 60134467254876L) + test(840L, 840L, 470727040462L) // int32, big - test(lg(-267334310, -1), lg(-267334310, -1), lg(1537718115, -134598983)) - test(lg(57, 0), lg(57, 0), lg(-1668867109, -10100325)) - test(lg(30332, 0), lg(30332, 0), lg(-615310153, -90004876)) - test(lg(187, 0), lg(187, 0), lg(-590535223, 8244144)) - test(lg(-2, -1), lg(-2, -1), lg(2125719729, 390762530)) - test(lg(-4252915, -1), lg(-4252915, -1), lg(2070489053, 23484863)) - test(lg(-2, -1), lg(-2, -1), lg(37507428, 96913792)) - test(lg(10, 0), lg(10, 0), lg(-533680689, -79923599)) - test(lg(-14, -1), lg(-14, -1), lg(-930313329, 2972085)) - test(lg(-20155233, -1), lg(-20155233, -1), lg(-49989774, -25498857)) - test(lg(-406, -1), lg(-406, -1), lg(2109762544, 126098611)) - test(lg(43, 0), lg(43, 0), lg(598811771, 154269509)) - test(lg(-4830, -1), lg(-4830, -1), lg(-1043650540, -2874494)) - test(lg(-4271, -1), lg(-4271, -1), lg(-950378080, -106126516)) - test(lg(126, 0), lg(126, 0), lg(-877412093, -90804729)) - test(lg(40445345, 0), lg(40445345, 0), lg(-1461218790, 6749169)) - test(lg(-1, -1), lg(-1, -1), lg(1776909778, 28425796)) - test(lg(-2123811, -1), lg(-2123811, -1), lg(-51805125, 44153129)) - test(lg(-25650126, -1), lg(-25650126, -1), lg(-1317209725, -16141386)) - test(lg(30, 0), lg(30, 0), lg(712479950, 158765535)) - test(lg(2494211, 0), lg(2494211, 0), lg(-432472367, 21859989)) - test(lg(100937174, 0), lg(100937174, 0), lg(212873269, -74778594)) - test(lg(901687, 0), lg(901687, 0), lg(-1225225931, -512562107)) - test(lg(-422854, -1), lg(-422854, -1), lg(-1361503923, -98826041)) - test(lg(2, 0), lg(2, 0), lg(386622050, -9945722)) - test(lg(-465211, -1), lg(-465211, -1), lg(-418132599, -160175963)) - test(lg(63, 0), lg(63, 0), lg(-1330189832, 180061391)) - test(lg(47, 0), lg(47, 0), lg(1439978282, -16520554)) - test(lg(233450563, 0), lg(233450563, 0), lg(-328511972, 377539644)) - test(lg(-134912, -1), lg(-134912, -1), lg(1349244684, -12612862)) - test(lg(-95441, -1), lg(-95441, -1), lg(511120357, 16112596)) - test(lg(-1160726496, -1), lg(-1160726496, -1), lg(-913371934, -9441145)) - test(lg(-502, -1), lg(-502, -1), lg(-1021329523, -377728463)) - test(lg(3313324, 0), lg(3313324, 0), lg(-67454848, 442297818)) - test(lg(-145, -1), lg(-145, -1), lg(-1010112762, 29724438)) - test(lg(-19091, -1), lg(-19091, -1), lg(-1944488998, -173788926)) - test(lg(-3331910, -1), lg(-3331910, -1), lg(2144172121, 73505274)) - test(lg(56622, 0), lg(56622, 0), lg(-1451372835, 5219178)) - test(lg(0, 0), lg(0, 0), lg(556032035, 32471322)) - test(lg(800, 0), lg(800, 0), lg(-1649243607, 2299368)) - test(lg(86949, 0), lg(86949, 0), lg(794150820, -1384562176)) - test(lg(10, 0), lg(10, 0), lg(-790693444, 1000869239)) - test(lg(-333236, -1), lg(-333236, -1), lg(-1020207444, 125043716)) - test(lg(-598, -1), lg(-598, -1), lg(-93061561, -329975227)) - test(lg(-19, -1), lg(-19, -1), lg(-1096862531, 163621631)) - test(lg(465328283, 0), lg(465328283, 0), lg(-21925149, -52057346)) - test(lg(-25837, -1), lg(-25837, -1), lg(677002620, 8643698)) - test(lg(-383633650, -1), lg(-383633650, -1), lg(1609519787, 8262009)) - test(lg(-66, -1), lg(-66, -1), lg(1917139359, 239618524)) - test(lg(1676620, 0), lg(1676620, 0), lg(910745834, 82765572)) + test(-267334310L, -267334310L, -578098228522141853L) + test(57L, 57L, -43380562927871013L) + test(30332L, 30332L, -386567995220878153L) + test(187L, 187L, 35408332567946697L) + test(-2L, -2L, 1678312288977938609L) + test(-4252915L, -4252915L, 100866720606529501L) + test(-2L, -2L, 416241567208853860L) + test(10L, 10L, -343269240122331697L) + test(-14L, -14L, 12765011240586127L) + test(-20155233L, -20155233L, -109516752655403150L) + test(-406L, -406L, 541589412425788400L) + test(43L, 43L, 662582496523789435L) + test(-4830L, -4830L, -12345854471231468L) + test(-4271L, -4271L, -455809912113831520L) + test(126L, 126L, -390003337959587581L) + test(40445345L, 40445345L, 28987462963925530L) + test(-1L, -1L, 122087865959677394L) + test(-2123811L, -2123811L, 189636249314231355L) + test(-25650126L, -25650126L, -69326722004354685L) + test(30L, 30L, 681892781269423310L) + test(2494211L, 2494211L, 93887941708414673L) + test(100937174L, 100937174L, -321171615457988555L) + test(901687L, 901687L, -2201437483664111307L) + test(-422854L, -422854L, -424454611154691763L) + test(2L, 2L, -42716550338485662L) + test(-465211L, -465211L, -687950518813471351L) + test(63L, 63L, 773357788582046200L) + test(47L, 47L, -70955237701823702L) + test(233450563L, 233450563L, 1621520427889937948L) + test(-134912L, -134912L, -54171828449716468L) + test(-95441L, -95441L, 69203073384780773L) + test(-1160726496L, -1160726496L, -40549405630198558L) + test(-502L, -502L, -1622331392079708275L) + test(3313324L, 3313324L, 1899654667629672576L) + test(-145L, -145L, 127665492386834182L) + test(-19091L, -19091L, -746417751226485798L) + test(-3331910L, -3331910L, 315702750057691225L) + test(56622L, 56622L, 22416201665597149L) + test(0L, 0L, 139463266603917347L) + test(800L, 800L, 9875713007192617L) + test(86949L, 86949L, -5946649264404445276L) + test(10L, 10L, 4298700652581681596L) + test(-333236L, -333236L, 537058674065071788L) + test(-598L, -598L, -1417232804253270457L) + test(-19L, -19L, 702749557261284541L) + test(465328283L, 465328283L, -223584594313514269L) + test(-25837L, -25837L, 37124400903503228L) + test(-383633650L, -383633650L, 35485060063777451L) + test(-66L, -66L, 1029153726012930463L) + test(1676620L, 1676620L, 355475425885479146L) // int53 / int32 - test(lg(15827410, 0), lg(1244623439, 3), lg(-231372097, -1)) - test(lg(15118, 0), lg(-1392787378, 124), lg(-20252, -1)) - test(lg(11, 0), lg(578165055, 72), lg(13, 0)) - test(lg(42298679, 0), lg(-1836745385, 3), lg(-95630157, -1)) - test(lg(17447610, 0), lg(-1766124150, 29), lg(-45315780, -1)) - test(lg(0, 0), lg(540281958, 253606), lg(-11, -1)) - test(lg(51980, 0), lg(-442404110, 7696), lg(1489246, 0)) - test(lg(2, 0), lg(-631827526, 1455), lg(8, 0)) - test(lg(5125741, 0), lg(1266390909, 49), lg(-34627848, -1)) - test(lg(77691, 0), lg(-453014259, 21413), lg(149449, 0)) - test(lg(521867604, 0), lg(1573062436, 653), lg(671211684, 0)) - test(lg(14579368, 0), lg(-21113520, 0), lg(177469767, 0)) - test(lg(0, 0), lg(-262825676, 31), lg(1, 0)) - test(lg(24027362, 0), lg(-163968426, 1), lg(33341027, 0)) - test(lg(6792805, 0), lg(668741217, 14380), lg(-11334498, -1)) - test(lg(9, 0), lg(808041281, 1818), lg(-10, -1)) - test(lg(204, 0), lg(-1601247507, 25), lg(-235, -1)) - test(lg(61089, 0), lg(-1577206289, 0), lg(1618642, 0)) - test(lg(289305533, 0), lg(863396135, 503), lg(-321808286, -1)) - test(lg(7272892, 0), lg(-900149281, 55), lg(15166197, 0)) - test(lg(3, 0), lg(1802954050, 3593), lg(7, 0)) - test(lg(12036, 0), lg(800669146, 41901), lg(-20591, -1)) - test(lg(29, 0), lg(-1055636867, 39), lg(48, 0)) - test(lg(0, 0), lg(-491067123, 14), lg(1, 0)) - test(lg(260441364, 0), lg(1420289126, 67), lg(1010219079, 0)) - test(lg(3936541, 0), lg(1338756461, 32), lg(-4427443, -1)) - test(lg(183313645, 0), lg(-820843233, 778), lg(-273780418, -1)) - test(lg(91783, 0), lg(-1033566360, 561225), lg(-156677, -1)) - test(lg(5, 0), lg(-1567070603, 38), lg(-8, -1)) - test(lg(11214823, 0), lg(-1649343541, 185302), lg(-19368267, -1)) - test(lg(75719, 0), lg(-591434325, 76351), lg(94212, 0)) - test(lg(10941, 0), lg(235794528, 55), lg(17599, 0)) - test(lg(5331, 0), lg(-763589741, 116), lg(-14942, -1)) - test(lg(1, 0), lg(-1283158225, 237055), lg(-2, -1)) - test(lg(24400, 0), lg(1537105400, 29108), lg(-37848, -1)) - test(lg(95, 0), lg(-56778611, 994650), lg(-170, -1)) - test(lg(9836, 0), lg(-2057746932, 7), lg(-10100, -1)) - test(lg(30255783, 0), lg(1365793356, 12), lg(-38454651, -1)) - test(lg(417, 0), lg(-2128793438, 4), lg(6825, 0)) - test(lg(0, 0), lg(1667515072, 8), lg(2, 0)) - test(lg(257, 0), lg(420324337, 980), lg(-845, -1)) - test(lg(82991, 0), lg(-771084081, 8204), lg(105392, 0)) - test(lg(691256, 0), lg(-332377894, 1), lg(882238, 0)) - test(lg(0, 0), lg(1749263284, 11), lg(-20, -1)) - test(lg(4, 0), lg(347303218, 1234317), lg(-13, -1)) - test(lg(150, 0), lg(1199079324, 17271), lg(11033, 0)) - test(lg(14, 0), lg(1196217208, 13), lg(-23, -1)) - test(lg(256216433, 0), lg(-1078128939, 0), lg(740155481, 0)) - test(lg(45583, 0), lg(-1354463473, 3691), lg(-63588, -1)) - test(lg(459, 0), lg(-1255896801, 1469630), lg(-502, -1)) + test(15827410L, 14129525327L, -231372097L) + test(15118L, 535478124622L, -20252L) + test(11L, 309815810367L, 13L) + test(42298679L, 15343123799L, -95630157L) + test(17447610L, 127082894730L, -45315780L) + test(0L, 1089230016351334L, -11L) + test(51980L, 33057920873202L, 1489246L) + test(2L, 6252840555450L, 8L) + test(5125741L, 211719788413L, -34627848L) + test(77691L, 91971976662285L, 149449L) + test(521867604L, 2806186706724L, 671211684L) + test(14579368L, 4273853776L, 177469767L) + test(0L, 137176127796L, 1L) + test(24027362L, 8425966166L, 33341027L) + test(6792805L, 61762298457697L, -11334498L) + test(9L, 7809058585409L, -10L) + test(204L, 110067902189L, -235L) + test(61089L, 2717761007L, 1618642L) + test(289305533L, 2161231946023L, -321808286L) + test(7272892L, 239618019295L, 15166197L) + test(3L, 15433620448578L, 7L) + test(12036L, 179964225338842L, -20591L) + test(29L, 170743054973L, 48L) + test(0L, 63933442317L, 1L) + test(260441364L, 289183097958L, 1010219079L) + test(3936541L, 138777709933L, -4427443L) + test(183313645L, 3344958680351L, -273780418L) + test(91783L, 2410446282098536L, -156677L) + test(5L, 165936653941L, -8L) + test(11214823L, 795868675507147L, -19368267L) + test(75719L, 327928751549867L, 94212L) + test(10941L, 236458995808L, 17599L) + test(5331L, 501747583891L, -14942L) + test(1L, 1018146484162351L, -2L) + test(24400L, 125019445157368L, -37848L) + test(95L, 4271993459155085L, -170L) + test(9836L, 32301991436L, -10100L) + test(30255783L, 52905400908L, -38454651L) + test(417L, 19346043042L, 6825L) + test(0L, 36027253440L, 2L) + test(257L, 4209488274417L, -845L) + test(82991L, 35239435579599L, 105392L) + test(691256L, 8257556698L, 882238L) + test(0L, 48993903540L, -20L) + test(4L, 5301351495200050L, -13L) + test(150L, 74179579248540L, 11033L) + test(14L, 57030792056L, -23L) + test(256216433L, 3216838357L, 740155481L) + test(45583L, 15855664793359L, -63588L) + test(459L, 6312015826290975L, -502L) // int53, int53 - test(lg(1805177178, 1), lg(1805177178, 1), lg(-1293833696, 410)) - test(lg(-583440651, 2), lg(647007072, 1811985), lg(1091239449, 3)) - test(lg(1346307032, 1), lg(1346307032, 1), lg(-672335266, 33)) - test(lg(858355422, 81), lg(858355422, 81), lg(1490435172, 162402)) - test(lg(744276027, 1), lg(-1299053281, 6330), lg(1042770708, 1)) - test(lg(29273105, 0), lg(-88774269, 25), lg(775537355, 1)) - test(lg(383200445, 2), lg(-962613261, 4309), lg(-529185362, 5)) - test(lg(-171009725, 445), lg(-171009725, 445), lg(-1167557775, 307982)) - test(lg(8166883, 15498), lg(1848497503, 78519), lg(1533824479, 15755)) - test(lg(-1752533311, 17), lg(-1752533311, 17), lg(1904799096, 73566)) - test(lg(-1641266817, 46), lg(-1641266817, 46), lg(-31936789, 751199)) - test(lg(-350685679, 656), lg(-637954451, 32352), lg(-10259599, 1131)) - test(lg(-1671876486, 0), lg(-1657673170, 122149), lg(-534342412, 0)) - test(lg(-660565679, 235), lg(-660565679, 235), lg(-897090894, 14655)) - test(lg(-1798560222, 612), lg(-1798560222, 612), lg(-236039758, 2924)) - test(lg(-28767936, 5704), lg(1010899296, 62798), lg(-1974205776, 9515)) - test(lg(-2004786867, 4), lg(1206965517, 91420), lg(880030876, 7)) - test(lg(712148070, 3), lg(712148070, 3), lg(472319826, 2838)) - test(lg(-1275175525, 44), lg(-1275175525, 44), lg(162799342, 861329)) - test(lg(1187224322, 14), lg(-516916094, 191396), lg(-1920802608, 30)) - test(lg(-1461747946, 0), lg(-1627551726, 4499), lg(1200735793, 1)) - test(lg(453535447, 39039), lg(453535447, 39039), lg(520791957, 141909)) - test(lg(216221627, 20), lg(216221627, 20), lg(-781572865, 8131)) - test(lg(1611884803, 23), lg(-1999221053, 528), lg(1107934896, 25)) - test(lg(1722095012, 0), lg(-701225584, 44), lg(-1403297482, 0)) - test(lg(-232837834, 5049), lg(-232837834, 5049), lg(1000581509, 15836)) - test(lg(-82376749, 239), lg(-82376749, 239), lg(-163409376, 7688)) - test(lg(2063025646, 2), lg(941363778, 110), lg(336092572, 3)) - test(lg(721574845, 383), lg(1004884706, 1133), lg(283309861, 750)) - test(lg(-2004547354, 47), lg(1436404594, 1595), lg(1522987410, 70)) - test(lg(1696970595, 8), lg(1696970595, 8), lg(-1168832286, 4163)) - test(lg(-2033329312, 6), lg(-1244970780, 32), lg(394179266, 13)) - test(lg(1864629418, 1), lg(1864629418, 1), lg(528888491, 970677)) - test(lg(1596298266, 43057), lg(-1763600443, 962032), lg(1535552275, 102108)) - test(lg(1181714932, 5), lg(1181714932, 5), lg(1296434411, 26359)) - test(lg(-2140209952, 7), lg(1535735456, 276446), lg(-1930593680, 7)) - test(lg(-1703068243, 11), lg(2079501385, 97596), lg(-1803771626, 21)) - test(lg(-1025858772, 33402), lg(286993796, 174379), lg(656426284, 70488)) - test(lg(-578045904, 11724), lg(221015334, 1635766), lg(-2014306775, 270673)) - test(lg(-2080784768, 56), lg(-2103734262, 977), lg(-22949494, 920)) - test(lg(-922083739, 29), lg(-922083739, 29), lg(2040148267, 19160)) - test(lg(-1728890579, 468), lg(-559850131, 11989), lg(1366001936, 2880)) - test(lg(1341547600, 13), lg(-1071198220, 2182), lg(1526886260, 17)) - test(lg(-896451936, 45), lg(-896451936, 45), lg(2132477227, 164356)) - test(lg(-1538011120, 53), lg(-561327714, 1420), lg(-368698210, 151)) - test(lg(1880884956, 621), lg(2112956103, 118429), lg(-374507565, 859)) - test(lg(902909663, 0), lg(380445410, 8), lg(-1822479769, 1)) - test(lg(-652149100, 56), lg(-1867274924, 105813), lg(175641312, 79)) - test(lg(-991170416, 37), lg(-991170416, 37), lg(1740161397, 88122)) - test(lg(-31602776, 1), lg(-31602776, 1), lg(-503633567, 241909)) + test(6100144474L, 6100144474L, 1763937724960L) + test(12301461237L, 7782416962849632L, 13976141337L) + test(5641274328L, 5641274328L, 145356552798L) + test(348750706398L, 348750706398L, 697512769240164L) + test(5039243323L, 27190138897695L, 5337738004L) + test(29273105L, 111580375427L, 5070504651L) + test(8973135037L, 18510346432499L, 25240618414L) + test(1915384404291L, 1915384404291L, 1322775745166193L) + test(66563411320291L, 337238385612127L, 67668743572959L) + test(75556878017L, 75556878017L, 315965468896632L) + test(200222196095L, 200222196095L, 3226379400818411L) + test(2821442827793L, 138954438973037L, 4861892719473L) + test(2623090810L, 524628597533230L, 3760624884L) + test(1012951716177L, 1012951716177L, 62946143599282L) + test(2631016392226L, 2631016392226L, 12562543301042L) + test(24502759655744L, 269716367153504L, 40868934582960L) + test(19470049613L, 392647117165837L, 30944801948L) + test(13597049958L, 13597049958L, 12189589505874L) + test(191998352795L, 191998352795L, 3699380048895726L) + test(61316766466L, 822043338636418L, 131223183568L) + test(2833219350L, 19325725280274L, 5495703089L) + test(167671681803991L, 167671681803991L, 609495034800021L) + test(86115567547L, 86115567547L, 34925892478207L) + test(100396132611L, 2270038478531L, 108482117296L) + test(1722095012L, 192572302736L, 2891669814L) + test(21689352006966L, 21689352006966L, 68016102680965L) + test(1030709774291L, 1030709774291L, 33023840129568L) + test(10652960238L, 473387766338L, 13220994460L) + test(1645694049213L, 4867202831074L, 3221508781861L) + test(204153882854L, 6851909241714L, 302170698130L) + test(36056708963L, 36056708963L, 17883074988258L) + test(28031441760L, 140488949988L, 56228754114L) + test(6159596714L, 6159596714L, 4169026498867883L) + test(184930003162138L, 4131898509072325L, 438552056212243L) + test(22656551412L, 22656551412L, 113212339389675L) + test(32219528416L, 1187328064845472L, 32429144688L) + test(49836539309L, 419173707721801L, 92685508886L) + test(143463766729516L, 748952389102980L, 302744311186732L) + test(50357913499696L, 7025561694924070L, 1162533963570729L) + test(242732351104L, 4198374281226L, 3955641930122L) + test(127926935141L, 127926935141L, 82293613539627L) + test(2012610771245L, 51496098028909L, 12370871814416L) + test(57176122448L, 9374842408948L, 74541330292L) + test(196672043680L, 196672043680L, 705905777378603L) + test(230390222864L, 6102587199902L, 652466330782L) + test(2669055575772L, 508650794854087L, 3693297366995L) + test(902909663L, 34740183778L, 6767454823L) + test(244160986772L, 454465802184020L, 339478057696L) + test(162217586832L, 162217586832L, 378482848219509L) + test(8558331816L, 8558331816L, 1038995034941793L) // int53, big - test(lg(-930109303, 3), lg(-930109303, 3), lg(1606982787, 925386547)) - test(lg(-717668907, 16251), lg(-717668907, 16251), lg(2079100937, 7825426)) - test(lg(265990345, 3), lg(265990345, 3), lg(-1140922127, -3108870)) - test(lg(-1181318422, 1), lg(-1181318422, 1), lg(1489652251, 75207246)) - test(lg(380276439, 59), lg(380276439, 59), lg(-1062351234, -3631372)) - test(lg(1080382784, 7211), lg(1080382784, 7211), lg(572850722, -139092025)) - test(lg(2020323378, 316), lg(2020323378, 316), lg(1716930349, -16333391)) - test(lg(1302118364, 5), lg(1302118364, 5), lg(-442067036, 1941456592)) - test(lg(-641137972, 602), lg(-641137972, 602), lg(1134212295, -135713760)) - test(lg(-761172703, 499), lg(-761172703, 499), lg(769981236, 12756336)) - test(lg(1601268090, 610), lg(1601268090, 610), lg(448513898, -160887452)) - test(lg(-16483553, 0), lg(-16483553, 0), lg(-1253549192, -1748027086)) - test(lg(-1284021361, 241), lg(-1284021361, 241), lg(13275221, -3818882)) - test(lg(1499414278, 26), lg(1499414278, 26), lg(570654893, -17498947)) - test(lg(-368610421, 5074), lg(-368610421, 5074), lg(685701351, 31070898)) - test(lg(1200134796, 70), lg(1200134796, 70), lg(1230376618, -2490370)) - test(lg(1537764087, 64483), lg(1537764087, 64483), lg(-1252591472, 66761881)) - test(lg(-1981129198, 15), lg(-1981129198, 15), lg(1937978150, 8201544)) - test(lg(32422964, 200), lg(32422964, 200), lg(2051327691, -20319622)) - test(lg(1404616230, 30), lg(1404616230, 30), lg(-748420073, -120320053)) - test(lg(-1860381107, 38), lg(-1860381107, 38), lg(392948122, 60098039)) - test(lg(1050519262, 106431), lg(1050519262, 106431), lg(361773491, -6329760)) - test(lg(460136491, 1681770), lg(460136491, 1681770), lg(1399049044, 759923035)) - test(lg(2065599344, 11089), lg(2065599344, 11089), lg(-465681057, 3484544)) - test(lg(1849358428, 418531), lg(1849358428, 418531), lg(1023666326, 3435570)) - test(lg(1292603836, 80), lg(1292603836, 80), lg(-1114872574, 250120091)) - test(lg(1456627133, 194844), lg(1456627133, 194844), lg(-1256385160, 59427917)) - test(lg(-568179858, 160), lg(-568179858, 160), lg(1142846538, 154324747)) - test(lg(-2133580755, 203337), lg(-2133580755, 203337), lg(111334842, 12695612)) - test(lg(1961218705, 6687), lg(1961218705, 6687), lg(-245612957, 134017780)) - test(lg(335350966, 55096), lg(335350966, 55096), lg(-1815119598, -120983980)) - test(lg(-767561503, 211), lg(-767561503, 211), lg(554589640, -7873602)) - test(lg(1476687067, 3767), lg(1476687067, 3767), lg(552659809, -753378142)) - test(lg(-1107393223, 30), lg(-1107393223, 30), lg(-78383575, -52663801)) - test(lg(607313614, 2), lg(607313614, 2), lg(-234099925, 59184919)) - test(lg(-1542671184, 616882), lg(-1542671184, 616882), lg(1370026838, -45628731)) - test(lg(525616384, 1001), lg(525616384, 1001), lg(1995646126, -11226360)) - test(lg(2109958916, 21549), lg(2109958916, 21549), lg(-419960245, -115959896)) - test(lg(-450913111, 32140), lg(-450913111, 32140), lg(-99267096, -3640047)) - test(lg(1515870052, 198), lg(1515870052, 198), lg(1415757861, -110282301)) - test(lg(124639649, 865615), lg(124639649, 865615), lg(-1354782388, 2569606)) - test(lg(557119825, 7205), lg(557119825, 7205), lg(683150209, -15864187)) - test(lg(992846513, 1385110), lg(992846513, 1385110), lg(1578961851, -8380578)) - test(lg(1081385155, 4176), lg(1081385155, 4176), lg(1892231070, 31130825)) - test(lg(-738492748, 8), lg(-738492748, 8), lg(-431212066, 687916944)) - test(lg(-1448153936, 8101), lg(-1448153936, 8101), lg(-584523654, -4814205)) - test(lg(-713251055, 243), lg(-713251055, 243), lg(261411225, 31444708)) - test(lg(881178812, 47057), lg(881178812, 47057), lg(823893049, -5940358)) - test(lg(-506817388, 0), lg(-506817388, 0), lg(-465610822, 10559551)) - test(lg(-420315839, 112832), lg(-420315839, 112832), lg(-686319219, -666166549)) + test(16249759881L, 16249759881L, 3974504957130349699L) + test(69801090825685L, 69801090825685L, 33609950826369033L) + test(13150892233L, 13150892233L, -13352491823470351L) + test(7408616170L, 7408616170L, 323012663481879067L) + test(253783346903L, 253783346903L, -15596620746994050L) + test(30972089554240L, 30972089554240L, -597395697936563678L) + test(1359229988914L, 1359229988914L, -70151378460850387L) + test(22776954844L, 22776954844L, 8338492573096515492L) + test(2589224141516L, 2589224141516L, -582886159682980665L) + test(2146722475297L, 2146722475297L, 54788046706768692L) + test(2621531318650L, 2621531318650L, -691006344228255894L) + test(4278483743L, 4278483743L, -7507719163850761352L) + test(1038098064271L, 1038098064271L, -16401973284007851L) + test(113168563974L, 113168563974L, -75157404508782419L) + test(21796590416779L, 21796590416779L, 133448491453053159L) + test(301847845516L, 301847845516L, -10696056474562902L) + test(276953913912055L, 276953913912055L, 286740098556819600L) + test(66738347538L, 66738347538L, 35225365194683174L) + test(859025882164L, 859025882164L, -87272109905754421L) + test(130253635110L, 130253635110L, -516770689141439465L) + test(165643343437L, 165643343437L, 258119112451680666L) + test(457118714799838L, 457118714799838L, -27186111829755469L) + test(7223147609530411L, 7223147609530411L, 3263844584201112404L) + test(47628957944688L, 47628957944688L, 14966006350759263L) + test(1797578806720604L, 1797578806720604L, 14755661816785046L) + test(344889987516L, 344889987516L, 1074257614097638658L) + test(836850064448957L, 836850064448957L, 255240963022984568L) + test(690921554798L, 690921554798L, 662819742471320650L) + test(873327926453293L, 873327926453293L, 54527238454039994L) + test(28722407527057L, 28722407527057L, 575601986231877219L) + test(236635853491382L, 236635853491382L, -519622234960070382L) + test(909765505249L, 909765505249L, -33816862537130552L) + test(16180618491099L, 16180618491099L, -3235734480858584223L) + test(132036592953L, 132036592953L, -226189298761468375L) + test(9197248206L, 9197248206L, 254197295582276395L) + test(2649490767787184L, 2649490767787184L, -195973906032954538L) + test(4299787879680L, 4299787879680L, -48216847057476434L) + test(92554360220420L, 92554360220420L, -498043957092554165L) + test(138044092947625L, 138044092947625L, -15633878625202712L) + test(851919394660L, 851919394660L, -473658874706870235L) + test(3717788240566689L, 3717788240566689L, 11036376673790284L) + test(30945796487505L, 30945796487505L, -68136163659478143L) + test(5949003144209073L, 5949003144209073L, -35994306852615237L) + test(17936864813251L, 17936864813251L, 133705877164730270L) + test(37916212916L, 37916212916L, 2954580780708018654L) + test(34796376878256L, 34796376878256L, -20676849320796038L) + test(1047258769169L, 1047258769169L, 135053992753680793L) + test(202109157226684L, 202109157226684L, -25513642512638919L) + test(3788149908L, 3788149908L, 45352930034800570L) + test(484613624593729L, 484613624593729L, -2861163538035533427L) // big, int32 - test(lg(-3, -1), lg(-412174169, -319069709), lg(-6, -1)) - test(lg(464005, 0), lg(1634601702, 814446468), lg(825883, 0)) - test(lg(34559370, 0), lg(-1005992901, 2694218), lg(108493743, 0)) - test(lg(-286379, -1), lg(1534700309, -630528658), lg(-506616, -1)) - test(lg(-62, -1), lg(-456613426, -23298167), lg(-206, -1)) - test(lg(386945695, 0), lg(857770611, 2618490), lg(1225551197, 0)) - test(lg(270232, 0), lg(2127943654, 2768088), lg(-291653, -1)) - test(lg(277129, 0), lg(1085973072, 3470797), lg(-29714535, -1)) - test(lg(15, 0), lg(1536124828, 1268901218), lg(-121, -1)) - test(lg(1, 0), lg(371220141, 34588968), lg(2, 0)) - test(lg(46669, 0), lg(-1712997009, 187259899), lg(129274, 0)) - test(lg(-1508, -1), lg(586579000, -243530833), lg(-31235, -1)) - test(lg(0, 0), lg(1745775262, -400161972), lg(-1, -1)) - test(lg(-1680, -1), lg(-1564631310, -56487209), lg(2626, 0)) - test(lg(53, 0), lg(-1848745069, 11533547), lg(59, 0)) - test(lg(-1699972, -1), lg(-1415791920, -26215621), lg(-2142359, -1)) - test(lg(-200041, -1), lg(-481609933, -25891343), lg(483607, 0)) - test(lg(-13123232, -1), lg(-889674017, -4084771), lg(428648085, 0)) - test(lg(0, 0), lg(1587465684, -367383975), lg(7, 0)) - test(lg(-4528, -1), lg(811562260, -335104547), lg(5502, 0)) - test(lg(-71, -1), lg(2107357891, -10075787), lg(110, 0)) - test(lg(0, 0), lg(-1356326655, 5174156), lg(-1, -1)) - test(lg(7872112, 0), lg(-1794856776, 3059124), lg(-29413816, -1)) - test(lg(-37, -1), lg(-1118254374, -3629384), lg(-85, -1)) - test(lg(14227, 0), lg(288539563, 70814306), lg(-14561, -1)) - test(lg(-49, -1), lg(-719069745, -128562664), lg(-256, -1)) - test(lg(6101, 0), lg(1530955727, 15829469), lg(195494, 0)) - test(lg(-6, -1), lg(2144004402, -5408490), lg(11, 0)) - test(lg(-137624717, -1), lg(-1766192560, -17443468), lg(-168087095, -1)) - test(lg(-3592, -1), lg(-524619138, -371121095), lg(4765, 0)) - test(lg(4335, 0), lg(-1960083221, 176122524), lg(-5564, -1)) - test(lg(-271754, -1), lg(1528631102, -597885631), lg(-413908, -1)) - test(lg(-361112, -1), lg(-1513123614, -30582360), lg(-496311, -1)) - test(lg(-4, -1), lg(-1975522255, -46421733), lg(29, 0)) - test(lg(414436, 0), lg(-1715879325, 3072313), lg(438221, 0)) - test(lg(0, 0), lg(-1321015849, -300384564), lg(1, 0)) - test(lg(-454, -1), lg(-1088390706, -277354665), lg(-1237, -1)) - test(lg(586891857, 0), lg(-1012773943, 223943652), lg(707359548, 0)) - test(lg(2, 0), lg(1097288344, 26740237), lg(-3, -1)) - test(lg(-24053960, -1), lg(-1121404205, -87484234), lg(80229261, 0)) - test(lg(-79944815, -1), lg(-1503637931, -163703901), lg(-983334452, -1)) - test(lg(2600110, 0), lg(2012820970, 445991475), lg(1035472980, 0)) - test(lg(74, 0), lg(2015362538, 2985510), lg(-148, -1)) - test(lg(0, 0), lg(1764134228, 50881407), lg(-1, -1)) - test(lg(106, 0), lg(-523555853, 77167937), lg(-563, -1)) - test(lg(0, 0), lg(1531888651, -2389306), lg(1, 0)) - test(lg(659, 0), lg(-181277952, 32599207), lg(-729, -1)) - test(lg(968, 0), lg(223126732, 88838488), lg(13378, 0)) - test(lg(920991, 0), lg(670834629, 46037187), lg(922370, 0)) - test(lg(2462152, 0), lg(1098978850, 6541822), lg(-8405198, -1)) + test(-3L, -1370393961416443737L, -6L) + test(464005L, 3498020946037312230L, 825883L) + test(34559370L, 11571581487268923L, 108493743L) + test(-286379L, -2708099963766068459L, -506616L) + test(-62L, -100064861483392562L, -206L) + test(386945695L, 11246329772673651L, 1225551197L) + test(270232L, 11888849560393702L, -291653L) + test(277129L, 14906960692027984L, -29714535L) + test(15L, 5449889234700691356L, -121L) + test(1L, 148558486733610669L, 2L) + test(46669L, 804275144639233391L, 129274L) + test(-1508L, -1045956962716058568L, -31235L) + test(0L, -1718682581097092450L, -1L) + test(-1680L, -242610712566980878L, 2626L) + test(53L, 49536209618101139L, 59L) + test(-1699972L, -112595231960155440L, -2142359L) + test(-200041L, -111202467621161165L, 483607L) + test(-13123232L, -17543954451355937L, 428648085L) + test(0L, -1577902156112015916L, 7L) + test(-4528L, -1439263069294332652L, 5502L) + test(-71L, -43275173539104061L, 110L) + test(0L, 22222833743042817L, -1L) + test(7872112L, 13138840034519224L, -29413816L) + test(-37L, -15588082407912742L, -85L) + test(14227L, 304145128647476139L, -14561L) + test(-49L, -552172433790738993L, -256L) + test(6101L, 67987053199001551L, 195494L) + test(-6L, -23229285526738638L, 11L) + test(-137624717L, -74919122060047792L, -168087095L) + test(-3592L, -1593952962110360962L, 4765L) + test(4335L, 756440483003859179L, -5564L) + test(-271754L, -2567899230364692674L, -413908L) + test(-361112L, -131350233252654878L, -496311L) + test(-4L, -199379822739198927L, 29L) + test(414436L, 13195486437163619L, 438221L) + test(0L, -1290141875629267497L, 1L) + test(-454L, -1191229212361459250L, -1237L) + test(586891857L, 961830664768998345L, 707359548L) + test(2L, 114848444499577496L, -3L) + test(-24053960L, -375741920772048173L, 80229261L) + test(-79944815L, -703102898231292331L, -983334452L) + test(2600110L, 1915518801432622570L, 1035472980L) + test(74L, 12822669827243498L, -148L) + test(0L, 218533980803599700L, -1L) + test(106L, 331433769486199795L, -563L) + test(0L, -10261989598247925L, 1L) + test(659L, 140012532054223616L, -729L) + test(968L, 381558400809215180L, 13378L) + test(920991L, 197728213235670981L, 922370L) + test(2462152L, 28096912645232162L, -8405198L) // big, int53 - test(lg(1057995305, 4748), lg(2008672965, 41566313), lg(313991275, 18390)) - test(lg(-1074209653, 18), lg(1922552561, 28139870), lg(-2083633557, 19)) - test(lg(1480601143, -11310), lg(843627074, -173776705), lg(1451117493, 14364)) - test(lg(-691687452, -38), lg(204865470, -6692402), lg(-645190286, 413)) - test(lg(-1218791457, -31), lg(952830559, -214594684), lg(-1778162360, 378)) - test(lg(-281609960, -1292), lg(1673740333, -69274846), lg(-1549261605, 2390)) - test(lg(-860426348, 1), lg(-1276804811, 367022678), lg(-678111623, 11)) - test(lg(-1244563205, -1264), lg(-1331527548, -33013551), lg(-1975438267, 2961)) - test(lg(-935830326, 135167), lg(1067523314, 72606174), lg(-1716982106, 255179)) - test(lg(-2025081444, -42140), lg(-937134490, -32649070), lg(-804857990, 57507)) - test(lg(85696931, 194), lg(108363299, 1224097478), lg(1137551776, 281)) - test(lg(-385517902, -5258), lg(-1965834834, -11053948), lg(-942300324, 6487)) - test(lg(-755355475, 2268), lg(-3151939, 171473802), lg(-2071379940, 3914)) - test(lg(-676865399, -663), lg(1465781759, -970108425), lg(-1251607207, 3003)) - test(lg(2042443783, -22321), lg(919308511, -1689158617), lg(658566728, 36406)) - test(lg(-903837593, 31415), lg(-418485001, 1000432592), lg(-1653953022, 31957)) - test(lg(496274972, -48207), lg(-880302655, -14116770), lg(913871933, 118223)) - test(lg(1210119082, -104892), lg(-525597278, -3790314), lg(2133284776, 127083)) - test(lg(473810731, -5), lg(-393124913, -28106221), lg(958070140, 159)) - test(lg(-1912903061, 25777), lg(6929245, 2749730), lg(1462129294, 43237)) - test(lg(1099532724, -19), lg(708024745, -15568245), lg(1288198049, 56)) - test(lg(920504149, 6836), lg(487601139, 13603229), lg(723875593, 45021)) - test(lg(1778080723, 29), lg(-2070321133, 115478389), lg(-1799479616, 75)) - test(lg(-720480381, 2735), lg(-307180735, 3049800), lg(1043781053, 3319)) - test(lg(1473972065, -1), lg(-1073877839, -6538577), lg(-1408649838, 0)) - test(lg(-1389255096, -200), lg(-1892822171, -1698321438), lg(96164237, 514)) - test(lg(857386403, 29656), lg(-674980011, 2764943), lg(-445529419, 65125)) - test(lg(-419043446, -22164), lg(2003347800, -46928389), lg(368897711, 128159)) - test(lg(-1599543668, -6569), lg(-1929871429, -241628283), lg(202358381, 7645)) - test(lg(581185953, 1), lg(419719197, 661188517), lg(2112360098, 1)) - test(lg(-1880704128, 171407), lg(1092830824, 1600823129), lg(-1827462760, 172800)) - test(lg(1210159480, -13), lg(-836779994, -27475595), lg(-417527207, 16)) - test(lg(807846066, 1), lg(-1759597755, 9157722), lg(-987185779, 1)) - test(lg(949995673, 1), lg(-1097231525, 20092165), lg(1106421078, 1)) - test(lg(-712450167, 7), lg(390678483, 3835040), lg(1221250555, 14)) - test(lg(1129531033, -4), lg(-284334384, -18425278), lg(-1111448031, 6)) - test(lg(2094997010, 3022), lg(-233961390, 53260849), lg(-613558136, 3663)) - test(lg(-496446555, 540290), lg(-3383211, 8039036), lg(-1668680584, 749874)) - test(lg(1280740603, -9472), lg(804358887, -189240235), lg(179665302, 12347)) - test(lg(2127427912, 6), lg(208769744, 280071599), lg(-325433064, 14)) - test(lg(-722136158, -1), lg(-1527711901, -51564742), lg(-1019145455, 0)) - test(lg(-1603688570, -2), lg(-159182038, -2145592347), lg(-483720705, 15)) - test(lg(-256578646, 177817), lg(1059926378, 477886379), lg(924988992, 543468)) - test(lg(1286157765, 80885), lg(-1800046387, 119696078), lg(436524799, 94037)) - test(lg(251450065, 19154), lg(-822280387, 44882065), lg(-940828508, 22947)) - test(lg(1310986115, 209), lg(1465101985, 269803551), lg(-1953360551, 334)) - test(lg(1436855439, -5), lg(-567675197, -8838663), lg(1903221047, 6)) - test(lg(296887390, -17), lg(689376065, -22622471), lg(1534988921, 63)) - test(lg(1577958450, -39), lg(-2017356377, -57717216), lg(-1390284125, 42)) - test(lg(661387374, 344542), lg(-128715878, 982583003), lg(2004099318, 988167)) + test(20393562716713L, 178525956958972613L, 78984762564715L) + test(80530168971L, 120859823286244081L, 83815712363L) + test(-48574599516617L, -746365263938012606L, 61694361357237L) + test(-159605477404L, -28743647516819522L, 1777471270258L) + test(-130067810337L, -921677148722623905L, 1626014442824L) + test(-5545084389096L, -297533196331696083L, 10267717543131L) + test(7729508244L, 1576350401918501173L, 50861495929L) + test(-5425788258053L, -141792118906388348L, 12719717692485L) + test(580541203635402L, 311841143885208818L, 1095988037611174L) + test(-180987651967588L, -140226684536981914L, 246994174400378L) + test(833309352355L, 5257458635234442787L, 1208023361952L) + test(-22579028592974L, -47476342822552146L, 27864805516124L) + test(9744525439149L, 736474376002594749L, 16812725583900L) + test(-2843945215351L, -4166583957483287041L, 12900830149977L) + test(-95865922570233L, -7254881016852281121L, 156363237944904L) + test(134929788733543L, 4296825268368993527L, 137256910892546L) + test(-207046992163300L, -60631062060489279L, 507764832506941L) + test(-450506499492950L, -16279270902200926L, 545819462162344L) + test(-21001025749L, -120715296107306033L, 683857870204L) + test(110713754053227L, 11810000429759325L, 185702963106446L) + test(-80504845900L, -66865102423090775L, 241806366625L) + test(29361316939605L, 58425424162599923L, 193364446508809L) + test(126332132307L, 495975906374412307L, 324618034880L) + test(11750310041475L, 13098795247127361L, 14256040236477L) + test(-2820995231L, -28082971156288335L, 2886317458L) + test(-856087747000L, -7294235031903546523L, 2207709354381L) + test(127372407516579L, 11875343380291413L, 279713594589877L) + test(-95189779224694L, -201555894005618344L, 550439082585775L) + test(-28210944743796L, -1037785570908536901L, 32835227336301L) + test(4876153249L, 2839783057425459229L, 6407327394L) + test(736189873568640L, 6875482986828220008L, 742172816253336L) + test(-54624415368L, -118006778504953818L, 72596916825L) + test(5102813362L, 39332119031229253L, 7602748813L) + test(5244962969L, 86295194778571611L, 5401388374L) + test(33647288201L, 16471371769530323L, 61350792699L) + test(-16050338151L, -79135962419075376L, 28953323041L) + test(12981486165522L, 228753608673200210L, 15736146614408L) + test(2320531678876581L, 34527401002950741L, 3220686932407416L) + test(-40680649487109L, -812780619607995673L, 53030140869014L) + test(27897231688L, 1202898358452196048L, 64099076376L) + test(-722136158L, -221468877749422237L, 3275821841L) + test(-5898655866L, -9215248956777098454L, 68235756031L) + test(763722238061482L, 2052506370068787562L, 2334178211411520L) + test(347399715894725L, 514090742964385997L, 403886276138751L) + test(82266055037649L, 192767004824633149L, 98559968680100L) + test(898959150979L, 1158797429354770081L, 1436860683609L) + test(-20037981041L, -37961764798073149L, 27673024823L) + test(-72717556642L, -97162772410332351L, 272117928569L) + test(-165925766094L, -247893552858557017L, 183293309603L) + test(1479797283485806L, 4220161867656721306L, 4244146952085750L) // big, big - test(lg(-320078007, 205603273), lg(-320078007, 205603273), lg(2020227799, -360928021)) - test(lg(408769930, -2221999), lg(-800732960, -371808530), lg(744251542, -11199592)) - test(lg(1575977183, -2441606), lg(-56774921, -32434115), lg(1413374280, -2726592)) - test(lg(-1897285736, 18894093), lg(1667937500, 228622683), lg(-243248020, 69909529)) - test(lg(-1333815518, 2097776), lg(-1333815518, 2097776), lg(-1750106076, 18608702)) - test(lg(-789967161, -4640836), lg(-162800691, -117885498), lg(-709007774, 8711127)) - test(lg(-1909427145, -2824029), lg(-1909427145, -2824029), lg(2028036056, -660713154)) - test(lg(14077923, 63046905), lg(14077923, 63046905), lg(-688765214, 375445962)) - test(lg(272760540, 19525127), lg(272760540, 19525127), lg(-396955631, 848435537)) - test(lg(-600396362, 406643261), lg(-600396362, 406643261), lg(-1533973181, 491661310)) - test(lg(1801834226, 200420454), lg(1801834226, 200420454), lg(-1889418050, -328758068)) - test(lg(361053022, 54544094), lg(1170836790, 510289402), lg(202445942, 113936327)) - test(lg(1369752396, -3152427), lg(-378923036, -1036580478), lg(905093048, 5526353)) - test(lg(1458911735, 21273958), lg(-2137034353, 1455139814), lg(1665353214, 27574343)) - test(lg(-1350216191, -3821167), lg(-1350216191, -3821167), lg(-1333339390, -4746360)) - test(lg(1166542449, -1370750), lg(-1289646201, -5193401), lg(1838778646, -3822651)) - test(lg(301867174, 5185218), lg(301867174, 5185218), lg(157012848, -15464466)) - test(lg(512572633, 48335882), lg(467711834, 155069651), lg(-44860799, 106733768)) - test(lg(1624269582, 11007763), lg(1624269582, 11007763), lg(-158694824, -491219717)) - test(lg(-1015519521, -163989350), lg(-1015519521, -163989350), lg(1652525166, 530116116)) - test(lg(-2127450406, -89864400), lg(2001612518, -452587333), lg(1115217917, 90680733)) - test(lg(-761803769, -6085789), lg(1039524645, -86121932), lg(1131434363, 13339357)) - test(lg(-1922291990, 6439098), lg(-1922291990, 6439098), lg(-1083372307, -20634200)) - test(lg(1508171882, 126457), lg(1408756974, 235847122), lg(-1813277898, -9066180)) - test(lg(-496706473, -2657930), lg(1121009342, -1533788016), lg(-1724900447, -5821788)) - test(lg(-1626361260, -113469353), lg(-1626361260, -113469353), lg(1216987736, -817139415)) - test(lg(-433139577, -182483493), lg(-433139577, -182483493), lg(1019490766, -595625160)) - test(lg(-1118452074, 1653764), lg(793542905, 198273616), lg(-82759497, -2621599)) - test(lg(-1199275184, 1262327), lg(425605214, 249789222), lg(392156278, 6716943)) - test(lg(213473729, 11660532), lg(213473729, 11660532), lg(-547058106, 894811834)) - test(lg(-1550227391, 2847368), lg(-1550227391, 2847368), lg(-1996700003, 689370771)) - test(lg(-1014778289, -3747071), lg(-144234222, -54239417), lg(-1102770075, -7213193)) - test(lg(524484467, 15124083), lg(524484467, 15124083), lg(-1101379967, -39968226)) - test(lg(-919997306, 2085072), lg(314758022, 5390195), lg(-1234755328, -3305123)) - test(lg(580679232, -10426812), lg(580679232, -10426812), lg(-1964013803, -1738507605)) - test(lg(225658926, -4189255), lg(1670083752, -254253193), lg(722212413, -125031969)) - test(lg(-495749254, -1833207), lg(-1744001445, -5443198), lg(1248252191, 3609991)) - test(lg(-1481543825, 608612), lg(-1786439869, 137339199), lg(1821158508, 2909161)) - test(lg(1026706952, -6267613), lg(1273422584, -284542935), lg(1626032463, -17392208)) - test(lg(-855876173, -4928311), lg(-513801887, -32580141), lg(-342074286, 27651829)) - test(lg(-1027906958, 55543678), lg(-1027906958, 55543678), lg(-1936394792, 928937151)) - test(lg(-1793811005, -17787029), lg(251585986, -50474191), lg(-2045396991, 32687162)) - test(lg(-356034186, -2235041), lg(66679938, -917589429), lg(2124767660, -3454168)) - test(lg(-924611099, -76507846), lg(-599564184, -209788131), lg(-325046915, 133280284)) - test(lg(838338995, -12983151), lg(838338995, -12983151), lg(-842402530, 19411056)) - test(lg(747658762, 18528439), lg(1444498155, 520850879), lg(851271837, 23920116)) - test(lg(-2028924578, -3124146), lg(2096765386, -117024114), lg(-1726450785, -5694999)) - test(lg(2056903464, -4954201), lg(-425905039, -180148939), lg(-1397064581, -15926795)) - test(lg(-2055992988, 596420), lg(-920215872, 219325473), lg(1357686103, 54682263)) - test(lg(1279110660, -10784541), lg(1279110660, -10784541), lg(278869448, 758126792)) + test(883059337460449097L, 883059337460449097L, -1550174044384773417L) + test(-9543412627974774L, -1596905473229600544L, -48101880624291690L) + test(-10486616343740193L, -139303458961510665L, -11710622056160952L) + test(81149513920264088L, 981926948276712668L, 300259144785482860L) + test(9009882275485474L, 9009882275485474L, 79923769055871012L) + test(-19932235341099321L, -506314354450506803L, 37414009162262114L) + test(-12129109812415433L, -12129109812415433L, -2837741386438975528L) + test(270784395103096803L, 270784395103096803L, 1612528131811460834L) + test(83859782188007132L, 83859782188007132L, 3644002888077209617L) + test(1746519510828363190L, 1746519510828363190L, 2111669249919511875L) + test(860799297181306610L, 860799297181306610L, -1412005147950594882L) + test(234265100281002846L, 2191676294256233782L, 489352798493807734L) + test(-13539569498274996L, -4452079248766003228L, 23735506306244536L) + test(91370955325389303L, 6249777914395455887L, 118430903059039742L) + test(-16411784352803327L, -16411784352803327L, -20385458013414654L) + test(-5887325254449551L, -22305484444692601L, -16418159190243050L) + test(22270342034497702L, 22270342034497702L, -66419375563091088L) + test(207601032925887705L, 666019080114845530L, 458418047188957825L) + test(47277983711388430L, 47277983711388430L, -2109772615529102760L) + test(-704328891862849825L, -704328891862849825L, 2276831382955067502L) + test(-385964656907145510L, -1943847791817249050L, 389470783727525885L) + test(-26138261192193017L, -369890880368811227L, 57292103196103035L) + test(27655717698414314L, 27655717698414314L, -88623210967528211L) + test(543130187522154L, 1012955677254479086L, -38938944117959882L) + test(-11415718626796457L, -6587569366595715394L, -25004386494178399L) + test(-487347157564673452L, -487347157564673452L, -3509587062480584104L) + test(-783760630633017209L, -783760630633017209L, -2558190581855276594L) + test(7102865471817366L, 851578697173205241L, -11259677756018505L) + test(5421656277549904L, 1072836539808888926L, 28849050906252406L) + test(50081603807435201L, 50081603807435201L, 3843187566851690054L) + test(12229355184416833L, 12229355184416833L, 2960824918561572509L) + test(-16093544120601009L, -232956518018373358L, -30980424842538907L) + test(64957442391474035L, 64957442391474035L, -171662220355549567L) + test(8955319424775302L, 23150711558820742L, -14195392134045440L) + test(-44782815960861120L, -44782815960861120L, -7466833304991332587L) + test(-17992712993945554L, -1092009147168492376L, -537008217087273411L) + test(-7873560312580230L, -23378354844686757L, 15504794532106527L) + test(2613971449376623L, 589867370672363331L, 12494753174957164L) + test(-26919191832277496L, -1222102598859431176L, -74698962939197105L) + test(-21166931130425933L, -139930636312903327L, 118763705182477394L) + test(238558283776615026L, 238558283776615026L, 3989754685942986200L) + test(-76394705346847293L, -216784999385471550L, 140390294038624257L) + test(-9599424061286026L, -3941016588643634046L, -14835536470122068L) + test(-328598693087048219L, -901033158038560664L, 572434464951512445L) + test(-55762208105690701L, -55762208105690701L, 83369854153389342L) + test(79579040298589706L, 2237037492842351339L, 102736116787798173L) + test(-13418102631886498L, -502614740376610358L, -24459831887236193L) + test(-21278129215907032L, -773733797545036687L, -68405060757193605L) + test(2561606633654628L, 941995737089482432L, 234858532613956951L) + test(-46319249618260476L, -46319249618260476L, 3256129778140263880L) } @Test def moduloByZero(): Unit = { diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/TryFinallyTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/TryFinallyTest.scala new file mode 100644 index 0000000000..57d6119aa4 --- /dev/null +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/TryFinallyTest.scala @@ -0,0 +1,272 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.testsuite.compiler + +import scala.collection.mutable + +import org.junit.Test +import org.junit.Assert._ + +// Much of the point of this test class is to test `return`s inside `try..finally`s +// scalastyle:off return + +class TryFinallyTest { + + /* Some of these tests are ported from the partest run/finally.scala. + * We have copies of them in our own test suite to more quickly identify + * any issues with our compilation scheme for try..finally. On JS it is + * straightforward, but it is a huge beast in Wasm. + */ + + type SideEffect = Any => Unit + + @noinline + def test(body: SideEffect => Unit)(expectedSideEffects: String*): Unit = { + val sideEffects = mutable.ListBuffer.empty[String] + + try { + body(x => sideEffects += ("" + x)) + } catch { + case e: Throwable => + sideEffects += ("CAUGHT: " + e) + } + + if (!sideEffects.sameElements(expectedSideEffects)) { + // Custom message for easier debugging + fail( + "Expected side effects:" + + expectedSideEffects.mkString("\n* ", "\n* ", "\n") + + "but got:" + + sideEffects.mkString("\n* ", "\n* ", "\n")) + } + } + + // test that finally is not covered by any exception handlers. + @Test + def throwCatchFinally(): Unit = { + test { println => + def bar(): Unit = { + try { + println("hi") + } catch { + case e: Throwable => println("SHOULD NOT GET HERE") + } finally { + println("In Finally") + throw new RuntimeException("ouch") + } + } + + try { + bar() + } catch { + case e: Throwable => println(e) + } + } ( + "hi", + "In Finally", + "java.lang.RuntimeException: ouch" + ) + } + + // test that finally is not covered by any exception handlers. + // return in catch (finally is executed) + @Test + def retCatch(): Unit = { + test { println => + def retCatchInner(): Unit = { + try { + throw new Exception + } catch { + case e: Throwable => + println(e) + return + } finally { + println("in finally") + } + } + + retCatchInner() + } ( + "java.lang.Exception", + "in finally" + ) + } + + // throw in catch (finally is executed, exception propagated) + @Test + def throwCatch(): Unit = { + test { println => + try { + throw new Exception + } catch { + case e: Throwable => + println(e) + throw e + } finally { + println("in finally") + } + } ( + "java.lang.Exception", + "in finally", + "CAUGHT: java.lang.Exception" + ) + } + + // return inside body (finally is executed) + @Test + def retBody(): Unit = { + test { println => + def retBodyInner(): Unit = { + try { + return + } catch { + case e: Throwable => + println(e) + throw e + } finally println("in finally") + } + + retBodyInner() + } ( + "in finally" + ) + } + + // throw inside body (finally and catch are executed) + @Test + def throwBody(): Unit = { + test { println => + try { + throw new Exception + } catch { + case e: Throwable => + println(e) + } finally { + println("in finally") + } + } ( + "java.lang.Exception", + "in finally" + ) + } + + // return inside finally (each finally is executed once) + @Test + def retFinally(): Unit = { + test { println => + def retFinallyInner(): Unit = { + try { + try { + println("body") + } finally { + println("in finally 1") + return + } + } finally { + println("in finally 2") + } + } + + retFinallyInner() + } ( + "body", + "in finally 1", + "in finally 2" + ) + } + + // throw inside finally (finally is executed once, exception is propagated) + @Test + def throwFinally(): Unit = { + test { println => + try { + try { + println("body") + } finally { + println("in finally") + throw new Exception + } + } catch { + case e: Throwable => println(e) + } + } ( + "body", + "in finally", + "java.lang.Exception" + ) + } + + // nested finally blocks with return value + @Test + def nestedFinallyBlocks(): Unit = { + test { println => + def nestedFinallyBlocksInner(): Int = { + try { + try { + return 10 + } finally { + try { () } catch { case _: Throwable => () } + println("in finally 1") + } + } finally { + println("in finally 2") + } + } + + assertEquals(10, nestedFinallyBlocksInner()) + } ( + "in finally 1", + "in finally 2" + ) + } + + @Test + def nonDefaultableTryResultType_Issue5165(): Unit = { + test { println => + // after the optimizer, some has type Some! (a non-nullable reference type) + val some = try { + println("in try") + Some(1) + } finally { + println("in finally") + } + assertEquals(1, some.value) + } ( + "in try", + "in finally" + ) + } + + @Test + def nonDefaultableLabeledResultType_Issue5165(): Unit = { + test { println => + /* After the optimizer, the result type of the Labeled block that gets + * inlined is a Some! (a non-nullable reference type). + */ + @inline def nonDefaultableLabeledResultTypeInner(): Some[Int] = { + try { + println("in try") + return Some(1) + } finally { + println("in finally") + } + } + + val some = nonDefaultableLabeledResultTypeInner() + assertEquals(1, some.value) + } ( + "in try", + "in finally" + ) + } +} diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/DoubleTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/DoubleTest.scala index b9abbfdf3b..f94cd1ca69 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/DoubleTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/DoubleTest.scala @@ -513,6 +513,66 @@ class DoubleTest { assertEquals(0x8000000000000001L, f(-Double.MinPositiveValue)) // smallest neg subnormal form assertEquals(0x800fffffffffffffL, f(-2.225073858507201e-308)) // largest neg subnormal form assertEquals(0x800c5d44ae45cb60L, f(-1.719471609939382e-308)) // an arbitrary neg subnormal form + + // #5208 Try very hard to produce non-canonical NaN's. They should be canonicalized anyway. + @noinline def fromBits(bits: Long): Double = JDouble.longBitsToDouble(bits) + + assertEquals(0x7ff8000000000000L, f(fromBits(0x7ff000fabcd12345L))) + assertEquals(0x7ff8000000000000L, f(fromBits(0xfff000fabcd12345L))) + assertEquals(0x7ff8000000000000L, f(fromBits(0xfff8000000000000L))) + assertEquals(0x7ff8000000000000L, f(fromBits(0x7fffffffffffffffL))) + assertEquals(0x7ff8000000000000L, f(fromBits(0xffffffffffffffffL))) + assertEquals(0x7ff8000000000000L, f(fromBits(0x7ff0000000000001L))) + assertEquals(0x7ff8000000000000L, f(fromBits(0xfff0000000000001L))) + } + + @Test def doubleToRawLongBits(): Unit = { + import JDouble.{doubleToRawLongBits => f} + + // Specials + assertEquals(0x7ff0000000000000L, f(Double.PositiveInfinity)) + assertEquals(0xfff0000000000000L, f(Double.NegativeInfinity)) + assertEquals(0x0000000000000000L, f(0.0)) + assertEquals(0x8000000000000000L, f(-0.0)) + assertEquals(0x7ff8000000000000L, f(Double.NaN)) // canonical NaN is preserved as is + + // Normal forms + assertEquals(0x0010000000000000L, f(2.2250738585072014e-308)) // smallest pos normal form + assertEquals(0x7fefffffffffffffL, f(1.7976931348623157e308)) // largest pos normal form + assertEquals(0x4d124568bc6584caL, f(1.8790766677624813e63)) // an arbitrary pos normal form + assertEquals(0x8010000000000000L, f(-2.2250738585072014e-308)) // smallest neg normal form + assertEquals(0xffefffffffffffffL, f(-1.7976931348623157e308)) // largest neg normal form + assertEquals(0xcd124568bc6584caL, f(-1.8790766677624813e63)) // an arbitrary neg normal form + + // #2911 Normal form very close under a power of 2 + assertEquals(4845873199050653695L, f(9007199254740991.0)) + + // #4433 Other corner cases + assertEquals(9214364837600034815L, f(8.988465674311579e+307)) + assertEquals(549439154539200514L, f(5.915260930833876e-272)) + assertEquals(9007199254740992L, f(4.450147717014403e-308)) + + // Subnormal forms + assertEquals(0x0000000000000001L, f(Double.MinPositiveValue)) // smallest pos subnormal form + assertEquals(0x000fffffffffffffL, f(2.225073858507201e-308)) // largest pos subnormal form + assertEquals(0x000c5d44ae45cb60L, f(1.719471609939382e-308)) // an arbitrary pos subnormal form + assertEquals(0x8000000000000001L, f(-Double.MinPositiveValue)) // smallest neg subnormal form + assertEquals(0x800fffffffffffffL, f(-2.225073858507201e-308)) // largest neg subnormal form + assertEquals(0x800c5d44ae45cb60L, f(-1.719471609939382e-308)) // an arbitrary neg subnormal form + + // #5208 Non-canonical NaNs can result in any NaN bit pattern + @noinline def fromBits(bits: Long): Double = JDouble.longBitsToDouble(bits) + + @noinline def isNaNBitPattern(bits: Long): Boolean = + (bits & ~Long.MinValue) > 0x7ff0000000000000L + + assertTrue(isNaNBitPattern(f(fromBits(0x7ff000fabcd12345L)))) + assertTrue(isNaNBitPattern(f(fromBits(0xfff000fabcd12345L)))) + assertTrue(isNaNBitPattern(f(fromBits(0xfff8000000000000L)))) + assertTrue(isNaNBitPattern(f(fromBits(0x7fffffffffffffffL)))) + assertTrue(isNaNBitPattern(f(fromBits(0xffffffffffffffffL)))) + assertTrue(isNaNBitPattern(f(fromBits(0x7ff0000000000001L)))) + assertTrue(isNaNBitPattern(f(fromBits(0xfff0000000000001L)))) } @Test def isFinite(): Unit = { @@ -551,6 +611,17 @@ class DoubleTest { test(Double.NaN, 2146959360) test(Double.PositiveInfinity, 2146435072) test(Double.NegativeInfinity, -1048576) + + // #5208 Try very hard to produce non-canonical NaN's. They should be canonicalized anyway. + @noinline def fromBits(bits: Long): Double = JDouble.longBitsToDouble(bits) + + test(fromBits(0x7ff000fabcd12345L), 2146959360) + test(fromBits(0xfff000fabcd12345L), 2146959360) + test(fromBits(0xfff8000000000000L), 2146959360) + test(fromBits(0x7fffffffffffffffL), 2146959360) + test(fromBits(0xffffffffffffffffL), 2146959360) + test(fromBits(0x7ff0000000000001L), 2146959360) + test(fromBits(0xfff0000000000001L), 2146959360) } // The following tests are only to make sure that things link diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/FloatTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/FloatTest.scala index 37bcc4838a..29e0f85a2c 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/FloatTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/FloatTest.scala @@ -57,6 +57,17 @@ class FloatTest { test(Float.NaN, 2146959360) test(Float.PositiveInfinity, 2146435072) test(Float.NegativeInfinity, -1048576) + + // #5208 Try very hard to produce non-canonical NaN's. They should be canonicalized anyway. + @noinline def fromBits(bits: Int): Float = JFloat.intBitsToFloat(bits) + + test(fromBits(0x7fc12345), 2146959360) + test(fromBits(0xffc12345), 2146959360) + test(fromBits(0xffc00000), 2146959360) + test(fromBits(0x7fffffff), 2146959360) + test(fromBits(0xffffffff), 2146959360) + test(fromBits(0x7f800001), 2146959360) + test(fromBits(0xff800001), 2146959360) } } @@ -510,6 +521,58 @@ class FloatTest { assertEquals(0x80000001, f(-Float.MinPositiveValue)) // smallest neg subnormal form assertEquals(0x807fffff, f(-1.1754942e-38f)) // largest neg subnormal form assertEquals(0x807c5d44, f(-1.1421059e-38f)) // an arbitrary neg subnormal form + + // #5208 Try very hard to produce non-canonical NaN's. They should be canonicalized anyway. + @noinline def fromBits(bits: Int): Float = JFloat.intBitsToFloat(bits) + + assertEquals(0x7fc00000, f(fromBits(0x7fc12345))) + assertEquals(0x7fc00000, f(fromBits(0xffc12345))) + assertEquals(0x7fc00000, f(fromBits(0xffc00000))) + assertEquals(0x7fc00000, f(fromBits(0x7fffffff))) + assertEquals(0x7fc00000, f(fromBits(0xffffffff))) + assertEquals(0x7fc00000, f(fromBits(0x7f800001))) + assertEquals(0x7fc00000, f(fromBits(0xff800001))) + } + + @Test def floatToRawIntBits(): Unit = { + import JFloat.{floatToRawIntBits => f} + + // Specials + assertEquals(0x7f800000, f(Float.PositiveInfinity)) + assertEquals(0xff800000, f(Float.NegativeInfinity)) + assertEquals(0x00000000, f(0.0f)) + assertEquals(0x80000000, f(-0.0f)) + assertEquals(0x7fc00000, f(Float.NaN)) // canonical NaN is preserved as is + + // Normal forms + assertEquals(0x00800000, f(1.17549435e-38f)) // smallest pos normal form + assertEquals(0x7f7fffff, f(3.4028234e38f)) // largest pos normal form + assertEquals(0x4d124568, f(1.53376384e8f)) // an arbitrary pos normal form + assertEquals(0x80800000, f(-1.17549435e-38f)) // smallest neg normal form + assertEquals(0xff7fffff, f(-3.4028234e38f)) // largest neg normal form + assertEquals(0xcd124568, f(-1.53376384e8f)) // an arbitrary neg normal form + + // Subnormal forms + assertEquals(0x00000001, f(Float.MinPositiveValue)) // smallest pos subnormal form + assertEquals(0x007fffff, f(1.1754942e-38f)) // largest pos subnormal form + assertEquals(0x007c5d44, f(1.1421059e-38f)) // an arbitrary pos subnormal form + assertEquals(0x80000001, f(-Float.MinPositiveValue)) // smallest neg subnormal form + assertEquals(0x807fffff, f(-1.1754942e-38f)) // largest neg subnormal form + assertEquals(0x807c5d44, f(-1.1421059e-38f)) // an arbitrary neg subnormal form + + // #5208 Non-canonical NaNs can result in any NaN bit pattern + @noinline def fromBits(bits: Int): Float = JFloat.intBitsToFloat(bits) + + @noinline def isNaNBitPattern(bits: Int): Boolean = + (bits & ~Int.MinValue) > 0x7f800000 + + assertTrue(isNaNBitPattern(f(fromBits(0x7fc12345)))) + assertTrue(isNaNBitPattern(f(fromBits(0xffc12345)))) + assertTrue(isNaNBitPattern(f(fromBits(0xffc00000)))) + assertTrue(isNaNBitPattern(f(fromBits(0x7fffffff)))) + assertTrue(isNaNBitPattern(f(fromBits(0xffffffff)))) + assertTrue(isNaNBitPattern(f(fromBits(0x7f800001)))) + assertTrue(isNaNBitPattern(f(fromBits(0xff800001)))) } @Test def isFinite(): Unit = { diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/IntegerTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/IntegerTest.scala index 95399527a7..6e3133bad2 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/IntegerTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/IntegerTest.scala @@ -572,8 +572,8 @@ class IntegerTest { test("abc") test("5a") - test("4294967296") - test("ffFFffFF", 20) + test("4294967296") // the last addition of `digit` (the '6') overflows + test("ffFFffFF", 20) // the last multiplication by `radix` overflows test("99", 8) test("-") test("") @@ -725,7 +725,7 @@ class IntegerTest { test("abc") test("5a") test("99", 8) - test("4294967296") + test("4294967296") // the last addition of `digit` (the '6') overflows test("-30000") test("+") test("-") diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/LongTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/LongTest.scala index 8566a378a2..df572ef989 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/LongTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/LongTest.scala @@ -27,6 +27,8 @@ class LongTest { final val MinRadix = Character.MIN_RADIX final val MaxRadix = Character.MAX_RADIX + @noinline def hideFromOptimizer(x: Long): Long = x + @Test def reverseBytes(): Unit = { assertEquals(0x14ff01d49c68abf5L, JLong.reverseBytes(0xf5ab689cd401ff14L)) assertEquals(0x780176af73b18fc7L, JLong.reverseBytes(0xc78fb173af760178L)) @@ -272,6 +274,33 @@ class LongTest { assertEquals("89000000005", JLong.toString(89000000005L)) assertEquals("-9223372036854775808", JLong.toString(JLong.MIN_VALUE)) assertEquals("9223372036854775807", JLong.toString(JLong.MAX_VALUE)) + + // Corner cases of the approximation inside RuntimeLong.toUnsignedString + + // Approximated quotient is too high + assertEquals("2777572447999999934", JLong.toString(0x268beb6cdcf3bfbeL)) + assertEquals("3611603422999999979", JLong.toString(0x321efe2d997ff5ebL)) + assertEquals("7742984029999999701", JLong.toString(0x6b749af381ac2ad5L)) + assertEquals("2161767614999999954", JLong.toString(0x1e0024313b04b5d2L)) + assertEquals("5388513109999999953", JLong.toString(0x4ac7d81fbd15dbd1L)) + assertEquals("3713052774999999769", JLong.toString(0x338769d386274519L)) + assertEquals("-5647508785999999800", JLong.toString(0xb1a004ae50928cc8L)) + assertEquals("-1406561754999999938", JLong.toString(0xec7ae3893e93323eL)) + assertEquals("-8621287367999999564", JLong.toString(0x885b08d0fbcc31b4L)) + assertEquals("-8876380314999999920", JLong.toString(0x84d0c321f127b250L)) + assertEquals("-5002322935999999598", JLong.toString(0xba942dcb0bee5192L)) + assertEquals("-4971399139999999950", JLong.toString(0xbb020ad25f9e1832L)) + assertEquals("-8515854999999999733", JLong.toString(0x89d19aff1644110bL)) + assertEquals("-4806014223999999712", JLong.toString(0xbd4d9b86d1016120L)) + assertEquals("-9133328502999999878", JLong.toString(0x813fe61df1bc1a7aL)) + assertEquals("-7816299703999999849", JLong.toString(0x9386ecd4ed16d097L)) + assertEquals("-7259227631999999909", JLong.toString(0x9b420aee02f0a05bL)) + assertEquals("-2526704305999999860", JLong.toString(0xdcef57d21c6b8c8cL)) + assertEquals("-1100666257999999982", JLong.toString(0xf0b9a5deb3a6cc12L)) + + // Approximated quotient is too low + assertEquals("7346875325000000000", JLong.toString(0x65f5582ec3b52200L)) + assertEquals("-7993685585000000000", JLong.toString(0x9110b95013ea1600L)) } @Test def toStringRadix(): Unit = { @@ -659,8 +688,12 @@ class LongTest { } @Test def divideUnsigned(): Unit = { - def test(dividend: Long, divisor: Long, result: Long): Unit = - assertEquals(result, JLong.divideUnsigned(dividend, divisor)) + @inline def test(x: Long, y: Long, result: Long): Unit = { + assertEquals(result, JLong.divideUnsigned(x, y)) + assertEquals(result, JLong.divideUnsigned(hideFromOptimizer(x), y)) + assertEquals(result, JLong.divideUnsigned(x, hideFromOptimizer(y))) + assertEquals(result, JLong.divideUnsigned(hideFromOptimizer(x), hideFromOptimizer(y))) + } test(-9223372034182170740L, 53886L, 171164533265177L) test(-9223372036854775807L, 1L, -9223372036854775807L) @@ -721,8 +754,12 @@ class LongTest { } @Test def remainderUnsigned(): Unit = { - def test(dividend: Long, divisor: Long, result: Long): Unit = - assertEquals(result, JLong.remainderUnsigned(dividend, divisor)) + @inline def test(x: Long, y: Long, result: Long): Unit = { + assertEquals(result, JLong.remainderUnsigned(x, y)) + assertEquals(result, JLong.remainderUnsigned(hideFromOptimizer(x), y)) + assertEquals(result, JLong.remainderUnsigned(x, hideFromOptimizer(y))) + assertEquals(result, JLong.remainderUnsigned(hideFromOptimizer(x), hideFromOptimizer(y))) + } test(97062081516L, 772L, 668L) test(-9223372036854775472L, 49L, 43L) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/MathTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/MathTest.scala index 86b26e4dfb..2719abd2fc 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/MathTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/lang/MathTest.scala @@ -18,51 +18,57 @@ import org.junit.Assume._ import java.lang.Math +// Imported under different names for historical reasons +import org.scalajs.testsuite.utils.AssertExtensions.{assertExactEquals => assertSameDouble} +import org.scalajs.testsuite.utils.AssertExtensions.{assertExactEquals => assertSameFloat} + import org.scalajs.testsuite.utils.AssertThrows.assertThrows import org.scalajs.testsuite.utils.Platform._ class MathTest { - /** Like `assertEquals` with `delta = 0.0`, but positive and negative zeros - * compare not equal. - */ - private def assertSameDouble(expected: Double, actual: Double): Unit = - assertTrue(s"expected: $expected but was: $actual", expected.equals(actual)) - - /** Like `assertEquals` with `delta = 0.0`, but positive and negative zeros - * compare not equal. - */ - private def assertSameDouble(msg: String, expected: Double, actual: Double): Unit = - assertTrue(s"$msg; expected: $expected but was: $actual", expected.equals(actual)) - - /** Like `assertEquals` with `delta = 0.0f`, but positive and negative zeros - * compare not equal. - */ - private def assertSameFloat(expected: Float, actual: Float): Unit = - assertTrue(s"expected: $expected but was: $actual", expected.equals(actual)) - - /** Like `assertEquals` with `delta = 0.0f`, but positive and negative zeros - * compare not equal. - */ - private def assertSameFloat(msg: String, expected: Float, actual: Float): Unit = - assertTrue(s"$msg; expected: $expected but was: $actual", expected.equals(actual)) - - @Test def abs(): Unit = { - assertSameDouble(0, Math.abs(0)) - assertSameDouble(0.0, Math.abs(-0.0)) - assertEquals(42, Math.abs(42)) - assertEquals(42, Math.abs(-42)) - assertTrue(Math.abs(0.0).equals(0.0)) - assertTrue(Math.abs(-0.0).equals(0.0)) - assertEquals(42.0, Math.abs(42.0), 0.0) - assertEquals(42.0, Math.abs(-42.0), 0.0) - assertEquals(Double.PositiveInfinity, Math.abs(Double.PositiveInfinity), 0.0) - assertEquals(Double.PositiveInfinity, Math.abs(Double.NegativeInfinity), 0.0) - assertTrue(Math.abs(Double.NaN).isNaN) + @Test def absInt(): Unit = { + assertEquals(0, Math.abs(0)) + assertEquals(156, Math.abs(156)) + assertEquals(156, Math.abs(-156)) + assertEquals(49841354, Math.abs(49841354)) + assertEquals(98433, Math.abs(-98433)) + assertEquals(Int.MaxValue, Math.abs(Int.MaxValue)) + assertEquals(Int.MaxValue, Math.abs(Int.MinValue + 1)) + assertEquals(Int.MinValue, Math.abs(Int.MinValue)) + } + + @Test def absLong(): Unit = { + assertEquals(0L, Math.abs(0L)) + assertEquals(156L, Math.abs(156L)) + assertEquals(156L, Math.abs(-156L)) + assertEquals(498413546584635135L, Math.abs(498413546584635135L)) + assertEquals(984335433487676L, Math.abs(-984335433487676L)) assertEquals(Long.MaxValue, Math.abs(Long.MaxValue)) + assertEquals(Long.MaxValue, Math.abs(Long.MinValue + 1L)) assertEquals(Long.MinValue, Math.abs(Long.MinValue)) } + @Test def absFloat(): Unit = { + assertSameFloat(0.0f, Math.abs(0.0f)) + assertSameFloat(0.0f, Math.abs(-0.0f)) + assertSameFloat(42.156f, Math.abs(42.156f)) + assertSameFloat(42.654f, Math.abs(-42.654f)) + assertSameFloat(Float.PositiveInfinity, Math.abs(Float.PositiveInfinity)) + assertSameFloat(Float.PositiveInfinity, Math.abs(Float.NegativeInfinity)) + assertSameFloat(Float.NaN, Math.abs(Float.NaN)) + } + + @Test def absDouble(): Unit = { + assertSameDouble(0.0, Math.abs(0.0)) + assertSameDouble(0.0, Math.abs(-0.0)) + assertSameDouble(42.156, Math.abs(42.156)) + assertSameDouble(42.654, Math.abs(-42.654)) + assertSameDouble(Double.PositiveInfinity, Math.abs(Double.PositiveInfinity)) + assertSameDouble(Double.PositiveInfinity, Math.abs(Double.NegativeInfinity)) + assertSameDouble(Double.NaN, Math.abs(Double.NaN)) + } + @Test def max(): Unit = { assertEquals(0, Math.max(0, 0)) assertEquals(2, Math.max(0, 2)) diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ArrayListTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ArrayListTest.scala index 9b9812f93c..400da32882 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ArrayListTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ArrayListTest.scala @@ -13,6 +13,11 @@ package org.scalajs.testsuite.javalib.util import org.junit.Test +import org.junit.Assert._ +import org.junit.Assume._ + +import org.scalajs.testsuite.utils.AssertThrows.assertThrows +import org.scalajs.testsuite.utils.Platform import java.{util => ju} @@ -20,7 +25,7 @@ import scala.reflect.ClassTag class ArrayListTest extends AbstractListTest { - override def factory: AbstractListFactory = new ArrayListFactory + override def factory: ArrayListFactory = new ArrayListFactory @Test def ensureCapacity(): Unit = { // note that these methods become no ops in js @@ -29,6 +34,86 @@ class ArrayListTest extends AbstractListTest { al.ensureCapacity(34) al.trimToSize() } + + @Test def constructorInitialCapacity(): Unit = { + val al1 = new ju.ArrayList(0) + assertTrue(al1.size() == 0) + assertTrue(al1.isEmpty()) + + val al2 = new ju.ArrayList(2) + assertTrue(al2.size() == 0) + assertTrue(al2.isEmpty()) + + assertThrows(classOf[IllegalArgumentException], new ju.ArrayList(-1)) + } + + @Test def constructorNullThrowsNullPointerException(): Unit = { + assumeTrue("assumed compliant NPEs", Platform.hasCompliantNullPointers) + assertThrows(classOf[NullPointerException], new ju.ArrayList(null)) + } + + @Test def testClone(): Unit = { + val al1 = factory.fromElements[Int](1, 2) + val al2 = al1.clone().asInstanceOf[ju.ArrayList[Int]] + al1.add(100) + al2.add(200) + assertTrue(Array[Int](1, 2, 100).sameElements(al1.toArray())) + assertTrue(Array[Int](1, 2, 200).sameElements(al2.toArray())) + } + + @Test def removeRangeFromIdenticalIndices(): Unit = { + val al = new ArrayListRangeRemovable[Int]( + TrivialImmutableCollection(-175, 24, 7, 44)) + val expected = Array[Int](-175, 24, 7, 44) + al.removeRangeList(0, 0) + assertTrue(al.toArray().sameElements(expected)) + al.removeRangeList(1, 1) + assertTrue(al.toArray().sameElements(expected)) + al.removeRangeList(al.size, al.size) // no op + assertTrue(al.toArray().sameElements(expected)) + } + + @Test def removeRangeFromToInvalidIndices(): Unit = { + val al = new ArrayListRangeRemovable[Int]( + TrivialImmutableCollection(175, -24, -7, -44)) + + assertThrows( + classOf[java.lang.IndexOutOfBoundsException], + al.removeRangeList(-1, 2) + ) // fromIndex < 0 + assertThrows( + classOf[java.lang.IndexOutOfBoundsException], + al.removeRangeList(0, al.size + 1) + ) // toIndex > size + assertThrows( + classOf[java.lang.IndexOutOfBoundsException], + al.removeRangeList(2, -1) + ) // toIndex < fromIndex + } + + @Test def removeRangeFromToFirstTwoElements(): Unit = { + val al = new ArrayListRangeRemovable[Int]( + TrivialImmutableCollection(284, -27, 995, 500, 267, 904)) + val expected = Array[Int](995, 500, 267, 904) + al.removeRangeList(0, 2) + assertTrue(al.toArray().sameElements(expected)) + } + + @Test def removeRangeFromToTwoElementsFromMiddle(): Unit = { + val al = new ArrayListRangeRemovable[Int]( + TrivialImmutableCollection(7, 9, -1, 20)) + val expected = Array[Int](7, 20) + al.removeRangeList(1, 3) + assertTrue(al.toArray().sameElements(expected)) + } + + @Test def removeRangeFromToLastTwoElementsAtTail(): Unit = { + val al = new ArrayListRangeRemovable[Int]( + TrivialImmutableCollection(50, 72, 650, 12, 7, 28, 3)) + val expected = Array[Int](50, 72, 650, 12, 7) + al.removeRangeList(al.size - 2, al.size) + assertTrue(al.toArray().sameElements(expected)) + } } class ArrayListFactory extends AbstractListFactory { @@ -37,4 +122,13 @@ class ArrayListFactory extends AbstractListFactory { override def empty[E: ClassTag]: ju.ArrayList[E] = new ju.ArrayList[E] + + override def fromElements[E: ClassTag](coll: E*): ju.ArrayList[E] = + new ju.ArrayList[E](TrivialImmutableCollection(coll: _*)) +} + +class ArrayListRangeRemovable[E](c: ju.Collection[_ <: E]) extends ju.ArrayList[E](c) { + def removeRangeList(fromIndex: Int, toIndex: Int): Unit = { + removeRange(fromIndex, toIndex) + } } diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/CollectionTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/CollectionTest.scala index 787d88a4c3..c73e6acccd 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/CollectionTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/CollectionTest.scala @@ -117,6 +117,16 @@ trait CollectionTest extends IterableTest { assertFalse(coll.contains(TestObj(200))) } + @Test def isEmpty(): Unit = { + val coll = factory.empty[Int] + assertTrue(coll.size() == 0) + assertTrue(coll.isEmpty()) + + val nonEmpty = factory.fromElements[Int](1) + assertTrue(nonEmpty.size() == 1) + assertFalse(nonEmpty.isEmpty()) + } + @Test def removeString(): Unit = { val coll = factory.empty[String] diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ListTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ListTest.scala index 8835696b00..98773fef7a 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ListTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/ListTest.scala @@ -96,6 +96,19 @@ trait ListTest extends CollectionTest with CollectionsTestBase { assertThrows(classOf[IndexOutOfBoundsException], lst.get(lst.size)) } + @Test def addAllIndexBounds(): Unit = { + val al = factory.fromElements[String]("one", "two", "three") + + val coll = factory.fromElements[String]("foo") + assertThrows(classOf[IndexOutOfBoundsException], al.addAll(-1, coll)) + assertThrows(classOf[IndexOutOfBoundsException], al.addAll(al.size + 1, coll)) + + assertThrows(classOf[IndexOutOfBoundsException], + al.addAll(-1, TrivialImmutableCollection("foo"))) + assertThrows(classOf[IndexOutOfBoundsException], + al.addAll(al.size + 1, TrivialImmutableCollection("foo"))) + } + @Test def removeStringRemoveIndex(): Unit = { val lst = factory.empty[String] diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/regex/RegexEngineTest.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/regex/RegexEngineTest.scala index 4cc2a720b4..d174bda341 100644 --- a/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/regex/RegexEngineTest.scala +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/regex/RegexEngineTest.scala @@ -54,11 +54,12 @@ class RegexEngineTest { private def debugEscape(pattern: String): String = { pattern.flatMap { - case '\t' => "`t" - case '\n' => "`n" - case '\r' => "`r" - case c if c < ' ' => "`x%02X".format(c.toInt) - case c => c.toString() + case '\t' => "`t" + case '\n' => "`n" + case '\r' => "`r" + case c if c < 0x10 => "`x0" + c.toInt.toHexString + case c if c < ' ' => "`x" + c.toInt.toHexString + case c => c.toString() } } @@ -1976,8 +1977,52 @@ class RegexEngineTest { val s = compile("s", CaseInsensitive | UnicodeCase) assertMatches(s, "s") assertMatches(s, "S") - assertMatches(s, "\u017F") // ſ LATIN SMALL LETTER LONG S + assertMatches(s, "\u017F") // ſ LATIN SMALL LETTER LONG S; 017F folds to 's' assertNotMatches(s, "t") + + val ranges = compile("[g-l\uFB00\u0175-\u0182\u0540-\u0550\u1F68-\u1F8E\u1FAA-\u1FAF\u2126]", + CaseInsensitive | UnicodeCase) + // g-l + assertMatches(ranges, "H") + assertMatches(ranges, "\u212A") // K KELVIN SIGN, folds to 'k' + // FB00 + assertMatches(ranges, "\uFB00") // ff LATIN SMALL LIGATURE FF + // 0175-0182 (contains 017F which folds to 's') + if (!executingInJVM) { + // https://bugs.openjdk.org/browse/JDK-8360459 + assertMatches(ranges, "s") + assertMatches(ranges, "S") + } + assertMatches(ranges, "\u017F") + assertMatches(ranges, "\u0180") // in range; does not participate in case folding + // 0540-0550 + assertMatches(ranges, "\u0547") // in range + assertMatches(ranges, "\u0577") // 0547 folds to 0577 + // 1F68-1F8E + assertMatches(ranges, "\u1F65") // 1F6D folds to 1F65 + assertMatches(ranges, "\u1F6D") // in range + assertMatches(ranges, "\u1F82") // 1F8A folds to 1F82, and 1F82 is also in range + // 1FAA-1FAF + assertMatches(ranges, "\u1FA4") // 1FAC folds to 1FA4 only in simple case folding + // 2126 + assertMatches(ranges, "\u2126") // in the set + assertMatches(ranges, "\u03C9") // 2126 folds to 03C9 + assertMatches(ranges, "\u03A9") // 03A9 also folds to 03C9 + // No matches + assertNotMatches(ranges, "t") + assertNotMatches(ranges, "ff") // ff FB00 would only match with full case folding + + // Demonstrate that the JVM recognizes 017F as folding to 's' if the range is ASCII + val rangeWithASCII_S = compile("[P-U]", CaseInsensitive | UnicodeCase) + assertMatches(rangeWithASCII_S, "s") + assertMatches(rangeWithASCII_S, "S") + assertMatches(rangeWithASCII_S, "\u017F") + + // Demonstrate that the JVM recognizes 017F as folding to 's' if it is not a range + val nonRangeWith_017F = compile("[\u017F\u0184]", CaseInsensitive | UnicodeCase) + assertMatches(nonRangeWith_017F, "s") + assertMatches(nonRangeWith_017F, "S") + assertMatches(nonRangeWith_017F, "\u017F") } @Test def wordBoundary(): Unit = { diff --git a/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertExtensions.scala b/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertExtensions.scala new file mode 100644 index 0000000000..3d959f7b6a --- /dev/null +++ b/test-suite/shared/src/test/scala/org/scalajs/testsuite/utils/AssertExtensions.scala @@ -0,0 +1,55 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.testsuite.utils + +import org.junit.Assert._ + +object AssertExtensions { + + /** Asserts that two Double values are exactly equal. + * + * Positive and negative zeros compare as *not* equal. `NaN` compares + * *equal* to itself. + */ + @noinline + def assertExactEquals(expected: Double, actual: Double): Unit = + assertTrue(s"expected: $expected but was: $actual", expected.equals(actual)) + + /** Asserts that two Double values are exactly equal. + * + * Positive and negative zeros compare as *not* equal. `NaN` compares + * *equal* to itself. + */ + @noinline + def assertExactEquals(msg: String, expected: Double, actual: Double): Unit = + assertTrue(s"$msg; expected: $expected but was: $actual", expected.equals(actual)) + + /** Asserts that two Float values are exactly equal. + * + * Positive and negative zeros compare as *not* equal. `NaN` compares + * *equal* to itself. + */ + @noinline + def assertExactEquals(expected: Float, actual: Float): Unit = + assertTrue(s"expected: $expected but was: $actual", expected.equals(actual)) + + /** Asserts that two Float values are exactly equal. + * + * Positive and negative zeros compare as *not* equal. `NaN` compares + * *equal* to itself. + */ + @noinline + def assertExactEquals(msg: String, expected: Float, actual: Float): Unit = + assertTrue(s"$msg; expected: $expected but was: $actual", expected.equals(actual)) + +}