Index: polly/trunk/include/polly/Support/ScopHelper.h =================================================================== --- polly/trunk/include/polly/Support/ScopHelper.h +++ polly/trunk/include/polly/Support/ScopHelper.h @@ -36,6 +36,7 @@ namespace polly { class Scop; +class ScopStmt; /// Type to remap values. using ValueMapT = llvm::DenseMap, @@ -458,5 +459,24 @@ // @param BoxedLoops Set of Boxed Loops we get from the SCoP. llvm::Loop *getFirstNonBoxedLoopFor(llvm::BasicBlock *BB, llvm::LoopInfo &LI, const BoxedLoopsSetTy &BoxedLoops); + +/// Is the given instruction a call to a debug function? +/// +/// A debug function can be used to insert output in Polly-optimized code which +/// normally does not allow function calls with side-effects. For instance, a +/// printf can be inserted to check whether a value still has the expected value +/// after Polly generated code: +/// +/// int sum = 0; +/// for (int i = 0; i < 16; i+=1) { +/// sum += i; +/// printf("The value of sum at i=%d is %d\n", sum, i); +/// } +bool isDebugCall(llvm::Instruction *Inst); + +/// Does the statement contain a call to a debug function? +/// +/// Such a statement must not be removed, even if has no side-effects. +bool hasDebugCall(ScopStmt *Stmt); } // namespace polly #endif Index: polly/trunk/lib/Analysis/ScopBuilder.cpp =================================================================== --- polly/trunk/lib/Analysis/ScopBuilder.cpp +++ polly/trunk/lib/Analysis/ScopBuilder.cpp @@ -559,7 +559,7 @@ if (CI == nullptr) return false; - if (CI->doesNotAccessMemory() || isIgnoredIntrinsic(CI)) + if (CI->doesNotAccessMemory() || isIgnoredIntrinsic(CI) || isDebugCall(CI)) return true; bool ReadOnly = false; Index: polly/trunk/lib/Analysis/ScopDetection.cpp =================================================================== --- polly/trunk/lib/Analysis/ScopDetection.cpp +++ polly/trunk/lib/Analysis/ScopDetection.cpp @@ -703,6 +703,12 @@ if (CalledFunction == nullptr) return false; + if (isDebugCall(&CI)) { + DEBUG(dbgs() << "Allow call to debug function: " + << CalledFunction->getName() << '\n'); + return true; + } + if (AllowModrefCall) { switch (AA.getModRefBehavior(CalledFunction)) { case FMRB_UnknownModRefBehavior: Index: polly/trunk/lib/Analysis/ScopInfo.cpp =================================================================== --- polly/trunk/lib/Analysis/ScopInfo.cpp +++ polly/trunk/lib/Analysis/ScopInfo.cpp @@ -3585,6 +3585,10 @@ void Scop::simplifySCoP(bool AfterHoisting) { auto ShouldDelete = [AfterHoisting](ScopStmt &Stmt) -> bool { + // Never delete statements that contain calls to debug functions. + if (hasDebugCall(&Stmt)) + return false; + bool RemoveStmt = Stmt.isEmpty(); // Remove read only statements only after invariant load hoisting. Index: polly/trunk/lib/Support/ScopHelper.cpp =================================================================== --- polly/trunk/lib/Support/ScopHelper.cpp +++ polly/trunk/lib/Support/ScopHelper.cpp @@ -35,6 +35,13 @@ cl::desc("Allow to speculate on the execution of 'error blocks'."), cl::Hidden, cl::init(true), cl::ZeroOrMore, cl::cat(PollyCategory)); +static cl::list DebugFunctions( + "polly-debug-func", + cl::desc("Allow calls to the specified functions in SCoPs even if their " + "side-effects are unknown. This can be used to do debug output in " + "Polly-transformed code."), + cl::Hidden, cl::ZeroOrMore, cl::CommaSeparated, cl::cat(PollyCategory)); + // Ensures that there is just one predecessor to the entry node from outside the // region. // The identity of the region entry node is preserved. @@ -401,6 +408,9 @@ for (Instruction &Inst : BB) if (CallInst *CI = dyn_cast(&Inst)) { + if (isDebugCall(CI)) + continue; + if (isIgnoredIntrinsic(CI)) continue; @@ -586,3 +596,45 @@ Loop *L = LI.getLoopFor(BB); return getFirstNonBoxedLoopFor(L, LI, BoxedLoops); } + +bool polly::isDebugCall(Instruction *Inst) { + auto *CI = dyn_cast(Inst); + if (!CI) + return false; + + Function *CF = CI->getCalledFunction(); + if (!CF) + return false; + + return std::find(DebugFunctions.begin(), DebugFunctions.end(), + CF->getName()) != DebugFunctions.end(); +} + +static bool hasDebugCall(BasicBlock *BB) { + for (Instruction &Inst : *BB) { + if (isDebugCall(&Inst)) + return true; + } + return false; +} + +bool polly::hasDebugCall(ScopStmt *Stmt) { + // Quick skip if no debug functions have been defined. + if (DebugFunctions.empty()) + return false; + + if (!Stmt) + return false; + + for (Instruction *Inst : Stmt->getInstructions()) + if (isDebugCall(Inst)) + return true; + + if (Stmt->isRegionStmt()) { + for (BasicBlock *RBB : Stmt->getRegion()->blocks()) + if (RBB != Stmt->getEntryBlock() && ::hasDebugCall(RBB)) + return true; + } + + return false; +} Index: polly/trunk/test/ScopInfo/debug_call.ll =================================================================== --- polly/trunk/test/ScopInfo/debug_call.ll +++ polly/trunk/test/ScopInfo/debug_call.ll @@ -0,0 +1,37 @@ +; RUN: opt %loadPolly -polly-debug-func=dbg_printf -polly-scops -analyze < %s | FileCheck %s -match-full-lines +; +; Check that the call to dbg_printf is accepted as a debug-function. +; +declare void @dbg_printf(i8*, ...) + +define void @func(i32 %n) { +entry: + br label %for + +for: + %j = phi i32 [0, %entry], [%j.inc, %inc] + %j.cmp = icmp slt i32 %j, %n + br i1 %j.cmp, label %body, label %exit + + body: + call void (i8*, ...) @dbg_printf(i8* null, i32 %j) + br label %inc + +inc: + %j.inc = add nuw nsw i32 %j, 1 + br label %for + +exit: + br label %return + +return: + ret void +} + +; CHECK: Statements { +; CHECK-NEXT: Stmt_body +; CHECK-NEXT: Domain := +; CHECK-NEXT: [n] -> { Stmt_body[i0] : 0 <= i0 < n }; +; CHECK-NEXT: Schedule := +; CHECK-NEXT: [n] -> { Stmt_body[i0] -> [i0] }; +; CHECK-NEXT: }