Index: llvm/trunk/include/llvm/Analysis/OptimizationDiagnosticInfo.h =================================================================== --- llvm/trunk/include/llvm/Analysis/OptimizationDiagnosticInfo.h +++ llvm/trunk/include/llvm/Analysis/OptimizationDiagnosticInfo.h @@ -18,12 +18,12 @@ #include "llvm/ADT/Optional.h" #include "llvm/Analysis/BlockFrequencyInfo.h" #include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/Function.h" #include "llvm/IR/PassManager.h" #include "llvm/Pass.h" namespace llvm { class DebugLoc; -class Function; class LLVMContext; class Loop; class Pass; @@ -194,6 +194,19 @@ void emitOptimizationRemarkAnalysisAliasing(const char *PassName, Loop *L, const Twine &Msg); + /// \brief Whether we allow for extra compile-time budget to perform more + /// analysis to produce fewer false positives. + /// + /// This is useful when reporting missed optimizations. In this case we can + /// use the extra analysis (1) to filter trivial false positives or (2) to + /// provide more context so that non-trivial false positives can be quickly + /// detected by the user. + bool allowExtraAnalysis() const { + // For now, only allow this with -fsave-optimization-record since the -Rpass + // options are handled in the front-end. + return F->getContext().getDiagnosticsOutputFile(); + } + private: Function *F; Index: llvm/trunk/lib/Transforms/Scalar/GVN.cpp =================================================================== --- llvm/trunk/lib/Transforms/Scalar/GVN.cpp +++ llvm/trunk/lib/Transforms/Scalar/GVN.cpp @@ -1208,6 +1208,38 @@ return false; } +/// \brief Try to locate the three instruction involved in a missed +/// load-elimination case that is due to an intervening store. +static void reportMayClobberedLoad(LoadInst *LI, MemDepResult DepInfo, + DominatorTree *DT, + OptimizationRemarkEmitter *ORE) { + using namespace ore; + User *OtherAccess = nullptr; + + OptimizationRemarkMissed R(DEBUG_TYPE, "LoadClobbered", LI); + R << "load of type " << NV("Type", LI->getType()) << " not eliminated" + << setExtraArgs(); + + for (auto *U : LI->getPointerOperand()->users()) + if (U != LI && (isa(U) || isa(U)) && + DT->dominates(cast(U), LI)) { + // FIXME: for now give up if there are multiple memory accesses that + // dominate the load. We need further analysis to decide which one is + // that we're forwarding from. + if (OtherAccess) + OtherAccess = nullptr; + else + OtherAccess = U; + } + + if (OtherAccess) + R << " in favor of " << NV("OtherAccess", OtherAccess); + + R << " because it is clobbered by " << NV("ClobberedBy", DepInfo.getInst()); + + ORE->emit(R); +} + bool GVN::AnalyzeLoadAvailability(LoadInst *LI, MemDepResult DepInfo, Value *Address, AvailableValue &Res) { @@ -1272,6 +1304,10 @@ Instruction *I = DepInfo.getInst(); dbgs() << " is clobbered by " << *I << '\n'; ); + + if (ORE->allowExtraAnalysis()) + reportMayClobberedLoad(LI, DepInfo, DT, ORE); + return false; } assert(DepInfo.isDef() && "follows from above"); Index: llvm/trunk/test/Transforms/GVN/opt-remarks.ll =================================================================== --- llvm/trunk/test/Transforms/GVN/opt-remarks.ll +++ llvm/trunk/test/Transforms/GVN/opt-remarks.ll @@ -40,7 +40,22 @@ ; YAML-NEXT: - String: ' in favor of ' ; YAML-NEXT: - InfavorOfValue: load ; YAML-NEXT: ... - +; YAML-NEXT: --- !Missed +; YAML-NEXT: Pass: gvn +; YAML-NEXT: Name: LoadClobbered +; YAML-NEXT: DebugLoc: { File: /tmp/s.c, Line: 3, Column: 3 } +; YAML-NEXT: Function: may_alias +; YAML-NEXT: Args: +; YAML-NEXT: - String: 'load of type ' +; YAML-NEXT: - Type: i32 +; YAML-NEXT: - String: ' not eliminated' +; YAML-NEXT: - String: ' in favor of ' +; YAML-NEXT: - OtherAccess: load +; YAML-NEXT: DebugLoc: { File: /tmp/s.c, Line: 1, Column: 13 } +; YAML-NEXT: - String: ' because it is clobbered by ' +; YAML-NEXT: - ClobberedBy: store +; YAML-NEXT: DebugLoc: { File: /tmp/s.c, Line: 2, Column: 10 } +; YAML-NEXT: ... define i32 @arg(i32* %p, i32 %i) { entry: @@ -63,3 +78,31 @@ %add = add i32 %load1, %load ret i32 %add } + +define i32 @may_alias(i32* %p, i32* %r) !dbg !7 { +entry: + %load1 = load i32, i32* %p, !tbaa !13, !dbg !9 + store i32 4, i32* %r, !tbaa !13, !dbg !10 + %load = load i32, i32* %p, !tbaa !13, !dbg !11 + %add = add i32 %load1, %load + ret i32 %add +} +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 4.0.0 (trunk 282540) (llvm/trunk 282542)", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, enums: !2) +!1 = !DIFile(filename: "/tmp/s.c", directory: "/tmp") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"PIC Level", i32 2} +!6 = !{!"clang version 4.0.0 (trunk 282540) (llvm/trunk 282542)"} +!7 = distinct !DISubprogram(name: "may_alias", scope: !1, file: !1, line: 1, type: !8, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: true, unit: !0, variables: !2) +!8 = !DISubroutineType(types: !2) +!9 = !DILocation(line: 1, column: 13, scope: !7) +!10 = !DILocation(line: 2, column: 10, scope: !7) +!11 = !DILocation(line: 3, column: 3, scope: !7) + +!12 = !{ !"tbaa root" } +!13 = !{ !"int", !12 }