Bug 433939: updating outparams check for latest Treehydra, r=bsmedberg

This commit is contained in:
David Mandelin 2008-06-05 11:21:29 -07:00
Родитель 6fc3274f41
Коммит 9704ef1a8b
1 изменённых файлов: 51 добавлений и 175 удалений

Просмотреть файл

@ -10,6 +10,8 @@ include('unstable/adts.js');
include('unstable/analysis.js');
include('unstable/esp.js');
include('unstable/liveness.js');
let Zero_NonZero = {};
include('unstable/zero_nonzero.js', Zero_NonZero);
include('mayreturn.js');
@ -148,7 +150,8 @@ function OutparamCheck(cfg, psem_list, outparam_list, retvar, retvar_set, finall
print(" " + expr_display(v));
}
}
ESP.Analysis.call(this, cfg, this.psvar_list, av.BOTTOM, av.meet, trace);
this.zeroNonzero = new Zero_NonZero.Zero_NonZero();
ESP.Analysis.call(this, cfg, this.psvar_list, av.meet, trace);
}
// Abstract values for outparam check
@ -165,10 +168,11 @@ AbstractValue.prototype.toString = function() {
return this.name + ' (' + this.ch + ')';
}
let avspec = [
// General-purpose abstract values
[ 'BOTTOM', '.' ], // any value, also used for dead vars
AbstractValue.prototype.toShortString = function() {
return this.ch;
}
let avspec = [
// Abstract values for outparam contents write status
[ 'NULL', 'x' ], // is a null pointer
[ 'NOT_WRITTEN', '-' ], // not written
@ -180,10 +184,6 @@ let avspec = [
// in last position), so if there is an error with it not being written,
// we can give a hint about the possible outparam in the warning.
[ 'MAYBE_WRITTEN', '?' ], // written if possible outparam is one
// Abstract values for rvs
[ 'ZERO', '0' ], // zero value
[ 'NONZERO', '1' ] // nonzero value
];
let av = {};
@ -191,11 +191,13 @@ for each (let [name, ch] in avspec) {
av[name] = new AbstractValue(name, ch);
}
av.ZERO = Zero_NonZero.Lattice.ZERO;
av.NONZERO = Zero_NonZero.Lattice.NONZERO;
/*
av.ZERO.negation = av.NONZERO;
av.NONZERO.negation = av.ZERO;
let cachedAVs = {};
// Abstract values for int constants. We use these to figure out feasible
// paths in the presence of GCC finally_tmp-controlled switches.
function makeIntAV(v) {
@ -207,13 +209,16 @@ function makeIntAV(v) {
ans.int_val = v;
return ans;
}
*/
let cachedAVs = {};
// Abstract values for pointers that contain a copy of an outparam
// pointer. We use these to figure out writes to a casted copy of
// an outparam passed to another method.
function makeOutparamAV(v) {
let key = 'outparam_' + DECL_UID(v);
if (cachedAVs.hasOwnProperty(key)) return cachedAVs[key];
if (key in cachedAVs) return cachedAVs[key];
let ans = cachedAVs[key] =
new AbstractValue('OUTPARAM:' + expr_display(v), 'P');
@ -230,31 +235,14 @@ av.intVal = function(v) {
/** Meet function for our abstract values. */
av.meet = function(v1, v2) {
// Important for following cases -- as output, undefined means top here.
if (v1 == undefined) v1 = av.BOTTOM;
if (v2 == undefined) v2 = av.BOTTOM;
// These cases apply for any lattice.
if (v1 == av.BOTTOM) return v2;
if (v2 == av.BOTTOM) return v1;
if (v1 == v2) return v1;
// At this point we know v1 != v2.
switch (v1) {
case av.ZERO:
return av.intVal(v2) == 0 ? v2 : undefined;
case av.NONZERO:
// Return a nonempty meet only if the other value is an integer
// and is nonzero.
let iv2 = av.intVal(v2);
return iv2 != undefined && iv2 != 0 ? v2 : undefined;
default:
let iv = av.intVal(v1);
if (iv == 0) return v2 == av.ZERO ? v1 : undefined;
if (iv != undefined) return v2 == av.NONZERO ? v1 : undefined;
return undefined;
}
}
let values = [v1,v2]
if (values.indexOf(av.LOCKED) != -1
|| values.indexOf(av.UNLOCKED) != -1)
return ESP.NOT_REACHED;
return Zero_NonZero.meet(v1, v2)
};
// Outparam check analysis
OutparamCheck.prototype = new ESP.Analysis;
@ -262,11 +250,17 @@ OutparamCheck.prototype = new ESP.Analysis;
OutparamCheck.prototype.startValues = function() {
let ans = create_decl_map();
for each (let p in this.psvar_list) {
ans.put(p, this.outparams.has(p) ? av.NOT_WRITTEN : av.BOTTOM);
ans.put(p, this.outparams.has(p) ? av.NOT_WRITTEN : ESP.TOP);
}
return ans;
}
OutparamCheck.prototype.split = function(vbl, v) {
// Can't happen for current version of ESP, but could change
if (v != ESP.TOP) throw new Error("not implemented");
return [ av.ZERO, av.NONZERO ];
}
OutparamCheck.prototype.updateEdgeState = function(e) {
e.state.keepOnly(e.dest.keepVars);
}
@ -283,93 +277,17 @@ OutparamCheck.prototype.flowState = function(isn, state) {
case COND_EXPR:
// This gets handled by flowStateCond instead, has no exec effect
break;
case RETURN_EXPR:
let op = isn.operands()[0];
if (op) this.processAssign(isn.operands()[0], state);
break;
case LABEL_EXPR:
case RESX_EXPR:
case ASM_EXPR:
// NOPs for us
break;
default:
print(TREE_CODE(isn));
throw new Error("ni");
this.zeroNonzero.flowState(isn, state);
}
}
OutparamCheck.prototype.flowStateCond = function(isn, truth, state) {
switch (TREE_CODE(isn)) {
case COND_EXPR:
this.flowStateIf(isn, truth, state);
break;
case SWITCH_EXPR:
this.flowStateSwitch(isn, truth, state);
break;
default:
throw new Error("ni " + TREE_CODE(isn));
}
this.zeroNonzero.flowStateCond(isn, truth, state);
}
OutparamCheck.prototype.flowStateIf = function(isn, truth, state) {
let exp = TREE_OPERAND(isn, 0);
if (DECL_P(exp)) {
this.filter(state, exp, av.NONZERO, truth, isn);
return;
}
switch (TREE_CODE(exp)) {
case EQ_EXPR:
case NE_EXPR:
// Handle 'x op <int lit>' pattern only
let op1 = TREE_OPERAND(exp, 0);
let op2 = TREE_OPERAND(exp, 1);
if (expr_literal_int(op1) != undefined) {
[op1,op2] = [op2,op1];
}
if (!DECL_P(op1)) break;
if (expr_literal_int(op2) != 0) break;
let val = TREE_CODE(exp) == EQ_EXPR ? av.ZERO : av.NONZERO;
this.filter(state, op1, val, truth, isn);
break;
default:
// Don't care about anything else.
}
};
OutparamCheck.prototype.flowStateSwitch = function(isn, truth, state) {
let exp = TREE_OPERAND(isn, 0);
if (DECL_P(exp)) {
if (truth != null) {
this.filter(state, exp, makeIntAV(truth), true, isn);
}
return;
}
throw new Error("ni");
}
// Apply a filter to the state. We need to special case it for this analysis
// because outparams only care about being a NULL pointer.
OutparamCheck.prototype.filter = function(state, vbl, val, truth, blame) {
if (truth != true && truth != false) throw new Error("ni " + truth);
if (this.outparams.has(vbl)) {
// We do need to check for true and false here because other values
// could get passed in from switches.
if (truth == true && val == av.ZERO || truth == false && val == av.NONZERO) {
state.assignValue(vbl, av.NULL, blame);
}
} else {
if (truth == false) {
val = val.negation;
}
state.filter(vbl, val, blame);
}
};
// For any outparams-specific semantics, we handle it here and then
// return. Otherwise we delegate to the zero-nonzero analysis.
OutparamCheck.prototype.processAssign = function(isn, state) {
let lhs = isn.operands()[0];
let rhs = isn.operands()[1];
@ -380,40 +298,20 @@ OutparamCheck.prototype.processAssign = function(isn, state) {
rhs = rhs.operands()[0];
}
if (DECL_P(rhs)) {
if (this.outparams.has(rhs)) {
if (DECL_P(rhs) && this.outparams.has(rhs)) {
// Copying an outparam pointer. We have to remember this so that
// if it is assigned thru later, we pick up the write.
state.assignValue(lhs, makeOutparamAV(rhs), isn);
} else {
state.assign(lhs, rhs, isn);
}
return
return;
}
switch (TREE_CODE(rhs)) {
case INTEGER_CST:
if (this.outparams.has(lhs)) {
warning("assigning to outparam pointer");
} else {
// Need to know the exact int value for finally_tmp.
if (is_finally_tmp(lhs)) {
let v = TREE_INT_CST_LOW(rhs);
state.assignValue(lhs, makeIntAV(v), isn);
} else {
let value = expr_literal_int(rhs) == 0 ? av.ZERO : av.NONZERO;
state.assignValue(lhs, value, isn);
}
return;
}
break;
case NE_EXPR: {
// We only care about gcc-generated x != 0 for conversions and such.
let [op1, op2] = rhs.operands();
if (DECL_P(op1) && expr_literal_int(op2) == 0) {
state.assign(lhs, op1, isn);
}
}
break;
case EQ_EXPR: {
// We only care about testing outparams for NULL (and then not writing)
let [op1, op2] = rhs.operands();
@ -425,6 +323,7 @@ OutparamCheck.prototype.processAssign = function(isn, state) {
s2.assignValue(lhs, av.ZERO, isn);
return [s1, s2];
});
return;
}
}
break;
@ -440,40 +339,18 @@ OutparamCheck.prototype.processAssign = function(isn, state) {
} else {
this.processCall(lhs, rhs, isn, state);
}
break;
// Stuff we don't analyze -- just kill the LHS info
case ADDR_EXPR:
case POINTER_PLUS_EXPR:
case ARRAY_REF:
case COMPONENT_REF:
case INDIRECT_REF:
case FILTER_EXPR:
case EXC_PTR_EXPR:
case CONSTRUCTOR:
case REAL_CST:
case STRING_CST:
case CONVERT_EXPR:
case TRUTH_NOT_EXPR:
case TRUTH_XOR_EXPR:
case BIT_FIELD_REF:
state.remove(lhs);
break;
default:
if (UNARY_CLASS_P(rhs) || BINARY_CLASS_P(rhs) || COMPARISON_CLASS_P(rhs)) {
state.remove(lhs);
break;
}
print(TREE_CODE(rhs));
throw new Error("ni");
return;
}
// Nothing special -- delegate
this.zeroNonzero.processAssign(isn, state);
return;
}
switch (TREE_CODE(lhs)) {
case INDIRECT_REF:
// Writing to an outparam. We want to try to figure out if we're writing NULL.
// Writing to an outparam. We want to try to figure out if we're
// writing NULL.
let e = TREE_OPERAND(lhs, 0);
if (this.outparams.has(e)) {
if (expr_literal_int(rhs) == 0) {
@ -509,9 +386,9 @@ OutparamCheck.prototype.processAssign = function(isn, state) {
OutparamCheck.prototype.processTest = function(lhs, call, val, blame, state) {
let arg = call_arg(call, 0);
if (DECL_P(arg)) {
state.predicate(lhs, val, arg, blame);
this.zeroNonzero.predicate(state, lhs, val, arg, blame);
} else {
state.assignValue(lhs, av.BOTTOM, blame);
state.assignValue(lhs, ESP.TOP, blame);
}
};
@ -650,14 +527,13 @@ OutparamCheck.prototype.checkSubstate = function(isvoid, fndecl, ss) {
} else {
let [succ, fail] = ret_coding(fndecl);
let rv = ss.get(this.retvar);
switch (rv) {
case succ:
// We want to check if the abstract value of the rv is entirely
// contained in the success or failure condition.
if (av.meet(rv, succ) == rv) {
this.checkSubstateSuccess(ss);
break;
case fail:
} else if (av.meet(rv, fail) == rv) {
this.checkSubstateFailure(ss);
break;
default:
} else {
// This condition indicates a bug in outparams.js. We'll just
// warn so we don't break static analysis builds.
warning("Outparams checker cannot determine rv success/failure",