:: Enseignements :: ESIPE :: E5INFO :: 2019-2020 :: Machine Virtuelle (et bazar autour ...) ::
[LOGO]

Lab 3c - JVM Interpreter / Object Optimization


Exercice 1 - get field/set field/method call

We have not finished the impelmentation of the JVM interpreter, all the object related syntax are not supported.
The aim of this exercise is to implement them.

Here is the code of the bootstrap methods (we will optimize them in the exercise 2).
  ...
  private static final MethodHandle GET_MH, LOOKUP_MH;
  static {
    var lookup = MethodHandles.lookup();
    try {
      LOOKUP = lookup.findVirtual(JSObject.class, "lookup", methodType(Object.class, String.class));
      REGISTER = lookup.findVirtual(JSObject.class, "register", methodType(void.class, String.class, Object.class));
      ...  
      GET_MH = lookup.findVirtual(JSObject.class, "getMethodHandle", methodType(MethodHandle.class));
      METH_LOOKUP_MH = lookup.findStatic(RT.class, "lookupMethodHandle", methodType(MethodHandle.class, JSObject.class, String.class));
    } catch (NoSuchMethodException | IllegalAccessException e) {
      throw new AssertionError(e);
    }
  }
  ...
  public static CallSite bsm_get(Lookup lookup, String name, MethodType type, String fieldName) {
    return new ConstantCallSite(insertArguments(LOOKUP, 1, fieldName).asType(type));
  }
  
  public static CallSite bsm_set(Lookup lookup, String name, MethodType type, String fieldName) {
    return new ConstantCallSite(insertArguments(REGISTER, 1, fieldName).asType(type));
  }
  
  @SuppressWarnings("unused")  // used by a method handle
  private static MethodHandle lookupMethodHandle(JSObject receiver, String fieldName) {
    var function = (JSObject)receiver.lookup(fieldName);
    return function.getMethodHandle();
  }
  
  public static CallSite bsm_methodcall(Lookup lookup, String name, MethodType type) {
    var combiner = MethodHandles.insertArguments(METH_LOOKUP_MH, 1, name).asType(methodType(MethodHandle.class, Object.class));
    var target = MethodHandles.foldArguments(invoker(type), combiner);
    return new ConstantCallSite(target);
  }
  ...
     

  1. Explain what bsm_get does ?
    why asType is called ?
  2. What the method MethodHandles.foldArguments does ?
    Explain how bsm_methodcall works ?
  3. We propose this code for rewriting a New AST node:
    .when(New.class, (_new, env) -> {
      mv.visitInsn(ACONST_NULL);
      mv.visitMethodInsn(INVOKESTATIC, JSOBJECT, "newObject", "(L" + JSOBJECT + ";)L" + JSOBJECT + ';', false);
      _new.getInitMap().forEach((key, init) -> {
        mv.visitInsn(DUP);
        mv.visitLdcInsn(key);
        visitor.visit(init, env);
        mv.visitMethodInsn(INVOKEVIRTUAL, JSOBJECT, "register", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
      });
    })
            
    What this code does ?
    Why DUP is needed here ?
    Verify that tests marked Q13 and Q14 pass.
  4. Implement the visit for the AST node FieldAccess and verify that tests marked Q15 pass.
  5. Implement the visit for the AST node FieldAssignment and verify that tests marked Q16 pass.
  6. Implement the visit for the AST node MethodCall and verify that tests marked Q17 pass.

Exercice 2 - optimizing field access using speculative optimizations

While JavaScript allows to dynamically add fields to any objects, in practice it's rare to find such occurence after the object creation, so it makes sense to optimize as if the object have the same constant set of fields.
If the set of fields of objects is mostly constant it makes sense to try to optimize using an inlining cache.
The idea is to associate one unique object (the layout) for each set of fields so if two objects have the same set the fields, then they share the same layout object. (this is what the class ArrayMap does).
So the algorithm is the following, first check the layout, if it's the same layout as the last time, then the index of the field is a constant for all the object with that layout.

The skeleton of an inlining cache for field access
  public static CallSite bsm_get(Lookup lookup, String name, MethodType type, String fieldName) {
    //return new ConstantCallSite(insertArguments(LOOKUP, 1, fieldName).asType(type));
    return new InliningFieldCache(type, fieldName);
  }
  
  private static class InliningFieldCache extends MutableCallSite {
    private static final MethodHandle SLOW_PATH, LAYOUT_CHECK, FAST_ACCESS;
    static {
      var lookup = MethodHandles.lookup();
      try {
        ...
      } catch (NoSuchMethodException | IllegalAccessException e) {
        throw new AssertionError(e);
      }
    }
    
    private final String fieldName;
    
    public InliningFieldCache(MethodType type, String fieldName) {
      super(type);
      this.fieldName = fieldName;
      setTarget(SLOW_PATH.bindTo(this));
    }
    
    @SuppressWarnings("unused")  // called by a MH
    private static boolean layoutCheck(ArrayMap.Layout layout, Object o) {
      return layout == ((JSObject)o).getLayout();
    }
    
    @SuppressWarnings("unused")  // called by a MH
    private Object slowPath(Object receiver) {
      var jsObject = (JSObject)receiver;
      
      // classical access to the value
      var value = jsObject.lookup(fieldName);
      
      // fast access
      //var layout = jsObject.getLayout();
      //var slot = layout.slot(fieldName);   // may be -1 !
      //var value = jsObject.fastAccess(slot);
       
      //TODO
      return value; 
    }
  }
      

  1. Add the guardWithTest to finish the implementation of the inlining cache..
    Verify that the tests marked Q15 and Q16 still work.
  2. Add a depth check so the inlining cache is bi-morphic.