blob: 3e3c05cb6463f1e7aa62d9e60f1c520fe602ee2a [file] [log] [blame]
//===--- 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 SourceLocInferenceBehavior : unsigned {
None = 0,
ForwardScan = 0x1,
BackwardScan = 0x2,
ForwardScan2nd = 0x4,
AlwaysInfer = 0x8,
ForwardThenBackwards = ForwardScan | BackwardScan,
BackwardsThenForwards = BackwardScan | ForwardScan2nd,
ForwardScanAlwaysInfer = ForwardScan | AlwaysInfer,
BackwardScanAlwaysInfer = BackwardScan | AlwaysInfer,
};
inline SourceLocInferenceBehavior operator&(SourceLocInferenceBehavior lhs,
SourceLocInferenceBehavior rhs) {
auto lhsVal = std::underlying_type<SourceLocInferenceBehavior>::type(lhs);
auto rhsVal = std::underlying_type<SourceLocInferenceBehavior>::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);
/// 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 <typename DerivedT>
class Remark {
/// Arguments collected via the streaming interface.
SmallVector<Argument, 4> 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)
: identifier((Twine("sil.") + identifier).str()),
location(inferOptRemarkSourceLoc(i, inferenceBehavior)),
function(i.getParent()->getParent()),
demangledFunctionName(Demangle::demangleSymbolAsString(
function->getName(),
Demangle::DemangleOptions::SimplifiedUIDemangleOptions())) {}
public:
DerivedT &operator<<(StringRef s) {
args.emplace_back(s);
return *static_cast<DerivedT *>(this);
}
DerivedT &operator<<(Argument a) {
args.push_back(std::move(a));
return *static_cast<DerivedT *>(this);
}
DerivedT &operator<<(IndentDebug indent) {
indentDebugWidth = indent.width;
return *static_cast<DerivedT *>(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<DerivedT> &getRemark() { return *this; }
SmallVector<Argument, 4> &getArgs() { return args; }
ArrayRef<Argument> getArgs() const { return args; }
void setPassName(StringRef name) { passName = name; }
};
/// Remark to report a successful optimization.
struct RemarkPassed : public Remark<RemarkPassed> {
RemarkPassed(StringRef id, SILInstruction &i)
: Remark(id, i, SourceLocInferenceBehavior::None) {}
RemarkPassed(StringRef id, SILInstruction &i,
SourceLocInferenceBehavior inferenceBehavior)
: Remark(id, i, inferenceBehavior) {}
};
/// Remark to report a unsuccessful optimization.
struct RemarkMissed : public Remark<RemarkMissed> {
RemarkMissed(StringRef id, SILInstruction &i)
: Remark(id, i, SourceLocInferenceBehavior::None) {}
RemarkMissed(StringRef id, SILInstruction &i,
SourceLocInferenceBehavior inferenceBehavior)
: Remark(id, i, inferenceBehavior) {}
};
/// 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 <typename RemarkT> 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 <typename T>
void emit(T remarkBuilder, decltype(remarkBuilder()) * = nullptr) {
using RemarkT = decltype(remarkBuilder());
// Avoid building the remark unless remarks are enabled.
if (isEnabled<RemarkT>() || fn.getModule().getSILRemarkStreamer()) {
auto rb = remarkBuilder();
rb.setPassName(passName);
emit(rb);
}
}
/// Emit an optimization remark or debug message.
template <typename T>
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<RemarkT>() ||
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<RemarkMissed>() {
return missedEnabled;
}
template <> inline bool Emitter::isEnabled<RemarkPassed>() {
return passedEnabled;
}
} // namespace OptRemark
} // namespace swift
#endif