blob: b0bb33dc24a07f88615d0252fdec6847607fbc7a [file] [log] [blame]
/*
* Copyright (c) 1996, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.tools.tree;
import sun.tools.java.*;
/**
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*/
public final
class Vset implements Constants {
long vset; // DA bits for first 64 variables
long uset; // DU bits for first 64 variables
// The extension array is interleaved, consisting of alternating
// blocks of 64 DA bits followed by 64 DU bits followed by 64 DA
// bits, and so on.
long x[]; // extension array for more bits
// An infinite vector of zeroes or an infinite vector of ones is
// represented by a special value of the extension array.
//
// IMPORTANT: The condition 'this.x == fullX' is used as a marker for
// unreachable code, i.e., for a dead-end. We maintain the invariant
// that (this.x != fullX || (this.vset == -1 && this.uset == -1)).
// A dead-end has the peculiar property that all variables are both
// definitely assigned and definitely unassigned. We always force this
// condition to hold, even when the normal bitvector operations performed
// during DA/DU analysis would produce a different result. This supresses
// reporting of DA/DU errors in unreachable code.
static final long emptyX[] = new long[0]; // all zeroes
static final long fullX[] = new long[0]; // all ones
// For more thorough testing of long vset support, it is helpful to
// temporarily redefine this value to a smaller number, such as 1 or 2.
static final int VBITS = 64; // number of bits in vset (uset)
/**
* This is the Vset which reports all vars assigned and unassigned.
* This impossibility is degenerately true exactly when
* control flow cannot reach this point.
*/
// We distinguish a canonical dead-end value generated initially for
// statements that do not complete normally, making the next one unreachable.
// Once an unreachable statement is reported, a non-canonical dead-end value
// is used for subsequent statements in order to suppress redundant error
// messages.
static final Vset DEAD_END = new Vset(-1, -1, fullX);
/**
* Create an empty Vset.
*/
public Vset() {
this.x = emptyX;
}
private Vset(long vset, long uset, long x[]) {
this.vset = vset;
this.uset = uset;
this.x = x;
}
/**
* Create an copy of the given Vset.
* (However, DEAD_END simply returns itself.)
*/
public Vset copy() {
if (this == DEAD_END) {
return this;
}
Vset vs = new Vset(vset, uset, x);
if (x.length > 0) {
vs.growX(x.length); // recopy the extension vector
}
return vs;
}
private void growX(int length) {
long newX[] = new long[length];
long oldX[] = x;
for (int i = 0; i < oldX.length; i++) {
newX[i] = oldX[i];
}
x = newX;
}
/**
* Ask if this is a vset for a dead end.
* Answer true only for the canonical dead-end, DEAD_END.
* A canonical dead-end is produced only as a result of
* a statement that cannot complete normally, as specified
* by the JLS. Due to the special-case rules for if-then
* and if-then-else, this may fail to detect actual unreachable
* code that could easily be identified.
*/
public boolean isDeadEnd() {
return (this == DEAD_END);
}
/**
* Ask if this is a vset for a dead end.
* Answer true for any dead-end.
* Since 'clearDeadEnd' has no effect on this predicate,
* if-then and if-then-else are handled in the more 'obvious'
* and precise way. This predicate is to be preferred for
* dead code elimination purposes.
* (Presently used in workaround for bug 4173473 in MethodExpression.java)
*/
public boolean isReallyDeadEnd() {
return (x == fullX);
}
/**
* Replace canonical DEAD_END with a distinct but
* equivalent Vset. The bits are unaltered, but
* the result does not answer true to 'isDeadEnd'.
* <p>
* Used mostly for error recovery, but see
* 'IfStatement.check', where it is used to
* implement the special-case treatment of
* statement reachability for such statements.
*/
public Vset clearDeadEnd() {
if (this == DEAD_END) {
return new Vset(-1, -1, fullX);
}
return this;
}
/**
* Ask if a var is definitely assigned.
*/
public boolean testVar(int varNumber) {
long bit = (1L << varNumber);
if (varNumber >= VBITS) {
int i = (varNumber / VBITS - 1) * 2;
if (i >= x.length) {
return (x == fullX);
}
return (x[i] & bit) != 0;
} else {
return (vset & bit) != 0;
}
}
/**
* Ask if a var is definitely un-assigned.
* (This is not just the negation of testVar:
* It's possible for neither to be true.)
*/
public boolean testVarUnassigned(int varNumber) {
long bit = (1L << varNumber);
if (varNumber >= VBITS) {
// index "uset" extension
int i = ((varNumber / VBITS - 1) * 2) + 1;
if (i >= x.length) {
return (x == fullX);
}
return (x[i] & bit) != 0;
} else {
return (uset & bit) != 0;
}
}
/**
* Note that a var is definitely assigned.
* (Side-effecting.)
*/
public Vset addVar(int varNumber) {
if (x == fullX) {
return this;
}
// gen DA, kill DU
long bit = (1L << varNumber);
if (varNumber >= VBITS) {
int i = (varNumber / VBITS - 1) * 2;
if (i >= x.length) {
growX(i+1);
}
x[i] |= bit;
if (i+1 < x.length) {
x[i+1] &=~ bit;
}
} else {
vset |= bit;
uset &=~ bit;
}
return this;
}
/**
* Note that a var is definitely un-assigned.
* (Side-effecting.)
*/
public Vset addVarUnassigned(int varNumber) {
if (x == fullX) {
return this;
}
// gen DU, kill DA
long bit = (1L << varNumber);
if (varNumber >= VBITS) {
// index "uset" extension
int i = ((varNumber / VBITS - 1) * 2) + 1;
if (i >= x.length) {
growX(i+1);
}
x[i] |= bit;
x[i-1] &=~ bit;
} else {
uset |= bit;
vset &=~ bit;
}
return this;
}
/**
* Retract any assertion about the var.
* This operation is ineffective on a dead-end.
* (Side-effecting.)
*/
public Vset clearVar(int varNumber) {
if (x == fullX) {
return this;
}
long bit = (1L << varNumber);
if (varNumber >= VBITS) {
int i = (varNumber / VBITS - 1) * 2;
if (i >= x.length) {
return this;
}
x[i] &=~ bit;
if (i+1 < x.length) {
x[i+1] &=~ bit;
}
} else {
vset &=~ bit;
uset &=~ bit;
}
return this;
}
/**
* Join with another vset. This is set intersection.
* (Side-effecting.)
*/
public Vset join(Vset other) {
// Return a dead-end if both vsets are dead-ends.
// Return the canonical DEAD_END only if both vsets
// are the canonical DEAD_END. Otherwise, an incoming
// dead-end vset has already produced an error message,
// and is now assumed to be reachable.
if (this == DEAD_END) {
return other.copy();
}
if (other == DEAD_END) {
return this;
}
if (x == fullX) {
return other.copy();
}
if (other.x == fullX) {
return this;
}
// DA = DA intersection DA
// DU = DU intersection DU
vset &= other.vset;
uset &= other.uset;
if (other.x == emptyX) {
x = emptyX;
} else {
// ASSERT(otherX.length > 0);
long otherX[] = other.x;
int selfLength = x.length;
int limit = (otherX.length < selfLength) ? otherX.length : selfLength;
for (int i = 0; i < limit; i++) {
x[i] &= otherX[i];
}
// If self is longer than other, all remaining
// bits are implicitly 0. In the result, then,
// the remaining DA and DU bits are cleared.
for (int i = limit; i < selfLength; i++) {
x[i] = 0;
}
}
return this;
}
/**
* Add in the definite assignment bits of another vset,
* but join the definite unassignment bits. This unusual
* operation is used only for 'finally' blocks. The
* original vset 'this' is destroyed by this operation.
* (Part of fix for 4068688.)
*/
public Vset addDAandJoinDU(Vset other) {
// Return a dead-end if either vset is a dead end.
// If either vset is the canonical DEAD_END, the
// result is also the canonical DEAD_END.
if (this == DEAD_END) {
return this;
}
if (other == DEAD_END) {
return other;
}
if (x == fullX) {
return this;
}
if (other.x == fullX) {
return other.copy();
}
// DA = DA union DA'
// DU = (DU intersection DU') - DA'
vset = vset | other.vset;
uset = (uset & other.uset) & ~other.vset;
int selfLength = x.length;
long otherX[] = other.x;
int otherLength = otherX.length;
if (otherX != emptyX) {
// ASSERT(otherX.length > 0);
if (otherLength > selfLength) {
growX(otherLength);
}
int i = 0;
while (i < otherLength) {
x[i] |= otherX[i];
i++;
if (i == otherLength) break;
x[i] = ((x[i] & otherX[i]) & ~otherX[i-1]);
i++;
}
}
// If self is longer than other, all remaining
// bits are implicitly 0. In the result, then,
// the remaining DA bits are left unchanged, and
// the DU bits are all cleared. First, align
// index to the next block of DU bits (odd index).
for (int i = (otherLength | 1); i < selfLength; i += 2) {
x[i] = 0;
}
return this;
}
/**
* Construct a vset consisting of the DA bits of the first argument
* and the DU bits of the second argument. This is a higly unusual
* operation, as it implies a case where the flowgraph for DA analysis
* differs from that for DU analysis. It is only needed for analysing
* 'try' blocks. The result is a dead-end iff the first argument is
* dead-end. (Part of fix for 4068688.)
*/
public static Vset firstDAandSecondDU(Vset sourceDA, Vset sourceDU) {
// Note that reachability status is received via 'sourceDA' only!
// This is a consequence of the fact that reachability and DA
// analysis are performed on an identical flow graph, whereas the
// flowgraph for DU analysis differs in the case of a 'try' statement.
if (sourceDA.x == fullX) {
return sourceDA.copy();
}
long sourceDAx[] = sourceDA.x;
int lenDA = sourceDAx.length;
long sourceDUx[] = sourceDU.x;
int lenDU = sourceDUx.length;
int limit = (lenDA > lenDU) ? lenDA : lenDU;
long x[] = emptyX;
if (limit > 0) {
x = new long[limit];
for (int i = 0; i < lenDA; i += 2) {
x[i] = sourceDAx[i];
}
for (int i = 1; i < lenDU; i += 2) {
x[i] = sourceDUx[i];
}
}
return new Vset(sourceDA.vset, sourceDU.uset, x);
}
/**
* Remove variables from the vset that are no longer part of
* a context. Zeroes are stored past varNumber.
* (Side-effecting.)<p>
* However, if this is a dead end, keep it so.
* That is, leave an infinite tail of bits set.
*/
public Vset removeAdditionalVars(int varNumber) {
if (x == fullX) {
return this;
}
long bit = (1L << varNumber);
if (varNumber >= VBITS) {
int i = (varNumber / VBITS - 1) * 2;
if (i < x.length) {
x[i] &= (bit - 1);
if (++i < x.length) {
x[i] &= (bit - 1); // do the "uset" extension also
}
while (++i < x.length) {
x[i] = 0;
}
}
} else {
if (x.length > 0) {
x = emptyX;
}
vset &= (bit - 1);
uset &= (bit - 1);
}
return this;
}
/**
* Return one larger than the highest bit set.
*/
public int varLimit() {
long vset;
int result;
scan: {
for (int i = (x.length / 2) * 2; i >= 0; i -= 2) {
if (i == x.length) continue; // oops
vset = x[i];
if (i+1 < x.length) {
vset |= x[i+1]; // check the "uset" also
}
if (vset != 0) {
result = (i/2 + 1) * VBITS;
break scan;
}
}
vset = this.vset;
vset |= this.uset; // check the "uset" also
if (vset != 0) {
result = 0;
break scan;
} else {
return 0;
}
}
while (vset != 0) {
result += 1;
vset >>>= 1;
}
return result;
}
public String toString() {
if (this == DEAD_END)
return "{DEAD_END}";
StringBuilder sb = new StringBuilder("{");
int maxVar = VBITS * (1 + (x.length+1)/2);
for (int i = 0; i < maxVar; i++) {
if (!testVarUnassigned(i)) {
if (sb.length() > 1) {
sb.append(' ');
}
sb.append(i);
if (!testVar(i)) {
sb.append('?'); // not definitely unassigned
}
}
}
if (x == fullX) {
sb.append("...DEAD_END");
}
sb.append('}');
return sb.toString();
}
}