Branch: Tag:

2014-08-07

2014-08-07 16:24:32 by Per Hedbor <ph@opera.com>

Optimized access to private/final global variables

Especially the machine code version is now significantly faster, it
will simply read the variable directly from the known byte offset
instead of calling a function that resolves it in the vtable.

Gives about a 20x speedup of trivial code along the lines of
globala = globala + globalb;

Also tried to disable some of the optimizations that causes lvalues to
be generated instead of the desired global/assign_global opcodes.

For now this is only done if the global variabeles are known to not be
arrays, multiset, strings, mapping or objects, since those
optimizations are needed to quickly append things to arrays (and
mappings/multiset, but that is less common. It is also needed for
destructive modifications of strings, something that is even less
common).

678:    emit1 (F_RANGE, bound_types);   }    + static void emit_global( int n ) + { +  struct compilation *c = THIS_COMPILATION; +  struct reference *ref = PTR_FROM_INT(Pike_compiler->new_program, n); +  struct identifier *id = ID_FROM_PTR(Pike_compiler->new_program, ref); +  if( (ref->id_flags & (ID_PRIVATE|ID_FINAL)) +  && !(id->identifier_flags & IDENTIFIER_NO_THIS_REF) +  && IDENTIFIER_IS_VARIABLE(id->identifier_flags) +  && !ref->inherit_offset +  && id->run_time_type == PIKE_T_MIXED ) +  { +  /* fprintf( stderr, "private global %d\n", (INT32)id->func.offset ); */ +  emit1(F_PRIVATE_GLOBAL, id->func.offset); +  } +  else +  emit1(F_GLOBAL, n); + } +  + static void emit_assign_global( int n, int and_pop ) + { +  struct compilation *c = THIS_COMPILATION; +  struct reference *ref = PTR_FROM_INT(Pike_compiler->new_program, n); +  struct identifier *id = ID_FROM_PTR(Pike_compiler->new_program, ref); +  +  if( (ref->id_flags & (ID_PRIVATE|ID_FINAL)) +  && !(id->identifier_flags & IDENTIFIER_NO_THIS_REF) +  && !ref->inherit_offset +  && id->run_time_type == PIKE_T_MIXED ) +  { +  /* fprintf( stderr, "assign private global and pop %d\n", */ +  /* (INT32)id->func.offset ); */ +  emit1((and_pop?F_ASSIGN_PRIVATE_GLOBAL_AND_POP:F_ASSIGN_PRIVATE_GLOBAL), +  id->func.offset); +  } +  else +  { +  emit1((and_pop?F_ASSIGN_GLOBAL_AND_POP:F_ASSIGN_GLOBAL), n); +  } + } +    static void emit_multi_assign(node *vals, node *vars, int no)   {    struct compilation *c = THIS_COMPILATION;
726:    }else{    code_expression(val, 0, "RHS");    emit_multi_assign(vals, vars, no+1); -  emit1(F_ASSIGN_GLOBAL_AND_POP, var->u.id.number); +  emit_assign_global( var->u.id.number, 1 );    }    break;   
785:    {    code_expression(val, 0, "RHS");    emit_multi_assign(vals, vars, no+1); -  emit1(F_ASSIGN_GLOBAL_AND_POP, var->u.integer.b); +  emit_assign_global( var->u.integer.b, 1 );    break;    }    }
991:    emit1(F_CONSTANT, id->func.const_info.offset);    }    }else{ -  emit1(F_GLOBAL, n->u.integer.b); +  emit_global( n->u.integer.b );    }    }    }
1271:    if(node_is_eq(CDR(n),CAAR(n)))    {    int num_args; -  tmp1=do_docode(CDR(n),DO_LVALUE); +  /* tmp1=do_docode(CDR(n),DO_LVALUE); */    if(match_types(CDR(n)->type, array_type_string) ||    match_types(CDR(n)->type, string_type_string) ||    match_types(CDR(n)->type, object_type_string) ||    match_types(CDR(n)->type, multiset_type_string) ||    match_types(CDR(n)->type, mapping_type_string))    { -  +  do_docode(CDR(n),DO_LVALUE);    num_args = do_docode(CDAR(n), 0);    switch (num_args)    {
1290:   #endif    }    }else{ +  goto do_not_suboptimize_assign;    emit0(F_LTOSVAL);    num_args = do_docode(CDAR(n), 0);    }
1314:    node **arg = my_get_arg(&args, 0);    if (arg && node_is_eq(CDR(n), *arg) &&    !(args->tree_info & OPT_ASSIGNMENT)) { +  if(match_types(CDR(n)->type, array_type_string) || +  match_types(CDR(n)->type, string_type_string) || +  match_types(CDR(n)->type, object_type_string) || +  match_types(CDR(n)->type, multiset_type_string) || +  match_types(CDR(n)->type, mapping_type_string)) +  {    /* First arg is the lvalue.    *    * We optimize this to allow for destructive operations.
1334:    }    }    } +  }    /* FALL_THROUGH */    default: -  +  do_not_suboptimize_assign:    switch(CDR(n)->token)    { -  +  case F_GLOBAL: +  if(CDR(n)->u.integer.b) goto normal_assign; +  code_expression(CAR(n), 0, "RHS"); +  emit_assign_global( CDR(n)->u.integer.a, flags & DO_POP ); +  break;    case F_LOCAL:    if(CDR(n)->u.integer.a >=    find_local_frame(CDR(n)->u.integer.b)->max_number_of_locals)
1362:    yyerror("Cannot assign functions or constants.\n");    }else{    code_expression(CAR(n), 0, "RHS"); -  emit1(flags & DO_POP ? F_ASSIGN_GLOBAL_AND_POP:F_ASSIGN_GLOBAL, -  CDR(n)->u.id.number); +  emit_assign_global( CDR(n)->u.id.number, flags & DO_POP );    }    break;   
1435:    IDENTIFIER_IS_VARIABLE( ID_FROM_INT(Pike_compiler->new_program, CDR(n)->u.integer.b)->identifier_flags))    {    code_expression(CAR(n), 0, "RHS"); -  emit1(flags & DO_POP ? F_ASSIGN_GLOBAL_AND_POP:F_ASSIGN_GLOBAL, -  CDR(n)->u.integer.b); +  emit_assign_global(CDR(n)->u.integer.b, flags & DO_POP );    break;    }    }
2770:    * prototype. */    emit1(F_LFUN,n->u.id.number);    else -  emit1(F_GLOBAL,n->u.id.number); +  emit_global(n->u.id.number);    }    }else{    if(flags & WANT_LVALUE)
2778:    emit1(F_GLOBAL_LVALUE,n->u.id.number);    return 2;    }else{ -  emit1(F_GLOBAL,n->u.id.number); +  emit_global(n->u.id.number);    }    }    return 1;