Files
swift-mirror/include/swift/SIL/SILLocation.h
Erik Eckstein 64ba6fdf5d SIL: simplify the SILNode inline bitfields.
The use of the SWIFT_INLINE_BITFIELD macros in SILNode were a constant source of confusion and bugs.
With this refactoring I tried to simplify the definition of "shared fields" in SILNode, SILValue and SILInstruction classes:

* Move `kind`, `locationKindAndFlags` and the 32-bit fields out of the 64-bitfield into their own member variables. This avoids _a lot_ of manual bit position computations.
* Now we have two separate "shared fields": an 8-bit field (e.g. for boolean flags) and a 32-bit field (e.g. for indices, which can potentially get large). Both fields can be used independently. Also, they are not "bit fields" per se. Instructions can use the field e.g. as a `bool`, `uint32_t`, or  - if multiple flags are to be stored - as a packed bit field.
* With these two separate fields, we don't have the need for defining bitfields both in a base class _and_ in a derived value/instruction class. We can get rid of the complex logic which handles such cases. Just keep a check to catch accidental overlaps of fields in base and derived classes.
* Still use preprocessor macros for the implementation, but much simpler ones than before.
* Add documentation.
2022-07-11 17:27:29 +02:00

667 lines
23 KiB
C++

//===--- SILLocation.h - Location information for SIL nodes -----*- 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SIL_LOCATION_H
#define SWIFT_SIL_LOCATION_H
#include "llvm/ADT/PointerUnion.h"
#include "swift/Basic/SourceLoc.h"
#include "swift/SIL/SILAllocated.h"
#include "swift/AST/TypeAlignments.h"
#include <cstddef>
#include <type_traits>
namespace swift {
class ReturnStmt;
class BraceStmt;
class AbstractClosureExpr;
class AbstractFunctionDecl;
class SILModule;
class SourceManager;
/// Represents a source location.
///
/// In most cases this is a pointer to an AST node from which the source
/// location can be retrieved from. In case the location refers to a parsed
/// or deserialized SIL file, the source location is represented differently.
/// For details see SILLocation::Storage.
class SILLocation {
public:
enum LocationKind : uint8_t {
RegularKind = 1,
ReturnKind = 2,
ImplicitReturnKind = 3,
InlinedKind = 4,
MandatoryInlinedKind = 5,
CleanupKind = 6,
ArtificialUnreachableKind = 7
};
/// Describes a position in a source file by explicitly storing the file name,
/// line and column.
///
/// This is used for parsed locations from a SIL file, for
/// "-sil-based-debuginfo" (see SILDebugInfoGenerator) and for the
/// "compiler-generated" singleton location.
/// In future we might also use it for de-serialized locations from a
/// swiftmodule file.
struct FilenameAndLocation : public SILAllocated<FilenameAndLocation> {
unsigned line;
uint16_t column;
StringRef filename;
FilenameAndLocation(unsigned line = 0, uint16_t column = 0,
StringRef filename = StringRef())
: line(line), column(column), filename(filename) {}
static FilenameAndLocation *alloc(unsigned line, unsigned column,
StringRef filename, SILModule &module);
inline bool operator==(const FilenameAndLocation &rhs) const {
return line == rhs.line && column == rhs.column &&
filename.equals(rhs.filename);
}
void dump() const;
void print(raw_ostream &OS) const;
friend llvm::hash_code hash_value(const FilenameAndLocation &);
};
protected:
template <class T, class Enable = void>
struct base_type;
template <class T>
struct base_type<T,
typename std::enable_if<std::is_base_of<Decl, T>::value>::type> {
using type = Decl;
};
template <class T>
struct base_type<T,
typename std::enable_if<std::is_base_of<Expr, T>::value>::type> {
using type = Expr;
};
template <class T>
struct base_type<T,
typename std::enable_if<std::is_base_of<Stmt, T>::value>::type> {
using type = Stmt;
};
template <class T>
struct base_type<T,
typename std::enable_if<std::is_base_of<Pattern, T>::value>::type> {
using type = Pattern;
};
using ASTNodeTy = llvm::PointerUnion<Stmt *, Expr *, Decl *, Pattern *>;
/// Used in case the location for diagnostics does not match the location for
/// debugging.
struct ExtendedASTNodeLoc : public SILAllocated<ExtendedASTNodeLoc> {
/// Primary AST location, always used for diagnostics.
ASTNodeTy primary;
/// Sometimes the location for diagnostics needs to be different than the
/// one used to emit the line table for debugging.
ASTNodeTy forDebugging;
ExtendedASTNodeLoc(ASTNodeTy primary, ASTNodeTy forDebugging) :
primary(primary), forDebugging(forDebugging) {}
};
private:
friend class SILInstruction;
/// Each kind corresponds to a union member in `Storage`.
enum StorageKind : uint8_t {
/// For usages see the struct `FilenameAndLocation`.
FilenameAndLocationKind,
/// The most common location kind: a pointer to the AST from which the
/// source location can be retrieved.
/// This is the "normal" case when compiling a swift file.
ASTNodeKind,
/// Use for a few locations in pattern-code. See ExtendedASTNodeLoc.
ExtendedASTNodeKind,
/// This is used when parsing a SIL file.
/// Note: this is only used for functions/instruction for which there is no
/// location specified in the SIL file.
/// In case a SIL instruction is annotated with a parsable location in
/// the SIL file, the instruction gets a FilenameAndLocation.
SourceLocKind
};
/// The storage which actually points to the source location. Each union
/// member corresponds to a StorageKind and is basically a pointer.
/// Note that a referenced FilenameAndLocation or ExtendedASTNodeLoc is not
/// owned by SILLocation, thus not freed on destruction. Those data structures
/// are allocated with the SILModule's bump pointer allocator.
union Storage {
Storage() : filePositionLoc(nullptr) {}
Storage(FilenameAndLocation *F) : filePositionLoc(F) {}
Storage(ASTNodeTy N) : ASTNodeLoc(N) {}
Storage(ExtendedASTNodeLoc *E) : extendedASTNodeLoc(E) {}
Storage(SourceLoc L) : sourceLoc(L) {}
const FilenameAndLocation *filePositionLoc;
ASTNodeTy ASTNodeLoc;
const ExtendedASTNodeLoc *extendedASTNodeLoc;
SourceLoc sourceLoc;
};
static_assert(sizeof(SILLocation::Storage) == sizeof(void *),
"SILLocation::Storage must stay small");
/// Contains the LocationKind, StorageKind and some extra flags.
/// This fits nicely in a single byte. If for some reason we need more flags
/// it's possible to extend this to e.g. a uint16_t.
/// But SILNode::locationKindAndFlags must be updated accordingly.
union KindAndFlags {
KindAndFlags() : packedKindAndFlags(0) {}
KindAndFlags(LocationKind kind, StorageKind storageKind)
: fields({kind, storageKind, 0, 0, 0 }) {
assert(fields.kind == kind && "LocationKind overflow");
assert(fields.storageKind == storageKind && "StorageKind overflow");
}
KindAndFlags(uint8_t packed) : packedKindAndFlags(packed) {}
uint8_t packedKindAndFlags;
struct Fields {
uint8_t kind: 3;
uint8_t storageKind: 2;
uint8_t autoGenerated: 1;
uint8_t pointsToEnd: 1;
uint8_t inPrologue: 1;
} fields;
};
static_assert(sizeof(SILLocation::KindAndFlags) == sizeof(uint8_t),
"SILLocation::KindAndFlags should be 1 byte");
// Data fields:
Storage storage;
KindAndFlags kindAndFlags;
StorageKind getStorageKind() const {
return (StorageKind)kindAndFlags.fields.storageKind;
}
ASTNodeTy getPrimaryASTNode() const {
switch (getStorageKind()) {
case ASTNodeKind:
return storage.ASTNodeLoc;
case ExtendedASTNodeKind:
return storage.extendedASTNodeLoc->primary;
case SourceLocKind:
case FilenameAndLocationKind:
llvm_unreachable("location type has no AST node");
}
llvm_unreachable("covered switch");
}
/// Returns true if the location has a separate AST node for debugging.
/// See ExtendedASTNodeLoc.
bool hasASTNodeForDebugging() const {
return getStorageKind() == ExtendedASTNodeKind;
}
template <typename T>
T *getNodeAs(ASTNodeTy Node) const {
using base = typename base_type<T>::type*;
return dyn_cast_or_null<T>(Node.dyn_cast<base>());
}
template <typename T>
bool isNode(ASTNodeTy Node) const {
assert(isASTNode());
ASTNodeTy primaryNd = getPrimaryASTNode();
if (primaryNd.is<typename base_type<T>::type*>())
return isa<T>(Node.get<typename base_type<T>::type*>());
return false;
}
template <typename T>
T *castNodeTo(ASTNodeTy Node) const {
return cast<T>(Node.get<typename base_type<T>::type*>());
}
SourceLoc getSourceLoc(ASTNodeTy N) const;
static SourceLoc getStartSourceLoc(ASTNodeTy N);
static SourceLoc getEndSourceLoc(ASTNodeTy N);
protected:
SILLocation(LocationKind K) : kindAndFlags(K, FilenameAndLocationKind) {}
/// Constructs a null location.
SILLocation() : SILLocation(RegularKind) { assert(isNull()); }
/// Constructs a location like \p l, but with a different kind.
SILLocation(SILLocation l, LocationKind K)
: storage(l.storage), kindAndFlags(l.kindAndFlags.packedKindAndFlags) {
kindAndFlags.fields.kind = K;
}
SILLocation(FilenameAndLocation *filePos, LocationKind K)
: storage(filePos), kindAndFlags(K, FilenameAndLocationKind) {
assert(filePos && !filePos->filename.empty());
}
// It is okay to pass a nullptr, but preferably, a null-location should be
// created with `invalid()`.
SILLocation(ASTNodeTy Node, LocationKind K)
: storage(Node), kindAndFlags(K, ASTNodeKind) {}
SILLocation(ExtendedASTNodeLoc *ext, LocationKind K)
: storage(ext), kindAndFlags(K, ExtendedASTNodeKind) {
assert(ext && !ext->forDebugging.isNull());
}
SILLocation(SourceLoc L, LocationKind K)
: storage(L), kindAndFlags(K, SourceLocKind) {}
// Used by SILInstruction.
SILLocation(Storage storage, uint8_t packedKindAndFlags) :
storage(storage), kindAndFlags(packedKindAndFlags) {}
public:
// When an ASTNode gets implicitly converted into a SILLocation we
// construct a RegularLocation. Since RegularLocations represent the majority
// of locations, this greatly simplifies the user code.
//
// It is okay to pass a nullptr to these constructors, but preferably, a
// null-location should be created with `invalid()`.
SILLocation(Stmt *S) : SILLocation(ASTNodeTy(S), RegularKind) {}
SILLocation(Expr *E) : SILLocation(ASTNodeTy(E), RegularKind) {}
SILLocation(Decl *D) : SILLocation(ASTNodeTy(D), RegularKind) {}
SILLocation(Pattern *P) : SILLocation(ASTNodeTy(P), RegularKind) {}
/// Returns a null location.
static SILLocation invalid() { return SILLocation(); }
LocationKind getKind() const {
return (LocationKind)kindAndFlags.fields.kind;
}
/// Artificial locations and the top-level module locations will be null.
bool isNull() const {
switch (getStorageKind()) {
case ASTNodeKind:
case ExtendedASTNodeKind: return !getPrimaryASTNode();
case FilenameAndLocationKind: return storage.filePositionLoc == nullptr;
case SourceLocKind: return storage.sourceLoc.isInvalid();
}
llvm_unreachable("covered switch");
}
explicit operator bool() const { return !isNull(); }
/// Return true if this location is backed by an AST node.
bool isASTNode() const {
switch (getStorageKind()) {
case ASTNodeKind:
case ExtendedASTNodeKind:
return true;
case SourceLocKind:
case FilenameAndLocationKind:
return false;
}
llvm_unreachable("covered switch");
}
/// Returns true if this location came from a SIL file.
bool isSILFile() const { return getStorageKind() == SourceLocKind; }
bool isFilenameAndLocation() const {
return getStorageKind() == FilenameAndLocationKind && !isNull();
}
const FilenameAndLocation *getFilenameAndLocation() const {
assert(isFilenameAndLocation());
return storage.filePositionLoc;
}
/// Marks the location as coming from auto-generated body.
void markAutoGenerated() { kindAndFlags.fields.autoGenerated = true; }
/// Returns this location with the auto-generated flag set.
SILLocation asAutoGenerated() const {
SILLocation loc = *this;
loc.markAutoGenerated();
return loc;
}
/// Returns true if the location represents an artificially generated
/// body, such as thunks or default destructors.
bool isAutoGenerated() const { return kindAndFlags.fields.autoGenerated; }
/// Returns false if the location should be represented in debuginfo.
bool isHiddenFromDebugInfo() const {
return isAutoGenerated() && !hasASTNodeForDebugging();
}
/// Returns true if the line number of this location is zero.
bool isLineZero(const SourceManager &SM) const {
return decodeForDebugging(SM).line == 0;
}
/// Changes the default source location position to point to the end of
/// the AST node.
void pointToEnd() { kindAndFlags.fields.pointsToEnd = true; }
/// Mark this location as being part of the function
/// prologue, which means that it deals with setting up the stack
/// frame. The first breakpoint location in a function is at the end
/// of the prologue.
void markAsPrologue() { kindAndFlags.fields.inPrologue = true; }
/// Check is this location is part of a function's implicit prologue.
bool isInPrologue() const { return kindAndFlags.fields.inPrologue; }
/// Check if the corresponding source code location definitely points
/// to the end of the AST node.
bool alwaysPointsToEnd() const { return kindAndFlags.fields.pointsToEnd; }
template <typename T> bool is() const { return T::isKind(*this); }
/// If the current value is of the specified AST unit type T,
/// return it, otherwise return null.
template <typename T> T *getAsASTNode() const {
return isASTNode() ? getNodeAs<T>(getPrimaryASTNode()) : nullptr;
}
/// Returns true if the Location currently points to the AST node
/// matching type T.
template <typename T> bool isASTNode() const {
return isASTNode() && isNode<T>(getPrimaryASTNode());
}
/// Returns the primary value as the specified AST node type. If the
/// specified type is incorrect, asserts.
template <typename T> T *castToASTNode() const {
return castNodeTo<T>(getPrimaryASTNode());
}
/// Return the location as a DeclContext or null.
DeclContext *getAsDeclContext() const;
/// Returns the source location for diagnoses.
SourceLoc getSourceLoc() const;
/// Returns the source location for debugging.
/// See ExtendedASTNodeLoc.
SourceLoc getSourceLocForDebugging() const;
SourceLoc getStartSourceLoc() const;
SourceLoc getEndSourceLoc() const;
SourceRange getSourceRange() const {
return {getStartSourceLoc(), getEndSourceLoc()};
}
/// Extract the line, column, and filename from \p Loc.
static FilenameAndLocation decode(SourceLoc Loc, const SourceManager &SM);
/// Return the decoded FilenameAndLocation.
/// In case the location has a separate AST node for debugging, this node is
/// used. See ExtendedASTNodeLoc.
FilenameAndLocation decodeForDebugging(const SourceManager &SM) const {
return isFilenameAndLocation() ? *getFilenameAndLocation()
: decode(getSourceLocForDebugging(), SM);
}
/// Compiler-generated locations may be applied to instructions without any
/// clear correspondence to an AST node in an otherwise normal function.
static FilenameAndLocation *getCompilerGeneratedLoc();
/// Pretty-print the value.
void dump() const;
void print(raw_ostream &OS, const SourceManager &SM) const;
inline bool operator==(const SILLocation& R) const {
return kindAndFlags.packedKindAndFlags == R.kindAndFlags.packedKindAndFlags
&& storage.filePositionLoc == R.storage.filePositionLoc;
}
inline bool operator!=(const SILLocation &R) const { return !(*this == R); }
friend llvm::hash_code hash_value(const SILLocation &);
};
inline llvm::hash_code hash_value(const SILLocation &R) {
return llvm::hash_combine(R.kindAndFlags.packedKindAndFlags,
*R.storage.filePositionLoc);
}
inline llvm::hash_code hash_value(const SILLocation::FilenameAndLocation &R) {
return llvm::hash_combine(R.line, R.column, R.filename);
}
/// Allowed on any instruction.
class RegularLocation : public SILLocation {
public:
RegularLocation(Stmt *S) : SILLocation(ASTNodeTy(S), RegularKind) {}
RegularLocation(Expr *E) : SILLocation(ASTNodeTy(E), RegularKind) {}
RegularLocation(Decl *D) : SILLocation(ASTNodeTy(D), RegularKind) {}
RegularLocation(Pattern *P) : SILLocation(ASTNodeTy(P), RegularKind) {}
RegularLocation(Stmt *S, Pattern *P, SILModule &Module);
RegularLocation(SILLocation ForDebuggingOnly, SILModule &Module);
RegularLocation(SourceLoc L) : SILLocation(L, RegularKind) {}
RegularLocation(FilenameAndLocation *filePos)
: SILLocation(filePos, RegularKind) {}
/// Convert \p loc to a RegularLocation.
explicit RegularLocation(SILLocation loc) : SILLocation(loc, RegularKind) {}
/// Returns a location representing the module.
/// This is just a null location.
static RegularLocation getModuleLocation() { return RegularLocation(); }
/// Compiler-generated locations may be applied to instructions without any
/// clear correspondence to an AST node in an otherwise normal function.
/// The auto-generated bit also turns off certain diagnostics passes such.
static RegularLocation getAutoGeneratedLocation() {
RegularLocation AL(getCompilerGeneratedLoc());
AL.markAutoGenerated();
return AL;
}
/// Returns a location that is compiler-generated, but with a hint as to where
/// it may have been generated from. These locations will have an artificial
/// line location of zero in DWARF, but in CodeView we want to use the given
/// line since line zero does not represent an artificial line in CodeView.
template <typename InputLocTy>
static RegularLocation getAutoGeneratedLocation(InputLocTy L) {
RegularLocation AL(L);
AL.markAutoGenerated();
return AL;
}
/// Returns a location that is empty for diagnostics, and L for the debug info
/// Used for \c hop_to_executor instructions.
static RegularLocation getDebugOnlyLocation(SILLocation L, SILModule &M) {
if (L.isASTNode())
return RegularLocation(L, M);
return getAutoGeneratedLocation(L);
}
static bool isKind(const SILLocation& L) {
return L.getKind() == RegularKind;
}
private:
RegularLocation() : SILLocation(RegularKind) {}
static SILLocation::ExtendedASTNodeLoc *
getDebugOnlyExtendedASTNodeLoc(SILLocation L, SILModule &Module);
};
/// Used to represent a return instruction in user code.
///
/// Allowed on an BranchInst, ReturnInst.
class ReturnLocation : public SILLocation {
public:
ReturnLocation(ReturnStmt *RS);
/// Construct the return location for a constructor or a destructor.
ReturnLocation(BraceStmt *BS);
ReturnLocation(FilenameAndLocation *filePos)
: SILLocation(filePos, ReturnKind) {}
static bool isKind(const SILLocation& L) {
return L.getKind() == ReturnKind;
}
};
/// Used on the instruction that was generated to represent an implicit
/// return from a function.
///
/// Allowed on an BranchInst, ReturnInst.
class ImplicitReturnLocation : public SILLocation {
public:
ImplicitReturnLocation(AbstractClosureExpr *E);
ImplicitReturnLocation(ReturnStmt *S);
ImplicitReturnLocation(AbstractFunctionDecl *AFD);
ImplicitReturnLocation(FilenameAndLocation *filePos)
: SILLocation(filePos, ImplicitReturnKind) {}
/// Convert \p loc to an ImplicitReturnLocation.
explicit ImplicitReturnLocation(SILLocation Loc);
static bool isKind(const SILLocation& L) {
return L.getKind() == ImplicitReturnKind;
}
};
/// Marks instructions that correspond to inlined function body and
/// setup code. This location should not be used for inlined transparent
/// bodies, see MandatoryInlinedLocation.
///
/// This location wraps the call site ASTNode.
///
/// Allowed on any instruction except for ReturnInst.
class InlinedLocation : public SILLocation {
public:
/// Convert \p loc to an InlinedLocation.
InlinedLocation(SILLocation L) : SILLocation(L, InlinedKind) {}
static bool isKind(const SILLocation& L) {
return L.getKind() == InlinedKind;
}
};
/// Marks instructions that correspond to inlined function body and
/// setup code for transparent functions, inlined as part of mandatory inlining
/// pass.
///
/// This location wraps the call site ASTNode.
///
/// Allowed on any instruction except for ReturnInst.
class MandatoryInlinedLocation : public SILLocation {
public:
MandatoryInlinedLocation() : SILLocation(MandatoryInlinedKind) {}
/// Convert \p loc to a MandatoryInlinedLocation.
explicit MandatoryInlinedLocation(SILLocation L)
: SILLocation(L, MandatoryInlinedKind) {}
static MandatoryInlinedLocation getAutoGeneratedLocation() {
return MandatoryInlinedLocation(
RegularLocation::getAutoGeneratedLocation());
}
static bool isKind(const SILLocation& L) {
return L.getKind() == MandatoryInlinedKind;
}
};
/// Used on the instruction performing auto-generated cleanup such as
/// deallocs, destructor calls.
///
/// The cleanups are performed after completing the evaluation of the AST Node
/// wrapped inside the SILLocation. This location wraps the statement
/// representing the enclosing scope, for example, FuncDecl, ParenExpr. The
/// scope's end location points to the SourceLoc that shows when the operation
/// is performed at runtime.
///
/// Allowed on any instruction except for ReturnInst.
/// Locations of an inlined destructor should also be represented by this.
class CleanupLocation : public SILLocation {
public:
CleanupLocation(Expr *E) : SILLocation(ASTNodeTy(E), CleanupKind) {}
CleanupLocation(Stmt *S) : SILLocation(ASTNodeTy(S), CleanupKind) {}
CleanupLocation(Pattern *P) : SILLocation(ASTNodeTy(P), CleanupKind) {}
CleanupLocation(Decl *D) : SILLocation(ASTNodeTy(D), CleanupKind) {}
/// Convert \p loc to a CleanupLocation.
explicit CleanupLocation(SILLocation L) : SILLocation(L, CleanupKind) {}
/// Returns a null location.
static CleanupLocation invalid() { return CleanupLocation(); }
/// Returns a location representing a cleanup on the module level.
/// This is just a null location.
static CleanupLocation getModuleCleanupLocation() {
return CleanupLocation();
}
static bool isKind(const SILLocation& L) {
return L.getKind() == CleanupKind;
}
private:
CleanupLocation() : SILLocation(CleanupKind) {}
};
/// Used to represent an unreachable location that was
/// auto-generated and has no correspondence to user code. It should
/// not be used in diagnostics or for debugging.
///
/// Differentiates an unreachable instruction, which is generated by
/// DCE, from an unreachable instruction in user code (output of SILGen).
/// Allowed on an unreachable instruction.
class ArtificialUnreachableLocation : public SILLocation {
public:
ArtificialUnreachableLocation() : SILLocation(ArtificialUnreachableKind) {}
static bool isKind(const SILLocation& L) {
return (L.getKind() == ArtificialUnreachableKind);
}
};
class SILDebugScope;
/// A SILLocation paired with a SILDebugScope.
class SILDebugLocation {
const SILDebugScope *debugScope;
SILLocation location;
public:
SILDebugLocation()
: debugScope(nullptr),
location(RegularLocation::getAutoGeneratedLocation()) {}
SILDebugLocation(SILLocation location, const SILDebugScope *debugScope)
: debugScope(debugScope), location(location) {}
SILLocation getLocation() const { return location; }
const SILDebugScope *getScope() const { return debugScope; }
operator bool() const { return bool(location) && debugScope; }
};
} // end swift namespace
#endif