Skip to content

Commit

Permalink
Merge pull request #9261 from dwijnand/2.13/dont-box-Double-NaN
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand committed Feb 1, 2021
2 parents 5b1d4f2 + 6fb7b5c commit 0ab9086
Show file tree
Hide file tree
Showing 23 changed files with 1,671 additions and 16 deletions.
82 changes: 82 additions & 0 deletions src/compiler/scala/tools/nsc/transform/CleanUp.scala
Expand Up @@ -620,6 +620,69 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
reducingTransformListApply(rest.elems.length) {
super.transform(localTyper.typedPos(tree.pos)(consed))
}

//methods on Double
//new Predef.doubleToDouble(x).isNaN() -> java.lang.Double.isNaN(x)
//new Predef.doubleToDouble(x).isInfinite() -> java.lang.Double.isInfinity(x)
//methods on Float
//new Predef.float2Float(x).isNaN() -> java.lang.Double.isNaN(x)
//new Predef.float2Float(x).isInfinite() -> java.lang.Double.isInfinity(x)

//methods on Number
//new Predef.<convert>(x).byteValue() -> x.toByte()
//new Predef.<convert>(x).shortValue() -> x.toShort()
//new Predef.<convert>(x).intValue() -> x.toInt()
//new Predef.<convert>(x).longValue() -> x.toLong()
//new Predef.<convert>(x).floatValue() -> x.toFloat()
//new Predef.<convert>(x).doubleValue() -> x.toDouble()
//
// for each of the conversions
// double2Double
// float2Float
// byte2Byte
// short2Short
// char2Character
// int2Integer
// long2Long
// boolean2Boolean
//
case Apply(Select(Apply(boxing @ Select(qual, _), params), methodName), Nil)
if currentRun.runDefinitions.PreDef_primitives2Primitives.contains(boxing.symbol) &&
params.size == 1 &&
allPrimitiveMethodsToRewrite.contains(methodName) &&
treeInfo.isExprSafeToInline(qual) =>
val newTree =
if (doubleAndFloatRedirectMethods.contains(methodName)) {
val cls =
if (boxing.symbol == currentRun.runDefinitions.Predef_double2Double)
definitions.BoxedDoubleClass
else definitions.BoxedFloatClass

val targetMethod = cls.companionModule.info.decl(doubleAndFloatRedirectMethods(methodName))
gen.mkMethodCall(targetMethod, params)
} else {
gen.mkMethodCall(Select(params.head, javaNumberConversions(methodName)), Nil)
}
super.transform(localTyper.typedPos(tree.pos)(newTree))

//(x:Int).hashCode is transformed to scala.Int.box(x).hashCode()
//(x:Int).toString is transformed to scala.Int.box(x).toString()
//
//rewrite
// scala.Int.box(x).hashCode() -> java.lang.Integer.hashCode(x)
// scala.Int.box(x).toString() -> java.lang.Integer.toString(x)
// similarly for all primitive types
case Apply(Select(Apply(box @ Select(boxer, _), params), methodName), Nil)
if objectMethods.contains(methodName) &&
params.size == 1 &&
currentRun.runDefinitions.isBox(box.symbol) &&
treeInfo.isExprSafeToInline(boxer)
=>
val target = boxedClass(boxer.symbol.companion)
val targetMethod = target.companionModule.info.decl(methodName)
val newTree = gen.mkMethodCall(targetMethod, params)
super.transform(localTyper.typedPos(tree.pos)(newTree))

// Seq() ~> Nil (note: List() ~> Nil is rewritten in the Typer)
case Apply(appMeth @ Select(appQual, _), List(nil))
if nil.symbol == NilModule && currentRun.runDefinitions.isSeqApply(appMeth) =>
Expand All @@ -633,4 +696,23 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {

} // CleanUpTransformer


private val objectMethods = Map[Name, TermName](
nme.hashCode_ -> nme.hashCode_,
nme.toString_ -> nme.toString_
)
private val doubleAndFloatRedirectMethods = Map[Name, TermName](
nme.isNaN -> nme.isNaN,
nme.isInfinite -> nme.isInfinite
)
private val javaNumberConversions = Map[Name, TermName](
nme.byteValue -> nme.toByte,
nme.shortValue -> nme.toShort,
nme.intValue -> nme.toInt,
nme.longValue -> nme.toLong,
nme.floatValue -> nme.toFloat,
nme.doubleValue -> nme.toDouble
)
private val allPrimitiveMethodsToRewrite = doubleAndFloatRedirectMethods.keySet ++ javaNumberConversions.keySet

}
4 changes: 3 additions & 1 deletion src/library/scala/runtime/RichByte.scala
Expand Up @@ -13,7 +13,6 @@
package scala
package runtime


