[llvm] r318356 - [globalisel][tablegen] Generate rule coverage and use it to identify untested rules
Eric Fiselier via llvm-commits
llvm-commits at lists.llvm.org
Sat Nov 18 14:42:53 PST 2017
I took the liberty of fixing the build error in r318602.
/Eric
On Sat, Nov 18, 2017 at 2:05 PM, Eric Fiselier <eric at efcs.ca> wrote:
> Hi Daniel,
>
> This broke the use of installed LLVM headers, since one of the changes
> adds a `config.h` include, but `config.h` isn't an installed header.
>
> /Eric
>
> On Wed, Nov 15, 2017 at 5:46 PM, Daniel Sanders via llvm-commits <
> llvm-commits at lists.llvm.org> wrote:
>
>> Author: dsanders
>> Date: Wed Nov 15 16:46:35 2017
>> New Revision: 318356
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=318356&view=rev
>> Log:
>> [globalisel][tablegen] Generate rule coverage and use it to identify
>> untested rules
>>
>> Summary:
>> This patch adds a LLVM_ENABLE_GISEL_COV which, like
>> LLVM_ENABLE_DAGISEL_COV,
>> causes TableGen to instrument the generated table to collect rule coverage
>> information. However, LLVM_ENABLE_GISEL_COV goes a bit further than
>> LLVM_ENABLE_DAGISEL_COV. The information is written to files
>> (${CMAKE_BINARY_DIR}/gisel-coverage-* by default). These files can then
>> be
>> concatenated into ${LLVM_GISEL_COV_PREFIX}-all after which TableGen will
>> read this information and use it to emit warnings about untested rules.
>>
>> This technique could also be used by SelectionDAG and can be further
>> extended to detect hot rules and give them priority over colder rules.
>>
>> Usage:
>> * Enable LLVM_ENABLE_GISEL_COV in CMake
>> * Build the compiler and run some tests
>> * cat gisel-coverage-[0-9]* > gisel-coverage-all
>> * Delete lib/Target/*/*GenGlobalISel.inc*
>> * Build the compiler
>>
>> Known issues:
>> * ${LLVM_GISEL_COV_PREFIX}-all must be generated as a manual
>> step due to a lack of a portable 'cat' command. It should be the
>> concatenation of all ${LLVM_GISEL_COV_PREFIX}-[0-9]* files.
>> * There's no mechanism to discard coverage information when the ruleset
>> changes
>>
>> Depends on D39742
>>
>> Reviewers: ab, qcolombet, t.p.northover, aditya_nandakumar, rovka
>>
>> Reviewed By: rovka
>>
>> Subscribers: vsk, arsenm, nhaehnle, mgorny, kristof.beyls, javed.absar,
>> igorb, llvm-commits
>>
>> Differential Revision: https://reviews.llvm.org/D39747
>>
>> Added:
>> llvm/trunk/include/llvm/Support/CodeGenCoverage.h
>> llvm/trunk/lib/Support/CodeGenCoverage.cpp
>> llvm/trunk/utils/llvm-gisel-cov.py
>> Modified:
>> llvm/trunk/CMakeLists.txt
>> llvm/trunk/cmake/modules/TableGen.cmake
>> llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
>> llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
>> llvm/trunk/include/llvm/Config/config.h.cmake
>> llvm/trunk/lib/CodeGen/GlobalISel/InstructionSelect.cpp
>> llvm/trunk/lib/Support/CMakeLists.txt
>> llvm/trunk/lib/Target/AArch64/AArch64InstructionSelector.cpp
>> llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp
>> llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.h
>> llvm/trunk/lib/Target/ARM/ARMInstructionSelector.cpp
>> llvm/trunk/lib/Target/X86/X86InstructionSelector.cpp
>> llvm/trunk/test/TableGen/GlobalISelEmitter.td
>> llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp
>>
>> Modified: llvm/trunk/CMakeLists.txt
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/CMakeLists.tx
>> t?rev=318356&r1=318355&r2=318356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/CMakeLists.txt (original)
>> +++ llvm/trunk/CMakeLists.txt Wed Nov 15 16:46:35 2017
>> @@ -167,6 +167,10 @@ if(LLVM_DEPENDENCY_DEBUGGING)
>> endif()
>>
>> option(LLVM_ENABLE_DAGISEL_COV "Debug: Prints tablegen patterns that
>> were used for selecting" OFF)
>> +option(LLVM_ENABLE_GISEL_COV "Enable collection of GlobalISel rule
>> coverage" OFF)
>> +if(LLVM_ENABLE_GISEL_COV)
>> + set(LLVM_GISEL_COV_PREFIX "${CMAKE_BINARY_DIR}/gisel-coverage-" CACHE
>> STRING "Provide a filename prefix to collect the GlobalISel rule coverage")
>> +endif()
>>
>> # Add path for custom modules
>> set(CMAKE_MODULE_PATH
>>
>> Modified: llvm/trunk/cmake/modules/TableGen.cmake
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/cmake/modules
>> /TableGen.cmake?rev=318356&r1=318355&r2=318356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/cmake/modules/TableGen.cmake (original)
>> +++ llvm/trunk/cmake/modules/TableGen.cmake Wed Nov 15 16:46:35 2017
>> @@ -52,6 +52,13 @@ function(tablegen project ofn)
>> list(APPEND LLVM_TABLEGEN_FLAGS "-instrument-coverage")
>> endif()
>> endif()
>> + if (LLVM_ENABLE_GISEL_COV)
>> + list(FIND ARGN "-gen-global-isel" idx)
>> + if( NOT idx EQUAL -1 )
>> + list(APPEND LLVM_TABLEGEN_FLAGS "-instrument-gisel-coverage")
>> + list(APPEND LLVM_TABLEGEN_FLAGS "-gisel-coverage-file=${LLVM_G
>> ISEL_COV_PREFIX}all")
>> + endif()
>> + endif()
>>
>> # We need both _TABLEGEN_TARGET and _TABLEGEN_EXE in the DEPENDS list
>> # (both the target and the file) to have .inc files rebuilt on
>>
>> Modified: llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelect
>> or.h
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/
>> CodeGen/GlobalISel/InstructionSelector.h?rev=318356&r1=
>> 318355&r2=318356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
>> (original)
>> +++ llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h Wed
>> Nov 15 16:46:35 2017
>> @@ -17,8 +17,9 @@
>> #define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H
>>
>> #include "llvm/ADT/DenseMap.h"
>> -#include "llvm/ADT/SmallVector.h"
>> #include "llvm/ADT/Optional.h"
>> +#include "llvm/ADT/SmallVector.h"
>> +#include "llvm/Support/CodeGenCoverage.h"
>> #include <bitset>
>> #include <cstddef>
>> #include <cstdint>
>> @@ -33,6 +34,7 @@ class APFloat;
>> class LLT;
>> class MachineInstr;
>> class MachineInstrBuilder;
>> +class MachineFunction;
>> class MachineOperand;
>> class MachineRegisterInfo;
>> class RegisterBankInfo;
>> @@ -262,6 +264,10 @@ enum {
>>
>> /// A successful emission
>> GIR_Done,
>> +
>> + /// Increment the rule coverage counter.
>> + /// - RuleID - The ID of the rule that was covered.
>> + GIR_Coverage,
>> };
>>
>> enum {
>> @@ -289,7 +295,7 @@ public:
>> /// if returns true:
>> /// for I in all mutated/inserted instructions:
>> /// !isPreISelGenericOpcode(I.getOpcode())
>> - virtual bool select(MachineInstr &I) const = 0;
>> + virtual bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo)
>> const = 0;
>>
>> protected:
>> using ComplexRendererFns =
>> @@ -328,8 +334,8 @@ protected:
>> const MatcherInfoTy<PredicateBitset, ComplexMatcherMemFn>
>> &MatcherInfo,
>> const int64_t *MatchTable, const TargetInstrInfo &TII,
>> MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI,
>> - const RegisterBankInfo &RBI,
>> - const PredicateBitset &AvailableFeatures) const;
>> + const RegisterBankInfo &RBI, const PredicateBitset
>> &AvailableFeatures,
>> + CodeGenCoverage &CoverageInfo) const;
>>
>> /// Constrain a register operand of an instruction \p I to a specified
>> /// register class. This could involve inserting COPYs before (for
>> uses) or
>>
>> Modified: llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelect
>> orImpl.h
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/
>> CodeGen/GlobalISel/InstructionSelectorImpl.h?rev=318356&r1=
>> 318355&r2=318356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
>> (original)
>> +++ llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
>> Wed Nov 15 16:46:35 2017
>> @@ -49,8 +49,8 @@ bool InstructionSelector::executeMatchTa
>> const MatcherInfoTy<PredicateBitset, ComplexMatcherMemFn>
>> &MatcherInfo,
>> const int64_t *MatchTable, const TargetInstrInfo &TII,
>> MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI,
>> - const RegisterBankInfo &RBI,
>> - const PredicateBitset &AvailableFeatures) const {
>> + const RegisterBankInfo &RBI, const PredicateBitset
>> &AvailableFeatures,
>> + CodeGenCoverage &CoverageInfo) const {
>> uint64_t CurrentIdx = 0;
>> SmallVector<uint64_t, 8> OnFailResumeAt;
>>
>> @@ -677,6 +677,16 @@ bool InstructionSelector::executeMatchTa
>> break;
>> }
>>
>> + case GIR_Coverage: {
>> + int64_t RuleID = MatchTable[CurrentIdx++];
>> + CoverageInfo.setCovered(RuleID);
>> +
>> + DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
>> + dbgs()
>> + << CurrentIdx << ": GIR_Coverage(" << RuleID
>> << ")");
>> + break;
>> + }
>> +
>> case GIR_Done:
>> DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
>> dbgs() << CurrentIdx << ": GIR_Done");
>>
>> Modified: llvm/trunk/include/llvm/Config/config.h.cmake
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/
>> Config/config.h.cmake?rev=318356&r1=318355&r2=318356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/include/llvm/Config/config.h.cmake (original)
>> +++ llvm/trunk/include/llvm/Config/config.h.cmake Wed Nov 15 16:46:35
>> 2017
>> @@ -437,4 +437,10 @@
>> /* Define to a function implementing strdup */
>> #cmakedefine strdup ${strdup}
>>
>> +/* Whether GlobalISel rule coverage is being collected */
>> +#cmakedefine01 LLVM_GISEL_COV_ENABLED
>> +
>> +/* Define to the default GlobalISel coverage file prefix */
>> +#cmakedefine LLVM_GISEL_COV_PREFIX "${LLVM_GISEL_COV_PREFIX}"
>> +
>> #endif
>>
>> Added: llvm/trunk/include/llvm/Support/CodeGenCoverage.h
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/
>> Support/CodeGenCoverage.h?rev=318356&view=auto
>> ============================================================
>> ==================
>> --- llvm/trunk/include/llvm/Support/CodeGenCoverage.h (added)
>> +++ llvm/trunk/include/llvm/Support/CodeGenCoverage.h Wed Nov 15
>> 16:46:35 2017
>> @@ -0,0 +1,37 @@
>> +//== llvm/Support/CodeGenCoverage.h ------------------------------*-
>> C++ -*-==//
>> +//
>> +// The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>> +//===------------------------------------------------------
>> ----------------===//
>> +/// \file This file provides rule coverage tracking for tablegen-erated
>> CodeGen.
>> +//===------------------------------------------------------
>> ----------------===//
>> +
>> +#ifndef LLVM_SUPPORT_CODEGENCOVERAGE_H
>> +#define LLVM_SUPPORT_CODEGENCOVERAGE_H
>> +
>> +#include "llvm/ADT/BitVector.h"
>> +#include "llvm/Config/config.h"
>> +
>> +namespace llvm {
>> +class LLVMContext;
>> +
>> +class CodeGenCoverage {
>> +protected:
>> + BitVector RuleCoverage;
>> +
>> +public:
>> + CodeGenCoverage();
>> +
>> + void setCovered(uint64_t RuleID);
>> + bool isCovered(uint64_t RuleID);
>> +
>> + bool parse(MemoryBuffer &Buffer, StringRef BackendName);
>> + bool emit(StringRef FilePrefix, StringRef BackendName) const;
>> + void reset();
>> +};
>> +} // end namespace llvm
>> +
>> +#endif // ifndef LLVM_SUPPORT_CODEGENCOVERAGE_H
>>
>> Modified: llvm/trunk/lib/CodeGen/GlobalISel/InstructionSelect.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/
>> GlobalISel/InstructionSelect.cpp?rev=318356&r1=318355&r2=318356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/lib/CodeGen/GlobalISel/InstructionSelect.cpp (original)
>> +++ llvm/trunk/lib/CodeGen/GlobalISel/InstructionSelect.cpp Wed Nov 15
>> 16:46:35 2017
>> @@ -20,10 +20,12 @@
>> #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
>> #include "llvm/CodeGen/MachineRegisterInfo.h"
>> #include "llvm/CodeGen/TargetPassConfig.h"
>> +#include "llvm/Config/config.h"
>> #include "llvm/IR/Constants.h"
>> #include "llvm/IR/Function.h"
>> #include "llvm/Support/CommandLine.h"
>> #include "llvm/Support/Debug.h"
>> +#include "llvm/Support/TargetRegistry.h"
>> #include "llvm/Target/TargetLowering.h"
>> #include "llvm/Target/TargetSubtargetInfo.h"
>>
>> @@ -31,6 +33,15 @@
>>
>> using namespace llvm;
>>
>> +#ifdef LLVM_GISEL_COV_PREFIX
>> +static cl::opt<std::string>
>> + CoveragePrefix("gisel-coverage-prefix",
>> cl::init(LLVM_GISEL_COV_PREFIX),
>> + cl::desc("Record GlobalISel rule coverage files of
>> this "
>> + "prefix if instrumentation was generated"));
>> +#else
>> +static const std::string CoveragePrefix = "";
>> +#endif
>> +
>> char InstructionSelect::ID = 0;
>> INITIALIZE_PASS_BEGIN(InstructionSelect, DEBUG_TYPE,
>> "Select target instructions out of generic
>> instructions",
>> @@ -66,6 +77,7 @@ bool InstructionSelect::runOnMachineFunc
>>
>> const TargetPassConfig &TPC = getAnalysis<TargetPassConfig>();
>> const InstructionSelector *ISel = MF.getSubtarget().getInstructi
>> onSelector();
>> + CodeGenCoverage CoverageInfo;
>> assert(ISel && "Cannot work without InstructionSelector");
>>
>> // An optimization remark emitter. Used to report failures.
>> @@ -127,7 +139,7 @@ bool InstructionSelect::runOnMachineFunc
>> continue;
>> }
>>
>> - if (!ISel->select(MI)) {
>> + if (!ISel->select(MI, CoverageInfo)) {
>> // FIXME: It would be nice to dump all inserted instructions.
>> It's
>> // not obvious how, esp. considering select() can insert after
>> MI.
>> reportGISelFailure(MF, TPC, MORE, "gisel-select", "cannot
>> select", MI);
>> @@ -187,6 +199,13 @@ bool InstructionSelect::runOnMachineFunc
>> auto &TLI = *MF.getSubtarget().getTargetLowering();
>> TLI.finalizeLowering(MF);
>>
>> + CoverageInfo.emit(CoveragePrefix,
>> + MF.getSubtarget()
>> + .getTargetLowering()
>> + ->getTargetMachine()
>> + .getTarget()
>> + .getBackendName());
>> +
>> // FIXME: Should we accurately track changes?
>> return true;
>> }
>>
>> Modified: llvm/trunk/lib/Support/CMakeLists.txt
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/
>> CMakeLists.txt?rev=318356&r1=318355&r2=318356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/lib/Support/CMakeLists.txt (original)
>> +++ llvm/trunk/lib/Support/CMakeLists.txt Wed Nov 15 16:46:35 2017
>> @@ -48,6 +48,7 @@ add_llvm_library(LLVMSupport
>> circular_raw_ostream.cpp
>> Chrono.cpp
>> COM.cpp
>> + CodeGenCoverage.cpp
>> CommandLine.cpp
>> Compression.cpp
>> ConvertUTF.cpp
>>
>> Added: llvm/trunk/lib/Support/CodeGenCoverage.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/
>> CodeGenCoverage.cpp?rev=318356&view=auto
>> ============================================================
>> ==================
>> --- llvm/trunk/lib/Support/CodeGenCoverage.cpp (added)
>> +++ llvm/trunk/lib/Support/CodeGenCoverage.cpp Wed Nov 15 16:46:35 2017
>> @@ -0,0 +1,118 @@
>> +//===- lib/Support/CodeGenCoverage.cpp ------------------------------
>> -------==//
>> +//
>> +// The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>> +//===------------------------------------------------------
>> ----------------===//
>> +/// \file
>> +/// This file implements the CodeGenCoverage class.
>> +//===------------------------------------------------------
>> ----------------===//
>> +
>> +#include "llvm/Support/CodeGenCoverage.h"
>> +
>> +#include "llvm/Support/Endian.h"
>> +#include "llvm/Support/FileSystem.h"
>> +#include "llvm/Support/Mutex.h"
>> +#include "llvm/Support/ScopedPrinter.h"
>> +#include "llvm/Support/ToolOutputFile.h"
>> +
>> +#if LLVM_ON_UNIX
>> +#include <unistd.h>
>> +#elif LLVM_ON_WIN32
>> +#include <windows.h>
>> +#endif
>> +
>> +using namespace llvm;
>> +
>> +static sys::SmartMutex<true> OutputMutex;
>> +
>> +CodeGenCoverage::CodeGenCoverage() {}
>> +
>> +void CodeGenCoverage::setCovered(uint64_t RuleID) {
>> + if (RuleCoverage.size() <= RuleID)
>> + RuleCoverage.resize(RuleID + 1, 0);
>> + RuleCoverage[RuleID] = true;
>> +}
>> +
>> +bool CodeGenCoverage::isCovered(uint64_t RuleID) {
>> + if (RuleCoverage.size() <= RuleID)
>> + return false;
>> + return RuleCoverage[RuleID];
>> +}
>> +
>> +bool CodeGenCoverage::parse(MemoryBuffer &Buffer, StringRef
>> BackendName) {
>> + const char *CurPtr = Buffer.getBufferStart();
>> +
>> + while (CurPtr != Buffer.getBufferEnd()) {
>> + // Read the backend name from the input.
>> + const char *LexedBackendName = CurPtr;
>> + while (*CurPtr++ != 0)
>> + ;
>> + if (CurPtr == Buffer.getBufferEnd())
>> + return false; // Data is invalid, expected rule id's to follow.
>> +
>> + bool IsForThisBackend = BackendName.equals(LexedBackendName);
>> + while (CurPtr != Buffer.getBufferEnd()) {
>> + if (std::distance(CurPtr, Buffer.getBufferEnd()) < 8)
>> + return false; // Data is invalid. Not enough bytes for another
>> rule id.
>> +
>> + uint64_t RuleID = support::endian::read64(CurPtr,
>> support::native);
>> + CurPtr += 8;
>> +
>> + // ~0ull terminates the rule id list.
>> + if (RuleID == ~0ull)
>> + break;
>> +
>> + // Anything else, is recorded or ignored depending on whether it's
>> + // intended for the backend we're interested in.
>> + if (IsForThisBackend)
>> + setCovered(RuleID);
>> + }
>> + }
>> +
>> + return true;
>> +}
>> +
>> +bool CodeGenCoverage::emit(StringRef CoveragePrefix,
>> + StringRef BackendName) const {
>> + if (!CoveragePrefix.empty() && !RuleCoverage.empty()) {
>> + sys::SmartScopedLock<true> Lock(OutputMutex);
>> +
>> + // We can handle locking within a process easily enough but we don't
>> want to
>> + // manage it between multiple processes. Use the process ID to
>> ensure no
>> + // more than one process is ever writing to the same file at the
>> same time.
>> + std::string Pid =
>> +#if LLVM_ON_UNIX
>> + llvm::to_string(::getpid());
>> +#elif LLVM_ON_WIN32
>> + llvm::to_string(::GetCurrentProcessId());
>> +#else
>> + "";
>> +#endif
>> +
>> + std::string CoverageFilename = (CoveragePrefix + Pid).str();
>> +
>> + std::error_code EC;
>> + sys::fs::OpenFlags OpenFlags = sys::fs::F_Append;
>> + std::unique_ptr<ToolOutputFile> CoverageFile =
>> + llvm::make_unique<ToolOutputFile>(CoverageFilename, EC,
>> OpenFlags);
>> + if (EC)
>> + return false;
>> +
>> + uint64_t Zero = 0;
>> + uint64_t InvZero = ~0ull;
>> + CoverageFile->os() << BackendName;
>> + CoverageFile->os().write((const char *)&Zero, sizeof(unsigned
>> char));
>> + for (uint64_t I : RuleCoverage.set_bits())
>> + CoverageFile->os().write((const char *)&I, sizeof(uint64_t));
>> + CoverageFile->os().write((const char *)&InvZero, sizeof(uint64_t));
>> +
>> + CoverageFile->keep();
>> + }
>> +
>> + return true;
>> +}
>> +
>> +void CodeGenCoverage::reset() { RuleCoverage.resize(0); }
>>
>> Modified: llvm/trunk/lib/Target/AArch64/AArch64InstructionSelector.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AA
>> rch64/AArch64InstructionSelector.cpp?rev=318356&r1=318355&
>> r2=318356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/lib/Target/AArch64/AArch64InstructionSelector.cpp
>> (original)
>> +++ llvm/trunk/lib/Target/AArch64/AArch64InstructionSelector.cpp Wed Nov
>> 15 16:46:35 2017
>> @@ -48,13 +48,13 @@ public:
>> const AArch64Subtarget &STI,
>> const AArch64RegisterBankInfo &RBI);
>>
>> - bool select(MachineInstr &I) const override;
>> + bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const
>> override;
>> static const char *getName() { return DEBUG_TYPE; }
>>
>> private:
>> /// tblgen-erated 'select' implementation, used as the initial
>> selector for
>> /// the patterns that don't require complex C++.
>> - bool selectImpl(MachineInstr &I) const;
>> + bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
>>
>> bool selectVaStartAAPCS(MachineInstr &I, MachineFunction &MF,
>> MachineRegisterInfo &MRI) const;
>> @@ -609,7 +609,8 @@ bool AArch64InstructionSelector::selectV
>> return true;
>> }
>>
>> -bool AArch64InstructionSelector::select(MachineInstr &I) const {
>> +bool AArch64InstructionSelector::select(MachineInstr &I,
>> + CodeGenCoverage &CoverageInfo)
>> const {
>> assert(I.getParent() && "Instruction should be in a basic block!");
>> assert(I.getParent()->getParent() && "Instruction should be in a
>> function!");
>>
>> @@ -667,7 +668,7 @@ bool AArch64InstructionSelector::select(
>> return false;
>> }
>>
>> - if (selectImpl(I))
>> + if (selectImpl(I, CoverageInfo))
>> return true;
>>
>> LLT Ty =
>>
>> Modified: llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AM
>> DGPU/AMDGPUInstructionSelector.cpp?rev=318356&r1=318355&r2=3
>> 18356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp (original)
>> +++ llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp Wed Nov
>> 15 16:46:35 2017
>> @@ -402,7 +402,8 @@ bool AMDGPUInstructionSelector::selectG_
>> return Ret;
>> }
>>
>> -bool AMDGPUInstructionSelector::select(MachineInstr &I) const {
>> +bool AMDGPUInstructionSelector::select(MachineInstr &I,
>> + CodeGenCoverage &CoverageInfo)
>> const {
>>
>> if (!isPreISelGenericOpcode(I.getOpcode()))
>> return true;
>>
>> Modified: llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.h
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AM
>> DGPU/AMDGPUInstructionSelector.h?rev=318356&r1=318355&r2=318356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.h (original)
>> +++ llvm/trunk/lib/Target/AMDGPU/AMDGPUInstructionSelector.h Wed Nov 15
>> 16:46:35 2017
>> @@ -35,7 +35,8 @@ public:
>> AMDGPUInstructionSelector(const SISubtarget &STI,
>> const AMDGPURegisterBankInfo &RBI);
>>
>> - bool select(MachineInstr &I) const override;
>> + bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const
>> override;
>> +
>> private:
>> struct GEPInfo {
>> const MachineInstr &GEP;
>>
>> Modified: llvm/trunk/lib/Target/ARM/ARMInstructionSelector.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AR
>> M/ARMInstructionSelector.cpp?rev=318356&r1=318355&r2=318356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/lib/Target/ARM/ARMInstructionSelector.cpp (original)
>> +++ llvm/trunk/lib/Target/ARM/ARMInstructionSelector.cpp Wed Nov 15
>> 16:46:35 2017
>> @@ -35,11 +35,11 @@ public:
>> ARMInstructionSelector(const ARMBaseTargetMachine &TM, const
>> ARMSubtarget &STI,
>> const ARMRegisterBankInfo &RBI);
>>
>> - bool select(MachineInstr &I) const override;
>> + bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const
>> override;
>> static const char *getName() { return DEBUG_TYPE; }
>>
>> private:
>> - bool selectImpl(MachineInstr &I) const;
>> + bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
>>
>> struct CmpConstants;
>> struct InsertInfo;
>> @@ -653,7 +653,8 @@ bool ARMInstructionSelector::selectShift
>> return constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI);
>> }
>>
>> -bool ARMInstructionSelector::select(MachineInstr &I) const {
>> +bool ARMInstructionSelector::select(MachineInstr &I,
>> + CodeGenCoverage &CoverageInfo) const
>> {
>> assert(I.getParent() && "Instruction should be in a basic block!");
>> assert(I.getParent()->getParent() && "Instruction should be in a
>> function!");
>>
>> @@ -668,7 +669,7 @@ bool ARMInstructionSelector::select(Mach
>> return true;
>> }
>>
>> - if (selectImpl(I))
>> + if (selectImpl(I, CoverageInfo))
>> return true;
>>
>> MachineInstrBuilder MIB{MF, I};
>>
>> Modified: llvm/trunk/lib/Target/X86/X86InstructionSelector.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X8
>> 6/X86InstructionSelector.cpp?rev=318356&r1=318355&r2=318356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/lib/Target/X86/X86InstructionSelector.cpp (original)
>> +++ llvm/trunk/lib/Target/X86/X86InstructionSelector.cpp Wed Nov 15
>> 16:46:35 2017
>> @@ -61,13 +61,13 @@ public:
>> X86InstructionSelector(const X86TargetMachine &TM, const X86Subtarget
>> &STI,
>> const X86RegisterBankInfo &RBI);
>>
>> - bool select(MachineInstr &I) const override;
>> + bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const
>> override;
>> static const char *getName() { return DEBUG_TYPE; }
>>
>> private:
>> /// tblgen-erated 'select' implementation, used as the initial
>> selector for
>> /// the patterns that don't require complex C++.
>> - bool selectImpl(MachineInstr &I) const;
>> + bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
>>
>> // TODO: remove after supported by Tablegen-erated instruction
>> selection.
>> unsigned getLoadStoreOp(const LLT &Ty, const RegisterBank &RB,
>> unsigned Opc,
>> @@ -93,9 +93,11 @@ private:
>> MachineFunction &MF) const;
>> bool selectCopy(MachineInstr &I, MachineRegisterInfo &MRI) const;
>> bool selectUnmergeValues(MachineInstr &I, MachineRegisterInfo &MRI,
>> - MachineFunction &MF) const;
>> + MachineFunction &MF,
>> + CodeGenCoverage &CoverageInfo) const;
>> bool selectMergeValues(MachineInstr &I, MachineRegisterInfo &MRI,
>> - MachineFunction &MF) const;
>> + MachineFunction &MF,
>> + CodeGenCoverage &CoverageInfo) const;
>> bool selectInsert(MachineInstr &I, MachineRegisterInfo &MRI,
>> MachineFunction &MF) const;
>> bool selectExtract(MachineInstr &I, MachineRegisterInfo &MRI,
>> @@ -294,7 +296,8 @@ bool X86InstructionSelector::selectCopy(
>> return true;
>> }
>>
>> -bool X86InstructionSelector::select(MachineInstr &I) const {
>> +bool X86InstructionSelector::select(MachineInstr &I,
>> + CodeGenCoverage &CoverageInfo) const
>> {
>> assert(I.getParent() && "Instruction should be in a basic block!");
>> assert(I.getParent()->getParent() && "Instruction should be in a
>> function!");
>>
>> @@ -318,7 +321,7 @@ bool X86InstructionSelector::select(Mach
>> assert(I.getNumOperands() == I.getNumExplicitOperands() &&
>> "Generic instruction has unexpected implicit operands\n");
>>
>> - if (selectImpl(I))
>> + if (selectImpl(I, CoverageInfo))
>> return true;
>>
>> DEBUG(dbgs() << " C++ instruction selection: "; I.print(dbgs()));
>> @@ -350,9 +353,9 @@ bool X86InstructionSelector::select(Mach
>> case TargetOpcode::G_UADDE:
>> return selectUadde(I, MRI, MF);
>> case TargetOpcode::G_UNMERGE_VALUES:
>> - return selectUnmergeValues(I, MRI, MF);
>> + return selectUnmergeValues(I, MRI, MF, CoverageInfo);
>> case TargetOpcode::G_MERGE_VALUES:
>> - return selectMergeValues(I, MRI, MF);
>> + return selectMergeValues(I, MRI, MF, CoverageInfo);
>> case TargetOpcode::G_EXTRACT:
>> return selectExtract(I, MRI, MF);
>> case TargetOpcode::G_INSERT:
>> @@ -1093,9 +1096,9 @@ bool X86InstructionSelector::selectInser
>> return constrainSelectedInstRegOperands(I, TII, TRI, RBI);
>> }
>>
>> -bool X86InstructionSelector::selectUnmergeValues(MachineInstr &I,
>> - MachineRegisterInfo
>> &MRI,
>> - MachineFunction &MF)
>> const {
>> +bool X86InstructionSelector::selectUnmergeValues(
>> + MachineInstr &I, MachineRegisterInfo &MRI, MachineFunction &MF,
>> + CodeGenCoverage &CoverageInfo) const {
>> assert((I.getOpcode() == TargetOpcode::G_UNMERGE_VALUES) &&
>> "unexpected instruction");
>>
>> @@ -1111,7 +1114,7 @@ bool X86InstructionSelector::selectUnmer
>> .addReg(SrcReg)
>> .addImm(Idx * DefSize);
>>
>> - if (!select(ExtrInst))
>> + if (!select(ExtrInst, CoverageInfo))
>> return false;
>> }
>>
>> @@ -1119,9 +1122,9 @@ bool X86InstructionSelector::selectUnmer
>> return true;
>> }
>>
>> -bool X86InstructionSelector::selectMergeValues(MachineInstr &I,
>> - MachineRegisterInfo &MRI,
>> - MachineFunction &MF)
>> const {
>> +bool X86InstructionSelector::selectMergeValues(
>> + MachineInstr &I, MachineRegisterInfo &MRI, MachineFunction &MF,
>> + CodeGenCoverage &CoverageInfo) const {
>> assert((I.getOpcode() == TargetOpcode::G_MERGE_VALUES) &&
>> "unexpected instruction");
>>
>> @@ -1153,7 +1156,7 @@ bool X86InstructionSelector::selectMerge
>>
>> DefReg = Tmp;
>>
>> - if (!select(InsertInst))
>> + if (!select(InsertInst, CoverageInfo))
>> return false;
>> }
>>
>> @@ -1161,7 +1164,7 @@ bool X86InstructionSelector::selectMerge
>> TII.get(TargetOpcode::COPY), DstReg)
>> .addReg(DefReg);
>>
>> - if (!select(CopyInst))
>> + if (!select(CopyInst, CoverageInfo))
>> return false;
>>
>> I.eraseFromParent();
>>
>> Modified: llvm/trunk/test/TableGen/GlobalISelEmitter.td
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/TableGen
>> /GlobalISelEmitter.td?rev=318356&r1=318355&r2=318356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/test/TableGen/GlobalISelEmitter.td (original)
>> +++ llvm/trunk/test/TableGen/GlobalISelEmitter.td Wed Nov 15 16:46:35
>> 2017
>> @@ -153,7 +153,7 @@ def HasC : Predicate<"Subtarget->hasC()"
>> // CHECK-NEXT: &MyTargetInstructionSelector::selectComplexPatternRR,
>> // gi_complex_rr
>> // CHECK-NEXT: }
>>
>> -// CHECK: bool MyTargetInstructionSelector::selectImpl(MachineInstr &I)
>> const {
>> +// CHECK: bool MyTargetInstructionSelector::selectImpl(MachineInstr &I,
>> CodeGenCoverage &CoverageInfo) const {
>> // CHECK-NEXT: MachineFunction &MF = *I.getParent()->getParent();
>> // CHECK-NEXT: MachineRegisterInfo &MRI = MF.getRegInfo();
>> // CHECK: AvailableFunctionFeatures = computeAvailableFunctionFeatures(&STI,
>> &MF);
>> @@ -899,6 +899,6 @@ def BR : I<(outs), (ins unknown:$target)
>>
>> // CHECK-NEXT: GIM_Reject,
>> // CHECK-NEXT: };
>> -// CHECK-NEXT: if (executeMatchTable(*this, OutMIs, State, MatcherInfo,
>> MatchTable0, TII, MRI, TRI, RBI, AvailableFeatures)) {
>> +// CHECK-NEXT: if (executeMatchTable(*this, OutMIs, State, MatcherInfo,
>> MatchTable0, TII, MRI, TRI, RBI, AvailableFeatures, CoverageInfo)) {
>> // CHECK-NEXT: return true;
>> // CHECK-NEXT: }
>>
>> Modified: llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/TableGe
>> n/GlobalISelEmitter.cpp?rev=318356&r1=318355&r2=318356&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp (original)
>> +++ llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp Wed Nov 15 16:46:35
>> 2017
>> @@ -36,6 +36,7 @@
>> #include "llvm/ADT/SmallSet.h"
>> #include "llvm/ADT/Statistic.h"
>> #include "llvm/CodeGen/MachineValueType.h"
>> +#include "llvm/Support/CodeGenCoverage.h"
>> #include "llvm/Support/CommandLine.h"
>> #include "llvm/Support/Error.h"
>> #include "llvm/Support/LowLevelTypeImpl.h"
>> @@ -43,8 +44,8 @@
>> #include "llvm/TableGen/Error.h"
>> #include "llvm/TableGen/Record.h"
>> #include "llvm/TableGen/TableGenBackend.h"
>> -#include <string>
>> #include <numeric>
>> +#include <string>
>> using namespace llvm;
>>
>> #define DEBUG_TYPE "gisel-emitter"
>> @@ -52,6 +53,7 @@ using namespace llvm;
>> STATISTIC(NumPatternTotal, "Total number of patterns");
>> STATISTIC(NumPatternImported, "Number of patterns imported from
>> SelectionDAG");
>> STATISTIC(NumPatternImportsSkipped, "Number of SelectionDAG imports
>> skipped");
>> +STATISTIC(NumPatternsTested, "Number of patterns executed according to
>> coverage information");
>> STATISTIC(NumPatternEmitted, "Number of patterns emitted");
>>
>> cl::OptionCategory GlobalISelEmitterCat("Options for -gen-global-isel");
>> @@ -62,6 +64,16 @@ static cl::opt<bool> WarnOnSkippedPatter
>> "in the GlobalISel selector"),
>> cl::init(false), cl::cat(GlobalISelEmitterCat));
>>
>> +static cl::opt<bool> GenerateCoverage(
>> + "instrument-gisel-coverage",
>> + cl::desc("Generate coverage instrumentation for GlobalISel"),
>> + cl::init(false), cl::cat(GlobalISelEmitterCat));
>> +
>> +static cl::opt<std::string> UseCoverageFile(
>> + "gisel-coverage-file", cl::init(""),
>> + cl::desc("Specify file to retrieve coverage information from"),
>> + cl::cat(GlobalISelEmitterCat));
>> +
>> namespace {
>> //===- Helper functions ------------------------------
>> ---------------------===//
>>
>> @@ -569,14 +581,20 @@ protected:
>> /// A map of Symbolic Names to ComplexPattern sub-operands.
>> DefinedComplexPatternSubOperandMap ComplexSubOperands;
>>
>> + uint64_t RuleID;
>> + static uint64_t NextRuleID;
>> +
>> public:
>> RuleMatcher(ArrayRef<SMLoc> SrcLoc)
>> : Matchers(), Actions(), InsnVariableIDs(), MutatableInsns(),
>> DefinedOperands(), NextInsnVarID(0), NextOutputInsnID(0),
>> - NextTempRegID(0), SrcLoc(SrcLoc), ComplexSubOperands() {}
>> + NextTempRegID(0), SrcLoc(SrcLoc), ComplexSubOperands(),
>> + RuleID(NextRuleID++) {}
>> RuleMatcher(RuleMatcher &&Other) = default;
>> RuleMatcher &operator=(RuleMatcher &&Other) = default;
>>
>> + uint64_t getRuleID() const { return RuleID; }
>> +
>> InstructionMatcher &addInstructionMatcher(StringRef SymbolicName);
>> void addRequiredFeature(Record *Feature);
>> const std::vector<Record *> &getRequiredFeatures() const;
>> @@ -664,6 +682,8 @@ public:
>> unsigned allocateTempRegID() { return NextTempRegID++; }
>> };
>>
>> +uint64_t RuleMatcher::NextRuleID = 0;
>> +
>> using action_iterator = RuleMatcher::action_iterator;
>>
>> template <class PredicateTy> class PredicateListMatcher {
>> @@ -2204,6 +2224,11 @@ void RuleMatcher::emit(MatchTable &Table
>>
>> for (const auto &MA : Actions)
>> MA->emitActionOpcodes(Table, *this);
>> +
>> + if (GenerateCoverage)
>> + Table << MatchTable::Opcode("GIR_Coverage") <<
>> MatchTable::IntValue(RuleID)
>> + << MatchTable::LineBreak;
>> +
>> Table << MatchTable::Opcode("GIR_Done", -1) << MatchTable::LineBreak
>> << MatchTable::Label(LabelID);
>> }
>> @@ -2309,6 +2334,9 @@ private:
>> // Map of predicates to their subtarget features.
>> SubtargetFeatureInfoMap SubtargetFeatures;
>>
>> + // Rule coverage information.
>> + Optional<CodeGenCoverage> RuleCoverage;
>> +
>> void gatherNodeEquivs();
>> Record *findNodeEquiv(Record *N) const;
>>
>> @@ -3227,6 +3255,20 @@ void GlobalISelEmitter::emitImmPredicate
>> }
>>
>> void GlobalISelEmitter::run(raw_ostream &OS) {
>> + if (!UseCoverageFile.empty()) {
>> + RuleCoverage = CodeGenCoverage();
>> + auto RuleCoverageBufOrErr = MemoryBuffer::getFile(UseCoverageFile);
>> + if (!RuleCoverageBufOrErr) {
>> + PrintWarning(SMLoc(), "Missing rule coverage data");
>> + RuleCoverage = None;
>> + } else {
>> + if (!RuleCoverage->parse(*RuleCoverageBufOrErr.get(),
>> Target.getName())) {
>> + PrintWarning(SMLoc(), "Ignoring invalid or missing rule coverage
>> data");
>> + RuleCoverage = None;
>> + }
>> + }
>> + }
>> +
>> // Track the GINodeEquiv definitions.
>> gatherNodeEquivs();
>>
>> @@ -3252,6 +3294,13 @@ void GlobalISelEmitter::run(raw_ostream
>> continue;
>> }
>>
>> + if (RuleCoverage) {
>> + if (RuleCoverage->isCovered(MatcherOrErr->getRuleID()))
>> + ++NumPatternsTested;
>> + else
>> + PrintWarning(Pat.getSrcRecord()->getLoc(),
>> + "Pattern is not covered by a test");
>> + }
>> Rules.push_back(std::move(MatcherOrErr.get()));
>> }
>>
>> @@ -3431,7 +3480,8 @@ void GlobalISelEmitter::run(raw_ostream
>> OS << "};\n\n";
>>
>> OS << "bool " << Target.getName()
>> - << "InstructionSelector::selectImpl(MachineInstr &I) const {\n"
>> + << "InstructionSelector::selectImpl(MachineInstr &I,
>> CodeGenCoverage "
>> + "&CoverageInfo) const {\n"
>> << " MachineFunction &MF = *I.getParent()->getParent();\n"
>> << " MachineRegisterInfo &MRI = MF.getRegInfo();\n"
>> << " // FIXME: This should be computed on a per-function basis
>> rather "
>> @@ -3452,7 +3502,7 @@ void GlobalISelEmitter::run(raw_ostream
>> Table.emitDeclaration(OS);
>> OS << " if (executeMatchTable(*this, OutMIs, State, MatcherInfo, ";
>> Table.emitUse(OS);
>> - OS << ", TII, MRI, TRI, RBI, AvailableFeatures)) {\n"
>> + OS << ", TII, MRI, TRI, RBI, AvailableFeatures, CoverageInfo)) {\n"
>> << " return true;\n"
>> << " }\n\n";
>>
>>
>> Added: llvm/trunk/utils/llvm-gisel-cov.py
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/llvm-gi
>> sel-cov.py?rev=318356&view=auto
>> ============================================================
>> ==================
>> --- llvm/trunk/utils/llvm-gisel-cov.py (added)
>> +++ llvm/trunk/utils/llvm-gisel-cov.py Wed Nov 15 16:46:35 2017
>> @@ -0,0 +1,67 @@
>> +#!/usr/bin/env python
>> +"""
>> +Summarize the information in the given coverage files.
>> +
>> +Emits the number of rules covered or the percentage of rules covered
>> depending
>> +on whether --num-rules has been used to specify the total number of
>> rules.
>> +"""
>> +
>> +import argparse
>> +import struct
>> +
>> +class FileFormatError(Exception):
>> + pass
>> +
>> +def backend_int_pair(s):
>> + backend, sep, value = s.partition('=')
>> + if (sep is None):
>> + raise argparse.ArgumentTypeError("'=' missing, expected name=value")
>> + if (not backend):
>> + raise argparse.ArgumentTypeError("Expected name=value")
>> + if (not value):
>> + raise argparse.ArgumentTypeError("Expected name=value")
>> + return backend, int(value)
>> +
>> +def main():
>> + parser = argparse.ArgumentParser(description=__doc__)
>> + parser.add_argument('input', nargs='+')
>> + parser.add_argument('--num-rules', type=backend_int_pair,
>> action='append',
>> + metavar='BACKEND=NUM',
>> + help='Specify the number of rules for a backend')
>> + args = parser.parse_args()
>> +
>> + covered_rules = {}
>> +
>> + for input_filename in args.input:
>> + with open(input_filename, 'rb') as input_fh:
>> + data = input_fh.read()
>> + pos = 0
>> + while data:
>> + backend, _, data = data.partition('\0')
>> + pos += len(backend)
>> + pos += 1
>> +
>> + if len(backend) == 0:
>> + raise FileFormatError()
>> + backend, = struct.unpack("%ds" % len(backend), backend)
>> +
>> + while data:
>> + if len(data) < 8:
>> + raise FileFormatError()
>> + rule_id, = struct.unpack("Q", data[:8])
>> + pos += 8
>> + data = data[8:]
>> + if rule_id == (2 ** 64) - 1:
>> + break
>> + covered_rules[backend] = covered_rules.get(backend, {})
>> + covered_rules[backend][rule_id] =
>> covered_rules[backend].get(rule_id, 0) + 1
>> +
>> + num_rules = dict(args.num_rules)
>> + for backend, rules_for_backend in covered_rules.items():
>> + if backend in num_rules:
>> + print "%s: %3.2f%% of rules covered" % (backend,
>> (float(len(rules_for_backend.keys())) / num_rules[backend]) * 100)
>> + else:
>> + print "%s: %d rules covered" % (backend,
>> len(rules_for_backend.keys()))
>> +
>> +if __name__ == '__main__':
>> + main()
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20171118/b49f4e5e/attachment-0001.html>
More information about the llvm-commits
mailing list