void BlockModset::ProcessUpdatedLval(BlockMemory *mcfg, Exp *lval, Exp *kind, bool consider_assign, bool from_call) { if (!m_modset_list) m_modset_list = new Vector<PointValue>(); if (!m_assign_list) m_assign_list = new Vector<GuardAssign>(); // use the ID from the memory rather than the ID from this modset, // as this modset has a temporary ID. BlockId *use_id = mcfg->GetId(); // hold a reference on the lvalue, drop it at exit. lval->IncRef(); goto entry; // exit label up here to avoid goofy gcc 'crosses initialization' errors. exit: lval->DecRef(); return; entry: ModsetIncludeVisitor visitor(use_id->Kind(), from_call); // use the base buffer if we are updating a terminator. if (kind) { if (ExpTerminate *nkind = kind->IfTerminate()) { // ignore field terminator modsets, these are pretty much useless. if (nkind->GetTerminateTest()->IsFld()) goto exit; Exp *new_lval = mcfg->GetBaseBuffer(lval, nkind->GetStrideType()); lval->DecRef(); lval = new_lval; visitor.buffer = true; } } visitor.SetFoundLval(true); lval->DoVisit(&visitor); visitor.SetFoundLval(false); visitor.rvalue = true; if (visitor.excluded) goto exit; Variable *root = lval->Root(); if (!root) goto exit; // argument lvals with zero dereferences are additionally excluded. // these updates are local to the current function. also look for updates // to 'this' which can come from frontend parse/tcheck errors. if (use_id->Kind() == B_Function && (root->Kind() == VK_Arg || root->Kind() == VK_This)) { if (lval->DrfCount() == 0) goto exit; } // add to the modset if this is not the function's return value. we don't // need to explicitly add the return value as it is special cased by // BlockMemory and is always treated as modified. if (root->Kind() != VK_Return) { lval->IncRef(); if (kind) kind->IncRef(); AddModset(lval, kind); } if (!consider_assign) goto exit; // should only be generating direct assignments for Drf() updates. Assert(kind == NULL); // don't generate assignments for loop iterations. if (use_id->Kind() != B_Function) goto exit; // don't generate assignments for global variables. if (root->IsGlobal()) goto exit; // see if we already have assignments for this lval. for (size_t ind = 0; ind < GetAssignCount(); ind++) { if (m_assign_list->At(ind).left == lval) goto exit; } PPoint exit_point = mcfg->GetCFG()->GetExitPoint(); if (!exit_point) goto exit; // temporary vector to hold assignments. if we find a problem with // the assigns (bad lvalue, etc.) we will bail out and clear this list. Vector<GuardAssign> assigns; GuardExpVector exit_values; mcfg->GetValComplete(lval, NULL, exit_point, &exit_values, true); // cases we will currently generate assignments for. in all cases // the rvalue and guard must be functionally determined from the arguments. // 1. one or two possible values for the lval. // 2. all values for the lval are constants. for (size_t ind = 0; ind < exit_values.Size(); ind++) { const GuardExp &val = exit_values[ind]; if (val.guard->Size() >= ASSIGN_BIT_CUTOFF) goto exit; if (exit_values.Size() <= 2) { if (val.exp->TermCountExceeds(ASSIGN_EXP_CUTOFF)) goto exit; } else { if (!val.exp->IsInt()) goto exit; } } for (size_t ind = 0; ind < exit_values.Size(); ind++) { const GuardExp &val = exit_values[ind]; val.exp->DoVisit(&visitor); val.guard->DoVisit(&visitor); lval->IncRef(); val.IncRef(); assigns.PushBack(GuardAssign(lval, val.exp, val.guard)); } if (visitor.excluded) { for (size_t ind = 0; ind < assigns.Size(); ind++) { const GuardAssign &gasn = assigns[ind]; gasn.left->DecRef(); gasn.right->DecRef(); gasn.guard->DecRef(); } } else { for (size_t ind = 0; ind < assigns.Size(); ind++) { const GuardAssign &gasn = assigns[ind]; AddAssign(gasn.left, gasn.right, gasn.guard); } } goto exit; }
void Visit(Exp *exp) { if (exp->IsVar()) { Variable *root = exp->AsVar()->GetVariable(); // allow global exps when the assign was not generated from a call. if (root->IsGlobal() && (!from_call || rvalue)) return; if (kind == B_Function) { // only consider exps derived from arguments, 'this' and the return // variable. note that we will special case the return var later in the // modset as it is automatically handled by BlockMemory, but we don't // exclude it here so that we can get exact side effects for it // if possible. if (root->Kind() != VK_Arg && root->Kind() != VK_This && root->Kind() != VK_Return) excluded = exp; // watch for taking the address of function arguments and leaving them // accessible in the caller. weird! if (root->Kind() == VK_Arg && !FoundLval()) excluded = exp; } else { // only consider exps derived from arguments, 'this' and locals. if (root->Kind() != VK_Arg && root->Kind() != VK_This && root->Kind() != VK_Local) excluded = exp; } return; } if (exp->IsDrf()) { if (!FoundLval()) return; if (!rvalue) { // limits on the number of dereferences in expressions. size_t max_derefs = buffer ? 2 : 1; if (exp->DrfCount() > max_derefs) excluded = exp; } return; } if (exp->IsIndex() && !rvalue) { // indexes are allowed only for assignment rvalues. excluded = exp; return; } if (exp->IsFld() && exp->FldCount() > 6) { // limit on the number of fields in expressions. this cuts off infinite // recursion during modset computation when the program does funny casts. excluded = exp; return; } if (exp->IsRfld()) { // all rfld expressions are excluded. these are usually here because // of indirect calls which could operate on a variety of structures // (this happens in both C and C++). excluded = exp; return; } if (exp->IsClobber() || exp->IsVal()) { excluded = exp; return; } }