143 changes: 91 additions & 52 deletions lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,13 @@ class ELFRelocation {

static unsigned RelocSymbol64(const ELFRelocation &rel);

static unsigned RelocOffset32(const ELFRelocation &rel);
static elf_addr RelocOffset32(const ELFRelocation &rel);

static unsigned RelocOffset64(const ELFRelocation &rel);
static elf_addr RelocOffset64(const ELFRelocation &rel);

static unsigned RelocAddend32(const ELFRelocation &rel);
static elf_sxword RelocAddend32(const ELFRelocation &rel);

static unsigned RelocAddend64(const ELFRelocation &rel);
static elf_sxword RelocAddend64(const ELFRelocation &rel);

bool IsRela() { return (reloc.is<ELFRela *>()); }

Expand Down Expand Up @@ -185,28 +185,28 @@ unsigned ELFRelocation::RelocSymbol64(const ELFRelocation &rel) {
return ELFRela::RelocSymbol64(*rel.reloc.get<ELFRela *>());
}

unsigned ELFRelocation::RelocOffset32(const ELFRelocation &rel) {
elf_addr ELFRelocation::RelocOffset32(const ELFRelocation &rel) {
if (rel.reloc.is<ELFRel *>())
return rel.reloc.get<ELFRel *>()->r_offset;
else
return rel.reloc.get<ELFRela *>()->r_offset;
}

unsigned ELFRelocation::RelocOffset64(const ELFRelocation &rel) {
elf_addr ELFRelocation::RelocOffset64(const ELFRelocation &rel) {
if (rel.reloc.is<ELFRel *>())
return rel.reloc.get<ELFRel *>()->r_offset;
else
return rel.reloc.get<ELFRela *>()->r_offset;
}

unsigned ELFRelocation::RelocAddend32(const ELFRelocation &rel) {
elf_sxword ELFRelocation::RelocAddend32(const ELFRelocation &rel) {
if (rel.reloc.is<ELFRel *>())
return 0;
else
return rel.reloc.get<ELFRela *>()->r_addend;
}

unsigned ELFRelocation::RelocAddend64(const ELFRelocation &rel) {
elf_sxword ELFRelocation::RelocAddend64(const ELFRelocation &rel) {
if (rel.reloc.is<ELFRel *>())
return 0;
else
Expand Down Expand Up @@ -2593,6 +2593,50 @@ ObjectFileELF::ParseTrampolineSymbols(Symtab *symbol_table, user_id_t start_id,
rel_data, symtab_data, strtab_data);
}

static void ApplyELF64ABS64Relocation(Symtab *symtab, ELFRelocation &rel,
DataExtractor &debug_data,
Section *rel_section) {
Symbol *symbol = symtab->FindSymbolByID(ELFRelocation::RelocSymbol64(rel));
if (symbol) {
addr_t value = symbol->GetAddressRef().GetFileAddress();
DataBufferSP &data_buffer_sp = debug_data.GetSharedDataBuffer();
// ObjectFileELF creates a WritableDataBuffer in CreateInstance.
WritableDataBuffer *data_buffer =
llvm::cast<WritableDataBuffer>(data_buffer_sp.get());
uint64_t *dst = reinterpret_cast<uint64_t *>(
data_buffer->GetBytes() + rel_section->GetFileOffset() +
ELFRelocation::RelocOffset64(rel));
uint64_t val_offset = value + ELFRelocation::RelocAddend64(rel);
memcpy(dst, &val_offset, sizeof(uint64_t));
}
}

static void ApplyELF64ABS32Relocation(Symtab *symtab, ELFRelocation &rel,
DataExtractor &debug_data,
Section *rel_section, bool is_signed) {
Symbol *symbol = symtab->FindSymbolByID(ELFRelocation::RelocSymbol64(rel));
if (symbol) {
addr_t value = symbol->GetAddressRef().GetFileAddress();
value += ELFRelocation::RelocAddend32(rel);
if ((!is_signed && (value > UINT32_MAX)) ||
(is_signed &&
((int64_t)value > INT32_MAX || (int64_t)value < INT32_MIN))) {
Log *log = GetLog(LLDBLog::Modules);
LLDB_LOGF(log, "Failed to apply debug info relocations");
return;
}
uint32_t truncated_addr = (value & 0xFFFFFFFF);
DataBufferSP &data_buffer_sp = debug_data.GetSharedDataBuffer();
// ObjectFileELF creates a WritableDataBuffer in CreateInstance.
WritableDataBuffer *data_buffer =
llvm::cast<WritableDataBuffer>(data_buffer_sp.get());
uint32_t *dst = reinterpret_cast<uint32_t *>(
data_buffer->GetBytes() + rel_section->GetFileOffset() +
ELFRelocation::RelocOffset32(rel));
memcpy(dst, &truncated_addr, sizeof(uint32_t));
}
}

unsigned ObjectFileELF::ApplyRelocations(
Symtab *symtab, const ELFHeader *hdr, const ELFSectionHeader *rel_hdr,
const ELFSectionHeader *symtab_hdr, const ELFSectionHeader *debug_hdr,
Expand Down Expand Up @@ -2656,55 +2700,50 @@ unsigned ObjectFileELF::ApplyRelocations(
reloc_type(rel));
}
} else {
switch (reloc_type(rel)) {
case R_AARCH64_ABS64:
case R_X86_64_64: {
symbol = symtab->FindSymbolByID(reloc_symbol(rel));
if (symbol) {
addr_t value = symbol->GetAddressRef().GetFileAddress();
DataBufferSP &data_buffer_sp = debug_data.GetSharedDataBuffer();
// ObjectFileELF creates a WritableDataBuffer in CreateInstance.
WritableDataBuffer *data_buffer =
llvm::cast<WritableDataBuffer>(data_buffer_sp.get());
uint64_t *dst = reinterpret_cast<uint64_t *>(
data_buffer->GetBytes() + rel_section->GetFileOffset() +
ELFRelocation::RelocOffset64(rel));
uint64_t val_offset = value + ELFRelocation::RelocAddend64(rel);
memcpy(dst, &val_offset, sizeof(uint64_t));
switch (hdr->e_machine) {
case llvm::ELF::EM_AARCH64:
switch (reloc_type(rel)) {
case R_AARCH64_ABS64:
ApplyELF64ABS64Relocation(symtab, rel, debug_data, rel_section);
break;
case R_AARCH64_ABS32:
ApplyELF64ABS32Relocation(symtab, rel, debug_data, rel_section, true);
break;
default:
assert(false && "unexpected relocation type");
}
break;
}
case R_X86_64_32:
case R_X86_64_32S:
case R_AARCH64_ABS32: {
symbol = symtab->FindSymbolByID(reloc_symbol(rel));
if (symbol) {
addr_t value = symbol->GetAddressRef().GetFileAddress();
value += ELFRelocation::RelocAddend32(rel);
if ((reloc_type(rel) == R_X86_64_32 && (value > UINT32_MAX)) ||
(reloc_type(rel) == R_X86_64_32S &&
((int64_t)value > INT32_MAX && (int64_t)value < INT32_MIN)) ||
(reloc_type(rel) == R_AARCH64_ABS32 &&
((int64_t)value > INT32_MAX && (int64_t)value < INT32_MIN))) {
Log *log = GetLog(LLDBLog::Modules);
LLDB_LOGF(log, "Failed to apply debug info relocations");
break;
}
uint32_t truncated_addr = (value & 0xFFFFFFFF);
DataBufferSP &data_buffer_sp = debug_data.GetSharedDataBuffer();
// ObjectFileELF creates a WritableDataBuffer in CreateInstance.
WritableDataBuffer *data_buffer =
llvm::cast<WritableDataBuffer>(data_buffer_sp.get());
uint32_t *dst = reinterpret_cast<uint32_t *>(
data_buffer->GetBytes() + rel_section->GetFileOffset() +
ELFRelocation::RelocOffset32(rel));
memcpy(dst, &truncated_addr, sizeof(uint32_t));
case llvm::ELF::EM_LOONGARCH:
switch (reloc_type(rel)) {
case R_LARCH_64:
ApplyELF64ABS64Relocation(symtab, rel, debug_data, rel_section);
break;
case R_LARCH_32:
ApplyELF64ABS32Relocation(symtab, rel, debug_data, rel_section, true);
break;
default:
assert(false && "unexpected relocation type");
}
break;
case llvm::ELF::EM_X86_64:
switch (reloc_type(rel)) {
case R_X86_64_64:
ApplyELF64ABS64Relocation(symtab, rel, debug_data, rel_section);
break;
case R_X86_64_32:
ApplyELF64ABS32Relocation(symtab, rel, debug_data, rel_section,
false);
break;
case R_X86_64_32S:
ApplyELF64ABS32Relocation(symtab, rel, debug_data, rel_section, true);
break;
case R_X86_64_PC32:
default:
assert(false && "unexpected relocation type");
}
break;
}
case R_X86_64_PC32:
default:
assert(false && "unexpected relocation type");
assert(false && "unsupported machine");
}
}
}
Expand Down
49 changes: 49 additions & 0 deletions lldb/test/Shell/ObjectFile/ELF/loongarch64-relocations.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# RUN: yaml2obj %s -o %t
# RUN: lldb-test object-file -contents %t | FileCheck %s

## Test that relocations are correctly applied to the .debug_info section on loongarch64.

# CHECK: Name: .debug_info
# CHECK: Data: (
## Before relocation:
## 0000: 00000000 00000000 00000000 00000000 00000000
## After relocation:
# CHECK-NEXT: 0000: 34120000 88776655 44332211 8899AABB CCDDEEFF
# CHECK-NEXT: )

--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_REL
Machine: EM_LOONGARCH
Sections:
- Name: .debug_str
Type: SHT_PROGBITS
- Name: .debug_info
Type: SHT_PROGBITS
Content: 0000000000000000000000000000000000000000
- Name: .rela.debug_info
Type: SHT_RELA
Info: .debug_info
Relocations:
- Offset: 0x0000000000000000
Symbol: .debug_str
Type: R_LARCH_32
Addend: 0x1234
- Offset: 0x0000000000000004
Symbol: .debug_str
Type: R_LARCH_64
Addend: 0x1122334455667788
- Offset: 0x000000000000000C
Symbol: .debug_str
Type: R_LARCH_64
Addend: 0xFFEEDDCCBBAA9988
Symbols:
- Name: .debug_str
Type: STT_SECTION
Section: .debug_str
- Name: .debug_info
Type: STT_SECTION
Section: .debug_info
...
206 changes: 136 additions & 70 deletions llvm/include/llvm/ADT/AddressRanges.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ class AddressRange {
uint64_t start() const { return Start; }
uint64_t end() const { return End; }
uint64_t size() const { return End - Start; }
uint64_t empty() const { return size() == 0; }
bool contains(uint64_t Addr) const { return Start <= Addr && Addr < End; }
bool contains(const AddressRange &R) const {
return Start <= R.Start && R.End <= End;
}
bool intersects(const AddressRange &R) const {
return Start < R.End && R.Start < End;
}
Expand All @@ -45,101 +49,163 @@ class AddressRange {
uint64_t End = 0;
};

/// The AddressRanges class helps normalize address range collections.
/// This class keeps a sorted vector of AddressRange objects and can perform
/// insertions and searches efficiently. The address ranges are always sorted
/// and never contain any invalid or empty address ranges.
/// Intersecting([100,200), [150,300)) and adjacent([100,200), [200,300))
/// address ranges are combined during insertion.
class AddressRanges {
/// The AddressRangesBase class presents the base functionality for the
/// normalized address ranges collection. This class keeps a sorted vector
/// of AddressRange-like objects and can perform searches efficiently.
/// The address ranges are always sorted and never contain any invalid,
/// empty or intersected address ranges.

template <typename T> class AddressRangesBase {
protected:
using Collection = SmallVector<AddressRange>;
using Collection = SmallVector<T>;
Collection Ranges;

public:
void clear() { Ranges.clear(); }
bool empty() const { return Ranges.empty(); }
bool contains(uint64_t Addr) const { return find(Addr) != Ranges.end(); }
bool contains(uint64_t Addr) const {
return find(Addr, Addr + 1) != Ranges.end();
}
bool contains(AddressRange Range) const {
return find(Range) != Ranges.end();
return find(Range.start(), Range.end()) != Ranges.end();
}
std::optional<AddressRange> getRangeThatContains(uint64_t Addr) const {
Collection::const_iterator It = find(Addr);
void reserve(size_t Capacity) { Ranges.reserve(Capacity); }
size_t size() const { return Ranges.size(); }

std::optional<T> getRangeThatContains(uint64_t Addr) const {
typename Collection::const_iterator It = find(Addr, Addr + 1);
if (It == Ranges.end())
return std::nullopt;

return *It;
}
Collection::const_iterator insert(AddressRange Range);
void reserve(size_t Capacity) { Ranges.reserve(Capacity); }
size_t size() const { return Ranges.size(); }
bool operator==(const AddressRanges &RHS) const {
return Ranges == RHS.Ranges;
}
const AddressRange &operator[](size_t i) const {

typename Collection::const_iterator begin() const { return Ranges.begin(); }
typename Collection::const_iterator end() const { return Ranges.end(); }

const T &operator[](size_t i) const {
assert(i < Ranges.size());
return Ranges[i];
}
Collection::const_iterator begin() const { return Ranges.begin(); }
Collection::const_iterator end() const { return Ranges.end(); }

bool operator==(const AddressRangesBase<T> &RHS) const {
return Ranges == RHS.Ranges;
}

protected:
Collection::const_iterator find(uint64_t Addr) const;
Collection::const_iterator find(AddressRange Range) const;
typename Collection::const_iterator find(uint64_t Start, uint64_t End) const {
if (Start >= End)
return Ranges.end();

auto It =
std::partition_point(Ranges.begin(), Ranges.end(), [=](const T &R) {
return AddressRange(R).start() <= Start;
});

if (It == Ranges.begin())
return Ranges.end();

--It;
if (End > AddressRange(*It).end())
return Ranges.end();

return It;
}
};

/// AddressRangesMap class maps values to the address ranges.
/// It keeps address ranges and corresponding values. If ranges
/// are combined during insertion, then combined range keeps
/// newly inserted value.
template <typename T> class AddressRangesMap : protected AddressRanges {
/// The AddressRanges class helps normalize address range collections.
/// This class keeps a sorted vector of AddressRange objects and can perform
/// insertions and searches efficiently. Intersecting([100,200), [150,300))
/// and adjacent([100,200), [200,300)) address ranges are combined during
/// insertion.
class AddressRanges : public AddressRangesBase<AddressRange> {
public:
void clear() {
Ranges.clear();
Values.clear();
Collection::const_iterator insert(AddressRange Range) {
if (Range.empty())
return Ranges.end();

auto It = llvm::upper_bound(Ranges, Range);
auto It2 = It;
while (It2 != Ranges.end() && It2->start() <= Range.end())
++It2;
if (It != It2) {
Range = {Range.start(), std::max(Range.end(), std::prev(It2)->end())};
It = Ranges.erase(It, It2);
}
if (It != Ranges.begin() && Range.start() <= std::prev(It)->end()) {
--It;
*It = {It->start(), std::max(It->end(), Range.end())};
return It;
}

return Ranges.insert(It, Range);
}
bool empty() const { return AddressRanges::empty(); }
bool contains(uint64_t Addr) const { return AddressRanges::contains(Addr); }
bool contains(AddressRange Range) const {
return AddressRanges::contains(Range);
}
void insert(AddressRange Range, T Value) {
size_t InputSize = Ranges.size();
Collection::const_iterator RangesIt = AddressRanges::insert(Range);
if (RangesIt == Ranges.end())
return;
};

// make Values match to Ranges.
size_t Idx = RangesIt - Ranges.begin();
typename ValuesCollection::iterator ValuesIt = Values.begin() + Idx;
if (InputSize < Ranges.size())
Values.insert(ValuesIt, T());
else if (InputSize > Ranges.size())
Values.erase(ValuesIt, ValuesIt + InputSize - Ranges.size());
assert(Ranges.size() == Values.size());

// set value to the inserted or combined range.
Values[Idx] = Value;
}
size_t size() const {
assert(Ranges.size() == Values.size());
return AddressRanges::size();
}
std::optional<std::pair<AddressRange, T>>
getRangeValueThatContains(uint64_t Addr) const {
Collection::const_iterator It = find(Addr);
if (It == Ranges.end())
return std::nullopt;
class AddressRangeValuePair {
public:
operator AddressRange() const { return Range; }

return std::make_pair(*It, Values[It - Ranges.begin()]);
}
std::pair<AddressRange, T> operator[](size_t Idx) const {
return std::make_pair(Ranges[Idx], Values[Idx]);
}
AddressRange Range;
int64_t Value = 0;
};

protected:
using ValuesCollection = SmallVector<T>;
ValuesCollection Values;
inline bool operator==(const AddressRangeValuePair &LHS,
const AddressRangeValuePair &RHS) {
return LHS.Range == RHS.Range && LHS.Value == RHS.Value;
}

/// AddressRangesMap class maps values to the address ranges.
/// It keeps normalized address ranges and corresponding values.
/// This class keeps a sorted vector of AddressRangeValuePair objects
/// and can perform insertions and searches efficiently.
/// Intersecting([100,200), [150,300)) ranges splitted into non-conflicting
/// parts([100,200), [200,300)). Adjacent([100,200), [200,300)) address
/// ranges are not combined during insertion.
class AddressRangesMap : public AddressRangesBase<AddressRangeValuePair> {
public:
void insert(AddressRange Range, int64_t Value) {
if (Range.empty())
return;

// Search for range which is less than or equal incoming Range.
auto It = std::partition_point(Ranges.begin(), Ranges.end(),
[=](const AddressRangeValuePair &R) {
return R.Range.start() <= Range.start();
});

if (It != Ranges.begin())
It--;

while (!Range.empty()) {
// Inserted range does not overlap with any range.
// Store it into the Ranges collection.
if (It == Ranges.end() || Range.end() <= It->Range.start()) {
Ranges.insert(It, {Range, Value});
return;
}

// Inserted range partially overlaps with current range.
// Store not overlapped part of inserted range.
if (Range.start() < It->Range.start()) {
It = Ranges.insert(It, {{Range.start(), It->Range.start()}, Value});
It++;
Range = {It->Range.start(), Range.end()};
continue;
}

// Inserted range fully overlaps with current range.
if (Range.end() <= It->Range.end())
return;

// Inserted range partially overlaps with current range.
// Remove overlapped part from the inserted range.
if (Range.start() < It->Range.end())
Range = {It->Range.end(), Range.end()};

It++;
}
}
};

} // namespace llvm
Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class DeclContext;

/// Mapped value in the address map is the offset to apply to the
/// linked address.
using RangesTy = AddressRangesMap<int64_t>;
using RangesTy = AddressRangesMap;

// FIXME: Delete this structure.
struct PatchLocation {
Expand Down
36 changes: 18 additions & 18 deletions llvm/lib/DWARFLinker/DWARFLinker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1659,7 +1659,7 @@ void DWARFLinker::patchRangesForUnit(const CompileUnit &Unit,
DWARFDataExtractor RangeExtractor(OrigDwarf.getDWARFObj(),
OrigDwarf.getDWARFObj().getRangesSection(),
OrigDwarf.isLittleEndian(), AddressSize);
std::optional<std::pair<AddressRange, int64_t>> CachedRange;
std::optional<AddressRangeValuePair> CachedRange;
DWARFUnit &OrigUnit = Unit.getOrigUnit();
auto OrigUnitDie = OrigUnit.getUnitDIE(false);
uint64_t UnitBaseAddress =
Expand Down Expand Up @@ -1687,9 +1687,9 @@ void DWARFLinker::patchRangesForUnit(const CompileUnit &Unit,
}

if (!CachedRange ||
!CachedRange->first.contains(Range.StartAddress + BaseAddress))
CachedRange = FunctionRanges.getRangeValueThatContains(
Range.StartAddress + BaseAddress);
!CachedRange->Range.contains(Range.StartAddress + BaseAddress))
CachedRange = FunctionRanges.getRangeThatContains(Range.StartAddress +
BaseAddress);

// All range entries should lie in the function range.
if (!CachedRange) {
Expand All @@ -1698,8 +1698,8 @@ void DWARFLinker::patchRangesForUnit(const CompileUnit &Unit,
}

LinkedRanges.insert(
{Range.StartAddress + BaseAddress + CachedRange->second,
Range.EndAddress + BaseAddress + CachedRange->second});
{Range.StartAddress + BaseAddress + CachedRange->Value,
Range.EndAddress + BaseAddress + CachedRange->Value});
}
}

Expand Down Expand Up @@ -1802,7 +1802,7 @@ void DWARFLinker::patchLineTableForUnit(CompileUnit &Unit,
// in NewRows.
std::vector<DWARFDebugLine::Row> Seq;
const auto &FunctionRanges = Unit.getFunctionRanges();
std::optional<std::pair<AddressRange, int64_t>> CurrRange;
std::optional<AddressRangeValuePair> CurrRange;

// FIXME: This logic is meant to generate exactly the same output as
// Darwin's classic dsymutil. There is a nicer way to implement this
Expand All @@ -1821,13 +1821,13 @@ void DWARFLinker::patchLineTableForUnit(CompileUnit &Unit,
// it is marked as end_sequence in the input (because in that
// case, the relocation offset is accurate and that entry won't
// serve as the start of another function).
if (!CurrRange || !CurrRange->first.contains(Row.Address.Address) ||
(Row.Address.Address == CurrRange->first.end() && !Row.EndSequence)) {
if (!CurrRange || !CurrRange->Range.contains(Row.Address.Address) ||
(Row.Address.Address == CurrRange->Range.end() && !Row.EndSequence)) {
// We just stepped out of a known range. Insert a end_sequence
// corresponding to the end of the range.
uint64_t StopAddress =
CurrRange ? CurrRange->first.end() + CurrRange->second : -1ULL;
CurrRange = FunctionRanges.getRangeValueThatContains(Row.Address.Address);
CurrRange ? CurrRange->Range.end() + CurrRange->Value : -1ULL;
CurrRange = FunctionRanges.getRangeThatContains(Row.Address.Address);
if (!CurrRange) {
if (StopAddress != -1ULL) {
// Try harder by looking in the Address ranges map.
Expand All @@ -1836,9 +1836,9 @@ void DWARFLinker::patchLineTableForUnit(CompileUnit &Unit,
// for now do as dsymutil.
// FIXME: Understand exactly what cases this addresses and
// potentially remove it along with the Ranges map.
if (std::optional<std::pair<AddressRange, int64_t>> Range =
Ranges.getRangeValueThatContains(Row.Address.Address))
StopAddress = Row.Address.Address + (*Range).second;
if (std::optional<AddressRangeValuePair> Range =
Ranges.getRangeThatContains(Row.Address.Address))
StopAddress = Row.Address.Address + (*Range).Value;
}
}
if (StopAddress != -1ULL && !Seq.empty()) {
Expand All @@ -1863,7 +1863,7 @@ void DWARFLinker::patchLineTableForUnit(CompileUnit &Unit,
continue;

// Relocate row address and add it to the current sequence.
Row.Address.Address += CurrRange->second;
Row.Address.Address += CurrRange->Value;
Seq.emplace_back(Row);

if (Row.EndSequence)
Expand Down Expand Up @@ -2002,8 +2002,8 @@ void DWARFLinker::patchFrameInfoForObject(const DWARFFile &File,
// the function entry point, thus we can't just lookup the address
// in the debug map. Use the AddressInfo's range map to see if the FDE
// describes something that we can relocate.
std::optional<std::pair<AddressRange, int64_t>> Range =
Ranges.getRangeValueThatContains(Loc);
std::optional<AddressRangeValuePair> Range =
Ranges.getRangeThatContains(Loc);
if (!Range) {
// The +4 is to account for the size of the InitialLength field itself.
InputOffset = EntryOffset + InitialLength + 4;
Expand Down Expand Up @@ -2032,7 +2032,7 @@ void DWARFLinker::patchFrameInfoForObject(const DWARFFile &File,
// fields that will get reconstructed by emitFDE().
unsigned FDERemainingBytes = InitialLength - (4 + AddrSize);
TheDwarfEmitter->emitFDE(IteratorInserted.first->getValue(), AddrSize,
Loc + Range->second,
Loc + Range->Value,
FrameData.substr(InputOffset, FDERemainingBytes));
InputOffset += FDERemainingBytes;
}
Expand Down
5 changes: 2 additions & 3 deletions llvm/lib/DWARFLinker/DWARFStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,10 +402,9 @@ void DwarfStreamer::emitUnitRangesEntries(CompileUnit &Unit,
// Linked addresses might end up in a different order.
// Build linked address ranges.
AddressRanges LinkedRanges;
for (size_t Idx = 0; Idx < FunctionRanges.size(); Idx++)
for (const AddressRangeValuePair &Range : FunctionRanges)
LinkedRanges.insert(
{FunctionRanges[Idx].first.start() + FunctionRanges[Idx].second,
FunctionRanges[Idx].first.end() + FunctionRanges[Idx].second});
{Range.Range.start() + Range.Value, Range.Range.end() + Range.Value});

if (!FunctionRanges.empty())
emitDwarfDebugArangesTable(Unit, LinkedRanges);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ void link_ELF_aarch64(std::unique_ptr<LinkGraph> G,
Config.PrePrunePasses.push_back(EHFrameEdgeFixer(
".eh_frame", 8, aarch64::Pointer32, aarch64::Pointer64,
aarch64::Delta32, aarch64::Delta64, aarch64::NegDelta32));
Config.PrePrunePasses.push_back(EHFrameNullTerminator(".eh_frame"));

// Add a mark-live pass.
if (auto MarkLive = Ctx->getMarkLivePass(TT))
Expand Down
35 changes: 35 additions & 0 deletions llvm/lib/IR/AutoUpgrade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicsAArch64.h"
#include "llvm/IR/IntrinsicsARM.h"
#include "llvm/IR/IntrinsicsWebAssembly.h"
#include "llvm/IR/IntrinsicsX86.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
Expand Down Expand Up @@ -1125,6 +1126,40 @@ static bool UpgradeIntrinsicFunction1(Function *F, Function *&NewFn) {
break;
}

case 'w':
if (Name.startswith("wasm.fma.")) {
rename(F);
NewFn = Intrinsic::getDeclaration(
F->getParent(), Intrinsic::wasm_relaxed_madd, F->getReturnType());
return true;
}
if (Name.startswith("wasm.fms.")) {
rename(F);
NewFn = Intrinsic::getDeclaration(
F->getParent(), Intrinsic::wasm_relaxed_nmadd, F->getReturnType());
return true;
}
if (Name.startswith("wasm.laneselect.")) {
rename(F);
NewFn = Intrinsic::getDeclaration(
F->getParent(), Intrinsic::wasm_relaxed_laneselect,
F->getReturnType());
return true;
}
if (Name == "wasm.dot.i8x16.i7x16.signed") {
rename(F);
NewFn = Intrinsic::getDeclaration(
F->getParent(), Intrinsic::wasm_relaxed_dot_i8x16_i7x16_signed);
return true;
}
if (Name == "wasm.dot.i8x16.i7x16.add.signed") {
rename(F);
NewFn = Intrinsic::getDeclaration(
F->getParent(), Intrinsic::wasm_relaxed_dot_i8x16_i7x16_add_signed);
return true;
}
break;

case 'x':
if (UpgradeX86IntrinsicFunction(F, Name, NewFn))
return true;
Expand Down
6 changes: 3 additions & 3 deletions llvm/lib/Passes/PassBuilderPipelines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,9 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
// keep one copy of each constant.
MPM.addPass(ConstantMergePass());

// Remove unused arguments from functions.
MPM.addPass(DeadArgumentEliminationPass());

// Reduce the code after globalopt and ipsccp. Both can open up significant
// simplification opportunities, and both can propagate functions through
// function pointers. When this happens, we often have to resolve varargs
Expand Down Expand Up @@ -1722,9 +1725,6 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
// transform it to pass arguments by value instead of by reference.
MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(ArgumentPromotionPass()));

// Remove unused arguments from functions.
MPM.addPass(DeadArgumentEliminationPass());

FunctionPassManager FPM;
// The IPO Passes may leave cruft around. Clean up after them.
FPM.addPass(InstCombinePass());
Expand Down
70 changes: 0 additions & 70 deletions llvm/lib/Support/AddressRanges.cpp

This file was deleted.

1 change: 0 additions & 1 deletion llvm/lib/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ endif()
add_subdirectory(BLAKE3)

add_llvm_component_library(LLVMSupport
AddressRanges.cpp
ABIBreak.cpp
AMDGPUMetadata.cpp
APFixedPoint.cpp
Expand Down
46 changes: 32 additions & 14 deletions llvm/lib/Target/BPF/BTFDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,17 @@ void BTFDebug::visitCompositeType(const DICompositeType *CTy,
visitEnumType(CTy, TypeId);
}

bool BTFDebug::IsForwardDeclCandidate(const DIType *Base) {
if (const auto *CTy = dyn_cast<DICompositeType>(Base)) {
auto CTag = CTy->getTag();
if ((CTag == dwarf::DW_TAG_structure_type ||
CTag == dwarf::DW_TAG_union_type) &&
!CTy->getName().empty() && !CTy->isForwardDecl())
return true;
}
return false;
}

/// Handle pointer, typedef, const, volatile, restrict and member types.
void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
bool CheckPointer, bool SeenPointer) {
Expand All @@ -796,20 +807,15 @@ void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
if (CheckPointer && SeenPointer) {
const DIType *Base = DTy->getBaseType();
if (Base) {
if (const auto *CTy = dyn_cast<DICompositeType>(Base)) {
auto CTag = CTy->getTag();
if ((CTag == dwarf::DW_TAG_structure_type ||
CTag == dwarf::DW_TAG_union_type) &&
!CTy->getName().empty() && !CTy->isForwardDecl()) {
/// Find a candidate, generate a fixup. Later on the struct/union
/// pointee type will be replaced with either a real type or
/// a forward declaration.
auto TypeEntry = std::make_unique<BTFTypeDerived>(DTy, Tag, true);
auto &Fixup = FixupDerivedTypes[CTy];
Fixup.push_back(std::make_pair(DTy, TypeEntry.get()));
TypeId = addType(std::move(TypeEntry), DTy);
return;
}
if (IsForwardDeclCandidate(Base)) {
/// Find a candidate, generate a fixup. Later on the struct/union
/// pointee type will be replaced with either a real type or
/// a forward declaration.
auto TypeEntry = std::make_unique<BTFTypeDerived>(DTy, Tag, true);
auto &Fixup = FixupDerivedTypes[cast<DICompositeType>(Base)];
Fixup.push_back(std::make_pair(DTy, TypeEntry.get()));
TypeId = addType(std::move(TypeEntry), DTy);
return;
}
}
}
Expand Down Expand Up @@ -844,6 +850,13 @@ void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
visitTypeEntry(DTy->getBaseType(), TempTypeId, CheckPointer, SeenPointer);
}

/// Visit a type entry. CheckPointer is true if the type has
/// one of its predecessors as one struct/union member. SeenPointer
/// is true if CheckPointer is true and one of its predecessors
/// is a pointer. The goal of CheckPointer and SeenPointer is to
/// do pruning for struct/union types so some of these types
/// will not be emitted in BTF and rather forward declarations
/// will be generated.
void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId,
bool CheckPointer, bool SeenPointer) {
if (!Ty || DIToIdMap.find(Ty) != DIToIdMap.end()) {
Expand Down Expand Up @@ -888,6 +901,11 @@ void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId,
if (DIToIdMap.find(BaseTy) != DIToIdMap.end()) {
DTy = dyn_cast<DIDerivedType>(BaseTy);
} else {
if (CheckPointer && DTy->getTag() == dwarf::DW_TAG_pointer_type) {
SeenPointer = true;
if (IsForwardDeclCandidate(BaseTy))
break;
}
uint32_t TmpTypeId;
visitTypeEntry(BaseTy, TmpTypeId, CheckPointer, SeenPointer);
break;
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/BPF/BTFDebug.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,9 @@ class BTFDebug : public DebugHandlerBase {
void visitMapDefType(const DIType *Ty, uint32_t &TypeId);
/// @}

/// Check whether the type is a forward declaration candidate or not.
bool IsForwardDeclCandidate(const DIType *Base);

/// Get the file content for the subprogram. Certain lines of the file
/// later may be put into string table and referenced by line info.
std::string populateFileContent(const DISubprogram *SP);
Expand Down
6 changes: 3 additions & 3 deletions llvm/lib/Target/X86/X86InstrCompiler.td
Original file line number Diff line number Diff line change
Expand Up @@ -896,15 +896,15 @@ multiclass ATOMIC_LOGIC_OP<Format Form, string s> {
multiclass ATOMIC_LOGIC_OP_RM<bits<8> Opc8, string s> {
let Defs = [EFLAGS], mayLoad = 1, mayStore = 1, isCodeGenOnly = 1,
SchedRW = [WriteBitTestSetRegRMW] in {
def 16rm : Ii8<Opc8, MRMDestMem, (outs), (ins i16mem:$src1, GR16:$src2),
def 16rm : I<Opc8, MRMDestMem, (outs), (ins i16mem:$src1, GR16:$src2),
!strconcat(s, "{w}\t{$src2, $src1|$src1, $src2}"),
[(set EFLAGS, (!cast<SDNode>("x86_rm_" # s) addr:$src1, GR16:$src2))]>,
OpSize16, TB, LOCK;
def 32rm : Ii8<Opc8, MRMDestMem, (outs), (ins i32mem:$src1, GR32:$src2),
def 32rm : I<Opc8, MRMDestMem, (outs), (ins i32mem:$src1, GR32:$src2),
!strconcat(s, "{l}\t{$src2, $src1|$src1, $src2}"),
[(set EFLAGS, (!cast<SDNode>("x86_rm_" # s) addr:$src1, GR32:$src2))]>,
OpSize32, TB, LOCK;
def 64rm : RIi8<Opc8, MRMDestMem, (outs), (ins i64mem:$src1, GR64:$src2),
def 64rm : RI<Opc8, MRMDestMem, (outs), (ins i64mem:$src1, GR64:$src2),
!strconcat(s, "{q}\t{$src2, $src1|$src1, $src2}"),
[(set EFLAGS, (!cast<SDNode>("x86_rm_" # s) addr:$src1, GR64:$src2))]>,
TB, LOCK;
Expand Down
17 changes: 17 additions & 0 deletions llvm/lib/Target/X86/X86InstrSSE.td
Original file line number Diff line number Diff line change
Expand Up @@ -577,20 +577,37 @@ let Predicates = [HasAVX, NoVLX] in {

def : Pat<(alignedloadv8f16 addr:$src),
(VMOVAPSrm addr:$src)>;
def : Pat<(alignedloadv8bf16 addr:$src),
(VMOVAPSrm addr:$src)>;
def : Pat<(loadv8f16 addr:$src),
(VMOVUPSrm addr:$src)>;
def : Pat<(loadv8bf16 addr:$src),
(VMOVUPSrm addr:$src)>;
def : Pat<(alignedstore (v8f16 VR128:$src), addr:$dst),
(VMOVAPSmr addr:$dst, VR128:$src)>;
def : Pat<(alignedstore (v8bf16 VR128:$src), addr:$dst),
(VMOVAPSmr addr:$dst, VR128:$src)>;
def : Pat<(store (v8f16 VR128:$src), addr:$dst),
(VMOVUPSmr addr:$dst, VR128:$src)>;
def : Pat<(store (v8bf16 VR128:$src), addr:$dst),
(VMOVUPSmr addr:$dst, VR128:$src)>;

def : Pat<(alignedloadv16f16 addr:$src),
(VMOVAPSYrm addr:$src)>;
def : Pat<(alignedloadv16bf16 addr:$src),
(VMOVAPSYrm addr:$src)>;
def : Pat<(loadv16f16 addr:$src),
(VMOVUPSYrm addr:$src)>;
def : Pat<(loadv16bf16 addr:$src),
(VMOVUPSYrm addr:$src)>;
def : Pat<(alignedstore (v16f16 VR256:$src), addr:$dst),
(VMOVAPSYmr addr:$dst, VR256:$src)>;
def : Pat<(alignedstore (v16bf16 VR256:$src), addr:$dst),
(VMOVAPSYmr addr:$dst, VR256:$src)>;
def : Pat<(store (v16f16 VR256:$src), addr:$dst),
(VMOVUPSYmr addr:$dst, VR256:$src)>;
def : Pat<(store (v16bf16 VR256:$src), addr:$dst),
(VMOVUPSYmr addr:$dst, VR256:$src)>;
}

// Use movaps / movups for SSE integer load / store (one byte shorter).
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/TargetParser/ARMTargetParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ StringRef ARM::computeDefaultTargetABI(const Triple &TT, StringRef CPU) {
default:
if (TT.isOSNetBSD())
return "apcs-gnu";
if (TT.isOSOpenBSD())
if (TT.isOSFreeBSD() || TT.isOSOpenBSD())
return "aapcs-linux";
return "aapcs";
}
Expand Down
16 changes: 6 additions & 10 deletions llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3184,16 +3184,6 @@ Instruction *InstCombinerImpl::foldICmpBinOpEqualityWithConstant(
}
break;
}
case Instruction::And: {
const APInt *BOC;
if (match(BOp1, m_APInt(BOC))) {
// If we have ((X & C) == C), turn it into ((X & C) != 0).
if (C == *BOC && C.isPowerOf2())
return new ICmpInst(isICMP_NE ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE,
BO, Constant::getNullValue(RHS->getType()));
}
break;
}
case Instruction::UDiv:
if (C.isZero()) {
// (icmp eq/ne (udiv A, B), 0) -> (icmp ugt/ule i32 B, A)
Expand Down Expand Up @@ -5653,6 +5643,12 @@ Instruction *InstCombinerImpl::foldICmpUsingKnownBits(ICmpInst &I) {
}
}
}

// Op0 eq C_Pow2 -> Op0 ne 0 if Op0 is known to be C_Pow2 or zero.
if (Op1Known.isConstant() && Op1Known.getConstant().isPowerOf2() &&
(Op0Known & Op1Known) == Op0Known)
return new ICmpInst(CmpInst::getInversePredicate(Pred), Op0,
ConstantInt::getNullValue(Op1->getType()));
break;
}
case ICmpInst::ICMP_ULT: {
Expand Down
53 changes: 53 additions & 0 deletions llvm/test/Assembler/autoupgrade-wasm-intrinsics.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S < %s | FileCheck %s

define <16 x i8> @test_laneselect(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c) {
; CHECK-LABEL: @test_laneselect(
; CHECK-NEXT: [[RES:%.*]] = call <16 x i8> @llvm.wasm.relaxed.laneselect.v16i8(<16 x i8> [[A:%.*]], <16 x i8> [[B:%.*]], <16 x i8> [[C:%.*]])
; CHECK-NEXT: ret <16 x i8> [[RES]]
;
%res = call <16 x i8> @llvm.wasm.laneselect.v16i8(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c)
ret <16 x i8> %res
}

define <8 x i16> @test_dot(<16 x i8> %a, <16 x i8> %b) {
; CHECK-LABEL: @test_dot(
; CHECK-NEXT: [[RES:%.*]] = call <8 x i16> @llvm.wasm.relaxed.dot.i8x16.i7x16.signed(<16 x i8> [[A:%.*]], <16 x i8> [[B:%.*]])
; CHECK-NEXT: ret <8 x i16> [[RES]]
;
%res = call <8 x i16> @llvm.wasm.dot.i8x16.i7x16.signed(<16 x i8> %a, <16 x i8> %b)
ret <8 x i16> %res
}

define <4 x i32> @test_dot_add(<16 x i8> %a, <16 x i8> %b, <4 x i32> %c) {
; CHECK-LABEL: @test_dot_add(
; CHECK-NEXT: [[RES:%.*]] = call <4 x i32> @llvm.wasm.relaxed.dot.i8x16.i7x16.add.signed(<16 x i8> [[A:%.*]], <16 x i8> [[B:%.*]], <4 x i32> [[C:%.*]])
; CHECK-NEXT: ret <4 x i32> [[RES]]
;
%res = call <4 x i32> @llvm.wasm.dot.i8x16.i7x16.add.signed(<16 x i8> %a, <16 x i8> %b, <4 x i32> %c)
ret <4 x i32> %res
}

define <4 x float> @test_fma(<4 x float> %a, <4 x float> %b, <4 x float> %c) {
; CHECK-LABEL: @test_fma(
; CHECK-NEXT: [[RES:%.*]] = call <4 x float> @llvm.wasm.relaxed.madd.v4f32(<4 x float> [[A:%.*]], <4 x float> [[B:%.*]], <4 x float> [[C:%.*]])
; CHECK-NEXT: ret <4 x float> [[RES]]
;
%res = call <4 x float> @llvm.wasm.fma.v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c)
ret <4 x float> %res
}

define <4 x float> @test_fms(<4 x float> %a, <4 x float> %b, <4 x float> %c) {
; CHECK-LABEL: @test_fms(
; CHECK-NEXT: [[RES:%.*]] = call <4 x float> @llvm.wasm.relaxed.nmadd.v4f32(<4 x float> [[A:%.*]], <4 x float> [[B:%.*]], <4 x float> [[C:%.*]])
; CHECK-NEXT: ret <4 x float> [[RES]]
;
%res = call <4 x float> @llvm.wasm.fms.v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c)
ret <4 x float> %res
}

declare <16 x i8> @llvm.wasm.laneselect.v16i8(<16 x i8>, <16 x i8>, <16 x i8>)
declare <8 x i16> @llvm.wasm.dot.i8x16.i7x16.signed(<16 x i8>, <16 x i8>)
declare <4 x i32> @llvm.wasm.dot.i8x16.i7x16.add.signed(<16 x i8>, <16 x i8>, <4 x i32>)
declare <4 x float> @llvm.wasm.fma.v4f32(<4 x float>, <4 x float>, <4 x float>)
declare <4 x float> @llvm.wasm.fms.v4f32(<4 x float>, <4 x float>, <4 x float>)
113 changes: 113 additions & 0 deletions llvm/test/CodeGen/BPF/BTF/pruning-dup-ptr-struct.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
; RUN: llc -march=bpfel -o - %s | FileCheck %s
; RUN: llc -march=bpfeb -o - %s | FileCheck %s
; Source:
; struct t1 {
; int a;
; };
; struct t2 {
; struct t1 *p1;
; struct t1 *p2;
; int b;
; };
; int foo(struct t2 *arg) {
; return arg->b;
; }
; Compilation flags:
; clang -target bpf -O2 -g -S -emit-llvm t.c

%struct.t2 = type { ptr, ptr, i32 }

; Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn
define dso_local i32 @foo(ptr nocapture noundef readonly %arg) local_unnamed_addr #0 !dbg !7 {
entry:
call void @llvm.dbg.value(metadata ptr %arg, metadata !22, metadata !DIExpression()), !dbg !23
%b = getelementptr inbounds %struct.t2, ptr %arg, i64 0, i32 2, !dbg !24
%0 = load i32, ptr %b, align 8, !dbg !24, !tbaa !25
ret i32 %0, !dbg !31
}

; CHECK: .long 0 # BTF_KIND_PTR(id = 1)
; CHECK-NEXT: .long 33554432 # 0x2000000
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long 1 # BTF_KIND_STRUCT(id = 2)
; CHECK-NEXT: .long 67108867 # 0x4000003
; CHECK-NEXT: .long 24
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long 0 # 0x0
; CHECK-NEXT: .long 7
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long 64 # 0x40
; CHECK-NEXT: .long 10
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 128 # 0x80
; CHECK-NEXT: .long 0 # BTF_KIND_PTR(id = 3)
; CHECK-NEXT: .long 33554432 # 0x2000000
; CHECK-NEXT: .long 7
; CHECK-NEXT: .long 12 # BTF_KIND_INT(id = 4)
; CHECK-NEXT: .long 16777216 # 0x1000000
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16777248 # 0x1000020
; CHECK-NEXT: .long 0 # BTF_KIND_FUNC_PROTO(id = 5)
; CHECK-NEXT: .long 218103809 # 0xd000001
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long 16
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long 20 # BTF_KIND_FUNC(id = 6)
; CHECK-NEXT: .long 201326593 # 0xc000001
; CHECK-NEXT: .long 5
; CHECK-NEXT: .long 60 # BTF_KIND_FWD(id = 7)
; CHECK-NEXT: .long 117440512 # 0x7000000
; CHECK-NEXT: .long 0

; CHECK: .ascii "t2" # string offset=1
; CHECK: .ascii "p1" # string offset=4
; CHECK: .ascii "p2" # string offset=7
; CHECK: .byte 98 # string offset=10
; CHECK: .ascii "int" # string offset=12
; CHECK: .ascii "arg" # string offset=16
; CHECK: .ascii "foo" # string offset=20
; CHECK: .ascii "t1" # string offset=60

; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
declare void @llvm.dbg.value(metadata, metadata, metadata) #1

attributes #0 = { argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { nocallback nofree nosync nounwind readnone speculatable willreturn }

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!2, !3, !4, !5}
!llvm.ident = !{!6}

!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 15.0.7 (https://github.com/llvm/llvm-project.git 8dfdcc7b7bf66834a761bd8de445840ef68e4d1a)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "t1.c", directory: "/tmp/home/yhs/tmp3/tests", checksumkind: CSK_MD5, checksum: "9a79ff24a6244249e556afd85288af94")
!2 = !{i32 7, !"Dwarf Version", i32 5}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !{i32 1, !"wchar_size", i32 4}
!5 = !{i32 7, !"frame-pointer", i32 2}
!6 = !{!"clang version 15.0.7 (https://github.com/llvm/llvm-project.git 8dfdcc7b7bf66834a761bd8de445840ef68e4d1a)"}
!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 9, type: !8, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !21)
!8 = !DISubroutineType(types: !9)
!9 = !{!10, !11}
!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64)
!12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t2", file: !1, line: 4, size: 192, elements: !13)
!13 = !{!14, !19, !20}
!14 = !DIDerivedType(tag: DW_TAG_member, name: "p1", scope: !12, file: !1, line: 5, baseType: !15, size: 64)
!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
!16 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t1", file: !1, line: 1, size: 32, elements: !17)
!17 = !{!18}
!18 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !16, file: !1, line: 2, baseType: !10, size: 32)
!19 = !DIDerivedType(tag: DW_TAG_member, name: "p2", scope: !12, file: !1, line: 6, baseType: !15, size: 64, offset: 64)
!20 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !12, file: !1, line: 7, baseType: !10, size: 32, offset: 128)
!21 = !{!22}
!22 = !DILocalVariable(name: "arg", arg: 1, scope: !7, file: !1, line: 9, type: !11)
!23 = !DILocation(line: 0, scope: !7)
!24 = !DILocation(line: 10, column: 15, scope: !7)
!25 = !{!26, !30, i64 16}
!26 = !{!"t2", !27, i64 0, !27, i64 8, !30, i64 16}
!27 = !{!"any pointer", !28, i64 0}
!28 = !{!"omnipotent char", !29, i64 0}
!29 = !{!"Simple C/C++ TBAA"}
!30 = !{!"int", !28, i64 0}
!31 = !DILocation(line: 10, column: 3, scope: !7)
43 changes: 43 additions & 0 deletions llvm/test/CodeGen/X86/avx512bf16-mov.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+avx512bf16 | FileCheck %s --check-prefix=X64
; RUN: llc < %s -mtriple=i686-unknown-unknown -mattr=+avx512bf16 | FileCheck %s --check-prefix=X86

define dso_local void @funbf16(ptr readonly %src, ptr writeonly %dst) {
; X64-LABEL: funbf16:
; X64: # %bb.0: # %entry
; X64-NEXT: vmovups (%rdi), %xmm0
; X64-NEXT: vmovups %xmm0, (%rsi)
; X64-NEXT: vmovaps (%rdi), %xmm0
; X64-NEXT: vmovaps %xmm0, (%rsi)
; X64-NEXT: vmovups (%rdi), %ymm0
; X64-NEXT: vmovups %ymm0, (%rsi)
; X64-NEXT: vmovaps (%rdi), %ymm0
; X64-NEXT: vmovaps %ymm0, (%rsi)
; X64-NEXT: vzeroupper
; X64-NEXT: retq
;
; X86-LABEL: funbf16:
; X86: # %bb.0: # %entry
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
; X86-NEXT: vmovups (%ecx), %xmm0
; X86-NEXT: vmovups %xmm0, (%eax)
; X86-NEXT: vmovaps (%ecx), %xmm0
; X86-NEXT: vmovaps %xmm0, (%eax)
; X86-NEXT: vmovups (%ecx), %ymm0
; X86-NEXT: vmovups %ymm0, (%eax)
; X86-NEXT: vmovaps (%ecx), %ymm0
; X86-NEXT: vmovaps %ymm0, (%eax)
; X86-NEXT: vzeroupper
; X86-NEXT: retl
entry:
%0 = load <8 x bfloat>, ptr %src, align 1
store <8 x bfloat> %0, ptr %dst, align 1
%1 = load <8 x bfloat>, ptr %src, align 32
store <8 x bfloat> %1, ptr %dst, align 32
%2 = load <16 x bfloat>, ptr %src, align 1
store <16 x bfloat> %2, ptr %dst, align 1
%3 = load <16 x bfloat>, ptr %src, align 32
store <16 x bfloat> %3, ptr %dst, align 32
ret void
}
19 changes: 19 additions & 0 deletions llvm/test/CodeGen/X86/pr61384.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=x86_64-unknown-linux-gnu -show-mc-encoding < %s | FileCheck %s

@a = external dso_local global { { i64 } }

define i32 @atomic_global() nounwind {
; CHECK-LABEL: atomic_global:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: xorl %eax, %eax # encoding: [0x31,0xc0]
; CHECK-NEXT: lock btsq %rax, a(%rip) # encoding: [0xf0,0x48,0x0f,0xab,0x05,A,A,A,A]
; CHECK-NEXT: # fixup A - offset: 5, value: a-4, kind: reloc_riprel_4byte
; CHECK-NEXT: xorl %eax, %eax # encoding: [0x31,0xc0]
; CHECK-NEXT: retq # encoding: [0xc3]
entry:
%shl.i = shl i64 1, 0
%0 = atomicrmw or ptr @a, i64 %shl.i monotonic, align 8
%and.i = and i64 %shl.i, %0
ret i32 0
}
2 changes: 1 addition & 1 deletion llvm/test/Other/new-pm-lto-defaults.ll
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
; CHECK-O23SZ-NEXT: Running pass: GlobalOptPass
; CHECK-O23SZ-NEXT: Running pass: PromotePass
; CHECK-O23SZ-NEXT: Running pass: ConstantMergePass
; CHECK-O23SZ-NEXT: Running pass: DeadArgumentEliminationPass
; CHECK-O23SZ-NEXT: Running pass: InstCombinePass
; CHECK-O3-NEXT: Running pass: AggressiveInstCombinePass
; CHECK-EP-Peephole-NEXT: Running pass: NoOpFunctionPass
Expand All @@ -81,7 +82,6 @@
; CHECK-O23SZ-NEXT: Running pass: OpenMPOptPass
; CHECK-O23SZ-NEXT: Running pass: GlobalDCEPass
; CHECK-O23SZ-NEXT: Running pass: ArgumentPromotionPass
; CHECK-O23SZ-NEXT: Running pass: DeadArgumentEliminationPass
; CHECK-O23SZ-NEXT: Running pass: InstCombinePass
; CHECK-EP-Peephole-NEXT: Running pass: NoOpFunctionPass
; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Transforms/InstCombine/div.ll
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ define i32 @test28(i32 %a) {

define i32 @test29(i32 %a) {
; CHECK-LABEL: @test29(
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[A:%.*]], -1
; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[A:%.*]], 0
; CHECK-NEXT: [[DIV:%.*]] = zext i1 [[TMP1]] to i32
; CHECK-NEXT: ret i32 [[DIV]]
;
Expand Down
44 changes: 44 additions & 0 deletions llvm/test/Transforms/InstCombine/icmp-range.ll
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,50 @@ define i1 @ashr_uge_sub(i8 %b, i8 %x, i8 %y) {
ret i1 %r
}

define i1 @icmp_eq_bool_0(ptr %ptr) {
; CHECK-LABEL: @icmp_eq_bool_0(
; CHECK-NEXT: [[VAL:%.*]] = load i64, ptr [[PTR:%.*]], align 8, !range [[RNG6:![0-9]+]]
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[VAL]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%val = load i64, ptr %ptr, align 8, !range !{i64 0, i64 2}
%cmp = icmp eq i64 %val, 0
ret i1 %cmp
}

define i1 @icmp_eq_bool_1(ptr %ptr) {
; CHECK-LABEL: @icmp_eq_bool_1(
; CHECK-NEXT: [[VAL:%.*]] = load i64, ptr [[PTR:%.*]], align 8, !range [[RNG6]]
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i64 [[VAL]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%val = load i64, ptr %ptr, align 8, !range !{i64 0, i64 2}
%cmp = icmp eq i64 %val, 1
ret i1 %cmp
}

define i1 @icmp_ne_bool_0(ptr %ptr) {
; CHECK-LABEL: @icmp_ne_bool_0(
; CHECK-NEXT: [[VAL:%.*]] = load i64, ptr [[PTR:%.*]], align 8, !range [[RNG6]]
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i64 [[VAL]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%val = load i64, ptr %ptr, align 8, !range !{i64 0, i64 2}
%cmp = icmp ne i64 %val, 0
ret i1 %cmp
}

define i1 @icmp_ne_bool_1(ptr %ptr) {
; CHECK-LABEL: @icmp_ne_bool_1(
; CHECK-NEXT: [[VAL:%.*]] = load i64, ptr [[PTR:%.*]], align 8, !range [[RNG6]]
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[VAL]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%val = load i64, ptr %ptr, align 8, !range !{i64 0, i64 2}
%cmp = icmp ne i64 %val, 1
ret i1 %cmp
}

!0 = !{i32 1, i32 6}
!1 = !{i32 0, i32 6}
!2 = !{i8 0, i8 1}
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Transforms/InstCombine/icmp-shl-nsw.ll
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ define i1 @icmp_sgt8(i8 %x) {

define i1 @icmp_sgt9(i8 %x) {
; CHECK-LABEL: @icmp_sgt9(
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 %x, -1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%shl = shl nsw i8 %x, 7
Expand Down Expand Up @@ -303,7 +303,7 @@ define i1 @icmp_sle8(i8 %x) {

define i1 @icmp_sle9(i8 %x) {
; CHECK-LABEL: @icmp_sle9(
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 %x, -1
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[CMP]]
;
%shl = shl nsw i8 %x, 7
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Transforms/InstCombine/icmp-shr-lt-gt.ll
Original file line number Diff line number Diff line change
Expand Up @@ -2767,7 +2767,7 @@ define i1 @lshrult_03_00_exact(i4 %x) {

define i1 @lshrult_03_01_exact(i4 %x) {
; CHECK-LABEL: @lshrult_03_01_exact(
; CHECK-NEXT: [[C:%.*]] = icmp ne i4 [[X:%.*]], -8
; CHECK-NEXT: [[C:%.*]] = icmp eq i4 [[X:%.*]], 0
; CHECK-NEXT: ret i1 [[C]]
;
%s = lshr exact i4 %x, 3
Expand Down
46 changes: 46 additions & 0 deletions llvm/test/Transforms/InstCombine/zext.ll
Original file line number Diff line number Diff line change
Expand Up @@ -702,3 +702,49 @@ define i8 @zext_icmp_eq_pow2(i8 %y, i8 %x) {
%r = zext i1 %c to i8
ret i8 %r
}

define i64 @zext_icmp_eq_bool_0(ptr %ptr) {
; CHECK-LABEL: @zext_icmp_eq_bool_0(
; CHECK-NEXT: [[VAL:%.*]] = load i64, ptr [[PTR:%.*]], align 8, !range [[RNG0:![0-9]+]]
; CHECK-NEXT: [[LEN:%.*]] = xor i64 [[VAL]], 1
; CHECK-NEXT: ret i64 [[LEN]]
;
%val = load i64, ptr %ptr, align 8, !range !{i64 0, i64 2}
%cmp = icmp eq i64 %val, 0
%len = zext i1 %cmp to i64
ret i64 %len
}

define i64 @zext_icmp_eq_bool_1(ptr %ptr) {
; CHECK-LABEL: @zext_icmp_eq_bool_1(
; CHECK-NEXT: [[VAL:%.*]] = load i64, ptr [[PTR:%.*]], align 8, !range [[RNG0]]
; CHECK-NEXT: ret i64 [[VAL]]
;
%val = load i64, ptr %ptr, align 8, !range !{i64 0, i64 2}
%cmp = icmp eq i64 %val, 1
%len = zext i1 %cmp to i64
ret i64 %len
}

define i64 @zext_icmp_ne_bool_0(ptr %ptr) {
; CHECK-LABEL: @zext_icmp_ne_bool_0(
; CHECK-NEXT: [[VAL:%.*]] = load i64, ptr [[PTR:%.*]], align 8, !range [[RNG0]]
; CHECK-NEXT: ret i64 [[VAL]]
;
%val = load i64, ptr %ptr, align 8, !range !{i64 0, i64 2}
%cmp = icmp ne i64 %val, 0
%len = zext i1 %cmp to i64
ret i64 %len
}

define i64 @zext_icmp_ne_bool_1(ptr %ptr) {
; CHECK-LABEL: @zext_icmp_ne_bool_1(
; CHECK-NEXT: [[VAL:%.*]] = load i64, ptr [[PTR:%.*]], align 8, !range [[RNG0]]
; CHECK-NEXT: [[LEN:%.*]] = xor i64 [[VAL]], 1
; CHECK-NEXT: ret i64 [[LEN]]
;
%val = load i64, ptr %ptr, align 8, !range !{i64 0, i64 2}
%cmp = icmp ne i64 %val, 1
%len = zext i1 %cmp to i64
ret i64 %len
}
52 changes: 52 additions & 0 deletions llvm/test/Transforms/PhaseOrdering/dae-dce.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -passes='default<O3>' < %s | FileCheck %s --check-prefixes=CHECK,DEFAULT
; RUN: opt -S -passes='lto<O3>' < %s | FileCheck %s --check-prefixes=CHECK,LTO

declare void @llvm.trap()

define void @do_trap(ptr %ptr) {
; CHECK-LABEL: @do_trap(
; CHECK-NEXT: tail call void @llvm.trap()
; CHECK-NEXT: unreachable
;
call void @llvm.trap()
unreachable
}

define internal void @capture_and_trap(ptr %ptr) noinline {
; CHECK-LABEL: @capture_and_trap(
; CHECK-NEXT: tail call void @llvm.trap()
; CHECK-NEXT: unreachable
;
%alloca = alloca ptr, align 4
store ptr %ptr, ptr %alloca, align 4
call void @do_trap(ptr noundef nonnull %alloca)
unreachable
}

define internal void @dead_fn1() {
ret void
}

define internal void @dead_fn2() {
ret void
}

define void @test(i1 %c) {
; CHECK-LABEL: @test(
; CHECK-NEXT: tail call fastcc void @capture_and_trap()
; CHECK-NEXT: unreachable
;
br i1 %c, label %if, label %else

if:
call void @capture_and_trap(ptr @dead_fn1)
unreachable

else:
call void @capture_and_trap(ptr @dead_fn2)
unreachable
}
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; DEFAULT: {{.*}}
; LTO: {{.*}}
285 changes: 241 additions & 44 deletions llvm/unittests/Support/AddressRangeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,31 @@ TEST(AddressRangeTest, TestRanges) {
EXPECT_EQ(Ranges[0], AddressRange(0x1000, 0x5000));
}

TEST(AddressRangeTest, TestRangesRandom) {
AddressRanges Ranges;
size_t NumElements = 100;

std::srand(std::time(nullptr));

// Fill ranges.
for (size_t Idx = 0; Idx < NumElements; Idx++) {
uint64_t Start = static_cast<uint64_t>(std::rand() % 1000);
uint64_t End = Start + static_cast<uint64_t>(std::rand() % 1000);
Ranges.insert({Start, End});
}

// Check ranges.
for (size_t Idx = 0; Idx + 1 < Ranges.size(); Idx++) {
// Check that ranges are not intersected.
EXPECT_FALSE(Ranges[Idx].intersects(Ranges[Idx + 1]));

// Check that ranges are sorted and not adjusted.
EXPECT_TRUE(Ranges[Idx].end() < Ranges[Idx + 1].start());
}
}

TEST(AddressRangeTest, TestRangesMap) {
AddressRangesMap<int> Ranges;
AddressRangesMap Ranges;

EXPECT_EQ(Ranges.size(), 0u);
EXPECT_TRUE(Ranges.empty());
Expand All @@ -162,73 +185,247 @@ TEST(AddressRangeTest, TestRangesMap) {
EXPECT_TRUE(Ranges.contains(0x1500));
EXPECT_TRUE(Ranges.contains(AddressRange(0x1000, 0x2000)));

///////////////////////////////////////
/// Check ranges with the same mapped value.

// Clear ranges.
Ranges.clear();
EXPECT_EQ(Ranges.size(), 0u);
EXPECT_TRUE(Ranges.empty());

// Add range and check mapped value.
Ranges.insert(AddressRange(0x1000, 0x2000), 0x11);
EXPECT_EQ(Ranges.size(), 1u);
EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0x11);

// Add adjacent range and check mapped value.
Ranges.insert(AddressRange(0x2000, 0x3000), 0x11);
EXPECT_EQ(Ranges.size(), 2u);
EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0x11);
EXPECT_EQ(Ranges.getRangeThatContains(0x2000)->Value, 0x11);
EXPECT_EQ(Ranges.getRangeThatContains(0x2900)->Value, 0x11);
EXPECT_FALSE(Ranges.getRangeThatContains(0x3000));

// Add intersecting range and check mapped value.
Ranges.insert(AddressRange(0x1000, 0x3000), 0x11);
EXPECT_EQ(Ranges.size(), 2u);
EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0x11);

// Add second range and check mapped values.
Ranges.insert(AddressRange(0x4000, 0x5000), 0x11);
EXPECT_EQ(Ranges.size(), 3u);
EXPECT_EQ(Ranges[0].Range, AddressRange(0x1000, 0x2000));
EXPECT_EQ(Ranges[0].Value, 0x11);
EXPECT_EQ(Ranges[1].Range, AddressRange(0x2000, 0x3000));
EXPECT_EQ(Ranges[1].Value, 0x11);
EXPECT_EQ(Ranges[2].Range, AddressRange(0x4000, 0x5000));
EXPECT_EQ(Ranges[2].Value, 0x11);
EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0x11);
EXPECT_EQ(Ranges.getRangeThatContains(0x4000)->Value, 0x11);

// Add intersecting range and check mapped value.
Ranges.insert(AddressRange(0x0, 0x6000), 0x11);
EXPECT_EQ(Ranges.size(), 6u);
EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0x11);

// Check that mapped values are correctly preserved for combined ranges.
Ranges.clear();
Ranges.insert(AddressRange(0x0, 0xff), 0x11);
Ranges.insert(AddressRange(0x100, 0x1ff), 0x11);
Ranges.insert(AddressRange(0x200, 0x2ff), 0x11);
Ranges.insert(AddressRange(0x500, 0x5ff), 0x11);
Ranges.insert(AddressRange(0x300, 0x3ff), 0x11);
Ranges.insert(AddressRange(0x400, 0x4ff), 0x11);
Ranges.insert(AddressRange(0x600, 0x6ff), 0x11);
EXPECT_EQ(Ranges.size(), 7u);

Ranges.insert(AddressRange(0x150, 0x350), 0x11);
EXPECT_EQ(Ranges.size(), 9u);
EXPECT_EQ(Ranges[0].Range, AddressRange(0x0, 0xff));
EXPECT_EQ(Ranges[0].Value, 0x11);
EXPECT_EQ(Ranges[1].Range, AddressRange(0x100, 0x1ff));
EXPECT_EQ(Ranges[1].Value, 0x11);
EXPECT_EQ(Ranges[2].Range, AddressRange(0x1ff, 0x200));
EXPECT_EQ(Ranges[2].Value, 0x11);
EXPECT_EQ(Ranges[3].Range, AddressRange(0x200, 0x2ff));
EXPECT_EQ(Ranges[3].Value, 0x11);
EXPECT_EQ(Ranges[4].Range, AddressRange(0x2ff, 0x300));
EXPECT_EQ(Ranges[4].Value, 0x11);
EXPECT_EQ(Ranges[5].Range, AddressRange(0x300, 0x3ff));
EXPECT_EQ(Ranges[5].Value, 0x11);
EXPECT_EQ(Ranges[6].Range, AddressRange(0x400, 0x4ff));
EXPECT_EQ(Ranges[6].Value, 0x11);
EXPECT_EQ(Ranges[7].Range, AddressRange(0x500, 0x5ff));
EXPECT_EQ(Ranges[7].Value, 0x11);
EXPECT_EQ(Ranges[8].Range, AddressRange(0x600, 0x6ff));
EXPECT_EQ(Ranges[8].Value, 0x11);

Ranges.insert(AddressRange(0x3ff, 0x400), 0x11);
EXPECT_EQ(Ranges.size(), 10u);
EXPECT_EQ(Ranges[0].Range, AddressRange(0x0, 0xff));
EXPECT_EQ(Ranges[0].Value, 0x11);
EXPECT_EQ(Ranges[1].Range, AddressRange(0x100, 0x1ff));
EXPECT_EQ(Ranges[1].Value, 0x11);
EXPECT_EQ(Ranges[2].Range, AddressRange(0x1ff, 0x200));
EXPECT_EQ(Ranges[2].Value, 0x11);
EXPECT_EQ(Ranges[3].Range, AddressRange(0x200, 0x2ff));
EXPECT_EQ(Ranges[3].Value, 0x11);
EXPECT_EQ(Ranges[4].Range, AddressRange(0x2ff, 0x300));
EXPECT_EQ(Ranges[4].Value, 0x11);
EXPECT_EQ(Ranges[5].Range, AddressRange(0x300, 0x3ff));
EXPECT_EQ(Ranges[5].Value, 0x11);
EXPECT_EQ(Ranges[6].Range, AddressRange(0x3ff, 0x400));
EXPECT_EQ(Ranges[6].Value, 0x11);
EXPECT_EQ(Ranges[7].Range, AddressRange(0x400, 0x4ff));
EXPECT_EQ(Ranges[7].Value, 0x11);
EXPECT_EQ(Ranges[8].Range, AddressRange(0x500, 0x5ff));
EXPECT_EQ(Ranges[8].Value, 0x11);
EXPECT_EQ(Ranges[9].Range, AddressRange(0x600, 0x6ff));
EXPECT_EQ(Ranges[9].Value, 0x11);

/////////////////////////////////////////////
/// Check ranges with various mapped values.

// Clear ranges.
Ranges.clear();
EXPECT_EQ(Ranges.size(), 0u);
EXPECT_TRUE(Ranges.empty());

// Add range and check value.
// Add range and check mapped value.
Ranges.insert(AddressRange(0x1000, 0x2000), 0xfe);
EXPECT_EQ(Ranges.size(), 1u);
EXPECT_EQ(Ranges.getRangeValueThatContains(0x1000)->second, 0xfe);
EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0xfe);

// Add adjacent range and check value.
// Add adjacent range and check mapped value.
Ranges.insert(AddressRange(0x2000, 0x3000), 0xfc);
EXPECT_EQ(Ranges.size(), 1u);
EXPECT_EQ(Ranges.getRangeValueThatContains(0x1000)->second, 0xfc);
EXPECT_EQ(Ranges.getRangeValueThatContains(0x2000)->second, 0xfc);
EXPECT_EQ(Ranges.getRangeValueThatContains(0x2900)->second, 0xfc);
EXPECT_FALSE(Ranges.getRangeValueThatContains(0x3000));
EXPECT_EQ(Ranges.size(), 2u);
EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0xfe);
EXPECT_EQ(Ranges.getRangeThatContains(0x2000)->Value, 0xfc);
EXPECT_EQ(Ranges.getRangeThatContains(0x2900)->Value, 0xfc);
EXPECT_FALSE(Ranges.getRangeThatContains(0x3000));

// Add intersecting range and check value.
Ranges.insert(AddressRange(0x2000, 0x3000), 0xff);
EXPECT_EQ(Ranges.size(), 1u);
EXPECT_EQ(Ranges.getRangeValueThatContains(0x1000)->second, 0xff);
// Add intersecting range and check mapped value.
Ranges.insert(AddressRange(0x1000, 0x3000), 0xff);
EXPECT_EQ(Ranges.size(), 2u);
EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0xfe);

// Add second range and check values.
// Add one more range and check mapped values.
Ranges.insert(AddressRange(0x4000, 0x5000), 0x0);
EXPECT_EQ(Ranges.size(), 2u);
EXPECT_EQ(Ranges[0].second, 0xff);
EXPECT_EQ(Ranges[1].second, 0x0);
EXPECT_EQ(Ranges.getRangeValueThatContains(0x1000)->second, 0xff);
EXPECT_EQ(Ranges.getRangeValueThatContains(0x4000)->second, 0x0);
EXPECT_EQ(Ranges.size(), 3u);
EXPECT_EQ(Ranges[0].Value, 0xfe);
EXPECT_EQ(Ranges[1].Value, 0xfc);
EXPECT_EQ(Ranges[2].Value, 0x0);
EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0xfe);
EXPECT_EQ(Ranges.getRangeThatContains(0x4000)->Value, 0x0);

// Add intersecting range and check value.
// Add intersecting range and check mapped value.
Ranges.insert(AddressRange(0x0, 0x6000), 0x1);
EXPECT_EQ(Ranges.size(), 1u);
EXPECT_EQ(Ranges.getRangeValueThatContains(0x1000)->second, 0x1);
EXPECT_EQ(Ranges.size(), 6u);
EXPECT_EQ(Ranges[0].Value, 0x1);
EXPECT_EQ(Ranges[1].Value, 0xfe);
EXPECT_EQ(Ranges[2].Value, 0xfc);
EXPECT_EQ(Ranges[3].Value, 0x1);
EXPECT_EQ(Ranges[4].Value, 0x0);
EXPECT_EQ(Ranges[5].Value, 0x1);
EXPECT_EQ(Ranges.getRangeThatContains(0x1000)->Value, 0xfe);

// Check that values are correctly preserved for combined ranges.
// Check that mapped values are correctly preserved for combined ranges.
Ranges.clear();
Ranges.insert(AddressRange(0x0, 0xff), 0x1);
Ranges.insert(AddressRange(0x100, 0x1ff), 0x2);
Ranges.insert(AddressRange(0x200, 0x2ff), 0x3);
Ranges.insert(AddressRange(0x300, 0x3ff), 0x4);
Ranges.insert(AddressRange(0x400, 0x4ff), 0x5);
Ranges.insert(AddressRange(0x500, 0x5ff), 0x6);
Ranges.insert(AddressRange(0x400, 0x4ff), 0x5);
Ranges.insert(AddressRange(0x600, 0x6ff), 0x7);
EXPECT_EQ(Ranges.size(), 7u);

Ranges.insert(AddressRange(0x150, 0x350), 0xff);
EXPECT_EQ(Ranges.size(), 5u);
EXPECT_EQ(Ranges[0].first, AddressRange(0x0, 0xff));
EXPECT_EQ(Ranges[0].second, 0x1);
EXPECT_EQ(Ranges[1].first, AddressRange(0x100, 0x3ff));
EXPECT_EQ(Ranges[1].second, 0xff);
EXPECT_EQ(Ranges[2].first, AddressRange(0x400, 0x4ff));
EXPECT_EQ(Ranges[2].second, 0x5);
EXPECT_EQ(Ranges[3].first, AddressRange(0x500, 0x5ff));
EXPECT_EQ(Ranges[3].second, 0x6);
EXPECT_EQ(Ranges[4].first, AddressRange(0x600, 0x6ff));
EXPECT_EQ(Ranges[4].second, 0x7);
EXPECT_EQ(Ranges.size(), 9u);
EXPECT_EQ(Ranges[0].Range, AddressRange(0x0, 0xff));
EXPECT_EQ(Ranges[0].Value, 0x1);
EXPECT_EQ(Ranges[1].Range, AddressRange(0x100, 0x1ff));
EXPECT_EQ(Ranges[1].Value, 0x2);
EXPECT_EQ(Ranges[2].Range, AddressRange(0x1ff, 0x200));
EXPECT_EQ(Ranges[2].Value, 0xff);
EXPECT_EQ(Ranges[3].Range, AddressRange(0x200, 0x2ff));
EXPECT_EQ(Ranges[3].Value, 0x3);
EXPECT_EQ(Ranges[4].Range, AddressRange(0x2ff, 0x300));
EXPECT_EQ(Ranges[4].Value, 0xff);
EXPECT_EQ(Ranges[5].Range, AddressRange(0x300, 0x3ff));
EXPECT_EQ(Ranges[5].Value, 0x4);
EXPECT_EQ(Ranges[6].Range, AddressRange(0x400, 0x4ff));
EXPECT_EQ(Ranges[6].Value, 0x5);
EXPECT_EQ(Ranges[7].Range, AddressRange(0x500, 0x5ff));
EXPECT_EQ(Ranges[7].Value, 0x6);
EXPECT_EQ(Ranges[8].Range, AddressRange(0x600, 0x6ff));
EXPECT_EQ(Ranges[8].Value, 0x7);

Ranges.insert(AddressRange(0x650, 0x700), 0x8);
Ranges.insert(AddressRange(0x3ff, 0x400), 0x5);
EXPECT_EQ(Ranges.size(), 4u);
EXPECT_EQ(Ranges[0].first, AddressRange(0x0, 0xff));
EXPECT_EQ(Ranges[0].second, 0x1);
EXPECT_EQ(Ranges[1].first, AddressRange(0x100, 0x4ff));
EXPECT_EQ(Ranges[1].second, 0x5);
EXPECT_EQ(Ranges[2].first, AddressRange(0x500, 0x5ff));
EXPECT_EQ(Ranges[2].second, 0x6);
EXPECT_EQ(Ranges[3].first, AddressRange(0x600, 0x6ff));
EXPECT_EQ(Ranges[3].second, 0x7);
Ranges.insert(AddressRange(0x0, 0x40), 0xee);
EXPECT_EQ(Ranges.size(), 11u);
EXPECT_EQ(Ranges[0].Range, AddressRange(0x0, 0xff));
EXPECT_EQ(Ranges[0].Value, 0x1);
EXPECT_EQ(Ranges[1].Range, AddressRange(0x100, 0x1ff));
EXPECT_EQ(Ranges[1].Value, 0x2);
EXPECT_EQ(Ranges[2].Range, AddressRange(0x1ff, 0x200));
EXPECT_EQ(Ranges[2].Value, 0xff);
EXPECT_EQ(Ranges[3].Range, AddressRange(0x200, 0x2ff));
EXPECT_EQ(Ranges[3].Value, 0x3);
EXPECT_EQ(Ranges[4].Range, AddressRange(0x2ff, 0x300));
EXPECT_EQ(Ranges[4].Value, 0xff);
EXPECT_EQ(Ranges[5].Range, AddressRange(0x300, 0x3ff));
EXPECT_EQ(Ranges[5].Value, 0x4);
EXPECT_EQ(Ranges[6].Range, AddressRange(0x3ff, 0x400));
EXPECT_EQ(Ranges[6].Value, 0x5);
EXPECT_EQ(Ranges[7].Range, AddressRange(0x400, 0x4ff));
EXPECT_EQ(Ranges[7].Value, 0x5);
EXPECT_EQ(Ranges[8].Range, AddressRange(0x500, 0x5ff));
EXPECT_EQ(Ranges[8].Value, 0x6);
EXPECT_EQ(Ranges[9].Range, AddressRange(0x600, 0x6ff));
EXPECT_EQ(Ranges[9].Value, 0x7);
EXPECT_EQ(Ranges[10].Range, AddressRange(0x6ff, 0x700));
EXPECT_EQ(Ranges[10].Value, 0x8);
}

TEST(AddressRangeTest, TestRangesMapRandom) {
AddressRangesMap Ranges;
size_t NumElements = 100;

std::srand(std::time(nullptr));

// Fill ranges. Use the same mapped value.
for (size_t Idx = 0; Idx < NumElements; Idx++) {
uint64_t Start = static_cast<uint64_t>(std::rand() % 1000);
uint64_t End = Start + static_cast<uint64_t>(std::rand() % 1000);
Ranges.insert({Start, End}, 0xffLL);
}

// Check ranges.
for (size_t Idx = 0; Idx + 1 < Ranges.size(); Idx++) {
// Check that ranges are not intersected.
EXPECT_FALSE(Ranges[Idx].Range.intersects(Ranges[Idx + 1].Range));

// Check that ranges are sorted and not adjusted.
EXPECT_TRUE(Ranges[Idx].Range.end() <= Ranges[Idx + 1].Range.start());
}

Ranges.clear();
// Fill ranges. Use the various mapped value.
for (size_t Idx = 0; Idx < NumElements; Idx++) {
uint64_t Start = static_cast<uint64_t>(std::rand() % 1000);
uint64_t End = Start + static_cast<uint64_t>(std::rand() % 1000);
int64_t Value = static_cast<int64_t>(std::rand() % 10);
Ranges.insert({Start, End}, Value);
}

// Check ranges.
for (size_t Idx = 0; Idx + 1 < Ranges.size(); Idx++) {
// Check that ranges are not intersected.
EXPECT_FALSE(Ranges[Idx].Range.intersects(Ranges[Idx + 1].Range));

// Check that ranges are sorted and not adjusted.
EXPECT_TRUE(Ranges[Idx].Range.end() <= Ranges[Idx + 1].Range.start());
}
}