//===--- OptimizationRemark.h - Optimization diagnostics --------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // /// \file /// This file defines the remark type and the emitter class that passes can use /// to emit optimization diagnostics. // //===----------------------------------------------------------------------===// #ifndef SWIFT_SIL_OPTIMIZATIONREMARKEMITTER_H #define SWIFT_SIL_OPTIMIZATIONREMARKEMITTER_H #include "swift/AST/SemanticAttrs.h" #include "swift/Basic/SourceLoc.h" #include "swift/Demangling/Demangler.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILModule.h" #include "llvm/ADT/StringRef.h" namespace swift { class SILFunction; namespace OptRemark { struct ArgumentKeyKind { enum InnerTy { // Just assume this is a normal msg that we are emitting. Default, // Assume this is a note that should be emitted as a separate // diagnostic when emitting diagnostics. Do nothing special // along the backend path. Note, // Assume that this is a note that should be emitted as a separate // diagnostic but that doesn't have its own source loc: we should reuse the // one for the original remark. // // This is intended to be used in situations where one needs to emit a // "note" warning due to us not being able to infer a part of our // opt-remark. ParentLocNote, }; InnerTy innerValue; ArgumentKeyKind(InnerTy value) : innerValue(value) {} operator InnerTy() const { return innerValue; } /// Return true if this argument is meant to be a separate diagnostic when we /// emit diagnostics but when we emit to the remark streamer (due to not /// having support for this), we just emit the remark inline. /// /// TODO: Unfortunate that this needs to be done. bool isSeparateDiagnostic() const { switch (innerValue) { case InnerTy::Default: return false; case InnerTy::Note: case InnerTy::ParentLocNote: return true; } llvm_unreachable("Covered switch isn't covered?!"); } }; struct ArgumentKey { ArgumentKeyKind kind; std::string data; ArgumentKey(ArgumentKeyKind kind, StringRef data) : kind(kind), data(data) {} ArgumentKey(ArgumentKeyKind::InnerTy kind, StringRef data) : kind(kind), data(data) {} ArgumentKey(ArgumentKey kind, StringRef data) : kind(kind.kind), data(data) {} }; /// Used in the streaming interface as the general argument type. It /// internally converts everything into a key-value pair. struct Argument { ArgumentKey key; std::string val; /// If set, the debug location corresponding to the value. SourceLoc loc; explicit Argument(StringRef str = "") : Argument({ArgumentKeyKind::Default, "String"}, str) {} Argument(StringRef key, StringRef val) : Argument({ArgumentKeyKind::Default, key}, val) {} Argument(ArgumentKey key, StringRef val) : key(key), val(val) {} Argument(StringRef key, int n); Argument(StringRef key, long n); Argument(StringRef key, long long n); Argument(StringRef key, unsigned n); Argument(StringRef key, unsigned long n); Argument(StringRef key, unsigned long long n); Argument(StringRef key, SILFunction *f) : Argument(ArgumentKey(ArgumentKeyKind::Default, key), f) {} Argument(ArgumentKey key, SILFunction *f); Argument(StringRef key, SILType ty); Argument(StringRef key, CanType ty); Argument(StringRef key, StringRef msg, const ValueDecl *decl) : Argument(ArgumentKey(ArgumentKeyKind::Default, key), msg, decl) {} Argument(ArgumentKey key, StringRef msg, const ValueDecl *decl) : key(key), val(msg), loc(decl->getLoc()) {} Argument(StringRef key, llvm::Twine &&msg, SILLocation loc) : Argument(ArgumentKey(ArgumentKeyKind::Default, key), std::move(msg), loc) {} Argument(ArgumentKey key, llvm::Twine &&msg, SILLocation loc) : key(key), val(msg.str()), loc(loc.getSourceLoc()) {} }; /// Shorthand to insert named-value pairs. using NV = Argument; /// Inserting this into a Remark indents the text when printed as a debug /// message. struct IndentDebug { explicit IndentDebug(unsigned width) : width(width) {} unsigned width; }; enum class SourceLocPresentationKind { StartRange, EndRange, }; enum class SourceLocInferenceBehavior : unsigned { None = 0, ForwardScan = 0x1, BackwardScan = 0x2, ForwardScan2nd = 0x4, AlwaysInfer = 0x8, ForwardThenBackwards = ForwardScan | BackwardScan, BackwardsThenForwards = BackwardScan | ForwardScan2nd, ForwardScanAlwaysInfer = ForwardScan | AlwaysInfer, BackwardScanAlwaysInfer = BackwardScan | AlwaysInfer, BackwardThenForwardAlwaysInfer = BackwardScan | ForwardScan2nd | AlwaysInfer, }; inline SourceLocInferenceBehavior operator&(SourceLocInferenceBehavior lhs, SourceLocInferenceBehavior rhs) { auto lhsVal = std::underlying_type::type(lhs); auto rhsVal = std::underlying_type::type(rhs); return SourceLocInferenceBehavior(lhsVal & rhsVal); } /// Infer the proper SourceLoc to use for the given SILInstruction. /// /// This means that if we have a valid location for the instruction, we just /// return that. Otherwise, we have a runtime instruction that does not have a /// valid source location. In such a case, we infer the source location from the /// surrounding code. If we can not find any surrounding code, we return an /// invalid SourceLoc. SourceLoc inferOptRemarkSourceLoc(SILInstruction &i, SourceLocInferenceBehavior inferBehavior, SourceLocPresentationKind presentationKind = SourceLocPresentationKind::StartRange); /// The base class for remarks. This can be created by optimization passed to /// report successful and unsuccessful optimizations. CRTP is used to preserve /// the underlying type encoding the remark kind in the insertion operator. template class Remark { /// Arguments collected via the streaming interface. SmallVector args; /// The name of the pass generating the remark. StringRef passName; /// Textual identifier for the remark (single-word, camel-case). Can be used /// by external tools reading the output file for optimization remarks to /// identify the remark. SmallString<32> identifier; /// Source location for the diagnostics. SourceLoc location; /// The function for the diagnostics. SILFunction *function; /// The demangled name of \p Function. SmallString<64> demangledFunctionName; /// Indentation used if this remarks is printed as a debug message. unsigned indentDebugWidth = 0; protected: Remark(StringRef identifier, SILInstruction &i, SourceLocInferenceBehavior inferenceBehavior, SourceLocPresentationKind locPresentationKind) : identifier((Twine("sil.") + identifier).str()), location( inferOptRemarkSourceLoc(i, inferenceBehavior, locPresentationKind)), function(i.getParent()->getParent()), demangledFunctionName(Demangle::demangleSymbolAsString( function->getName(), Demangle::DemangleOptions::SimplifiedUIDemangleOptions())) {} public: DerivedT &operator<<(StringRef s) { args.emplace_back(s); return *static_cast(this); } DerivedT &operator<<(Argument a) { args.push_back(std::move(a)); return *static_cast(this); } DerivedT &operator<<(IndentDebug indent) { indentDebugWidth = indent.width; return *static_cast(this); } StringRef getPassName() const { return passName; } StringRef getIdentifier() const { return identifier; } SILFunction *getFunction() const { return function; } StringRef getDemangledFunctionName() const { return demangledFunctionName; } SourceLoc getLocation() const { return location; } std::string getMsg() const; std::string getDebugMsg() const; Remark &getRemark() { return *this; } SmallVector &getArgs() { return args; } ArrayRef getArgs() const { return args; } void setPassName(StringRef name) { passName = name; } }; /// Remark to report a successful optimization. struct RemarkPassed : public Remark { RemarkPassed(StringRef id, SILInstruction &i) : Remark(id, i, SourceLocInferenceBehavior::None, SourceLocPresentationKind::StartRange) {} RemarkPassed(StringRef id, SILInstruction &i, SourceLocInferenceBehavior inferenceBehavior) : Remark(id, i, inferenceBehavior, SourceLocPresentationKind::StartRange) {} RemarkPassed(StringRef id, SILInstruction &i, SourceLocInferenceBehavior inferenceBehavior, SourceLocPresentationKind locPresentationKind) : Remark(id, i, inferenceBehavior, locPresentationKind) {} }; /// Remark to report a unsuccessful optimization. struct RemarkMissed : public Remark { RemarkMissed(StringRef id, SILInstruction &i) : Remark(id, i, SourceLocInferenceBehavior::None, SourceLocPresentationKind::StartRange) {} RemarkMissed(StringRef id, SILInstruction &i, SourceLocInferenceBehavior inferenceBehavior) : Remark(id, i, inferenceBehavior, SourceLocPresentationKind::StartRange) {} RemarkMissed(StringRef id, SILInstruction &i, SourceLocInferenceBehavior inferenceBehavior, SourceLocPresentationKind locPresentationKind) : Remark(id, i, inferenceBehavior, locPresentationKind) {} }; /// Used to emit the remarks. Passes reporting remarks should create an /// instance of this. class Emitter { SILFunction &fn; std::string passName; bool passedEnabled; bool missedEnabled; // Making these non-generic allows out-of-line definition. void emit(const RemarkPassed &remark); void emit(const RemarkMissed &remark); static void emitDebug(const RemarkPassed &remark); static void emitDebug(const RemarkMissed &remark); template bool isEnabled(); public: Emitter(StringRef passName, SILFunction &fn); /// Take a lambda that returns a remark which will be emitted. The /// lambda is not evaluated unless remarks are enabled. Second argument is /// only used to restrict this to functions. template void emit(T remarkBuilder, decltype(remarkBuilder()) * = nullptr) { using RemarkT = decltype(remarkBuilder()); // Avoid building the remark unless remarks are enabled. if (isEnabled() || fn.getModule().getSILRemarkStreamer()) { auto rb = remarkBuilder(); rb.setPassName(passName); emit(rb); } } /// Emit an optimization remark or debug message. template static void emitOrDebug(const char *passName, Emitter *emitter, T remarkBuilder, decltype(remarkBuilder()) * = nullptr) { using RemarkT = decltype(remarkBuilder()); // Avoid building the remark unless remarks are enabled. bool emitRemark = emitter && (emitter->isEnabled() || emitter->fn.getModule().getSILRemarkStreamer()); // Same for DEBUG. bool shouldEmitDebug = false; #ifndef NDEBUG shouldEmitDebug |= llvm::DebugFlag && llvm::isCurrentDebugType(passName); #endif // NDEBUG if (emitRemark || shouldEmitDebug) { auto r = remarkBuilder(); if (shouldEmitDebug) emitDebug(r); if (emitRemark) { // If we have an Emitter use the PassName that was set up. DEBUG_TYPE // may be different if a pass is calling other modules. r.setPassName(emitter->passName); emitter->emit(r); } } } }; #define REMARK_OR_DEBUG(...) \ OptRemark::Emitter::emitOrDebug(DEBUG_TYPE, __VA_ARGS__) template <> inline bool Emitter::isEnabled() { return missedEnabled; } template <> inline bool Emitter::isEnabled() { return passedEnabled; } } // namespace OptRemark } // namespace swift #endif