final class RichByte(val self: Byte) extends AnyVal with ScalaWholeNumberProxy[Byte] {
protected def num = scala.math.Numeric.ByteIsIntegral
protected def ord = scala.math.Ordering.Byte
Expand All @@ -27,6 +26,9 @@ final class RichByte(val self: Byte) extends AnyVal with ScalaWholeNumberProxy[B

override def isValidByte = true

// These method are all overridden and redefined to call out to scala.math to avoid 3 allocations:
// the primitive boxing, the value class boxing and instantiation of the Numeric num.
// We'd like to redefine signum and sign too but forwards binary compatibility doesn't allow us to.
override def abs: Byte = math.abs(self).toByte
override def max(that: Byte): Byte = math.max(self, that).toByte
override def min(that: Byte): Byte = math.min(self, that).toByte
Expand Down
6 changes: 3 additions & 3 deletions src/library/scala/runtime/RichChar.scala
Expand Up @@ -13,9 +13,6 @@
package scala
package runtime


import java.lang.Character

final class RichChar(val self: Char) extends AnyVal with IntegralProxy[Char] {
protected def num = scala.math.Numeric.CharIsIntegral
protected def ord = scala.math.Ordering.Char
Expand All @@ -29,6 +26,9 @@ final class RichChar(val self: Char) extends AnyVal with IntegralProxy[Char] {

override def isValidChar = true

// These method are all overridden and redefined to call out to scala.math to avoid 3 allocations:
// the primitive boxing, the value class boxing and instantiation of the Numeric num.
// We'd like to redefine signum and sign too but forwards binary compatibility doesn't allow us to.
override def abs: Char = self
override def max(that: Char): Char = math.max(self.toInt, that.toInt).toChar
override def min(that: Char): Char = math.min(self.toInt, that.toInt).toChar
Expand Down
6 changes: 5 additions & 1 deletion src/library/scala/runtime/RichDouble.scala
Expand Up @@ -42,10 +42,14 @@ final class RichDouble(val self: Double) extends AnyVal with FractionalProxy[Dou
def isPosInfinity: Boolean = Double.PositiveInfinity == self
def isNegInfinity: Boolean = Double.NegativeInfinity == self

// These method are all overridden and redefined to call out to scala.math to avoid 3 allocations:
// the primitive boxing, the value class boxing and instantiation of the Numeric num.
// We'd like to redefine sign too but forwards binary compatibility doesn't allow us to.
override def abs: Double = math.abs(self)
override def max(that: Double): Double = math.max(self, that)
override def min(that: Double): Double = math.min(self, that)
@deprecated("signum does not handle -0.0 or Double.NaN; use `sign` method instead", since = "2.13.0") override def signum: Int = num.signum(self)
@deprecated("signum does not handle -0.0 or Double.NaN; use `sign` method instead", since = "2.13.0")
override def signum: Int = math.signum(self).toInt

def round: Long = math.round(self)
def ceil: Double = math.ceil(self)
Expand Down
6 changes: 5 additions & 1 deletion src/library/scala/runtime/RichFloat.scala
Expand Up @@ -42,10 +42,14 @@ final class RichFloat(val self: Float) extends AnyVal with FractionalProxy[Float
def isPosInfinity: Boolean = Float.PositiveInfinity == self
def isNegInfinity: Boolean = Float.NegativeInfinity == self

// These method are all overridden and redefined to call out to scala.math to avoid 3 allocations:
// the primitive boxing, the value class boxing and instantiation of the Numeric num.
// We'd like to redefine sign too but forwards binary compatibility doesn't allow us to.
override def abs: Float = math.abs(self)
override def max(that: Float): Float = math.max(self, that)
override def min(that: Float): Float = math.min(self, that)
@deprecated("signum does not handle -0.0f or Float.NaN; use `sign` method instead", since = "2.13.0") override def signum: Int = num.signum(self)
@deprecated("signum does not handle -0.0f or Float.NaN; use `sign` method instead", since = "2.13.0")
override def signum: Int = math.signum(self).toInt

def round: Int = math.round(self)
def ceil: Float = math.ceil(self.toDouble).toFloat
Expand Down
3 changes: 3 additions & 0 deletions src/library/scala/runtime/RichInt.scala
Expand Up @@ -36,6 +36,9 @@ final class RichInt(val self: Int) extends AnyVal with ScalaNumberProxy[Int] wit
override def isValidInt = true
def isValidLong = true

// These method are all overridden and redefined to call out to scala.math to avoid 3 allocations:
// the primitive boxing, the value class boxing and instantiation of the Numeric num.
// We'd like to redefine signum and sign too but forwards binary compatibility doesn't allow us to.
override def abs: Int = math.abs(self)
override def max(that: Int): Int = math.max(self, that)
override def min(that: Int): Int = math.min(self, that)
Expand Down
3 changes: 3 additions & 0 deletions src/library/scala/runtime/RichLong.scala
Expand Up @@ -32,6 +32,9 @@ final class RichLong(val self: Long) extends AnyVal with IntegralProxy[Long] {
// override def isValidFloat = self.toFloat.toLong == self && self != Long.MaxValue
// override def isValidDouble = self.toDouble.toLong == self && self != Long.MaxValue

// These method are all overridden and redefined to call out to scala.math to avoid 3 allocations:
// the primitive boxing, the value class boxing and instantiation of the Numeric num.
// We'd like to redefine signum and sign too but forwards binary compatibility doesn't allow us to.
override def abs: Long = math.abs(self)
override def max(that: Long): Long = math.max(self, that)
override def min(that: Long): Long = math.min(self, that)
Expand Down
4 changes: 3 additions & 1 deletion src/library/scala/runtime/RichShort.scala
Expand Up @@ -13,7 +13,6 @@
package scala
package runtime


final class RichShort(val self: Short) extends AnyVal with ScalaWholeNumberProxy[Short] {
protected def num = scala.math.Numeric.ShortIsIntegral
protected def ord = scala.math.Ordering.Short
Expand All @@ -27,6 +26,9 @@ final class RichShort(val self: Short) extends AnyVal with ScalaWholeNumberProxy

override def isValidShort = true

// These method are all overridden and redefined to call out to scala.math to avoid 3 allocations:
// the primitive boxing, the value class boxing and instantiation of the Numeric num.
// We'd like to redefine signum and sign too but forwards binary compatibility doesn't allow us to.
override def abs: Short = math.abs(self.toInt).toShort
override def max(that: Short): Short = math.max(self.toInt, that.toInt).toShort
override def min(that: Short): Short = math.min(self.toInt, that.toInt).toShort
Expand Down
14 changes: 14 additions & 0 deletions src/reflect/scala/reflect/internal/Definitions.scala
Expand Up @@ -1758,6 +1758,20 @@ trait Definitions extends api.StandardDefinitions {
lazy val SubType_refl = getMemberMethod(SubTypeModule, nme.refl)

lazy val Predef_classOf = getMemberMethod(PredefModule, nme.classOf)

lazy val Predef_double2Double = getMemberMethod(PredefModule, nme.double2Double)
lazy val Predef_float2Float = getMemberMethod(PredefModule, nme.float2Float)
lazy val Predef_byte2Byte = getMemberMethod(PredefModule, nme.byte2Byte)
lazy val Predef_short2Short = getMemberMethod(PredefModule, nme.short2Short)
lazy val Predef_char2Character = getMemberMethod(PredefModule, nme.char2Character)
lazy val Predef_int2Integer = getMemberMethod(PredefModule, nme.int2Integer)
lazy val Predef_long2Long = getMemberMethod(PredefModule, nme.long2Long)
lazy val Predef_boolean2Boolean = getMemberMethod(PredefModule, nme.boolean2Boolean)

lazy val PreDef_primitives2Primitives =
Set[Symbol](Predef_double2Double, Predef_float2Float, Predef_byte2Byte, Predef_short2Short,
Predef_char2Character, Predef_int2Integer, Predef_long2Long, Predef_boolean2Boolean)

lazy val Predef_implicitly = getMemberMethod(PredefModule, nme.implicitly)
lazy val Predef_??? = DefinitionsClass.this.Predef_???
lazy val Predef_any2stringaddMethod = getMemberMethod(PredefModule, nme.any2stringadd).suchThat(_.isMethod)
Expand Down
17 changes: 17 additions & 0 deletions src/reflect/scala/reflect/internal/StdNames.scala
Expand Up @@ -649,6 +649,15 @@ trait StdNames {

val copyArrayToImmutableIndexedSeq: NameType = nameType("copyArrayToImmutableIndexedSeq")

val double2Double: NameType = nameType("double2Double")
val float2Float: NameType = nameType("float2Float")
val byte2Byte: NameType = nameType("byte2Byte")
val short2Short: NameType = nameType("short2Short")
val char2Character: NameType = nameType("char2Character")
val int2Integer: NameType = nameType("int2Integer")
val long2Long: NameType = nameType("long2Long")
val boolean2Boolean: NameType = nameType("boolean2Boolean")

// Compiler utilized names

val AnnotatedType: NameType = nameType("AnnotatedType")
Expand Down Expand Up @@ -720,6 +729,7 @@ trait StdNames {
val async : NameType = nameType("async")
val await : NameType = nameType("await")
val box: NameType = nameType("box")
val byteValue: NameType = nameType("byteValue")
val bytes: NameType = nameType("bytes")
val c: NameType = nameType("c")
val canEqual_ : NameType = nameType("canEqual")
Expand All @@ -734,6 +744,7 @@ trait StdNames {
val delayedInitArg: NameType = nameType("delayedInit$body")
val dollarScope: NameType = nameType("$scope")
val doubleHash: NameType = nameType("doubleHash")
val doubleValue: NameType = nameType("doubleValue")
val drop: NameType = nameType("drop")
val elem: NameType = nameType("elem")
val noSelfType: NameType = nameType("noSelfType")
Expand All @@ -754,6 +765,7 @@ trait StdNames {
val find_ : NameType = nameType("find")
val flatMap: NameType = nameType("flatMap")
val floatHash: NameType = nameType("floatHash")
val floatValue: NameType = nameType("floatValue")
val foreach: NameType = nameType("foreach")
val freshTermName: NameType = nameType("freshTermName")
val freshTypeName: NameType = nameType("freshTypeName")
Expand All @@ -768,19 +780,23 @@ trait StdNames {
val initialized : NameType = nameType("initialized")
val internal: NameType = nameType("internal")
val inlinedEquals: NameType = nameType("inlinedEquals")
val intValue: NameType = nameType("intValue")
val ioobe : NameType = nameType("ioobe")
val isArray: NameType = nameType("isArray")
val isDefinedAt: NameType = nameType("isDefinedAt")
val isEmpty: NameType = nameType("isEmpty")
val isInfinite: NameType = nameType("isInfinite")
val isInstanceOf_ : NameType = nameType("isInstanceOf")
val isInstanceOf_Ob : NameType = nameType(s"$$isInstanceOf") // looks like missing interpolator due to Any member in scope
val isNaN: NameType = nameType("isNaN")
val java: NameType = nameType("java")
val key: NameType = nameType("key")
val lang: NameType = nameType("lang")
val length: NameType = nameType("length")
val lengthCompare: NameType = nameType("lengthCompare")
val locally: NameType = nameType("locally")
val longHash: NameType = nameType("longHash")
val longValue: NameType = nameType("longValue")
val macroContext : NameType = nameType("c")
val main: NameType = nameType("main")
val manifestToTypeTag: NameType = nameType("manifestToTypeTag")
Expand Down Expand Up @@ -835,6 +851,7 @@ trait StdNames {
val setInfo: NameType = nameType("setInfo")
val setSymbol: NameType = nameType("setSymbol")
val setType: NameType = nameType("setType")
val shortValue: NameType = nameType("shortValue")
val splice: NameType = nameType("splice")
val staticClass : NameType = nameType("staticClass")
val staticModule : NameType = nameType("staticModule")
Expand Down
8 changes: 4 additions & 4 deletions src/testkit/scala/tools/testkit/AllocationTest.scala
Expand Up @@ -26,9 +26,9 @@ object AllocationTest {
allocationCounter.setThreadAllocatedMemoryEnabled(true)

private object coster extends AllocationTest {
def byte = 1.toByte
def byte = 99.toByte

def short = 1.toShort
def short = 9999.toShort

def int = 100000000

Expand All @@ -38,9 +38,9 @@ object AllocationTest {

def char = 's'

def float = 1F
def float = 123456F

def double = 1D
def double = 123456D

@nowarn("cat=lint-nullary-unit")
def unit = ()
Expand Down

0 comments on commit 0ab9086

Please sign in to comment.