std::vector<CallExpr *> Utils::callListForChain(CallExpr *lastCallExpr) { if (!lastCallExpr) return {}; const bool isOperator = isa<CXXOperatorCallExpr>(lastCallExpr); vector<CallExpr *> callexprs = { lastCallExpr }; Stmt *s = lastCallExpr; do { const int childCount = std::distance(s->child_begin(), s->child_end()); if (isOperator && childCount > 1) { // for operator case, the chained call childs are in the second child s = *(++s->child_begin()); } else { s = childCount > 0 ? *s->child_begin() : nullptr; } if (s) { CallExpr *callExpr = dyn_cast<CallExpr>(s); if (callExpr && callExpr->getCalleeDecl()) { callexprs.push_back(callExpr); } else if (MemberExpr *memberExpr = dyn_cast<MemberExpr>(s)) { if (isa<FieldDecl>(memberExpr->getMemberDecl())) break; // accessing a public member via . or -> breaks the chain } } } while (s); return callexprs; }
void ImplicitCasts::VisitStmt(clang::Stmt *stmt) { if (isMacroToIgnore(stmt->getLocStart())) return; // Lets check only in function calls. Otherwise there are too many false positives, it's common // to implicit cast to bool when checking pointers for validity, like if (ptr) CallExpr *callExpr = dyn_cast<CallExpr>(stmt); CXXConstructExpr *ctorExpr = dyn_cast<CXXConstructExpr>(stmt); if (!callExpr && !ctorExpr) return; FunctionDecl *func = callExpr ? callExpr->getDirectCallee() : ctorExpr->getConstructor(); if (isInterestingFunction(func)) { // Check pointer->bool implicit casts iterateCallExpr<CallExpr>(callExpr, this); iterateCallExpr<CXXConstructExpr>(ctorExpr, this); } else if (isInterestingFunction2(func)) { // Check bool->int implicit casts iterateCallExpr2<CallExpr>(callExpr, this, m_parentMap); iterateCallExpr2<CXXConstructExpr>(ctorExpr, this, m_parentMap); } }
// // Determine the index type for a ParamForLoop. // // This implementation creates a range with low/high values and then // asks for its type. // Type* ParamForLoop::indexType() { SymExpr* lse = lowExprGet(); SymExpr* hse = highExprGet(); CallExpr* range = new CallExpr("chpl_build_bounded_range", lse->copy(), hse->copy()); Type* idxType = 0; insertBefore(range); resolveCall(range); if (FnSymbol* sym = range->isResolved()) { resolveFormals(sym); DefExpr* formal = toDefExpr(sym->formals.get(1)); if (toArgSymbol(formal->sym)->typeExpr) idxType = toArgSymbol(formal->sym)->typeExpr->body.tail->typeInfo(); else idxType = formal->sym->type; range->remove(); } else { INT_FATAL("unresolved range"); } return idxType; }
// // Returns true if 'ref' is only used in the following cases: // // 1) Used in 'defCall', usually a PRIM_ADDR_OF or PRIM_SET_REFERENCE // // 2) Passed to an initializer in the 'this' slot. // // 3) Initialized from a function call, as represented by return-by-ref. // // An initializer can thwart detection of a 'const' thing because it takes the // "this" argument by ref. Normally such a pattern would cause us to assume // the variable was not const, but in this case we know it is a single def. // // defCall: The CallExpr where 'ref' is set from a PRIM_ADDR_OF or // PRIM_SET_REFERENCE. This call will be ignored while considering uses of // the 'ref' Symbol. // static bool onlyUsedForInitOrRetarg(Symbol* ref, CallExpr* defCall) { bool seenInitOrRetarg = false; INT_ASSERT(ref->isRef()); INT_ASSERT(defCall != NULL); for_SymbolSymExprs(use, ref) { if (use->parentExpr == defCall) continue; CallExpr* call = toCallExpr(use->parentExpr); if (FnSymbol* fn = call->resolvedFunction()) { ArgSymbol* form = actual_to_formal(use); if (passedToInitOrRetarg(use, form, fn)) { INT_ASSERT(!seenInitOrRetarg); // init/retarg happens just once seenInitOrRetarg = true; } else { return false; // a use other than what we are looking for } } } return true; }
BlockStmt* DoWhileStmt::build(Expr* cond, BlockStmt* body) { VarSymbol* condVar = newTemp(); CallExpr* condTest = new CallExpr("_cond_test", cond); LabelSymbol* continueLabel = new LabelSymbol("_continueLabel"); LabelSymbol* breakLabel = new LabelSymbol("_breakLabel"); DoWhileStmt* loop = 0; BlockStmt* retval = new BlockStmt(); // make variables declared in the scope of the body visible to // expressions in the condition of a do..while block if (body->length() == 1 && toBlockStmt(body->body.only())) { body = toBlockStmt(body->body.only()); body->remove(); } body->insertAtTail(new DefExpr(continueLabel)); body->insertAtTail(new CallExpr(PRIM_MOVE, condVar, condTest->copy())); loop = new DoWhileStmt(condVar, body); loop->mContinueLabel = continueLabel; loop->mBreakLabel = breakLabel; retval->insertAtTail(new DefExpr(condVar)); retval->insertAtTail(loop); retval->insertAtTail(new DefExpr(breakLabel)); return retval; }
void IRGenerator::accept(CallExpr& call) { FNTRACE(); std::vector<Value*> args; Value* callee = codegen(call.callee()); for (Expr* arg: call.args().values()) { if (Value* v = codegen(arg)) { args.push_back(v); } else { return; } } if (call.callee()->isFunction()) { // builtin function result_ = createCallFunction(static_cast<IRBuiltinFunction*>(callee), args); } else if (call.callee()->isBuiltin()) { // builtin handler result_ = createInvokeHandler(args); } else { // source handler result_ = nullptr; // TODO: inline source handler } }
void ReturnByRef::insertAssignmentToFormal(FnSymbol* fn, ArgSymbol* formal) { Expr* returnPrim = fn->body->body.tail; SET_LINENO(returnPrim); CallExpr* returnCall = toCallExpr(returnPrim); Expr* returnValue = returnCall->get(1)->remove(); CallExpr* moveExpr = new CallExpr(PRIM_ASSIGN, formal, returnValue); Expr* expr = returnPrim; // Walk backwards while the previous element is an autoDestroy call while (expr->prev != NULL) { bool stop = true; if (CallExpr* call = toCallExpr(expr->prev)) if (FnSymbol* calledFn = call->isResolved()) if (calledFn->hasFlag(FLAG_AUTO_DESTROY_FN)) stop = false; if (stop) break; expr = expr->prev; } Expr* returnOrFirstAutoDestroy = expr; // Add the move to return before the first autoDestroy // At this point we could also invoke some other function // if that turns out to be necessary. It might well be // necessary in order to return array slices by value. returnOrFirstAutoDestroy->insertBefore(moveExpr); }
static void removeVoidReturn(BlockStmt* cloneBody) { CallExpr* retexpr = toCallExpr(cloneBody->body.tail); INT_ASSERT(retexpr && retexpr->isPrimitive(PRIM_RETURN)); INT_ASSERT(toSymExpr(retexpr->get(1))->symbol() == gVoid); retexpr->remove(); }
void QDeleteAll::VisitStmt(clang::Stmt *stmt) { // Find a call to QMap/QSet/QHash::values CXXMemberCallExpr *valuesCall = dyn_cast<CXXMemberCallExpr>(stmt); if (valuesCall && valuesCall->getDirectCallee() && valuesCall->getDirectCallee()->getNameAsString() == "values") { const std::string valuesClassName = valuesCall->getMethodDecl()->getParent()->getNameAsString(); if (valuesClassName == "QMap" || valuesClassName == "QSet" || valuesClassName == "QHash") { // QMultiHash and QMultiMap automatically supported // Once found see if the first parent call is qDeleteAll int i = 1; Stmt *p = Utils::parent(m_parentMap, stmt, i); while (p) { CallExpr *pc = dyn_cast<CallExpr>(p); if (pc) { if (pc->getDirectCallee() && pc->getDirectCallee()->getNameAsString() == "qDeleteAll") { emitWarning(p->getLocStart(), "Calling qDeleteAll with " + valuesClassName + "::values, call qDeleteAll on the container itself"); } break; } ++i; p = Utils::parent(m_parentMap, stmt, i); } } } }
/* * Attempts to replace iteration over simple anonymous ranges with calls to * direct iterators that take low, high and stride as arguments. This is to * avoid the cost of constructing ranges, and if the stride is known at compile * time, provide a more optimized iterator that uses "<, <=, >, or >=" as the * relational operator. * * This is only meant to replace anonymous range iteration for "simple" bounded * ranges. Simple means it's a range of the form "low..high" or "low..high by * stride". Anything more complex is ignored with the thinking that this should * optimize the most common range iterators, but it could be expanded to handle * more cases. * * An alternative is to update scalar replacement of aggregates to work on * ranges, which should be able to achieve similar results as this optimization * while handling all ranges, including non-anonymous ranges. * * Will optimize things like: * - "for i in 1..10" * - "for i in 1..10+1" * - "var lo=1, hi=10;for i in lo..hi" * - "for i in 1..10 by 2" * - "for (i, j) in zip(1..10 by 2, 1..10 by -2)" * - "for (i, j) in zip(A, 1..10 by 2)" // will optimize range iter still * - "coforall i in 1..10 by 2" // works for coforalls as well * * Will not optimize ranges like: * - "for in in (1..)" // doesn't handle unbounded ranges * - "for i in 1..10 by 2 by 2" // doesn't handle more than one by operator * - "for i in 1..10 align 2" // doesn't handle align operator * - "for i in 1..#10" // doesn't handle # operator * - "var r = 1..10"; for i in r" // not an anonymous range * - "forall i in 1..10" // does not get applied to foralls * * Note that this function is pretty fragile because it relies on names of * functions/iterators as well as the arguments and order of those * functions/iterators but there's not really a way around it this early in * compilation. If the iterator can't be replaced, it is left unchanged. */ static void tryToReplaceWithDirectRangeIterator(Expr* iteratorExpr) { CallExpr* range = NULL; Expr* stride = NULL; if (CallExpr* call = toCallExpr(iteratorExpr)) { // grab the stride if we have a strided range if (call->isNamed("chpl_by")) { range = toCallExpr(call->get(1)->copy()); stride = toExpr(call->get(2)->copy()); } // assume the call is the range (checked below) and set default stride else { range = call; stride = new SymExpr(new_IntSymbol(1)); } // see if we're looking at a builder for a bounded range. the builder is // iteratable since range has these() iterators if (range && range->isNamed("chpl_build_bounded_range")) { // replace the range construction with a direct range iterator Expr* low = range->get(1)->copy(); Expr* high = range->get(2)->copy(); iteratorExpr->replace(new CallExpr("chpl_direct_range_iter", low, high, stride)); } } }
forv_Vec(FnSymbol, fn, gFnSymbols) { if (VarSymbol* ret = toVarSymbol(fn->getReturnSymbol())) { // The return value of an initCopy function should not be autodestroyed. // Normally, the return value of a function is autoCopied, but since // autoCopy is typically defined in terms of initCopy, this would lead to // infinite recursion. That is, the return value of initCopy must be // handled specially. if (fn->hasFlag(FLAG_INIT_COPY_FN)) ret->removeFlag(FLAG_INSERT_AUTO_DESTROY); // This is just a workaround for memory management being handled specially // for internally reference-counted types. (sandboxing) TypeSymbol* ts = ret->type->symbol; if (ts->hasFlag(FLAG_ARRAY) || ts->hasFlag(FLAG_DOMAIN)) ret->removeFlag(FLAG_INSERT_AUTO_DESTROY); // Do we need to add other record-wrapped types here? Testing will tell. // NOTE 1: When the value of a record field is established in a default // constructor, it is initialized using a MOVE. That means that ownership // of that value is shared between the formal_tmp and the record field. // If the autodestroy flag is left on that formal temp, then it will be // destroyed which -- for ref-counted types -- can result in a dangling // reference. So here, we look for that case and remove it. if (fn->hasFlag(FLAG_DEFAULT_CONSTRUCTOR)) { Map<Symbol*,Vec<SymExpr*>*> defMap; Map<Symbol*,Vec<SymExpr*>*> useMap; buildDefUseMaps(fn, defMap, useMap); std::vector<DefExpr*> defs; collectDefExprs(fn, defs); for_vector(DefExpr, def, defs) { if (VarSymbol* var = toVarSymbol(def->sym)) { // Examine only those bearing the explicit autodestroy flag. if (! var->hasFlag(FLAG_INSERT_AUTO_DESTROY)) continue; // Look for a use in a PRIM_SET_MEMBER where the field is a record // type, and remove the flag. // (We don't actually check that var is of record type, because // chpl__autoDestroy() does nothing when applied to all other types. for_uses(se, useMap, var) { CallExpr* call = toCallExpr(se->parentExpr); if (call->isPrimitive(PRIM_SET_MEMBER) && toSymExpr(call->get(3))->var == var) var->removeFlag(FLAG_INSERT_AUTO_DESTROY); } } } freeDefUseMaps(defMap, useMap); }
void NetworkDriverRewriteVisitor::InstrumentEntryPoints(FunctionDecl* funcDecl, string fdFile) { if (funcDecl->getStorageClass() == SC_Static) RW.RemoveText(funcDecl->getInnerLocStart(), 7); if (DI->getInstance().GetInitFunction() == funcDecl->getNameInfo().getName().getAsString()) return; if (funcDecl->getParamDecl(0)->getOriginalType().getAsString() != "struct device *" && funcDecl->getParamDecl(0)->getOriginalType().getAsString() != "struct pci_dev *") return; SourceRange sr = funcDecl->getParamDecl(0)->getSourceRange(); RW.InsertTextBefore(sr.getBegin(), "struct net_device *dev, "); Stmt *body = funcDecl->getBody(); list<DeclStmt*> stmtsToRewrite; for (auto i = body->child_begin(), e = body->child_end(); i != e; ++i) { if (!isa<DeclStmt>(*i)) continue; DeclStmt *declStmt = cast<DeclStmt>(*i); if (!declStmt->isSingleDecl() && !isa<VarDecl>(declStmt->getSingleDecl())) continue; VarDecl *var = cast<VarDecl>(declStmt->getSingleDecl()); if (!var->hasInit()) continue; Expr *expr = var->getInit(); if (!isa<ImplicitCastExpr>(expr)) continue; ImplicitCastExpr *implicit = cast<ImplicitCastExpr>(expr); if (!isa<CallExpr>(implicit->getSubExpr())) continue; CallExpr *call = cast<CallExpr>(implicit->getSubExpr()); DeclRefExpr *callee = cast<DeclRefExpr>(cast<ImplicitCastExpr>(call->getCallee())->getSubExpr()); if (callee->getNameInfo().getName().getAsString() == "to_pci_dev" || callee->getNameInfo().getName().getAsString() == "pci_get_drvdata") { stmtsToRewrite.push_back(declStmt); } } while (!stmtsToRewrite.empty()) { DeclStmt *stmt = stmtsToRewrite.back(); RW.RemoveText(stmt->getSourceRange()); stmtsToRewrite.pop_back(); } }
void QGetEnv::VisitStmt(clang::Stmt *stmt) { // Lets check only in function calls. Otherwise there are too many false positives, it's common // to implicit cast to bool when checking pointers for validity, like if (ptr) CXXMemberCallExpr *memberCall = dyn_cast<CXXMemberCallExpr>(stmt); if (!memberCall) return; CXXMethodDecl *method = memberCall->getMethodDecl(); if (!method) return; CXXRecordDecl *record = method->getParent(); if (!record || record->getNameAsString() != "QByteArray") { return; } std::vector<CallExpr *> calls = Utils::callListForChain(memberCall); if (calls.size() != 2) return; CallExpr *qgetEnvCall = calls.back(); FunctionDecl *func = qgetEnvCall->getDirectCallee(); if (!func || func->getNameAsString() != "qgetenv") return; string methodname = method->getNameAsString(); string errorMsg; std::string replacement; if (methodname == "isEmpty") { errorMsg = "qgetenv().isEmpty() allocates."; replacement = "qEnvironmentVariableIsEmpty"; } else if (methodname == "isNull") { errorMsg = "qgetenv().isNull() allocates."; replacement = "qEnvironmentVariableIsSet"; } else if (methodname == "toInt") { errorMsg = "qgetenv().toInt() is slow."; replacement = "qEnvironmentVariableIntValue"; } if (!errorMsg.empty()) { std::vector<FixItHint> fixits; if (isFixitEnabled(FixitAll)) { const bool success = FixItUtils::transformTwoCallsIntoOne(m_ci, qgetEnvCall, memberCall, replacement, fixits); if (!success) { queueManualFixitWarning(memberCall->getLocStart(), FixitAll); } } errorMsg += " Use " + replacement + "() instead"; emitWarning(memberCall->getLocStart(), errorMsg.c_str(), fixits); } }
void FlowCallVisitor::accept(CallExpr& call) { for (const auto& arg: call.args().values()) { visit(arg); } if (call.callee() && call.callee()->isBuiltin()) { calls_.push_back(&call); } }
void InstantiationVisitor::visit(CallExpr& c) { std::vector<std::unique_ptr<Expr>> args; for (auto& arg : c.arguments()) args.emplace_back(clone(*arg)); _expr.reset(new CallExpr(c.sloc(), c.name(), std::move(args), c.type())); static_cast<CallExpr&>(*_expr).target(c.target()); }
static void addNoAliasSetForFormal(ArgSymbol* arg, std::vector<ArgSymbol*> notAliasingThese) { SET_LINENO(arg); CallExpr* c = new CallExpr(PRIM_NO_ALIAS_SET, new SymExpr(arg)); for_vector(ArgSymbol, other, notAliasingThese) { c->insertAtTail(new SymExpr(other)); }
void ResolutionCandidate::resolveTypeConstructor(CallInfo& info) { SET_LINENO(fn); // Ignore tuple constructors; they were generated // with their type constructors. if (fn->hasFlag(FLAG_PARTIAL_TUPLE) == false) { CallExpr* typeConstructorCall = new CallExpr(astr("_type", fn->name)); for_formals(formal, fn) { if (formal->hasFlag(FLAG_IS_MEME) == false) { if (fn->_this->type->symbol->hasFlag(FLAG_TUPLE)) { if (formal->instantiatedFrom != NULL) { typeConstructorCall->insertAtTail(formal->type->symbol); } else if (formal->hasFlag(FLAG_INSTANTIATED_PARAM)) { typeConstructorCall->insertAtTail(paramMap.get(formal)); } } else { if (strcmp(formal->name, "outer") == 0 || formal->type == dtMethodToken) { typeConstructorCall->insertAtTail(formal); } else if (formal->instantiatedFrom != NULL) { SymExpr* se = new SymExpr(formal->type->symbol); NamedExpr* ne = new NamedExpr(formal->name, se); typeConstructorCall->insertAtTail(ne); } else if (formal->hasFlag(FLAG_INSTANTIATED_PARAM)) { SymExpr* se = new SymExpr(paramMap.get(formal)); NamedExpr* ne = new NamedExpr(formal->name, se); typeConstructorCall->insertAtTail(ne); } } } } info.call->insertBefore(typeConstructorCall); // If instead we call resolveCallAndCallee(typeConstructorCall) // then the line number reported in an error would change // e.g.: domains/deitz/test_generic_class_of_sparse_domain // or: classes/diten/multipledestructor resolveCall(typeConstructorCall); INT_ASSERT(typeConstructorCall->isResolved()); resolveFunction(typeConstructorCall->resolvedFunction()); fn->_this->type = typeConstructorCall->resolvedFunction()->retType; typeConstructorCall->remove(); }
/// Build calls to await_ready, await_suspend, and await_resume for a co_await /// expression. static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, VarDecl *CoroPromise, SourceLocation Loc, Expr *E) { OpaqueValueExpr *Operand = new (S.Context) OpaqueValueExpr(Loc, E->getType(), VK_LValue, E->getObjectKind(), E); // Assume invalid until we see otherwise. ReadySuspendResumeResult Calls = {{}, Operand, /*IsInvalid=*/true}; ExprResult CoroHandleRes = buildCoroutineHandle(S, CoroPromise->getType(), Loc); if (CoroHandleRes.isInvalid()) return Calls; Expr *CoroHandle = CoroHandleRes.get(); const StringRef Funcs[] = {"await_ready", "await_suspend", "await_resume"}; MultiExprArg Args[] = {None, CoroHandle, None}; for (size_t I = 0, N = llvm::array_lengthof(Funcs); I != N; ++I) { ExprResult Result = buildMemberCall(S, Operand, Loc, Funcs[I], Args[I]); if (Result.isInvalid()) return Calls; Calls.Results[I] = Result.get(); } // Assume the calls are valid; all further checking should make them invalid. Calls.IsInvalid = false; using ACT = ReadySuspendResumeResult::AwaitCallType; CallExpr *AwaitReady = cast<CallExpr>(Calls.Results[ACT::ACT_Ready]); if (!AwaitReady->getType()->isDependentType()) { // [expr.await]p3 [...] // — await-ready is the expression e.await_ready(), contextually converted // to bool. ExprResult Conv = S.PerformContextuallyConvertToBool(AwaitReady); if (Conv.isInvalid()) { S.Diag(AwaitReady->getDirectCallee()->getLocStart(), diag::note_await_ready_no_bool_conversion); S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required) << AwaitReady->getDirectCallee() << E->getSourceRange(); Calls.IsInvalid = true; } Calls.Results[ACT::ACT_Ready] = Conv.get(); } CallExpr *AwaitSuspend = cast<CallExpr>(Calls.Results[ACT::ACT_Suspend]); if (!AwaitSuspend->getType()->isDependentType()) { // [expr.await]p3 [...] // - await-suspend is the expression e.await_suspend(h), which shall be // a prvalue of type void or bool. QualType RetType = AwaitSuspend->getType(); if (RetType != S.Context.BoolTy && RetType != S.Context.VoidTy) { S.Diag(AwaitSuspend->getCalleeDecl()->getLocation(), diag::err_await_suspend_invalid_return_type) << RetType; S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required) << AwaitSuspend->getDirectCallee(); Calls.IsInvalid = true; } } return Calls; }
C2::ExprResult C2Sema::ActOnCallExpr(Expr* Fn, Expr** args, unsigned numArgs, SourceLocation RParenLoc) { #ifdef SEMA_DEBUG std::cerr << COL_SEMA"SEMA: call to " << "TODO Fn" << " at " << "TODO Fn"; //expr2loc(Fn).dump(SourceMgr); std::cerr << ANSI_NORMAL"\n"; #endif CallExpr* call = new CallExpr(Fn, RParenLoc); assert(call); for (unsigned i=0; i<numArgs; i++) call->addArg(args[i]); return ExprResult(call); }
void printCallStackCalls() { printf("\n" "callStack %d elms\n\n", callStack.n); for (int i = 0; i < callStack.n; i++) { CallExpr* call = callStack.v[i]; FnSymbol* cfn = call->isResolved(); printf("%d %d %s <- %d %s\n", i, cfn ? cfn->id : 0, cfn ? cfn->name: "<no callee>", call ? call->id : 0, call ? call->stringLoc() : "<no call>"); } printf("\n"); }
void ReturnByRef::insertAssignmentToFormal(FnSymbol* fn, ArgSymbol* formal) { Expr* returnPrim = fn->body->body.tail; SET_LINENO(returnPrim); CallExpr* returnCall = toCallExpr(returnPrim); Expr* returnValue = returnCall->get(1)->remove(); CallExpr* moveExpr = new CallExpr(PRIM_MOVE, formal, returnValue); returnPrim->insertBefore(moveExpr); }
void Inliner::VisitBinaryOperator(BinaryOperator * node) { Expr * lhs = node->getLHS(), * rhs = node->getRHS(); CallExpr * call = dyn_cast<CallExpr>(rhs); if (call && call->getDirectCallee()->isThisDeclarationADefinition()) { // replace: x = foo(y); with: /*x = */ { //inlined foo } Rewriter &rewriter = (current_func_->isMain()) ? main_rewriter_ : rewriter_; rewriter.InsertText(lhs->getLocStart(),"/*"); rewriter.InsertText(rhs->getLocStart(),"*/"); } return_vars_.push_back(Utils::PrintStmt(lhs,contex_)); VisitChildren(node); return_vars_.pop_back(); }
void localizeGlobals() { if (fNoGlobalConstOpt) return; forv_Vec(FnSymbol, fn, gFnSymbols) { Map<Symbol*,VarSymbol*> globals; std::vector<BaseAST*> asts; collect_asts(fn->body, asts); for_vector(BaseAST, ast, asts) { if (SymExpr* se = toSymExpr(ast)) { Symbol* var = se->symbol(); ModuleSymbol* parentmod = toModuleSymbol(var->defPoint->parentSymbol); CallExpr* parentExpr = toCallExpr(se->parentExpr); bool inAddrOf = parentExpr && parentExpr->isPrimitive(PRIM_ADDR_OF); bool lhsOfMove = parentExpr && isMoveOrAssign(parentExpr) && (parentExpr->get(1) == se); // Is var a global constant? // Don't replace the var name in its init function since that's // where we're setting the value. Similarly, dont replace them // inside initStringLiterals // If the parentSymbol is the rootModule, the var is 'void,' // 'false,' '0,' ... // Also don't replace it when it's in an addr of primitive. if (parentmod && fn != parentmod->initFn && fn != initStringLiterals && !inAddrOf && !lhsOfMove && var->hasFlag(FLAG_CONST) && var->defPoint->parentSymbol != rootModule) { VarSymbol* local_global = globals.get(var); SET_LINENO(se); // Set the se line number for output if (!local_global) { const char * newname = astr("local_", var->cname); local_global = newTemp(newname, var->type); fn->insertAtHead(new CallExpr(PRIM_MOVE, local_global, var)); fn->insertAtHead(new DefExpr(local_global)); // Copy string immediates to localized strings so that // we can show the string value in comments next to uses. if (!llvmCodegen) if (VarSymbol* localVarSym = toVarSymbol(var)) if (Immediate* immediate = localVarSym->immediate) if (immediate->const_kind == CONST_KIND_STRING) local_global->immediate = new Immediate(immediate->v_string, immediate->string_kind); globals.put(var, local_global); } se->replace(new SymExpr(toSymbol(local_global))); } } } }
void QStringAllocations::VisitFromLatin1OrUtf8(Stmt *stmt) { CallExpr *callExpr = dyn_cast<CallExpr>(stmt); if (!callExpr) return; FunctionDecl *functionDecl = callExpr->getDirectCallee(); if (!StringUtils::functionIsOneOf(functionDecl, {"fromLatin1", "fromUtf8"})) return; CXXMethodDecl *methodDecl = dyn_cast<CXXMethodDecl>(functionDecl); if (!StringUtils::isOfClass(methodDecl, "QString")) return; if (!Utils::callHasDefaultArguments(callExpr) || !hasCharPtrArgument(functionDecl, 2)) // QString::fromLatin1("foo", 1) is ok return; if (!containsStringLiteralNoCallExpr(callExpr)) return; if (!isOptionSet("no-msvc-compat")) { StringLiteral *lt = stringLiteralForCall(callExpr); if (lt && lt->getNumConcatenated() > 1) { return; // Nothing to do here, MSVC doesn't like it } } vector<ConditionalOperator*> ternaries; HierarchyUtils::getChilds(callExpr, ternaries, 2); if (!ternaries.empty()) { auto ternary = ternaries[0]; if (Utils::ternaryOperatorIsOfStringLiteral(ternary)) { emitWarning(stmt->getLocStart(), string("QString::fromLatin1() being passed a literal")); } return; } std::vector<FixItHint> fixits; if (isFixitEnabled(FromLatin1_FromUtf8Allocations)) { const FromFunction fromFunction = functionDecl->getNameAsString() == "fromLatin1" ? FromLatin1 : FromUtf8; fixits = fixItReplaceFromLatin1OrFromUtf8(callExpr, fromFunction); } if (functionDecl->getNameAsString() == "fromLatin1") { emitWarning(stmt->getLocStart(), string("QString::fromLatin1() being passed a literal"), fixits); } else { emitWarning(stmt->getLocStart(), string("QString::fromUtf8() being passed a literal"), fixits); } }
static bool removeIdentityDefs(Symbol* sym) { bool change = false; for_defs(def, defMap, sym) { CallExpr* move = toCallExpr(def->parentExpr); if (move && isMoveOrAssign(move)) { SymExpr* rhs = toSymExpr(move->get(2)); if (rhs && def->symbol() == rhs->symbol()) { move->remove(); change = true; } } }
static bool removeIdentityDefs(Symbol* sym) { bool change = false; for_defs(def, defMap, sym) { CallExpr* move = toCallExpr(def->parentExpr); if (move && move->isPrimitive(PRIM_MOVE)) { SymExpr* rhs = toSymExpr(move->get(2)); if (rhs && def->var == rhs->var) { move->remove(); change = true; } } }
// // Do a breadth first search starting from functions generated for local blocks // for all function calls in each level of the search, if they directly cause // communication, add a local temp that isn't wide. If it is a resolved call, // meaning that it isn't a primitive or external function, clone it and add it // to the queue of functions to handle at the next iteration of the BFS. // static void handleLocalBlocks() { Map<FnSymbol*,FnSymbol*> cache; // cache of localized functions Vec<BlockStmt*> queue; // queue of blocks to localize forv_Vec(BlockStmt, block, gBlockStmts) { if (block->parentSymbol) { // NOAKES 2014/11/25 Transitional. Avoid calling blockInfoGet() if (block->isLoopStmt() == true) { } else if (block->blockInfoGet()) { if (block->blockInfoGet()->isPrimitive(PRIM_BLOCK_LOCAL)) { queue.add(block); } } } } forv_Vec(BlockStmt, block, queue) { std::vector<CallExpr*> calls; collectCallExprs(block, calls); for_vector(CallExpr, call, calls) { localizeCall(call); if (FnSymbol* fn = call->isResolved()) { SET_LINENO(fn); if (FnSymbol* alreadyLocal = cache.get(fn)) { call->baseExpr->replace(new SymExpr(alreadyLocal)); } else { if (!fn->hasFlag(FLAG_EXTERN)) { FnSymbol* local = fn->copy(); local->addFlag(FLAG_LOCAL_FN); local->name = astr("_local_", fn->name); local->cname = astr("_local_", fn->cname); fn->defPoint->insertBefore(new DefExpr(local)); call->baseExpr->replace(new SymExpr(local)); queue.add(local->body); cache.put(fn, local); cache.put(local, local); // to handle recursion if (local->retType->symbol->hasFlag(FLAG_WIDE_REF)) { CallExpr* ret = toCallExpr(local->body->body.tail); INT_ASSERT(ret && ret->isPrimitive(PRIM_RETURN)); // Capture the return expression in a local temp. insertLocalTemp(ret->get(1)); local->retType = ret->get(1)->typeInfo(); } } } } }
// true for: QString::fromLatin1().arg() // false for: QString::fromLatin1("") // true for: QString s = QString::fromLatin1("foo") // false for: s += QString::fromLatin1("foo"), etc. static bool isQStringLiteralCandidate(Stmt *s, ParentMap *map, const LangOptions &lo, const SourceManager &sm , int currentCall = 0) { if (!s) return false; MemberExpr *memberExpr = dyn_cast<MemberExpr>(s); if (memberExpr) return true; auto constructExpr = dyn_cast<CXXConstructExpr>(s); if (StringUtils::isOfClass(constructExpr, "QString")) return true; if (Utils::isAssignOperator(dyn_cast<CXXOperatorCallExpr>(s), "QString", "QLatin1String", lo)) return true; if (Utils::isAssignOperator(dyn_cast<CXXOperatorCallExpr>(s), "QString", "QString", lo)) return true; CallExpr *callExpr = dyn_cast<CallExpr>(s); StringLiteral *literal = stringLiteralForCall(callExpr); auto operatorCall = dyn_cast<CXXOperatorCallExpr>(s); if (operatorCall && StringUtils::returnTypeName(operatorCall, lo) != "QTestData") { // QTest::newRow will static_assert when using QLatin1String // Q_STATIC_ASSERT_X(QMetaTypeId2<T>::Defined, "Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system"); string className = StringUtils::classNameFor(operatorCall); if (className == "QString") { return false; } else if (className.empty() && StringUtils::hasArgumentOfType(operatorCall->getDirectCallee(), "QString", lo)) { return false; } } if (currentCall > 0 && callExpr) { auto fDecl = callExpr->getDirectCallee(); if (fDecl && betterTakeQLatin1String(dyn_cast<CXXMethodDecl>(fDecl), literal)) return false; return true; } if (currentCall == 0 || dyn_cast<ImplicitCastExpr>(s) || dyn_cast<CXXBindTemporaryExpr>(s) || dyn_cast<MaterializeTemporaryExpr>(s)) // skip this cruft return isQStringLiteralCandidate(HierarchyUtils::parent(map, s), map, lo, sm, currentCall + 1); return false; }
// // Find the _waitEndCount and _endCountFree calls that comes after 'fromHere'. // Since these two calls come together, it is prettier to insert // our stuff after the latter. // static Expr* findTailInsertionPoint(Expr* fromHere, bool isCoforall) { Expr* curr = fromHere; if (isCoforall) curr = curr->parentExpr; CallExpr* result = NULL; while ((curr = curr->next)) { if (CallExpr* call = toCallExpr(curr)) if (call->isNamed("_waitEndCount")) { result = call; break; } } INT_ASSERT(result); CallExpr* freeEC = toCallExpr(result->next); // Currently these two calls come together. INT_ASSERT(freeEC && freeEC->isNamed("_endCountFree")); return freeEC; }
// // Print the module name, line number, and function signature of each function // on the call stack. This can be called from a debugger to to see what the // call chain looks like e.g. after a resolution error. // void printCallStack(bool force, bool shortModule, FILE* out) { if (!force) { if (!fPrintCallStackOnError || err_print || callStack.n <= 1) return; } if (!developer) fprintf(out, "while processing the following Chapel call chain:\n"); for (int i = callStack.n-1; i >= 0; i--) { CallExpr* call = callStack.v[i]; FnSymbol* fn = call->getFunction(); ModuleSymbol* module = call->getModule(); fprintf(out, " %s:%d: %s%s%s\n", (shortModule ? module->name : cleanFilename(fn->fname())), call->linenum(), toString(fn), (module->modTag == MOD_INTERNAL ? " [internal module]" : ""), (fn->hasFlag(FLAG_COMPILER_GENERATED) ? " [compiler-generated]" : "")); } }