Index: lib/Transform/ZoneAlgo.cpp =================================================================== --- lib/Transform/ZoneAlgo.cpp +++ lib/Transform/ZoneAlgo.cpp @@ -338,11 +338,34 @@ if (!isl_union_map_is_disjoint(Stores.keep(), AccRel.keep())) { OptimizationRemarkMissed R(PassName, "StoreAfterStore", MA->getAccessInstruction()); - R << "store after store of same element in same statement"; - R << " (previous stores: " << Stores; - R << ", storing: " << AccRel << ")"; - S->getFunction().getContext().diagnose(R); - return false; + + // Stores to the same location are OK, if the very same value is + // stored. Check for this exception. + Value *V = nullptr; + bool IsOK = true; + for (auto *MA : *Stmt) { + if (!MA->isLatestArrayKind() || !MA->isMustWrite() || + !MA->isOriginalArrayKind()) + continue; + + if (!V) { + V = MA->getAccessValue(); + } else { + if (V == MA->getAccessValue()) { + continue; + } else { + IsOK = false; + break; + } + } + } + if (!IsOK) { + R << "store after store of same element in same statement"; + R << " (previous stores: " << Stores; + R << ", storing: " << AccRel << ")"; + S->getFunction().getContext().diagnose(R); + return false; + } } Stores = give(isl_union_map_union(Stores.take(), AccRel.take())); Index: test/ForwardOpTree/forward_load_double_write.ll =================================================================== --- /dev/null +++ test/ForwardOpTree/forward_load_double_write.ll @@ -0,0 +1,55 @@ +; RUN: opt %loadPolly -polly-optree -analyze < %s | FileCheck %s -match-full-lines +; +; Rematerialize a load. +; +define void @func(i32 %n, double* noalias nonnull %A, double* noalias nonnull %B) { +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 %bodyA, label %exit + + bodyA: + %B_idx = getelementptr inbounds double, double* %B, i32 %j + %val = load double, double* %B_idx + br label %bodyB + + bodyB: + %A_idx = getelementptr inbounds double, double* %A, i32 %j + store double %val, double* %A_idx + store double %val, double* %A_idx + br label %inc + +inc: + %j.inc = add nuw nsw i32 %j, 1 + br label %for + +exit: + br label %return + +return: + ret void +} + + +; CHECK: Statistics { +; CHECK: Known loads forwarded: 1 +; CHECK: Operand trees forwarded: 1 +; CHECK: Statements with forwarded operand trees: 1 +; CHECK: } + +; CHECK: Stmt_bodyB +; CHECK-NEXT: ReadAccess := [Reduction Type: NONE] [Scalar: 0] +; CHECK-NEXT: null; +; CHECK-NEXT: new: [n] -> { Stmt_bodyB[i0] -> MemRef_B[i0] }; +; CHECK-NEXT: MustWriteAccess := [Reduction Type: NONE] [Scalar: 0] +; CHECK-NEXT: [n] -> { Stmt_bodyB[i0] -> MemRef_A[i0] }; +; CHECK-NEXT: MustWriteAccess := [Reduction Type: NONE] [Scalar: 0] +; CHECK-NEXT: [n] -> { Stmt_bodyB[i0] -> MemRef_A[i0] }; +; CHECK-NEXT: Instructions { +; CHECK-NEXT: %val = load double, double* %B_idx +; CHECK-NEXT: store double %val, double* %A_idx +; CHECK-NEXT: store double %val, double* %A_idx +; CHECK-NEXT: }