diff --git a/lld/test/CMakeLists.txt b/lld/test/CMakeLists.txt --- a/lld/test/CMakeLists.txt +++ b/lld/test/CMakeLists.txt @@ -34,7 +34,7 @@ set(LLD_TEST_DEPS lld) if (NOT LLD_BUILT_STANDALONE) list(APPEND LLD_TEST_DEPS - FileCheck count llc llvm-ar llvm-as llvm-bcanalyzer llvm-config llvm-cvtres + FileCheck count extract llc llvm-ar llvm-as llvm-bcanalyzer llvm-config llvm-cvtres llvm-dis llvm-dwarfdump llvm-lib llvm-lipo llvm-mc llvm-nm llvm-objcopy llvm-objdump llvm-pdbutil llvm-readelf llvm-readobj llvm-strip not obj2yaml opt yaml2obj diff --git a/lld/test/ELF/linkerscript/noload.s b/lld/test/ELF/linkerscript/noload.s --- a/lld/test/ELF/linkerscript/noload.s +++ b/lld/test/ELF/linkerscript/noload.s @@ -1,11 +1,7 @@ # REQUIRES: x86 -# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o -# RUN: echo "SECTIONS { \ -# RUN: .data_noload_a (NOLOAD) : { *(.data_noload_a) } \ -# RUN: .data_noload_b (0x10000) (NOLOAD) : { *(.data_noload_b) } \ -# RUN: .no_input_sec_noload (NOLOAD) : { . += 1; } \ -# RUN: .text (0x20000) : { *(.text) } };" > %t.script -# RUN: ld.lld -o %t --script %t.script %t.o +# RUN: extract asm %s -o %t.s && extract lds %s -o %t.lds +# RUN: llvm-mc -filetype=obj -triple=x86_64 %t.s -o %t.o +# RUN: ld.lld -o %t --script %t.lds %t.o # RUN: llvm-readelf -S -l %t | FileCheck %s # CHECK: Name Type Address Off Size @@ -16,6 +12,7 @@ # CHECK: Type Offset VirtAddr PhysAddr # CHECK-NEXT: LOAD 0x001000 0x0000000000020000 0x0000000000020000 +#--- asm .section .text,"ax",@progbits nop @@ -24,3 +21,11 @@ .section .data_noload_b,"aw",@progbits .zero 4096 + +#--- lds +SECTIONS { + .data_noload_a (NOLOAD) : { *(.data_noload_a) } + .data_noload_b (0x10000) (NOLOAD) : { *(.data_noload_b) } + .no_input_sec_noload (NOLOAD) : { . += 1; } + .text (0x20000) : { *(.text) } +} diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py --- a/lld/test/lit.cfg.py +++ b/lld/test/lit.cfg.py @@ -39,9 +39,9 @@ llvm_config.use_lld() tool_patterns = [ - 'llc', 'llvm-as', 'llvm-mc', 'llvm-nm', 'llvm-objdump', 'llvm-pdbutil', - 'llvm-dwarfdump', 'llvm-readelf', 'llvm-readobj', 'obj2yaml', 'yaml2obj', - 'opt', 'llvm-dis'] + 'extract', 'llc', 'llvm-as', 'llvm-mc', 'llvm-nm', 'llvm-objdump', + 'llvm-pdbutil', 'llvm-dwarfdump', 'llvm-readelf', 'llvm-readobj', + 'obj2yaml', 'yaml2obj', 'opt', 'llvm-dis'] llvm_config.add_tool_substitutions(tool_patterns) @@ -87,7 +87,7 @@ # Indirectly check if the mt.exe Microsoft utility exists by searching for # cvtres, which always accompanies it. Alternatively, check if we can use # libxml2 to merge manifests. -if (lit.util.which('cvtres', config.environment['PATH']) or +if (lit.util.which('cvtres', config.environment['PATH']) or config.llvm_libxml2_enabled): config.available_features.add('manifest_tool') diff --git a/llvm/docs/TestingGuide.rst b/llvm/docs/TestingGuide.rst --- a/llvm/docs/TestingGuide.rst +++ b/llvm/docs/TestingGuide.rst @@ -271,8 +271,27 @@ Extra files ----------- -If your test requires extra files besides the file containing the ``RUN:`` -lines, the idiomatic place to put them is in a subdirectory ``Inputs``. +If your test requires extra files besides the file containing the ``RUN:`` lines +and the extra files are small, consider specifying them in the same file and +using ``extract`` to extract them. For example, + +.. code-block:: llvm + + ; RUN: extract b %s -o %tb.ll + ; RUN: extract a %s | llvm-link - %tb.ll -S | FileCheck %s + + ; CHECK: ... + + ;--- a + ... + ;--- b + ... + +The parts are separated by the regex ``^(.|//)--- ``. By default the +extracted content has leading empty lines to preserve line numbers. Specify +``--no-leading-lines`` to drop leading lines. + +If the extra files are large, the idiomatic place to put them is in a subdirectory ``Inputs``. You can then refer to the extra files as ``%S/Inputs/foo.bar``. For example, consider ``test/Linker/ident.ll``. The directory structure is diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt --- a/llvm/test/CMakeLists.txt +++ b/llvm/test/CMakeLists.txt @@ -52,6 +52,7 @@ UnitTests bugpoint count + extract llc lli lli-child-target diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -145,7 +145,7 @@ # FIXME: Why do we have both `lli` and `%lli` that do slightly different things? tools.extend([ - 'dsymutil', 'lli', 'lli-child-target', 'llvm-ar', 'llvm-as', + 'dsymutil', 'extract', 'lli', 'lli-child-target', 'llvm-ar', 'llvm-as', 'llvm-addr2line', 'llvm-bcanalyzer', 'llvm-config', 'llvm-cov', 'llvm-cxxdump', 'llvm-cvtres', 'llvm-diff', 'llvm-dis', 'llvm-dwarfdump', 'llvm-exegesis', 'llvm-extract', 'llvm-isel-fuzzer', 'llvm-ifs', diff --git a/llvm/test/tools/extract/basic.test b/llvm/test/tools/extract/basic.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/extract/basic.test @@ -0,0 +1,38 @@ +# AA-NOT: {{.}} +# AA: {{^}}aa{{$}} +#--- aa +aa +; BB-NOT: {{.}} +; BB: {{^}}bb{{$}} +;--- bb +bb + +// CC: // Comments are preserved. +//--- cc +cc +// Comments are preserved. +;--- dup +;--- dup + +## The first line to the end of aa spans 6 lines. +# RUN: extract aa %s | FileCheck %s --check-prefix=AA --implicit-check-not='{{^}}bb' +# RUN: extract aa %s | count 6 + +## The first line to the end of bb spans 10 lines. +# RUN: extract bb - < %s | FileCheck %s --check-prefix=BB --implicit-check-not='{{^}}aa' +# RUN: extract bb - < %s | count 10 + +# RUN: extract cc %s | FileCheck %s --check-prefix=CC +# RUN: extract cc %s | count 13 + +# RUN: not extract aa 2>&1 | FileCheck %s --check-prefix=NO_INPUT + +# NO_INPUT: extract: error: input filename is not specified + +# RUN: not extract dup %s 2>&1 | FileCheck %s --check-prefix=DUP + +# DUP: extract: error: {{.*}}.test: ';--- dup' occurs more than once + +# RUN: not extract not_exist %s 2>&1 | FileCheck %s --check-prefix=NOT_EXIST + +# NOT_EXIST: extract: error: {{.*}}.test: ';--- not_exist' was not found diff --git a/llvm/test/tools/extract/no-leading-lines.test b/llvm/test/tools/extract/no-leading-lines.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/extract/no-leading-lines.test @@ -0,0 +1,10 @@ +## With --no-leading-lines, don't add leading lines (which is used to preserve line numbers). + +# RUN: extract --no-leading-lines input %s -o %t +# RUN: count 1 < %t +# RUN: FileCheck %s < %t + +# CHECK: input + +#--- input +input diff --git a/llvm/test/tools/gold/X86/multiple-sections.ll b/llvm/test/tools/gold/X86/multiple-sections.ll --- a/llvm/test/tools/gold/X86/multiple-sections.ll +++ b/llvm/test/tools/gold/X86/multiple-sections.ll @@ -1,10 +1,8 @@ -; RUN: echo ".text.tin" > %t_order_lto.txt -; RUN: echo ".text._start" >> %t_order_lto.txt -; RUN: echo ".text.pat" >> %t_order_lto.txt -; RUN: llvm-as %s -o %t.o +; RUN: extract order %s -o %t.order +; RUN: extract ir %s | llvm-as -o %t.o ; RUN: %gold -plugin %llvmshlibdir/LLVMgold%shlibext \ ; RUN: -m elf_x86_64 -o %t.exe %t.o \ -; RUN: --section-ordering-file=%t_order_lto.txt +; RUN: --section-ordering-file=%t.order ; RUN: llvm-readelf -s %t.exe | FileCheck %s ; Check that the order of the sections is tin -> _start -> pat. @@ -13,6 +11,12 @@ ; CHECK: 00000000004000b0 1 FUNC LOCAL DEFAULT 1 tin ; CHECK: 00000000004000c0 15 FUNC GLOBAL DEFAULT 1 _start +;--- order +.text.tin +.text._start +.text.pat + +;--- ir target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/test/tools/llvm-objcopy/ELF/strip-symbol.test b/llvm/test/tools/llvm-objcopy/ELF/strip-symbol.test --- a/llvm/test/tools/llvm-objcopy/ELF/strip-symbol.test +++ b/llvm/test/tools/llvm-objcopy/ELF/strip-symbol.test @@ -1,19 +1,24 @@ -# RUN: yaml2obj %s -o %t +# RUN: extract yaml %s | yaml2obj - -o %t # RUN: llvm-objcopy --strip-symbol baz -N bar %t %t2 # RUN: llvm-readobj --symbols --sections %t2 | FileCheck %s # RUN: llvm-strip --strip-symbol baz -N bar %t -o %t3 # RUN: cmp %t2 %t3 # RUN: llvm-strip --regex --strip-symbol '^b.*' -N bar %t -o %t4 # RUN: cmp %t3 %t4 -# RUN: echo " bar # bar" > %t-list.txt -# RUN: echo " baz # baz" >> %t-list.txt -# RUN: echo " # no symbol" >> %t-list.txt -# RUN: llvm-objcopy --strip-symbols %t-list.txt %t %t5 +# RUN: extract list1 %s -o %t-list.txt && llvm-objcopy --strip-symbols %t-list.txt %t %t5 # RUN: cmp %t3 %t5 -# RUN: echo "b.* # bar & baz" > %t-list2.txt -# RUN: llvm-objcopy --regex --strip-symbols %t-list2.txt %t %t6 +# RUN: extract list2 %s -o %t-list2.txt && llvm-objcopy --regex --strip-symbols %t-list2.txt %t %t6 # RUN: cmp %t3 %t6 +#--- list1 +bar # bar +baz # baz +# no symbol + +#--- list2 +b.* # bar & baz + +#--- yaml !ELF FileHeader: Class: ELFCLASS64 diff --git a/llvm/test/tools/llvm-strings/radix.test b/llvm/test/tools/llvm-strings/radix.test --- a/llvm/test/tools/llvm-strings/radix.test +++ b/llvm/test/tools/llvm-strings/radix.test @@ -1,15 +1,18 @@ ## Show that llvm-strings can handle the -t/--radix switch properly. -RUN: echo one > %t -RUN: echo two >> %t -RUN: echo three >> %t -RUN: echo four >> %t -RUN: echo five >> %t -RUN: echo six >> %t -RUN: echo seven >> %t -RUN: echo eight >> %t -RUN: echo nine >> %t -RUN: echo ten >> %t +RUN: extract --no-leading-lines input %s -o %t +#--- input +one +two +three +four +five +six +seven +eight +nine +ten +#--- end RUN: llvm-strings %t | FileCheck %s -check-prefix CHECK-NONE --implicit-check-not={{.}} RUN: llvm-strings -t d %t | FileCheck %s -check-prefix CHECK-DEC --strict-whitespace --implicit-check-not={{.}} diff --git a/llvm/tools/extract/.clang-tidy b/llvm/tools/extract/.clang-tidy new file mode 100644 --- /dev/null +++ b/llvm/tools/extract/.clang-tidy @@ -0,0 +1,19 @@ +# Almost identical to the top-level .clang-tidy, except that {Member,Parameter,Variable}Case use camelBack. +Checks: '-*,clang-diagnostic-*,llvm-*,misc-*,-misc-unused-parameters,-misc-non-private-member-variables-in-classes,readability-identifier-naming' +CheckOptions: + - key: readability-identifier-naming.ClassCase + value: CamelCase + - key: readability-identifier-naming.EnumCase + value: CamelCase + - key: readability-identifier-naming.FunctionCase + value: camelBack + - key: readability-identifier-naming.MemberCase + value: camelBack + - key: readability-identifier-naming.ParameterCase + value: camelBack + - key: readability-identifier-naming.UnionCase + value: CamelCase + - key: readability-identifier-naming.VariableCase + value: camelBack + - key: readability-identifier-naming.IgnoreMainLikeFunctions + value: 1 diff --git a/llvm/tools/extract/CMakeLists.txt b/llvm/tools/extract/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/tools/extract/CMakeLists.txt @@ -0,0 +1,7 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_llvm_tool(extract + extract.cpp + ) diff --git a/llvm/tools/extract/extract.cpp b/llvm/tools/extract/extract.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/extract/extract.cpp @@ -0,0 +1,113 @@ +//===- extract.cpp - Input splitting utility ------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Split input into multipe parts separated by regex '^(.|//)--- ' and extract +// the specified part. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" +#include + +using namespace llvm; + +static cl::OptionCategory cat("extract Options"); + +static cl::opt part(cl::Positional, cl::desc("part"), + cl::cat(cat)); + +static cl::opt input(cl::Positional, cl::desc("filename"), + cl::cat(cat)); + +static cl::opt output("o", cl::desc("Output filename"), + cl::value_desc("filename"), cl::init("-"), + cl::cat(cat)); + +static cl::opt noLeadingLines("no-leading-lines", + cl::desc("Don't preserve line numbers"), + cl::cat(cat)); + +static StringRef toolName; + +LLVM_ATTRIBUTE_NORETURN static void error(StringRef filename, + const Twine &message) { + if (filename.empty()) + WithColor::error(errs(), toolName) << message << '\n'; + else + WithColor::error(errs(), toolName) << filename << ": " << message << '\n'; + exit(1); +} + +static void handle(MemoryBuffer &inputBuf, StringRef input) { + const char *partBegin = nullptr, *partEnd = nullptr; + int numEmptyLines = 0; + StringRef separator; + for (line_iterator i(inputBuf, /*SkipBlanks=*/false, '\0'); !i.is_at_eof();) { + StringRef line = *i++; + size_t markerLen = line.startswith("//") ? 6 : 5; + if (!(line.size() > markerLen && + line.substr(markerLen - 4).startswith("--- "))) + continue; + separator = line.substr(0, markerLen); + StringRef cur = line.substr(markerLen); + if (cur == part) { + if (partBegin) + error(input, "'" + separator + cur + "' occurs more than once"); + if (!noLeadingLines) + numEmptyLines = i.line_number() - 1; + if (i.is_at_eof()) + break; + partBegin = i->data(); + } else if (partBegin && !partEnd) { + partEnd = line.data(); + } + } + if (!partBegin) + error(input, "'" + separator + part + "' was not found"); + if (!partEnd) + partEnd = inputBuf.getBufferEnd(); + + Expected> outputBuf = + FileOutputBuffer::create(output, numEmptyLines + (partEnd - partBegin)); + if (!outputBuf) + error(input, toString(outputBuf.takeError())); + uint8_t *buf = (*outputBuf)->getBufferStart(); + + // If --no-leading-lines is not specified, numEmptyLines is 0. Append newlines + // so that the extracted part preserves line numbers. + std::fill_n(buf, numEmptyLines, '\n'); + std::copy(partBegin, partEnd, buf + numEmptyLines); + if (Error e = (*outputBuf)->commit()) + error(input, toString(std::move(e))); +} + +int main(int argc, const char **argv) { + toolName = sys::path::stem(argv[0]); + cl::HideUnrelatedOptions({&cat}); + cl::ParseCommandLineOptions( + argc, argv, + "Split input into multiple parts separated by regex '^(.|//)--- ' and " + "extract the part specified by '^(.|//)--- '\n", + nullptr, + /*EnvVar=*/nullptr, + /*LongOptionsUseDoubleDash=*/true); + + if (input.empty()) + error("", "input filename is not specified"); + ErrorOr> bufferOrErr = + MemoryBuffer::getFileOrSTDIN(input); + if (std::error_code ec = bufferOrErr.getError()) + error(input, ec.message()); + handle(**bufferOrErr, input); +}