/** * Optimizations for OP_convert_i: convert_i, convert_i -> convert_i coerce_i, convert_i -> * coerce_i OP_bitand, convert_i -> OP_bitand OP_bitor, convert_i -> OP_bitor OP_bitxor, convert_i * -> OP_bitxor lshift, convert_i -> lshift rshift, convert_i -> rshift add_i, convert_i -> add_i * subtract_i, convert_i -> subtract_i increment_i, convert_i -> increment_i decrement_i, * convert_i -> decrement_i multiply_i, convert_i -> multiply_i pushbyte, convert_i -> pushbyte * pushshort, convert_i -> pushshort pushint, convert_i -> pushint li8, convert_i -> li8 li16, * convert_i -> li16 li32, convert_i -> li32 sxi1, convert_i -> sxi1 sxi8, convert_i -> sxi8 * sxi16, convert_i -> sxi16 * * @param i The convert_b instruction */ private void op_convert_i(InstructionInfo i) { InstructionInfo prev = previous(1); switch (prev.getOpcode()) { case OP_convert_i: case OP_coerce_i: case OP_bitand: case OP_bitor: case OP_bitxor: case OP_lshift: case OP_rshift: case OP_add_i: case OP_subtract_i: case OP_increment_i: case OP_decrement_i: case OP_multiply_i: case OP_pushbyte: case OP_pushshort: case OP_pushint: case OP_li8: case OP_li16: case OP_li32: case OP_sxi1: case OP_sxi8: case OP_sxi16: { // result is already an int, just erase the op_convert_i delete(0); break; } default: { // nothing to do - instruction has already been added } } }
/** * Optimizations for OP_convert_b: equals, convert_b -> equals strictequals, convert_b -> * strictequals not, convert_b -> not greaterthan, convert_b -> greaterthan lessthan, convert_b -> * lessthan greaterequals, convert_b -> greaterequals lessequals, convert_b -> lessequals istype, * convert_b -> istype istypelate, convert_b -> istypelate instanceof, convert_b -> instanceof * deleteproperty, convert_d -> deleteproperty in, convert_d -> in convert_b, convert_b -> * convert_b pushtrue, convert_b -> pushtrue pushfalse, convert_b -> pushfalse * * @param i The convert_b instruction */ private void op_convert_b(InstructionInfo i) { InstructionInfo prev = previous(1); switch (prev.getOpcode()) { case OP_equals: case OP_strictequals: case OP_not: case OP_greaterthan: case OP_lessthan: case OP_greaterequals: case OP_lessequals: case OP_istype: case OP_istypelate: case OP_instanceof: case OP_deleteproperty: case OP_in: case OP_convert_b: case OP_pushtrue: case OP_pushfalse: { // result is already a boolean, just erase the op_convert_b delete(0); break; } default: { // nothing to do - instruction has already been added } } }
/** * Optimizations for OP_returnvoid: returnvoid, returnvoid -> returnvoid getlocal0, pushscope, * returnvoid -> returnvoid (insn without side effect), returnvoid -> returnvoid */ private void op_returnvoid(InstructionInfo i) { InstructionInfo prev = previous(1); switch (prev.getOpcode()) { case OP_returnvoid: { delete(0); break; } // Any instruction without side effects that // the CG is likely to generate can go here. case OP_pop: { replace(1, i.getInstruction()); break; } case OP_pushscope: { if (previous(2).getOpcode() == OP_getlocal0) // Trivial function. replace(2, i.getInstruction()); break; } default: { // nothing to do, instruction has already been added } } }
/** * Optimizations for OP_getproperty: findpropstrict name, getproperty name -> getlex name * * @param i the getproperty instruction */ private void op_getproperty(InstructionInfo i) { InstructionInfo prev = previous(1); switch (prev.getOpcode()) { case OP_findpropstrict: { // can optimize findpropstrict, followed by getprop of the same name // if there are any instructions btwn the findpropstrict and getprop (such as to compute a // runtime // multiname), we won't match this pattern if (i.getOperand(0).equals(prev.getOperand(0))) replace( 1, InstructionFactory.createModifiedInstruction(OP_getlex, prev.getInstruction())); break; } } }
/** * Optimizations for OP_convert_i: convert_u, convert_u -> convert_u pushuint, convert_u -> * pushuint * * @param i The convert_b instruction */ private void op_convert_u(InstructionInfo i) { InstructionInfo prev = previous(1); switch (prev.getOpcode()) { case OP_convert_u: case OP_pushuint: { // result is already a uint, just erase the op_convert_u delete(0); break; } default: { // nothing to do - instruction has already been added } } }
/** * Optimizations for OP_convert_s: coerce_s, convert_s -> coerce_s convert_s, convert_s -> * convert_s pushstring, convert_s -> pushstring typeof, convert_s -> typeof * * @param i The convert_b instruction */ private void op_convert_s(InstructionInfo i) { InstructionInfo prev = previous(1); switch (prev.getOpcode()) { case OP_coerce_s: case OP_convert_s: case OP_pushstring: case OP_typeof: { // result is already a String, just erase the op_convert_s delete(0); break; } default: { // nothing to do - instruction has already been added } } }
/** * Helper method to perform optimizations around jumps when we get a labeling operation. Shared * with labelNext and labelCurrent methods * * @param l the Label object passed into the label operation * @param kind what kind of labeling operation are we doing (labelNext or labelCurrent) */ private void jumpOptimizations(Label l, LabelKind kind) { // Need to start looking at different indexes for label current vs. label next // this is because for a label next, the next instruction won't have come in yet. int idx = kind == LabelKind.LABEL_CURRENT ? 1 : 0; InstructionInfo prev = previous(idx); switch (prev.getOpcode()) { case OP_jump: { InstructionInfo prev2 = previous(idx + 1); Instruction insn = prev2.getInstruction(); if (insn != null && insn.isBranch() && insn.getTarget() == l) { // If the previous instructions were an if that jumped here, and it // only jumped over another jump, then we can invert the if instruction // and save a jump // iffalse L1, jump L2, L1 -> iftrue L2, L1 Instruction newIf = invertIf(prev2, prev); if (newIf != null) { if (kind == LabelKind.LABEL_CURRENT) { // labelCurrent, so we need to preserve the last instruction Instruction[] newInsns = {newIf, previous(0).getInstruction()}; replace(idx + 1, newInsns); } else { // labelNext so we can just delete the last instruction replace(idx + 1, newIf); } } } // If the previous instruction was a jump, and it just jumped // to the next instruction, then we can remove the jump and just fall // through // jump L1, L1 -> L1 else if (prev.getOperand(0) == l) { if (kind == LabelKind.LABEL_NEXT) // can just delete the jump because we don't have the next instruction yet delete(idx); else // replace the jump with its target replace(idx, previous(0).getInstruction()); } } } }
/** * evaluates an instruction and determines if it will result in true or false on TOS. * * @param i is an instruction to analyze * @return whether TOS is true, false, or not known */ private static ConstantBoolean isTrueInstructionInfo(InstructionInfo i) { ConstantBoolean ret = ConstantBoolean.DONT_KNOW; switch (i.getOpcode()) { case OP_pushtrue: ret = ConstantBoolean.TRUE; break; case OP_pushfalse: ret = ConstantBoolean.FALSE; break; case OP_pushbyte: { int value = i.getImmediate(); assert value >= 0; ret = ECMASupport.toBoolean(value) ? ConstantBoolean.TRUE : ConstantBoolean.FALSE; break; } case OP_pushint: { int value = (Integer) i.getOperand(0); ret = ECMASupport.toBoolean(value) ? ConstantBoolean.TRUE : ConstantBoolean.FALSE; break; } case OP_pushuint: { long value = (Long) i.getOperand(0); ret = ECMASupport.toBoolean(value) ? ConstantBoolean.TRUE : ConstantBoolean.FALSE; break; } case OP_pushstring: { String value = i.getOperand(0).toString(); ret = ECMASupport.toBoolean(value) ? ConstantBoolean.TRUE : ConstantBoolean.FALSE; break; } case OP_pushnull: ret = ConstantBoolean.FALSE; break; } return ret; }
/** Optimizations for OP_getlocal: setlocal N, getlocal N -> dup, setlocal N */ private void OP_getlocal(InstructionInfo i) { InstructionInfo prev = previous(1); InstructionInfo cur = previous(0); switch (prev.getOpcode()) { case OP_setlocal: { if (cur.getInstruction().getImmediate() != prev.getInstruction().getImmediate()) break; // set,get of different locals Instruction[] newInsns = { InstructionFactory.getInstruction(OP_dup), prev.getInstruction() }; replace(1, newInsns); break; } default: { // nothing to do, instruction has already been added } } }
/** * Optimizations for OP_convert_d: convert_d, convert_d -> convert_d pushbyte n, convert_d -> * pushdouble n pushint n, convert_d -> pushdouble n pushuint n, convert_d -> pushdouble n * pushdouble n, convert_d -> pushdouble n pushnan, convert_d -> pushnan lf32, convert_d -> lf32 * lf64, convert_d -> lf64 * * @param i The convert_d instruction */ private void op_convert_d(InstructionInfo i) { InstructionInfo prev = previous(1); switch (prev.getOpcode()) { case OP_pushbyte: { // replace pushbyte, convert d with pushdouble - should be faster replace( 1, InstructionFactory.getInstruction( OP_pushdouble, new Double(convertByteImmediateToDouble(prev.getImmediate())))); break; } case OP_pushint: case OP_pushuint: { // replace pushint , convert d with pushdouble - should be faster replace( 1, InstructionFactory.getInstruction( OP_pushdouble, new Double(((Number) prev.getOperand(0)).doubleValue()))); break; } case OP_pushdouble: case OP_pushnan: case OP_lf32: case OP_lf64: case OP_convert_d: { // result is already a double, just erase the op_convert_d delete(0); break; } default: { // nothing to do - instruction has already been added } } }
/** Optimizations for OP_pop: callprop, pop -> callpropvoid callsuper, pop -> callsupervoid */ private void op_pop(InstructionInfo i) { InstructionInfo prev = previous(1); switch (prev.getOpcode()) { case OP_callproperty: { replace( 1, InstructionFactory.createModifiedInstruction(OP_callpropvoid, prev.getInstruction())); break; } case OP_callsuper: { replace( 1, InstructionFactory.createModifiedInstruction( OP_callsupervoid, prev.getInstruction())); break; } default: { // nothing to do, instruction has already been added } } }
/** * Optimizations for OP_iftrue: convert_b, iftrue -> iftrue equals, iftrue -> ifeq strictequals, * iftrue -> ifstricteq lessthen, iftrue -> iflt lessequals, iftrue -> ifle greaterthan, iftrue -> * ifgt greaterequals, iftrue -> ifge pushfalse, iftrue -> jump pushtrue, iftrue -> nothing * strictequals, not, iftrue -> ifstrictne equals, not, iftrue -> ifne lessthan, not, iftrue -> * ifnlt lessequals, not, iftrue -> ifnle greaterthan, not, iftrue -> ifngt greaterequals, * not,iftrue -> ifnge not, iftrue -> iffalse * * @param i the iftrue instruction */ private void op_iftrue(InstructionInfo i) { InstructionInfo prev = previous(1); switch (isTrueInstructionInfo(prev)) { case FALSE: delete(1); return; case TRUE: replace(1, InstructionFactory.createModifiedInstruction(OP_jump, i.getInstruction())); return; case DONT_KNOW: break; // continue on if we don't know it's a constant boolean default: assert false; } switch (prev.getOpcode()) { case OP_convert_b: { replace(1, i.getInstruction()); break; } case OP_equals: { replace(1, InstructionFactory.createModifiedInstruction(OP_ifeq, i.getInstruction())); break; } case OP_strictequals: { replace( 1, InstructionFactory.createModifiedInstruction(OP_ifstricteq, i.getInstruction())); break; } case OP_lessthan: { replace(1, InstructionFactory.createModifiedInstruction(OP_iflt, i.getInstruction())); break; } case OP_lessequals: { replace(1, InstructionFactory.createModifiedInstruction(OP_ifle, i.getInstruction())); break; } case OP_greaterthan: { replace(1, InstructionFactory.createModifiedInstruction(OP_ifgt, i.getInstruction())); break; } case OP_greaterequals: { replace(1, InstructionFactory.createModifiedInstruction(OP_ifge, i.getInstruction())); break; } case OP_not: { InstructionInfo prev2 = previous(2); switch (prev2.getOpcode()) { case OP_strictequals: { replace( 2, InstructionFactory.createModifiedInstruction( OP_ifstrictne, i.getInstruction())); break; } case OP_equals: { replace( 2, InstructionFactory.createModifiedInstruction(OP_ifne, i.getInstruction())); break; } case OP_lessthan: { replace( 2, InstructionFactory.createModifiedInstruction(OP_ifnlt, i.getInstruction())); break; } case OP_lessequals: { replace( 2, InstructionFactory.createModifiedInstruction(OP_ifnle, i.getInstruction())); break; } case OP_greaterthan: { replace( 2, InstructionFactory.createModifiedInstruction(OP_ifngt, i.getInstruction())); break; } case OP_greaterequals: { replace( 2, InstructionFactory.createModifiedInstruction(OP_ifnge, i.getInstruction())); break; } default: { replace( 1, InstructionFactory.createModifiedInstruction(OP_iffalse, i.getInstruction())); break; } } break; } default: // nothing to do, instruction was already added } }
/** * Optimizations for OP_iffalse: convert_b, iffalse -> iffalse equals, iffalse -> ifne * strictequals, iffalse -> strictne lessthen, iffalse -> ifnlt lessequals, iffalse -> ifnle * greaterthan, iffalse -> ifngt greaterequals, iffalse -> ifnge pushfalse, iffalse -> jump * pushtrue, iffalse -> nothing strictequals, not, iffalse -> ifstrictequals equals, not, iffalse * -> ifeq lessthan, not, iffalse -> iflt lessequals, not, iffalse -> ifle greaterthan, not, * iffalse -> ifgt greaterequals, not,iffalse -> ifge not, iffalse -> iftrue * * @param i the iffalse instruction */ private void op_iffalse(InstructionInfo i) { InstructionInfo prev = previous(1); // Check if we know what's on the stack. If so, we don't need // to do a conditional branch switch (isTrueInstructionInfo(prev)) { case TRUE: delete(1); // if we can't branch, just eat the push / iffalse instructions return; case FALSE: replace(1, InstructionFactory.createModifiedInstruction(OP_jump, i.getInstruction())); // If we always branch, then replace the push / iffalse with an uncontidional jump return; case DONT_KNOW: break; // continue on if we don't know it's a constant boolean default: assert false; } switch (prev.getOpcode()) { case OP_convert_b: { // replace with just iffalse replace(1, i.getInstruction()); break; } case OP_equals: { replace(1, InstructionFactory.createModifiedInstruction(OP_ifne, i.getInstruction())); break; } case OP_strictequals: { replace( 1, InstructionFactory.createModifiedInstruction(OP_ifstrictne, i.getInstruction())); break; } case OP_lessthan: { replace(1, InstructionFactory.createModifiedInstruction(OP_ifnlt, i.getInstruction())); break; } case OP_lessequals: { replace(1, InstructionFactory.createModifiedInstruction(OP_ifnle, i.getInstruction())); break; } case OP_greaterthan: { replace(1, InstructionFactory.createModifiedInstruction(OP_ifngt, i.getInstruction())); break; } case OP_greaterequals: { replace(1, InstructionFactory.createModifiedInstruction(OP_ifnge, i.getInstruction())); break; } case OP_not: { InstructionInfo prev2 = previous(2); switch (prev2.getOpcode()) { case OP_strictequals: { replace( 2, InstructionFactory.createModifiedInstruction( OP_ifstricteq, i.getInstruction())); break; } case OP_equals: { replace( 2, InstructionFactory.createModifiedInstruction(OP_ifeq, i.getInstruction())); break; } case OP_lessthan: { replace( 2, InstructionFactory.createModifiedInstruction(OP_iflt, i.getInstruction())); break; } case OP_lessequals: { replace( 2, InstructionFactory.createModifiedInstruction(OP_ifle, i.getInstruction())); break; } case OP_greaterthan: { replace( 2, InstructionFactory.createModifiedInstruction(OP_ifgt, i.getInstruction())); break; } case OP_greaterequals: { replace( 2, InstructionFactory.createModifiedInstruction(OP_ifge, i.getInstruction())); break; } default: { replace( 1, InstructionFactory.createModifiedInstruction(OP_iftrue, i.getInstruction())); break; } } break; } default: { // nothing to do, instruction was already added } } }
/** * Helper method to invert if expr target jump other-target to if not expr other-target Used to * optimize some common patterns with if's and jumps */ private Instruction invertIf(InstructionInfo oldIf, InstructionInfo oldJump) { Instruction newIf = null; switch (oldIf.getOpcode()) { case OP_ifeq: { newIf = InstructionFactory.createModifiedInstruction(OP_ifne, oldJump.getInstruction()); break; } case OP_ifstricteq: { newIf = InstructionFactory.createModifiedInstruction(OP_ifstrictne, oldJump.getInstruction()); break; } case OP_ifstrictne: { newIf = InstructionFactory.createModifiedInstruction(OP_ifstricteq, oldJump.getInstruction()); break; } case OP_ifge: { newIf = InstructionFactory.createModifiedInstruction(OP_ifnge, oldJump.getInstruction()); break; } case OP_ifgt: { newIf = InstructionFactory.createModifiedInstruction(OP_ifngt, oldJump.getInstruction()); break; } case OP_iflt: { newIf = InstructionFactory.createModifiedInstruction(OP_ifnlt, oldJump.getInstruction()); break; } case OP_ifle: { newIf = InstructionFactory.createModifiedInstruction(OP_ifnle, oldJump.getInstruction()); break; } case OP_ifne: { newIf = InstructionFactory.createModifiedInstruction(OP_ifeq, oldJump.getInstruction()); break; } case OP_ifnge: { newIf = InstructionFactory.createModifiedInstruction(OP_ifge, oldJump.getInstruction()); break; } case OP_ifngt: { newIf = InstructionFactory.createModifiedInstruction(OP_ifgt, oldJump.getInstruction()); break; } case OP_ifnlt: { newIf = InstructionFactory.createModifiedInstruction(OP_iflt, oldJump.getInstruction()); break; } case OP_ifnle: { newIf = InstructionFactory.createModifiedInstruction(OP_ifle, oldJump.getInstruction()); break; } case OP_iftrue: { newIf = InstructionFactory.createModifiedInstruction(OP_iffalse, oldJump.getInstruction()); break; } case OP_iffalse: { newIf = InstructionFactory.createModifiedInstruction(OP_iftrue, oldJump.getInstruction()); break; } } return newIf; }
/** * Perform optimizations on the instructions currently held in the window. This is so we don't do * the optimizations until we are sure a particular instruction was or was not the target of a * jump. If it was a target, then we don't do the optimizations as we don't have enough * information to determine all the ways control might flow to that instruction. */ private void processPreviousInstructions() { InstructionInfo last = previous(0); InstructionInfo secondLast = previous(1); // Check if we have a labelCurrent on the last instruction, or a labelNext on the previous // instruction if (last.getLabelCurrents().isEmpty() && secondLast.getLabelNexts().isEmpty()) { switch (last.getOpcode()) { case OP_convert_b: { op_convert_b(last); break; } case OP_convert_d: { op_convert_d(last); break; } case OP_convert_i: { op_convert_i(last); break; } case OP_convert_u: { op_convert_u(last); break; } case OP_convert_s: { op_convert_s(last); break; } case OP_getproperty: { op_getproperty(last); break; } case OP_iffalse: { op_iffalse(last); break; } case OP_iftrue: { op_iftrue(last); break; } case OP_pop: { op_pop(last); break; } case OP_nop: { delete(0); break; } case OP_getlocal: { OP_getlocal(last); break; } case OP_returnvoid: { op_returnvoid(last); break; } default: { break; } } } }