private static LambdaForm makePreparedFieldLambdaForm(
      byte formOp, boolean isVolatile, int ftypeKind) {
    boolean isGetter = (formOp & 1) == (AF_GETFIELD & 1);
    boolean isStatic = (formOp >= AF_GETSTATIC);
    boolean needsInit = (formOp >= AF_GETSTATIC_INIT);
    boolean needsCast = (ftypeKind == FT_CHECKED_REF);
    Wrapper fw = (needsCast ? Wrapper.OBJECT : ALL_WRAPPERS[ftypeKind]);
    Class<?> ft = fw.primitiveType();
    assert (ftypeKind(needsCast ? String.class : ft) == ftypeKind);

    // getObject, putIntVolatile, etc.
    StringBuilder nameBuilder = new StringBuilder();
    if (isGetter) {
      nameBuilder.append("get");
    } else {
      nameBuilder.append("put");
    }
    nameBuilder.append(fw.primitiveSimpleName());
    nameBuilder.setCharAt(3, Character.toUpperCase(nameBuilder.charAt(3)));
    if (isVolatile) {
      nameBuilder.append("Volatile");
    }

    MethodType linkerType;
    if (isGetter) linkerType = MethodType.methodType(ft, Object.class, long.class);
    else linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
    MemberName linker =
        new MemberName(Unsafe.class, nameBuilder.toString(), linkerType, REF_invokeVirtual);
    try {
      linker =
          IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class);
    } catch (ReflectiveOperationException ex) {
      throw newInternalError(ex);
    }

    // What is the external type of the lambda form?
    MethodType mtype;
    if (isGetter) mtype = MethodType.methodType(ft);
    else mtype = MethodType.methodType(void.class, ft);
    mtype = mtype.basicType(); // erase short to int, etc.
    if (!isStatic) mtype = mtype.insertParameterTypes(0, Object.class);
    final int DMH_THIS = 0;
    final int ARG_BASE = 1;
    final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
    // if this is for non-static access, the base pointer is stored at this index:
    final int OBJ_BASE = isStatic ? -1 : ARG_BASE;
    // if this is for write access, the value to be written is stored at this index:
    final int SET_VALUE = isGetter ? -1 : ARG_LIMIT - 1;
    int nameCursor = ARG_LIMIT;
    final int F_HOLDER = (isStatic ? nameCursor++ : -1); // static base if any
    final int F_OFFSET = nameCursor++; // Either static offset or field offset.
    final int OBJ_CHECK = (OBJ_BASE >= 0 ? nameCursor++ : -1);
    final int INIT_BAR = (needsInit ? nameCursor++ : -1);
    final int PRE_CAST = (needsCast && !isGetter ? nameCursor++ : -1);
    final int LINKER_CALL = nameCursor++;
    final int POST_CAST = (needsCast && isGetter ? nameCursor++ : -1);
    final int RESULT = nameCursor - 1; // either the call or the cast
    Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
    if (needsInit) names[INIT_BAR] = new Name(NF_ensureInitialized, names[DMH_THIS]);
    if (needsCast && !isGetter)
      names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
    Object[] outArgs = new Object[1 + linkerType.parameterCount()];
    assert (outArgs.length == (isGetter ? 3 : 4));
    outArgs[0] = UNSAFE;
    if (isStatic) {
      outArgs[1] = names[F_HOLDER] = new Name(NF_staticBase, names[DMH_THIS]);
      outArgs[2] = names[F_OFFSET] = new Name(NF_staticOffset, names[DMH_THIS]);
    } else {
      outArgs[1] = names[OBJ_CHECK] = new Name(NF_checkBase, names[OBJ_BASE]);
      outArgs[2] = names[F_OFFSET] = new Name(NF_fieldOffset, names[DMH_THIS]);
    }
    if (!isGetter) {
      outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
    }
    for (Object a : outArgs) assert (a != null);
    names[LINKER_CALL] = new Name(linker, outArgs);
    if (needsCast && isGetter)
      names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
    for (Name n : names) assert (n != null);
    // add some detail to the lambdaForm debugname,
    // significant only for debugging
    if (isStatic) {
      nameBuilder.append("Static");
    } else {
      nameBuilder.append("Field");
    }
    if (needsCast) nameBuilder.append("Cast");
    if (needsInit) nameBuilder.append("Init");
    return new LambdaForm(nameBuilder.toString(), ARG_LIMIT, names, RESULT);
  }
  static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
    boolean needsInit = (which == LF_INVSTATIC_INIT);
    boolean doesAlloc = (which == LF_NEWINVSPECIAL);
    String linkerName;
    LambdaForm.Kind kind;
    switch (which) {
      case LF_INVVIRTUAL:
        linkerName = "linkToVirtual";
        kind = DIRECT_INVOKE_VIRTUAL;
        break;
      case LF_INVSTATIC:
        linkerName = "linkToStatic";
        kind = DIRECT_INVOKE_STATIC;
        break;
      case LF_INVSTATIC_INIT:
        linkerName = "linkToStatic";
        kind = DIRECT_INVOKE_STATIC_INIT;
        break;
      case LF_INVSPECIAL:
        linkerName = "linkToSpecial";
        kind = DIRECT_INVOKE_SPECIAL;
        break;
      case LF_INVINTERFACE:
        linkerName = "linkToInterface";
        kind = DIRECT_INVOKE_INTERFACE;
        break;
      case LF_NEWINVSPECIAL:
        linkerName = "linkToSpecial";
        kind = DIRECT_NEW_INVOKE_SPECIAL;
        break;
      default:
        throw new InternalError("which=" + which);
    }

    MethodType mtypeWithArg = mtype.appendParameterTypes(MemberName.class);
    if (doesAlloc)
      mtypeWithArg =
          mtypeWithArg
              .insertParameterTypes(0, Object.class) // insert newly allocated obj
              .changeReturnType(void.class); // <init> returns void
    MemberName linker =
        new MemberName(MethodHandle.class, linkerName, mtypeWithArg, REF_invokeStatic);
    try {
      linker =
          IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, NoSuchMethodException.class);
    } catch (ReflectiveOperationException ex) {
      throw newInternalError(ex);
    }
    final int DMH_THIS = 0;
    final int ARG_BASE = 1;
    final int ARG_LIMIT = ARG_BASE + mtype.parameterCount();
    int nameCursor = ARG_LIMIT;
    final int NEW_OBJ = (doesAlloc ? nameCursor++ : -1);
    final int GET_MEMBER = nameCursor++;
    final int LINKER_CALL = nameCursor++;
    Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
    assert (names.length == nameCursor);
    if (doesAlloc) {
      // names = { argx,y,z,... new C, init method }
      names[NEW_OBJ] = new Name(NF_allocateInstance, names[DMH_THIS]);
      names[GET_MEMBER] = new Name(NF_constructorMethod, names[DMH_THIS]);
    } else if (needsInit) {
      names[GET_MEMBER] = new Name(NF_internalMemberNameEnsureInit, names[DMH_THIS]);
    } else {
      names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]);
    }
    assert (findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]);
    Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER + 1, Object[].class);
    assert (outArgs[outArgs.length - 1] == names[GET_MEMBER]); // look, shifted args!
    int result = LAST_RESULT;
    if (doesAlloc) {
      assert (outArgs[outArgs.length - 2] == names[NEW_OBJ]); // got to move this one
      System.arraycopy(outArgs, 0, outArgs, 1, outArgs.length - 2);
      outArgs[0] = names[NEW_OBJ];
      result = NEW_OBJ;
    }
    names[LINKER_CALL] = new Name(linker, outArgs);
    String lambdaName = kind.defaultLambdaName + "_" + shortenSignature(basicTypeSignature(mtype));
    LambdaForm lform = new LambdaForm(lambdaName, ARG_LIMIT, names, result, kind);

    // This is a tricky bit of code.  Don't send it through the LF interpreter.
    lform.compileToBytecode();
    return lform;
  }