rorygraves / scalac_perf

The Scala programming language
http://www.scala-lang.org/
16 stars 3 forks source link

compile time constants #60

Open mkeskells opened 6 years ago

mkeskells commented 6 years ago

It seems that some constants are deferred to run time. This means that they generate more bytecode, and cant (probably) be inlined

e.g. Flags.scala has code like

  final val PRIVATE       = 1 << 2
...
  final val LateShift     = 47
  final val AntiShift     = 56
...
  final val notPRIVATE    = (PRIVATE: Long) << AntiShift

generate bytecode like


  // access flags 0x11
  public final AntiFlags()J
   L0
    LDC 504403158265495552
    LRETURN
   L1
    LOCALVARIABLE this Lscala/reflect/internal/Flags; L0 L1 0
    MAXSTACK = 2
    MAXLOCALS = 1

  // access flags 0x11
  public final LateShift()I
   L0
    BIPUSH 47
    IRETURN
   L1
    LOCALVARIABLE this Lscala/reflect/internal/Flags; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

but not for all fields e.g


  // access flags 0x11
  public final notPRIVATE()J
   L0
    LINENUMBER 231 L0
    ALOAD 0
    GETFIELD scala/reflect/internal/Flags.notPRIVATE : J
    LRETURN
   L1
    LOCALVARIABLE this Lscala/reflect/internal/Flags; L0 L1 0
    MAXSTACK = 2
    MAXLOCALS = 1
retronym commented 6 years ago

(PRIVATE: Long) << AntiShift is not a constant expression, so it fall through the constant folder.

We could update that to make a widening of a constant also be a constant. In addition to changing that code, this part of the spec would need to be augmented.

retronym commented 6 years ago

Or, without changing the spec, we could just use 0L + PRIVATE instead of (PRIVATE: Long).

scala> object C { final val PRIVATE = 1 << 2; final val AntiShift     = 56; final val notPRIVATE = (0L + PRIVATE) << AntiShift }
defined object C

scala> class Test { def foo = C.notPRIVATE }
defined class Test

scala> :javap -c Test#foo
  public long foo();
    Code:
       0: ldc2_w        #16                 // long 288230376151711744l
       3: lreturn
retronym commented 6 years ago

https://github.com/scala/scala/pull/6445

mkeskells commented 6 years ago

I think that it is worth considering changing the spec, as that will be beneficial to other cases outside the Flags.

It seem strange that implicit widening is optimised, but explicit is not. Presumably narrowing isn't either

This will have an impact on performance I think as these constants get inlined to the caller @retronym @adriaanm do you have any concerns about widened the spec (in 2.13)

mkeskells commented 6 years ago

discussed with @retronym on gitter - ~Long is not folding as expected

retronym commented 6 years ago

WIP https://github.com/scala/scala/compare/2.13.x...retronym:topic/constant-folder-unary?expand=1

There is overlap with https://github.com/scala/scala/pull/6452, which also needs to introduce some constant-related logic in typedSelect. We're discussing it all over there.