scala-dev icon indicating copy to clipboard operation
scala-dev copied to clipboard

Result of inlining a method dealing with generic arrays is disappointing

Open retronym opened this issue 7 years ago • 2 comments
trafficstars

Perhaps a heuristic to inline array_apply / array_update / array_length when we know the specific array type (reference vs primitive) could do better.

  @inline final def hashArray[T](s: Array[T], hashF: T => Unit): Unit = {
    extend(s.length)
    var i = 0
    while (i < s.length) {
      hashF(s(i))
      i += 1
    }
  }
def hashValueParameters(valueParameters: Array[ParameterList]) =
    hashArray(valueParameters, hashValueParameterList)
public hashValueParameters([Lxsbti/api/ParameterList;)V
    // parameter final  valueParameters
    GETSTATIC scala/runtime/ScalaRunTime$.MODULE$ : Lscala/runtime/ScalaRunTime$;
    ALOAD 1
    INVOKEVIRTUAL scala/runtime/ScalaRunTime$.array_length (Ljava/lang/Object;)I
    ISTORE 3
    ALOAD 0
    GETSTATIC scala/util/hashing/MurmurHash3$.MODULE$ : Lscala/util/hashing/MurmurHash3$;
    ALOAD 0
    GETFIELD xsbt/api/HashAPI.xsbt$api$HashAPI$$hash : I
    ILOAD 3
    INVOKEVIRTUAL scala/util/hashing/MurmurHash3$.mix (II)I
    PUTFIELD xsbt/api/HashAPI.xsbt$api$HashAPI$$hash : I
    ICONST_0
    ISTORE 2
   L0
    ILOAD 2
    GETSTATIC scala/runtime/ScalaRunTime$.MODULE$ : Lscala/runtime/ScalaRunTime$;
    ALOAD 1
    INVOKEVIRTUAL scala/runtime/ScalaRunTime$.array_length (Ljava/lang/Object;)I
    IF_ICMPGE L1
    GETSTATIC scala/runtime/ScalaRunTime$.MODULE$ : Lscala/runtime/ScalaRunTime$;
    ALOAD 1
    ILOAD 2
    INVOKEVIRTUAL scala/runtime/ScalaRunTime$.array_apply (Ljava/lang/Object;I)Ljava/lang/Object;
    CHECKCAST xsbti/api/ParameterList
    ASTORE 4
    ALOAD 0
    ALOAD 4
    INVOKESTATIC xsbt/api/HashAPI.$anonfun$hashValueParameters$1 (Lxsbt/api/HashAPI;Lxsbti/api/ParameterList;)V
    ILOAD 2
    ICONST_1
    IADD
    ISTORE 2
    GOTO L0
   L1
    RETURN
    MAXSTACK = 4
    MAXLOCALS = 5

retronym avatar Feb 17 '18 03:02 retronym

A workaround is the declare the method as:

  @inline final def hashArray[T <: AnyRef](s: Array[T], hashF: T => Unit): Unit = {
    extend(s.length)
    var i = 0
    while (i < s.length) {
      hashF(s(i))
      i += 1
    }
  }

public hashValueParameters([Lxsbti/api/ParameterList;)V
    // parameter final  valueParameters
    ALOAD 1
    CHECKCAST [Ljava/lang/Object;
    ASTORE 2
    ALOAD 2
    ARRAYLENGTH
    ISTORE 4
    ALOAD 0
    GETSTATIC scala/util/hashing/MurmurHash3$.MODULE$ : Lscala/util/hashing/MurmurHash3$;
    ALOAD 0
    GETFIELD xsbt/api/HashAPI.xsbt$api$HashAPI$$hash : I
    ILOAD 4
    INVOKEVIRTUAL scala/util/hashing/MurmurHash3$.mix (II)I
    PUTFIELD xsbt/api/HashAPI.xsbt$api$HashAPI$$hash : I
    ICONST_0
    ISTORE 3
   L0
    ILOAD 3
    ALOAD 2
    ARRAYLENGTH
    IF_ICMPGE L1
    ALOAD 2
    ILOAD 3
    AALOAD
    CHECKCAST xsbti/api/ParameterList
    ASTORE 5
    ALOAD 0
    ALOAD 5
    INVOKESTATIC xsbt/api/HashAPI.$anonfun$hashValueParameters$1 (Lxsbt/api/HashAPI;Lxsbti/api/ParameterList;)V
    ILOAD 3
    ICONST_1
    IADD
    ISTORE 3
    GOTO L0

retronym avatar Feb 17 '18 03:02 retronym

FWIW, the Scala.js optimizer does intrinsify those three methods, and replaces them with the appropriate IR node (bytecode) if the array argument is known to have an array type (as opposed to Object).

sjrd avatar Feb 17 '18 06:02 sjrd