mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-03 12:35:58 +00:00
Patch from Igor:
Currently omj/TokenStream and omj/optimizer/Optimizer.java both contain code to convert number value to a wrapper object of smallest size. The attached patch moves this wrapping to Node constructor to avoid code duplication and eliminate special treatment of exact integers in Optimizer.java. The constant folding code in omj/optimizer/Optimizer.java currently always replaces x * 1, x - 0 by simply x which does not force the toNumber convertion, which is visible, for example, via typeof. For example, when running at optimization level 2, the following function f() { return "0" * 1; } print(typeof(f())); prints "string" instead of expected "number". The const_fold.patch fixes this via replacing x*1 by (+x) to force number convertion. It assumes that the patch with number wrapping changes is in place.
This commit is contained in:
parent
7ccac99fc4
commit
7c030bfefb
@ -54,8 +54,8 @@ public class Optimizer {
|
||||
{
|
||||
}
|
||||
|
||||
static final boolean DEBUG_OPTIMIZER = false;
|
||||
static final boolean DO_CONSTANT_FOLDING = true;
|
||||
private static final boolean DEBUG_OPTIMIZER = false;
|
||||
private static final boolean DO_CONSTANT_FOLDING = true;
|
||||
|
||||
static int blockCount = 0;
|
||||
|
||||
@ -776,255 +776,225 @@ public class Optimizer {
|
||||
/* at this point n has two children or more */
|
||||
int lt = lChild.getType();
|
||||
int rt = rChild.getType();
|
||||
|
||||
Node replace = null;
|
||||
|
||||
/* two or more children */
|
||||
switch(n.getType()){
|
||||
/* numerical addition and string concatenation */
|
||||
case TokenStream.ADD:
|
||||
/* number addition -- both numbers */
|
||||
if(lt == TokenStream.NUMBER && rt == TokenStream.NUMBER){
|
||||
if(lChild.getDatum() instanceof Double ||
|
||||
rChild.getDatum() instanceof Double){
|
||||
parent.replaceChild(n, new Node(TokenStream.NUMBER,
|
||||
lChild.getDouble() + rChild.getDouble()));
|
||||
}else{
|
||||
long longval = lChild.getLong() + rChild.getLong();
|
||||
switch (n.getType()) {
|
||||
case TokenStream.ADD:
|
||||
// numerical addition and string concatenation
|
||||
if(lt == TokenStream.NUMBER && rt == TokenStream.NUMBER) {
|
||||
// num + num
|
||||
replace = new Node(TokenStream.NUMBER,
|
||||
lChild.getDouble() + rChild.getDouble());
|
||||
}
|
||||
else if (lt == TokenStream.STRING && rt == TokenStream.STRING) {
|
||||
// string + string
|
||||
replace = new Node(TokenStream.STRING,
|
||||
lChild.getString() + rChild.getString());
|
||||
}
|
||||
else if (lt == TokenStream.STRING && rt == TokenStream.NUMBER) {
|
||||
// string + num
|
||||
replace = new Node(TokenStream.STRING,
|
||||
lChild.getString() +
|
||||
ScriptRuntime.numberToString(rChild.getDouble(), 10));
|
||||
}
|
||||
else if (lt == TokenStream.NUMBER && rt == TokenStream.STRING) {
|
||||
// num + string
|
||||
replace = new Node(TokenStream.STRING,
|
||||
ScriptRuntime.numberToString(lChild.getDouble(), 10) +
|
||||
rChild.getString());
|
||||
}
|
||||
// can't do anything if we don't know both types - since
|
||||
// 0 + object is supposed to call toString on the object and do
|
||||
// string concantenation rather than addition
|
||||
break;
|
||||
|
||||
parent.replaceChild(n, new Node(TokenStream.NUMBER,
|
||||
toSmallestType(longval)));
|
||||
}
|
||||
}else
|
||||
/* string + string */
|
||||
if(lt == TokenStream.STRING && rt == TokenStream.STRING){
|
||||
parent.replaceChild(n, new Node(TokenStream.STRING,
|
||||
lChild.getString() + rChild.getString()));
|
||||
}else
|
||||
/* string + num */
|
||||
if(lt == TokenStream.STRING && rt == TokenStream.NUMBER){
|
||||
parent.replaceChild(n, new Node(TokenStream.STRING, lChild.getString() +
|
||||
ScriptRuntime.numberToString(rChild.getDouble(), 10)));
|
||||
}else
|
||||
/* num + string */
|
||||
if(lt == TokenStream.NUMBER && rt == TokenStream.STRING){
|
||||
parent.replaceChild(n, new Node(TokenStream.STRING,
|
||||
ScriptRuntime.numberToString(lChild.getDouble(), 10) +
|
||||
rChild.getString()));
|
||||
}
|
||||
/* can't do anything if we don't know both types - since 0 + object
|
||||
is supposed to call toString on the object and do string concantenation
|
||||
rather than addition
|
||||
*/
|
||||
break;
|
||||
/* subtraction */
|
||||
case TokenStream.SUB:
|
||||
/* both numbers */
|
||||
if(lt == TokenStream.NUMBER && rt == TokenStream.NUMBER){
|
||||
if(lChild.getDatum() instanceof Double || rChild.getDatum() instanceof Double){
|
||||
parent.replaceChild(n, new Node(TokenStream.NUMBER,
|
||||
lChild.getDouble() -rChild.getDouble()));
|
||||
}else{
|
||||
long longval = lChild.getLong() - rChild.getLong();
|
||||
parent.replaceChild(n, new Node(TokenStream.NUMBER,
|
||||
toSmallestType(longval)));
|
||||
}
|
||||
}else
|
||||
/* first 0: 0-x -> -x */
|
||||
if(lt == TokenStream.NUMBER && lChild.getDouble() == 0){
|
||||
parent.replaceChild(n, new Node(TokenStream.UNARYOP, rChild,
|
||||
TokenStream.SUB));
|
||||
}else
|
||||
/* second 0: x-0 -> x */
|
||||
if(rt == TokenStream.NUMBER && rChild.getDouble() == 0){
|
||||
parent.replaceChild(n, lChild);
|
||||
}
|
||||
break;
|
||||
/* handle multiplication by zero and one */
|
||||
case TokenStream.MUL:
|
||||
/* both constants -- just multiply */
|
||||
if(lt == TokenStream.NUMBER && rt == TokenStream.NUMBER){
|
||||
/* at least one double */
|
||||
|
||||
if(lChild.getDatum() instanceof Double || rChild.getDatum() instanceof Double){
|
||||
parent.replaceChild(n, new Node(TokenStream.NUMBER,
|
||||
lChild.getDouble() * rChild.getDouble()));
|
||||
}else{
|
||||
long longval =
|
||||
lChild.getLong() *
|
||||
rChild.getLong();
|
||||
case TokenStream.SUB:
|
||||
// subtraction
|
||||
if (lt == TokenStream.NUMBER && rt == TokenStream.NUMBER) {
|
||||
//both numbers
|
||||
replace = new Node(TokenStream.NUMBER,
|
||||
lChild.getDouble() - rChild.getDouble());
|
||||
}
|
||||
else if (lt == TokenStream.NUMBER && lChild.getDouble() == 0) {
|
||||
// first 0: 0-x -> -x
|
||||
replace = new Node(TokenStream.UNARYOP,
|
||||
rChild, TokenStream.SUB);
|
||||
}
|
||||
else if (rt == TokenStream.NUMBER && rChild.getDouble() == 0) {
|
||||
//second 0: x - 0 -> +x
|
||||
// can not make simply x because x - 0 must be number
|
||||
replace = new Node(TokenStream.UNARYOP,
|
||||
lChild, TokenStream.ADD);
|
||||
}
|
||||
break;
|
||||
|
||||
parent.replaceChild(n, new Node(TokenStream.NUMBER,
|
||||
toSmallestType(longval)));
|
||||
}
|
||||
/* one of them is constant -- zero or one */
|
||||
/* can't do zero, since zero * infinity has to become a NaN */
|
||||
}else{
|
||||
if(lt == TokenStream.NUMBER){
|
||||
double ld = lChild.getDouble();
|
||||
case TokenStream.MUL:
|
||||
// multiplication
|
||||
if (lt == TokenStream.NUMBER && rt == TokenStream.NUMBER) {
|
||||
// both constants -- just multiply
|
||||
replace = new Node(TokenStream.NUMBER,
|
||||
lChild.getDouble() * rChild.getDouble());
|
||||
}
|
||||
else if (lt == TokenStream.NUMBER && lChild.getDouble() == 1) {
|
||||
// first 1: 1*x -> +x
|
||||
// not simply x to force number convertion
|
||||
replace = new Node(TokenStream.UNARYOP,
|
||||
rChild, TokenStream.ADD);
|
||||
}
|
||||
else if (rt == TokenStream.NUMBER && rChild.getDouble() == 1) {
|
||||
// second 1: x*1 -> +x
|
||||
// not simply x to force number convertion
|
||||
replace = new Node(TokenStream.UNARYOP,
|
||||
lChild, TokenStream.ADD);
|
||||
}
|
||||
// can't do x*0: Infinity * 0 gives NaN, not 0
|
||||
break;
|
||||
|
||||
if(ld == 1){
|
||||
parent.replaceChild(n, rChild);
|
||||
}
|
||||
}else
|
||||
if(rt == TokenStream.NUMBER){
|
||||
double rd = rChild.getDouble();
|
||||
case TokenStream.DIV:
|
||||
// division
|
||||
if (lt == TokenStream.NUMBER && rt == TokenStream.NUMBER) {
|
||||
// both constants -- just divide, trust Java to handle x/0
|
||||
replace = new Node(TokenStream.NUMBER,
|
||||
lChild.getDouble() / rChild.getDouble());
|
||||
}
|
||||
else if (rt == TokenStream.NUMBER && rChild.getDouble() == 1) {
|
||||
// second 1: x/1 -> +x
|
||||
// not simply x to force number convertion
|
||||
replace = new Node(TokenStream.UNARYOP,
|
||||
lChild, TokenStream.ADD);
|
||||
}
|
||||
break;
|
||||
|
||||
if(rd == 1){
|
||||
parent.replaceChild(n, lChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TokenStream.DIV:
|
||||
/* both constants -- just divide */
|
||||
if(lt == TokenStream.NUMBER && rt == TokenStream.NUMBER){
|
||||
/* at least one double */
|
||||
if((lChild.getDatum() instanceof Double || rChild.getDatum() instanceof Double)){
|
||||
double d = rChild.getDouble();
|
||||
case TokenStream.AND: {
|
||||
int isLDefined = isAlwaysDefinedBoolean(lChild);
|
||||
if (isLDefined == ALWAYS_FALSE_BOOLEAN) {
|
||||
// if the first one is false, replace with FALSE
|
||||
if (!IRFactory.hasSideEffects(rChild)) {
|
||||
replace = new Node(TokenStream.PRIMARY,
|
||||
TokenStream.FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
if(d == 0)
|
||||
return; // division by zero
|
||||
else
|
||||
parent.replaceChild(n, new Node(TokenStream.NUMBER,
|
||||
lChild.getDouble() / d));
|
||||
}else{
|
||||
int d = rChild.getInt();
|
||||
int isRDefined = isAlwaysDefinedBoolean(rChild);
|
||||
if (isRDefined == ALWAYS_FALSE_BOOLEAN) {
|
||||
// if the second one is false, replace with FALSE
|
||||
if (!IRFactory.hasSideEffects(lChild)) {
|
||||
replace = new Node(TokenStream.PRIMARY,
|
||||
TokenStream.FALSE);
|
||||
}
|
||||
}
|
||||
else if (isRDefined == ALWAYS_TRUE_BOOLEAN) {
|
||||
// if second is true, set to first
|
||||
replace = lChild;
|
||||
}
|
||||
|
||||
if(d == 0)
|
||||
return; // division by zero
|
||||
else{
|
||||
long longval =
|
||||
lChild.getLong() /
|
||||
rChild.getLong();
|
||||
if (isLDefined == ALWAYS_TRUE_BOOLEAN) {
|
||||
// if first is true, set to second
|
||||
replace = rChild;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
parent.replaceChild(n, new Node(TokenStream.NUMBER,
|
||||
toSmallestType(longval)));
|
||||
}
|
||||
}
|
||||
/* 0/x , x!=0 */
|
||||
}else
|
||||
/* x/1 */
|
||||
if((rt == TokenStream.NUMBER) && // added cast -roger
|
||||
rChild.getDouble()==1){
|
||||
parent.replaceChild(n, lChild);
|
||||
}
|
||||
break;
|
||||
case TokenStream.AND:
|
||||
/* if the first one is false, replace with FALSE */
|
||||
if(
|
||||
((lt == TokenStream.PRIMARY && lChild.getInt() == TokenStream.NULL) ||
|
||||
(lt == TokenStream.PRIMARY && lChild.getInt() == TokenStream.UNDEFINED)) &&
|
||||
!IRFactory.hasSideEffects(rChild)
|
||||
){
|
||||
parent.replaceChild(n, new Node(TokenStream.PRIMARY, TokenStream.FALSE));
|
||||
}else
|
||||
/* if the second one is false, replace with FALSE */
|
||||
if(
|
||||
((rt==TokenStream.PRIMARY && rChild.getInt()==TokenStream.NULL) ||
|
||||
(rt==TokenStream.PRIMARY && rChild.getInt()==TokenStream.UNDEFINED)) &&
|
||||
!IRFactory.hasSideEffects(lChild)
|
||||
){
|
||||
parent.replaceChild(n, new Node(TokenStream.PRIMARY, TokenStream.FALSE));
|
||||
}else
|
||||
/* if first is true, set to second */
|
||||
if(
|
||||
(lt == TokenStream.PRIMARY && lChild.getInt() == TokenStream.TRUE) ||
|
||||
(lt == TokenStream.NUMBER && lChild.getDouble() != 0)
|
||||
){
|
||||
parent.replaceChild(n, rChild);
|
||||
}else
|
||||
/* if second is true, set to first */
|
||||
if(
|
||||
(rt == TokenStream.PRIMARY && rChild.getInt() == TokenStream.TRUE) ||
|
||||
(rt == TokenStream.NUMBER && rChild.getDouble() != 0)
|
||||
){
|
||||
parent.replaceChild(n, lChild);
|
||||
}
|
||||
break;
|
||||
case TokenStream.OR:
|
||||
/* if first is false, set to second */
|
||||
if(
|
||||
(lt == TokenStream.PRIMARY && lChild.getInt() == TokenStream.NULL) ||
|
||||
(lt == TokenStream.PRIMARY && lChild.getInt() == TokenStream.UNDEFINED) ||
|
||||
(lt == TokenStream.PRIMARY && lChild.getInt() == TokenStream.FALSE) ||
|
||||
(lt == TokenStream.NUMBER && lChild.getDouble() == 0)
|
||||
){
|
||||
parent.replaceChild(n, rChild);
|
||||
}else
|
||||
/* if second is true, set to first */
|
||||
if(
|
||||
(rt == TokenStream.PRIMARY && rChild.getInt() == TokenStream.NULL) ||
|
||||
(rt == TokenStream.PRIMARY && rChild.getInt() == TokenStream.UNDEFINED) ||
|
||||
(rt == TokenStream.PRIMARY && rChild.getInt() == TokenStream.FALSE) ||
|
||||
(rt == TokenStream.NUMBER && rChild.getDouble() == 0)
|
||||
){
|
||||
parent.replaceChild(n, lChild);
|
||||
}else
|
||||
/* if first one is true, replace with TRUE */
|
||||
if(
|
||||
((lt == TokenStream.PRIMARY && lChild.getInt() == TokenStream.TRUE) ||
|
||||
(lt == TokenStream.NUMBER && lChild.getDouble() != 0)) &&
|
||||
!IRFactory.hasSideEffects(rChild)
|
||||
){
|
||||
parent.replaceChild(n, new Node(TokenStream.PRIMARY, TokenStream.TRUE));
|
||||
}else
|
||||
/* if second one is true, replace with TRUE */
|
||||
if(
|
||||
((rt == TokenStream.PRIMARY && rChild.getInt() == TokenStream.TRUE) ||
|
||||
(rt == TokenStream.NUMBER && rChild.getDouble() != 0)) &&
|
||||
!IRFactory.hasSideEffects(lChild)
|
||||
){
|
||||
parent.replaceChild(n, new Node(TokenStream.PRIMARY, TokenStream.TRUE));
|
||||
}
|
||||
break;
|
||||
case TokenStream.BLOCK:
|
||||
/* if statement */
|
||||
if(lChild.getType() == TokenStream.IFNE){
|
||||
Node condition = lChild.getFirstChild();
|
||||
/* if(false) -> replace by the else clause if it exists */
|
||||
if(condition.getType() == TokenStream.PRIMARY){
|
||||
int id = condition.getInt();
|
||||
if(id == TokenStream.FALSE||id == TokenStream.NULL||id == TokenStream.UNDEFINED){
|
||||
Node elseClause = null;
|
||||
try{
|
||||
elseClause =
|
||||
rChild.getNextSibling().getNextSibling().getNextSibling().getFirstChild();
|
||||
}catch(Exception e){
|
||||
return;
|
||||
}
|
||||
case TokenStream.OR: {
|
||||
int isLDefined = isAlwaysDefinedBoolean(lChild);
|
||||
if (isLDefined == ALWAYS_TRUE_BOOLEAN) {
|
||||
// if the first one is true, replace with TRUE
|
||||
if (!IRFactory.hasSideEffects(rChild)) {
|
||||
replace = new Node(TokenStream.PRIMARY,
|
||||
TokenStream.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if(elseClause != null){
|
||||
parent.replaceChild(n, elseClause);
|
||||
}
|
||||
/* if(true) -> replace by the then clause if it exists */
|
||||
}else
|
||||
if((condition.getType() == TokenStream.PRIMARY && condition.getInt() == TokenStream.TRUE) ||
|
||||
(condition.getType() == TokenStream.NUMBER && condition.getDouble() != 0))
|
||||
{
|
||||
if(rChild.getType() == TokenStream.BLOCK){
|
||||
parent.replaceChild(n, rChild.getFirstChild());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
int isRDefined = isAlwaysDefinedBoolean(rChild);
|
||||
if (isRDefined == ALWAYS_TRUE_BOOLEAN) {
|
||||
// if the second one is true, replace with TRUE
|
||||
if (!IRFactory.hasSideEffects(lChild)) {
|
||||
replace = new Node(TokenStream.PRIMARY,
|
||||
TokenStream.TRUE);
|
||||
}
|
||||
}
|
||||
else if (isRDefined == ALWAYS_FALSE_BOOLEAN) {
|
||||
// if second is false, set to first
|
||||
replace = lChild;
|
||||
}
|
||||
|
||||
if (isLDefined == ALWAYS_FALSE_BOOLEAN) {
|
||||
// if first is false, set to second
|
||||
replace = rChild;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TokenStream.BLOCK:
|
||||
/* if statement */
|
||||
if (lChild.getType() == TokenStream.IFNE) {
|
||||
Node condition = lChild.getFirstChild();
|
||||
int definedBoolean = isAlwaysDefinedBoolean(condition);
|
||||
|
||||
if (definedBoolean == ALWAYS_FALSE_BOOLEAN) {
|
||||
//if(false) -> replace by the else clause if it exists
|
||||
Node next1 = rChild.getNextSibling();
|
||||
if (next1 != null) {
|
||||
Node next2 = next1.getNextSibling();
|
||||
if (next2 != null) {
|
||||
Node next3 = next2.getNextSibling();
|
||||
if (next3 != null) {
|
||||
Node elseClause = next3.getFirstChild();
|
||||
if (elseClause != null) {
|
||||
replace = elseClause;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (definedBoolean == ALWAYS_TRUE_BOOLEAN) {
|
||||
if (rChild.getType() == TokenStream.BLOCK) {
|
||||
replace = rChild.getFirstChild();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}//switch
|
||||
}
|
||||
|
||||
if (replace != null) {
|
||||
parent.replaceChild(n, replace);
|
||||
}
|
||||
|
||||
/*
|
||||
Converts a long to the smalles possible integral type.
|
||||
*/
|
||||
private static Number toSmallestType(long longval){
|
||||
// We generate the smallest possible type here
|
||||
if (Byte.MIN_VALUE <= longval && longval <= Byte.MAX_VALUE)
|
||||
return new Byte((byte)longval);
|
||||
else if (Short.MIN_VALUE <= longval &&
|
||||
longval <= Short.MAX_VALUE)
|
||||
return new Short((short)longval);
|
||||
else if (Integer.MIN_VALUE <= longval &&
|
||||
longval <= Integer.MAX_VALUE)
|
||||
return new Integer((int)longval);
|
||||
else
|
||||
return new Double(longval);
|
||||
}
|
||||
|
||||
private static final int ALWAYS_TRUE_BOOLEAN = 1;
|
||||
private static final int ALWAYS_FALSE_BOOLEAN = -1;
|
||||
|
||||
// Check if Node always mean true or false in boolean context
|
||||
private static int isAlwaysDefinedBoolean(Node node) {
|
||||
int result = 0;
|
||||
int type = node.getType();
|
||||
if (type == TokenStream.PRIMARY) {
|
||||
int id = node.getInt();
|
||||
if (id == TokenStream.FALSE || id == TokenStream.NULL
|
||||
|| id == TokenStream.UNDEFINED)
|
||||
{
|
||||
result = ALWAYS_FALSE_BOOLEAN;
|
||||
}
|
||||
else if (id == TokenStream.TRUE) {
|
||||
result = ALWAYS_TRUE_BOOLEAN;
|
||||
}
|
||||
}
|
||||
else if (type == TokenStream.NUMBER) {
|
||||
double num = node.getDouble();
|
||||
if (num == 0) {
|
||||
// Is it neccessary to check for -0.0 here?
|
||||
if (1 / num > 0) {
|
||||
result = ALWAYS_FALSE_BOOLEAN;
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = ALWAYS_TRUE_BOOLEAN;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void replaceVariableAccess(Node n, VariableTable theVariables)
|
||||
|
Loading…
x
Reference in New Issue
Block a user