diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -252,6 +252,7 @@ bool pie; bool printGcSections; bool printIcfSections; + bool printMemoryUsage; bool relax; bool relaxGP; bool relocatable; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1235,6 +1235,7 @@ args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false); config->printGcSections = args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); + config->printMemoryUsage = args.hasArg(OPT_print_memory_usage); config->printArchiveStats = args.getLastArgValue(OPT_print_archive_stats); config->printSymbolOrder = args.getLastArgValue(OPT_print_symbol_order); diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -151,6 +151,9 @@ uint32_t negInvFlags; uint64_t curPos = 0; + uint64_t getOrigin() const { return origin().getValue(); } + uint64_t getLength() const { return length().getValue(); } + bool compatibleWith(uint32_t secFlags) const { if ((secFlags & negFlags) || (~secFlags & negInvFlags)) return false; @@ -333,6 +336,9 @@ // Used to handle INSERT AFTER statements. void processInsertCommands(); + // Describe memory region usage. + void printMemoryUsage(raw_ostream& os); + // SECTIONS command list. SmallVector sectionCommands; diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -157,8 +157,8 @@ static void expandMemoryRegion(MemoryRegion *memRegion, uint64_t size, StringRef secName) { memRegion->curPos += size; - uint64_t newSize = memRegion->curPos - (memRegion->origin)().getValue(); - uint64_t length = (memRegion->length)().getValue(); + uint64_t newSize = memRegion->curPos - memRegion->getOrigin(); + uint64_t length = memRegion->getLength(); if (newSize > length) error("section '" + secName + "' will not fit in region '" + memRegion->name + "': overflowed by " + Twine(newSize - length) + @@ -1429,3 +1429,30 @@ } return ret; } + +void LinkerScript::printMemoryUsage(raw_ostream& os) { + auto printSize = [&](uint64_t size) { + if ((size & 0x3fffffff) == 0) + os << format_decimal(size >> 30, 10) << " GB"; + else if ((size & 0xfffff) == 0) + os << format_decimal(size >> 20, 10) << " MB"; + else if ((size & 0x3ff) == 0) + os << format_decimal(size >> 10, 10) << " KB"; + else + os << " " << format_decimal(size, 10) << " B"; + }; + os << "Memory region Used Size Region Size %age Used\n"; + for (auto &pair : memoryRegions) { + MemoryRegion *m = pair.second; + uint64_t usedLength = m->curPos - m->getOrigin(); + os << right_justify(m->name, 16) << ": "; + printSize(usedLength); + uint64_t length = m->getLength(); + if (length != 0) { + printSize(length); + double percent = usedLength * 100.0 / length; + os << " " << format("%6.2f%%", percent); + } + os << "\n"; + } +} diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -351,6 +351,9 @@ def print_map: F<"print-map">, HelpText<"Print a link map to the standard output">; +def print_memory_usage: F<"print-memory-usage">, + HelpText<"Report target memory usage">; + defm relax: BB<"relax", "Enable target-specific relaxations if supported (default)", "Disable target-specific relaxations">; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -559,6 +559,10 @@ // fails, for example, due to an erroneous file size. writeMapAndCref(); + // Handle --print-memory-usage option. + if (config->printMemoryUsage) + script->printMemoryUsage(lld::outs()); + if (config->checkSections) checkSections(); diff --git a/lld/test/ELF/linkerscript/print-memory-usage1.s b/lld/test/ELF/linkerscript/print-memory-usage1.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/linkerscript/print-memory-usage1.s @@ -0,0 +1,27 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: echo "MEMORY { \ +# RUN: ROM (RX) : ORIGIN = 0x0, LENGTH = 1K \ +# RUN: RAM (W) : ORIGIN = 0x100000, LENGTH = 256K \ +# RUN: } \ +# RUN: SECTIONS { \ +# RUN: . = 0; \ +# RUN: .text : { *(.text) } \ +# RUN: .data : { *(.data) } \ +# RUN: }" > %t.script +# RUN: ld.lld %t.o --script %t.script -o %t --print-memory-usage | FileCheck %s + +# CHECK: Memory region Used Size Region Size %age Used +# CHECK: ROM: 4 B 1 KB 0.39% +# CHECK: RAM: 4 B 256 KB 0.00% + +.text +.globl _start +_start: + .long 1 + +.data +.globl b +b: + .long 2 diff --git a/lld/test/ELF/linkerscript/print-memory-usage2.s b/lld/test/ELF/linkerscript/print-memory-usage2.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/linkerscript/print-memory-usage2.s @@ -0,0 +1,21 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: echo "SECTIONS { \ +# RUN: . = 0; \ +# RUN: .text : { *(.text) } \ +# RUN: .data : { *(.data) } \ +# RUN: }" > %t.script +# RUN: ld.lld %t.o --script %t.script -o %t --print-memory-usage | FileCheck %s + +# CHECK: Memory region Used Size Region Size %age Used + +.text +.globl _start +_start: + .long 1 + +.data +.globl b +data_symbol: + .long 2 diff --git a/lld/test/ELF/linkerscript/print-memory-usage3.s b/lld/test/ELF/linkerscript/print-memory-usage3.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/linkerscript/print-memory-usage3.s @@ -0,0 +1,27 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: echo "MEMORY { \ +# RUN: ROM (RX) : ORIGIN = 0x0, LENGTH = 1M \ +# RUN: RAM (W) : ORIGIN = 0x1000000, LENGTH = 2048M \ +# RUN: } \ +# RUN: SECTIONS { \ +# RUN: . = 0; \ +# RUN: .text : { *(.text) } \ +# RUN: .data : { *(.data) } \ +# RUN: }" > %t.script +# RUN: ld.lld %t.o --script %t.script -o %t --print-memory-usage | FileCheck %s + +# CHECK: Memory region Used Size Region Size %age Used +# CHECK: ROM: 256 KB 1 MB 25.00% +# CHECK: RAM: 32 B 2 GB 0.00% + +.text +.globl _start +_start: + .space 256*1024 + +.data +.globl b +b: + .space 32