mirror of
https://github.com/apple/swift.git
synced 2025-12-25 12:15:36 +01:00
The drivers for this change are providing a simpler API to SIL pass authors, having a more efficient of the in-memory representation, and ruling out an entire class of common bugs that usually result in hard-to-debug backend crashes. Summary ------- SILInstruction Old New +---------------+ +------------------+ +-----------------+ |SILInstruction | |SILInstruction | |SILDebugLocation | +---------------+ +------------------+ +-----------------+ | ... | | ... | | ... | |SILLocation | |SILDebugLocation *| -> |SILLocation | |SILDebugScope *| +------------------+ |SILDebugScope * | +---------------+ +-----------------+ We’re introducing a new class SILDebugLocation which represents the combination of a SILLocation and a SILDebugScope. Instead of storing an inline SILLocation and a SILDebugScope pointer, SILInstruction now only has one SILDebugLocation pointer. The APIs of SILBuilder and SILDebugLocation guarantees that every SILInstruction has a nonempty SILDebugScope. Developer-visible changes include: SILBuilder ---------- In the old design SILBuilder populated the InsertedInstrs list to allow setting the debug scopes of all built instructions in bulk at the very end (as the responsibility of the user). In the new design, SILBuilder now carries a "current debug scope" state and immediately sets the debug scope when an instruction is inserted. This fixes a use-after-free issue with with SIL passes that delete instructions before destroying the SILBuilder that created them. Because of this, SILBuilderWithScopes no longer needs to be a template, which simplifies its call sites. SILInstruction -------------- It is neither possible or necessary to manually call setDebugScope() on a SILInstruction any more. The function still exists as a private method, but is only used when splicing instructions from one function to another. Efficiency ---------- In addition to dropping 20 bytes from each SILInstruction, SILDebugLocations are now allocated in the SILModule's bump pointer allocator and are uniqued by SILBuilder. Unfortunately repeat compiles of the standard library already vary by about 5% so I couldn’t yet produce reliable numbers for how much this saves overall. rdar://problem/22017421
206 lines
6.9 KiB
C++
206 lines
6.9 KiB
C++
//===- SourceLoc.h - Source Locations and Ranges ----------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines types used to reason about source locations and ranges.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SOURCELOC_H
|
|
#define SWIFT_SOURCELOC_H
|
|
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/SMLoc.h"
|
|
#include <functional>
|
|
|
|
namespace swift {
|
|
class SourceManager;
|
|
|
|
/// SourceLoc in swift is just an SMLoc. We define it as a different type
|
|
/// (instead of as a typedef) just to remove the "getFromPointer" methods and
|
|
/// enforce purity in the Swift codebase.
|
|
class SourceLoc {
|
|
friend class SourceManager;
|
|
friend class SourceRange;
|
|
friend class CharSourceRange;
|
|
friend class DiagnosticConsumer;
|
|
|
|
llvm::SMLoc Value;
|
|
|
|
public:
|
|
SourceLoc() {}
|
|
explicit SourceLoc(llvm::SMLoc Value) : Value(Value) {}
|
|
|
|
bool isValid() const { return Value.isValid(); }
|
|
bool isInvalid() const { return !Value.isValid(); }
|
|
|
|
bool operator==(const SourceLoc &RHS) const { return RHS.Value == Value; }
|
|
bool operator!=(const SourceLoc &RHS) const { return RHS.Value != Value; }
|
|
|
|
/// Return a source location advanced a specified number of bytes.
|
|
SourceLoc getAdvancedLoc(int ByteOffset) const {
|
|
assert(isValid() && "Can't advance an invalid location");
|
|
return SourceLoc(
|
|
llvm::SMLoc::getFromPointer(Value.getPointer() + ByteOffset));
|
|
}
|
|
|
|
SourceLoc getAdvancedLocOrInvalid(int ByteOffset) const {
|
|
if (isValid())
|
|
return getAdvancedLoc(ByteOffset);
|
|
return SourceLoc();
|
|
}
|
|
|
|
const void *getOpaquePointerValue() const { return Value.getPointer(); }
|
|
|
|
/// Print out the SourceLoc. If this location is in the same buffer
|
|
/// as specified by \c LastBufferID, then we don't print the filename. If
|
|
/// not, we do print the filename, and then update \c LastBufferID with the
|
|
/// BufferID printed.
|
|
void print(raw_ostream &OS, const SourceManager &SM,
|
|
unsigned &LastBufferID) const;
|
|
|
|
void printLineAndColumn(raw_ostream &OS, const SourceManager &SM) const;
|
|
|
|
void print(raw_ostream &OS, const SourceManager &SM) const {
|
|
unsigned Tmp = ~0U;
|
|
print(OS, SM, Tmp);
|
|
}
|
|
|
|
void dump(const SourceManager &SM) const;
|
|
};
|
|
|
|
/// SourceRange in swift is a pair of locations. However, note that the end
|
|
/// location is the start of the last token in the range, not the last character
|
|
/// in the range. This is unlike SMRange, so we use a distinct type to make
|
|
/// sure that proper conversions happen where important.
|
|
class SourceRange {
|
|
public:
|
|
SourceLoc Start, End;
|
|
|
|
SourceRange() {}
|
|
SourceRange(SourceLoc Loc) : Start(Loc), End(Loc) { }
|
|
SourceRange(SourceLoc Start, SourceLoc End) : Start(Start), End(End) {
|
|
assert(Start.isValid() == End.isValid() &&
|
|
"Start and end should either both be valid or both be invalid!");
|
|
}
|
|
|
|
bool isValid() const { return Start.isValid(); }
|
|
bool isInvalid() const { return Start.isInvalid(); }
|
|
|
|
/// Print out the SourceRange. If the locations are in the same buffer
|
|
/// as specified by LastBufferID, then we don't print the filename. If not,
|
|
/// we do print the filename, and then update LastBufferID with the BufferID
|
|
/// printed.
|
|
void print(raw_ostream &OS, const SourceManager &SM,
|
|
unsigned &LastBufferID, bool PrintText = true) const;
|
|
|
|
void print(raw_ostream &OS, const SourceManager &SM,
|
|
bool PrintText = true) const {
|
|
unsigned Tmp = ~0U;
|
|
print(OS, SM, Tmp, PrintText);
|
|
}
|
|
|
|
void dump(const SourceManager &SM) const;
|
|
};
|
|
|
|
/// A half-open character-based source range.
|
|
class CharSourceRange {
|
|
SourceLoc Start;
|
|
unsigned ByteLength;
|
|
|
|
public:
|
|
/// \brief Constructs an invalid range.
|
|
CharSourceRange() {}
|
|
|
|
CharSourceRange(SourceLoc Start, unsigned ByteLength)
|
|
: Start(Start), ByteLength(ByteLength) { }
|
|
|
|
/// \brief Constructs a character range which starts and ends at the
|
|
/// specified character locations.
|
|
CharSourceRange(const SourceManager &SM, SourceLoc Start, SourceLoc End);
|
|
|
|
/// Use Lexer::getCharSourceRangeFromSourceRange() instead.
|
|
CharSourceRange(const SourceManager &SM, SourceRange Range) = delete;
|
|
|
|
bool isValid() const { return Start.isValid(); }
|
|
bool isInvalid() const { return Start.isInvalid(); }
|
|
|
|
SourceLoc getStart() const { return Start; }
|
|
SourceLoc getEnd() const {
|
|
if (Start.isValid())
|
|
return Start.getAdvancedLoc(ByteLength);
|
|
else
|
|
return SourceLoc();
|
|
}
|
|
|
|
/// Returns true if the given source location is contained in the range.
|
|
bool contains(SourceLoc loc) const {
|
|
auto less = std::less<const char *>();
|
|
auto less_equal = std::less_equal<const char *>();
|
|
return less_equal(getStart().Value.getPointer(), loc.Value.getPointer()) &&
|
|
less(loc.Value.getPointer(), getEnd().Value.getPointer());
|
|
}
|
|
|
|
bool contains(CharSourceRange Other) const {
|
|
auto less_equal = std::less_equal<const char *>();
|
|
return contains(Other.getStart()) &&
|
|
less_equal(Other.getEnd().Value.getPointer(), getEnd().Value.getPointer());
|
|
}
|
|
|
|
/// \brief expands *this to cover Other
|
|
void widen(CharSourceRange Other) {
|
|
auto Diff = Other.getEnd().Value.getPointer() - getEnd().Value.getPointer();
|
|
if (Diff > 0) {
|
|
ByteLength += Diff;
|
|
}
|
|
const auto MyStartPtr = getStart().Value.getPointer();
|
|
Diff = MyStartPtr - Other.getStart().Value.getPointer();
|
|
if (Diff > 0) {
|
|
ByteLength += Diff;
|
|
Start = SourceLoc(llvm::SMLoc::getFromPointer(MyStartPtr - Diff));
|
|
}
|
|
}
|
|
|
|
bool overlaps(CharSourceRange Other) const {
|
|
return contains(Other.getStart()) || contains(Other.getEnd());
|
|
}
|
|
|
|
StringRef str() const {
|
|
return StringRef(Start.Value.getPointer(), ByteLength);
|
|
}
|
|
|
|
/// \brief Return the length of this valid range in bytes. Can be zero.
|
|
unsigned getByteLength() const {
|
|
assert(isValid() && "length does not make sense for an invalid range");
|
|
return ByteLength;
|
|
}
|
|
|
|
/// Print out the CharSourceRange. If the locations are in the same buffer
|
|
/// as specified by LastBufferID, then we don't print the filename. If not,
|
|
/// we do print the filename, and then update LastBufferID with the BufferID
|
|
/// printed.
|
|
void print(raw_ostream &OS, const SourceManager &SM,
|
|
unsigned &LastBufferID, bool PrintText = true) const;
|
|
|
|
void print(raw_ostream &OS, const SourceManager &SM,
|
|
bool PrintText = true) const {
|
|
unsigned Tmp = ~0U;
|
|
print(OS, SM, Tmp, PrintText);
|
|
}
|
|
|
|
void dump(const SourceManager &SM) const;
|
|
};
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|